The packages passed as an
- * argument are the ones specified on the command line.
- *
Local URLs will be generated for these packages.
- * @param root The root of docs as provided by the javadoc API
- * @param optionProvider The main option provider
- * @param contextDoc The current context for generating relative links, may be a ClassDoc
- * or a PackageDoc (used by UMLDoc)
- */
- public ClassGraph(RootDoc root, OptionProvider optionProvider, Doc contextDoc) {
- this.optionProvider = optionProvider;
- this.collectionClassDoc = root.classNamed("java.util.Collection");
- this.mapClassDoc = root.classNamed("java.util.Map");
-
- // to gather the packages containing specified classes, loop thru them and gather
- // package definitions. User root.specifiedPackages is not safe, since the user
- // may specify just a list of classes (human users usually don't, but automated tools do)
- rootClasses = new HashSet();
- for (ClassDoc classDoc : root.classes()) {
- rootClasses.add(classDoc.qualifiedName());
- rootClassdocs.put(classDoc.qualifiedName(), classDoc);
- }
-
- // determine the context path, relative to the root
- if (contextDoc instanceof ClassDoc)
- contextPackageName = ((ClassDoc) contextDoc).containingPackage().name();
- else if (contextDoc instanceof PackageDoc)
- contextPackageName = ((PackageDoc) contextDoc).name();
- else
- contextPackageName = null; // Not available
-
- Options opt = optionProvider.getGlobalOptions();
- linePrefix = opt.compact ? "" : "\t";
- linePostfix = opt.compact ? "" : "\n";
- }
-
+ // used only when generating context class diagrams in UMLDoc, to generate the
+ // proper relative links to other classes in the image map
+ protected final Name contextPackageName;
+
+ /**
+ * Create a new ClassGraph.
+ *
+ * The packages passed as an argument are the ones specified on the command
+ * line.
+ *
+ *
+ * Local URLs will be generated for these packages.
+ *
+ *
+ * @param root The root of docs as provided by the javadoc API
+ * @param optionProvider The main option provider
+ * @param contextDoc The current context for generating relative links, may
+ * be a ClassDoc or a PackageDoc (used by UMLDoc)
+ */
+ public ClassGraph(DocletEnvironment root, OptionProvider optionProvider, Element contextDoc) {
+ this.optionProvider = optionProvider;
+ this.elementUtils = root.getElementUtils();
+ this.docTrees = root.getDocTrees();
+ this.types = root.getTypeUtils();
+ this.fileManager = root.getJavaFileManager();
+ this.collectionClassDoc = elementUtils.getTypeElement("java.util.Collection");
+ this.mapClassDoc = elementUtils.getTypeElement("java.util.Map");
+
+ // to gather the packages containing specified classes, loop thru them and
+ // gather
+ // package definitions. User root.specifiedPackages is not safe, since the user
+ // may specify just a list of classes (human users usually don't, but automated
+ // tools do)
+ rootClasses = new HashSet<>();
+ for (Element classDoc : root.getIncludedElements()) {
+ if (classDoc instanceof TypeElement) {
+ rootClasses.add(((TypeElement) classDoc).getQualifiedName());
+ rootClassdocs.put(((TypeElement) classDoc).getQualifiedName(), (TypeElement) classDoc);
+ }
+ }
+
+ // determine the context path, relative to the root
+ if (contextDoc instanceof TypeElement) {
+ contextPackageName = ElementUtil.getPackageOf(elementUtils, contextDoc).getQualifiedName();
+ } else if (contextDoc instanceof PackageElement) {
+ contextPackageName = ((PackageElement) contextDoc).getQualifiedName();
+ } else {
+ contextPackageName = null; // Not available
+ }
+
+ Options opt = optionProvider.getGlobalOptions();
+ linePrefix = opt.compact ? "" : "\t";
+ linePostfix = opt.compact ? "" : "\n";
+ }
/** Return the class's name, possibly by stripping the leading path */
- private static String qualifiedName(Options opt, String r) {
- if (opt.hideGenerics)
- r = removeTemplate(r);
- // Fast path - nothing to do:
- if (opt.showQualified && (opt.showQualifiedGenerics || r.indexOf('<') < 0))
- return r;
- StringBuilder buf = new StringBuilder(r.length());
- qualifiedNameInner(opt, r, buf, 0, !opt.showQualified);
- return buf.toString();
+ private String qualifiedName(Options opt, Name className) {
+ if (opt.hideGenerics) {
+ className = removeTemplate(elementUtils, className);
+ }
+ // Fast path - nothing to do:
+ if (opt.showQualified && (opt.showQualifiedGenerics || className.toString().indexOf('<') < 0)) {
+ return className.toString();
+ }
+ StringBuilder buf = new StringBuilder(className.length());
+ qualifiedNameInner(opt, className, buf, 0, !opt.showQualified);
+ return buf.toString();
}
- private static int qualifiedNameInner(Options opt, String r, StringBuilder buf, int last, boolean strip) {
- strip = strip && last < r.length() && Character.isLowerCase(r.charAt(last));
- for (int i = last; i < r.length(); i++) {
- char c = r.charAt(i);
- if (c == '.' || c == '$') {
- if (strip)
- last = i + 1; // skip dot
- strip = strip && last < r.length() && Character.isLowerCase(r.charAt(last));
- continue;
- }
- if (Character.isJavaIdentifierPart(c))
- continue;
- buf.append(r, last, i);
- last = i;
- // Handle nesting of generics
- if (c == '<') {
- buf.append('<');
- i = last = qualifiedNameInner(opt, r, buf, ++last, !opt.showQualifiedGenerics);
- buf.append('>');
- } else if (c == '>')
- return i + 1;
- }
- buf.append(r, last, r.length());
- return r.length();
+ private static int qualifiedNameInner(Options opt, Name r, StringBuilder buf, int last, boolean strip) {
+ strip = strip && last < r.length() && Character.isLowerCase(r.charAt(last));
+ for (int i = last; i < r.length(); i++) {
+ char c = r.charAt(i);
+ if (c == '.' || c == '$') {
+ if (strip)
+ last = i + 1; // skip dot
+ strip = strip && last < r.length() && Character.isLowerCase(r.charAt(last));
+ continue;
+ }
+ if (Character.isJavaIdentifierPart(c))
+ continue;
+ buf.append(r, last, i);
+ last = i;
+ // Handle nesting of generics
+ if (c == '<') {
+ buf.append('<');
+ i = last = qualifiedNameInner(opt, r, buf, ++last, !opt.showQualifiedGenerics);
+ buf.append('>');
+ } else if (c == '>')
+ return i + 1;
+ }
+ buf.append(r, last, r.length());
+ return r.length();
}
/**
- * Print the visibility adornment of element e prefixed by
- * any stereotypes
+ * Print the visibility adornment of element e prefixed by any stereotypes
*/
- private String visibility(Options opt, ProgramElementDoc e) {
- return opt.showVisibility ? Visibility.get(e).symbol : " ";
+ private String visibility(Options opt, Element e) {
+ return opt.showVisibility ? Visibility.get(e).symbol : " ";
}
/** Print the method parameter p */
- private String parameter(Options opt, Parameter p[]) {
- StringBuilder par = new StringBuilder(1000);
- for (int i = 0; i < p.length; i++) {
- par.append(p[i].name() + typeAnnotation(opt, p[i].type()));
- if (i + 1 < p.length)
- par.append(", ");
- }
- return par.toString();
+ private String parameter(Options opt, List extends VariableElement> params) {
+ StringBuilder par = new StringBuilder(1000);
+ for (int i = 0; i < params.size(); i++) {
+ par.append(params.get(i).getSimpleName() + typeAnnotation(opt, params.get(i).asType()));
+ if (i + 1 < params.size()) {
+ par.append(", ");
+ }
+ }
+ return par.toString();
}
/** Print a a basic type t */
- private String type(Options opt, Type t, boolean generics) {
- return ((generics ? opt.showQualifiedGenerics : opt.showQualified) ? //
- t.qualifiedTypeName() : t.typeName()) //
- + (opt.hideGenerics ? "" : typeParameters(opt, t.asParameterizedType()));
+ private String type(Options opt, TypeMirror t, boolean generics) {
+ return ((generics ? opt.showQualifiedGenerics : opt.showQualified) ? //
+ ElementUtil.getQualifiedName(types, t) : ElementUtil.getSimpleName(types, t)) //
+ + (opt.hideGenerics ? "" : typeParameters(opt, t instanceof DeclaredType ? (DeclaredType) t : null));
}
/** Print the parameters of the parameterized type t */
- private String typeParameters(Options opt, ParameterizedType t) {
- if (t == null)
- return "";
- StringBuffer tp = new StringBuffer(1000).append("<");
- Type args[] = t.typeArguments();
- for (int i = 0; i < args.length; i++) {
- tp.append(type(opt, args[i], true));
- if (i != args.length - 1)
- tp.append(", ");
- }
- return tp.append(">").toString();
+ private String typeParameters(Options opt, DeclaredType t) {
+ if (t == null || t.getTypeArguments() == null || t.getTypeArguments().isEmpty()) {
+ return "";
+ }
+ StringBuffer tp = new StringBuffer(1000).append("<");
+ List extends TypeMirror> args = t.getTypeArguments();
+ for (int i = 0; i < args.size(); i++) {
+ tp.append(type(opt, args.get(i), true));
+ if (i != args.size() - 1) {
+ tp.append(", ");
+ }
+ }
+ return tp.append(">").toString();
}
/** Annotate an field/argument with its type t */
- private String typeAnnotation(Options opt, Type t) {
- if (t.typeName().equals("void"))
- return "";
- return " : " + type(opt, t, false) + t.dimension();
+ private String typeAnnotation(Options opt, TypeMirror t) {
+ if (t.getKind() == TypeKind.VOID) {
+ return "";
+ }
+ return " : " + type(opt, t, false) + ElementUtil.dimensions(t);
}
/** Print the class's attributes fd */
- private void attributes(Options opt, FieldDoc fd[]) {
- for (FieldDoc f : fd) {
- if (hidden(f))
- continue;
- stereotype(opt, f, Align.LEFT);
- String att = visibility(opt, f) + f.name();
- if (opt.showType)
- att += typeAnnotation(opt, f.type());
- tableLine(Align.LEFT, att);
- tagvalue(opt, f);
- }
- }
-
- /*
- * The following two methods look similar, but can't
- * be refactored into one, because their common interface,
- * ExecutableMemberDoc, doesn't support returnType for ctors.
- */
-
- /** Print the class's constructors m */
- private boolean operations(Options opt, ConstructorDoc m[]) {
- boolean printed = false;
- for (ConstructorDoc cd : m) {
- if (hidden(cd))
- continue;
- stereotype(opt, cd, Align.LEFT);
- String cs = visibility(opt, cd) + cd.name() //
- + (opt.showType ? "(" + parameter(opt, cd.parameters()) + ")" : "()");
- tableLine(Align.LEFT, cs);
- tagvalue(opt, cd);
- printed = true;
- }
- return printed;
+ private void attributes(Options opt, List fd) {
+ for (VariableElement f : fd) {
+ if (hidden(f)) {
+ continue;
+ }
+ stereotype(opt, f, Align.LEFT);
+ String att = visibility(opt, f) + f.getSimpleName();
+ if (opt.showType) {
+ att += typeAnnotation(opt, f.asType());
+ }
+ tableLine(Align.LEFT, att);
+ tagvalue(opt, f);
+ }
}
/** Print the class's operations m */
- private boolean operations(Options opt, MethodDoc m[]) {
- boolean printed = false;
- for (MethodDoc md : m) {
- if (hidden(md))
- continue;
- // Filter-out static initializer method
- if (md.name().equals("") && md.isStatic() && md.isPackagePrivate())
- continue;
- stereotype(opt, md, Align.LEFT);
- String op = visibility(opt, md) + md.name() + //
- (opt.showType ? "(" + parameter(opt, md.parameters()) + ")" + typeAnnotation(opt, md.returnType())
- : "()");
- tableLine(Align.LEFT, (md.isAbstract() ? Font.ABSTRACT : Font.NORMAL).wrap(opt, op));
- printed = true;
+ private boolean operations(Options opt, List m) {
+ boolean printed = false;
+ for (ExecutableElement md : m) {
+ if (hidden(md)) {
+ continue;
+ }
+ // Filter-out static initializer method
+ if (md.getSimpleName().toString().equals("") && md.getModifiers().contains(Modifier.STATIC) && Visibility.get(md) == Visibility.PACKAGE) {
+ continue;
+ }
+ Name name = "".equals(md.getSimpleName().toString()) ? ElementUtil.containingTypeElement(md).getSimpleName() : md.getSimpleName();
+ stereotype(opt, md, Align.LEFT);
+ String op = visibility(opt, md) + name +
+ (opt.showType ? "(" + parameter(opt, md.getParameters()) + ")" + typeAnnotation(opt, md.getReturnType()) : "()");
+ tableLine(Align.LEFT, (md.getModifiers().contains(Modifier.ABSTRACT) ? Font.ABSTRACT : Font.NORMAL).wrap(opt, op));
+ printed = true;
- tagvalue(opt, md);
- }
- return printed;
+ tagvalue(opt, md);
+ }
+ return printed;
}
/** Print the common class node's properties */
private void nodeProperties(Options opt) {
- Options def = opt.getGlobalOptions();
- if (opt.nodeFontName != def.nodeFontName)
- w.print(",fontname=\"" + opt.nodeFontName + "\"");
- if (opt.nodeFontColor != def.nodeFontColor)
- w.print(",fontcolor=\"" + opt.nodeFontColor + "\"");
- if (opt.nodeFontSize != def.nodeFontSize)
- w.print(",fontsize=" + fmt(opt.nodeFontSize));
- w.print(opt.shape.style);
- w.println("];");
+ Options def = opt.getGlobalOptions();
+ if (opt.nodeFontName != def.nodeFontName) {
+ w.print(",fontname=\"" + opt.nodeFontName + "\"");
+ }
+ if (opt.nodeFontColor != def.nodeFontColor) {
+ w.print(",fontcolor=\"" + opt.nodeFontColor + "\"");
+ }
+ if (opt.nodeFontSize != def.nodeFontSize) {
+ w.print(",fontsize=" + fmt(opt.nodeFontSize));
+ }
+ w.print(opt.shape.style);
+ w.println("];");
}
/**
* Return as a string the tagged values associated with c
- * @param opt the Options used to guess font names
- * @param c the Doc entry to look for @tagvalue
+ *
+ * @param opt the Options used to guess font names
+ * @param c the Doc entry to look for @tagvalue
* @param prevterm the termination string for the previous element
- * @param term the termination character for each tagged value
+ * @param term the termination character for each tagged value
*/
- private void tagvalue(Options opt, Doc c) {
- Tag tags[] = c.tags("tagvalue");
- if (tags.length == 0)
- return;
-
- for (Tag tag : tags) {
- String t[] = tokenize(tag.text());
- if (t.length != 2) {
- System.err.println("@tagvalue expects two fields: " + tag.text());
- continue;
- }
- tableLine(Align.RIGHT, Font.TAG.wrap(opt, "{" + t[0] + " = " + t[1] + "}"));
- }
+ private void tagvalue(Options opt, Element c) {
+ List tags = TagUtil.getTag(docTrees, c, "tagvalue");
+ if (tags.isEmpty()) {
+ return;
+ }
+
+ for (String tag : tags) {
+ String t[] = tokenize(tag);
+ if (t.length != 2) {
+ System.err.println("@tagvalue expects two fields: " + tag);
+ continue;
+ }
+ tableLine(Align.RIGHT, Font.TAG.wrap(opt, "{" + t[0] + " = " + t[1] + "}"));
+ }
}
/**
- * Return as a string the stereotypes associated with c
- * terminated by the escape character term
+ * Return as a string the stereotypes associated with c terminated by the escape character term
*/
- private void stereotype(Options opt, Doc c, Align align) {
- for (Tag tag : c.tags("stereotype")) {
- String t[] = tokenize(tag.text());
- if (t.length != 1) {
- System.err.println("@stereotype expects one field: " + tag.text());
- continue;
- }
- tableLine(align, guilWrap(opt, t[0]));
- }
+ private void stereotype(Options opt, Element c, Align align) {
+ List tags = TagUtil.getTag(docTrees, c, "stereotype");
+ if (tags.isEmpty()) {
+ return;
+ }
+ for (String tag : tags) {
+ String t[] = tokenize(tag);
+ if (t.length != 1) {
+ System.err.println("@stereotype expects one field: " + tag);
+ continue;
+ }
+ tableLine(align, guilWrap(opt, t[0]));
+ }
}
/** Return true if c has a @hidden tag associated with it */
- private boolean hidden(ProgramElementDoc c) {
- if (c.tags("hidden").length > 0 || c.tags("view").length > 0)
- return true;
- Options opt = optionProvider.getOptionsFor(c instanceof ClassDoc ? (ClassDoc) c : c.containingClass());
- return opt.matchesHideExpression(c.toString()) //
- || (opt.hidePrivateInner && c instanceof ClassDoc && c.isPrivate() && ((ClassDoc) c).containingClass() != null);
+ private boolean hidden(Element c) {
+ Map> tags = TagUtil.getTags(docTrees, c);
+ if (tags.get("hidden") != null || tags.get("view") != null) {
+ return true;
+ }
+ Options opt = optionProvider.getOptionsFor(docTrees, c instanceof TypeElement ? (TypeElement) c : ElementUtil.containingTypeElement(c));
+ return opt.matchesHideExpression(c.getSimpleName()) //
+ || (opt.hidePrivateInner && c instanceof TypeElement && c.getModifiers().contains(Modifier.PRIVATE)
+ && ((TypeElement) c).getEnclosingElement() != null);
}
- protected ClassInfo getClassInfo(ClassDoc cd, boolean create) {
- return getClassInfo(cd, cd.toString(), create);
+ protected ClassInfo getClassInfo(TypeElement cd, boolean create) {
+ return getClassInfo(cd, cd.getQualifiedName(), create);
}
- protected ClassInfo getClassInfo(String className, boolean create) {
- return getClassInfo(null, className, create);
+ protected ClassInfo getClassInfo(Name className, boolean create) {
+ return getClassInfo(null, className, create);
}
- protected ClassInfo getClassInfo(ClassDoc cd, String className, boolean create) {
- className = removeTemplate(className);
- ClassInfo ci = classnames.get(className);
- if (ci == null && create) {
- boolean hidden = cd != null ? hidden(cd) : optionProvider.getOptionsFor(className).matchesHideExpression(className);
- ci = new ClassInfo(hidden);
- classnames.put(className, ci);
- }
- return ci;
+ protected ClassInfo getClassInfo(TypeElement cd, Name className, boolean create) {
+ className = removeTemplate(elementUtils, className);
+ ClassInfo ci = classnames.get(className);
+ if (ci == null && create) {
+ boolean hidden = cd != null ? hidden(cd)
+ : optionProvider.getOptionsFor(className).matchesHideExpression(className);
+ ci = new ClassInfo(hidden);
+ classnames.put(className, ci);
+ }
+ return ci;
}
- /** Return true if the class name is associated to an hidden class or matches a hide expression */
- private boolean hidden(String className) {
- className = removeTemplate(className);
- ClassInfo ci = classnames.get(className);
- return ci != null ? ci.hidden : optionProvider.getOptionsFor(className).matchesHideExpression(className);
+ /**
+ * Return true if the class name is associated to an hidden class or matches a
+ * hide expression
+ */
+ private boolean hidden(CharSequence className) {
+ ClassInfo ci = classnames.get(className);
+ return ci != null ? ci.hidden : optionProvider.getOptionsFor(className).matchesHideExpression(className);
}
/**
* Prints the class if needed.
*
* A class is a rootClass if it's included among the classes returned by
- * RootDoc.classes(), this information is used to properly compute
- * relative links in diagrams for UMLDoc
+ * RootDoc.classes(), this information is used to properly compute relative
+ * links in diagrams for UMLDoc
*/
- public String printClass(ClassDoc c, boolean rootClass) {
- ClassInfo ci = getClassInfo(c, true);
- if(ci.nodePrinted || ci.hidden)
- return ci.name;
- Options opt = optionProvider.getOptionsFor(c);
- if (c.isEnum() && !opt.showEnumerations)
- return ci.name;
- String className = c.toString();
- // Associate classname's alias
- w.println(linePrefix + "// " + className);
- // Create label
- w.print(linePrefix + ci.name + " [label=");
+ public String printClass(TypeElement c, boolean rootClass) {
+ ClassInfo ci = getClassInfo(c, true);
+ if (ci.nodePrinted || ci.hidden)
+ return ci.name;
+ Options opt = optionProvider.getOptionsFor(docTrees, c);
+ if (c.getKind() == ElementKind.ENUM && !opt.showEnumerations) {
+ return ci.name;
+ }
+ String className = c.toString();
+ // Associate classname's alias
+ w.println(linePrefix + "// " + className);
+ // Create label
+ w.print(linePrefix + ci.name + " [label=");
- boolean showMembers =
- (opt.showAttributes && c.fields().length > 0) ||
- (c.isEnum() && opt.showEnumConstants && c.enumConstants().length > 0) ||
- (opt.showOperations && c.methods().length > 0) ||
- (opt.showConstructors && c.constructors().length > 0);
+ boolean showMembers = (opt.showAttributes && !ElementUtil.getFields(c).isEmpty())
+ || (c.getKind() == ElementKind.ENUM && opt.showEnumConstants && !ElementUtil.getEnumConstants(c).isEmpty())
+ || (opt.showOperations && !ElementUtil.getMethods(c).isEmpty())
+ || (opt.showConstructors && !ElementUtil.getConstructors(c).isEmpty());
- final String url = classToUrl(c, rootClass);
- externalTableStart(opt, c.qualifiedName(), url);
+ final String url = classToUrl(c, rootClass);
+ externalTableStart(opt, c.getQualifiedName(), url);
- firstInnerTableStart(opt);
- if (c.isInterface())
- tableLine(Align.CENTER, guilWrap(opt, "interface"));
- if (c.isEnum())
- tableLine(Align.CENTER, guilWrap(opt, "enumeration"));
- stereotype(opt, c, Align.CENTER);
- Font font = c.isAbstract() && !c.isInterface() ? Font.CLASS_ABSTRACT : Font.CLASS;
- String qualifiedName = qualifiedName(opt, className);
- int idx = splitPackageClass(qualifiedName);
- if (opt.showComment)
- tableLine(Align.LEFT, Font.CLASS.wrap(opt, htmlNewline(escape(c.commentText()))));
- else if (opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
- String packageName = qualifiedName.substring(0, idx);
- String cn = qualifiedName.substring(idx + 1);
- tableLine(Align.CENTER, font.wrap(opt, escape(cn)));
- tableLine(Align.CENTER, Font.PACKAGE.wrap(opt, packageName));
- } else {
- tableLine(Align.CENTER, font.wrap(opt, escape(qualifiedName)));
- }
- tagvalue(opt, c);
- firstInnerTableEnd(opt);
+ firstInnerTableStart(opt);
+ if (c.getKind() == ElementKind.INTERFACE) {
+ tableLine(Align.CENTER, guilWrap(opt, "interface"));
+ }
+ if (c.getKind() == ElementKind.ENUM) {
+ tableLine(Align.CENTER, guilWrap(opt, "enumeration"));
+ }
+ stereotype(opt, c, Align.CENTER);
+ Font font = c.getModifiers().contains(Modifier.ABSTRACT) && c.getKind() != ElementKind.INTERFACE ? Font.CLASS_ABSTRACT : Font.CLASS;
+ String qualifiedName = qualifiedName(opt, c.getQualifiedName());
+ int idx = splitPackageClass(qualifiedName);
+ if (opt.showComment) {
+ tableLine(Align.LEFT, Font.CLASS.wrap(opt, htmlNewline(escape(TagUtil.getComment(docTrees, c)))));
+ } else if (opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
+ String packageName = qualifiedName.substring(0, idx);
+ String cn = qualifiedName.substring(idx + 1);
+ tableLine(Align.CENTER, font.wrap(opt, escape(cn)));
+ tableLine(Align.CENTER, Font.PACKAGE.wrap(opt, packageName));
+ } else {
+ tableLine(Align.CENTER, font.wrap(opt, escape(qualifiedName)));
+ }
+ tagvalue(opt, c);
+ firstInnerTableEnd(opt);
- /*
- * Warning: The boolean expressions guarding innerTableStart()
- * in this block, should match those in the code block above
- * marked: "Calculate the number of innerTable rows we will emmit"
- */
- if (showMembers) {
- if (opt.showAttributes) {
- innerTableStart();
- FieldDoc[] fields = c.fields();
- // if there are no fields, print an empty line to generate proper HTML
- if (fields.length == 0)
- tableLine(Align.LEFT, "");
- else
- attributes(opt, c.fields());
- innerTableEnd();
- } else if(!c.isEnum() && (opt.showConstructors || opt.showOperations)) {
- // show an emtpy box if we don't show attributes but
- // we show operations
- innerTableStart();
- tableLine(Align.LEFT, "");
- innerTableEnd();
- }
- if (c.isEnum() && opt.showEnumConstants) {
- innerTableStart();
- FieldDoc[] ecs = c.enumConstants();
- // if there are no constants, print an empty line to generate proper HTML
- if (ecs.length == 0) {
- tableLine(Align.LEFT, "");
- } else {
- for (FieldDoc fd : c.enumConstants()) {
- tableLine(Align.LEFT, fd.name());
- }
- }
- innerTableEnd();
- }
- if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) {
- innerTableStart();
- boolean printedLines = false;
- if (opt.showConstructors)
- printedLines |= operations(opt, c.constructors());
- if (opt.showOperations)
- printedLines |= operations(opt, c.methods());
+ /*
+ * Warning: The boolean expressions guarding innerTableStart() in this block,
+ * should match those in the code block above marked:
+ * "Calculate the number of innerTable rows we will emmit"
+ */
+ if (showMembers) {
+ if (opt.showAttributes) {
+ innerTableStart();
+ List fields = ElementUtil.getFields(c);
+ // if there are no fields, print an empty line to generate proper HTML
+ if (fields.size() == 0) {
+ tableLine(Align.LEFT, "");
+ } else {
+ attributes(opt, fields);
+ }
+ innerTableEnd();
+ } else if (c.getKind() != ElementKind.ENUM && (opt.showConstructors || opt.showOperations)) {
+ // show an emtpy box if we don't show attributes but
+ // we show operations
+ innerTableStart();
+ tableLine(Align.LEFT, "");
+ innerTableEnd();
+ }
+ if (c.getKind() == ElementKind.ENUM && opt.showEnumConstants) {
+ innerTableStart();
+ List ecs = ElementUtil.getEnumConstants(c);
+ // if there are no constants, print an empty line to generate proper HTML
+ if (ecs.size() == 0) {
+ tableLine(Align.LEFT, "");
+ } else {
+ for (VariableElement fd : ecs) {
+ tableLine(Align.LEFT, fd.getSimpleName());
+ }
+ }
+ innerTableEnd();
+ }
+ if (c.getKind() != ElementKind.ENUM && (opt.showConstructors || opt.showOperations)) {
+ innerTableStart();
+ boolean printedLines = false;
+ if (opt.showConstructors) {
+ printedLines |= operations(opt, ElementUtil.getConstructors(c));
+ }
+ if (opt.showOperations) {
+ printedLines |= operations(opt, ElementUtil.getMethods(c));
+ }
- if (!printedLines)
- // if there are no operations nor constructors,
- // print an empty line to generate proper HTML
- tableLine(Align.LEFT, "");
+ if (!printedLines) {
+ // if there are no operations nor constructors,
+ // print an empty line to generate proper HTML
+ tableLine(Align.LEFT, "");
+ }
- innerTableEnd();
- }
- }
- externalTableEnd();
- if (url != null)
- w.print(", URL=\"" + url + "\"");
- nodeProperties(opt);
+ innerTableEnd();
+ }
+ }
+ externalTableEnd();
+ if (url != null)
+ w.print(", URL=\"" + url + "\"");
+ nodeProperties(opt);
- // If needed, add a note for this node
- int ni = 0;
- for (Tag t : c.tags("note")) {
- String noteName = "n" + ni + "c" + ci.name;
- w.print(linePrefix + "// Note annotation\n");
- w.print(linePrefix + noteName + " [label=");
- externalTableStart(UmlGraph.getCommentOptions(), c.qualifiedName(), url);
- innerTableStart();
- tableLine(Align.LEFT, Font.CLASS.wrap(UmlGraph.getCommentOptions(), htmlNewline(escape(t.text()))));
- innerTableEnd();
- externalTableEnd();
- nodeProperties(UmlGraph.getCommentOptions());
- ClassInfo ci1 = getClassInfo(c, true);
- w.print(linePrefix + noteName + " -> " + ci1.name + "[arrowhead=none];\n");
- ni++;
- }
- ci.nodePrinted = true;
- return ci.name;
+ // If needed, add a note for this node
+ int ni = 0;
+ List tags = TagUtil.getTag(docTrees, c, "note");
+ for (String t : tags) {
+ String noteName = "n" + ni + "c" + ci.name;
+ w.print(linePrefix + "// Note annotation\n");
+ w.print(linePrefix + noteName + " [label=");
+ externalTableStart(UmlGraph.getCommentOptions(), c.getQualifiedName(), url);
+ innerTableStart();
+ tableLine(Align.LEFT, Font.CLASS.wrap(UmlGraph.getCommentOptions(), htmlNewline(escape(t))));
+ innerTableEnd();
+ externalTableEnd();
+ nodeProperties(UmlGraph.getCommentOptions());
+ ClassInfo ci1 = getClassInfo(c, true);
+ w.print(linePrefix + noteName + " -> " + ci1.name + "[arrowhead=none];\n");
+ ni++;
+ }
+ ci.nodePrinted = true;
+ return ci.name;
}
/**
* Print all relations for a given's class's tag
- * @param tagname the tag containing the given relation
- * @param from the source class
+ *
+ * @param tagname the tag containing the given relation
+ * @param from the source class
* @param edgetype the dot edge specification
*/
- private void allRelation(Options opt, RelationType rt, ClassDoc from) {
- String tagname = rt.lower;
- for (Tag tag : from.tags(tagname)) {
- String t[] = tokenize(tag.text()); // l-src label l-dst target
- t = t.length == 1 ? new String[] { "-", "-", "-", t[0] } : t; // Shorthand
- if (t.length != 4) {
- System.err.println("Error in " + from + "\n" + tagname + " expects four fields (l-src label l-dst target): " + tag.text());
- return;
- }
- ClassDoc to = from.findClass(t[3]);
- if (to != null) {
- if(hidden(to))
- continue;
- relation(opt, rt, from, to, t[0], t[1], t[2]);
- } else {
- if(hidden(t[3]))
- continue;
- relation(opt, rt, from, from.toString(), to, t[3], t[0], t[1], t[2]);
- }
- }
+ private void allRelation(Options opt, RelationType rt, TypeElement from) {
+ String tagname = rt.lower;
+ List tags = TagUtil.getTag(docTrees, from, tagname);
+ for (String tag : tags) {
+ String t[] = tokenize(tag); // l-src label l-dst target
+ t = t.length == 1 ? new String[] { "-", "-", "-", t[0] } : t; // Shorthand
+ if (t.length != 4) {
+ System.err.println("Error in " + from + "\n" + tagname
+ + " expects four fields (l-src label l-dst target): " + tag);
+ return;
+ }
+ TypeElement to = elementUtils.getTypeElement(t[3]);
+ if (to != null) {
+ if (hidden(to)) {
+ continue;
+ }
+ relation(opt, rt, from, to, t[0], t[1], t[2]);
+ } else {
+ Name t3 = elementUtils.getName(t[3]);
+ if (hidden(t3)) {
+ continue;
+ }
+ relation(opt, rt, from, from.getQualifiedName(), to, t3, t[0], t[1], t[2]);
+ }
+ }
}
/**
* Print the specified relation
- * @param from the source class (may be null)
+ *
+ * @param from the source class (may be null)
* @param fromName the source class's name
- * @param to the destination class (may be null)
- * @param toName the destination class's name
+ * @param to the destination class (may be null)
+ * @param toName the destination class's name
*/
- private void relation(Options opt, RelationType rt, ClassDoc from, String fromName,
- ClassDoc to, String toName, String tailLabel, String label, String headLabel) {
- tailLabel = (tailLabel != null && !tailLabel.isEmpty()) ? ",taillabel=\"" + tailLabel + "\"" : "";
- label = (label != null && !label.isEmpty()) ? ",label=\"" + guillemize(opt, label) + "\"" : "";
- headLabel = (headLabel != null && !headLabel.isEmpty()) ? ",headlabel=\"" + headLabel + "\"" : "";
- boolean unLabeled = tailLabel.isEmpty() && label.isEmpty() && headLabel.isEmpty();
+ private void relation(Options opt, RelationType rt, TypeElement from, Name fromName, TypeElement to, Name toName,
+ String tailLabel, String label, String headLabel) {
+ tailLabel = (tailLabel != null && !tailLabel.isEmpty()) ? ",taillabel=\"" + tailLabel + "\"" : "";
+ label = (label != null && !label.isEmpty()) ? ",label=\"" + guillemize(opt, label) + "\"" : "";
+ headLabel = (headLabel != null && !headLabel.isEmpty()) ? ",headlabel=\"" + headLabel + "\"" : "";
+ boolean unLabeled = tailLabel.isEmpty() && label.isEmpty() && headLabel.isEmpty();
- ClassInfo ci1 = getClassInfo(from, fromName, true), ci2 = getClassInfo(to, toName, true);
- String n1 = ci1.name, n2 = ci2.name;
- // For ranking we need to output extends/implements backwards.
- if (rt.backorder) { // Swap:
- n1 = ci2.name;
- n2 = ci1.name;
- String tmp = tailLabel;
- tailLabel = headLabel;
- headLabel = tmp;
- }
- Options def = opt.getGlobalOptions();
- // print relation
- w.println(linePrefix + "// " + fromName + " " + rt.lower + " " + toName);
- w.println(linePrefix + n1 + " -> " + n2 + " [" + rt.style +
- (opt.edgeColor != def.edgeColor ? ",color=\"" + opt.edgeColor + "\"" : "") +
- (unLabeled ? "" :
- (opt.edgeFontName != def.edgeFontName ? ",fontname=\"" + opt.edgeFontName + "\"" : "") +
- (opt.edgeFontColor != def.edgeFontColor ? ",fontcolor=\"" + opt.edgeFontColor + "\"" : "") +
- (opt.edgeFontSize != def.edgeFontSize ? ",fontsize=" + fmt(opt.edgeFontSize) : "")) +
- tailLabel + label + headLabel +
- "];");
-
- // update relation info
- RelationDirection d = RelationDirection.BOTH;
- if(rt == RelationType.NAVASSOC || rt == RelationType.DEPEND)
- d = RelationDirection.OUT;
- ci1.addRelation(toName, rt, d);
- ci2.addRelation(fromName, rt, d.inverse());
+ ClassInfo ci1 = getClassInfo(from, fromName, true), ci2 = getClassInfo(to, toName, true);
+ String n1 = ci1.name, n2 = ci2.name;
+ // For ranking we need to output extends/implements backwards.
+ if (rt.backorder) { // Swap:
+ n1 = ci2.name;
+ n2 = ci1.name;
+ String tmp = tailLabel;
+ tailLabel = headLabel;
+ headLabel = tmp;
+ }
+ Options def = opt.getGlobalOptions();
+ // print relation
+ w.println(linePrefix + "// " + fromName + " " + rt.lower + " " + toName);
+ w.println(linePrefix + n1 + " -> " + n2 + " [" + rt.style
+ + (opt.edgeColor != def.edgeColor ? ",color=\"" + opt.edgeColor + "\"" : "")
+ + (unLabeled ? ""
+ : (opt.edgeFontName != def.edgeFontName ? ",fontname=\"" + opt.edgeFontName + "\"" : "")
+ + (opt.edgeFontColor != def.edgeFontColor ? ",fontcolor=\"" + opt.edgeFontColor + "\""
+ : "")
+ + (opt.edgeFontSize != def.edgeFontSize ? ",fontsize=" + fmt(opt.edgeFontSize) : ""))
+ + tailLabel + label + headLabel + "];");
+
+ // update relation info
+ RelationDirection d = RelationDirection.BOTH;
+ if (rt == RelationType.NAVASSOC || rt == RelationType.DEPEND)
+ d = RelationDirection.OUT;
+ ci1.addRelation(toName, rt, d);
+ ci2.addRelation(fromName, rt, d.inverse());
}
/**
* Print the specified relation
+ *
* @param from the source class
- * @param to the destination class
+ * @param to the destination class
*/
- private void relation(Options opt, RelationType rt, ClassDoc from,
- ClassDoc to, String tailLabel, String label, String headLabel) {
- relation(opt, rt, from, from.toString(), to, to.toString(), tailLabel, label, headLabel);
+ private void relation(Options opt, RelationType rt, TypeElement from, TypeElement to, String tailLabel, String label,
+ String headLabel) {
+ relation(opt, rt, from, from.getQualifiedName(), to, to.getQualifiedName(), tailLabel, label, headLabel);
}
-
/** Print a class's relations */
- public void printRelations(ClassDoc c) {
- Options opt = optionProvider.getOptionsFor(c);
- if (hidden(c) || c.name().equals("")) // avoid phantom classes, they may pop up when the source uses annotations
- return;
- // Print generalization (through the Java superclass)
- Type s = c.superclassType();
- ClassDoc sc = s != null && !s.qualifiedTypeName().equals(Object.class.getName()) ? s.asClassDoc() : null;
- if (sc != null && !c.isEnum() && !hidden(sc))
- relation(opt, RelationType.EXTENDS, c, sc, null, null, null);
- // Print generalizations (through @extends tags)
- for (Tag tag : c.tags("extends"))
- if (!hidden(tag.text()))
- relation(opt, RelationType.EXTENDS, c, c.findClass(tag.text()), null, null, null);
- // Print realizations (Java interfaces)
- for (Type iface : c.interfaceTypes()) {
- ClassDoc ic = iface.asClassDoc();
- if (!hidden(ic))
- relation(opt, RelationType.IMPLEMENTS, c, ic, null, null, null);
- }
- // Print other associations
- allRelation(opt, RelationType.COMPOSED, c);
- allRelation(opt, RelationType.NAVCOMPOSED, c);
- allRelation(opt, RelationType.HAS, c);
- allRelation(opt, RelationType.NAVHAS, c);
- allRelation(opt, RelationType.ASSOC, c);
- allRelation(opt, RelationType.NAVASSOC, c);
- allRelation(opt, RelationType.DEPEND, c);
+ public void printRelations(TypeElement c) {
+ Options opt = optionProvider.getOptionsFor(docTrees, c);
+ if (hidden(c) || "".equals(c.getSimpleName().toString())) { // avoid phantom classes, they may pop up when the source uses annotations
+ return;
+ }
+ // Print generalization (through the Java superclass)
+ TypeMirror clazz = c.getSuperclass();
+ Element scd;
+ if (clazz == null || clazz.getKind() == TypeKind.NONE || !(clazz instanceof DeclaredType)) {
+ scd = null;
+ } else {
+ scd = ((DeclaredType) clazz).asElement();
+ }
+ TypeElement s = scd instanceof TypeElement ? (TypeElement) scd : null;
+ TypeElement sc = s != null && !s.getQualifiedName().toString().equals(Object.class.getName()) ? s : null;
+ if (sc != null && c.getKind() != ElementKind.ENUM && !hidden(sc)) {
+ relation(opt, RelationType.EXTENDS, c, sc, null, null, null);
+ }
+ // Print generalizations (through @extends tags)
+ List tags = TagUtil.getTag(docTrees, c, "extends");
+ for (String tag : tags) {
+ if (!hidden(tag)) {
+ relation(opt, RelationType.EXTENDS, c, elementUtils.getTypeElement(tag), null, null, null);
+ }
+ }
+ // Print realizations (Java interfaces)
+ for (TypeMirror iface : c.getInterfaces()) {
+ TypeElement ic = ElementUtil.getTypeElement(iface);
+ if (!hidden(ic)) {
+ relation(opt, RelationType.IMPLEMENTS, c, ic, null, null, null);
+ }
+ }
+ // Print other associations
+ allRelation(opt, RelationType.COMPOSED, c);
+ allRelation(opt, RelationType.NAVCOMPOSED, c);
+ allRelation(opt, RelationType.HAS, c);
+ allRelation(opt, RelationType.NAVHAS, c);
+ allRelation(opt, RelationType.ASSOC, c);
+ allRelation(opt, RelationType.NAVASSOC, c);
+ allRelation(opt, RelationType.DEPEND, c);
}
/** Print classes that were parts of relationships, but not parsed by javadoc */
- public void printExtraClasses(RootDoc root) {
- Set names = new HashSet(classnames.keySet());
- for(String className: names) {
- ClassInfo info = getClassInfo(className, true);
- if (info.nodePrinted)
- continue;
- ClassDoc c = root.classNamed(className);
- if(c != null) {
- printClass(c, false);
- continue;
- }
- // Handle missing classes:
- Options opt = optionProvider.getOptionsFor(className);
- if(opt.matchesHideExpression(className))
- continue;
- w.println(linePrefix + "// " + className);
- w.print(linePrefix + info.name + "[label=");
- externalTableStart(opt, className, classToUrl(className));
- innerTableStart();
- String qualifiedName = qualifiedName(opt, className);
- int startTemplate = qualifiedName.indexOf('<');
- int idx = qualifiedName.lastIndexOf('.', startTemplate < 0 ? qualifiedName.length() - 1 : startTemplate);
- if(opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
- String packageName = qualifiedName.substring(0, idx);
- String cn = qualifiedName.substring(idx + 1);
- tableLine(Align.CENTER, Font.CLASS.wrap(opt, escape(cn)));
- tableLine(Align.CENTER, Font.PACKAGE.wrap(opt, packageName));
- } else {
- tableLine(Align.CENTER, Font.CLASS.wrap(opt, escape(qualifiedName)));
- }
- innerTableEnd();
- externalTableEnd();
- if (className == null || className.length() == 0)
- w.print(",URL=\"" + classToUrl(className) + "\"");
- nodeProperties(opt);
- }
- }
-
- /**
- * Prints associations recovered from the fields of a class. An association is inferred only
- * if another relation between the two classes is not already in the graph.
- * @param classes
- */
- public void printInferredRelations(ClassDoc c) {
- // check if the source is excluded from inference
- if (hidden(c))
- return;
-
- Options opt = optionProvider.getOptionsFor(c);
-
- for (FieldDoc field : c.fields(false)) {
- if(hidden(field))
- continue;
- // skip statics
- if(field.isStatic())
- continue;
- // skip primitives
- FieldRelationInfo fri = getFieldRelationInfo(field);
- if (fri == null)
- continue;
- // check if the destination is excluded from inference
- if (hidden(fri.cd))
- continue;
-
- // if source and dest are not already linked, add a dependency
- RelationPattern rp = getClassInfo(c, true).getRelation(fri.cd.toString());
- if (rp == null) {
- String destAdornment = fri.multiple ? "*" : "";
- relation(opt, opt.inferRelationshipType, c, fri.cd, "", "", destAdornment);
+ public void printExtraClasses(DocletEnvironment root) {
+ Set names = new HashSet<>(classnames.keySet());
+ for (Name className : names) {
+ ClassInfo info = getClassInfo(className, true);
+ if (info.nodePrinted) {
+ continue;
}
- }
- }
-
- /** Returns an array representing the imported classes of c.
- * Disables the deprecation warning, which is output, because the
- * imported classed are an implementation detail.
- */
- @SuppressWarnings( "deprecation" )
- ClassDoc[] importedClasses(ClassDoc c) {
- return c.importedClasses();
+ TypeElement c = elementUtils.getTypeElement(className);
+ if (c != null) {
+ printClass(c, false);
+ continue;
+ }
+ // Handle missing classes:
+ Options opt = optionProvider.getOptionsFor(className);
+ if (opt.matchesHideExpression(className))
+ continue;
+ w.println(linePrefix + "// " + className);
+ w.print(linePrefix + info.name + "[label=");
+ externalTableStart(opt, className, classToUrl(className));
+ innerTableStart();
+ String qualifiedName = qualifiedName(opt, className);
+ int startTemplate = qualifiedName.indexOf('<');
+ int idx = qualifiedName.lastIndexOf('.', startTemplate < 0 ? qualifiedName.length() - 1 : startTemplate);
+ if (opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
+ String packageName = qualifiedName.substring(0, idx);
+ String cn = qualifiedName.substring(idx + 1);
+ tableLine(Align.CENTER, Font.CLASS.wrap(opt, escape(cn)));
+ tableLine(Align.CENTER, Font.PACKAGE.wrap(opt, packageName));
+ } else {
+ tableLine(Align.CENTER, Font.CLASS.wrap(opt, escape(qualifiedName)));
+ }
+ innerTableEnd();
+ externalTableEnd();
+ if (className == null || className.length() == 0)
+ w.print(",URL=\"" + classToUrl(className) + "\"");
+ nodeProperties(opt);
+ }
}
/**
- * Prints dependencies recovered from the methods of a class. A
- * dependency is inferred only if another relation between the two
- * classes is not already in the graph.
+ * Prints associations recovered from the fields of a class. An association is
+ * inferred only if another relation between the two classes is not already in
+ * the graph.
+ *
* @param classes
- */
- public void printInferredDependencies(ClassDoc c) {
- if (hidden(c))
- return;
-
- Options opt = optionProvider.getOptionsFor(c);
- Set types = new HashSet();
- // harvest method return and parameter types
- for (MethodDoc method : filterByVisibility(c.methods(false), opt.inferDependencyVisibility)) {
- types.add(method.returnType());
- for (Parameter parameter : method.parameters()) {
- types.add(parameter.type());
- }
- }
- // and the field types
- if (!opt.inferRelationships) {
- for (FieldDoc field : filterByVisibility(c.fields(false), opt.inferDependencyVisibility)) {
- types.add(field.type());
- }
- }
- // see if there are some type parameters
- if (c.asParameterizedType() != null) {
- ParameterizedType pt = c.asParameterizedType();
- types.addAll(Arrays.asList(pt.typeArguments()));
- }
- // see if type parameters extend something
- for(TypeVariable tv: c.typeParameters()) {
- if(tv.bounds().length > 0 )
- types.addAll(Arrays.asList(tv.bounds()));
- }
-
- // and finally check for explicitly imported classes (this
- // assumes there are no unused imports...)
- if (opt.useImports)
- types.addAll(Arrays.asList(importedClasses(c)));
-
- // compute dependencies
- for (Type type : types) {
- // skip primitives and type variables, as well as dependencies
- // on the source class
- if (type.isPrimitive() || type instanceof WildcardType || type instanceof TypeVariable
- || c.toString().equals(type.asClassDoc().toString()))
- continue;
-
- // check if the destination is excluded from inference
- ClassDoc fc = type.asClassDoc();
- if (hidden(fc))
- continue;
-
- // check if source and destination are in the same package and if we are allowed
- // to infer dependencies between classes in the same package
- if(!opt.inferDepInPackage && c.containingPackage().equals(fc.containingPackage()))
- continue;
-
- // if source and dest are not already linked, add a dependency
- RelationPattern rp = getClassInfo(c, true).getRelation(fc.toString());
- if (rp == null || rp.matchesOne(new RelationPattern(RelationDirection.OUT))) {
- relation(opt, RelationType.DEPEND, c, fc, "", "", "");
- }
-
- }
- }
-
- /**
- * Returns all program element docs that have a visibility greater or
- * equal than the specified level
*/
- private List filterByVisibility(T[] docs, Visibility visibility) {
- if (visibility == Visibility.PRIVATE)
- return Arrays.asList(docs);
+ public void printInferredRelations(TypeElement c) {
+ // check if the source is excluded from inference
+ if (hidden(c)) {
+ return;
+ }
- List filtered = new ArrayList();
- for (T doc : docs) {
- if (Visibility.get(doc).compareTo(visibility) > 0)
- filtered.add(doc);
- }
- return filtered;
+ Options opt = optionProvider.getOptionsFor(docTrees, c);
+
+ for (VariableElement field : ElementUtil.getFields(c)) {
+ if (hidden(field)) {
+ continue;
+ }
+ // skip statics
+ if (field.getModifiers().contains(Modifier.STATIC)) {
+ continue;
+ }
+ // skip primitives
+ FieldRelationInfo fri = getFieldRelationInfo(field);
+ if (fri == null) {
+ continue;
+ }
+ // check if the destination is excluded from inference
+ if (hidden(fri.cd)) {
+ continue;
+ }
+
+ // if source and dest are not already linked, add a dependency
+ RelationPattern rp = getClassInfo(c, true).getRelation(fri.cd.toString());
+ if (rp == null) {
+ String destAdornment = fri.multiple ? "*" : "";
+ relation(opt, opt.inferRelationshipType, c, fri.cd, "", "", destAdornment);
+ }
+ }
}
-
-
- private FieldRelationInfo getFieldRelationInfo(FieldDoc field) {
- Type type = field.type();
- if(type.isPrimitive() || type instanceof WildcardType || type instanceof TypeVariable)
- return null;
-
- if (type.dimension().endsWith("[]")) {
- return new FieldRelationInfo(type.asClassDoc(), true);
- }
-
- Options opt = optionProvider.getOptionsFor(type.asClassDoc());
- if (opt.matchesCollPackageExpression(type.qualifiedTypeName())) {
- Type[] argTypes = getInterfaceTypeArguments(collectionClassDoc, type);
- if (argTypes != null && argTypes.length == 1 && !argTypes[0].isPrimitive())
- return new FieldRelationInfo(argTypes[0].asClassDoc(), true);
-
- argTypes = getInterfaceTypeArguments(mapClassDoc, type);
- if (argTypes != null && argTypes.length == 2 && !argTypes[1].isPrimitive())
- return new FieldRelationInfo(argTypes[1].asClassDoc(), true);
- }
-
- return new FieldRelationInfo(type.asClassDoc(), false);
+ /**
+ * Returns an array representing the imported classes of c. Disables the
+ * deprecation warning, which is output, because the imported classed are an
+ * implementation detail.
+ */
+ List importedClasses(TypeElement c) {
+ JavaFileObject source;
+ try {
+ source = fileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, c.getQualifiedName().toString(), JavaFileObject.Kind.SOURCE);
+ } catch (IOException e) {
+ source = null;
+ }
+
+ if (source == null) {
+ return Collections.emptyList();
+ }
+ return Collections.emptyList(); // c.importedClasses();
}
-
- private Type[] getInterfaceTypeArguments(ClassDoc iface, Type t) {
- if (t instanceof ParameterizedType) {
- ParameterizedType pt = (ParameterizedType) t;
- if (iface != null && iface.equals(t.asClassDoc())) {
- return pt.typeArguments();
- } else {
- for (Type pti : pt.interfaceTypes()) {
- Type[] result = getInterfaceTypeArguments(iface, pti);
- if (result != null)
- return result;
- }
- if (pt.superclassType() != null)
- return getInterfaceTypeArguments(iface, pt.superclassType());
- }
- } else if (t instanceof ClassDoc) {
- ClassDoc cd = (ClassDoc) t;
- for (Type pti : cd.interfaceTypes()) {
- Type[] result = getInterfaceTypeArguments(iface, pti);
- if (result != null)
- return result;
- }
- if (cd.superclassType() != null)
- return getInterfaceTypeArguments(iface, cd.superclassType());
- }
- return null;
+
+ /**
+ * Prints dependencies recovered from the methods of a class. A dependency is
+ * inferred only if another relation between the two classes is not already in
+ * the graph.
+ *
+ * @param classes
+ */
+ public void printInferredDependencies(TypeElement c) {
+ if (hidden(c)) {
+ return;
+ }
+
+ Options opt = optionProvider.getOptionsFor(docTrees, c);
+ Set types = new HashSet<>();
+
+ // harvest method return and parameter types
+ for (ExecutableElement method : filterByVisibility(ElementUtil.getMethods(c), opt.inferDependencyVisibility)) {
+ types.add(method.getReturnType());
+ for (VariableElement parameter : method.getParameters()) {
+ types.add(parameter.asType());
+ }
+ }
+ // and the field types
+ if (!opt.inferRelationships) {
+ for (VariableElement field : filterByVisibility(ElementUtil.getFields(c), opt.inferDependencyVisibility)) {
+ types.add(field.asType());
+ }
+ }
+ // see if there are some type parameters
+ if (c.asType() instanceof DeclaredType) {
+ DeclaredType pt = (DeclaredType) c.asType();
+ types.addAll(pt.getTypeArguments());
+ }
+ // see if type parameters extend something
+ for (TypeParameterElement tv : c.getTypeParameters()) {
+ if (tv.getBounds().size() > 0) {
+ types.addAll(tv.getBounds());
+ }
+ }
+
+ // and finally check for explicitly imported classes (this
+ // assumes there are no unused imports...)
+ if (opt.useImports) {
+ types.addAll(importedClasses(c));
+ }
+
+ // compute dependencies
+ for (TypeMirror type : types) {
+ // skip primitives and type variables, as well as dependencies
+ // on the source class
+ if (type.getKind().isPrimitive() || type instanceof NoType || type instanceof WildcardType || type instanceof TypeVariable
+ || c.toString().equals(ElementUtil.getTypeElement(type).toString())) {
+ continue;
+ }
+
+ // check if the destination is excluded from inference
+ TypeElement fc = ElementUtil.getTypeElement(type);
+ if (hidden(fc)) {
+ continue;
+ }
+
+ // check if source and destination are in the same package and if we are allowed
+ // to infer dependencies between classes in the same package
+ if (!opt.inferDepInPackage && ElementUtil.getPackageOf(elementUtils, c).equals(ElementUtil.getPackageOf(elementUtils, fc))) {
+ continue;
+ }
+
+ // if source and dest are not already linked, add a dependency
+ RelationPattern rp = getClassInfo(c, true).getRelation(fc.toString());
+ if (rp == null || rp.matchesOne(new RelationPattern(RelationDirection.OUT))) {
+ relation(opt, RelationType.DEPEND, c, fc, "", "", "");
+ }
+
+ }
+ }
+
+ /**
+ * Returns all program element docs that have a visibility greater or equal than
+ * the specified level
+ */
+ private List filterByVisibility(List docs, Visibility visibility) {
+ if (visibility == Visibility.PRIVATE) {
+ return docs;
+ }
+
+ List filtered = new ArrayList<>();
+ for (T doc : docs) {
+ if (Visibility.get(doc).compareTo(visibility) > 0) {
+ filtered.add(doc);
+ }
+ }
+ return filtered;
+ }
+
+ private FieldRelationInfo getFieldRelationInfo(VariableElement field) {
+ TypeMirror type = field.asType();
+ if (type.getKind().isPrimitive() || type.getKind() == TypeKind.WILDCARD || type.getKind() == TypeKind.TYPEVAR) {
+ return null;
+ }
+
+ if (ElementUtil.dimensions(type).endsWith("[]")) {
+ return new FieldRelationInfo(ElementUtil.getTypeElement(type), true);
+ }
+
+ Options opt = optionProvider.getOptionsFor(docTrees, ElementUtil.getTypeElement(type));
+ if (opt.matchesCollPackageExpression(ElementUtil.getQualifiedName(types, type))) {
+ List extends TypeMirror> argTypes = getInterfaceTypeArguments(collectionClassDoc, type);
+ if (argTypes != null && argTypes.size() == 1 && !argTypes.get(0).getKind().isPrimitive()) {
+ return new FieldRelationInfo(ElementUtil.getTypeElement(argTypes.get(0)), true);
+ }
+
+ argTypes = getInterfaceTypeArguments(mapClassDoc, type);
+ if (argTypes != null && argTypes.size() == 2 && !argTypes.get(1).getKind().isPrimitive()) {
+ return new FieldRelationInfo(ElementUtil.getTypeElement(argTypes.get(1)), true);
+ }
+ }
+
+ return new FieldRelationInfo(ElementUtil.getTypeElement(type), false);
+ }
+
+ private List extends TypeMirror> getInterfaceTypeArguments(TypeElement iface, TypeMirror t) {
+ if (t instanceof DeclaredType) {
+ DeclaredType pt = (DeclaredType) t;
+ if (iface != null && iface.equals(pt.asElement())) {
+ return pt.getTypeArguments();
+ } else {
+ for (TypeMirror pti : ElementUtil.getInterfacesTypes(iface)) {
+ List extends TypeMirror> result = getInterfaceTypeArguments(iface, pti);
+ if (result != null) {
+ return result;
+ }
+ }
+ if (ElementUtil.getSuperclassType(pt) != null) {
+ return getInterfaceTypeArguments(iface, ElementUtil.getSuperclassType(pt));
+ }
+ }
+ }
+ return null;
}
/** Convert the class name into a corresponding URL */
- public String classToUrl(ClassDoc cd, boolean rootClass) {
- // building relative path for context and package diagrams
- if(contextPackageName != null && rootClass)
- return buildRelativePathFromClassNames(contextPackageName, cd.containingPackage().name()) + cd.name() + ".html";
- return classToUrl(cd.qualifiedName());
+ public String classToUrl(TypeElement cd, boolean rootClass) {
+ // building relative path for context and package diagrams
+ if (contextPackageName != null && rootClass) {
+ return buildRelativePathFromClassNames(
+ contextPackageName, ElementUtil.getPackageOf(elementUtils, cd).getQualifiedName().toString()) + cd.getSimpleName() + ".html";
+ }
+ return classToUrl(cd.getQualifiedName());
}
/** Convert the class name into a corresponding URL */
- public String classToUrl(String className) {
- ClassDoc classDoc = rootClassdocs.get(className);
- if (classDoc != null) {
- String docRoot = optionProvider.getGlobalOptions().apiDocRoot;
- if (docRoot == null)
- return null;
- return new StringBuilder(docRoot.length() + className.length() + 10).append(docRoot) //
- .append(classDoc.containingPackage().name().replace('.', '/')) //
- .append('/').append(classDoc.name()).append(".html").toString();
- }
- String docRoot = optionProvider.getGlobalOptions().getApiDocRoot(className);
- if (docRoot == null)
- return null;
- int split = splitPackageClass(className);
- StringBuilder buf = new StringBuilder(docRoot.length() + className.length() + 10).append(docRoot);
- if (split > 0) // Avoid -1, and the extra slash then.
- buf.append(className.substring(0, split).replace('.', '/')).append('/');
- return buf.append(className, Math.min(split + 1, className.length()), className.length()) //
- .append(".html").toString();
+ public String classToUrl(Name className) {
+ TypeElement classDoc = rootClassdocs.get(className);
+ if (classDoc != null) {
+ String docRoot = optionProvider.getGlobalOptions().apiDocRoot;
+ if (docRoot == null) {
+ return null;
+ }
+ return new StringBuilder(docRoot.length() + className.length() + 10).append(docRoot) //
+ .append(ElementUtil.getPackageOf(elementUtils, classDoc).getQualifiedName().toString().replace('.', '/')) //
+ .append('/').append(classDoc.getSimpleName()).append(".html").toString();
+ }
+ String docRoot = optionProvider.getGlobalOptions().getApiDocRoot(className);
+ if (docRoot == null) {
+ return null;
+ }
+ int split = splitPackageClass(className);
+ StringBuilder buf = new StringBuilder(docRoot.length() + className.length() + 10).append(docRoot);
+ if (split > 0) { // Avoid -1, and the extra slash then.
+ buf.append(className.toString().substring(0, split).replace('.', '/')).append('/');
+ }
+ return buf.append(className, Math.min(split + 1, className.length()), className.length()) //
+ .append(".html").toString();
}
- /** Dot prologue
- * @throws IOException */
+ /**
+ * Dot prologue
+ *
+ * @throws IOException
+ */
public void prologue() throws IOException {
- Options opt = optionProvider.getGlobalOptions();
- OutputStream os;
+ Options opt = optionProvider.getGlobalOptions();
+ OutputStream os;
- if (opt.outputFileName.equals("-"))
- os = System.out;
- else {
- // prepare output file. Use the output file name as a full path unless the output
- // directory is specified
- File file = new File(opt.outputDirectory, opt.outputFileName);
- // make sure the output directory are there, otherwise create them
- if (file.getParentFile() != null
- && !file.getParentFile().exists())
- file.getParentFile().mkdirs();
- os = new FileOutputStream(file);
- }
+ if (opt.outputFileName.equals("-")) {
+ os = System.out;
+ } else {
+ // prepare output file. Use the output file name as a full path unless the
+ // output
+ // directory is specified
+ File file = new File(opt.outputDirectory, opt.outputFileName);
+ // make sure the output directory are there, otherwise create them
+ if (file.getParentFile() != null && !file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();
+ }
+ os = new FileOutputStream(file);
+ }
- // print prologue
- w = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(os), opt.outputEncoding));
- w.println(
- "#!/usr/local/bin/dot\n" +
- "#\n" +
- "# Class diagram \n" +
- "# Generated by UMLGraph version " +
- Version.VERSION + " (http://www.spinellis.gr/umlgraph/)\n" +
- "#\n\n" +
- "digraph G {\n" +
- linePrefix + "graph [fontnames=\"svg\"]\n" +
- linePrefix + "edge [fontname=\"" + opt.edgeFontName +
- "\",fontsize=" + fmt(opt.edgeFontSize) +
- ",labelfontname=\"" + opt.edgeFontName +
- "\",labelfontsize=" + fmt(opt.edgeFontSize) +
- ",color=\"" + opt.edgeColor + "\"];\n" +
- linePrefix + "node [fontname=\"" + opt.nodeFontName +
- "\",fontcolor=\"" + opt.nodeFontColor +
- "\",fontsize=" + fmt(opt.nodeFontSize) +
- ",shape=plaintext,margin=0,width=0,height=0];"
- );
+ // print prologue
+ w = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(os), opt.outputEncoding));
+ w.println("#!/usr/local/bin/dot\n" + "#\n" + "# Class diagram \n" + "# Generated by UMLGraph version "
+ + Version.VERSION + " (http://www.spinellis.gr/umlgraph/)\n" + "#\n\n" + "digraph G {\n" + linePrefix
+ + "graph [fontnames=\"svg\"]\n" + linePrefix + "edge [fontname=\"" + opt.edgeFontName + "\",fontsize="
+ + fmt(opt.edgeFontSize) + ",labelfontname=\"" + opt.edgeFontName + "\",labelfontsize="
+ + fmt(opt.edgeFontSize) + ",color=\"" + opt.edgeColor + "\"];\n" + linePrefix + "node [fontname=\""
+ + opt.nodeFontName + "\",fontcolor=\"" + opt.nodeFontColor + "\",fontsize=" + fmt(opt.nodeFontSize)
+ + ",shape=plaintext,margin=0,width=0,height=0];");
- w.println(linePrefix + "nodesep=" + opt.nodeSep + ";");
- w.println(linePrefix + "ranksep=" + opt.rankSep + ";");
- if (opt.horizontal)
- w.println(linePrefix + "rankdir=LR;");
- if (opt.bgColor != null)
- w.println(linePrefix + "bgcolor=\"" + opt.bgColor + "\";\n");
+ w.println(linePrefix + "nodesep=" + opt.nodeSep + ";");
+ w.println(linePrefix + "ranksep=" + opt.rankSep + ";");
+ if (opt.horizontal) {
+ w.println(linePrefix + "rankdir=LR;");
+ }
+ if (opt.bgColor != null) {
+ w.println(linePrefix + "bgcolor=\"" + opt.bgColor + "\";\n");
+ }
}
/** Dot epilogue */
public void epilogue() {
- w.println("}\n");
- w.flush();
- w.close();
+ w.println("}\n");
+ w.flush();
+ w.close();
}
-
- private void externalTableStart(Options opt, String name, String url) {
- String bgcolor = opt.nodeFillColor == null ? "" : (" bgcolor=\"" + opt.nodeFillColor + "\"");
- String href = url == null ? "" : (" href=\"" + url + "\" target=\"_parent\"");
- w.print("<
" + linePostfix);
}
private static class FieldRelationInfo {
- ClassDoc cd;
- boolean multiple;
+ TypeElement cd;
+ boolean multiple;
- public FieldRelationInfo(ClassDoc cd, boolean multiple) {
- this.cd = cd;
- this.multiple = 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 f498210..5939a0c 100644
--- a/src/main/java/org/umlgraph/doclet/ClassInfo.java
+++ b/src/main/java/org/umlgraph/doclet/ClassInfo.java
@@ -22,9 +22,12 @@ 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
+ * Class's dot-compatible alias name (for fully qualified class names) and
+ * printed information
+ *
* @version $Revision$
* @author Diomidis Spinellis
*/
@@ -36,37 +39,35 @@ class ClassInfo {
boolean nodePrinted;
/** True if the class class node is hidden */
boolean hidden;
- /**
- * The list of classes that share a relation with this one. Contains
- * all the classes linked with a bi-directional relation , and the ones
- * referred by a directed relation
+ /**
+ * The list of classes that share a relation with this one. Contains all the
+ * 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;
- name = "c" + classNumber;
- classNumber++;
+ hidden = h;
+ name = "c" + classNumber;
+ classNumber++;
}
-
- public void addRelation(String dest, RelationType rt, RelationDirection d) {
- RelationPattern ri = relatedClasses.get(dest);
- if(ri == null) {
- ri = new RelationPattern(RelationDirection.NONE);
- relatedClasses.put(dest, ri);
- }
- ri.addRelation(rt, d);
+
+ public void addRelation(Name dest, RelationType rt, RelationDirection d) {
+ RelationPattern ri = relatedClasses.get(dest);
+ if (ri == null) {
+ ri = new RelationPattern(RelationDirection.NONE);
+ relatedClasses.put(dest, ri);
+ }
+ ri.addRelation(rt, d);
}
-
- public RelationPattern getRelation(String dest) {
- return relatedClasses.get(dest);
+
+ public RelationPattern getRelation(CharSequence dest) {
+ return relatedClasses.get(dest);
}
/** Start numbering from zero. */
public static void reset() {
- classNumber = 0;
+ classNumber = 0;
}
-
}
-
diff --git a/src/main/java/org/umlgraph/doclet/ClassMatcher.java b/src/main/java/org/umlgraph/doclet/ClassMatcher.java
index 9665376..294fcfa 100644
--- a/src/main/java/org/umlgraph/doclet/ClassMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/ClassMatcher.java
@@ -17,22 +17,22 @@
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 condition. The nature of the condition is dependent on
- * the kind of matcher
+ * A ClassMatcher is used to check if a class definition matches a specific
+ * condition. The nature of the condition is dependent on the kind of matcher
+ *
* @author wolf
*/
public interface ClassMatcher {
/**
- * Returns the options for the specified class.
+ * Returns the options for the specified class.
*/
- public boolean matches(ClassDoc cd);
-
+ public boolean matches(TypeElement cd);
+
/**
- * Returns the options for the specified class.
+ * 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 cc59d00..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
@@ -38,6 +40,7 @@ import com.sun.javadoc.RootDoc;
* This class needs to perform quite a bit of computations in order to gather
* the network of class releationships, so you are allowed to reuse it should
* you
+ *
* @author wolf
*
* @depend - - - DevNullWriter
@@ -45,34 +48,35 @@ 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;
/**
* Builds the context matcher
- * @param root The root doc returned by JavaDoc
- * @param pattern The pattern that will match the "center" of this
- * context
- * @param keepParentHide If true, parent option hide patterns will be
- * preserved, so that classes hidden by the options won't
- * be shown in the context
+ *
+ * @param root The root doc returned by JavaDoc
+ * @param pattern The pattern that will match the "center" of this
+ * context
+ * @param keepParentHide If true, parent option hide patterns will be preserved,
+ * so that classes hidden by the options won't be shown in
+ * the context
* @throws IOException
*/
- public ContextMatcher(RootDoc root, Pattern pattern, Options options, boolean keepParentHide) throws IOException {
- this.pattern = pattern;
- this.root = root;
- this.keepParentHide = keepParentHide;
- opt = (Options) options.clone();
- opt.setOption(new String[] { "!hide" });
- opt.setOption(new String[] { "!attributes" });
- opt.setOption(new String[] { "!operations" });
- this.cg = new ClassGraphHack(root, opt);
+ public ContextMatcher(DocletEnvironment root, Pattern pattern, Options options, boolean keepParentHide) throws IOException {
+ this.pattern = pattern;
+ this.root = root;
+ this.keepParentHide = keepParentHide;
+ opt = (Options) options.clone();
+ opt.setOption(new String[] { "!hide" });
+ opt.setOption(new String[] { "!attributes" });
+ opt.setOption(new String[] { "!operations" });
+ this.cg = new ClassGraphHack(root, opt);
- setContextCenter(pattern);
+ setContextCenter(pattern);
}
/**
@@ -80,116 +84,126 @@ public class ContextMatcher implements ClassMatcher {
*
* This can be used to speed up subsequent matching with the same global
* options, since the class network informations will be reused.
+ *
* @param pattern
*/
public void setContextCenter(Pattern pattern) {
- // 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);
- }
- }
+ // 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 (Element cd : root.getIncludedElements()) {
+ if (cd instanceof TypeElement && pattern.matcher(cd.toString()).matches()) {
+ matched.add((TypeElement) cd);
+ addToGraph((TypeElement) cd);
+ }
+ }
}
/**
- * Adds the specified class to the internal class graph along with its
- * relations and dependencies, eventually inferring them, according to the
- * Options specified for this matcher
+ * Adds the specified class to the internal class graph along with its relations
+ * and dependencies, eventually inferring them, according to the Options
+ * specified for this matcher
+ *
* @param cd
*/
- private void addToGraph(ClassDoc 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()))
- return;
+ 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())) {
+ return;
+ }
- visited.add(cd.toString());
- cg.printClass(cd, false);
- cg.printRelations(cd);
- if (opt.inferRelationships)
- cg.printInferredRelations(cd);
- if (opt.inferDependencies)
- cg.printInferredDependencies(cd);
+ visited.add(cd.toString());
+ cg.printClass(cd, false);
+ cg.printRelations(cd);
+ if (opt.inferRelationships) {
+ cg.printInferredRelations(cd);
+ }
+ 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()))
- return false;
+ 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))
- return true;
+ // if the class is matched, it's in by default.
+ 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
- addToGraph(cd);
- return matches(cd.toString());
+ // otherwise, add the class to the graph and see if it's associated
+ // with any of the matched classes using the classgraph hack
+ addToGraph(cd);
+ return matches(cd.toString());
}
/**
- * @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())
- return true;
+ public boolean matches(CharSequence name) {
+ if (pattern.matcher(name).matches()) {
+ return true;
+ }
- for (ClassDoc mcd : matched) {
- RelationPattern rp = cg.getClassInfo(mcd, true).getRelation(name);
- if (rp != null && opt.contextRelationPattern.matchesOne(rp))
- return true;
- }
- return false;
+ for (TypeElement mcd : matched) {
+ RelationPattern rp = cg.getClassInfo(mcd, true).getRelation(name);
+ if (rp != null && opt.contextRelationPattern.matchesOne(rp)) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * A quick hack to compute class dependencies reusing ClassGraph but
- * without generating output. Will be removed once the ClassGraph class
- * will be split into two classes for graph computation and output
- * generation.
+ * A quick hack to compute class dependencies reusing ClassGraph but without
+ * generating output. Will be removed once the ClassGraph class will be split
+ * into two classes for graph computation and output generation.
+ *
* @author wolf
*
*/
private static class ClassGraphHack extends ClassGraph {
- public ClassGraphHack(RootDoc root, OptionProvider optionProvider) throws IOException {
- super(root, optionProvider, null);
- prologue();
- }
+ public ClassGraphHack(DocletEnvironment root, OptionProvider optionProvider) throws IOException {
+ super(root, optionProvider, null);
+ prologue();
+ }
- @Override
- public void prologue() throws IOException {
- w = new PrintWriter(new DevNullWriter());
- }
+ @Override
+ public void prologue() throws IOException {
+ w = new PrintWriter(new DevNullWriter());
+ }
}
/**
* Simple dev/null imitation
+ *
* @author wolf
*/
private static class DevNullWriter extends Writer {
- @Override
- public void write(char[] cbuf, int off, int len) throws IOException {
- // nothing to do
- }
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ // nothing to do
+ }
- @Override
- public void flush() throws IOException {
- // nothing to do
- }
+ @Override
+ public void flush() throws IOException {
+ // nothing to do
+ }
- @Override
- public void close() throws IOException {
- // nothing to do
- }
+ @Override
+ public void close() throws IOException {
+ // nothing to do
+ }
}
diff --git a/src/main/java/org/umlgraph/doclet/ContextView.java b/src/main/java/org/umlgraph/doclet/ContextView.java
index 040725e..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
@@ -14,12 +20,14 @@ import com.sun.javadoc.RootDoc;
* single {@linkplain ContextMatcher}, but provides some extra configuration
* such as context highlighting and output path configuration (and it is
* specified in code rather than in javadoc comments).
+ *
* @author wolf
*
*/
public class ContextView implements OptionProvider {
- private ClassDoc cd;
+ private TypeElement cd;
+ private DocletEnvironment root;
private ContextMatcher matcher;
private Options globalOptions;
private Options myGlobalOptions;
@@ -28,93 +36,98 @@ 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 {
- this.cd = cd;
- String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name()
- + ".dot";
+ public ContextView(String outputFolder, TypeElement cd, DocletEnvironment root, Options parent) throws IOException {
+ this.cd = cd;
+ 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
- this.globalOptions = parent.getGlobalOptions();
-
- this.packageOptions = parent.getGlobalOptions();
- this.packageOptions.showQualified = false;
+ // setup options statically, so that we won't need to change them so
+ // often
+ this.globalOptions = parent.getGlobalOptions();
- this.myGlobalOptions = parent.getGlobalOptions();
- this.myGlobalOptions.setOption(new String[] { "output", outputPath });
- this.myGlobalOptions.setOption(HIDE_OPTIONS);
+ this.packageOptions = parent.getGlobalOptions();
+ this.packageOptions.showQualified = false;
- this.hideOptions = parent.getGlobalOptions();
- this.hideOptions.setOption(HIDE_OPTIONS);
+ this.myGlobalOptions = parent.getGlobalOptions();
+ this.myGlobalOptions.setOption(new String[] { "output", outputPath });
+ this.myGlobalOptions.setOption(HIDE_OPTIONS);
- this.centerOptions = parent.getGlobalOptions();
- this.centerOptions.nodeFillColor = "lemonChiffon";
- this.centerOptions.showQualified = false;
+ this.hideOptions = parent.getGlobalOptions();
+ this.hideOptions.setOption(HIDE_OPTIONS);
- this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.toString())),
- myGlobalOptions, true);
+ this.centerOptions = parent.getGlobalOptions();
+ this.centerOptions.nodeFillColor = "lemonChiffon";
+ this.centerOptions.showQualified = false;
+
+ this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.getQualifiedName().toString())), myGlobalOptions, true);
}
- public void setContextCenter(ClassDoc contextCenter) {
- this.cd = contextCenter;
- String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name()
- + ".dot";
- this.myGlobalOptions.setOption(new String[] { "output", outputPath });
- matcher.setContextCenter(Pattern.compile(Pattern.quote(cd.toString())));
+ public void setContextCenter(TypeElement contextCenter) {
+ this.cd = contextCenter;
+ 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())));
}
public String getDisplayName() {
- return "Context view for class " + cd;
+ return "Context view for class " + cd;
}
public Options getGlobalOptions() {
- return myGlobalOptions;
+ return myGlobalOptions;
}
- public Options getOptionsFor(ClassDoc cd) {
- Options opt;
- if (globalOptions.matchesHideExpression(cd.qualifiedName())
- || !(matcher.matches(cd) || globalOptions.matchesIncludeExpression(cd.qualifiedName()))) {
- opt = hideOptions;
- } else if (cd.equals(this.cd)) {
- opt = centerOptions;
- } else if(cd.containingPackage().equals(this.cd.containingPackage())){
- opt = packageOptions;
- } else {
- opt = globalOptions;
- }
- Options optionClone = (Options) opt.clone();
- overrideForClass(optionClone, cd);
- return optionClone;
+ public Options getOptionsFor(DocTrees dt, TypeElement cd) {
+ Options opt;
+ if (globalOptions.matchesHideExpression(cd.getQualifiedName())
+ || !(matcher.matches(cd) || globalOptions.matchesIncludeExpression(cd.getQualifiedName()))) {
+ opt = hideOptions;
+ } else if (cd.equals(this.cd)) {
+ opt = centerOptions;
+ } else if (root.getElementUtils().getPackageOf(cd).equals(root.getElementUtils().getPackageOf(this.cd))) {
+ opt = packageOptions;
+ } else {
+ opt = globalOptions;
+ }
+ Options optionClone = (Options) opt.clone();
+ overrideForClass(optionClone, cd);
+ return optionClone;
}
- public Options getOptionsFor(String name) {
- Options opt;
- if (!matcher.matches(name))
- opt = hideOptions;
- else if (name.equals(cd.name()))
- opt = centerOptions;
- else
- opt = globalOptions;
- Options optionClone = (Options) opt.clone();
- overrideForClass(optionClone, name);
- return optionClone;
+ public Options getOptionsFor(CharSequence name) {
+ Options opt;
+ if (!matcher.matches(name)) {
+ opt = hideOptions;
+ } else if (name.equals(cd.getQualifiedName())) {
+ opt = centerOptions;
+ } 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())))
- opt.setOption(HIDE_OPTIONS);
- if (cd.equals(this.cd))
- opt.nodeFillColor = "lemonChiffon";
+ 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)) {
+ opt.nodeFillColor = "lemonChiffon";
+ }
}
- public void overrideForClass(Options opt, String className) {
- if (!(matcher.matches(className) || opt.matchesIncludeExpression(className)))
- opt.setOption(HIDE_OPTIONS);
+ 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 09355ea..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.
@@ -33,12 +33,12 @@ public enum Font {
// Static initialization of further values.
static {
- // use an appropriate font depending on the current operating system
- if (System.getProperty("os.name").toLowerCase().contains("windows")) {
- DEFAULT_FONT = "Arial";
- } else {
- DEFAULT_FONT = "Helvetica"; // TODO: can we use just "sans"?
- }
+ // use an appropriate font depending on the current operating system
+ if (System.getProperty("os.name").toLowerCase().contains("windows")) {
+ DEFAULT_FONT = "Arial";
+ } else {
+ DEFAULT_FONT = "Helvetica"; // TODO: can we use just "sans"?
+ }
}
/**
@@ -49,53 +49,53 @@ public enum Font {
* @return Wrapped text
*/
public String wrap(Options opt, String text) {
- if (text.isEmpty() || this == NORMAL)
- return text;
- String face = null;
- double size = -1;
- boolean italic = false;
- switch (this) {
- case EDGE:
- case NODE:
- // Not used with the wrap function.
- throw new UnsupportedOperationException();
- case ABSTRACT:
- italic = opt.nodeFontAbstractItalic;
- case NORMAL:
- break;
- case CLASS_ABSTRACT:
- italic = opt.nodeFontAbstractItalic;
- case CLASS:
- face = opt.nodeFontClassName;
- size = opt.nodeFontClassSize;
- break;
- case PACKAGE:
- face = opt.nodeFontPackageName;
- size = opt.nodeFontPackageSize;
- break;
- case TAG:
- face = opt.nodeFontTagName;
- size = opt.nodeFontTagSize;
- break;
- }
- if (face == null && size < 0 && !italic)
- return text;
- StringBuilder buf = new StringBuilder(text.length() + 100);
- if (face != null || size > 0) {
- buf.append(" 0)
- buf.append(" point-size=\"").append(size).append('"');
- buf.append('>');
- }
- if (italic)
- buf.append("");
- buf.append(text);
- if (italic)
- buf.append("");
- if (face != null || size > 0)
- buf.append("");
- return buf.toString();
+ if (text.isEmpty() || this == NORMAL)
+ return text;
+ String face = null;
+ double size = -1;
+ boolean italic = false;
+ switch (this) {
+ case EDGE:
+ case NODE:
+ // Not used with the wrap function.
+ throw new UnsupportedOperationException();
+ case ABSTRACT:
+ italic = opt.nodeFontAbstractItalic;
+ case NORMAL:
+ break;
+ case CLASS_ABSTRACT:
+ italic = opt.nodeFontAbstractItalic;
+ case CLASS:
+ face = opt.nodeFontClassName;
+ size = opt.nodeFontClassSize;
+ break;
+ case PACKAGE:
+ face = opt.nodeFontPackageName;
+ size = opt.nodeFontPackageSize;
+ break;
+ case TAG:
+ face = opt.nodeFontTagName;
+ size = opt.nodeFontTagSize;
+ break;
+ }
+ if (face == null && size < 0 && !italic)
+ return text;
+ StringBuilder buf = new StringBuilder(text.length() + 100);
+ if (face != null || size > 0) {
+ buf.append(" 0)
+ buf.append(" point-size=\"").append(size).append('"');
+ buf.append('>');
+ }
+ if (italic)
+ buf.append("");
+ buf.append(text);
+ if (italic)
+ buf.append("");
+ if (face != null || size > 0)
+ buf.append("");
+ return buf.toString();
}
}
diff --git a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java
index 6c63b98..a4a6f13 100644
--- a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java
+++ b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java
@@ -2,41 +2,51 @@ 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 matched by regular expression provided.
+ * Matches every class that implements (directly or indirectly) an interfaces
+ * matched by regular expression provided.
*/
public class InterfaceMatcher implements ClassMatcher {
- protected RootDoc root;
+ protected DocletEnvironment root;
protected Pattern pattern;
- public InterfaceMatcher(RootDoc root, Pattern pattern) {
- this.root = root;
- this.pattern = pattern;
+ public InterfaceMatcher(DocletEnvironment root, Pattern pattern) {
+ this.root = root;
+ this.pattern = pattern;
}
- public boolean matches(ClassDoc cd) {
- // if it's the interface we're looking for, match
- if(cd.isInterface() && 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))
- return true;
-
- // recurse on supeclass, if available
- return cd.superclass() == null ? false : matches(cd.superclass());
+ public boolean matches(TypeElement cd) {
+ // if it's the interface we're looking for, match
+ 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 (TypeMirror type : cd.getInterfaces()) {
+ TypeElement iType = ElementUtil.getTypeElement(type);
+ if (iType != null && matches(iType)) {
+ return true;
+ }
+ }
+
+ // 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);
- return cd == null ? false : matches(cd);
+ 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 8287780..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,16 +43,17 @@ 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.
- *
Will be used to provide progress feedback on the console
+ *
+ * Will be used to provide progress feedback on the console
*/
public String getDisplayName();
}
diff --git a/src/main/java/org/umlgraph/doclet/Options.java b/src/main/java/org/umlgraph/doclet/Options.java
index 90b7542..f5b4175 100644
--- a/src/main/java/org/umlgraph/doclet/Options.java
+++ b/src/main/java/org/umlgraph/doclet/Options.java
@@ -29,32 +29,571 @@ 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
+ *
* @version $Revision$
* @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("--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"
+ + "