Skip to content

Commit bca1ac5

Browse files
Support for client core libraries (#7955)
Improving APIView to support non-Azure libraries, in particular the new generic core
1 parent e99e474 commit bca1ac5

36 files changed

Lines changed: 656 additions & 218 deletions

src/java/apiview-java-processor/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
<dependency>
2121
<groupId>com.fasterxml.jackson.core</groupId>
2222
<artifactId>jackson-databind</artifactId>
23-
<version>2.13.4.2</version>
23+
<version>2.17.0</version>
2424
</dependency>
2525

2626
<dependency>
2727
<groupId>com.github.javaparser</groupId>
2828
<artifactId>javaparser-symbol-solver-core</artifactId>
29-
<version>3.24.2</version>
29+
<version>3.25.9</version>
3030
</dependency>
3131

3232
<dependency>

src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/Main.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.fasterxml.jackson.annotation.JsonInclude;
99
import com.fasterxml.jackson.databind.ObjectMapper;
1010
import com.fasterxml.jackson.databind.ObjectWriter;
11+
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
1112

1213
import java.io.File;
1314
import java.io.FileInputStream;
@@ -164,8 +165,6 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing)
164165
}
165166
System.out.println(" Using '" + apiListing.getLanguageVariant() + "' for the language variant");
166167

167-
final Analyser analyser = new JavaASTAnalyser(apiListing);
168-
169168
// Read all files within the jar file so that we can create a list of files to analyse
170169
final List<Path> allFiles = new ArrayList<>();
171170
try (FileSystem fs = FileSystems.newFileSystem(inputFile.toPath(), Main.class.getClassLoader())) {
@@ -179,6 +178,9 @@ private static void processJavaSourcesJar(File inputFile, APIListing apiListing)
179178
apiListing.setApiViewProperties(properties);
180179
System.out.println(" Found apiview_properties.json file in jar file");
181180
System.out.println(" - Found " + properties.getCrossLanguageDefinitionIds().size() + " cross-language definition IDs");
181+
} catch (InvalidFormatException e) {
182+
System.out.println(" ERROR: Unable to parse apiview_properties.json file in jar file");
183+
e.printStackTrace();
182184
} catch (Exception e) {
183185
// this is fine, we just won't have any APIView properties to read in
184186
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)
194196
});
195197

196198
// Do the analysis while the filesystem is still represented in memory
199+
final Analyser analyser = new JavaASTAnalyser(apiListing);
197200
analyser.analyse(allFiles);
198201
} catch (Exception e) {
199202
e.printStackTrace();

src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/JavaASTAnalyser.java

Lines changed: 98 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.azure.tools.apiview.processor.analysers;
22

3+
import com.azure.tools.apiview.processor.analysers.models.AnnotationRendererModel;
4+
import com.azure.tools.apiview.processor.analysers.models.AnnotationRule;
35
import com.azure.tools.apiview.processor.analysers.util.MiscUtils;
46
import com.azure.tools.apiview.processor.analysers.util.TokenModifier;
57
import com.azure.tools.apiview.processor.diagnostics.Diagnostics;
@@ -51,19 +53,10 @@
5153
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
5254
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
5355

54-
import java.io.File;
5556
import java.io.IOException;
5657
import java.nio.file.Files;
5758
import java.nio.file.Path;
58-
import java.util.Arrays;
59-
import java.util.Comparator;
60-
import java.util.HashMap;
61-
import java.util.HashSet;
62-
import java.util.List;
63-
import java.util.Map;
64-
import java.util.Optional;
65-
import java.util.Set;
66-
import java.util.TreeMap;
59+
import java.util.*;
6760
import java.util.concurrent.atomic.AtomicInteger;
6861
import java.util.function.Consumer;
6962
import java.util.regex.Matcher;
@@ -75,6 +68,7 @@
7568
import org.apache.commons.lang.StringEscapeUtils;
7669

7770
import static com.azure.tools.apiview.processor.analysers.util.ASTUtils.*;
71+
import static com.azure.tools.apiview.processor.analysers.util.MiscUtils.*;
7872
import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.INDENT;
7973
import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.NEWLINE;
8074
import static com.azure.tools.apiview.processor.analysers.util.TokenModifier.NOTHING;
@@ -101,8 +95,22 @@ public class JavaASTAnalyser implements Analyser {
10195
public static final String MODULE_INFO_KEY = "module-info";
10296

10397
private static final boolean SHOW_JAVADOC = true;
104-
private static final Set<String> BLOCKED_ANNOTATIONS =
105-
new HashSet<>(Arrays.asList("ServiceMethod", "SuppressWarnings"));
98+
99+
private static final Map<String, AnnotationRule> ANNOTATION_RULE_MAP;
100+
static {
101+
/*
102+
For some annotations, we want to customise how they are displayed. Sometimes, we don't show them in any
103+
circumstance. Other times, we want to show them but not their attributes. This map is used to define these
104+
customisations. These rules override the default output that APIView will do, based on the location
105+
annotation in the code.
106+
*/
107+
ANNOTATION_RULE_MAP = new HashMap<>();
108+
ANNOTATION_RULE_MAP.put("ServiceMethod", new AnnotationRule().setHidden(true));
109+
ANNOTATION_RULE_MAP.put("SuppressWarnings", new AnnotationRule().setHidden(true));
110+
111+
// we always want @Metadata annotations to be fully expanded, but in a condensed form
112+
ANNOTATION_RULE_MAP.put("Metadata", new AnnotationRule().setShowProperties(true).setCondensed(true));
113+
}
106114

107115
private static final Pattern SPLIT_NEWLINE = Pattern.compile(MiscUtils.LINEBREAK);
108116

@@ -112,12 +120,13 @@ public class JavaASTAnalyser implements Analyser {
112120

113121
private final Map<String, JavadocComment> packageNameToPackageInfoJavaDoc = new HashMap<>();
114122

115-
private final Diagnostics diagnostic = new Diagnostics();
123+
private final Diagnostics diagnostic;
116124

117125
private int indent = 0;
118126

119127
public JavaASTAnalyser(APIListing apiListing) {
120128
this.apiListing = apiListing;
129+
diagnostic = new Diagnostics(apiListing);
121130
}
122131

123132
@Override
@@ -1032,34 +1041,24 @@ private void getAnnotations(final NodeWithAnnotations<?> nodeWithAnnotations,
10321041
final boolean showAnnotationProperties,
10331042
final boolean addNewline) {
10341043
Consumer<AnnotationExpr> consumer = annotation -> {
1035-
if (addNewline) {
1036-
addToken(makeWhitespace());
1037-
}
1038-
1039-
addToken(new Token(TYPE_NAME, "@" + annotation.getName().toString(), makeId(annotation, nodeWithAnnotations)));
1040-
if (showAnnotationProperties) {
1041-
if (annotation instanceof NormalAnnotationExpr) {
1042-
addToken(new Token(PUNCTUATION, "("));
1043-
NodeList<MemberValuePair> pairs = ((NormalAnnotationExpr) annotation).getPairs();
1044-
for (int i = 0; i < pairs.size(); i++) {
1045-
MemberValuePair pair = pairs.get(i);
1046-
1047-
addToken(new Token(TEXT, pair.getNameAsString()));
1048-
addToken(new Token(PUNCTUATION, " = "));
1044+
// Check the annotation rules map for any overrides
1045+
final String annotationName = annotation.getName().toString();
1046+
AnnotationRule annotationRule = ANNOTATION_RULE_MAP.get(annotationName);
10491047

1050-
Expression valueExpr = pair.getValue();
1051-
processAnnotationValueExpression(valueExpr);
1048+
AnnotationRendererModel model = new AnnotationRendererModel(
1049+
annotation, nodeWithAnnotations, annotationRule, showAnnotationProperties, addNewline);
10521050

1053-
if (i < pairs.size() - 1) {
1054-
addToken(new Token(PUNCTUATION, ", "));
1055-
}
1056-
}
1051+
if (model.isHidden()) {
1052+
return;
1053+
}
10571054

1058-
addToken(new Token(PUNCTUATION, ")"));
1059-
}
1055+
if (model.isAddNewline()) {
1056+
addToken(makeWhitespace());
10601057
}
10611058

1062-
if (addNewline) {
1059+
renderAnnotation(model).forEach(JavaASTAnalyser.this::addToken);
1060+
1061+
if (model.isAddNewline()) {
10631062
addNewLine();
10641063
} else {
10651064
addToken(new Token(WHITESPACE, " "));
@@ -1070,43 +1069,96 @@ private void getAnnotations(final NodeWithAnnotations<?> nodeWithAnnotations,
10701069
.stream()
10711070
.filter(annotationExpr -> {
10721071
String id = annotationExpr.getName().getIdentifier();
1073-
return !BLOCKED_ANNOTATIONS.contains(id) && !id.startsWith("Json");
1072+
return !id.startsWith("Json");
10741073
})
10751074
.sorted(Comparator.comparing(a -> a.getName().getIdentifier())) // we sort the annotations alphabetically
10761075
.forEach(consumer);
10771076
}
10781077

1079-
private void processAnnotationValueExpression(Expression valueExpr) {
1078+
private List<Token> renderAnnotation(AnnotationRendererModel m) {
1079+
final AnnotationExpr a = m.getAnnotation();
1080+
List<Token> tokens = new ArrayList<>();
1081+
tokens.add(new Token(TYPE_NAME, "@" + a.getNameAsString(), makeId(a, m.getAnnotationParent())));
1082+
if (m.isShowProperties()) {
1083+
if (a instanceof NormalAnnotationExpr) {
1084+
tokens.add(new Token(PUNCTUATION, "("));
1085+
NodeList<MemberValuePair> pairs = ((NormalAnnotationExpr) a).getPairs();
1086+
for (int i = 0; i < pairs.size(); i++) {
1087+
MemberValuePair pair = pairs.get(i);
1088+
1089+
// If the pair is a boolean expression, and we are condensed, we only take the name.
1090+
// If we are not a boolean expression, and we are condensed, we only take the value.
1091+
// If we are not condensed, we take both.
1092+
final Expression valueExpr = pair.getValue();
1093+
if (m.isCondensed()) {
1094+
if (valueExpr.isBooleanLiteralExpr()) {
1095+
tokens.add(new Token(MEMBER_NAME, upperCase(pair.getNameAsString())));
1096+
} else {
1097+
processAnnotationValueExpression(valueExpr, m.isCondensed(), tokens);
1098+
}
1099+
} else {
1100+
tokens.add(new Token(MEMBER_NAME, pair.getNameAsString()));
1101+
tokens.add(new Token(PUNCTUATION, " = "));
1102+
1103+
processAnnotationValueExpression(valueExpr, m.isCondensed(), tokens);
1104+
}
1105+
1106+
if (i < pairs.size() - 1) {
1107+
tokens.add(new Token(PUNCTUATION, ", "));
1108+
}
1109+
}
1110+
1111+
tokens.add(new Token(PUNCTUATION, ")"));
1112+
}
1113+
}
1114+
return tokens;
1115+
}
1116+
1117+
private void processAnnotationValueExpression(final Expression valueExpr, final boolean condensed, final List<Token> tokens) {
10801118
if (valueExpr.isClassExpr()) {
10811119
// lookup to see if the type is known about, if so, make it a link, otherwise leave it as text
10821120
String typeName = valueExpr.getChildNodes().get(0).toString();
10831121
if (apiListing.getKnownTypes().containsKey(typeName)) {
10841122
final Token token = new Token(TYPE_NAME, typeName);
10851123
token.setNavigateToId(apiListing.getKnownTypes().get(typeName));
1086-
addToken(token);
1124+
tokens.add(token);
10871125
return;
10881126
}
10891127
} else if (valueExpr.isArrayInitializerExpr()) {
1090-
addToken(new Token(PUNCTUATION, "{ "));
1128+
if (!condensed) {
1129+
tokens.add(new Token(PUNCTUATION, "{ "));
1130+
}
10911131
for (int i = 0; i < valueExpr.getChildNodes().size(); i++) {
10921132
Node n = valueExpr.getChildNodes().get(i);
10931133

10941134
if (n instanceof Expression) {
1095-
processAnnotationValueExpression((Expression) n);
1135+
processAnnotationValueExpression((Expression) n, condensed, tokens);
10961136
} else {
1097-
addToken(new Token(TEXT, valueExpr.toString()));
1137+
tokens.add(new Token(TEXT, valueExpr.toString()));
10981138
}
10991139

11001140
if (i < valueExpr.getChildNodes().size() - 1) {
1101-
addToken(new Token(PUNCTUATION, ", "));
1141+
tokens.add(new Token(PUNCTUATION, ", "));
11021142
}
11031143
}
1104-
addToken(new Token(PUNCTUATION, " }"));
1144+
if (!condensed) {
1145+
tokens.add(new Token(PUNCTUATION, " }"));
1146+
}
11051147
return;
11061148
}
11071149

1108-
// if we fall through to here, just treat it as a string
1109-
addToken(new Token(TEXT, valueExpr.toString()));
1150+
// if we fall through to here, just treat it as a string.
1151+
// If we are in condensed mode, we strip off everything before the last period
1152+
String value = valueExpr.toString();
1153+
if (condensed) {
1154+
int lastPeriod = value.lastIndexOf('.');
1155+
if (lastPeriod != -1) {
1156+
value = value.substring(lastPeriod + 1);
1157+
}
1158+
tokens.add(new Token(TEXT, upperCase(value)));
1159+
} else {
1160+
tokens.add(new Token(TEXT, value));
1161+
}
11101162
}
11111163

11121164
private void getModifiers(NodeList<Modifier> modifiers) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.azure.tools.apiview.processor.analysers.models;
2+
3+
import com.github.javaparser.ast.expr.AnnotationExpr;
4+
import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations;
5+
6+
public class AnnotationRendererModel {
7+
private final AnnotationExpr annotation;
8+
9+
private final NodeWithAnnotations<?> annotationParent;
10+
11+
private final AnnotationRule rule;
12+
13+
private final boolean showProperties;
14+
15+
private final boolean addNewline;
16+
17+
public AnnotationRendererModel(AnnotationExpr annotation,
18+
NodeWithAnnotations<?> nodeWithAnnotations,
19+
AnnotationRule rule,
20+
boolean showAnnotationProperties,
21+
boolean addNewline) {
22+
this.annotation = annotation;
23+
this.annotationParent = nodeWithAnnotations;
24+
this.rule = rule;
25+
26+
// we override the showAnnotationProperties flag if the annotation rule specifies it
27+
this.showProperties = rule == null ? showAnnotationProperties : rule.isShowProperties().orElse(showAnnotationProperties);
28+
this.addNewline = rule == null ? addNewline : rule.isShowOnNewline().orElse(addNewline);
29+
}
30+
31+
public boolean isAddNewline() {
32+
return addNewline;
33+
}
34+
35+
public boolean isShowProperties() {
36+
return showProperties;
37+
}
38+
39+
public AnnotationExpr getAnnotation() {
40+
return annotation;
41+
}
42+
43+
public NodeWithAnnotations<?> getAnnotationParent() {
44+
return annotationParent;
45+
}
46+
47+
public boolean isHidden() {
48+
return rule != null && rule.isHidden().orElse(false);
49+
}
50+
51+
public boolean isCondensed() {
52+
return rule != null && rule.isCondensed().orElse(false);
53+
}
54+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.azure.tools.apiview.processor.analysers.models;
2+
3+
import java.util.Optional;
4+
5+
public class AnnotationRule {
6+
7+
private Optional<Boolean> hideAnnotation = Optional.empty();
8+
9+
private Optional<Boolean> showProperties = Optional.empty();
10+
11+
private Optional<Boolean> showOnNewline = Optional.empty();
12+
13+
private Optional<Boolean> condensed = Optional.empty();
14+
15+
public AnnotationRule setHidden(boolean hidden) {
16+
this.hideAnnotation = Optional.of(hidden);
17+
return this;
18+
}
19+
20+
public Optional<Boolean> isHidden() {
21+
return hideAnnotation;
22+
}
23+
24+
public AnnotationRule setShowProperties(boolean showProperties) {
25+
this.showProperties = Optional.of(showProperties);
26+
return this;
27+
}
28+
29+
public Optional<Boolean> isShowProperties() {
30+
return showProperties;
31+
}
32+
33+
public AnnotationRule setShowOnNewline(boolean showOnNewline) {
34+
this.showOnNewline = Optional.of(showOnNewline);
35+
return this;
36+
}
37+
38+
public Optional<Boolean> isShowOnNewline() {
39+
return showOnNewline;
40+
}
41+
42+
public AnnotationRule setCondensed(boolean condensed) {
43+
this.condensed = Optional.of(condensed);
44+
return this;
45+
}
46+
47+
public Optional<Boolean> isCondensed() {
48+
return condensed;
49+
}
50+
}

src/java/apiview-java-processor/src/main/java/com/azure/tools/apiview/processor/analysers/util/ASTUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ public static boolean isPublicOrProtected(AccessSpecifier accessSpecifier) {
181181
* @return Whether the access specifier is package-private or private.
182182
*/
183183
public static boolean isPrivateOrPackagePrivate(AccessSpecifier accessSpecifier) {
184-
return (accessSpecifier == AccessSpecifier.PRIVATE) || (accessSpecifier == AccessSpecifier.PACKAGE_PRIVATE);
184+
return (accessSpecifier == AccessSpecifier.PRIVATE) || (accessSpecifier == AccessSpecifier.NONE);
185185
}
186186

187187
public static String makeId(CompilationUnit cu) {

0 commit comments

Comments
 (0)