| " //
+ text // MAY contain markup!
@@ -956,10 +1028,10 @@ class ClassGraph {
}
private static class FieldRelationInfo {
- ClassDoc cd;
+ TypeElement cd;
boolean multiple;
- public FieldRelationInfo(ClassDoc cd, boolean multiple) {
+ public FieldRelationInfo(TypeElement cd, boolean multiple) {
this.cd = cd;
this.multiple = multiple;
}
diff --git a/src/main/java/org/umlgraph/doclet/ClassInfo.java b/src/main/java/org/umlgraph/doclet/ClassInfo.java
index ccb67e0..5939a0c 100644
--- a/src/main/java/org/umlgraph/doclet/ClassInfo.java
+++ b/src/main/java/org/umlgraph/doclet/ClassInfo.java
@@ -22,6 +22,8 @@ package org.umlgraph.doclet;
import java.util.HashMap;
import java.util.Map;
+import javax.lang.model.element.Name;
+
/**
* Class's dot-compatible alias name (for fully qualified class names) and
* printed information
@@ -42,7 +44,7 @@ class ClassInfo {
* classes linked with a bi-directional relation , and the ones referred by a
* directed relation
*/
- Map relatedClasses = new HashMap();
+ Map relatedClasses = new HashMap<>();
ClassInfo(boolean h) {
hidden = h;
@@ -50,7 +52,7 @@ class ClassInfo {
classNumber++;
}
- public void addRelation(String dest, RelationType rt, RelationDirection d) {
+ public void addRelation(Name dest, RelationType rt, RelationDirection d) {
RelationPattern ri = relatedClasses.get(dest);
if (ri == null) {
ri = new RelationPattern(RelationDirection.NONE);
@@ -59,7 +61,7 @@ class ClassInfo {
ri.addRelation(rt, d);
}
- public RelationPattern getRelation(String dest) {
+ public RelationPattern getRelation(CharSequence dest) {
return relatedClasses.get(dest);
}
diff --git a/src/main/java/org/umlgraph/doclet/ClassMatcher.java b/src/main/java/org/umlgraph/doclet/ClassMatcher.java
index d89df36..294fcfa 100644
--- a/src/main/java/org/umlgraph/doclet/ClassMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/ClassMatcher.java
@@ -17,7 +17,7 @@
package org.umlgraph.doclet;
-import com.sun.javadoc.ClassDoc;
+import javax.lang.model.element.TypeElement;
/**
* A ClassMatcher is used to check if a class definition matches a specific
@@ -29,10 +29,10 @@ public interface ClassMatcher {
/**
* Returns the options for the specified class.
*/
- public boolean matches(ClassDoc cd);
+ public boolean matches(TypeElement cd);
/**
* Returns the options for the specified class.
*/
- public boolean matches(String name);
+ public boolean matches(CharSequence name);
}
diff --git a/src/main/java/org/umlgraph/doclet/ContextMatcher.java b/src/main/java/org/umlgraph/doclet/ContextMatcher.java
index 626fab0..eafdf1b 100644
--- a/src/main/java/org/umlgraph/doclet/ContextMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/ContextMatcher.java
@@ -26,8 +26,10 @@ import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.RootDoc;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
/**
* Matches classes that are directly connected to one of the classes matched by
@@ -46,11 +48,11 @@ import com.sun.javadoc.RootDoc;
public class ContextMatcher implements ClassMatcher {
ClassGraphHack cg;
Pattern pattern;
- List matched;
- Set visited = new HashSet();
+ List matched;
+ Set visited = new HashSet<>();
/** The options will be used to decide on inference */
Options opt;
- RootDoc root;
+ DocletEnvironment root;
boolean keepParentHide;
/**
@@ -64,7 +66,7 @@ public class ContextMatcher implements ClassMatcher {
* the context
* @throws IOException
*/
- public ContextMatcher(RootDoc root, Pattern pattern, Options options, boolean keepParentHide) throws IOException {
+ public ContextMatcher(DocletEnvironment root, Pattern pattern, Options options, boolean keepParentHide) throws IOException {
this.pattern = pattern;
this.root = root;
this.keepParentHide = keepParentHide;
@@ -89,11 +91,11 @@ public class ContextMatcher implements ClassMatcher {
// build up the classgraph printing the relations for all of the
// classes that make up the "center" of this context
this.pattern = pattern;
- matched = new ArrayList();
- for (ClassDoc cd : root.classes()) {
- if (pattern.matcher(cd.toString()).matches()) {
- matched.add(cd);
- addToGraph(cd);
+ matched = new ArrayList<>();
+ for (Element cd : root.getIncludedElements()) {
+ if (cd instanceof TypeElement && pattern.matcher(cd.toString()).matches()) {
+ matched.add((TypeElement) cd);
+ addToGraph((TypeElement) cd);
}
}
}
@@ -105,31 +107,36 @@ public class ContextMatcher implements ClassMatcher {
*
* @param cd
*/
- private void addToGraph(ClassDoc cd) {
+ private void addToGraph(TypeElement cd) {
// avoid adding twice the same class, but don't rely on cg.getClassInfo
// since there are other ways to add a classInfor than printing the class
- if (visited.contains(cd.toString()))
+ if (visited.contains(cd.toString())) {
return;
+ }
visited.add(cd.toString());
cg.printClass(cd, false);
cg.printRelations(cd);
- if (opt.inferRelationships)
+ if (opt.inferRelationships) {
cg.printInferredRelations(cd);
- if (opt.inferDependencies)
+ }
+ if (opt.inferDependencies) {
cg.printInferredDependencies(cd);
+ }
}
/**
- * @see org.umlgraph.doclet.ClassMatcher#matches(com.sun.javadoc.ClassDoc)
+ * @see org.umlgraph.doclet.ClassMatcher#matches(TypeElement)
*/
- public boolean matches(ClassDoc cd) {
- if (keepParentHide && opt.matchesHideExpression(cd.toString()))
+ public boolean matches(TypeElement cd) {
+ if (keepParentHide && opt.matchesHideExpression(cd.getQualifiedName())) {
return false;
+ }
// if the class is matched, it's in by default.
- if (matched.contains(cd))
+ if (matched.contains(cd)) {
return true;
+ }
// otherwise, add the class to the graph and see if it's associated
// with any of the matched classes using the classgraph hack
@@ -138,16 +145,18 @@ public class ContextMatcher implements ClassMatcher {
}
/**
- * @see org.umlgraph.doclet.ClassMatcher#matches(java.lang.String)
+ * @see org.umlgraph.doclet.ClassMatcher#matches(CharSequence)
*/
- public boolean matches(String name) {
- if (pattern.matcher(name).matches())
+ public boolean matches(CharSequence name) {
+ if (pattern.matcher(name).matches()) {
return true;
+ }
- for (ClassDoc mcd : matched) {
+ for (TypeElement mcd : matched) {
RelationPattern rp = cg.getClassInfo(mcd, true).getRelation(name);
- if (rp != null && opt.contextRelationPattern.matchesOne(rp))
+ if (rp != null && opt.contextRelationPattern.matchesOne(rp)) {
return true;
+ }
}
return false;
}
@@ -162,7 +171,7 @@ public class ContextMatcher implements ClassMatcher {
*/
private static class ClassGraphHack extends ClassGraph {
- public ClassGraphHack(RootDoc root, OptionProvider optionProvider) throws IOException {
+ public ClassGraphHack(DocletEnvironment root, OptionProvider optionProvider) throws IOException {
super(root, optionProvider, null);
prologue();
}
diff --git a/src/main/java/org/umlgraph/doclet/ContextView.java b/src/main/java/org/umlgraph/doclet/ContextView.java
index 48e9737..5202315 100755
--- a/src/main/java/org/umlgraph/doclet/ContextView.java
+++ b/src/main/java/org/umlgraph/doclet/ContextView.java
@@ -3,8 +3,14 @@ package org.umlgraph.doclet;
import java.io.IOException;
import java.util.regex.Pattern;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.RootDoc;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.TypeElement;
+
+import org.umlgraph.doclet.util.ElementUtil;
+
+import com.sun.source.util.DocTrees;
/**
* A view designed for UMLDoc, filters out everything that it's not directly
@@ -20,7 +26,8 @@ import com.sun.javadoc.RootDoc;
*/
public class ContextView implements OptionProvider {
- private ClassDoc cd;
+ private TypeElement cd;
+ private DocletEnvironment root;
private ContextMatcher matcher;
private Options globalOptions;
private Options myGlobalOptions;
@@ -29,9 +36,12 @@ public class ContextView implements OptionProvider {
private Options packageOptions;
private static final String[] HIDE_OPTIONS = new String[] { "hide" };
- public ContextView(String outputFolder, ClassDoc cd, RootDoc root, Options parent) throws IOException {
+ public ContextView(String outputFolder, TypeElement cd, DocletEnvironment root, Options parent) throws IOException {
this.cd = cd;
- String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() + ".dot";
+ this.root = root;
+ ModuleElement md = ElementUtil.getModuleOf(root, cd);
+ String pathPrefix = Runtime.version().major() > 10 && md != null ? md.getQualifiedName().toString() + "/" : "";
+ String outputPath = pathPrefix + ElementUtil.getPackageOf(root, cd).getQualifiedName().toString().replace('.', '/') + "/" + cd.getSimpleName() + ".dot";
// setup options statically, so that we won't need to change them so
// often
@@ -51,13 +61,15 @@ public class ContextView implements OptionProvider {
this.centerOptions.nodeFillColor = "lemonChiffon";
this.centerOptions.showQualified = false;
- this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.toString())), myGlobalOptions, true);
+ this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.getQualifiedName().toString())), myGlobalOptions, true);
}
- public void setContextCenter(ClassDoc contextCenter) {
+ public void setContextCenter(TypeElement contextCenter) {
this.cd = contextCenter;
- String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() + ".dot";
+ ModuleElement md = ElementUtil.getModuleOf(root, contextCenter);
+ String pathPrefix = Runtime.version().major() > 10 && md != null ? md.getQualifiedName().toString() + "/" : "";
+ String outputPath = pathPrefix + ElementUtil.getPackageOf(root, cd).getQualifiedName().toString().replace('.', '/') + "/" + cd.getSimpleName() + ".dot";
this.myGlobalOptions.setOption(new String[] { "output", outputPath });
matcher.setContextCenter(Pattern.compile(Pattern.quote(cd.toString())));
}
@@ -70,14 +82,14 @@ public class ContextView implements OptionProvider {
return myGlobalOptions;
}
- public Options getOptionsFor(ClassDoc cd) {
+ public Options getOptionsFor(DocTrees dt, TypeElement cd) {
Options opt;
- if (globalOptions.matchesHideExpression(cd.qualifiedName())
- || !(matcher.matches(cd) || globalOptions.matchesIncludeExpression(cd.qualifiedName()))) {
+ if (globalOptions.matchesHideExpression(cd.getQualifiedName())
+ || !(matcher.matches(cd) || globalOptions.matchesIncludeExpression(cd.getQualifiedName()))) {
opt = hideOptions;
} else if (cd.equals(this.cd)) {
opt = centerOptions;
- } else if (cd.containingPackage().equals(this.cd.containingPackage())) {
+ } else if (root.getElementUtils().getPackageOf(cd).equals(root.getElementUtils().getPackageOf(this.cd))) {
opt = packageOptions;
} else {
opt = globalOptions;
@@ -87,31 +99,35 @@ public class ContextView implements OptionProvider {
return optionClone;
}
- public Options getOptionsFor(String name) {
+ public Options getOptionsFor(CharSequence name) {
Options opt;
- if (!matcher.matches(name))
+ if (!matcher.matches(name)) {
opt = hideOptions;
- else if (name.equals(cd.name()))
+ } else if (name.equals(cd.getQualifiedName())) {
opt = centerOptions;
- else
+ } else {
opt = globalOptions;
+ }
Options optionClone = (Options) opt.clone();
overrideForClass(optionClone, name);
return optionClone;
}
- public void overrideForClass(Options opt, ClassDoc cd) {
- opt.setOptions(cd);
- if (opt.matchesHideExpression(cd.qualifiedName())
- || !(matcher.matches(cd) || opt.matchesIncludeExpression(cd.qualifiedName())))
+ public void overrideForClass(Options opt, TypeElement cd) {
+ opt.setOptions(root.getDocTrees(), cd);
+ if (opt.matchesHideExpression(cd.getQualifiedName())
+ || !(matcher.matches(cd) || opt.matchesIncludeExpression(cd.getQualifiedName()))) {
opt.setOption(HIDE_OPTIONS);
- if (cd.equals(this.cd))
+ }
+ if (cd.equals(this.cd)) {
opt.nodeFillColor = "lemonChiffon";
+ }
}
- public void overrideForClass(Options opt, String className) {
- if (!(matcher.matches(className) || opt.matchesIncludeExpression(className)))
+ public void overrideForClass(Options opt, CharSequence className) {
+ if (!(matcher.matches(className) || opt.matchesIncludeExpression(className))) {
opt.setOption(HIDE_OPTIONS);
+ }
}
}
diff --git a/src/main/java/org/umlgraph/doclet/Font.java b/src/main/java/org/umlgraph/doclet/Font.java
index f9bcc0d..0e2b0a7 100644
--- a/src/main/java/org/umlgraph/doclet/Font.java
+++ b/src/main/java/org/umlgraph/doclet/Font.java
@@ -4,7 +4,7 @@ package org.umlgraph.doclet;
* Class to represent a font for graphviz.
*
* This is a fairly complicated model, because it is rather an API into graphviz
- * formatting strings rather than a standalone thing. Some fonts (edge, node,
* abstract) are set on the top level elements, whereas others (class name, tag,
* package) are inserted as {@code } tags, and these can be omitted if not
* set. Inheritance of properties then happens in graphviz.
diff --git a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java
index e39518d..a4a6f13 100644
--- a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java
@@ -2,8 +2,13 @@ package org.umlgraph.doclet;
import java.util.regex.Pattern;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.RootDoc;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+import org.umlgraph.doclet.util.ElementUtil;
/**
* Matches every class that implements (directly or indirectly) an interfaces
@@ -11,31 +16,36 @@ import com.sun.javadoc.RootDoc;
*/
public class InterfaceMatcher implements ClassMatcher {
- protected RootDoc root;
+ protected DocletEnvironment root;
protected Pattern pattern;
- public InterfaceMatcher(RootDoc root, Pattern pattern) {
+ public InterfaceMatcher(DocletEnvironment root, Pattern pattern) {
this.root = root;
this.pattern = pattern;
}
- public boolean matches(ClassDoc cd) {
+ public boolean matches(TypeElement cd) {
// if it's the interface we're looking for, match
- if (cd.isInterface() && pattern.matcher(cd.toString()).matches())
+ if (cd.getKind() == ElementKind.INTERFACE && pattern.matcher(cd.toString()).matches()) {
return true;
+ }
// for each interface, recurse, since classes and interfaces
// are treated the same in the doclet API
- for (ClassDoc iface : cd.interfaces())
- if (matches(iface))
+ for (TypeMirror type : cd.getInterfaces()) {
+ TypeElement iType = ElementUtil.getTypeElement(type);
+ if (iType != null && matches(iType)) {
return true;
+ }
+ }
- // recurse on supeclass, if available
- return cd.superclass() == null ? false : matches(cd.superclass());
+ // recurse on superclass, if available
+ TypeElement scd = ElementUtil.getTypeElement(cd.getSuperclass());
+ return scd == null ? false : matches(scd);
}
- public boolean matches(String name) {
- ClassDoc cd = root.classNamed(name);
+ public boolean matches(CharSequence name) {
+ TypeElement cd = root.getElementUtils().getTypeElement(name);
return cd == null ? false : matches(cd);
}
diff --git a/src/main/java/org/umlgraph/doclet/Option.java b/src/main/java/org/umlgraph/doclet/Option.java
new file mode 100644
index 0000000..e419601
--- /dev/null
+++ b/src/main/java/org/umlgraph/doclet/Option.java
@@ -0,0 +1,53 @@
+package org.umlgraph.doclet;
+
+import java.util.List;
+
+import jdk.javadoc.doclet.Doclet;
+
+/**
+ * A base class for declaring options. Subtypes for specific options should
+ * implement the {@link #process(String,List) process} method to handle
+ * instances of the option found on the command line.
+ */
+public abstract class Option implements Doclet.Option {
+ private final String name;
+ private final int argumentCount;
+ private final String description;
+ private final String parameters;
+
+ public Option(String name, boolean hasArg, String description, String parameters) {
+ this(name, hasArg ? 1 : 0, description, parameters);
+ }
+
+ public Option(String name, int argumentCount, String description, String parameters) {
+ this.name = name;
+ this.argumentCount = argumentCount;
+ this.description = description;
+ this.parameters = parameters;
+ }
+
+ @Override
+ public int getArgumentCount() {
+ return argumentCount;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.STANDARD;
+ }
+
+ @Override
+ public List getNames() {
+ return List.of(name);
+ }
+
+ @Override
+ public String getParameters() {
+ return argumentCount == 0 ? "" : parameters;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/umlgraph/doclet/OptionProvider.java b/src/main/java/org/umlgraph/doclet/OptionProvider.java
index 151d2b7..e3f8e89 100644
--- a/src/main/java/org/umlgraph/doclet/OptionProvider.java
+++ b/src/main/java/org/umlgraph/doclet/OptionProvider.java
@@ -17,22 +17,23 @@
package org.umlgraph.doclet;
-import com.sun.javadoc.ClassDoc;
+import javax.lang.model.element.TypeElement;
+
+import com.sun.source.util.DocTrees;
/**
- * A factory class that builds Options object for general use or for a specific
- * class
+ * A factory class that builds Options object for general use or for a specific class
*/
public interface OptionProvider {
/**
* Returns the options for the specified class.
*/
- public Options getOptionsFor(ClassDoc cd);
+ public Options getOptionsFor(DocTrees dt, TypeElement cd);
/**
* Returns the options for the specified class.
*/
- public Options getOptionsFor(String name);
+ public Options getOptionsFor(CharSequence name);
/**
* Returns the global options (the class independent definition)
@@ -42,12 +43,12 @@ public interface OptionProvider {
/**
* Gets a base Options and applies the overrides for the specified class
*/
- public void overrideForClass(Options opt, ClassDoc cd);
+ public void overrideForClass(Options opt, TypeElement cd);
/**
* Gets a base Options and applies the overrides for the specified class
*/
- public void overrideForClass(Options opt, String className);
+ public void overrideForClass(Options opt, CharSequence className);
/**
* Returns user displayable name for this option provider.
diff --git a/src/main/java/org/umlgraph/doclet/Options.java b/src/main/java/org/umlgraph/doclet/Options.java
index 7018da8..7b5cc98 100644
--- a/src/main/java/org/umlgraph/doclet/Options.java
+++ b/src/main/java/org/umlgraph/doclet/Options.java
@@ -29,18 +29,26 @@ import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.Doc;
-import com.sun.javadoc.Tag;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+
+import org.umlgraph.doclet.util.TagUtil;
+
+import com.sun.source.util.DocTrees;
+
+import jdk.javadoc.doclet.Doclet;
/**
* Represent the program options
@@ -49,13 +57,550 @@ import com.sun.javadoc.Tag;
* @author Diomidis Spinellis
*/
public class Options implements Cloneable, OptionProvider {
+
+ public final Set extends Doclet.Option> OPTIONS = Set.of(
+ new Option("--d", true, "Specify the output directory (defaults to the current directory).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ outputDirectory = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-qualify", false, "Produce fully-qualified class names.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showQualified = true;
+ return true;
+ }
+ },
+ new Option("-qualifyGenerics", false, "Use fully-qualified class names in Java generics.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showQualifiedGenerics = true;
+ return true;
+ }
+ },
+ new Option("-hideGenerics", false, "FIXME Missing doc", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ hideGenerics = true;
+ return true;
+ }
+ },
+ new Option("-horizontal", false, "Layout the graph in the horizontal direction.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ horizontal = true;
+ return true;
+ }
+ },
+ new Option("-attributes", false, "Show class attributes (Java fields)", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showAttributes = true;
+ return true;
+ }
+ },
+ new Option("-enumconstants", false, "When showing enumerations, also show the values they can take", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showEnumConstants = true;
+ return true;
+ }
+ },
+ new Option("-operations", false, "Show class operations (Java methods)", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showOperations = true;
+ return true;
+ }
+ },
+ new Option("-enumerations", false, "Show enumarations as separate stereotyped primitive types", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showEnumerations = true;
+ return true;
+ }
+ },
+ new Option("-constructors", false, "Show a class's constructors", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showConstructors = true;
+ return true;
+ }
+ },
+ new Option("-visibility", false, "Adorn class elements according to their visibility (private, public, protected, package)", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showVisibility = true;
+ return true;
+ }
+ },
+ new Option("-types", false, "Add type information to attributes and operations", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showType = true;
+ return true;
+ }
+ },
+ new Option("-autosize", false, "Fits generated graph to the width of the page/window. Defaults to true.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ autoSize = true;
+ return true;
+ }
+ },
+ new Option("-commentname", false, "Name the element using the text in the javadoc comment, instead of the name of its class.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ showComment = true;
+ return true;
+ }
+ },
+ new Option("-all", false, "Same as -attributes -operations -visibility -types -enumerations -enumconstants", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ setAll();
+ return true;
+ }
+ },
+ new Option("--bgcolor", true, "Specify the graph's background color.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ bgColor = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("--edgecolor", true, "Specify the color for drawing edges.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ edgeColor = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("--edgefontcolor", true, "Specify the font color to use for edge labels.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ edgeFontColor = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("--edgefontname", true, "Specify the font name to use for edge labels.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ edgeFontName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-edgefontsize", true, "Specify the font size to use for edge labels.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ edgeFontSize = Double.parseDouble(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-nodefontcolor", true, "Specify the font color to use inside nodes", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontColor = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-nodefontname", true, "Specify the font name to use inside nodes", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-nodefontabstractitalic", false, "FIXME no documentation", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontAbstractItalic = true;
+ return true;
+ }
+ },
+ new Option("-nodefontsize", true, "Specify the font size to use inside nodes", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontSize = Double.parseDouble(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-nodefontclassname", true, "Specify the font name to use for the class names", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontClassName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-nodefontclasssize", true, "Specify the font size to use for the class names.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontClassSize = Double.parseDouble(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-nodefonttagname", true, "Specify the font name to use for the tag names.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontTagName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-nodefonttagsize", true, "Specify the font size to use for the tag names", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontTagSize = Double.parseDouble(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-nodefontpackagename", true, "Specify the font name to use for the package names (used only when the package name is postfixed, see -postfixpackage).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontPackageName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-nodefontpackagesize", true, "Specify the font size to use for the package names (used only when it package name is postfixed, see -postfixpackage).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFontPackageSize = Double.parseDouble(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-nodefillcolor", true, "Specify the color to use to fill the shapes", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ nodeFillColor = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-shape", true, "Specify the shape to use for the rendered element(s).\n"
+ + " The following UML shapes are available: class (default), node,"
+ + " component, package, collaboration, usecase, activeclass", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ shape = Shape.of(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-output", true, "Specify the output file (default graph.dot).\n"
+ + "If the output directory is provided, -output can only specify a file name,\n"
+ + "otherwise a full path is accepted as well.\n"
+ + "If the filename specified is a dash, then the results are printed on the\n"
+ + "standard output, and can be directly piped into dot.\n"
+ + "Note that, in order to avoid javadoc messages to contaminate\n"
+ + "UMLGraph's output, you must execute UMLGraph directly as a jar,\n"
+ + "not through javadoc.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ outputFileName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-outputencoding", true, "Specify the output encoding character set (default UTF-8).\n"
+ + "When using dot to generate SVG diagrams you should specify\n"
+ + "UTF-8 as the output encoding, to have guillemots correctly\n"
+ + "appearing in the resulting SVG.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ outputEncoding = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("--hide", true, "Specify entities to hide from the graph."
+ + " Matching is done using a non-anchored regular match."
+ + " For instance, \"-hide (Big|\\.)Widget\" would hide \"com.foo.widgets.Widget\" and "
+ + " \"com.foo.widgets.BigWidget\". Can also be used without arguments, "
+ + " in this case it will hide everything (useful in the context of views "
+ + " to selectively unhide some portions of the graph, see the view chapter for "
+ + " further details).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ if (arguments == null || arguments.isEmpty()) {
+ hidePatterns.clear();
+ hidePatterns.add(allPattern);
+ } else {
+ try {
+ hidePatterns.add(Pattern.compile(arguments.get(0)));
+ } catch (PatternSyntaxException e) {
+ System.err.println("Skipping invalid pattern " + arguments.get(0));
+ }
+ }
+ return true;
+ }
+ },
+ new Option("--include", true, "Match classes to include with a non-anchored match. This is weaker than\n"
+ + " the -hide option, but can be used to include classes from foreign packages\n"
+ + " in the package view (which would by default filter to only include package members).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ try {
+ includePatterns.add(Pattern.compile(arguments.get(0)));
+ } catch (PatternSyntaxException e) {
+ System.err.println("Skipping invalid pattern " + arguments.get(0));
+ }
+ return true;
+ }
+ },
+ new Option("-apidocroot", true, "Specify the URL that should be used as the \"root\" for local classes.\n"
+ + "This URL will be used as a prefix, to which the page name for the local class or\n"
+ + "package will be appended (following the JavaDoc convention).\n"
+ + "For example, if the value http://www.acme.org/apidocs is\n"
+ + "provided, the class org.acme.util.MyClass will be mapped to the URL\n"
+ + "http://www.acme.org/apidocs/org/acme/util/MyClass.html.\n"
+ + "This URL will then be added to .dot diagram and can be surfaced in the\n"
+ + "final class diagram by setting the output to SVG, or by creating an HTML page\n"
+ + "that associates the diagram static image (a .gif or .png) with a client-side\n"
+ + "image map.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ apiDocRoot = fixApiDocRoot(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-apidocmap", true, "Specify the file name of the URL mapping table. \n"
+ + "The is a standard Java property file, where the property name is a regular\n"
+ + "expression (as defined in the java.util.regex package) and the property value is\n"
+ + "an URL \"root\" as described above.\n"
+ + "This table is used to resolved external class names (class names that do not\n"
+ + "belong to the current package being processed by UMLGraph). If no file is provided,\n"
+ + "external classes will just be mapped to the on-line Java API documentation.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ setApiDocMapFile(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-noguillemot", false, "Specify that guillemot characters should not be used to denote "
+ + "special terms like \"interface\" and stereotype names."
+ + "This is used on some platforms to circumvent problems associated with displaying non-ASCII characters.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ guilOpen = "<<";
+ guilClose = ">>";
+ return true;
+ }
+ },
+ new Option("-view", true, "Specify the fully qualified name of a class that contains\n"
+ + " a view definition. Only the class diagram specified by this view will be generated. \n"
+ + " See the views chapter for more details.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ viewName = arguments.get(0);
+ return true;
+ }
+ },
+ new Option("-views", false, "Generate a class diagram for every view found in the source path..", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ findViews = true;
+ return true;
+ }
+ },
+ new Option("-inferrel", false, "Try to automatically infer relationships between classes by inspecting "
+ + "field values. See the class diagram inference chapter for further details. Disabled by default.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ inferRelationships = true;
+ return true;
+ }
+ },
+ new Option("-inferreltype", true, "The type of relationship inferred when -inferrel is activated. \n"
+ + "Defaults to \"navassoc\" (see the class modelling chapter for a list of relationship types).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ try {
+ inferRelationshipType = RelationType.valueOf(arguments.get(0).toUpperCase());
+ } catch (IllegalArgumentException e) {
+ System.err.println("Unknown association type " + arguments.get(0));
+ }
+ return true;
+ }
+ },
+ new Option("-inferdepvis", true, "Specifies the lowest visibility level of elements used to infer\n"
+ + "dependencies among classes. Possible values are private, package, protected, public, in this\n"
+ + "order. The default value is private. Use higher levels to limit the number of inferred dependencies", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ try {
+ inferDependencyVisibility = Visibility.valueOf(arguments.get(0).toUpperCase());
+ } catch (IllegalArgumentException e) {
+ System.err.println("Ignoring invalid visibility specification for " + "dependency inference: " + arguments.get(0));
+ }
+ return true;
+ }
+ },
+ new Option("-collapsible", false, "Enhance the javadoc HTML files containing UML diagrams with Javascript "
+ + "that provides a link for showing the (initially collapsed) diagrams.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ collapsibleDiagrams = true;
+ return true;
+ }
+ },
+ new Option("-inferdep", false, "Try to automatically infer dependencies between classes by inspecting "
+ + "methods and fields. See the class diagram inference chapter for more details. Disabled by default.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ inferDependencies = true;
+ return true;
+ }
+ },
+ new Option("-inferdepinpackage", false, "Enable or disable dependency inference among classes in the\n"
+ + "same package. This option is disabled by default, because classes in the same package are supposed\n"
+ + "to be related anyway, and also because there's no working mechanism to actually detect all\n"
+ + "of these dependencies since imports are not required to use classes in the same package.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ inferDepInPackage = true;
+ return true;
+ }
+ },
+ new Option("-hideprivateinner", false, "FIXME NO DOC", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ hidePrivateInner = true;
+ return true;
+ }
+ },
+ new Option("-useimports", false, "Will also use imports to infer dependencies. \n"
+ + "Disabled by default, since it does not work properly if there are multiple\n"
+ + "classes in the same source file (will add dependencies to every class in\n"
+ + "the source file).", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ System.err.println("useimports has been set but will have no effect since not (yet) available");
+ useImports = true;
+ return true;
+ }
+ },
+ new Option("--collpackages", true, "Specify the classes that will be treated as "
+ + "containers for one to many relationships when inference is enabled. "
+ + "Matching is done using a non-anchored regular match. Empty by default.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ try {
+ collPackages.add(Pattern.compile(arguments.get(0)));
+ } catch (PatternSyntaxException e) {
+ System.err.println("Skipping invalid pattern " + arguments.get(0));
+ }
+ return true;
+ }
+ },
+ new Option("-compact", false, "Generate compact dot files, that is, print HTML labels\n"
+ + "in a single line instead of \"pretty printing\" them. Useful if the dot file\n"
+ + "has to be manipulated by an automated tool\n"
+ + "(e.g., the UMLGraph regression test suite).", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ compact = true;
+ return true;
+ }
+ },
+ new Option("-postfixpackage", false, "When using qualified class names, put the package name in the line after the class name, in order to reduce the width of class nodes.", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ postfixPackage = true;
+ return true;
+ }
+ },
+
+ new Option("-hideGenerics", false, "?", null) {
+ @Override
+ public boolean process(String option, List arguments) {
+ hideGenerics = true;
+ return true;
+ }
+ },
+ new Option("--link", true, "A clone of the standard doclet\n"
+ + "-link\n"
+ + "option, allows UMLGraph to generate links from class symbols to their external javadoc\n"
+ + "documentation (image maps are automatically generated in UMLGraphDoc, you'll have to generate them\n"
+ + "manually with graphviz if using UMLGraph).", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ System.err.println("add -link option with args : " + arguments.get(0));
+ addApiDocRoots(arguments.get(0));
+ return true;
+ }
+ },
+ new Option("-linkoffline", 2, "Specify\n"
+ + "links to javadoc-generated documentation for external referenced classes.\n"
+ + "The -linkoffline option takes two arguments:\n"
+ + "the first for the string to be embedded in the href\n"
+ + "links, the second telling it where to find the package-list.\n"
+ + "Example:\n"
+ + "\n"
+ + "-linkoffline http://developer.android.com/reference file:/home/doc/android/\n"
+ + " \n"
+ + "See the javadoc documentation for more details.", " ") {
+ @Override
+ public boolean process(String option, List arguments) {
+ addApiDocRootsOffline(arguments.get(0), arguments.get(1));
+ return true;
+ }
+ },
+ new Option("-contextPattern", 2, "FIXME MISSING DOC.", " ") {
+ @Override
+ public boolean process(String option, List arguments) {
+ RelationDirection d;
+ RelationType rt;
+ try {
+ d = RelationDirection.valueOf(arguments.get(1).toUpperCase());
+ if ("all".equalsIgnoreCase(arguments.get(0))) {
+ contextRelationPattern = new RelationPattern(d);
+ } else {
+ rt = RelationType.valueOf(arguments.get(0).toUpperCase());
+ contextRelationPattern.addRelation(rt, d);
+ }
+ } catch (IllegalArgumentException e) {
+
+ }
+ return true;
+ }
+ },
+ new Option("-nodesep", true, "Specify the horizontal separation between the class nodes (0.25 by default). Decreasing this can make a diagram more compact.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ try {
+ nodeSep = Double.parseDouble(arguments.get(0));
+ } catch (NumberFormatException e) {
+ System.err.println("Skipping invalid nodesep " + arguments.get(0));
+ }
+ return true;
+ }
+ },
+ new Option("-ranksep", true, "Specify the vertical separation between the class nodes (0.5 by default). Decreasing this can make a diagram more compact.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ try {
+ rankSep = Double.parseDouble(arguments.get(0));
+ } catch (NumberFormatException e) {
+ System.err.println("Skipping invalid ranksep " + arguments.get(0));
+ }
+ return true;
+ }
+ },
+ new Option("-dotexecutable", true, "Specify the path of the dot executable.", "") {
+ @Override
+ public boolean process(String option, List arguments) {
+ dotExecutable = arguments.get(0);
+ return true;
+ }
+ }
+
+
+ );
+
// reused often, especially in UmlGraphDoc, worth creating just once and reusing
private static final Pattern allPattern = Pattern.compile(".*");
protected static final String DEFAULT_EXTERNAL_APIDOC = "http://docs.oracle.com/javase/7/docs/api/";
// instance fields
- List hidePatterns = new ArrayList();
- List includePatterns = new ArrayList();
+ List hidePatterns = new ArrayList<>();
+ List includePatterns = new ArrayList<>();
boolean showQualified = false;
boolean showQualifiedGenerics = false;
boolean hideGenerics = false;
@@ -87,8 +632,8 @@ public class Options implements Cloneable, OptionProvider {
Shape shape = Shape.CLASS;
String bgColor = null;
public String outputFileName = "graph.dot";
- String outputEncoding = "ISO-8859-1"; // TODO: default to UTF-8 now?
- Map apiDocMap = new HashMap();
+ String outputEncoding = StandardCharsets.UTF_8.name();
+ Map apiDocMap = new HashMap<>();
String apiDocRoot = null;
boolean postfixPackage = false;
boolean useGuillemot = true;
@@ -96,7 +641,7 @@ public class Options implements Cloneable, OptionProvider {
String viewName = null;
double nodeSep = 0.25;
double rankSep = 0.5;
- public String outputDirectory = null;
+ public String outputDirectory = ".";
/*
* Numeric values are preferable to symbolic here. Symbolic reportedly fail on
* MacOSX, and also are more difficult to verify with XML tools.
@@ -113,26 +658,23 @@ public class Options implements Cloneable, OptionProvider {
Visibility inferDependencyVisibility = Visibility.PRIVATE;
boolean inferDepInPackage = false;
RelationType inferRelationshipType = RelationType.NAVASSOC;
- private List collPackages = new ArrayList();
+ private List collPackages = new ArrayList<>();
boolean compact = false;
boolean hidePrivateInner = false;
// internal option, used by UMLDoc to generate relative links between classes
boolean relativeLinksForSourcePackages = false;
// internal option, used by UMLDoc to force strict matching on the class names
- // and avoid problems with packages in the template declaration making UmlGraph
- // hide
- // classes outside of them (for example, class gr.spinellis.Foo
+ // and avoid problems with packages in the template declaration making UmlGraph hide
+ // classes outside of them (for example, class gr.spinellis.Foo
// would have been hidden by the hide pattern "java.*"
- // TODO: consider making this standard behaviour
- boolean strictMatching = false;
+ boolean strictMatching = true;
String dotExecutable = "dot";
Options() {
}
@Override
- public Object clone() {
+ public Options clone() {
Options clone = null;
try {
clone = (Options) super.clone();
@@ -140,10 +682,10 @@ public class Options implements Cloneable, OptionProvider {
throw new RuntimeException("Cannot clone?!?", e); // Should not happen
}
// deep clone the hide and collection patterns
- clone.hidePatterns = new ArrayList(hidePatterns);
- clone.includePatterns = new ArrayList(includePatterns);
- clone.collPackages = new ArrayList(collPackages);
- clone.apiDocMap = new HashMap(apiDocMap);
+ clone.hidePatterns = new ArrayList<>(hidePatterns);
+ clone.includePatterns = new ArrayList<>(includePatterns);
+ clone.collPackages = new ArrayList<>(collPackages);
+ clone.apiDocMap = new HashMap<>(apiDocMap);
return clone;
}
@@ -159,7 +701,7 @@ public class Options implements Cloneable, OptionProvider {
}
/**
- * Match strings, ignoring leading -, -!, and !.
+ * Match strings, ignoring leading -, --, -!, and !.
*
* @param given Given string
* @param expect Expected string
@@ -170,7 +712,7 @@ public class Options implements Cloneable, OptionProvider {
}
/**
- * Match strings, ignoring leading -, -!, and !.
+ * Match strings, ignoring leading -, --, -!, and !.
*
* @param given Given string
* @param expect Expected string
@@ -179,10 +721,16 @@ public class Options implements Cloneable, OptionProvider {
*/
protected static boolean matchOption(String given, String expect, boolean negative) {
int begin = 0, end = given.length();
- if (begin < end && given.charAt(begin) == '-')
+ if (begin < end && given.charAt(begin) == '-') {
++begin;
- if (negative && begin < end && given.charAt(begin) == '!')
+ }
+ // double dashed option
+ if (begin < end && given.charAt(begin) == '-') {
++begin;
+ }
+ if (negative && begin < end && given.charAt(begin) == '!') {
+ ++begin;
+ }
return expect.length() == end - begin && expect.regionMatches(0, given, begin, end - begin);
}
@@ -204,10 +752,10 @@ public class Options implements Cloneable, OptionProvider {
|| matchOption(option, "inferrel", true) || matchOption(option, "useimports", true)
|| matchOption(option, "collapsible", true) || matchOption(option, "inferdep", true)
|| matchOption(option, "inferdepinpackage", true) || matchOption(option, "hideprivateinner", true)
- || matchOption(option, "compact", true))
+ || matchOption(option, "compact", true)) {
return 1;
- else if (matchOption(option, "nodefillcolor") || matchOption(option, "nodefontcolor")
+ } else if (matchOption(option, "nodefillcolor") || matchOption(option, "nodefontcolor")
|| matchOption(option, "nodefontsize") || matchOption(option, "nodefontname")
|| matchOption(option, "nodefontclasssize") || matchOption(option, "nodefontclassname")
|| matchOption(option, "nodefonttagsize") || matchOption(option, "nodefonttagname")
@@ -221,12 +769,13 @@ public class Options implements Cloneable, OptionProvider {
|| matchOption(option, "inferreltype") || matchOption(option, "inferdepvis")
|| matchOption(option, "collpackages") || matchOption(option, "nodesep")
|| matchOption(option, "ranksep") || matchOption(option, "dotexecutable")
- || matchOption(option, "link"))
+ || matchOption(option, "link")) {
return 2;
- else if (matchOption(option, "contextPattern") || matchOption(option, "linkoffline"))
+ } else if (matchOption(option, "contextPattern") || matchOption(option, "linkoffline")) {
return 3;
- else
+ } else {
return 0;
+ }
}
/** Set the options based on a single option and its arguments */
@@ -436,7 +985,7 @@ public class Options implements Cloneable, OptionProvider {
BufferedReader br = null;
packageListUrl = fixApiDocRoot(packageListUrl);
try {
- URL url = new URL(packageListUrl + "/package-list");
+ URL url = new URL(packageListUrl + "package-list");
br = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = br.readLine()) != null) {
@@ -447,11 +996,12 @@ public class Options implements Cloneable, OptionProvider {
} catch (IOException e) {
System.err.println("Errors happened while accessing the package-list file at " + packageListUrl);
} finally {
- if (br != null)
+ if (br != null) {
try {
br.close();
} catch (IOException e) {
}
+ }
}
}
@@ -468,7 +1018,7 @@ public class Options implements Cloneable, OptionProvider {
BufferedReader br = null;
packageListUrl = fixApiDocRoot(packageListUrl);
try {
- URL url = new URL(packageListUrl + "/package-list");
+ URL url = new URL(packageListUrl + "package-list");
br = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = br.readLine()) != null) {
@@ -479,11 +1029,12 @@ public class Options implements Cloneable, OptionProvider {
} catch (IOException e) {
System.err.println("Unable to access the package-list file at " + packageListUrl);
} finally {
- if (br != null)
+ if (br != null) {
try {
br.close();
} catch (IOException e) {
}
+ }
}
}
@@ -528,44 +1079,47 @@ public class Options implements Cloneable, OptionProvider {
* the constructor of the api doc root, so it depends on the order of "-link"
* and "-apiDocMap" parameters.
*/
- public String getApiDocRoot(String className) {
- if (apiDocMap.isEmpty())
+ public String getApiDocRoot(Name className) {
+ if (apiDocMap.isEmpty()) {
apiDocMap.put(Pattern.compile(".*"), DEFAULT_EXTERNAL_APIDOC);
+ }
for (Map.Entry mapEntry : apiDocMap.entrySet()) {
- if (mapEntry.getKey().matcher(className).matches())
+ if (mapEntry.getKey().matcher(className).matches()) {
return mapEntry.getValue();
+ }
}
return null;
}
/** Trim and append a file separator to the string */
private String fixApiDocRoot(String str) {
- if (str == null)
+ if (str == null) {
return null;
+ }
String fixed = str.trim();
- if (fixed.isEmpty())
+ if (fixed.isEmpty()) {
return "";
- if (File.separatorChar != '/')
+ }
+ if (File.separatorChar != '/') {
fixed = fixed.replace(File.separatorChar, '/');
- if (!fixed.endsWith("/"))
+ }
+ if (!fixed.endsWith("/")) {
fixed = fixed + "/";
+ }
return fixed;
}
- /** Set the options based on the command line parameters */
- public void setOptions(String[][] options) {
- for (String s[] : options)
- setOption(s);
- }
-
/** Set the options based on the tag elements of the ClassDoc parameter */
- public void setOptions(Doc p) {
- if (p == null)
+ public void setOptions(DocTrees docTrees, Element p) {
+ if (p == null) {
return;
+ }
- for (Tag tag : p.tags("opt"))
- setOption(StringUtil.tokenize(tag.text()));
+ List tags = TagUtil.getTag(docTrees, p, "opt");
+ for (String tag : tags) {
+ setOption(StringUtil.tokenize(tag));
+ }
}
/**
@@ -574,15 +1128,17 @@ public class Options implements Cloneable, OptionProvider {
*
* @return true if the string matches.
*/
- public boolean matchesHideExpression(String s) {
+ public boolean matchesHideExpression(CharSequence s) {
for (Pattern hidePattern : hidePatterns) {
// micro-optimization because the "all pattern" is heavily used in UmlGraphDoc
- if (hidePattern == allPattern)
+ if (hidePattern == allPattern) {
return true;
+ }
Matcher m = hidePattern.matcher(s);
- if (strictMatching ? m.matches() : m.find())
+ if (strictMatching ? m.matches() : m.find()) {
return true;
+ }
}
return false;
}
@@ -593,11 +1149,12 @@ public class Options implements Cloneable, OptionProvider {
*
* @return true if the string matches.
*/
- public boolean matchesIncludeExpression(String s) {
+ public boolean matchesIncludeExpression(CharSequence s) {
for (Pattern includePattern : includePatterns) {
Matcher m = includePattern.matcher(s);
- if (strictMatching ? m.matches() : m.find())
+ if (strictMatching ? m.matches() : m.find()) {
return true;
+ }
}
return false;
}
@@ -608,7 +1165,7 @@ public class Options implements Cloneable, OptionProvider {
*
* @return true if the string matches.
*/
- public boolean matchesCollPackageExpression(String s) {
+ public boolean matchesCollPackageExpression(CharSequence s) {
for (Pattern collPattern : collPackages) {
Matcher m = collPattern.matcher(s);
if (strictMatching ? m.matches() : m.find())
@@ -621,13 +1178,13 @@ public class Options implements Cloneable, OptionProvider {
// OptionProvider methods
// ----------------------------------------------------------------
- public Options getOptionsFor(ClassDoc cd) {
+ public Options getOptionsFor(DocTrees docTrees, TypeElement cd) {
Options localOpt = getGlobalOptions();
- localOpt.setOptions(cd);
+ localOpt.setOptions(docTrees, cd);
return localOpt;
}
- public Options getOptionsFor(String name) {
+ public Options getOptionsFor(CharSequence name) {
return getGlobalOptions();
}
@@ -635,11 +1192,11 @@ public class Options implements Cloneable, OptionProvider {
return (Options) clone();
}
- public void overrideForClass(Options opt, ClassDoc cd) {
+ public void overrideForClass(Options opt, TypeElement cd) {
// nothing to do
}
- public void overrideForClass(Options opt, String className) {
+ public void overrideForClass(Options opt, CharSequence className) {
// nothing to do
}
diff --git a/src/main/java/org/umlgraph/doclet/PackageMatcher.java b/src/main/java/org/umlgraph/doclet/PackageMatcher.java
index ecc3002..6a2d268 100644
--- a/src/main/java/org/umlgraph/doclet/PackageMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/PackageMatcher.java
@@ -1,24 +1,32 @@
package org.umlgraph.doclet;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.PackageDoc;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+
+import org.umlgraph.doclet.util.ElementUtil;
public class PackageMatcher implements ClassMatcher {
- protected PackageDoc packageDoc;
+ protected DocletEnvironment root;
+ protected PackageElement packageDoc;
- public PackageMatcher(PackageDoc packageDoc) {
- super();
+ public PackageMatcher(DocletEnvironment root, PackageElement packageDoc) {
+ this.root = root;
this.packageDoc = packageDoc;
}
- public boolean matches(ClassDoc cd) {
- return cd.containingPackage().equals(packageDoc);
+ public boolean matches(TypeElement cd) {
+ return packageDoc.equals(ElementUtil.getPackageOf(root, cd));
}
- public boolean matches(String name) {
- for (ClassDoc cd : packageDoc.allClasses())
- if (cd.qualifiedName().equals(name))
+ public boolean matches(CharSequence name) {
+ for (Element cd : packageDoc.getEnclosedElements()) {
+ if (cd instanceof TypeElement && ((TypeElement) cd).getQualifiedName().equals(name)) {
return true;
+ }
+ }
return false;
}
diff --git a/src/main/java/org/umlgraph/doclet/PackageView.java b/src/main/java/org/umlgraph/doclet/PackageView.java
index 83af937..e528e8a 100755
--- a/src/main/java/org/umlgraph/doclet/PackageView.java
+++ b/src/main/java/org/umlgraph/doclet/PackageView.java
@@ -1,8 +1,14 @@
package org.umlgraph.doclet;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.PackageDoc;
-import com.sun.javadoc.RootDoc;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+
+import org.umlgraph.doclet.util.ElementUtil;
+
+import com.sun.source.util.DocTrees;
/**
* A view designed for UMLDoc, filters out everything that it's not contained in
@@ -19,19 +25,23 @@ import com.sun.javadoc.RootDoc;
public class PackageView implements OptionProvider {
private static final String[] HIDE = new String[] { "hide" };
- private PackageDoc pd;
+ private PackageElement pd;
private OptionProvider parent;
private ClassMatcher matcher;
private String outputPath;
private Options opt;
+ private DocTrees docTrees;
- public PackageView(String outputFolder, PackageDoc pd, RootDoc root, OptionProvider parent) {
+ public PackageView(String outputFolder, PackageElement pd, DocletEnvironment root, OptionProvider parent) {
this.parent = parent;
this.pd = pd;
- this.matcher = new PackageMatcher(pd);
+ this.docTrees = root.getDocTrees();
+ this.matcher = new PackageMatcher(root, pd);
this.opt = parent.getGlobalOptions();
- this.opt.setOptions(pd);
- this.outputPath = pd.name().replace('.', '/') + "/" + pd.name() + ".dot";
+ this.opt.setOptions(docTrees, pd);
+ ModuleElement md = ElementUtil.getModuleOf(root, pd);
+ String pathPrefix = Runtime.version().major() > 10 && md != null ? md.getQualifiedName().toString() + "/" : "";
+ this.outputPath = pathPrefix + pd.getQualifiedName().toString().replace('.', '/') + "/" + pd.getSimpleName().toString() + ".dot";
}
public String getDisplayName() {
@@ -47,36 +57,39 @@ public class PackageView implements OptionProvider {
return go;
}
- public Options getOptionsFor(ClassDoc cd) {
+ public Options getOptionsFor(DocTrees dt, TypeElement cd) {
Options go = parent.getGlobalOptions();
overrideForClass(go, cd);
return go;
}
- public Options getOptionsFor(String name) {
+ public Options getOptionsFor(CharSequence name) {
Options go = parent.getGlobalOptions();
overrideForClass(go, name);
return go;
}
- public void overrideForClass(Options opt, ClassDoc cd) {
- opt.setOptions(cd);
+ public void overrideForClass(Options opt, TypeElement cd) {
+ opt.setOptions(docTrees, cd);
boolean inPackage = matcher.matches(cd);
- if (inPackage)
+ if (inPackage) {
opt.showQualified = false;
- boolean included = inPackage || this.opt.matchesIncludeExpression(cd.qualifiedName());
- if (!included || this.opt.matchesHideExpression(cd.qualifiedName()))
+ }
+ boolean included = inPackage || this.opt.matchesIncludeExpression(cd.getQualifiedName());
+ if (!included || this.opt.matchesHideExpression(cd.getQualifiedName())) {
opt.setOption(HIDE);
+ }
}
- public void overrideForClass(Options opt, String className) {
+ public void overrideForClass(Options opt, CharSequence className) {
opt.showQualified = false;
boolean inPackage = matcher.matches(className);
if (inPackage)
opt.showQualified = false;
boolean included = inPackage || this.opt.matchesIncludeExpression(className);
- if (!included || this.opt.matchesHideExpression(className))
+ if (!included || this.opt.matchesHideExpression(className)) {
opt.setOption(HIDE);
+ }
}
}
diff --git a/src/main/java/org/umlgraph/doclet/PatternMatcher.java b/src/main/java/org/umlgraph/doclet/PatternMatcher.java
index a809db6..7e5c4b5 100644
--- a/src/main/java/org/umlgraph/doclet/PatternMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/PatternMatcher.java
@@ -18,7 +18,7 @@ package org.umlgraph.doclet;
import java.util.regex.Pattern;
-import com.sun.javadoc.ClassDoc;
+import javax.lang.model.element.TypeElement;
/**
* Matches classes performing a regular expression match on the qualified class
@@ -33,11 +33,11 @@ public class PatternMatcher implements ClassMatcher {
this.pattern = pattern;
}
- public boolean matches(ClassDoc cd) {
- return matches(cd.toString());
+ public boolean matches(TypeElement cd) {
+ return matches(cd.getQualifiedName());
}
- public boolean matches(String name) {
+ public boolean matches(CharSequence name) {
return pattern.matcher(name).matches();
}
diff --git a/src/main/java/org/umlgraph/doclet/StringUtil.java b/src/main/java/org/umlgraph/doclet/StringUtil.java
index 1aee85b..441e4d9 100644
--- a/src/main/java/org/umlgraph/doclet/StringUtil.java
+++ b/src/main/java/org/umlgraph/doclet/StringUtil.java
@@ -19,6 +19,9 @@
package org.umlgraph.doclet;
+import javax.lang.model.element.Name;
+import javax.lang.model.util.Elements;
+
import java.util.*;
import java.util.regex.Pattern;
@@ -148,10 +151,11 @@ class StringUtil {
}
/** Removes the template specs from a class name. */
- public static String removeTemplate(String name) {
- int openIdx = name.indexOf('<');
- if (openIdx == -1)
+ public static Name removeTemplate(Elements elements, Name name) {
+ int openIdx = name.toString().indexOf('<');
+ if (openIdx == -1) {
return name;
+ }
StringBuilder buf = new StringBuilder(name.length());
for (int i = 0, depth = 0; i < name.length(); i++) {
char c = name.charAt(i);
@@ -162,26 +166,29 @@ class StringUtil {
else if (depth == 0)
buf.append(c);
}
- return buf.toString();
+ return elements.getName(buf.toString());
}
- public static String buildRelativePathFromClassNames(String contextPackageName, String classPackageName) {
+ public static String buildRelativePathFromClassNames(Name contextPackageName, String classPackageName) {
// path, relative to the root, of the destination class
- String[] contextClassPath = contextPackageName.split("\\.");
+ String[] contextClassPath = contextPackageName.toString().split("\\.");
String[] currClassPath = classPackageName.split("\\.");
// compute relative path between the context and the destination
// ... first, compute common part
int i = 0, e = Math.min(contextClassPath.length, currClassPath.length);
- while (i < e && contextClassPath[i].equals(currClassPath[i]))
+ while (i < e && contextClassPath[i].equals(currClassPath[i])) {
i++;
+ }
// ... go up with ".." to reach the common root
StringBuilder buf = new StringBuilder(classPackageName.length());
- for (int j = i; j < contextClassPath.length; j++)
+ for (int j = i; j < contextClassPath.length; j++) {
buf.append("../");
+ }
// ... go down from the common root to the destination
- for (int j = i; j < currClassPath.length; j++)
+ for (int j = i; j < currClassPath.length; j++) {
buf.append(currClassPath[j]).append('/'); // Always use HTML seperators
+ }
return buf.toString();
}
@@ -195,10 +202,11 @@ class StringUtil {
* users adhere to Java conventions, of beginning package names with a lowercase
* letter.
*
- * @param className
+ * @param name
* @return Splitting point (Either referring to a dot, or -1)
*/
- public static int splitPackageClass(String className) {
+ public static int splitPackageClass(CharSequence name) {
+ String className = name.toString();
int gen = className.indexOf('<'); // Begin before generics.
int end = gen >= 0 ? gen : className.length();
int start = className.lastIndexOf('.', end);
diff --git a/src/main/java/org/umlgraph/doclet/SubclassMatcher.java b/src/main/java/org/umlgraph/doclet/SubclassMatcher.java
index f9282c6..acbabb4 100644
--- a/src/main/java/org/umlgraph/doclet/SubclassMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/SubclassMatcher.java
@@ -2,8 +2,11 @@ package org.umlgraph.doclet;
import java.util.regex.Pattern;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.RootDoc;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+import javax.lang.model.element.TypeElement;
+
+import org.umlgraph.doclet.util.ElementUtil;
/**
* Matches every class that extends (directly or indirectly) a class matched by
@@ -11,25 +14,27 @@ import com.sun.javadoc.RootDoc;
*/
public class SubclassMatcher implements ClassMatcher {
- protected RootDoc root;
+ protected DocletEnvironment root;
protected Pattern pattern;
- public SubclassMatcher(RootDoc root, Pattern pattern) {
+ public SubclassMatcher(DocletEnvironment root, Pattern pattern) {
this.root = root;
this.pattern = pattern;
}
- public boolean matches(ClassDoc cd) {
+ public boolean matches(TypeElement cd) {
// if it's the class we're looking for return
- if (pattern.matcher(cd.toString()).matches())
+ if (pattern.matcher(cd.toString()).matches()) {
return true;
+ }
- // recurse on supeclass, if available
- return cd.superclass() == null ? false : matches(cd.superclass());
+ // recurse on superclass, if available
+ TypeElement scd = ElementUtil.getTypeElement(cd.getSuperclass());
+ return scd == null ? false : matches(scd);
}
- public boolean matches(String name) {
- ClassDoc cd = root.classNamed(name);
+ public boolean matches(CharSequence name) {
+ TypeElement cd = root.getElementUtils().getTypeElement(name);
return cd == null ? false : matches(cd);
}
diff --git a/src/main/java/org/umlgraph/doclet/TagScanner.java b/src/main/java/org/umlgraph/doclet/TagScanner.java
new file mode 100644
index 0000000..420eca8
--- /dev/null
+++ b/src/main/java/org/umlgraph/doclet/TagScanner.java
@@ -0,0 +1,121 @@
+package org.umlgraph.doclet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.umlgraph.doclet.util.CommentHelper;
+
+import com.sun.source.doctree.*;
+import com.sun.source.util.SimpleDocTreeVisitor;
+
+/**
+ * A visitor to gather the block tags found in a comment.
+ */
+public class TagScanner extends SimpleDocTreeVisitor {
+
+ private final Map> tags;
+
+ public TagScanner(Map> tags) {
+ this.tags = tags;
+ }
+
+ @Override
+ public Void visitDocComment(DocCommentTree tree, Void p) {
+ if (tree == null) {
+ return null;
+ }
+ String body = CommentHelper.getText(tree.getFullBody());
+ tags.put("comment", new ArrayList<>(List.of(body)));
+ return visit(tree.getBlockTags(), null);
+ }
+
+ @Override
+ public Void visitAuthor(AuthorTree node, Void p) {
+ String name = node.getTagName();
+ String content = node.getName().toString();
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitDeprecated(DeprecatedTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getBody());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitProvides(ProvidesTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getDescription());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitSince(SinceTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getBody());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitVersion(VersionTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getBody());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitParam(ParamTree node, Void p) {
+ String name = node.getTagName();
+ String content = node.getName().toString() + " " + CommentHelper.getText(node.getDescription());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitReturn(ReturnTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getDescription());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitSee(SeeTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getReference());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitThrows(ThrowsTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getDescription());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+ @Override
+ public Void visitUses(UsesTree node, Void p) {
+ String name = node.getTagName();
+ String content = CommentHelper.getText(node.getDescription());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+
+
+ @Override
+ public Void visitUnknownBlockTag(UnknownBlockTagTree tree, Void p) {
+ String name = tree.getTagName();
+ String content = CommentHelper.getText(tree.getContent());
+ tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content);
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/umlgraph/doclet/UmlGraph.java b/src/main/java/org/umlgraph/doclet/UmlGraph.java
index 5bc08a0..f14d76b 100644
--- a/src/main/java/org/umlgraph/doclet/UmlGraph.java
+++ b/src/main/java/org/umlgraph/doclet/UmlGraph.java
@@ -20,14 +20,26 @@
package org.umlgraph.doclet;
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
+import java.util.Set;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.Doc;
-import com.sun.javadoc.LanguageVersion;
-import com.sun.javadoc.RootDoc;
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+import jdk.javadoc.doclet.StandardDoclet;
+
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+import org.umlgraph.doclet.util.ElementUtil;
+import org.umlgraph.doclet.util.TagUtil;
/**
* Doclet API implementation
@@ -41,7 +53,7 @@ import com.sun.javadoc.RootDoc;
* @version $Revision$
* @author Diomidis Spinellis
*/
-public class UmlGraph {
+public class UmlGraph implements Doclet {
private static final String programName = "UmlGraph";
private static final String docletName = "org.umlgraph.doclet.UmlGraph";
@@ -49,25 +61,66 @@ public class UmlGraph {
/** Options used for commenting nodes */
private static Options commentOptions;
- /** Entry point through javadoc */
- public static boolean start(RootDoc root) throws IOException {
- Options opt = buildOptions(root);
- root.printNotice("UMLGraph doclet version " + Version.VERSION + " started");
- View[] views = buildViews(opt, root, root);
- if (views == null)
- return false;
- if (views.length == 0)
- buildGraph(root, opt, null);
- else
- for (int i = 0; i < views.length; i++)
- buildGraph(root, views[i], null);
- return true;
+ private Locale locale;
+ private Reporter reporter;
+ private Options options;
+ private StandardDoclet standard;
+
+ @Override
+ public void init(Locale locale, Reporter reporter) {
+ this.locale = locale;
+ this.reporter = reporter;
+ this.options = new Options();
+ this.standard = new StandardDoclet();
}
- public static void main(String args[]) {
- PrintWriter err = new PrintWriter(System.err);
- com.sun.tools.javadoc.Main.execute(programName, err, err, err, docletName, args);
+ @Override
+ public String getName() {
+ return docletName;
+ }
+
+ @Override
+ public Set extends Doclet.Option> getSupportedOptions() {
+ Set options = new HashSet<>(standard.getSupportedOptions());
+ for (Doclet.Option opt : this.options.OPTIONS) {
+ options.add(opt);
+ }
+ return options;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return standard.getSupportedSourceVersion();
+ }
+
+ /**
+ * Standard doclet entry point
+ *
+ * @param root
+ * @return
+ */
+ @Override
+ /** Entry point through javadoc */
+ public boolean run(DocletEnvironment root) {
+ reporter.print(Diagnostic.Kind.NOTE, "UMLGraph doclet version " + Version.VERSION + " started");
+
+ List views = buildViews(options, root, root);
+ if (views == null) {
+ return false;
+ }
+ try {
+ if (views.isEmpty()) {
+ buildGraph(reporter, root, options, null, null);
+ } else {
+ for (View view : views) {
+ buildGraph(reporter, root, options, view, null);
+ }
+ }
+ } catch (IOException ioe) {
+ reporter.print(Diagnostic.Kind.ERROR, "UMLGraph doclet error : " + ioe);
+ }
+ return true;
}
public static Options getCommentOptions() {
@@ -79,49 +132,62 @@ public class UmlGraph {
* the command line and the ones specified in the UMLOptions class, if
* available. Also create the globally accessible commentOptions object.
*/
- public static Options buildOptions(RootDoc root) {
- commentOptions = new Options();
- commentOptions.setOptions(root.options());
- commentOptions.setOptions(findClass(root, "UMLNoteOptions"));
+ public static Options buildOptions(DocletEnvironment root, Options o) {
+ commentOptions = o.clone();
+ commentOptions.setOptions(root.getDocTrees(), findClass(root, "UMLNoteOptions"));
commentOptions.shape = Shape.NOTE;
- Options opt = new Options();
- opt.setOptions(root.options());
- opt.setOptions(findClass(root, "UMLOptions"));
+ Options opt = o.clone();
+ opt.setOptions(root.getDocTrees(), findClass(root, "UMLOptions"));
return opt;
}
/** Return the ClassDoc for the specified class; null if not found. */
- private static ClassDoc findClass(RootDoc root, String name) {
- ClassDoc[] classes = root.classes();
- for (ClassDoc cd : classes)
- if (cd.name().equals(name))
- return cd;
+ private static TypeElement findClass(DocletEnvironment root, String name) {
+ Set extends Element> classes = root.getIncludedElements();
+ for (Element element : classes) {
+ if (element instanceof TypeElement && ((TypeElement) element).getQualifiedName().toString().equals(name)) {
+ return (TypeElement) element;
+ }
+ }
return null;
}
/**
* Builds and outputs a single graph according to the view overrides
*/
- public static void buildGraph(RootDoc root, OptionProvider op, Doc contextDoc) throws IOException {
- if (getCommentOptions() == null)
- buildOptions(root);
+ public static void buildGraph(Reporter reporter, DocletEnvironment root, Options options, OptionProvider op, Element contextDoc) throws IOException {
+ if (getCommentOptions() == null) {
+ buildOptions(root, options);
+ }
Options opt = op.getGlobalOptions();
- root.printNotice("Building " + op.getDisplayName());
- ClassDoc[] classes = root.classes();
+ reporter.print(Diagnostic.Kind.NOTE, "Building " + op.getDisplayName());
+ Set extends Element> elements = root.getIncludedElements();
+ Set classes = new HashSet<>();
+ for (Element element : elements) {
+ if (element instanceof TypeElement) {
+ classes.add((TypeElement) element);
+ }
+ }
ClassGraph c = new ClassGraph(root, op, contextDoc);
c.prologue();
- for (ClassDoc cd : classes)
+ for (TypeElement cd : classes) {
c.printClass(cd, true);
- for (ClassDoc cd : classes)
+ }
+ for (TypeElement cd : classes) {
c.printRelations(cd);
- if (opt.inferRelationships)
- for (ClassDoc cd : classes)
+ }
+ if (opt.inferRelationships) {
+ for (TypeElement cd : classes) {
c.printInferredRelations(cd);
- if (opt.inferDependencies)
- for (ClassDoc cd : classes)
+ }
+ }
+ if (opt.inferDependencies) {
+ for (TypeElement cd : classes) {
c.printInferredDependencies(cd);
+ }
+ }
c.printExtraClasses(root);
c.epilogue();
@@ -135,54 +201,51 @@ public class UmlGraph {
* @param viewRootDoc The RootDoc for the view classes (may be different, or may
* be the same as the srcRootDoc)
*/
- public static View[] buildViews(Options opt, RootDoc srcRootDoc, RootDoc viewRootDoc) {
+ public static List buildViews(Options opt, DocletEnvironment srcRootDoc, DocletEnvironment viewRootDoc) {
if (opt.viewName != null) {
- ClassDoc viewClass = viewRootDoc.classNamed(opt.viewName);
+ TypeElement viewClass = findClass(viewRootDoc, opt.viewName);
if (viewClass == null) {
System.out.println("View " + opt.viewName + " not found! Exiting without generating any output.");
return null;
}
- if (viewClass.tags("view").length == 0) {
+ if (TagUtil.getTag(viewRootDoc, viewClass, "view").isEmpty()) {
System.out.println(viewClass + " is not a view!");
return null;
}
- if (viewClass.isAbstract()) {
+ if (viewClass.getModifiers().contains(Modifier.ABSTRACT)) {
System.out.println(viewClass + " is an abstract view, no output will be generated!");
return null;
}
- return new View[] { buildView(srcRootDoc, viewClass, opt) };
+ return List.of(buildView(srcRootDoc, viewClass, opt));
} else if (opt.findViews) {
- List views = new ArrayList();
- ClassDoc[] classes = viewRootDoc.classes();
+ List views = new ArrayList<>();
+ Set extends Element> classes = viewRootDoc.getIncludedElements();
// find view classes
- for (int i = 0; i < classes.length; i++)
- if (classes[i].tags("view").length > 0 && !classes[i].isAbstract())
- views.add(buildView(srcRootDoc, classes[i], opt));
+ for (Element elmt : classes) {
+ if (!(elmt instanceof TypeElement)) {
+ continue;
+ }
+ TypeElement element = (TypeElement) elmt;
+ if (TagUtil.getTag(viewRootDoc, element, "view").size() > 0 && !element.getModifiers().contains(Modifier.ABSTRACT)) {
+ views.add(buildView(srcRootDoc, element, opt));
+ }
+ }
- return views.toArray(new View[views.size()]);
+ return views;
} else
- return new View[0];
+ return Collections.emptyList();
}
/**
* Builds a view along with its parent views, recursively
*/
- private static View buildView(RootDoc root, ClassDoc viewClass, OptionProvider provider) {
- ClassDoc superClass = viewClass.superclass();
- if (superClass == null || superClass.tags("view").length == 0)
+ private static View buildView(DocletEnvironment root, TypeElement viewClass, OptionProvider provider) {
+ TypeElement superClass = ElementUtil.getSuperclass(viewClass);
+ if (superClass == null || TagUtil.getTag(root, superClass, "view").isEmpty()) {
return new View(root, viewClass, provider);
+ }
return new View(root, viewClass, buildView(root, superClass, provider));
}
-
- /** Option checking */
- public static int optionLength(String option) {
- return Options.optionLength(option);
- }
-
- /** Indicate the language version we support */
- public static LanguageVersion languageVersion() {
- return LanguageVersion.JAVA_1_5;
- }
}
diff --git a/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java b/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java
index a22c021..6a8f796 100644
--- a/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java
+++ b/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java
@@ -3,22 +3,32 @@ package org.umlgraph.doclet;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.FileOutputStream;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStreamReader;
-import java.util.HashSet;
-import java.util.TreeSet;
+import java.io.OutputStreamWriter;
import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Locale;
import java.util.Set;
+import java.util.TreeSet;
import java.util.regex.Pattern;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.LanguageVersion;
-import com.sun.javadoc.PackageDoc;
-import com.sun.javadoc.RootDoc;
-import com.sun.tools.doclets.standard.Standard;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+import org.umlgraph.doclet.util.ElementUtil;
+
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+import jdk.javadoc.doclet.StandardDoclet;
/**
* Chaining doclet that runs the standart Javadoc doclet first, and on success,
@@ -29,17 +39,42 @@ import com.sun.tools.doclets.standard.Standard;
* @depend - - - WrappedClassDoc
* @depend - - - WrappedRootDoc
*/
-public class UmlGraphDoc {
- /**
- * Option check, forwards options to the standard doclet, if that one refuses
- * them, they are sent to UmlGraph
- */
- public static int optionLength(String option) {
- int result = Standard.optionLength(option);
- if (result != 0)
- return result;
- else
- return UmlGraph.optionLength(option);
+public class UmlGraphDoc implements Doclet {
+
+ private Locale locale;
+ private Reporter reporter;
+ private Options options;
+ private StandardDoclet standard;
+
+ public UmlGraphDoc() {
+ this.options = new Options();
+ this.standard = new StandardDoclet();
+ }
+
+ @Override
+ public void init(Locale locale, Reporter reporter) {
+ this.locale = locale;
+ this.reporter = reporter;
+ this.standard.init(locale, reporter);
+ }
+
+ @Override
+ public String getName() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public Set extends Doclet.Option> getSupportedOptions() {
+ Set options = new HashSet<>(standard.getSupportedOptions());
+ for (Doclet.Option opt : this.options.OPTIONS) {
+ options.add(opt);
+ }
+ return options;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return standard.getSupportedSourceVersion();
}
/**
@@ -48,56 +83,53 @@ public class UmlGraphDoc {
* @param root
* @return
*/
- public static boolean start(RootDoc root) {
- root.printNotice("UmlGraphDoc version " + Version.VERSION + ", running the standard doclet");
- Standard.start(root);
- root.printNotice("UmlGraphDoc version " + Version.VERSION + ", altering javadocs");
+ @Override
+ public boolean run(DocletEnvironment root) {
+ reporter.print(Diagnostic.Kind.NOTE, "UmlGraphDoc version " + Version.VERSION + ", running the standard doclet");
+ standard.run(root);
+ reporter.print(Diagnostic.Kind.NOTE, "UmlGraphDoc version " + Version.VERSION + ", altering javadocs");
try {
- String outputFolder = findOutputPath(root.options());
-
- Options opt = UmlGraph.buildOptions(root);
- opt.setOptions(root.options());
+ Options opt = UmlGraph.buildOptions(root, this.options);
// in javadoc enumerations are always printed
opt.showEnumerations = true;
opt.relativeLinksForSourcePackages = true;
// enable strict matching for hide expressions
opt.strictMatching = true;
-// root.printNotice(opt.toString());
+ reporter.print(Diagnostic.Kind.NOTE, opt.toString());
- generatePackageDiagrams(root, opt, outputFolder);
- generateContextDiagrams(root, opt, outputFolder);
+ generatePackageDiagrams(root, reporter, opt, this.options.outputDirectory);
+ generateContextDiagrams(root, reporter, opt, this.options.outputDirectory);
} catch (Throwable t) {
- root.printWarning("Error: " + t.toString());
+ reporter.print(Diagnostic.Kind.WARNING, "Error: " + t.toString());
t.printStackTrace();
return false;
}
return true;
}
- /**
- * Standand doclet entry
- *
- * @return
- */
- public static LanguageVersion languageVersion() {
- return Standard.languageVersion();
- }
-
/**
* Generates the package diagrams for all of the packages that contain classes
* among those returned by RootDoc.class()
*/
- private static void generatePackageDiagrams(RootDoc root, Options opt, String outputFolder) throws IOException {
- Set packages = new HashSet();
- for (ClassDoc classDoc : root.classes()) {
- PackageDoc packageDoc = classDoc.containingPackage();
- if (!packages.contains(packageDoc.name())) {
- packages.add(packageDoc.name());
+ private static void generatePackageDiagrams(DocletEnvironment root, Reporter reporter, Options opt, String outputFolder) throws IOException {
+ Set packages = new HashSet<>();
+ for (Element classDoc : root.getIncludedElements()) {
+ PackageElement packageDoc = null;
+ if (classDoc instanceof PackageElement) {
+ packageDoc = (PackageElement) classDoc;
+ } else if (classDoc instanceof TypeElement) {
+ packageDoc = ElementUtil.getPackageOf(root, classDoc);
+ } else {
+ continue;
+ }
+ if (!packages.contains(packageDoc.getSimpleName())) {
+ reporter.print(Diagnostic.Kind.WARNING, "Package processed " + packageDoc.getQualifiedName());
+ packages.add(packageDoc.getSimpleName());
OptionProvider view = new PackageView(outputFolder, packageDoc, root, opt);
- UmlGraph.buildGraph(root, view, packageDoc);
- runGraphviz(opt.dotExecutable, outputFolder, packageDoc.name(), packageDoc.name(), root);
- alterHtmlDocs(opt, outputFolder, packageDoc.name(), packageDoc.name(), "package-summary.html",
- Pattern.compile("([Hh]2>)|()|( classDocs = new TreeSet(new Comparator() {
- public int compare(ClassDoc cd1, ClassDoc cd2) {
- return cd1.name().compareTo(cd2.name());
+ private static void generateContextDiagrams(DocletEnvironment root, Reporter reporter, Options opt, String outputFolder) throws IOException {
+ Set classDocs = new TreeSet<>(new Comparator<>() {
+ public int compare(TypeElement cd1, TypeElement cd2) {
+ return cd1.getSimpleName().toString().compareTo(cd2.getSimpleName().toString());
}
});
- for (ClassDoc classDoc : root.classes())
- classDocs.add(classDoc);
+ for (Element classDoc : root.getIncludedElements()) {
+ if (classDoc instanceof TypeElement) {
+ classDocs.add((TypeElement) classDoc);
+ }
+ }
ContextView view = null;
- for (ClassDoc classDoc : classDocs) {
+ for (TypeElement classDoc : classDocs) {
+ reporter.print(Diagnostic.Kind.WARNING, "Class processed " + classDoc.getQualifiedName());
try {
- if (view == null)
+ if (view == null) {
view = new ContextView(outputFolder, classDoc, root, opt);
- else
+ } else {
view.setContextCenter(classDoc);
- UmlGraph.buildGraph(root, view, classDoc);
- runGraphviz(opt.dotExecutable, outputFolder, classDoc.containingPackage().name(), classDoc.name(),
- root);
- alterHtmlDocs(opt, outputFolder, classDoc.containingPackage().name(), classDoc.name(),
- classDoc.name() + ".html",
- Pattern.compile(".*(Class|Interface|Enum) " + classDoc.name() + ".*"), root);
+ }
+ UmlGraph.buildGraph(reporter, root, opt, view, classDoc);
+ runGraphviz(opt.dotExecutable, outputFolder, ElementUtil.getModuleOf(root, classDoc), ElementUtil.getPackageOf(root, classDoc).getQualifiedName(), classDoc.getSimpleName(), reporter);
+ alterHtmlDocs(opt, outputFolder, ElementUtil.getModuleOf(root, classDoc), ElementUtil.getPackageOf(root, classDoc).getQualifiedName(), classDoc.getSimpleName(),
+ classDoc.getSimpleName() + ".html",
+ Pattern.compile(".*(Class|Interface|Enum) " + classDoc.getSimpleName() + ".*"), reporter);
} catch (Exception e) {
- throw new RuntimeException("Error generating " + classDoc.name(), e);
+ throw new RuntimeException("Error generating " + classDoc.getSimpleName(), e);
}
}
}
@@ -137,24 +173,29 @@ public class UmlGraphDoc {
* Runs Graphviz dot building both a diagram (in png format) and a client side
* map for it.
*/
- private static void runGraphviz(String dotExecutable, String outputFolder, String packageName, String name,
- RootDoc root) {
+ private static void runGraphviz(String dotExecutable, String outputFolder, ModuleElement module, Name packageName, Name name, Reporter reporter) {
if (dotExecutable == null) {
dotExecutable = "dot";
}
- File dotFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".dot");
- File svgFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".svg");
+ String fileName = packageName.toString().replace(".", "/") + "/" + name;
+ if (Runtime.version().major() > 10 && module != null) {
+ fileName = module.getQualifiedName().toString() + '/' + fileName;
+ }
+ File dotFile = new File(outputFolder, fileName + ".dot");
+ File svgFile = new File(outputFolder, fileName + ".svg");
try {
Process p = Runtime.getRuntime().exec(new String[] { dotExecutable, "-Tsvg", "-o",
svgFile.getAbsolutePath(), dotFile.getAbsolutePath() });
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line;
- while ((line = reader.readLine()) != null)
- root.printWarning(line);
+ while ((line = reader.readLine()) != null) {
+ reporter.print(Diagnostic.Kind.WARNING, line);
+ }
int result = p.waitFor();
- if (result != 0)
- root.printWarning("Errors running Graphviz on " + dotFile);
+ if (result != 0) {
+ reporter.print(Diagnostic.Kind.WARNING, "Errors running Graphviz on " + dotFile);
+ }
} catch (Exception e) {
e.printStackTrace();
System.err.println("Ensure that dot is in your path and that its path does not contain spaces");
@@ -186,10 +227,11 @@ public class UmlGraphDoc {
* point, and inserts the diagram image reference and a client side map in that
* point.
*/
- private static void alterHtmlDocs(Options opt, String outputFolder, String packageName, String className,
- String htmlFileName, Pattern insertPointPattern, RootDoc root) throws IOException {
+ private static void alterHtmlDocs(Options opt, String outputFolder, ModuleElement module, Name packageName, Name className,
+ String htmlFileName, Pattern insertPointPattern, Reporter reporter) throws IOException {
// setup files
- File output = new File(outputFolder, packageName.replace(".", "/"));
+ String prefix = Runtime.version().major() > 10 && module != null ? module.getQualifiedName().toString() + '/' : "";
+ File output = new File(outputFolder, prefix + packageName.toString().replace(".", "/"));
File htmlFile = new File(output, htmlFileName);
File alteredFile = new File(htmlFile.getAbsolutePath() + ".uml");
if (!htmlFile.exists()) {
@@ -213,12 +255,14 @@ public class UmlGraphDoc {
matched = true;
String tag;
- if (opt.autoSize)
+ if (opt.autoSize) {
tag = String.format(UML_AUTO_SIZED_DIV_TAG, className);
- else
+ } else {
tag = String.format(UML_DIV_TAG, className);
- if (opt.collapsibleDiagrams)
+ }
+ if (opt.collapsibleDiagrams) {
tag = String.format(EXPANDABLE_UML, tag, "Show UML class diagram", "Hide UML class diagram");
+ }
writer.write("");
writer.newLine();
writer.write(tag);
@@ -226,10 +270,12 @@ public class UmlGraphDoc {
}
}
} finally {
- if (writer != null)
+ if (writer != null) {
writer.close();
- if (reader != null)
+ }
+ if (reader != null) {
reader.close();
+ }
}
// if altered, delete old file and rename new one to the old file name
@@ -237,20 +283,9 @@ public class UmlGraphDoc {
htmlFile.delete();
alteredFile.renameTo(htmlFile);
} else {
- root.printNotice("Warning, could not find a line that matches the pattern '"
- + insertPointPattern.pattern() + "'.\n Class diagram reference not inserted");
+ reporter.print(Diagnostic.Kind.NOTE, "Warning, could not find a line that matches the pattern '"
+ + insertPointPattern.pattern() + "'.\n Class diagram reference not inserted");
alteredFile.delete();
}
}
-
- /**
- * Returns the output path specified on the javadoc options
- */
- private static String findOutputPath(String[][] options) {
- for (int i = 0; i < options.length; i++) {
- if (options[i][0].equals("-d"))
- return options[i][1];
- }
- return ".";
- }
}
diff --git a/src/main/java/org/umlgraph/doclet/View.java b/src/main/java/org/umlgraph/doclet/View.java
index f3b2dd4..b58ecbe 100644
--- a/src/main/java/org/umlgraph/doclet/View.java
+++ b/src/main/java/org/umlgraph/doclet/View.java
@@ -23,9 +23,12 @@ import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.RootDoc;
-import com.sun.javadoc.Tag;
+import javax.lang.model.element.TypeElement;
+
+import org.umlgraph.doclet.util.TagUtil;
+
+import jdk.javadoc.doclet.DocletEnvironment;
+import com.sun.source.util.DocTrees;
/**
* Contains the definition of a View. A View is a set of option overrides that
@@ -44,32 +47,35 @@ import com.sun.javadoc.Tag;
*
*/
public class View implements OptionProvider {
- Map> optionOverrides = new LinkedHashMap>();
- ClassDoc viewDoc;
+ Map> optionOverrides = new LinkedHashMap<>();
+ TypeElement viewDoc;
OptionProvider provider;
List globalOptions;
- RootDoc root;
+ DocletEnvironment root;
/**
* Builds a view given the class that contains its definition
*/
- public View(RootDoc root, ClassDoc c, OptionProvider provider) {
+ public View(DocletEnvironment root, TypeElement c, OptionProvider provider) {
this.viewDoc = c;
this.provider = provider;
this.root = root;
- Tag[] tags = c.tags();
+ Map> tags = TagUtil.getTags(root, viewDoc);
ClassMatcher currMatcher = null;
// parse options, get the global ones, and build a map of the
// pattern matched overrides
globalOptions = new ArrayList();
- for (int i = 0; i < tags.length; i++) {
- if (tags[i].name().equals("@match")) {
- currMatcher = buildMatcher(tags[i].text());
+ if (tags.get("match") != null) {
+ for (String text : tags.get("match")) {
+ currMatcher = buildMatcher(text);
if (currMatcher != null) {
- optionOverrides.put(currMatcher, new ArrayList());
+ optionOverrides.put(currMatcher, new ArrayList<>());
}
- } else if (tags[i].name().equals("@opt")) {
- String[] opts = StringUtil.tokenize(tags[i].text());
+ }
+ }
+ if (tags.get("opt") != null) {
+ for (String text : tags.get("opt")) {
+ String[] opts = StringUtil.tokenize(text);
opts[0] = "-" + opts[0];
if (currMatcher == null) {
globalOptions.add(opts);
@@ -118,14 +124,14 @@ public class View implements OptionProvider {
// OptionProvider methods
// ----------------------------------------------------------------
- public Options getOptionsFor(ClassDoc cd) {
+ public Options getOptionsFor(DocTrees dt, TypeElement cd) {
Options localOpt = getGlobalOptions();
overrideForClass(localOpt, cd);
- localOpt.setOptions(cd);
+ localOpt.setOptions(dt, cd);
return localOpt;
}
- public Options getOptionsFor(String name) {
+ public Options getOptionsFor(CharSequence name) {
Options localOpt = getGlobalOptions();
overrideForClass(localOpt, name);
return localOpt;
@@ -136,34 +142,42 @@ public class View implements OptionProvider {
boolean outputSet = false;
for (String[] opts : globalOptions) {
- if (Options.matchOption(opts[0], "output"))
+ if (Options.matchOption(opts[0], "output")) {
outputSet = true;
+ }
go.setOption(opts);
}
- if (!outputSet)
- go.setOption(new String[] { "output", viewDoc.name() + ".dot" });
+ if (!outputSet) {
+ go.setOption(new String[] { "output", viewDoc.getSimpleName() + ".dot" });
+ }
return go;
}
- public void overrideForClass(Options opt, ClassDoc cd) {
+ public void overrideForClass(Options opt, TypeElement cd) {
provider.overrideForClass(opt, cd);
- for (ClassMatcher cm : optionOverrides.keySet())
- if (cm.matches(cd))
- for (String[] override : optionOverrides.get(cm))
+ for (ClassMatcher cm : optionOverrides.keySet()) {
+ if (cm.matches(cd)) {
+ for (String[] override : optionOverrides.get(cm)) {
opt.setOption(override);
+ }
+ }
+ }
}
- public void overrideForClass(Options opt, String className) {
+ public void overrideForClass(Options opt, CharSequence className) {
provider.overrideForClass(opt, className);
- for (ClassMatcher cm : optionOverrides.keySet())
- if (cm.matches(className))
- for (String[] override : optionOverrides.get(cm))
+ for (ClassMatcher cm : optionOverrides.keySet()) {
+ if (cm.matches(className)) {
+ for (String[] override : optionOverrides.get(cm)) {
opt.setOption(override);
+ }
+ }
+ }
}
public String getDisplayName() {
- return "view " + viewDoc.name();
+ return "view " + viewDoc.getSimpleName();
}
}
diff --git a/src/main/java/org/umlgraph/doclet/Visibility.java b/src/main/java/org/umlgraph/doclet/Visibility.java
index 831d64b..5804de7 100644
--- a/src/main/java/org/umlgraph/doclet/Visibility.java
+++ b/src/main/java/org/umlgraph/doclet/Visibility.java
@@ -18,7 +18,10 @@
*/
package org.umlgraph.doclet;
-import com.sun.javadoc.ProgramElementDoc;
+import java.util.Set;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
/**
* Enumerates the possible visibilities in a Java program. For brevity, package
@@ -35,14 +38,16 @@ public enum Visibility {
this.symbol = symbol;
}
- public static Visibility get(ProgramElementDoc doc) {
- if (doc.isPrivate())
+ public static Visibility get(Element doc) {
+ Set mods = doc.getModifiers();
+ if (mods.contains(Modifier.PRIVATE)) {
return PRIVATE;
- else if (doc.isPackagePrivate())
- return PACKAGE;
- else if (doc.isProtected())
+ } else if (mods.contains(Modifier.PROTECTED)) {
return PROTECTED;
- else
+ } else if (mods.contains(Modifier.PUBLIC)) {
return PUBLIC;
+ } else {
+ return PACKAGE;
+ }
}
}
diff --git a/src/main/java/org/umlgraph/doclet/util/CommentHelper.java b/src/main/java/org/umlgraph/doclet/util/CommentHelper.java
new file mode 100644
index 0000000..a3ac18f
--- /dev/null
+++ b/src/main/java/org/umlgraph/doclet/util/CommentHelper.java
@@ -0,0 +1,168 @@
+package org.umlgraph.doclet.util;
+
+import static com.sun.source.doctree.DocTree.Kind.CODE;
+
+import java.util.List;
+
+import com.sun.source.doctree.AttributeTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.doctree.EndElementTree;
+import com.sun.source.doctree.EntityTree;
+import com.sun.source.doctree.LinkTree;
+import com.sun.source.doctree.LiteralTree;
+import com.sun.source.doctree.ReferenceTree;
+import com.sun.source.doctree.SeeTree;
+import com.sun.source.doctree.SerialTree;
+import com.sun.source.doctree.StartElementTree;
+import com.sun.source.doctree.TextTree;
+import com.sun.source.doctree.UnknownBlockTagTree;
+import com.sun.source.doctree.ValueTree;
+import com.sun.source.doctree.AttributeTree.ValueKind;
+import com.sun.source.util.SimpleDocTreeVisitor;
+
+public class CommentHelper {
+ public static final String SPACER = " ";
+
+ public static String getText(List extends DocTree> list) {
+ StringBuilder sb = new StringBuilder();
+ for (DocTree dt : list) {
+ sb.append(getText0(dt));
+ }
+ return sb.toString();
+ }
+
+ public static String getText(DocTree dt) {
+ return getText0(dt).toString();
+ }
+
+ private static StringBuilder getText0(DocTree dt) {
+ final StringBuilder sb = new StringBuilder();
+ new SimpleDocTreeVisitor() {
+ @Override
+ public Void visitAttribute(AttributeTree node, Void p) {
+ sb.append(SPACER).append(node.getName());
+ if (node.getValueKind() == ValueKind.EMPTY) {
+ return null;
+ }
+
+ sb.append("=");
+ String quote;
+ switch (node.getValueKind()) {
+ case DOUBLE:
+ quote = "\"";
+ break;
+ case SINGLE:
+ quote = "\'";
+ break;
+ default:
+ quote = "";
+ break;
+ }
+ sb.append(quote);
+ node.getValue().stream().forEach((dt) -> {
+ dt.accept(this, null);
+ });
+ sb.append(quote);
+ return null;
+ }
+
+ @Override
+ public Void visitEndElement(EndElementTree node, Void p) {
+ sb.append("")
+ .append(node.getName())
+ .append(">");
+ return null;
+ }
+
+ @Override
+ public Void visitEntity(EntityTree node, Void p) {
+ sb.append(node.toString());
+ return null;
+ }
+
+ @Override
+ public Void visitLink(LinkTree node, Void p) {
+ if (node.getReference() == null) {
+ return null;
+ }
+
+ node.getReference().accept(this, null);
+ node.getLabel().stream().forEach((dt) -> {
+ dt.accept(this, null);
+ });
+ return null;
+ }
+
+ @Override
+ public Void visitLiteral(LiteralTree node, Void p) {
+ if (node.getKind() == CODE) {
+ sb.append("<").append(node.getKind().tagName).append(">");
+ }
+ sb.append(node.getBody().toString());
+ if (node.getKind() == CODE) {
+ sb.append("").append(node.getKind().tagName).append(">");
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitReference(ReferenceTree node, Void p) {
+ sb.append(node.getSignature());
+ return null;
+ }
+
+ @Override
+ public Void visitSee(SeeTree node, Void p) {
+ node.getReference().stream().forEach((dt) -> {
+ dt.accept(this, null);
+ });
+ return null;
+ }
+
+ @Override
+ public Void visitSerial(SerialTree node, Void p) {
+ node.getDescription().stream().forEach((dt) -> {
+ dt.accept(this, null);
+ });
+ return null;
+ }
+
+ @Override
+ public Void visitStartElement(StartElementTree node, Void p) {
+ sb.append("<");
+ sb.append(node.getName());
+ node.getAttributes().stream().forEach((dt) -> {
+ dt.accept(this, null);
+ });
+ sb.append((node.isSelfClosing() ? "/>" : ">"));
+ return null;
+ }
+
+ @Override
+ public Void visitText(TextTree node, Void p) {
+ sb.append(node.getBody());
+ return null;
+ }
+
+ @Override
+ public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
+ node.getContent().stream().forEach((dt) -> {
+ dt.accept(this, null);
+ });
+ return null;
+ }
+
+ @Override
+ public Void visitValue(ValueTree node, Void p) {
+ return node.getReference().accept(this, null);
+ }
+
+ @Override
+ protected Void defaultAction(DocTree node, Void p) {
+ sb.append(node.toString());
+ return null;
+ }
+ }.visit(dt, null);
+ return sb;
+ }
+}
diff --git a/src/main/java/org/umlgraph/doclet/util/ElementUtil.java b/src/main/java/org/umlgraph/doclet/util/ElementUtil.java
new file mode 100644
index 0000000..e00c884
--- /dev/null
+++ b/src/main/java/org/umlgraph/doclet/util/ElementUtil.java
@@ -0,0 +1,159 @@
+package org.umlgraph.doclet.util;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import jdk.javadoc.doclet.DocletEnvironment;
+
+public class ElementUtil {
+
+ public static CharSequence getSimpleName(Types types, TypeMirror t) {
+ if (t instanceof ArrayType) {
+ return getSimpleName(types, ((ArrayType) t).getComponentType()) + "[]";
+ }
+ if (t.getKind().isPrimitive()) {
+ return t.getKind().name().toLowerCase();
+ }
+ Element element = types.asElement(t);
+ if (element != null) {
+ return element.getSimpleName();
+ }
+ return "";
+ }
+
+ public static CharSequence getQualifiedName(Types types, TypeMirror t) {
+ if (t instanceof ArrayType) {
+ return getQualifiedName(types, ((ArrayType) t).getComponentType()) + "[]";
+ }
+ if (t.getKind().isPrimitive()) {
+ return t.getKind().name().toLowerCase();
+ }
+ Element element = types.asElement(t);
+ if (element instanceof TypeElement) {
+ return ((TypeElement) element).getQualifiedName();
+ }
+ return "";
+ }
+
+ public static TypeElement containingTypeElement(Element c) {
+ if (c instanceof VariableElement) {
+ Element enclosing = c.getEnclosingElement();
+ return enclosing instanceof TypeElement ? (TypeElement) enclosing : null;
+ } else if (c instanceof ExecutableElement) {
+ Element enclosing = c.getEnclosingElement();
+ return enclosing instanceof TypeElement ? (TypeElement) enclosing : null;
+ }
+ if (c == null) {
+ System.err.println("containingElement will return null for null element");
+ return null;
+ }
+ System.err.println("containingElement will return null for kind " + c.getKind() + " class " + c.getClass() + " name " + c.getSimpleName());
+ return null;
+ }
+
+ public static TypeElement getTypeElement(TypeMirror clazz) {
+ if (clazz == null || clazz.getKind() == TypeKind.NONE) {
+ return null;
+ }
+ if (clazz instanceof ArrayType) {
+ return getTypeElement(((ArrayType) clazz).getComponentType());
+ }
+ if (!(clazz instanceof DeclaredType)) {
+ return null;
+ }
+ Element scd = ((DeclaredType) clazz).asElement();
+ return scd instanceof TypeElement ? (TypeElement) scd : null;
+ }
+
+ public static TypeMirror getSuperclassType(DeclaredType pt) {
+ return pt.asElement() instanceof TypeElement ? ((TypeElement) pt.asElement()).getSuperclass() : null;
+ }
+
+ public static TypeElement getSuperclass(TypeElement element) {
+ return getTypeElement(element.getSuperclass());
+ }
+
+ public static List extends TypeMirror> getInterfacesTypes(TypeElement element) {
+ return element.getInterfaces();
+ }
+
+ public static List getInterfaces(TypeElement element) {
+ List extends TypeMirror> interfaces = getInterfacesTypes(element);
+ return interfaces.stream().map(i -> getTypeElement(i)).filter(i -> i != null).collect(Collectors.toList());
+ }
+
+ public static ModuleElement getModuleOf(DocletEnvironment root, Element element) {
+ return getModuleOf(root.getElementUtils(), element);
+ }
+
+ public static ModuleElement getModuleOf(Elements elements, Element element) {
+ if (element == null) {
+ return null;
+ }
+ if (element instanceof ModuleElement) {
+ return (ModuleElement) element;
+ }
+ return elements.getModuleOf(element);
+ }
+
+ public static PackageElement getPackageOf(DocletEnvironment root, Element element) {
+ return getPackageOf(root.getElementUtils(), element);
+ }
+
+ public static PackageElement getPackageOf(Elements elements, Element element) {
+ if (element == null) {
+ return null;
+ }
+ if (element instanceof PackageElement) {
+ return (PackageElement) element;
+ }
+ return elements.getPackageOf(element);
+ }
+
+ public static List getFields(TypeElement element) {
+ List extends Element> enclosed = element.getEnclosedElements();
+ return ElementFilter.fieldsIn(enclosed).stream().filter(v -> v.getKind() == ElementKind.FIELD).collect(Collectors.toList());
+ }
+
+ public static List getEnumConstants(TypeElement element) {
+ List extends Element> enclosed = element.getEnclosedElements();
+ return ElementFilter.fieldsIn(enclosed).stream().filter(v -> v.getKind() == ElementKind.ENUM_CONSTANT).collect(Collectors.toList());
+ }
+
+ public static List getMethods(TypeElement element) {
+ List extends Element> enclosed = element.getEnclosedElements();
+ return ElementFilter.methodsIn(enclosed);
+ }
+
+ public static List getConstructors(TypeElement element) {
+ List extends Element> enclosed = element.getEnclosedElements();
+ return ElementFilter.constructorsIn(enclosed);
+ }
+
+ public static String dimensions(TypeMirror type) {
+ StringBuilder sb = new StringBuilder();
+
+ // First append root component type
+ TypeMirror t = type;
+ while (t.getKind() == TypeKind.ARRAY) {
+ sb.append("[]");
+ t = ((ArrayType) t).getComponentType();
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/umlgraph/doclet/util/TagUtil.java b/src/main/java/org/umlgraph/doclet/util/TagUtil.java
new file mode 100644
index 0000000..e1c1c02
--- /dev/null
+++ b/src/main/java/org/umlgraph/doclet/util/TagUtil.java
@@ -0,0 +1,50 @@
+package org.umlgraph.doclet.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.lang.model.element.Element;
+
+import org.umlgraph.doclet.TagScanner;
+
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.util.DocTrees;
+
+import jdk.javadoc.doclet.DocletEnvironment;
+
+public class TagUtil {
+
+ public static Map> getTags(DocletEnvironment root, Element c) {
+ return getTags(root.getDocTrees(), c);
+ }
+
+ public static Map> getTags(DocTrees docTrees, Element c) {
+ DocCommentTree tree = docTrees.getDocCommentTree(c);
+ Map> tagsByName = new HashMap<>();
+ new TagScanner(tagsByName).visitDocComment(tree, null);
+ return tagsByName;
+ }
+
+ public static String getComment(DocletEnvironment root, Element c) {
+ return getComment(root.getDocTrees(), c);
+ }
+
+ public static String getComment(DocTrees docTrees, Element c) {
+ List comments = getTag(docTrees, c, "comment");
+ if (comments == null || comments.isEmpty()) {
+ return "";
+ }
+ return comments.get(0);
+ }
+
+ public static List getTag(DocletEnvironment root, Element c, String tagName) {
+ return getTag(root.getDocTrees(), c, tagName);
+ }
+
+ public static List getTag(DocTrees docTrees, Element c, String tagName) {
+ List tags = getTags(docTrees, c).get(tagName);
+ return tags == null ? Collections.emptyList() : tags;
+ }
+}
|