diff --git a/src/java/apiview-java-processor/pom.xml b/src/java/apiview-java-processor/pom.xml index 9c8e17abe78..953b5f22319 100644 --- a/src/java/apiview-java-processor/pom.xml +++ b/src/java/apiview-java-processor/pom.xml @@ -18,15 +18,15 @@ - com.fasterxml.jackson.core - jackson-databind - 2.17.0 + com.azure + azure-json + 1.1.0 com.github.javaparser - javaparser-symbol-solver-core - 3.25.9 + javaparser-core + 3.25.10 @@ -50,14 +50,14 @@ org.mockito mockito-core - 4.0.0 + 4.11.0 test - - commons-lang - commons-lang - 2.6 - + + org.unbescape + unbescape + 1.1.6.RELEASE + @@ -66,7 +66,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.12.1 1.8 1.8 @@ -79,7 +79,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.1.1 + 3.3.0 jar-with-dependencies diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/Main.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/Main.java index d5eedab17b7..28dc2dcd135 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/Main.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/Main.java @@ -1,19 +1,22 @@ package com.azure.tools.apiview.processor; -import com.azure.tools.apiview.processor.analysers.JavaASTAnalyser; +import com.azure.json.JsonProviders; +import com.azure.json.JsonReader; +import com.azure.json.JsonWriter; import com.azure.tools.apiview.processor.analysers.Analyser; +import com.azure.tools.apiview.processor.analysers.JavaASTAnalyser; import com.azure.tools.apiview.processor.analysers.XMLASTAnalyser; -import com.azure.tools.apiview.processor.model.*; +import com.azure.tools.apiview.processor.model.APIListing; +import com.azure.tools.apiview.processor.model.ApiViewProperties; +import com.azure.tools.apiview.processor.model.Diagnostic; +import com.azure.tools.apiview.processor.model.DiagnosticKind; +import com.azure.tools.apiview.processor.model.LanguageVariant; +import com.azure.tools.apiview.processor.model.Token; import com.azure.tools.apiview.processor.model.maven.Pom; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.exc.InvalidFormatException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.net.URL; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -26,17 +29,8 @@ import java.util.stream.Stream; import static com.azure.tools.apiview.processor.model.TokenKind.LINE_ID_MARKER; -import static com.fasterxml.jackson.databind.MapperFeature.AUTO_DETECT_CREATORS; -import static com.fasterxml.jackson.databind.MapperFeature.AUTO_DETECT_FIELDS; -import static com.fasterxml.jackson.databind.MapperFeature.AUTO_DETECT_GETTERS; -import static com.fasterxml.jackson.databind.MapperFeature.AUTO_DETECT_IS_GETTERS; public class Main { - private static final ObjectWriter WRITER = new ObjectMapper() - .disable(AUTO_DETECT_CREATORS, AUTO_DETECT_FIELDS, AUTO_DETECT_GETTERS, AUTO_DETECT_IS_GETTERS) - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .writerWithDefaultPrettyPrinter(); - // expected argument order: // [inputFiles] public static void main(String[] args) throws IOException { @@ -133,7 +127,9 @@ private static void processFile(final File inputFile, final File outputFile) thr } // Write out to the filesystem - WRITER.writeValue(outputFile, apiListing); + try (JsonWriter jsonWriter = JsonProviders.createWriter(Files.newBufferedWriter(outputFile.toPath()))) { + apiListing.toJson(jsonWriter); + } } private static void processJavaSourcesJar(File inputFile, APIListing apiListing) throws IOException { @@ -168,25 +164,7 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing) // Read all files within the jar file so that we can create a list of files to analyse final List allFiles = new ArrayList<>(); try (FileSystem fs = FileSystems.newFileSystem(inputFile.toPath(), Main.class.getClassLoader())) { - - try { - // we eagerly load the apiview_properties.json file into an ApiViewProperties object, so that it can - // be used throughout the analysis process, as required - // the filename is [_]apiview_properties.json - String filename = (artifactId != null && !artifactId.isEmpty() ? (artifactId + "_") : "") + "apiview_properties.json"; - URL apiViewPropertiesFile = fs.getPath("/META-INF/" + filename).toUri().toURL(); - final ObjectMapper objectMapper = new ObjectMapper(); - ApiViewProperties properties = objectMapper.readValue(apiViewPropertiesFile, ApiViewProperties.class); - apiListing.setApiViewProperties(properties); - System.out.println(" Found apiview_properties.json file in jar file"); - System.out.println(" - Found " + properties.getCrossLanguageDefinitionIds().size() + " cross-language definition IDs"); - } catch (InvalidFormatException e) { - System.out.println(" ERROR: Unable to parse apiview_properties.json file in jar file"); - e.printStackTrace(); - } catch (Exception e) { - // this is fine, we just won't have any APIView properties to read in - System.out.println(" No apiview_properties.json file found in jar file - continuing..."); - } + tryParseApiViewProperties(fs, apiListing, artifactId); fs.getRootDirectories().forEach(root -> { try (Stream paths = Files.walk(root)) { @@ -205,6 +183,42 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing) } } + /** + * Attempts to process the {@code apiview_properties.json} file in the jar file, if it exists. + *

+ * If the file was found and successfully parsed as {@link ApiViewProperties}, it is set on the {@link APIListing} + * object. + * + * @param fs the {@link FileSystem} representing the jar file + * @param apiListing the {@link APIListing} object to set the {@link ApiViewProperties} on + * @param artifactId the artifact ID of the jar file + */ + private static void tryParseApiViewProperties(FileSystem fs, APIListing apiListing, String artifactId) { + // the filename is [_]apiview_properties.json + String artifactName = (artifactId != null && !artifactId.isEmpty()) ? (artifactId + "_") : ""; + String filePath = "/META-INF/" + artifactName + "apiview_properties.json"; + Path apiviewPropertiesPath = fs.getPath(filePath); + if (!Files.exists(apiviewPropertiesPath)) { + System.out.println(" No apiview_properties.json file found in jar file - continuing..."); + return; + } + + try { + // we eagerly load the apiview_properties.json file into an ApiViewProperties object, so that it can + // be used throughout the analysis process, as required + try (JsonReader reader = JsonProviders.createReader(Files.readAllBytes(apiviewPropertiesPath))) { + ApiViewProperties properties = ApiViewProperties.fromJson(reader); + apiListing.setApiViewProperties(properties); + System.out.println(" Found apiview_properties.json file in jar file"); + System.out.println(" - Found " + properties.getCrossLanguageDefinitionIds().size() + + " cross-language definition IDs"); + } + } catch (IOException e) { + System.out.println(" ERROR: Unable to parse apiview_properties.json file in jar file - continuing..."); + e.printStackTrace(); + } + } + private static void processXmlFile(File inputFile, APIListing apiListing) { final ReviewProperties reviewProperties = new ReviewProperties(); diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/Analyser.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/Analyser.java index 903a726eb73..2c01c8b501b 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/Analyser.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/Analyser.java @@ -1,8 +1,7 @@ package com.azure.tools.apiview.processor.analysers; - import java.nio.file.Path; -import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -25,6 +24,6 @@ public interface Analyser { void analyse(List allFiles); default void analyse(Path file) { - analyse(Arrays.asList(file)); + analyse(Collections.singletonList(file)); } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/JavaASTAnalyser.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/JavaASTAnalyser.java index 9ae94e720de..21bb153c477 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/JavaASTAnalyser.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/JavaASTAnalyser.java @@ -11,8 +11,9 @@ import com.azure.tools.apiview.processor.model.TypeKind; import com.azure.tools.apiview.processor.model.maven.Dependency; import com.azure.tools.apiview.processor.model.maven.Pom; +import com.github.javaparser.JavaParser; +import com.github.javaparser.JavaParserAdapter; import com.github.javaparser.ParserConfiguration; -import com.github.javaparser.StaticJavaParser; import com.github.javaparser.TokenRange; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.ImportDeclaration; @@ -48,27 +49,36 @@ import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.type.TypeParameter; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; +import org.unbescape.html.HtmlEscape; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang.StringEscapeUtils; - -import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.*; -import static com.azure.tools.apiview.processor.analysers.util.MiscUtils.*; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.attemptToFindJavadocComment; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.getNodeFullyQualifiedName; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.getPackageName; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.isInterfaceType; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.isPrivateOrPackagePrivate; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.isPublicOrProtected; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.isTypeAPublicAPI; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.makeId; +import static com.azure.tools.apiview.processor.analysers.util.MiscUtils.upperCase; import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.INDENT; import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.NEWLINE; import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.NOTHING; @@ -78,6 +88,8 @@ import static com.azure.tools.apiview.processor.model.TokenKind.DEPRECATED_RANGE_START; import static com.azure.tools.apiview.processor.model.TokenKind.DOCUMENTATION_RANGE_END; import static com.azure.tools.apiview.processor.model.TokenKind.DOCUMENTATION_RANGE_START; +import static com.azure.tools.apiview.processor.model.TokenKind.EXTERNAL_LINK_END; +import static com.azure.tools.apiview.processor.model.TokenKind.EXTERNAL_LINK_START; import static com.azure.tools.apiview.processor.model.TokenKind.KEYWORD; import static com.azure.tools.apiview.processor.model.TokenKind.MEMBER_NAME; import static com.azure.tools.apiview.processor.model.TokenKind.NEW_LINE; @@ -87,8 +99,6 @@ import static com.azure.tools.apiview.processor.model.TokenKind.TEXT; import static com.azure.tools.apiview.processor.model.TokenKind.TYPE_NAME; import static com.azure.tools.apiview.processor.model.TokenKind.WHITESPACE; -import static com.azure.tools.apiview.processor.model.TokenKind.EXTERNAL_LINK_START; -import static com.azure.tools.apiview.processor.model.TokenKind.EXTERNAL_LINK_END; public class JavaASTAnalyser implements Analyser { public static final String MAVEN_KEY = "Maven"; @@ -112,7 +122,19 @@ public class JavaASTAnalyser implements Analyser { ANNOTATION_RULE_MAP.put("Metadata", new AnnotationRule().setShowProperties(true).setCondensed(true)); } - private static final Pattern SPLIT_NEWLINE = Pattern.compile(MiscUtils.LINEBREAK); + private static final JavaParserAdapter JAVA_8_PARSER; + private static final JavaParserAdapter JAVA_11_PARSER; + + static { + // Configure JavaParser to use type resolution + JAVA_8_PARSER = JavaParserAdapter.of(new JavaParser(new ParserConfiguration() + .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8) + .setDetectOriginalLineSeparator(false))); + + JAVA_11_PARSER = JavaParserAdapter.of(new JavaParser(new ParserConfiguration() + .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_11) + .setDetectOriginalLineSeparator(false))); + } // This is the model that we build up as the AST of all files are analysed. The APIListing is then output as // JSON that can be understood by APIView. @@ -205,19 +227,11 @@ private Optional scanForTypes(Path path) { return Optional.of(new ScanClass(path, null)); } try { - // Set up a minimal type solver that only looks at the classes used to run this sample. - CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(); - combinedTypeSolver.add(new ReflectionTypeSolver(false)); + CompilationUnit compilationUnit = path.endsWith("module-info.java") + ? JAVA_11_PARSER.parse(Files.newBufferedReader(path)) + : JAVA_8_PARSER.parse(Files.newBufferedReader(path)); - ParserConfiguration parserConfiguration = new ParserConfiguration() - .setStoreTokens(true) - .setSymbolResolver(new JavaSymbolSolver(combinedTypeSolver)) - .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_11); - - // Configure JavaParser to use type resolution - StaticJavaParser.setConfiguration(parserConfiguration); - - CompilationUnit compilationUnit = StaticJavaParser.parse(path); + compilationUnit.setStorage(path, StandardCharsets.UTF_8); new ScanForClassTypeVisitor().visit(compilationUnit, null); if (path.endsWith("package-info.java")) { @@ -516,7 +530,7 @@ private void visitModuleDeclaration(ModuleDeclaration moduleDeclaration) { addToken(new Token(KEYWORD, "to"), SPACE); for (int i = 0; i < names.size(); i++) { - addToken(new Token(TYPE_NAME, names.get(i).toString())); + addToken(new Token(TYPE_NAME, names.get(i).asString())); if (i < names.size() - 1) { addToken(new Token(PUNCTUATION, ","), SPACE); @@ -571,7 +585,7 @@ private void visitModuleDeclaration(ModuleDeclaration moduleDeclaration) { NodeList names = d.getWith(); for (int i = 0; i < names.size(); i++) { - addToken(new Token(TYPE_NAME, names.get(i).toString())); + addToken(new Token(TYPE_NAME, names.get(i).asString())); if (i < names.size() - 1) { addToken(new Token(PUNCTUATION, ","), SPACE); @@ -827,7 +841,7 @@ private void tokeniseFields(boolean isInterfaceDeclaration, TypeDeclaration t final NodeList fieldModifiers = fieldDeclaration.getModifiers(); // public, protected, static, final for (final Modifier fieldModifier : fieldModifiers) { - addToken(new Token(KEYWORD, fieldModifier.toString())); + addToken(new Token(KEYWORD, fieldModifier.getKeyword().asString())); } // field type and name @@ -1163,7 +1177,7 @@ private void processAnnotationValueExpression(final Expression valueExpr, final private void getModifiers(NodeList modifiers) { for (final Modifier modifier : modifiers) { - addToken(new Token(KEYWORD, modifier.toString())); + addToken(new Token(KEYWORD, modifier.getKeyword().asString())); } } @@ -1251,7 +1265,7 @@ private void getThrowException(CallableDeclaration callableDeclaration) { addToken(new Token(KEYWORD, "throws"), SPACE); for (int i = 0, max = thrownExceptions.size(); i < max; i++) { - final String exceptionName = thrownExceptions.get(i).getElementType().toString(); + final String exceptionName = thrownExceptions.get(i).getElementType().asString(); final Token throwsToken = new Token(TYPE_NAME, exceptionName); // we look up the package name in case it is a custom type in the same library, @@ -1290,7 +1304,7 @@ private void getType(Object type) { private void getClassType(Type type) { if (type.isPrimitiveType()) { - addToken(new Token(TYPE_NAME, type.asPrimitiveType().toString())); + addToken(new Token(TYPE_NAME, type.asPrimitiveType().asString())); } else if (type.isVoidType()) { addToken(new Token(TYPE_NAME, "void")); } else if (type.isReferenceType()) { @@ -1417,7 +1431,7 @@ public void visit(CompilationUnit compilationUnit, Map arg) { compilationUnit.getImports().stream() .map(ImportDeclaration::getName) .forEach(name -> name.getQualifier().ifPresent(packageName -> - apiListing.addPackageTypeMapping(packageName.toString(), name.getIdentifier()))); + apiListing.addPackageTypeMapping(packageName.asString(), name.getIdentifier()))); } } @@ -1468,13 +1482,11 @@ private void visitJavaDoc(JavadocComment javadoc) { // The updated configuration from getDeclarationAsString removes the comment option and hence the toString // returns an empty string now. So, here we are using the toString overload that takes a PrintConfiguration // to get the old behavior. - String javaDocText = javadoc.toString(new DefaultPrinterConfiguration()); - Arrays.stream(SPLIT_NEWLINE.split(javaDocText)).forEach(line -> { + splitNewLine(javadoc.asString()).forEach(line -> { // we want to wrap our javadocs so that they are easier to read, so we wrap at 120 chars - final String wrappedString = MiscUtils.wrap(line, 120); - Arrays.stream(SPLIT_NEWLINE.split(wrappedString)).forEach(line2 -> { + MiscUtils.wrap(line, 120).forEach(line2 -> { if (line2.contains("&")) { - line2 = StringEscapeUtils.unescapeHtml(line2); + line2 = HtmlEscape.unescapeHtml(line2); } addToken(makeWhitespace()); @@ -1515,11 +1527,13 @@ private void unindent() { } private Token makeWhitespace() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < indent; i++) { - sb.append(" "); - } - return new Token(WHITESPACE, sb.toString()); + // Use a byte array with Arrays.fill with empty space (' ') character rather than StringBuilder as StringBuilder + // will check that it has sufficient size every time a new character is appended. We know ahead of time the size + // needed and can remove all those checks by removing usage of StringBuilder with this simpler pattern. + byte[] bytes = new byte[indent]; + Arrays.fill(bytes, (byte) ' '); + + return new Token(WHITESPACE, new String(bytes, StandardCharsets.UTF_8)); } private void addComment(String comment) { @@ -1559,4 +1573,12 @@ private void handleTokenModifier(TokenModifier modifier) { break; } } + + private static Stream splitNewLine(String input) { + if (input == null || input.isEmpty()) { + return Stream.empty(); + } + + return Stream.of(input.split("\n")); + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/XMLASTAnalyser.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/XMLASTAnalyser.java index 070e5d4cf13..ac33a082204 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/XMLASTAnalyser.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/XMLASTAnalyser.java @@ -8,11 +8,11 @@ import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.List; +import java.util.Objects; import static com.azure.tools.apiview.processor.model.TokenKind.COMMENT; import static com.azure.tools.apiview.processor.model.TokenKind.KEYWORD; @@ -51,7 +51,7 @@ private void processFile(Path file) { XMLStreamReader reader = null; try { - reader = factory.createXMLStreamReader(new FileInputStream(file.toFile())); + reader = factory.createXMLStreamReader(Files.newBufferedReader(file)); while (reader.hasNext()) { final int eventType = reader.next(); @@ -126,7 +126,7 @@ private void processFile(Path file) { } } } - } catch (XMLStreamException | FileNotFoundException e) { + } catch (XMLStreamException | IOException e) { e.printStackTrace(); } finally { if (reader != null) { @@ -174,7 +174,7 @@ private void startElement(final XMLStreamReader reader) { if (attributeCount > 0) { for (int i = 0; i < attributeCount; i++) { final String prefix = reader.getAttributePrefix(i); - final String key = (prefix == "" ? "" : prefix + ":") + reader.getAttributeLocalName(i); + final String key = (Objects.equals(prefix, "") ? "" : prefix + ":") + reader.getAttributeLocalName(i); final String value = reader.getAttributeValue(i); addAttribute(key, value); @@ -256,4 +256,4 @@ private void addToken(Token token) { apiListing.getTokens().add(token); lastToken = token; } -} \ No newline at end of file +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/ASTUtils.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/ASTUtils.java index aea307a1fd4..5f979ed8c94 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/ASTUtils.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/ASTUtils.java @@ -25,14 +25,12 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.regex.Pattern; import java.util.stream.Stream; /** * Abstract syntax tree (AST) utility methods. */ public final class ASTUtils { - private static final Pattern MAKE_ID = Pattern.compile("\"| "); /** * Attempts to get the package name for a compilation unit. @@ -118,7 +116,9 @@ public static Stream getPublicOrProtectedConstructors(Co * @return All public API constructors contained in the type declaration. */ public static Stream getPublicOrProtectedConstructors(TypeDeclaration typeDeclaration) { - return typeDeclaration.getConstructors().stream() + return typeDeclaration.getMembers().stream() + .filter(member -> member instanceof ConstructorDeclaration) + .map(member -> (ConstructorDeclaration) member) .filter(type -> isPublicOrProtected(type.getAccessSpecifier())); } @@ -139,7 +139,9 @@ public static Stream getPublicOrProtectedMethods(CompilationU * @return All public API methods contained in the type declaration. */ public static Stream getPublicOrProtectedMethods(TypeDeclaration typeDeclaration) { - return typeDeclaration.getMethods().stream() + return typeDeclaration.getMembers().stream() + .filter(member -> member instanceof MethodDeclaration) + .map(member -> (MethodDeclaration) member) .filter(type -> isPublicOrProtected(type.getAccessSpecifier())); } @@ -160,7 +162,9 @@ public static Stream getPublicOrProtectedFields(CompilationUni * @return All public API fields contained in the type declaration. */ public static Stream getPublicOrProtectedFields(TypeDeclaration typeDeclaration) { - return typeDeclaration.getFields().stream() + return typeDeclaration.getMembers().stream() + .filter(member -> member instanceof FieldDeclaration) + .map(member -> (FieldDeclaration) member) .filter(type -> isPublicOrProtected(type.getAccessSpecifier())); } @@ -209,7 +213,44 @@ public static String makeId(EnumConstantDeclaration enumDeclaration) { } public static String makeId(String fullPath) { - return MAKE_ID.matcher(fullPath).replaceAll("-"); + // Previously, this used a regex to replace '"' and ' ' with '-', that wasn't necessary. The replacement pattern + // is simple and can be replaced with a simple loop. This is a performance optimization. + // + // The logic is that we iterate over the string, if no replacements are needed we return the original string. + // Otherwise, we create a new StringBuilder the size of the string being replaced, as the replacement size is + // the same as the search size, and we append the parts of the string that don't need to be replaced, and the + // replacement character for the parts that do. At the end of the loop, we append the last part of the string + // that doesn't need to be replaced and return the final string. + if (fullPath == null || fullPath.isEmpty()) { + return fullPath; + } + + StringBuilder sb = null; + int prevStart = 0; + + int length = fullPath.length(); + for (int i = 0; i < length; i++) { + char c = fullPath.charAt(i); + if (c == '"' || c == ' ') { + if (sb == null) { + sb = new StringBuilder(length); + } + + if (prevStart != i) { + sb.append(fullPath, prevStart, i); + } + + sb.append('-'); + prevStart = i + 1; + } + } + + if (sb == null) { + return fullPath; + } + + sb.append(fullPath, prevStart, length); + return sb.toString(); } public static String makeId(AnnotationExpr annotation, NodeWithAnnotations nodeWithAnnotations) { diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/MiscUtils.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/MiscUtils.java index 85ba4a865e2..6bdd018e527 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/MiscUtils.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/MiscUtils.java @@ -2,6 +2,8 @@ import com.azure.tools.apiview.processor.model.Token; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Pattern; import static com.azure.tools.apiview.processor.model.TokenKind.TEXT; @@ -10,33 +12,17 @@ * Miscellaneous utility methods. */ public final class MiscUtils { + public static final Pattern URL_MATCH = Pattern.compile( + "https?://(www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)"); - public static final String LINEBREAK = "\n"; // or "\r\n"; - private static final Pattern QUOTED_LINEBREAK = Pattern.compile(Pattern.quote(LINEBREAK)); - - public static final Pattern URL_MATCH = Pattern.compile("https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)"); - - public static String escapeHTML(final String s) { - final StringBuilder out = new StringBuilder(Math.max(16, s.length())); - for (int i = 0; i < s.length(); i++) { - final char c = s.charAt(i); - if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') { - out.append("&#"); - out.append((int) c); - out.append(';'); - } else { - out.append(c); - } - } - return out.toString(); - } + public static List wrap(String string, int lineLength) { + List wrappedLines = new ArrayList<>(); - public static String wrap(final String string, final int lineLength) { - final StringBuilder b = new StringBuilder(); - for (final String line : QUOTED_LINEBREAK.split(string)) { - b.append(wrapLine(line, lineLength)); + for (String line : string.split("\n")) { + wrapLine(line, lineLength, wrappedLines); } - return b.toString(); + + return wrappedLines; } /** @@ -63,28 +49,30 @@ public static Token tokeniseKeyValue(String key, Object value, String prefix) { return new Token(TEXT, value == null ? "" : value.toString(), prefix + key + "-" + value); } - private static String wrapLine(final String line, final int lineLength) { - if (line.isEmpty()) return LINEBREAK; - if (line.length() <= lineLength) return line + LINEBREAK; + private static void wrapLine(String line, int lineLength, List collector) { + if (line.isEmpty()) { + return; + } + + if (line.length() <= lineLength) { + collector.add(line); + return; + } final String[] words = line.split(" "); - final StringBuilder allLines = new StringBuilder(); StringBuilder trimmedLine = new StringBuilder(); for (final String word : words) { if (trimmedLine.length() + 1 + word.length() > lineLength) { - allLines.append(trimmedLine).append(LINEBREAK); + collector.add(trimmedLine.toString()); trimmedLine = new StringBuilder(); } trimmedLine.append(word).append(" "); } if (trimmedLine.length() > 0) { - allLines.append(trimmedLine); + collector.add(trimmedLine.toString()); } - - allLines.append(LINEBREAK); - return allLines.toString(); } /** diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java index 653b8effa90..d1e3b952ff8 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java @@ -11,9 +11,7 @@ import java.util.Comparator; import java.util.HashSet; -import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.makeId; @@ -41,27 +39,32 @@ public void scanFinal(APIListing listing) { .min(Comparator.comparingInt(String::length)) .orElse(""); - // Check for the presence of module-info - Optional moduleInfoToken = listing.getTokens().stream() - .filter(token -> token.getKind().equals(TokenKind.TYPE_NAME)) - .filter(token -> token.getDefinitionId() != null && token.getDefinitionId().equals(JavaASTAnalyser.MODULE_INFO_KEY)) - .findFirst(); + Token moduleInfoToken = null; + Set exportsPackages = new HashSet<>(); - // Collect all packages that are exported - Set exportsPackages = listing.getTokens().stream() - .filter(token -> token.getKind().equals(TokenKind.TYPE_NAME)) - .filter(token -> token.getDefinitionId() != null && token.getDefinitionId().startsWith("module-info" + - "-exports")) - .map(token -> token.getValue()) - .collect(Collectors.toSet()); + for (Token token : listing.getTokens()) { + if (!TokenKind.TYPE_NAME.equals(token.getKind()) || token.getDefinitionId() == null) { + continue; + } + + // Check for the presence of module-info + if (token.getDefinitionId().equals(JavaASTAnalyser.MODULE_INFO_KEY) && moduleInfoToken == null) { + moduleInfoToken = token; + } + + // Collect all packages that are exported + if (token.getDefinitionId().startsWith("module-info-exports")) { + exportsPackages.add(token.getValue()); + } + } - if (!moduleInfoToken.isPresent()) { + if (moduleInfoToken == null) { listing.addDiagnostic(new Diagnostic(DiagnosticKind.WARNING, makeId(basePackageName), "This module is missing module-info.java")); return; } - String moduleName = moduleInfoToken.get().getValue(); + String moduleName = moduleInfoToken.getValue(); if (moduleName != null) { // special casing azure-core as the base package doesn't have any classes and hence not included in the // list of packages diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java index 1a2c0b380b5..4415b6458b8 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java @@ -52,7 +52,7 @@ public void scanIndividual(final CompilationUnit cu, final APIListing listing) { void checkJavaDoc(NodeWithJavadoc n, String id, APIListing listing) { n.getJavadocComment().ifPresent(javadoc -> { - final String javadocString = javadoc.toString().toLowerCase(); + final String javadocString = javadoc.asString().toLowerCase(); Matcher matcher = LANGUAGE_PATTERN.matcher(javadocString); while (matcher.find()) { diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java index cdf9950f49c..9884d57729c 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java @@ -34,7 +34,7 @@ private void processFluentType(final TypeDeclaration type, final APIListing l getPublicOrProtectedMethods(type) .filter(method -> method.getNameAsString().startsWith("set")) .forEach(method -> { - if (!method.getType().toString().equals(typeName)) { + if (!method.getType().asString().equals(typeName)) { listing.addDiagnostic(new Diagnostic( ERROR, makeId(method), diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/APIListing.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/APIListing.java index ca9606aa481..9840081d12b 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/APIListing.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/APIListing.java @@ -1,62 +1,39 @@ package com.azure.tools.apiview.processor.model; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; import com.azure.tools.apiview.processor.model.maven.Pom; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; -public class APIListing { - @JsonProperty("Navigation") +public class APIListing implements JsonSerializable { private List navigation; - - @JsonIgnore private ChildItem rootNav; - - @JsonProperty("Name") private String name; - - @JsonProperty("Language") private String language; - - @JsonProperty("LanguageVariant") private LanguageVariant languageVariant; - - @JsonProperty("PackageName") private String packageName; - - @JsonProperty("PackageVersion") private String packageVersion; // This string is taken from here: // https://github.com/Azure/azure-sdk-tools/blob/main/src/dotnet/APIView/APIView/Languages/CodeFileBuilder.cs#L50 - @JsonProperty("VersionString") private final String versionString = "21"; - - @JsonProperty("Tokens") private List tokens; - - @JsonProperty("Diagnostics") private List diagnostics; - - @JsonIgnore private Map knownTypes; // a map of package names to a list of types within that package - @JsonIgnore private final Map> packageNamesToTypesMap; - - @JsonIgnore private final Map typeToPackageNameMap; - - @JsonIgnore private Pom mavenPom; - - @JsonIgnore private ApiViewProperties apiViewProperties; public APIListing() { @@ -132,7 +109,7 @@ public void setTokens(List tokens) { @Override public String toString() { - return "APIListing [rootNav = "+rootNav+", Name = "+ name +", Tokens = "+tokens+"]"; + return "APIListing [rootNav = " + rootNav + ", Name = " + name + ", Tokens = " + tokens + "]"; } /** @@ -170,4 +147,52 @@ public void setApiViewProperties(ApiViewProperties properties) { public ApiViewProperties getApiViewProperties() { return apiViewProperties; } -} \ No newline at end of file + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + return jsonWriter.writeStartObject() + .writeArrayField("Navigation", navigation, JsonWriter::writeJson) + .writeStringField("Name", name) + .writeStringField("Language", language) + .writeStringField("LanguageVariant", Objects.toString(languageVariant, null)) + .writeStringField("PackageName", packageName) + .writeStringField("PackageVersion", packageVersion) + .writeStringField("VersionString", versionString) + .writeArrayField("Tokens", tokens, JsonWriter::writeJson) + .writeArrayField("Diagnostics", diagnostics, JsonWriter::writeJson) + .writeEndObject(); + } + + public static APIListing fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + APIListing apiListing = new APIListing(); + + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if ("Navigation".equals(fieldName)) { + apiListing.navigation = reader.readArray(ChildItem::fromJson); + } else if ("Name".equals(fieldName)) { + apiListing.name = reader.getString(); + } else if ("Language".equals(fieldName)) { + apiListing.language = reader.getString(); + } else if ("LanguageVariant".equals(fieldName)) { + apiListing.languageVariant = LanguageVariant.fromString(reader.getString()); + } else if ("PackageName".equals(fieldName)) { + apiListing.packageName = reader.getString(); + } else if ("PackageVersion".equals(fieldName)) { + apiListing.packageVersion = reader.getString(); + } else if ("Tokens".equals(fieldName)) { + apiListing.tokens = reader.readArray(Token::fromJson); + } else if ("Diagnostics".equals(fieldName)) { + apiListing.diagnostics = reader.readArray(Diagnostic::fromJson); + } else { + reader.skipChildren(); + } + } + + return apiListing; + }); + } +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ApiViewProperties.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ApiViewProperties.java index 2781c90adda..e7f4fef61ee 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ApiViewProperties.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ApiViewProperties.java @@ -1,7 +1,11 @@ package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -11,14 +15,11 @@ * Sometimes libraries carry additional metadata with them that can make the output from APIView more useful. This * class is used to store that metadata, as it is deserialized from the /META-INF/apiview_properties.json file. */ -public class ApiViewProperties { - - @JsonProperty("flavor") +public class ApiViewProperties implements JsonSerializable { private Flavor flavor; // This is a map of model names and methods to their TypeSpec definition IDs. - @JsonProperty("CrossLanguageDefinitionId") - private final Map crossLanguageDefinitionIds = new HashMap<>(); + private Map crossLanguageDefinitionIds = new HashMap<>(); /** * Cross Languages Definition ID is used to map from a model name or a method name to a TypeSpec definition ID. This @@ -39,4 +40,37 @@ public Map getCrossLanguageDefinitionIds() { public Flavor getFlavor() { return flavor; } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject() + .writeMapField("CrossLanguageDefinitionId", crossLanguageDefinitionIds, JsonWriter::writeString); + + if (flavor != null) { + jsonWriter.writeStringField("flavor", flavor.getSerializationValue()); + } + + return jsonWriter.writeEndObject(); + } + + public static ApiViewProperties fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + ApiViewProperties properties = new ApiViewProperties(); + + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if ("CrossLanguageDefinitionId".equals(fieldName)) { + properties.crossLanguageDefinitionIds = reader.readMap(JsonReader::getString); + } else if ("flavor".equals(fieldName)) { + properties.flavor = Flavor.fromSerializationValue(reader.getString()); + } else { + reader.skipChildren(); + } + } + + return properties; + }); + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ChildItem.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ChildItem.java index 37f882eb600..825413d7944 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ChildItem.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/ChildItem.java @@ -1,29 +1,24 @@ package com.azure.tools.apiview.processor.model; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; import com.azure.tools.apiview.processor.analysers.JavaASTAnalyser; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.IOException; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; -public class ChildItem implements Comparable { - @JsonProperty("ChildItems") +public class ChildItem implements Comparable, JsonSerializable { private Set childItems; - - @JsonIgnore private Map packageNameToChildMap; - - @JsonProperty("NavigationId") private String navigationId; - - @JsonProperty("Text") private String text; - - @JsonProperty("Tags") private Tags tags; public ChildItem(final String text, TypeKind typeKind) { @@ -111,4 +106,39 @@ public boolean equals(final Object o) { public int hashCode() { return Objects.hash(text); } -} \ No newline at end of file + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + return jsonWriter.writeStartObject() + .writeArrayField("ChildItems", childItems, JsonWriter::writeJson) + .writeStringField("NavigationId", navigationId) + .writeStringField("Text", text) + .writeJsonField("Tags", tags) + .writeEndObject(); + } + + public static ChildItem fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + ChildItem childItem = new ChildItem(null, null, null); + + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if ("ChildItems".equals(fieldName)) { + childItem.childItems = new LinkedHashSet<>(reader.readArray(ChildItem::fromJson)); + } else if ("NavigationId".equals(fieldName)) { + childItem.navigationId = reader.getString(); + } else if ("Text".equals(fieldName)) { + childItem.text = reader.getString(); + } else if ("Tags".equals(fieldName)) { + childItem.tags = Tags.fromJson(reader); + } else { + reader.skipChildren(); + } + } + + return childItem; + }); + } +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Diagnostic.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Diagnostic.java index 1efddb63e0b..aa40c5e84d7 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Diagnostic.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Diagnostic.java @@ -1,23 +1,18 @@ package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonWriter; -public class Diagnostic { +import java.io.IOException; + +public class Diagnostic implements JsonSerializable { private static int diagnosticIdCounter = 1; - @JsonProperty("DiagnosticId") private String diagnosticId; - - @JsonProperty("Text") private String text; - - @JsonProperty("HelpLinkUri") private String helpLinkUri; - - @JsonProperty("TargetId") private String targetId; - - @JsonProperty("Level") private DiagnosticKind level; public Diagnostic(DiagnosticKind level, String targetId, String text) { @@ -39,4 +34,53 @@ public String getText() { public String getHelpLinkUri() { return helpLinkUri; } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject() + .writeStringField("DiagnosticId", diagnosticId) + .writeStringField("Text", text) + .writeStringField("HelpLinkUri", helpLinkUri) + .writeStringField("TargetId", targetId); + + if (level != null) { + jsonWriter.writeIntField("Level", level.getLevel()); + } + + return jsonWriter.writeEndObject(); + } + + public static Diagnostic fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + String diagnosticId = null; + String text = null; + String helpLinkUri = null; + String targetId = null; + DiagnosticKind level = null; + + while (reader.nextToken() != null) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if ("DiagnosticId".equals(fieldName)) { + diagnosticId = reader.getString(); + } else if ("Text".equals(fieldName)) { + text = reader.getString(); + } else if ("HelpLinkUri".equals(fieldName)) { + helpLinkUri = reader.getString(); + } else if ("TargetId".equals(fieldName)) { + targetId = reader.getString(); + } else if ("Level".equals(fieldName)) { + level = DiagnosticKind.fromInt(reader.getInt()); + } else { + reader.skipChildren(); + } + } + + Diagnostic diagnostic = new Diagnostic(level, targetId, text, helpLinkUri); + diagnostic.diagnosticId = diagnosticId; + + return diagnostic; + }); + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/DiagnosticKind.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/DiagnosticKind.java index 5e34bcd39a9..f8afa6c61ac 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/DiagnosticKind.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/DiagnosticKind.java @@ -1,6 +1,5 @@ -package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonValue; +package com.azure.tools.apiview.processor.model; public enum DiagnosticKind { ERROR(3), // red @@ -13,7 +12,19 @@ public enum DiagnosticKind { this.level = level; } - @JsonValue + public static DiagnosticKind fromInt(int level) { + switch (level) { + case 3: + return ERROR; + case 2: + return WARNING; + case 1: + return INFO; + default: + return null; + } + } + public int getLevel() { return level; } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Flavor.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Flavor.java index 9563c4c5e8d..5bd8ebca95a 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Flavor.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Flavor.java @@ -1,27 +1,37 @@ package com.azure.tools.apiview.processor.model; import com.azure.tools.apiview.processor.model.maven.Dependency; -import com.fasterxml.jackson.annotation.JsonProperty; public enum Flavor { - @JsonProperty("azure") - AZURE("com.azure"), - - @JsonProperty("generic") - GENERIC("io.clientcore"), - - UNKNOWN(null); + AZURE("com.azure", "azure"), + GENERIC("io.clientcore", "generic"), + UNKNOWN(null, "unknown"); private final String packagePrefix; + private final String serializationValue; - private Flavor(String packagePrefix) { + Flavor(String packagePrefix, String serializationValue) { this.packagePrefix = packagePrefix; + this.serializationValue = serializationValue; } public String getPackagePrefix() { return packagePrefix; } + public String getSerializationValue() { + return serializationValue; + } + + public static Flavor fromSerializationValue(String serializationValue) { + for (Flavor flavor : values()) { + if (flavor.getSerializationValue().equals(serializationValue)) { + return flavor; + } + } + return UNKNOWN; + } + public static Flavor getFlavor(APIListing apiListing) { Flavor flavor = apiListing.getApiViewProperties().getFlavor(); if (flavor != null) { @@ -61,6 +71,4 @@ public static Flavor getFlavor(APIListing apiListing) { // see which count is greatest (and non-zero), and return that flavour. If equal, return unknown return azureCount > genericCount ? AZURE : genericCount > azureCount ? GENERIC : UNKNOWN; } - - -} \ No newline at end of file +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/LanguageVariant.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/LanguageVariant.java index 329e2e82716..65d077740f7 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/LanguageVariant.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/LanguageVariant.java @@ -1,6 +1,5 @@ -package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonValue; +package com.azure.tools.apiview.processor.model; public enum LanguageVariant { DEFAULT("Default"), @@ -14,8 +13,16 @@ public enum LanguageVariant { } @Override - @JsonValue public String toString() { return this.variantName; } + + public static LanguageVariant fromString(String name) { + for (LanguageVariant variant : LanguageVariant.values()) { + if (variant.toString().equals(name)) { + return variant; + } + } + return null; + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Tags.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Tags.java index a009a1e7faa..038d2534c23 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Tags.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Tags.java @@ -1,9 +1,13 @@ package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; -public class Tags { - @JsonProperty("TypeKind") +import java.io.IOException; + +public class Tags implements JsonSerializable { private TypeKind typeKind; public Tags(TypeKind typeKind) { @@ -22,4 +26,33 @@ public void setTypeKind(TypeKind TypeKind) { public String toString() { return "Tags [typeKind = "+ typeKind +"]"; } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + + if (typeKind != null) { + jsonWriter.writeStringField("TypeKind", typeKind.getName()); + } + + return jsonWriter.writeEndObject(); + } + + public static Tags fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + TypeKind typeKind = null; + + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if (fieldName.equals("TypeKind")) { + typeKind = TypeKind.fromName(reader.getString()); + } else { + reader.skipChildren(); + } + } + return new Tags(typeKind); + }); + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Token.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Token.java index e51ca69db93..2d74e985b9d 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Token.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Token.java @@ -1,21 +1,17 @@ + package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonWriter; -public class Token { - @JsonProperty("DefinitionId") - private String definitionId; +import java.io.IOException; - @JsonProperty("NavigateToId") +public class Token implements JsonSerializable { + private String definitionId; private String navigateToId; - - @JsonProperty("Kind") private TokenKind kind; - - @JsonProperty("Value") private String value; - - @JsonProperty("CrossLanguageDefinitionId") private String crossLanguageDefinitionId; public Token(final TokenKind kind) { @@ -80,4 +76,54 @@ public void setValue(String Value) { public String toString() { return "Token [definitionId = "+definitionId+", navigateToId = "+navigateToId+", kind = "+kind+", value = "+value+"]"; } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject() + .writeStringField("DefinitionId", definitionId) + .writeStringField("NavigateToId", navigateToId); + + if (kind != null) { + jsonWriter.writeIntField("Kind", kind.getId()); + } + + return jsonWriter.writeStringField("Value", value) + .writeStringField("CrossLanguageDefinitionId", crossLanguageDefinitionId) + .writeEndObject(); + } + + public static Token fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + String definitionId = null; + String navigateToId = null; + TokenKind kind = null; + String value = null; + String crossLanguageDefinitionId = null; + + while (reader.nextToken() != null) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if (fieldName.equals("DefinitionId")) { + definitionId = reader.getString(); + } else if (fieldName.equals("NavigateToId")) { + navigateToId = reader.getString(); + } else if (fieldName.equals("Kind")) { + kind = TokenKind.fromId(reader.getInt()); + } else if (fieldName.equals("Value")) { + value = reader.getString(); + } else if (fieldName.equals("CrossLanguageDefinitionId")) { + crossLanguageDefinitionId = reader.getString(); + } else { + reader.skipChildren(); + } + } + + Token token = new Token(kind, value, definitionId); + token.setNavigateToId(navigateToId); + token.setCrossLanguageDefinitionId(crossLanguageDefinitionId); + + return token; + }); + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TokenKind.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TokenKind.java index 686c7a2d88c..493aba9c39f 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TokenKind.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TokenKind.java @@ -1,6 +1,5 @@ -package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonValue; +package com.azure.tools.apiview.processor.model; public enum TokenKind { TEXT(0), @@ -36,14 +35,31 @@ public enum TokenKind { EXTERNAL_LINK_START(28), EXTERNAL_LINK_END(29); + private static final TokenKind[] VALUES; + + static { + VALUES = new TokenKind[30]; + for (TokenKind kind : TokenKind.values()) { + VALUES[kind.id] = kind; + } + } + private final int id; TokenKind(int id) { this.id = id; } - @JsonValue public int getId() { return id; } + + public static TokenKind fromId(int id) { + for (TokenKind kind : TokenKind.values()) { + if (kind.id == id) { + return kind; + } + } + return null; + } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TypeKind.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TypeKind.java index c26ee1b1412..a2921937310 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TypeKind.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/TypeKind.java @@ -1,7 +1,5 @@ package com.azure.tools.apiview.processor.model; -import com.fasterxml.jackson.annotation.JsonValue; - public enum TypeKind { ASSEMBLY("assembly"), // i.e. a Jar File NAMESPACE("namespace"), // i.e. a Java package @@ -20,7 +18,6 @@ public enum TypeKind { this.name = name; } - @JsonValue public String getName() { return name; } @@ -34,4 +31,13 @@ public static TypeKind fromClass(Class cls) { return CLASS; } } + + public static TypeKind fromName(String name) { + for (TypeKind typeKind : TypeKind.values()) { + if (typeKind.getName().equals(name)) { + return typeKind; + } + } + return null; + } }