All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.avaje.prism.internal.UtilWriter Maven / Gradle / Ivy

The newest version!
package io.avaje.prism.internal;

import java.io.PrintWriter;

public class UtilWriter {
  private UtilWriter() {}

  public static void write(PrintWriter out, String packageName) {

    out.append(
        "package "
            + packageName
            + ";\n"
            + "\n"
            + "\n"
            + "import java.util.Map;\n"
            + "import java.util.regex.Matcher;\n"
            + "import java.util.regex.Pattern;\n"
            + "\n"
            + "import javax.annotation.processing.Generated;\n"
            + "import javax.lang.model.element.Element;\n"
            + "import javax.lang.model.element.VariableElement;\n"
            + "\n"
            + "@Generated(\"avaje-prism-generator\")\n"
            + "public final class ProcessorUtils {\n"
            + "\n"
            + "  private static final Pattern WHITE_SPACE_REGEX =\n"
            + "      Pattern.compile(\"\\\\s+(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)\");\n"
            + "  private static final Pattern COMMA_PATTERN =\n"
            + "      Pattern.compile(\", (?=(?:[^\\\\\\\"]*\\\\\\\"[^\\\\\\\"]*\\\\\\\")*[^\\\\\\\"]*$)\");\n"
            + "  private static final Pattern PARENTHESIS_CONTENT = Pattern.compile(\"\\\\((.*?)\\\\)\");\n"
            + "\n"
            + "  private ProcessorUtils() {}\n"
            + "\n"
            + "  private static final Map BOX_MAP =\n"
            + "      Map.of(\n"
            + "          \"char\", \"Character\",\n"
            + "          \"byte\", \"Byte\",\n"
            + "          \"int\", \"Integer\",\n"
            + "          \"long\", \"Long\",\n"
            + "          \"short\", \"Short\",\n"
            + "          \"double\", \"Double\",\n"
            + "          \"float\", \"Float\",\n"
            + "          \"boolean\", \"Boolean\");\n"
            + "\n"
            + "  /**\n"
            + "   * Returns boxed type if type string is primitive, otherwise return the input unchanged\n"
            + "   *\n"
            + "   * @param type type string\n"
            + "   * @return boxed type string if type is primitive\n"
            + "   */\n"
            + "  public static String boxedPrimitive(String type) {\n"
            + "    final var wrapped = BOX_MAP.get(type);\n"
            + "    return wrapped != null ? \"java.lang.\" + wrapped : null;\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Return true if type string is of a primitive type\n"
            + "   *\n"
            + "   * @param type\n"
            + "   * @return true if type represents a primitive\n"
            + "   */\n"
            + "  public static boolean isPrimitive(String type) {\n"
            + "    return BOX_MAP.containsKey(type);\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Get Package from a given fqn string\n"
            + "   *\n"
            + "   * @param fqn the fully qualified type string\n"
            + "   * @return the package of the type\n"
            + "   */\n"
            + "  public static String packageOf(String fqn) {\n"
            + "\n"
            + "    return fqn.replace(\".\" + shortType(fqn), \"\");\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Get short type from a given fqn string. Nested Classes will have parent classes as part of the\n"
            + "   * short name\n"
            + "   *\n"
            + "   * @param fqn the fully qualified type string\n"
            + "   * @return the short type\n"
            + "   */\n"
            + "  public static String shortType(String fqn) {\n"
            + "    final int p = fqn.lastIndexOf('.');\n"
            + "    if (p == -1) {\n"
            + "      return fqn;\n"
            + "    }\n"
            + "    var result = \"\";\n"
            + "    var foundClass = false;\n"
            + "    for (final String part : fqn.split(\"\\\\.\")) {\n"
            + "      char firstChar = part.charAt(0);\n"
            + "      if (foundClass\n"
            + "          || Character.isUpperCase(firstChar)\n"
            + "          || (!Character.isAlphabetic(firstChar) && Character.isJavaIdentifierStart(firstChar))) {\n"
            + "        foundClass = true;\n"
            + "        result += (result.isEmpty() ? \"\" : \".\") + part;\n"
            + "      }\n"
            + "    }\n"
            + "    // when in doubt, do the basic thing\n"
            + "    if (result.isBlank()) {\n"
            + "      return fqn.substring(p + 1);\n"
            + "    }\n"
            + "    return result;\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Remove all annotations and their values from a string.\n"
            + "   *\n"
            + "   * @param input string to remove annotations from\n"
            + "   * @return input free of annotations\n"
            + "   */\n"
            + "  public static String trimAnnotations(String input) {\n"
            + "    input = COMMA_PATTERN.matcher(input).replaceAll(\",\");\n"
            + "    return cutAnnotations(input);\n"
            + "  }\n"
            + "\n"
            + "  private static String cutAnnotations(String input) {\n"
            + "    final int pos = input.indexOf(\"@\");\n"
            + "    if (pos == -1) {\n"
            + "      return input;\n"
            + "    }\n"
            + "\n"
            + "    final Matcher matcher = WHITE_SPACE_REGEX.matcher(input);\n"
            + "\n"
            + "    int currentIndex = 0;\n"
            + "    if (matcher.find()) {\n"
            + "      currentIndex = matcher.start();\n"
            + "    }\n"
            + "    final var result = input.substring(0, pos) + input.substring(currentIndex + 1);\n"
            + "    return cutAnnotations(result);\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Return the common parent package between two classes/packages.\n"
            + "   *\n"
            + "   * @param firstPkg first class/package string\n"
            + "   * @param secondPkg second class/package string\n"
            + "   * @return the common package between the two classes\n"
            + "   */\n"
            + "  public static String commonParent(String firstPkg, String secondPkg) {\n"
            + "    if (secondPkg == null) return firstPkg;\n"
            + "    if (firstPkg == null) return packageOf(secondPkg);\n"
            + "    if (secondPkg.startsWith(firstPkg)) {\n"
            + "      return firstPkg;\n"
            + "    }\n"
            + "    int next;\n"
            + "    do {\n"
            + "      next = firstPkg.lastIndexOf('.');\n"
            + "      if (next > -1) {\n"
            + "        firstPkg = firstPkg.substring(0, next);\n"
            + "        if (secondPkg.startsWith(firstPkg)) {\n"
            + "          return firstPkg;\n"
            + "        }\n"
            + "      }\n"
            + "    } while (next > -1);\n"
            + "\n"
            + "    return firstPkg;\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Determine if a VariableElement is a varargs parameter\n"
            + "   *\n"
            + "   * @param element the parameter element\n"
            + "   * @param position the position of the parameter in the signature\n"
            + "   * @return true if element is a varargs parameter, false otherwise\n"
            + "   */\n"
            + "  public static boolean isVarArg(VariableElement element, int position) {\n"
            + "    final var methodString = trimAnnotations(element.getEnclosingElement().toString());\n"
            + "    final var typeString = trimAnnotations(element.asType().toString()).replace(\"[]\", \"\");\n"
            + "    final Matcher matcher = PARENTHESIS_CONTENT.matcher(methodString);\n"
            + "\n"
            + "    if (matcher.find()) {\n"
            + "      final var param = matcher.group(1).split(\",\")[position];\n"
            + "\n"
            + "      return param.replace(\"[]\", \"\").contains(typeString) && param.endsWith(\"...\");\n"
            + "    }\n"
            + "    return false;\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Check if element has an annotation with a simple name that matches the given short name\n"
            + "   *\n"
            + "   * @param element element to check\n"
            + "   * @param simpleName the simple name of the target annotation\n"
            + "   * @return true if a matching annotation is present\n"
            + "   */\n"
            + "  public static boolean hasAnnotationWithName(Element element, String simpleName) {\n"
            + "    for (final var mirror : element.getAnnotationMirrors()) {\n"
            + "      if (simpleName.equals(mirror.getAnnotationType().asElement().getSimpleName().toString())) {\n"
            + "        return true;\n"
            + "      }\n"
            + "    }\n"
            + "    return false;\n"
            + "  }\n"
            + "\n"
            + "  /**\n"
            + "   * Sanitize an import string to remove invalid characters\n"
            + "   *\n"
            + "   * @param input input to sanitize\n"
            + "   * @return sanitized import statement\n"
            + "   */\n"
            + "  public static String sanitizeImports(String input) {\n"
            + "    final int pos = input.indexOf(\"@\");\n"
            + "    if (pos == -1) {\n"
            + "      return removeInvalidChars(input);\n"
            + "    }\n"
            + "    final var start = pos == 0 ? input.substring(0, pos) : \"\";\n"
            + "    return start + removeInvalidChars(input.substring(input.lastIndexOf(' ') + 1));\n"
            + "  }\n"
            + "\n"
            + "  private static String removeInvalidChars(String type) {\n"
            + "    return type.replaceAll(\"[^\\\\n\\\\r\\\\t $;\\\\w.]\", \"\");\n"
            + "  }\n"
            + "  /**\n"
            + "   * Get the enclosed type from a nested class, or return the type itself if not nested. (i.e.\n"
            + "   * {@code io.package.Top.Nested} will become {@code io.package.Top})\n"
            + "   *\n"
            + "   * @param fqn fully qualified type to extract\n"
            + "   * @return sanitized import statement\n"
            + "   */\n"
            + "  public static String extractEnclosingFQN(String fqn) {\n"
            + "    int p = fqn.lastIndexOf('.');\n"
            + "    if (p == -1) {\n"
            + "      return fqn;\n"
            + "    }\n"
            + "    final StringBuilder result = new StringBuilder();\n"
            + "    var foundClass = false;\n"
            + "    var firstClass = true;\n"
            + "    for (final String part : fqn.split(\"\\\\.\")) {\n"
            + "      if (Character.isUpperCase(part.charAt(0))) {\n"
            + "        foundClass = true;\n"
            + "      }\n"
            + "      result.append(foundClass && !firstClass ? \"/\" : \".\").append(part);\n"
            + "      if (foundClass) {\n"
            + "        firstClass = false;\n"
            + "      }\n"
            + "    }\n"
            + "    if (result.charAt(0) == '.') {\n"
            + "      result.deleteCharAt(0);\n"
            + "    }\n"
            + "    final var fullResult = result.toString();\n"
            + "\n"
            + "    p = fullResult.lastIndexOf('/');\n"
            + "    if (p == -1) {\n"
            + "      return fullResult;\n"
            + "    }\n"
            + "    return fullResult.substring(0, p);\n"
            + "  }\n"
            + "}\n");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy