diff --git a/eng/pipelines/templates/jobs/azuresdkpartnerdrops-to-nugetfeed.yml b/eng/pipelines/templates/jobs/azuresdkpartnerdrops-to-nugetfeed.yml index fff2833ab70..bebd3e22c02 100644 --- a/eng/pipelines/templates/jobs/azuresdkpartnerdrops-to-nugetfeed.yml +++ b/eng/pipelines/templates/jobs/azuresdkpartnerdrops-to-nugetfeed.yml @@ -4,7 +4,7 @@ resources: - repository: azure-sdk-build-tools type: git name: internal/azure-sdk-build-tools - ref: refs/tags/azure-sdk-build-tools_20240320.1 + ref: refs/tags/azure-sdk-build-tools_20240409.1 parameters: - name: BuildToolsRepoPath diff --git a/eng/pipelines/templates/stages/1es-redirect.yml b/eng/pipelines/templates/stages/1es-redirect.yml index 172e5e8f10b..1d36278fc6f 100644 --- a/eng/pipelines/templates/stages/1es-redirect.yml +++ b/eng/pipelines/templates/stages/1es-redirect.yml @@ -7,7 +7,7 @@ resources: - repository: azure-sdk-build-tools type: git name: internal/azure-sdk-build-tools - ref: refs/heads/remove-optional-publish + ref: refs/tags/azure-sdk-build-tools_20240409.1 parameters: - name: stages diff --git a/src/dotnet/APIView/APIViewWeb/Managers/ReviewManager.cs b/src/dotnet/APIView/APIViewWeb/Managers/ReviewManager.cs index 3ee528afc9c..f813bd7b000 100644 --- a/src/dotnet/APIView/APIViewWeb/Managers/ReviewManager.cs +++ b/src/dotnet/APIView/APIViewWeb/Managers/ReviewManager.cs @@ -134,6 +134,10 @@ public async Task GetReviewAsync(ClaimsPrincipal user, stri /// public async Task> GetReviewsAsync(IEnumerable reviewIds, bool? isClosed = null) { + if (reviewIds == null || !reviewIds.Any()) + { + return new List(); + } return await _reviewsRepository.GetReviewsAsync(reviewIds, isClosed); } diff --git a/src/java/apiview-java-processor/pom.xml b/src/java/apiview-java-processor/pom.xml index c2b42637b99..9c8e17abe78 100644 --- a/src/java/apiview-java-processor/pom.xml +++ b/src/java/apiview-java-processor/pom.xml @@ -20,13 +20,13 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4.2 + 2.17.0 com.github.javaparser javaparser-symbol-solver-core - 3.24.2 + 3.25.9 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 dc7b7a53bbb..babec810213 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 @@ -8,6 +8,7 @@ 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; @@ -164,8 +165,6 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing) } System.out.println(" Using '" + apiListing.getLanguageVariant() + "' for the language variant"); - final Analyser analyser = new JavaASTAnalyser(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())) { @@ -179,6 +178,9 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing) 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..."); @@ -194,6 +196,7 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing) }); // Do the analysis while the filesystem is still represented in memory + final Analyser analyser = new JavaASTAnalyser(apiListing); analyser.analyse(allFiles); } catch (Exception e) { e.printStackTrace(); 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 58415428da8..9ae94e720de 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 @@ -1,5 +1,7 @@ package com.azure.tools.apiview.processor.analysers; +import com.azure.tools.apiview.processor.analysers.models.AnnotationRendererModel; +import com.azure.tools.apiview.processor.analysers.models.AnnotationRule; import com.azure.tools.apiview.processor.analysers.util.MiscUtils; import com.azure.tools.apiview.processor.analysers.util.TokenModifier; import com.azure.tools.apiview.processor.diagnostics.Diagnostics; @@ -51,19 +53,10 @@ import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.TreeMap; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.regex.Matcher; @@ -75,6 +68,7 @@ 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.TokenModifier.INDENT; import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.NEWLINE; import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.NOTHING; @@ -101,8 +95,22 @@ public class JavaASTAnalyser implements Analyser { public static final String MODULE_INFO_KEY = "module-info"; private static final boolean SHOW_JAVADOC = true; - private static final Set BLOCKED_ANNOTATIONS = - new HashSet<>(Arrays.asList("ServiceMethod", "SuppressWarnings")); + + private static final Map ANNOTATION_RULE_MAP; + static { + /* + For some annotations, we want to customise how they are displayed. Sometimes, we don't show them in any + circumstance. Other times, we want to show them but not their attributes. This map is used to define these + customisations. These rules override the default output that APIView will do, based on the location + annotation in the code. + */ + ANNOTATION_RULE_MAP = new HashMap<>(); + ANNOTATION_RULE_MAP.put("ServiceMethod", new AnnotationRule().setHidden(true)); + ANNOTATION_RULE_MAP.put("SuppressWarnings", new AnnotationRule().setHidden(true)); + + // we always want @Metadata annotations to be fully expanded, but in a condensed form + ANNOTATION_RULE_MAP.put("Metadata", new AnnotationRule().setShowProperties(true).setCondensed(true)); + } private static final Pattern SPLIT_NEWLINE = Pattern.compile(MiscUtils.LINEBREAK); @@ -112,12 +120,13 @@ public class JavaASTAnalyser implements Analyser { private final Map packageNameToPackageInfoJavaDoc = new HashMap<>(); - private final Diagnostics diagnostic = new Diagnostics(); + private final Diagnostics diagnostic; private int indent = 0; public JavaASTAnalyser(APIListing apiListing) { this.apiListing = apiListing; + diagnostic = new Diagnostics(apiListing); } @Override @@ -1032,34 +1041,24 @@ private void getAnnotations(final NodeWithAnnotations nodeWithAnnotations, final boolean showAnnotationProperties, final boolean addNewline) { Consumer consumer = annotation -> { - if (addNewline) { - addToken(makeWhitespace()); - } - - addToken(new Token(TYPE_NAME, "@" + annotation.getName().toString(), makeId(annotation, nodeWithAnnotations))); - if (showAnnotationProperties) { - if (annotation instanceof NormalAnnotationExpr) { - addToken(new Token(PUNCTUATION, "(")); - NodeList pairs = ((NormalAnnotationExpr) annotation).getPairs(); - for (int i = 0; i < pairs.size(); i++) { - MemberValuePair pair = pairs.get(i); - - addToken(new Token(TEXT, pair.getNameAsString())); - addToken(new Token(PUNCTUATION, " = ")); + // Check the annotation rules map for any overrides + final String annotationName = annotation.getName().toString(); + AnnotationRule annotationRule = ANNOTATION_RULE_MAP.get(annotationName); - Expression valueExpr = pair.getValue(); - processAnnotationValueExpression(valueExpr); + AnnotationRendererModel model = new AnnotationRendererModel( + annotation, nodeWithAnnotations, annotationRule, showAnnotationProperties, addNewline); - if (i < pairs.size() - 1) { - addToken(new Token(PUNCTUATION, ", ")); - } - } + if (model.isHidden()) { + return; + } - addToken(new Token(PUNCTUATION, ")")); - } + if (model.isAddNewline()) { + addToken(makeWhitespace()); } - if (addNewline) { + renderAnnotation(model).forEach(JavaASTAnalyser.this::addToken); + + if (model.isAddNewline()) { addNewLine(); } else { addToken(new Token(WHITESPACE, " ")); @@ -1070,43 +1069,96 @@ private void getAnnotations(final NodeWithAnnotations nodeWithAnnotations, .stream() .filter(annotationExpr -> { String id = annotationExpr.getName().getIdentifier(); - return !BLOCKED_ANNOTATIONS.contains(id) && !id.startsWith("Json"); + return !id.startsWith("Json"); }) .sorted(Comparator.comparing(a -> a.getName().getIdentifier())) // we sort the annotations alphabetically .forEach(consumer); } - private void processAnnotationValueExpression(Expression valueExpr) { + private List renderAnnotation(AnnotationRendererModel m) { + final AnnotationExpr a = m.getAnnotation(); + List tokens = new ArrayList<>(); + tokens.add(new Token(TYPE_NAME, "@" + a.getNameAsString(), makeId(a, m.getAnnotationParent()))); + if (m.isShowProperties()) { + if (a instanceof NormalAnnotationExpr) { + tokens.add(new Token(PUNCTUATION, "(")); + NodeList pairs = ((NormalAnnotationExpr) a).getPairs(); + for (int i = 0; i < pairs.size(); i++) { + MemberValuePair pair = pairs.get(i); + + // If the pair is a boolean expression, and we are condensed, we only take the name. + // If we are not a boolean expression, and we are condensed, we only take the value. + // If we are not condensed, we take both. + final Expression valueExpr = pair.getValue(); + if (m.isCondensed()) { + if (valueExpr.isBooleanLiteralExpr()) { + tokens.add(new Token(MEMBER_NAME, upperCase(pair.getNameAsString()))); + } else { + processAnnotationValueExpression(valueExpr, m.isCondensed(), tokens); + } + } else { + tokens.add(new Token(MEMBER_NAME, pair.getNameAsString())); + tokens.add(new Token(PUNCTUATION, " = ")); + + processAnnotationValueExpression(valueExpr, m.isCondensed(), tokens); + } + + if (i < pairs.size() - 1) { + tokens.add(new Token(PUNCTUATION, ", ")); + } + } + + tokens.add(new Token(PUNCTUATION, ")")); + } + } + return tokens; + } + + private void processAnnotationValueExpression(final Expression valueExpr, final boolean condensed, final List tokens) { if (valueExpr.isClassExpr()) { // lookup to see if the type is known about, if so, make it a link, otherwise leave it as text String typeName = valueExpr.getChildNodes().get(0).toString(); if (apiListing.getKnownTypes().containsKey(typeName)) { final Token token = new Token(TYPE_NAME, typeName); token.setNavigateToId(apiListing.getKnownTypes().get(typeName)); - addToken(token); + tokens.add(token); return; } } else if (valueExpr.isArrayInitializerExpr()) { - addToken(new Token(PUNCTUATION, "{ ")); + if (!condensed) { + tokens.add(new Token(PUNCTUATION, "{ ")); + } for (int i = 0; i < valueExpr.getChildNodes().size(); i++) { Node n = valueExpr.getChildNodes().get(i); if (n instanceof Expression) { - processAnnotationValueExpression((Expression) n); + processAnnotationValueExpression((Expression) n, condensed, tokens); } else { - addToken(new Token(TEXT, valueExpr.toString())); + tokens.add(new Token(TEXT, valueExpr.toString())); } if (i < valueExpr.getChildNodes().size() - 1) { - addToken(new Token(PUNCTUATION, ", ")); + tokens.add(new Token(PUNCTUATION, ", ")); } } - addToken(new Token(PUNCTUATION, " }")); + if (!condensed) { + tokens.add(new Token(PUNCTUATION, " }")); + } return; } - // if we fall through to here, just treat it as a string - addToken(new Token(TEXT, valueExpr.toString())); + // if we fall through to here, just treat it as a string. + // If we are in condensed mode, we strip off everything before the last period + String value = valueExpr.toString(); + if (condensed) { + int lastPeriod = value.lastIndexOf('.'); + if (lastPeriod != -1) { + value = value.substring(lastPeriod + 1); + } + tokens.add(new Token(TEXT, upperCase(value))); + } else { + tokens.add(new Token(TEXT, value)); + } } private void getModifiers(NodeList modifiers) { diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/models/AnnotationRendererModel.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/models/AnnotationRendererModel.java new file mode 100644 index 00000000000..0119cc9493c --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/models/AnnotationRendererModel.java @@ -0,0 +1,54 @@ +package com.azure.tools.apiview.processor.analysers.models; + +import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; + +public class AnnotationRendererModel { + private final AnnotationExpr annotation; + + private final NodeWithAnnotations annotationParent; + + private final AnnotationRule rule; + + private final boolean showProperties; + + private final boolean addNewline; + + public AnnotationRendererModel(AnnotationExpr annotation, + NodeWithAnnotations nodeWithAnnotations, + AnnotationRule rule, + boolean showAnnotationProperties, + boolean addNewline) { + this.annotation = annotation; + this.annotationParent = nodeWithAnnotations; + this.rule = rule; + + // we override the showAnnotationProperties flag if the annotation rule specifies it + this.showProperties = rule == null ? showAnnotationProperties : rule.isShowProperties().orElse(showAnnotationProperties); + this.addNewline = rule == null ? addNewline : rule.isShowOnNewline().orElse(addNewline); + } + + public boolean isAddNewline() { + return addNewline; + } + + public boolean isShowProperties() { + return showProperties; + } + + public AnnotationExpr getAnnotation() { + return annotation; + } + + public NodeWithAnnotations getAnnotationParent() { + return annotationParent; + } + + public boolean isHidden() { + return rule != null && rule.isHidden().orElse(false); + } + + public boolean isCondensed() { + return rule != null && rule.isCondensed().orElse(false); + } +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/models/AnnotationRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/models/AnnotationRule.java new file mode 100644 index 00000000000..b3adb001a6c --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/models/AnnotationRule.java @@ -0,0 +1,50 @@ +package com.azure.tools.apiview.processor.analysers.models; + +import java.util.Optional; + +public class AnnotationRule { + + private Optional hideAnnotation = Optional.empty(); + + private Optional showProperties = Optional.empty(); + + private Optional showOnNewline = Optional.empty(); + + private Optional condensed = Optional.empty(); + + public AnnotationRule setHidden(boolean hidden) { + this.hideAnnotation = Optional.of(hidden); + return this; + } + + public Optional isHidden() { + return hideAnnotation; + } + + public AnnotationRule setShowProperties(boolean showProperties) { + this.showProperties = Optional.of(showProperties); + return this; + } + + public Optional isShowProperties() { + return showProperties; + } + + public AnnotationRule setShowOnNewline(boolean showOnNewline) { + this.showOnNewline = Optional.of(showOnNewline); + return this; + } + + public Optional isShowOnNewline() { + return showOnNewline; + } + + public AnnotationRule setCondensed(boolean condensed) { + this.condensed = Optional.of(condensed); + return this; + } + + public Optional isCondensed() { + return condensed; + } +} 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 bcc6eb24214..aea307a1fd4 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 @@ -181,7 +181,7 @@ public static boolean isPublicOrProtected(AccessSpecifier accessSpecifier) { * @return Whether the access specifier is package-private or private. */ public static boolean isPrivateOrPackagePrivate(AccessSpecifier accessSpecifier) { - return (accessSpecifier == AccessSpecifier.PRIVATE) || (accessSpecifier == AccessSpecifier.PACKAGE_PRIVATE); + return (accessSpecifier == AccessSpecifier.PRIVATE) || (accessSpecifier == AccessSpecifier.NONE); } public static String makeId(CompilationUnit cu) { 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 649c380e267..85ba4a865e2 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 @@ -87,6 +87,22 @@ private static String wrapLine(final String line, final int lineLength) { return allLines.toString(); } + /** + * Makes all characters in the string lowercase, other than the first character. + */ + public static String upperCase(String s) { + return upperCase(s, 0); + } + + /** + * Makes all characters in the string lowercase, other than the given index. + */ + public static String upperCase(String s, int index) { + return s.substring(0, index).toLowerCase() + + Character.toUpperCase(s.charAt(index)) + + s.substring(index + 1).toLowerCase(); + } + private MiscUtils() { } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/Diagnostics.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/Diagnostics.java index a5d787fc033..3a047b3ea49 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/Diagnostics.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/Diagnostics.java @@ -1,73 +1,107 @@ package com.azure.tools.apiview.processor.diagnostics; import com.azure.tools.apiview.processor.diagnostics.rules.*; +import com.azure.tools.apiview.processor.diagnostics.rules.azure.*; +import com.azure.tools.apiview.processor.diagnostics.rules.clientcore.ClientCoreBuilderTraitsDiagnosticRule; +import com.azure.tools.apiview.processor.diagnostics.rules.clientcore.ClientCoreFluentSetterReturnTypeDiagnosticRule; +import com.azure.tools.apiview.processor.diagnostics.rules.clientcore.ExpandableEnumDiagnosticRule; +import com.azure.tools.apiview.processor.diagnostics.rules.general.*; +import com.azure.tools.apiview.processor.diagnostics.rules.utils.MiscUtils; import com.azure.tools.apiview.processor.model.APIListing; -import com.azure.tools.apiview.processor.model.Diagnostic; +import com.azure.tools.apiview.processor.model.Flavor; import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.type.ClassOrInterfaceType; -import com.github.javaparser.ast.type.Type; import java.util.ArrayList; import java.util.List; -import java.util.Optional; +import java.util.regex.Pattern; -import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.makeId; -import static com.azure.tools.apiview.processor.diagnostics.rules.IllegalMethodNamesDiagnosticRule.Rule; +import static com.azure.tools.apiview.processor.model.Flavor.*; + +import static com.azure.tools.apiview.processor.diagnostics.rules.general.IllegalMethodNamesDiagnosticRule.Rule; import static com.azure.tools.apiview.processor.diagnostics.rules.RequiredBuilderMethodsDiagnosticRule.DirectSubclassCheckFunction; import static com.azure.tools.apiview.processor.diagnostics.rules.RequiredBuilderMethodsDiagnosticRule.ExactTypeNameCheckFunction; import static com.azure.tools.apiview.processor.diagnostics.rules.RequiredBuilderMethodsDiagnosticRule.ParameterAllowedTypes; -import static com.azure.tools.apiview.processor.diagnostics.rules.BadAnnotationDiagnosticRule.BadAnnotation; -import static com.azure.tools.apiview.processor.model.DiagnosticKind.WARNING; +import static com.azure.tools.apiview.processor.diagnostics.rules.general.BadAnnotationDiagnosticRule.BadAnnotation; public class Diagnostics { - private final List diagnostics = new ArrayList<>(); - - public Diagnostics() { - diagnostics.add(new PackageNameDiagnosticRule()); - diagnostics.add(new ImportsDiagnosticRule("com.sun")); - diagnostics.add(new IllegalPackageAPIExportsDiagnosticRule("implementation", "netty")); - diagnostics.add(new NoPublicFieldsDiagnosticRule()); - diagnostics.add(new UpperCaseNamingDiagnosticRule("URL", "HTTP", "XML", "JSON", "SAS", "CPK", "API")); - diagnostics.add(new MissingAnnotationsDiagnosticRule()); - diagnostics.add(new FluentSetterReturnTypeDiagnosticRule()); - diagnostics.add(new ConsiderFinalClassDiagnosticRule()); - diagnostics.add(new IllegalMethodNamesDiagnosticRule( - new Rule("Builder$", "tokenCredential"), // it should just be 'credential' - new Rule("Builder$", "^set"), // we shouldn't have setters in the builder - new Rule("^isHas"), - new Rule("^setHas") + final List rules; + + public Diagnostics(APIListing apiListing) { + rules = new ArrayList<>(); + + System.out.println(" Setting up diagnostics..."); + + Flavor flavor = Flavor.getFlavor(apiListing); + + // Special rules for com.azure or io.clientcore libraries only + switch (flavor) { + case AZURE: { + System.out.println(" Applying com.azure specific diagnostics..."); + addAzureCoreDiagnostics(); + break; + } + case GENERIC: { + System.out.println(" Applying io.clientcore specific diagnostics..."); + addClientCoreDiagnostics(); + break; + } + case UNKNOWN: + default: { + System.out.println(" Unknown library flavor..."); + break; + } + } + + System.out.println(" Applying general-purpose diagnostics..."); + + // general rules applicable in all cases + rules.add(new UpperCaseEnumValuesDiagnosticRule()); + rules.add(new ImportsDiagnosticRule("com.sun")); + rules.add(new IllegalPackageAPIExportsDiagnosticRule("implementation", "netty")); + rules.add(new NoPublicFieldsDiagnosticRule()); + rules.add(new UpperCaseNamingDiagnosticRule("URL", "HTTP", "XML", "JSON", "SAS", "CPK", "API")); + rules.add(new ConsiderFinalClassDiagnosticRule()); + rules.add(new IllegalMethodNamesDiagnosticRule( + new Rule("Builder$", "tokenCredential"), // it should just be 'credential' + new Rule("Builder$", "^set"), // we shouldn't have setters in the builder + new Rule("^isHas"), + new Rule("^setHas") )); - diagnostics.add(new MissingJavaDocDiagnosticRule()); - diagnostics.add(new MissingJavadocCodeSnippetsRule()); - diagnostics.add(new NoLocalesInJavadocUrlDiagnosticRule()); - diagnostics.add(new ModuleInfoDiagnosticRule()); - diagnostics.add(new ServiceVersionDiagnosticRule()); - diagnostics.add(new BadAnnotationDiagnosticRule( - new BadAnnotation("JacksonXmlRootElement", - "From the Jackson JavaDoc: \"NOTE! Since 2.4 this annotation is usually not necessary and " + - "you should use JsonRootName instead. About the only expected usage may be to have different " + - "root name for XML content than other formats.\"") + rules.add(new MissingJavaDocDiagnosticRule()); + rules.add(new MissingJavadocCodeSnippetsRule()); + rules.add(new NoLocalesInJavadocUrlDiagnosticRule()); + rules.add(new ModuleInfoDiagnosticRule()); + rules.add(new BadAnnotationDiagnosticRule( + new BadAnnotation("JacksonXmlRootElement", + "From the Jackson JavaDoc: \"NOTE! Since 2.4 this annotation is usually not necessary and " + + "you should use JsonRootName instead. About the only expected usage may be to have different " + + "root name for XML content than other formats.\"") )); - diagnostics.add(new BuilderTraitsDiagnosticRule()); - diagnostics.add(new MavenPackageAndDescriptionDiagnosticRule()); - diagnostics.add(new ExpandableStringEnumDiagnosticRule()); - diagnostics.add(new UpperCaseEnumValuesDiagnosticRule()); + } + + private void addAzureCoreDiagnostics() { + rules.add(new PackageNameDiagnosticRule(Pattern.compile("^" + AZURE.getPackagePrefix() + "(\\.[a-z0-9]+)+$"))); + rules.add(new AzureCoreBuilderTraitsDiagnosticRule()); + rules.add(new MissingAnnotationsDiagnosticRule(AZURE.getPackagePrefix())); + rules.add(new AzureCoreFluentSetterReturnTypeDiagnosticRule()); + rules.add(new ServiceVersionDiagnosticRule()); + rules.add(new ExpandableEnumDiagnosticRule("ExpandableStringEnum")); + rules.add(new MavenPackageAndDescriptionDiagnosticRule()); // common APIs for all builders (below we will do rules for http or amqp builders) - diagnostics.add(new RequiredBuilderMethodsDiagnosticRule(null) + rules.add(new RequiredBuilderMethodsDiagnosticRule(null) .add("configuration", new ExactTypeNameCheckFunction("Configuration")) .add("clientOptions", new ExactTypeNameCheckFunction("ClientOptions")) .add("connectionString", new ExactTypeNameCheckFunction("String")) .add("credential", new ExactTypeNameCheckFunction(new ParameterAllowedTypes("TokenCredential", - "AzureKeyCredential", "AzureSasCredential", "AzureNamedKeyCredential", "KeyCredential"))) + "AzureKeyCredential", "AzureSasCredential", "AzureNamedKeyCredential", "KeyCredential"))) .add("endpoint", new ExactTypeNameCheckFunction("String")) - .add("serviceVersion", this::checkServiceVersionType)); - diagnostics.add(new RequiredBuilderMethodsDiagnosticRule("amqp") + .add("serviceVersion", m -> MiscUtils.checkMethodParameterTypeSuffix(m, "ServiceVersion"))); + rules.add(new RequiredBuilderMethodsDiagnosticRule("amqp") .add("proxyOptions", new ExactTypeNameCheckFunction("ProxyOptions")) .add("retry", new ExactTypeNameCheckFunction("AmqpRetryOptions")) .add("transportType", new DirectSubclassCheckFunction("AmqpTransportType"))); - diagnostics.add(new RequiredBuilderMethodsDiagnosticRule("http") + rules.add(new RequiredBuilderMethodsDiagnosticRule("http") .add("addPolicy", new ExactTypeNameCheckFunction("HttpPipelinePolicy")) .add("httpClient", new ExactTypeNameCheckFunction("HttpClient")) .add("httpLogOptions", new ExactTypeNameCheckFunction("HttpLogOptions")) @@ -75,16 +109,28 @@ public Diagnostics() { .add("retryPolicy", new ExactTypeNameCheckFunction("RetryPolicy"))); } - private Optional checkServiceVersionType(MethodDeclaration methodDeclaration) { - Type parameterType = methodDeclaration.getParameter(0).getType(); - ClassOrInterfaceType classOrInterfaceType = parameterType.asClassOrInterfaceType(); - if (!classOrInterfaceType.getNameAsString().endsWith("ServiceVersion")) { - return Optional.of( - new Diagnostic(WARNING, makeId(methodDeclaration), - "Incorrect type being supplied to this builder method. Expected an enum " - + "implementing ServiceVersion but was " + classOrInterfaceType.getNameAsString() + ".")); - } - return Optional.empty(); + private void addClientCoreDiagnostics() { + rules.add(new ClientCoreBuilderTraitsDiagnosticRule()); + rules.add(new MissingAnnotationsDiagnosticRule(GENERIC.getPackagePrefix())); + rules.add(new ClientCoreFluentSetterReturnTypeDiagnosticRule()); + rules.add(new ExpandableEnumDiagnosticRule("ExpandableEnum")); + + // common APIs for all builders (below we will do rules for http or amqp builders) + rules.add(new RequiredBuilderMethodsDiagnosticRule(null) + .add("endpoint", new ExactTypeNameCheckFunction("String")) + .add("configuration", new ExactTypeNameCheckFunction("Configuration")) + .add("credential", new ExactTypeNameCheckFunction(new ParameterAllowedTypes("KeyCredential"))) + .add("addHttpPipelinePolicy", new ExactTypeNameCheckFunction("HttpPipelinePolicy")) + .add("httpClient", new ExactTypeNameCheckFunction("HttpClient")) + .add("httpLogOptions", new ExactTypeNameCheckFunction("HttpLogOptions")) + .add("httpPipeline", new ExactTypeNameCheckFunction("HttpPipeline")) + .add("httpRetryOptions", new ExactTypeNameCheckFunction("HttpRetryOptions")) + .add("httpRedirectOptions", new ExactTypeNameCheckFunction("HttpRedirectOptions")) + .add("proxyOptions", new ExactTypeNameCheckFunction("ProxyOptions"))); + } + + private void add(DiagnosticRule rule) { + rules.add(rule); } /** @@ -95,13 +141,13 @@ public void scanIndividual(CompilationUnit cu, APIListing listing) { if (!cu.getPrimaryType().isPresent()) { return; } - diagnostics.forEach(rule -> rule.scanIndividual(cu, listing)); + rules.forEach(rule -> rule.scanIndividual(cu, listing)); } /** * Called once to allow for any full analysis to be performed after all individual scans have been completed. */ public void scanFinal(APIListing listing) { - diagnostics.forEach(rule -> rule.scanFinal(listing)); + rules.forEach(rule -> rule.scanFinal(listing)); } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/AzureCoreBuilderTraitsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/AzureCoreBuilderTraitsDiagnosticRule.java new file mode 100644 index 00000000000..d9befaa4b3d --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/AzureCoreBuilderTraitsDiagnosticRule.java @@ -0,0 +1,59 @@ +package com.azure.tools.apiview.processor.diagnostics.rules.azure; + +import com.azure.tools.apiview.processor.diagnostics.rules.utils.BuilderTraitsDiagnosticRule; + +import java.util.HashMap; +import java.util.Map; + +/** + * This diagnostic ensures that builder traits are applied correctly against client builders. + */ +public class AzureCoreBuilderTraitsDiagnosticRule extends BuilderTraitsDiagnosticRule { + private static final Map traits; + static { + traits = new HashMap<>(); + traits.put("KeyCredentialTrait", new TraitClass( + new TraitMethod("credential", "KeyCredential") + )); + traits.put("AzureKeyCredentialTrait", new TraitClass( + new TraitMethod("credential", "AzureKeyCredential") + )); + traits.put("AzureNamedKeyCredentialTrait", new TraitClass( + new TraitMethod("credential", "AzureNamedKeyCredential") + )); + traits.put("AzureSasCredentialTrait", new TraitClass( + new TraitMethod("credential", "AzureSasCredential") + )); + traits.put("TokenCredentialTrait", new TraitClass( + new TraitMethod("credential", "TokenCredential") + )); + traits.put("ConfigurationTrait", new TraitClass( + new TraitMethod("configuration", "Configuration") + )); + traits.put("ConnectionStringTrait", new TraitClass( + new TraitMethod("connectionString", "String") + )); + traits.put("EndpointTrait", new TraitClass( + new TraitMethod("endpoint", "String") + )); + traits.put("HttpTrait", new TraitClass(TraitClass.BUILDER_PROTOCOL_HTTP, + new TraitMethod("httpClient", "HttpClient"), + new TraitMethod("pipeline", "HttpPipeline"), + new TraitMethod("addPolicy", "HttpPipelinePolicy"), + new TraitMethod("retryOptions", "RetryOptions"), + new TraitMethod("httpLogOptions", "HttpLogOptions"), + new TraitMethod("clientOptions", "ClientOptions") + )); + traits.put("AmqpTrait", new TraitClass(TraitClass.BUILDER_PROTOCOL_AMQP, + new TraitMethod("retryOptions", "AmqpRetryOptions"), + new TraitMethod("transportType", "AmqpTransportType"), + new TraitMethod("proxyOptions", "ProxyOptions"), + new TraitMethod("clientOptions", "ClientOptions") + )); + } + + public AzureCoreBuilderTraitsDiagnosticRule() { + super(traits); + } +} + diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/AzureCoreFluentSetterReturnTypeDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/AzureCoreFluentSetterReturnTypeDiagnosticRule.java new file mode 100644 index 00000000000..491608adf78 --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/AzureCoreFluentSetterReturnTypeDiagnosticRule.java @@ -0,0 +1,10 @@ +package com.azure.tools.apiview.processor.diagnostics.rules.azure; + +import com.azure.tools.apiview.processor.diagnostics.rules.utils.FluentSetterReturnTypeDiagnosticRule; + +public class AzureCoreFluentSetterReturnTypeDiagnosticRule extends FluentSetterReturnTypeDiagnosticRule { + + public AzureCoreFluentSetterReturnTypeDiagnosticRule() { + super(type -> type.getAnnotationByName("Fluent").isPresent()); + } +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MavenPackageAndDescriptionDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/MavenPackageAndDescriptionDiagnosticRule.java similarity index 98% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MavenPackageAndDescriptionDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/MavenPackageAndDescriptionDiagnosticRule.java index 93e65df4651..d3940783ff8 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MavenPackageAndDescriptionDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/MavenPackageAndDescriptionDiagnosticRule.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.azure; import com.azure.tools.apiview.processor.analysers.util.MiscUtils; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ServiceVersionDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/ServiceVersionDiagnosticRule.java similarity index 98% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ServiceVersionDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/ServiceVersionDiagnosticRule.java index 8682c664dd0..5f233d3617d 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ServiceVersionDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/ServiceVersionDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.azure; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ClientCoreBuilderTraitsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ClientCoreBuilderTraitsDiagnosticRule.java new file mode 100644 index 00000000000..6becaf74160 --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ClientCoreBuilderTraitsDiagnosticRule.java @@ -0,0 +1,39 @@ +package com.azure.tools.apiview.processor.diagnostics.rules.clientcore; + +import com.azure.tools.apiview.processor.diagnostics.rules.utils.BuilderTraitsDiagnosticRule; + +import java.util.*; + +/** + * This diagnostic ensures that builder traits are applied correctly against client builders. + */ +public class ClientCoreBuilderTraitsDiagnosticRule extends BuilderTraitsDiagnosticRule { + private static final Map traits; + static { + traits = new HashMap<>(); + traits.put("KeyCredentialTrait", new TraitClass( + new TraitMethod("credential", "KeyCredential") + )); + traits.put("ConfigurationTrait", new TraitClass( + new TraitMethod("configuration", "Configuration") + )); + traits.put("ProxyTrait", new TraitClass( + new TraitMethod("proxyOptions", "ProxyOptions") + )); + traits.put("EndpointTrait", new TraitClass( + new TraitMethod("endpoint", "String") + )); + traits.put("HttpTrait", new TraitClass(TraitClass.BUILDER_PROTOCOL_HTTP, + new TraitMethod("httpClient", "HttpClient"), + new TraitMethod("httpPipeline", "HttpPipeline"), + new TraitMethod("addHttpPipelinePolicy", "HttpPipelinePolicy"), + new TraitMethod("httpRetryOptions", "HttpRetryOptions"), + new TraitMethod("httpLogOptions", "HttpLogOptions"), + new TraitMethod("httpRedirectOptions", "HttpRedirectOptions") + )); + } + + public ClientCoreBuilderTraitsDiagnosticRule() { + super(traits); + } +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ClientCoreFluentSetterReturnTypeDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ClientCoreFluentSetterReturnTypeDiagnosticRule.java new file mode 100644 index 00000000000..90766aca608 --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ClientCoreFluentSetterReturnTypeDiagnosticRule.java @@ -0,0 +1,13 @@ +package com.azure.tools.apiview.processor.diagnostics.rules.clientcore; + +import com.azure.tools.apiview.processor.diagnostics.rules.utils.FluentSetterReturnTypeDiagnosticRule; + +public class ClientCoreFluentSetterReturnTypeDiagnosticRule extends FluentSetterReturnTypeDiagnosticRule { + + public ClientCoreFluentSetterReturnTypeDiagnosticRule() { + super(type -> type.getAnnotationByName("Metadata") + .map(annotationExpr -> annotationExpr.asNormalAnnotationExpr().getPairs().stream() + .filter(pair -> pair.getName().asString().equals("conditions")) + .anyMatch(pair -> pair.getValue().toString().contains("FLUENT"))).orElse(false)); + } +} diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ExpandableStringEnumDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ExpandableEnumDiagnosticRule.java similarity index 80% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ExpandableStringEnumDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ExpandableEnumDiagnosticRule.java index 608fe69f3e6..f82bff5aad1 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ExpandableStringEnumDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/clientcore/ExpandableEnumDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.clientcore; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -18,25 +18,29 @@ * Diagnostic rule to validate if types extending from ExpandableStringEnum include * fromString() and values() methods. */ -public class ExpandableStringEnumDiagnosticRule implements DiagnosticRule { +public class ExpandableEnumDiagnosticRule implements DiagnosticRule { + private final String parentTypeName; + + public ExpandableEnumDiagnosticRule(String parentTypeName) { + this.parentTypeName = parentTypeName; + } @Override public void scanIndividual(final CompilationUnit cu, final APIListing listing) { - cu.getPrimaryType().ifPresent(typeDeclaration -> { if (typeDeclaration instanceof ClassOrInterfaceDeclaration) { ClassOrInterfaceDeclaration classDeclaration = (ClassOrInterfaceDeclaration) typeDeclaration; // check if the type extends ExpandableStringEnum if (classDeclaration.getExtendedTypes() != null && classDeclaration.getExtendedTypes().stream() - .anyMatch(extendedType -> extendedType.getNameAsString().equals("ExpandableStringEnum"))) { + .anyMatch(extendedType -> extendedType.getNameAsString().equals(parentTypeName))) { checkRequiredMethodsExist(cu, listing); } } }); } - private static void checkRequiredMethodsExist(CompilationUnit cu, APIListing listing) { + private void checkRequiredMethodsExist(CompilationUnit cu, APIListing listing) { getClassName(cu).ifPresent(className -> { AtomicBoolean hasFromStringMethod = new AtomicBoolean(false); AtomicBoolean hasValuesMethod = new AtomicBoolean(false); @@ -55,14 +59,14 @@ private static void checkRequiredMethodsExist(CompilationUnit cu, APIListing lis listing.addDiagnostic(new Diagnostic( ERROR, makeId(cu), - "Types extending ExpandableStringEnum should include public static fromString() method.")); + "Types extending " + parentTypeName + " should include public static fromString() method.")); } if (!hasValuesMethod.get()) { // Missing values method can be logged at warning level. listing.addDiagnostic(new Diagnostic( WARNING, makeId(cu), - "Types extending ExpandableStringEnum should include public static values() method.")); + "Types extending " + parentTypeName + " should include public static values() method.")); } }); } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/BadAnnotationDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/BadAnnotationDiagnosticRule.java similarity index 97% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/BadAnnotationDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/BadAnnotationDiagnosticRule.java index 420644a26b5..dab54d42c79 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/BadAnnotationDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/BadAnnotationDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -26,7 +26,6 @@ public BadAnnotationDiagnosticRule(BadAnnotation... badAnnotations) { @Override public void scanIndividual(final CompilationUnit cu, final APIListing listing) { - getClasses(cu).forEach(typeDeclaration -> { // check annotations on the type itself typeDeclaration.getAnnotations() @@ -41,6 +40,7 @@ public void scanIndividual(final CompilationUnit cu, final APIListing listing) { getPublicOrProtectedConstructors(typeDeclaration) .forEach(constructor -> constructor.getAnnotations() .forEach(annotation -> checkForBadAnnotations(listing, constructor, annotation))); + // check annotations on methods getPublicOrProtectedMethods(typeDeclaration) .forEach(method -> method.getAnnotations() diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ConsiderFinalClassDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ConsiderFinalClassDiagnosticRule.java similarity index 97% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ConsiderFinalClassDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ConsiderFinalClassDiagnosticRule.java index fc8b00e5e68..c81c533e5a0 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ConsiderFinalClassDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ConsiderFinalClassDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/IllegalMethodNamesDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/IllegalMethodNamesDiagnosticRule.java similarity index 98% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/IllegalMethodNamesDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/IllegalMethodNamesDiagnosticRule.java index 5dcf4c967b3..114f3aa04d8 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/IllegalMethodNamesDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/IllegalMethodNamesDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/IllegalPackageAPIExportsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/IllegalPackageAPIExportsDiagnosticRule.java similarity index 98% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/IllegalPackageAPIExportsDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/IllegalPackageAPIExportsDiagnosticRule.java index e4c3d01d64a..abf93ea2ec5 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/IllegalPackageAPIExportsDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/IllegalPackageAPIExportsDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ImportsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ImportsDiagnosticRule.java similarity index 96% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ImportsDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ImportsDiagnosticRule.java index 470ecc2a613..8504a069867 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ImportsDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ImportsDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.analysers.util.ASTUtils; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingAnnotationsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingAnnotationsDiagnosticRule.java similarity index 86% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingAnnotationsDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingAnnotationsDiagnosticRule.java index ae5751e897e..d5322df2d30 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingAnnotationsDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingAnnotationsDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -10,12 +10,22 @@ import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.*; import static com.azure.tools.apiview.processor.model.DiagnosticKind.*; +/** + * This diagnostic, whilst being in the 'general' package, is still very heavily skewed towards azure and client core + * libraries, so it isn't enabled for all libraries. + */ public class MissingAnnotationsDiagnosticRule implements DiagnosticRule { + private final String packagePrefix; + + public MissingAnnotationsDiagnosticRule(String packagePrefix) { + this.packagePrefix = packagePrefix; + } + @Override public void scanIndividual(final CompilationUnit cu, final APIListing listing) { getClasses(cu) - .filter(type -> getPackageName(type).startsWith("com.azure")) // we only want to give this guidance to Azure SDK developers + .filter(type -> getPackageName(type).startsWith(packagePrefix)) // we only want to give this guidance to Azure SDK developers .forEach(typeDeclaration -> { String className = typeDeclaration.getNameAsString(); diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingJavaDocDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingJavaDocDiagnosticRule.java similarity index 97% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingJavaDocDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingJavaDocDiagnosticRule.java index dd4daa8ae46..a0f5b14bacd 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingJavaDocDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingJavaDocDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingJavadocCodeSnippetsRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingJavadocCodeSnippetsRule.java similarity index 98% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingJavadocCodeSnippetsRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingJavadocCodeSnippetsRule.java index 111a9fc9e0d..4d602bcd588 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/MissingJavadocCodeSnippetsRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/MissingJavadocCodeSnippetsRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ModuleInfoDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java similarity index 98% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ModuleInfoDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java index ce53e74f129..653b8effa90 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/ModuleInfoDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/ModuleInfoDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.analysers.JavaASTAnalyser; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/NoLocalesInJavadocUrlDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java similarity index 97% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/NoLocalesInJavadocUrlDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java index 0719bea5da4..1a2c0b380b5 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/NoLocalesInJavadocUrlDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/NoPublicFieldsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoPublicFieldsDiagnosticRule.java similarity index 93% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/NoPublicFieldsDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoPublicFieldsDiagnosticRule.java index b4d37abbfdc..c8e97ee216d 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/NoPublicFieldsDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoPublicFieldsDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/PackageNameDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/PackageNameDiagnosticRule.java similarity index 79% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/PackageNameDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/PackageNameDiagnosticRule.java index 7b5a731ebc7..16cc9c91dce 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/PackageNameDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/PackageNameDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -11,7 +11,11 @@ import static com.azure.tools.apiview.processor.model.DiagnosticKind.*; public class PackageNameDiagnosticRule implements DiagnosticRule { - final static Pattern regex = Pattern.compile("^com.azure(\\.[a-z0-9]+)+$"); + private final Pattern regex; + + public PackageNameDiagnosticRule(Pattern regex) { + this.regex = regex; + } @Override public void scanIndividual(final CompilationUnit cu, final APIListing listing) { @@ -22,7 +26,7 @@ public void scanIndividual(final CompilationUnit cu, final APIListing listing) { listing.addDiagnostic(new Diagnostic( ERROR, typeId, - "Package name must start with 'com.azure..', and it must be lower-case, with no underscores or hyphens.")); + "Package name match the following regex: " + regex.pattern())); } }); }); diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/UpperCaseEnumValuesDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/UpperCaseEnumValuesDiagnosticRule.java similarity index 60% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/UpperCaseEnumValuesDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/UpperCaseEnumValuesDiagnosticRule.java index c84d516beaf..b0d09f20a90 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/UpperCaseEnumValuesDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/UpperCaseEnumValuesDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -21,16 +21,14 @@ public void scanIndividual(final CompilationUnit cu, final APIListing listing) { getClasses(cu) .filter(TypeDeclaration::isEnumDeclaration) .map(TypeDeclaration::asEnumDeclaration) - .forEach(enumDeclaration -> { - enumDeclaration.getEntries().forEach(enumConstantDeclaration -> { - String name = enumConstantDeclaration.getName().asString(); - if (!name.equals(name.toUpperCase())) { - listing.addDiagnostic(new Diagnostic( - WARNING, - makeId(enumConstantDeclaration), - "All enum constants should be upper case, using underscores as necessary between words.")); - } - }); - }); + .forEach(enumDeclaration -> enumDeclaration.getEntries().forEach(enumConstantDeclaration -> { + String name = enumConstantDeclaration.getName().asString(); + if (!name.equals(name.toUpperCase())) { + listing.addDiagnostic(new Diagnostic( + WARNING, + makeId(enumConstantDeclaration), + "All enum constants should be upper case, using underscores as necessary between words.")); + } + })); } } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/UpperCaseNamingDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/UpperCaseNamingDiagnosticRule.java similarity index 95% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/UpperCaseNamingDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/UpperCaseNamingDiagnosticRule.java index d2b226536f8..4abad29a776 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/UpperCaseNamingDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/general/UpperCaseNamingDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/BuilderTraitsDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/BuilderTraitsDiagnosticRule.java similarity index 70% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/BuilderTraitsDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/BuilderTraitsDiagnosticRule.java index bdbbea304e9..05f80478853 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/BuilderTraitsDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/BuilderTraitsDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.utils; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -8,64 +8,20 @@ import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.expr.AnnotationExpr; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; -import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.*; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.isTypeImplementingInterface; +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.makeId; import static com.azure.tools.apiview.processor.model.DiagnosticKind.ERROR; import static com.azure.tools.apiview.processor.model.DiagnosticKind.WARNING; -/** - * This diagnostic ensures that builder traits are applied correctly against client builders. - */ -public class BuilderTraitsDiagnosticRule implements DiagnosticRule { +public abstract class BuilderTraitsDiagnosticRule implements DiagnosticRule { private static final String ANNOTATION_SERVICE_CLIENT_BUILDER = "ServiceClientBuilder"; private final Map traits; - public BuilderTraitsDiagnosticRule() { - traits = new HashMap<>(); - traits.put("KeyCredentialTrait", new TraitClass( - new TraitMethod("credential", "KeyCredential") - )); - traits.put("AzureKeyCredentialTrait", new TraitClass( - new TraitMethod("credential", "AzureKeyCredential") - )); - traits.put("AzureNamedKeyCredentialTrait", new TraitClass( - new TraitMethod("credential", "AzureNamedKeyCredential") - )); - traits.put("AzureSasCredentialTrait", new TraitClass( - new TraitMethod("credential", "AzureSasCredential") - )); - traits.put("TokenCredentialTrait", new TraitClass( - new TraitMethod("credential", "TokenCredential") - )); - traits.put("ConfigurationTrait", new TraitClass( - new TraitMethod("configuration", "Configuration") - )); - traits.put("ConnectionStringTrait", new TraitClass( - new TraitMethod("connectionString", "String") - )); - traits.put("EndpointTrait", new TraitClass( - new TraitMethod("endpoint", "String") - )); - traits.put("HttpTrait", new TraitClass(TraitClass.BUILDER_PROTOCOL_HTTP, - new TraitMethod("httpClient", "HttpClient"), - new TraitMethod("pipeline", "HttpPipeline"), - new TraitMethod("addPolicy", "HttpPipelinePolicy"), - new TraitMethod("retryOptions", "RetryOptions"), - new TraitMethod("httpLogOptions", "HttpLogOptions"), - new TraitMethod("clientOptions", "ClientOptions") - )); - traits.put("AmqpTrait", new TraitClass(TraitClass.BUILDER_PROTOCOL_AMQP, - new TraitMethod("retryOptions", "AmqpRetryOptions"), - new TraitMethod("transportType", "AmqpTransportType"), - new TraitMethod("proxyOptions", "ProxyOptions"), - new TraitMethod("clientOptions", "ClientOptions") - )); + protected BuilderTraitsDiagnosticRule(Map traits) { + this.traits = traits; } @Override @@ -146,10 +102,10 @@ public void scanIndividual(final CompilationUnit cu, final APIListing listing) { // that mean this method is non-standard and not part of the trait. String message = isTypeImplementingInterface(type, traitName) ? "This builder implements the " + traitName + " trait, but offers duplicate " + - "functionality. Consider whether this impacts consistency with other builders." : + "functionality. Consider whether this impacts consistency with other builders." : "This builder does not implement the " + traitName + " trait, but offers " + - "similar functionality. Consider implementing the trait and aligning the parameters " + - "in this builder method."; + "similar functionality. Consider implementing the trait and aligning the parameters " + + "in this builder method."; listing.addDiagnostic(new Diagnostic(WARNING, makeId(matchingNameMethod), message)); } } @@ -158,10 +114,10 @@ public void scanIndividual(final CompilationUnit cu, final APIListing listing) { }); } - private static class TraitClass { - static final String BUILDER_PROTOCOL_NOT_APPLICABLE = "N/A"; - static final String BUILDER_PROTOCOL_HTTP = "ServiceClientProtocol.HTTP"; - static final String BUILDER_PROTOCOL_AMQP = "ServiceClientProtocol.AMQP"; + public static class TraitClass { + public static final String BUILDER_PROTOCOL_NOT_APPLICABLE = "N/A"; + public static final String BUILDER_PROTOCOL_HTTP = "ServiceClientProtocol.HTTP"; + public static final String BUILDER_PROTOCOL_AMQP = "ServiceClientProtocol.AMQP"; private final String builderProtocol; private final List methods; @@ -176,7 +132,7 @@ public TraitClass(String builderProtocol, TraitMethod... methods) { } } - private static class TraitMethod { + public static class TraitMethod { private final String methodName; private final String[] methodParamTypes; diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/FluentSetterReturnTypeDiagnosticRule.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java similarity index 66% rename from src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/FluentSetterReturnTypeDiagnosticRule.java rename to src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java index c4bc4b98646..cdf9950f49c 100644 --- a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/FluentSetterReturnTypeDiagnosticRule.java +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/FluentSetterReturnTypeDiagnosticRule.java @@ -1,4 +1,4 @@ -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.utils; import com.azure.tools.apiview.processor.diagnostics.DiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; @@ -6,20 +6,25 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.TypeDeclaration; +import java.util.function.Function; +import java.util.function.Predicate; + import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.getPublicOrProtectedMethods; import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.makeId; -import static com.azure.tools.apiview.processor.model.DiagnosticKind.*; +import static com.azure.tools.apiview.processor.model.DiagnosticKind.ERROR; -public class FluentSetterReturnTypeDiagnosticRule implements DiagnosticRule { +public abstract class FluentSetterReturnTypeDiagnosticRule implements DiagnosticRule { + private Predicate> isFluentType; - public FluentSetterReturnTypeDiagnosticRule() { - // no-op + public FluentSetterReturnTypeDiagnosticRule(Predicate> isFluentType) { + this.isFluentType = isFluentType; } @Override public void scanIndividual(final CompilationUnit cu, final APIListing listing) { - cu.getTypes().forEach(type -> - type.getAnnotationByName("Fluent").ifPresent(a -> processFluentType(type, listing))); + cu.getTypes().stream() + .filter(isFluentType) + .forEach(type -> processFluentType(type, listing)); } private void processFluentType(final TypeDeclaration type, final APIListing listing) { @@ -33,7 +38,7 @@ private void processFluentType(final TypeDeclaration type, final APIListing l listing.addDiagnostic(new Diagnostic( ERROR, makeId(method), - "Setter methods in a @Fluent class must return the same type as the fluent type.")); + "Setter methods in a fluent class must return the same type as the fluent type.")); } }); } diff --git a/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/MiscUtils.java b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/MiscUtils.java new file mode 100644 index 00000000000..a173601a969 --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/diagnostics/rules/utils/MiscUtils.java @@ -0,0 +1,44 @@ +package com.azure.tools.apiview.processor.diagnostics.rules.utils; + +import com.azure.tools.apiview.processor.model.Diagnostic; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.type.Type; + +import java.util.Optional; + +import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.makeId; +import static com.azure.tools.apiview.processor.model.DiagnosticKind.WARNING; + +public class MiscUtils { + + /** + * Checks that a given method declaration accepts a type whose name ends with the given suffix. + */ + public static Optional checkMethodParameterTypeSuffix(MethodDeclaration methodDeclaration, String suffix) { + return checkMethodParameterTypeSuffix(methodDeclaration, 0, suffix); + } + + /** + * Checks that a given method declaration accepts a type whose name ends with the given suffix. + */ + public static Optional checkMethodParameterTypeSuffix(MethodDeclaration methodDeclaration, + int paramIndex, + String suffix) { + if (paramIndex >= methodDeclaration.getParameters().size()) { + // The method does not have the expected number of parameters. + return Optional.of(new Diagnostic(WARNING, makeId(methodDeclaration), + "Incorrect number of parameters for this builder method. Expected " + paramIndex + " but was " + + methodDeclaration.getParameters().size() + ".")); + } + + Type parameterType = methodDeclaration.getParameter(paramIndex).getType(); + ClassOrInterfaceType classOrInterfaceType = parameterType.asClassOrInterfaceType(); + if (!classOrInterfaceType.getNameAsString().endsWith(suffix)) { + return Optional.of(new Diagnostic(WARNING, makeId(methodDeclaration), + "Incorrect type being supplied to this builder method. Expected a type ending with '" + + suffix + "' but was " + classOrInterfaceType.getNameAsString() + ".")); + } + return Optional.empty(); + } +} 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 e71027c5966..2781c90adda 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 @@ -13,6 +13,9 @@ */ public class ApiViewProperties { + @JsonProperty("flavor") + 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<>(); @@ -32,4 +35,8 @@ public Optional getCrossLanguageDefinitionId(String fullyQualifiedName) public Map getCrossLanguageDefinitionIds() { return Collections.unmodifiableMap(crossLanguageDefinitionIds); } + + public Flavor getFlavor() { + return flavor; + } } 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 new file mode 100644 index 00000000000..9563c4c5e8d --- /dev/null +++ b/src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/model/Flavor.java @@ -0,0 +1,66 @@ +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); + + private final String packagePrefix; + + private Flavor(String packagePrefix) { + this.packagePrefix = packagePrefix; + } + + public String getPackagePrefix() { + return packagePrefix; + } + + public static Flavor getFlavor(APIListing apiListing) { + Flavor flavor = apiListing.getApiViewProperties().getFlavor(); + if (flavor != null) { + return flavor; + } + + // we are reviewing a library that does not have a flavor metadata in its apiview_properties.json file, + // so we will use alternate means to determine the flavor. + + // Firstly, check the package name - does it start with one of the known package prefixes? + if (apiListing.getPackageName().startsWith(AZURE.getPackagePrefix())) { + return AZURE; + } else if (apiListing.getPackageName().startsWith(GENERIC.getPackagePrefix())) { + return GENERIC; + } + + // we still don't know the flavor, so the next thing we can do is look at the dependencies of the library + // to see if it brings in com.azure or io.clientcore libraries. + int azureCount = 0; + int genericCount = 0; + for (Dependency dependency : apiListing.getMavenPom().getDependencies()) { + if (dependency.getGroupId().equals(AZURE.getPackagePrefix())) { + // if we have azure-core, then we are an azure library and we bail + if (dependency.getArtifactId().equals("azure-core")) { + return AZURE; + } + azureCount++; + } else if (dependency.getGroupId().equals(GENERIC.getPackagePrefix())) { + // if we have 'core', then we are a clientcore library and we bail + if (dependency.getArtifactId().equals("core")) { + return GENERIC; + } + genericCount++; + } + } + + // 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/test/java/com/azure/tools/apiview/processor/diagnostics/rules/MavenPackageAndDescriptionDiagnosticRuleTests.java b/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/MavenPackageAndDescriptionDiagnosticRuleTests.java similarity index 94% rename from src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/MavenPackageAndDescriptionDiagnosticRuleTests.java rename to src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/MavenPackageAndDescriptionDiagnosticRuleTests.java index 3d66aaeb483..5e437741102 100644 --- a/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/MavenPackageAndDescriptionDiagnosticRuleTests.java +++ b/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/azure/MavenPackageAndDescriptionDiagnosticRuleTests.java @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.azure; +import com.azure.tools.apiview.processor.diagnostics.rules.azure.MavenPackageAndDescriptionDiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; import com.azure.tools.apiview.processor.model.maven.Pom; import org.junit.jupiter.api.Test; import java.util.regex.Pattern; -import static com.azure.tools.apiview.processor.diagnostics.rules.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_DESCRIPTION; -import static com.azure.tools.apiview.processor.diagnostics.rules.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_DESCRIPTION_GUIDELINE_LINK; -import static com.azure.tools.apiview.processor.diagnostics.rules.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_NAME; -import static com.azure.tools.apiview.processor.diagnostics.rules.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_NAME_GUIDELINE_LINK; +import static com.azure.tools.apiview.processor.diagnostics.rules.azure.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_DESCRIPTION; +import static com.azure.tools.apiview.processor.diagnostics.rules.azure.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_DESCRIPTION_GUIDELINE_LINK; +import static com.azure.tools.apiview.processor.diagnostics.rules.azure.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_NAME; +import static com.azure.tools.apiview.processor.diagnostics.rules.azure.MavenPackageAndDescriptionDiagnosticRule.DEFAULT_MAVEN_NAME_GUIDELINE_LINK; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; diff --git a/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/NoLocalesInJavadocUrlDiagnosticRuleTests.java b/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRuleTests.java similarity index 94% rename from src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/NoLocalesInJavadocUrlDiagnosticRuleTests.java rename to src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRuleTests.java index 04ef51d1a1b..17125ce2325 100644 --- a/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/NoLocalesInJavadocUrlDiagnosticRuleTests.java +++ b/src/java/apiview-java-processor/src/test/java/com/azure/tools/apiview/processor/diagnostics/rules/general/NoLocalesInJavadocUrlDiagnosticRuleTests.java @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.tools.apiview.processor.diagnostics.rules; +package com.azure.tools.apiview.processor.diagnostics.rules.general; +import com.azure.tools.apiview.processor.diagnostics.rules.general.NoLocalesInJavadocUrlDiagnosticRule; import com.azure.tools.apiview.processor.model.APIListing; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.comments.Comment; diff --git a/tools/apiview/parsers/cpp-api-parser/ApiViewProcessor/AstNode.cpp b/tools/apiview/parsers/cpp-api-parser/ApiViewProcessor/AstNode.cpp index 7f3c90761dc..bef846e88f3 100644 --- a/tools/apiview/parsers/cpp-api-parser/ApiViewProcessor/AstNode.cpp +++ b/tools/apiview/parsers/cpp-api-parser/ApiViewProcessor/AstNode.cpp @@ -1318,6 +1318,45 @@ static std::string GetNavigationId(clang::FunctionDecl const* func) return navigationId; } +static std::string GetNavigationId( + clang::FriendDecl const* friendDecl, + std::string const& parentNodeNavigationId) +{ + std::string navigationId = parentNodeNavigationId + "_friend_"; + auto dc = friendDecl->getFriendDecl(); + if (dc) + { + if (isa(dc)) + { + auto fd = cast(dc); + navigationId += GetNavigationId(fd); + } + else if (isa(dc)) + { + auto rd = cast(dc); + navigationId += rd->getQualifiedNameAsString(); + } + else + { + dc->dump(llvm::outs()); + } + } + else + { + auto friendType = friendDecl->getFriendType(); + if (friendType) + { + navigationId + += QualType::getAsString(friendDecl->getFriendType()->getType().split(), LangOptions{}); + } + else + { + friendDecl->dump(llvm::outs()); + } + } + return navigationId; +} + static std::string GetNavigationId(clang::ParmVarDecl const* param) { @@ -2690,6 +2729,7 @@ class AstField : public AstNamedNode { class AstFriend : public AstNode { std::string m_friendType; + std::string m_navigationId; std::unique_ptr m_friendFunction; public: @@ -2727,6 +2767,7 @@ class AstFriend : public AstNode { { if (ShouldIncludeFriendDeclaration(friendDecl)) { + m_navigationId = GetNavigationId(friendDecl, parentNode->NavigationId); if (friendDecl->getFriendType()) { m_friendType @@ -2763,7 +2804,7 @@ class AstFriend : public AstNode { } else { - dumper->InsertTypeName(m_friendType, "friend_" + m_friendType); + dumper->InsertTypeName(m_friendType, m_navigationId); if (dumpOptions.NeedsTrailingSemi) { dumper->InsertPunctuation(';');