Merge pull request #63 from laurentschoelens/java9

jdk9 : bugfixes and update readme
This commit is contained in:
Diomidis Spinellis 2023-03-27 17:53:37 +03:00 committed by GitHub
commit 62acb7337b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 120 additions and 91 deletions

View File

@ -21,12 +21,9 @@ Visit the project's [home page](http://www.spinellis.gr/umlgraph) for more infor
## Compatibility ## Compatibility
Currently, only Java 8 is supported by the Doclet. If you build against Java 8, please use latest version of 5.X of the Doclet.
In Java 9 the JavaDoc Doclet API changed substantially, and the doclet therefore Since Java 9 doclet APIs where completely rewritten, the Doclet in version 6 supports only supports Java 9 and above.
needs to be largely rewritten.
Sorry, this has not happened yet - volunteers for this task are welcome.
## Development versions ## Development versions

30
pom.xml
View File

@ -110,19 +110,19 @@
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version> <version>3.5.0</version>
<configuration> <configuration>
<tags> <tags>
<tag> <tag>
<name>depend</name> <name>depend</name>
<placement>X</placement> <placement>X</placement>
</tag> </tag>
<tag> <tag>
<name>hidden</name> <name>hidden</name>
<placement>X</placement> <placement>X</placement>
</tag> </tag>
<tag> <tag>
<name>opt</name> <name>opt</name>
<placement>X</placement> <placement>X</placement>
</tag> </tag>
</tags> </tags>
<doclet>org.umlgraph.doclet.UmlGraphDoc</doclet> <doclet>org.umlgraph.doclet.UmlGraphDoc</doclet>
<docletPath>${project.build.directory}${file.separator}${project.build.finalName}.jar</docletPath> <docletPath>${project.build.directory}${file.separator}${project.build.finalName}.jar</docletPath>
@ -134,12 +134,10 @@
<option>--hide="java.*"</option> <option>--hide="java.*"</option>
<option>--collpackages=</option> <option>--collpackages=</option>
<option>-qualify</option> <option>-qualify</option>
<option>-all</option>
<option>-postfixpackage</option> <option>-postfixpackage</option>
<option>-nodefontsize 9</option> <option>-nodefontsize 9</option>
<option>-nodefontpackagesize 7</option> <option>-nodefontpackagesize 7</option>
<option>--link="https://docs.oracle.com/javase/7/docs/jdk/api/javadoc/doclet/"</option> <option>--link="https://docs.oracle.com/javase/9/docs/api/"</option>
<option>--link="https://download.oracle.com/javase/7/docs/api/"</option>
</additionalOptions> </additionalOptions>
<detectJavaApiLink>false</detectJavaApiLink> <detectJavaApiLink>false</detectJavaApiLink>
<useStandardDocletOptions>true</useStandardDocletOptions> <useStandardDocletOptions>true</useStandardDocletOptions>

View File

@ -57,8 +57,6 @@ import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.NoType; import javax.lang.model.type.NoType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
import javax.tools.JavaFileManager; import javax.tools.JavaFileManager;
@ -501,8 +499,9 @@ class ClassGraph {
} }
} }
externalTableEnd(); externalTableEnd();
if (url != null) if (url != null) {
w.print(", URL=\"" + url + "\""); w.print(", URL=\"" + url + "\"");
}
nodeProperties(opt); nodeProperties(opt);
// If needed, add a note for this node // If needed, add a note for this node
@ -674,8 +673,9 @@ class ClassGraph {
} }
// Handle missing classes: // Handle missing classes:
Options opt = optionProvider.getOptionsFor(className); Options opt = optionProvider.getOptionsFor(className);
if (opt.matchesHideExpression(className)) if (opt.matchesHideExpression(className)) {
continue; continue;
}
w.println(linePrefix + "// " + className); w.println(linePrefix + "// " + className);
w.print(linePrefix + info.name + "[label="); w.print(linePrefix + info.name + "[label=");
externalTableStart(opt, className, classToUrl(className)); externalTableStart(opt, className, classToUrl(className));
@ -733,7 +733,7 @@ class ClassGraph {
} }
// if source and dest are not already linked, add a dependency // if source and dest are not already linked, add a dependency
RelationPattern rp = getClassInfo(c, true).getRelation(fri.cd.toString()); RelationPattern rp = getClassInfo(c, true).getRelation(fri.cd.getQualifiedName());
if (rp == null) { if (rp == null) {
String destAdornment = fri.multiple ? "*" : ""; String destAdornment = fri.multiple ? "*" : "";
relation(opt, opt.inferRelationshipType, c, fri.cd, "", "", destAdornment); relation(opt, opt.inferRelationshipType, c, fri.cd, "", "", destAdornment);
@ -810,7 +810,9 @@ class ClassGraph {
for (TypeMirror type : types) { for (TypeMirror type : types) {
// skip primitives and type variables, as well as dependencies // skip primitives and type variables, as well as dependencies
// on the source class // on the source class
if (type.getKind().isPrimitive() || type instanceof NoType || type instanceof WildcardType || type instanceof TypeVariable if (ElementUtil.isPrimitive(type)
|| ElementUtil.isType(type, TypeKind.TYPEVAR) || ElementUtil.isType(type, TypeKind.WILDCARD)
|| type instanceof NoType
|| c.toString().equals(ElementUtil.getTypeElement(type).toString())) { || c.toString().equals(ElementUtil.getTypeElement(type).toString())) {
continue; continue;
} }
@ -828,7 +830,7 @@ class ClassGraph {
} }
// if source and dest are not already linked, add a dependency // if source and dest are not already linked, add a dependency
RelationPattern rp = getClassInfo(c, true).getRelation(fc.toString()); RelationPattern rp = getClassInfo(c, true).getRelation(fc.getQualifiedName());
if (rp == null || rp.matchesOne(new RelationPattern(RelationDirection.OUT))) { if (rp == null || rp.matchesOne(new RelationPattern(RelationDirection.OUT))) {
relation(opt, RelationType.DEPEND, c, fc, "", "", ""); relation(opt, RelationType.DEPEND, c, fc, "", "", "");
} }
@ -856,10 +858,10 @@ class ClassGraph {
private FieldRelationInfo getFieldRelationInfo(VariableElement field) { private FieldRelationInfo getFieldRelationInfo(VariableElement field) {
TypeMirror type = field.asType(); TypeMirror type = field.asType();
if (type.getKind().isPrimitive() || type.getKind() == TypeKind.WILDCARD || type.getKind() == TypeKind.TYPEVAR) { if (ElementUtil.isPrimitive(type) || ElementUtil.isType(type, TypeKind.WILDCARD) || ElementUtil.isType(type, TypeKind.TYPEVAR)) {
return null; return null;
} }
if (ElementUtil.dimensions(type).endsWith("[]")) { if (ElementUtil.dimensions(type).endsWith("[]")) {
return new FieldRelationInfo(ElementUtil.getTypeElement(type), true); return new FieldRelationInfo(ElementUtil.getTypeElement(type), true);
} }
@ -867,35 +869,49 @@ class ClassGraph {
Options opt = optionProvider.getOptionsFor(docTrees, ElementUtil.getTypeElement(type)); Options opt = optionProvider.getOptionsFor(docTrees, ElementUtil.getTypeElement(type));
if (opt.matchesCollPackageExpression(ElementUtil.getQualifiedName(types, type))) { if (opt.matchesCollPackageExpression(ElementUtil.getQualifiedName(types, type))) {
List<? extends TypeMirror> argTypes = getInterfaceTypeArguments(collectionClassDoc, type); List<? extends TypeMirror> argTypes = getInterfaceTypeArguments(collectionClassDoc, type);
if (argTypes != null && argTypes.size() == 1 && !argTypes.get(0).getKind().isPrimitive()) { if (argTypes != null && argTypes.size() == 1 && !argTypes.get(0).getKind().isPrimitive() && !ElementUtil.isType(argTypes.get(0), TypeKind.TYPEVAR)) {
return new FieldRelationInfo(ElementUtil.getTypeElement(argTypes.get(0)), true); TypeMirror arg = argTypes.get(0);
return new FieldRelationInfo(ElementUtil.getTypeElement(arg), true);
} }
argTypes = getInterfaceTypeArguments(mapClassDoc, type); argTypes = getInterfaceTypeArguments(mapClassDoc, type);
if (argTypes != null && argTypes.size() == 2 && !argTypes.get(1).getKind().isPrimitive()) { if (argTypes != null && argTypes.size() == 2 && !argTypes.get(1).getKind().isPrimitive() && !ElementUtil.isType(argTypes.get(1), TypeKind.TYPEVAR)) {
return new FieldRelationInfo(ElementUtil.getTypeElement(argTypes.get(1)), true); TypeMirror arg = argTypes.get(1);
return new FieldRelationInfo(ElementUtil.getTypeElement(arg), true);
} }
} }
return new FieldRelationInfo(ElementUtil.getTypeElement(type), false); return new FieldRelationInfo(ElementUtil.getTypeElement(type), false);
} }
private List<? extends TypeMirror> getInterfaceTypeArguments(TypeElement iface, TypeMirror t) { private List<? extends TypeMirror> getInterfaceTypeArguments(Element iface, TypeMirror t) {
if (t instanceof DeclaredType) { if (t instanceof DeclaredType) {
DeclaredType pt = (DeclaredType) t; DeclaredType pt = (DeclaredType) t;
if (iface != null && iface.equals(pt.asElement())) { if (iface != null && iface.equals(pt.asElement())) {
return pt.getTypeArguments(); return pt.getTypeArguments();
} else { } else {
for (TypeMirror pti : ElementUtil.getInterfacesTypes(iface)) { for (TypeMirror pti : ElementUtil.getInterfacesTypes(pt.asElement())) {
List<? extends TypeMirror> result = getInterfaceTypeArguments(iface, pti); List<? extends TypeMirror> result = getInterfaceTypeArguments(iface, pti);
if (result != null) { if (result != null) {
return result; return result;
} }
} }
if (ElementUtil.getSuperclassType(pt) != null) { TypeMirror superType = ElementUtil.getSuperclassType(pt);
return getInterfaceTypeArguments(iface, ElementUtil.getSuperclassType(pt)); if (superType != null && superType.getKind() != TypeKind.NONE) {
return getInterfaceTypeArguments(iface, superType);
} }
} }
} else if (iface instanceof TypeElement) {
for (TypeMirror pti : ElementUtil.getInterfacesTypes((TypeElement) iface)) {
List<? extends TypeMirror> result = getInterfaceTypeArguments(iface, pti);
if (result != null) {
return result;
}
}
TypeElement superType = ElementUtil.getSuperclass((TypeElement) iface);
if (superType != null && superType.asType().getKind() != TypeKind.NONE) {
return getInterfaceTypeArguments(iface, superType.asType());
}
} }
return null; return null;
} }

View File

@ -44,7 +44,7 @@ class ClassInfo {
* classes linked with a bi-directional relation , and the ones referred by a * classes linked with a bi-directional relation , and the ones referred by a
* directed relation * directed relation
*/ */
Map<Name, RelationPattern> relatedClasses = new HashMap<>(); Map<String, RelationPattern> relatedClasses = new HashMap<>();
ClassInfo(boolean h) { ClassInfo(boolean h) {
hidden = h; hidden = h;
@ -53,16 +53,16 @@ class ClassInfo {
} }
public void addRelation(Name dest, RelationType rt, RelationDirection d) { public void addRelation(Name dest, RelationType rt, RelationDirection d) {
RelationPattern ri = relatedClasses.get(dest); RelationPattern ri = relatedClasses.get(dest.toString());
if (ri == null) { if (ri == null) {
ri = new RelationPattern(RelationDirection.NONE); ri = new RelationPattern(RelationDirection.NONE);
relatedClasses.put(dest, ri); relatedClasses.put(dest.toString(), ri);
} }
ri.addRelation(rt, d); ri.addRelation(rt, d);
} }
public RelationPattern getRelation(CharSequence dest) { public RelationPattern getRelation(CharSequence dest) {
return relatedClasses.get(dest); return relatedClasses.get(dest.toString());
} }
/** Start numbering from zero. */ /** Start numbering from zero. */

View File

@ -61,7 +61,7 @@ public class ContextView implements OptionProvider {
this.centerOptions.nodeFillColor = "lemonChiffon"; this.centerOptions.nodeFillColor = "lemonChiffon";
this.centerOptions.showQualified = false; this.centerOptions.showQualified = false;
this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.getQualifiedName().toString())), myGlobalOptions, true); this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.toString())), myGlobalOptions, true);
} }
@ -103,7 +103,7 @@ public class ContextView implements OptionProvider {
Options opt; Options opt;
if (!matcher.matches(name)) { if (!matcher.matches(name)) {
opt = hideOptions; opt = hideOptions;
} else if (name.equals(cd.getQualifiedName())) { } else if (name.equals(cd.getSimpleName())) {
opt = centerOptions; opt = centerOptions;
} else { } else {
opt = globalOptions; opt = globalOptions;

View File

@ -589,7 +589,7 @@ public class Options implements Cloneable, OptionProvider {
// reused often, especially in UmlGraphDoc, worth creating just once and reusing // reused often, especially in UmlGraphDoc, worth creating just once and reusing
private static final Pattern allPattern = Pattern.compile(".*"); private static final Pattern allPattern = Pattern.compile(".*");
protected static final String DEFAULT_EXTERNAL_APIDOC = "http://docs.oracle.com/javase/7/docs/api/"; protected static final String DEFAULT_EXTERNAL_APIDOC = "https://docs.oracle.com/javase/9/docs/api/";
// instance fields // instance fields
List<Pattern> hidePatterns = new ArrayList<>(); List<Pattern> hidePatterns = new ArrayList<>();
@ -975,28 +975,37 @@ public class Options implements Cloneable, OptionProvider {
* @param packageListUrl * @param packageListUrl
*/ */
private void addApiDocRoots(String packageListUrl) { private void addApiDocRoots(String packageListUrl) {
tryFetch(null, packageListUrl);
}
private void tryFetch(String docUrl, String packageListUrl) {
BufferedReader br = null; BufferedReader br = null;
packageListUrl = fixApiDocRoot(packageListUrl); packageListUrl = fixApiDocRoot(packageListUrl);
try { String finalDocUrl = docUrl == null ? null : fixApiDocRoot(docUrl);
URL url = new URL(packageListUrl + "package-list"); for (String suffix : List.of("package-list", "element-list")) {
br = new BufferedReader(new InputStreamReader(url.openStream())); try {
String line; URL url = new URL(packageListUrl + suffix);
while ((line = br.readLine()) != null) { br = new BufferedReader(new InputStreamReader(url.openStream()));
line = line + "."; String line;
Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*"); while ((line = br.readLine()) != null) {
apiDocMap.put(pattern, packageListUrl); if (!line.startsWith("module:")) {
} line = line + ".";
} catch (IOException e) { Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*");
System.err.println("Errors happened while accessing the package-list file at " + packageListUrl); apiDocMap.put(pattern, finalDocUrl == null ? packageListUrl : finalDocUrl);
} finally { }
if (br != null) { }
try { break;
br.close(); } catch (IOException e) {
} catch (IOException e) { System.err.println("Errors happened while accessing the " + suffix + " file at " + packageListUrl);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
}
} }
} }
} }
} }
/** /**
@ -1008,27 +1017,7 @@ public class Options implements Cloneable, OptionProvider {
* @param packageListUrl folder containing the package-list * @param packageListUrl folder containing the package-list
*/ */
private void addApiDocRootsOffline(String docUrl, String packageListUrl) { private void addApiDocRootsOffline(String docUrl, String packageListUrl) {
BufferedReader br = null; tryFetch(docUrl, packageListUrl);
packageListUrl = fixApiDocRoot(packageListUrl);
try {
URL url = new URL(packageListUrl + "package-list");
br = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = br.readLine()) != null) {
line = line + ".";
Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*");
apiDocMap.put(pattern, fixApiDocRoot(docUrl));
}
} catch (IOException e) {
System.err.println("Unable to access the package-list file at " + packageListUrl);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
}
}
}
} }
/** /**
@ -1161,8 +1150,9 @@ public class Options implements Cloneable, OptionProvider {
public boolean matchesCollPackageExpression(CharSequence s) { public boolean matchesCollPackageExpression(CharSequence s) {
for (Pattern collPattern : collPackages) { for (Pattern collPattern : collPackages) {
Matcher m = collPattern.matcher(s); Matcher m = collPattern.matcher(s);
if (strictMatching ? m.matches() : m.find()) if (strictMatching ? m.matches() : m.find()) {
return true; return true;
}
} }
return false; return false;
} }

View File

@ -123,13 +123,12 @@ public class UmlGraphDoc implements Doclet {
continue; continue;
} }
if (!packages.contains(packageDoc.getSimpleName())) { if (!packages.contains(packageDoc.getSimpleName())) {
reporter.print(Diagnostic.Kind.WARNING, "Package processed " + packageDoc.getQualifiedName());
packages.add(packageDoc.getSimpleName()); packages.add(packageDoc.getSimpleName());
OptionProvider view = new PackageView(outputFolder, packageDoc, root, opt); OptionProvider view = new PackageView(outputFolder, packageDoc, root, opt);
UmlGraph.buildGraph(reporter, root, opt, view, packageDoc); UmlGraph.buildGraph(reporter, root, opt, view, packageDoc);
runGraphviz(opt.dotExecutable, outputFolder, ElementUtil.getModuleOf(root, packageDoc), packageDoc.getQualifiedName(), packageDoc.getSimpleName(), reporter); runGraphviz(opt.dotExecutable, outputFolder, ElementUtil.getModuleOf(root, packageDoc), packageDoc.getQualifiedName(), packageDoc.getSimpleName(), reporter);
alterHtmlDocs(opt, outputFolder, ElementUtil.getModuleOf(root, packageDoc), packageDoc.getQualifiedName(), packageDoc.getSimpleName(), "package-summary.html", alterHtmlDocs(opt, outputFolder, ElementUtil.getModuleOf(root, packageDoc), packageDoc.getQualifiedName(), packageDoc.getSimpleName(), "package-summary.html",
Pattern.compile("(</[Hh]2>)|(<h1 title=\"Package\").*"), reporter); Pattern.compile("(</[Hh]2>)|(<h1 title=\"Package( |\")).*"), reporter);
} }
} }
} }
@ -151,7 +150,6 @@ public class UmlGraphDoc implements Doclet {
ContextView view = null; ContextView view = null;
for (TypeElement classDoc : classDocs) { for (TypeElement classDoc : classDocs) {
reporter.print(Diagnostic.Kind.WARNING, "Class processed " + classDoc.getQualifiedName());
try { try {
if (view == null) { if (view == null) {
view = new ContextView(outputFolder, classDoc, root, opt); view = new ContextView(outputFolder, classDoc, root, opt);

View File

@ -1,5 +1,6 @@
package org.umlgraph.doclet.util; package org.umlgraph.doclet.util;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -21,6 +22,26 @@ import javax.lang.model.util.Types;
import jdk.javadoc.doclet.DocletEnvironment; import jdk.javadoc.doclet.DocletEnvironment;
public class ElementUtil { public class ElementUtil {
public static boolean isPrimitive(TypeMirror type) {
if (type.getKind().isPrimitive()) {
return true;
}
if (type.getKind() == TypeKind.ARRAY) {
return isPrimitive(((ArrayType) type).getComponentType());
}
return false;
}
public static boolean isType(TypeMirror type, TypeKind kind) {
if (type.getKind() == kind) {
return true;
}
if (type.getKind() == TypeKind.ARRAY) {
return isType(((ArrayType) type).getComponentType(), kind);
}
return false;
}
public static CharSequence getSimpleName(Types types, TypeMirror t) { public static CharSequence getSimpleName(Types types, TypeMirror t) {
if (t instanceof ArrayType) { if (t instanceof ArrayType) {
@ -71,13 +92,19 @@ public class ElementUtil {
return null; return null;
} }
if (clazz instanceof ArrayType) { if (clazz instanceof ArrayType) {
return getTypeElement(((ArrayType) clazz).getComponentType()); TypeMirror componentType = ((ArrayType) clazz).getComponentType();
return getTypeElement(componentType);
} }
if (!(clazz instanceof DeclaredType)) { if (!(clazz instanceof DeclaredType)) {
System.err.println("getTypeElement will return null for type class : " + clazz + " instanceof " + clazz.getClass() + " and kind " + clazz.getKind());
return null; return null;
} }
Element scd = ((DeclaredType) clazz).asElement(); Element scd = ((DeclaredType) clazz).asElement();
return scd instanceof TypeElement ? (TypeElement) scd : null; if (!(scd instanceof TypeElement)) {
System.err.println("getTypeElement will return null for element class : " + scd == null ? "null" : scd.getClass());
return null;
}
return (TypeElement) scd;
} }
public static TypeMirror getSuperclassType(DeclaredType pt) { public static TypeMirror getSuperclassType(DeclaredType pt) {
@ -88,8 +115,11 @@ public class ElementUtil {
return getTypeElement(element.getSuperclass()); return getTypeElement(element.getSuperclass());
} }
public static List<? extends TypeMirror> getInterfacesTypes(TypeElement element) { public static List<? extends TypeMirror> getInterfacesTypes(Element element) {
return element.getInterfaces(); if (element instanceof TypeElement) {
return ((TypeElement) element).getInterfaces();
}
return Collections.emptyList();
} }
public static List<TypeElement> getInterfaces(TypeElement element) { public static List<TypeElement> getInterfaces(TypeElement element) {