From d80597d547b9f422be27d372096cece32dedbeae Mon Sep 17 00:00:00 2001 From: Laurent SCHOELENS Date: Tue, 21 Mar 2023 08:15:34 +0100 Subject: [PATCH 1/4] remove .classpath / .project files and add it to .gitignore --- .classpath | 9 --------- .gitignore | 2 ++ .project | 17 ----------------- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 .classpath delete mode 100644 .project diff --git a/.classpath b/.classpath deleted file mode 100644 index 0c326bc..0000000 --- a/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/.gitignore b/.gitignore index 8d44357..3f39de9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.iws *.swp .settings +.project +.classpath _vimrc build CHECKSUM.MD5 diff --git a/.project b/.project deleted file mode 100644 index 553dcbd..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - umlgraph_cvs - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - From c8f805bdef0152a0bea4faf20133a1fbc2c100ad Mon Sep 17 00:00:00 2001 From: Laurent SCHOELENS Date: Tue, 21 Mar 2023 09:15:55 +0100 Subject: [PATCH 2/4] reformat classes --- .../java/org/umlgraph/doclet/ClassGraph.java | 1360 +++++++++-------- .../java/org/umlgraph/doclet/ClassInfo.java | 41 +- .../org/umlgraph/doclet/ClassMatcher.java | 12 +- .../org/umlgraph/doclet/ContextMatcher.java | 165 +- .../java/org/umlgraph/doclet/ContextView.java | 117 +- src/main/java/org/umlgraph/doclet/Font.java | 108 +- .../org/umlgraph/doclet/InterfaceMatcher.java | 36 +- .../org/umlgraph/doclet/OptionProvider.java | 7 +- .../java/org/umlgraph/doclet/Options.java | 883 ++++++----- .../org/umlgraph/doclet/PackageMatcher.java | 14 +- .../java/org/umlgraph/doclet/PackageView.java | 63 +- .../org/umlgraph/doclet/PatternMatcher.java | 7 +- .../umlgraph/doclet/RelationDirection.java | 21 +- .../org/umlgraph/doclet/RelationPattern.java | 32 +- .../org/umlgraph/doclet/RelationType.java | 6 +- src/main/java/org/umlgraph/doclet/Shape.java | 24 +- .../java/org/umlgraph/doclet/StringUtil.java | 258 ++-- .../org/umlgraph/doclet/SubclassMatcher.java | 24 +- .../java/org/umlgraph/doclet/UMLOptions.java | 4 +- .../java/org/umlgraph/doclet/UmlGraph.java | 172 +-- .../java/org/umlgraph/doclet/UmlGraphDoc.java | 342 ++--- .../java/org/umlgraph/doclet/Version.java | 6 +- src/main/java/org/umlgraph/doclet/View.java | 178 ++- .../java/org/umlgraph/doclet/Visibility.java | 19 +- 24 files changed, 1942 insertions(+), 1957 deletions(-) diff --git a/src/main/java/org/umlgraph/doclet/ClassGraph.java b/src/main/java/org/umlgraph/doclet/ClassGraph.java index fe2e21e..83552ea 100644 --- a/src/main/java/org/umlgraph/doclet/ClassGraph.java +++ b/src/main/java/org/umlgraph/doclet/ClassGraph.java @@ -61,6 +61,7 @@ import com.sun.javadoc.WildcardType; /** * Class graph generation engine + * * @depend - - - StringUtil * @depend - - - Options * @composed - - * ClassInfo @@ -71,891 +72,896 @@ import com.sun.javadoc.WildcardType; */ class ClassGraph { enum Align { - LEFT, CENTER, RIGHT; + LEFT, CENTER, RIGHT; - public final String lower; + public final String lower; - private Align() { - this.lower = toString().toLowerCase(); - } + private Align() { + this.lower = toString().toLowerCase(); + } }; protected Map classnames = new HashMap(); protected Set rootClasses; - protected Map rootClassdocs = new HashMap(); + protected Map rootClassdocs = new HashMap(); protected OptionProvider optionProvider; protected PrintWriter w; protected ClassDoc collectionClassDoc; protected ClassDoc mapClassDoc; protected String linePostfix; protected String linePrefix; - - // used only when generating context class diagrams in UMLDoc, to generate the proper + + // used only when generating context class diagrams in UMLDoc, to generate the + // proper // relative links to other classes in the image map protected final String 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 + * 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) + * @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"; - } + 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"; + } /** 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(); + 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 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(); + 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 : " "; + 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(); + 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(); } /** 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())); + return ((generics ? opt.showQualifiedGenerics : opt.showQualified) ? // + t.qualifiedTypeName() : t.typeName()) // + + (opt.hideGenerics ? "" : typeParameters(opt, t.asParameterizedType())); } /** 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(); + 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(); } /** 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(); + if (t.typeName().equals("void")) + return ""; + return " : " + type(opt, t, false) + t.dimension(); } /** 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); - } + 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. + * 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; + 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; } /** 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; + 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; - 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] + "}")); - } + 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] + "}")); + } } /** - * 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])); - } + 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])); + } } /** 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); + 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); } protected ClassInfo getClassInfo(ClassDoc cd, boolean create) { - return getClassInfo(cd, cd.toString(), create); + return getClassInfo(cd, cd.toString(), create); } protected ClassInfo getClassInfo(String className, boolean create) { - return getClassInfo(null, className, 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; + 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; } - /** Return true if the class name is associated to an hidden class or matches a hide expression */ + /** + * 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); + className = removeTemplate(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="); + 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="); - 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 && c.fields().length > 0) + || (c.isEnum() && opt.showEnumConstants && c.enumConstants().length > 0) + || (opt.showOperations && c.methods().length > 0) + || (opt.showConstructors && c.constructors().length > 0); - final String url = classToUrl(c, rootClass); - externalTableStart(opt, c.qualifiedName(), url); + final String url = classToUrl(c, rootClass); + externalTableStart(opt, c.qualifiedName(), 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.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); - /* - * 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(); + 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()); - 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; + 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; } /** * 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]); - } - } + 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]); + } + } } /** * 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, 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(); - 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, ClassDoc from, ClassDoc to, String tailLabel, String label, + String headLabel) { + relation(opt, rt, from, from.toString(), to, to.toString(), 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); + 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); } /** 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); + 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); + } } - /** Returns an array representing the imported classes of c. - * Disables the deprecation warning, which is output, because the - * imported classed are an implementation detail. + /** + * 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 */ - @SuppressWarnings( "deprecation" ) + 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); + } + } + } + + /** + * 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(); } /** - * 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 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(ClassDoc c) { - if (hidden(c)) - return; + 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())); - } + 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))); + // 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; + // 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; + // check if the destination is excluded from inference + ClassDoc fc = type.asClassDoc(); + if (hidden(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, "", "", ""); - } - - } + // 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 + * 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); + if (visibility == Visibility.PRIVATE) + return Arrays.asList(docs); - List filtered = new ArrayList(); - for (T doc : docs) { - if (Visibility.get(doc).compareTo(visibility) > 0) - filtered.add(doc); - } - return filtered; + List filtered = new ArrayList(); + for (T doc : docs) { + if (Visibility.get(doc).compareTo(visibility) > 0) + filtered.add(doc); + } + return filtered; } - - 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); + Type type = field.type(); + if (type.isPrimitive() || type instanceof WildcardType || type instanceof TypeVariable) + return null; - argTypes = getInterfaceTypeArguments(mapClassDoc, type); - if (argTypes != null && argTypes.length == 2 && !argTypes[1].isPrimitive()) - return new FieldRelationInfo(argTypes[1].asClassDoc(), true); - } + if (type.dimension().endsWith("[]")) { + return new FieldRelationInfo(type.asClassDoc(), true); + } - return new FieldRelationInfo(type.asClassDoc(), false); + 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); } - + 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; + 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; } /** 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()); + // 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()); } /** 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(); + 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(); } - /** 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); + String bgcolor = opt.nodeFillColor == null ? "" : (" bgcolor=\"" + opt.nodeFillColor + "\""); + String href = url == null ? "" : (" href=\"" + url + "\" target=\"_parent\""); + w.print("<
" + linePostfix); } - + private void externalTableEnd() { - w.print(linePrefix + linePrefix + "
>"); + w.print(linePrefix + linePrefix + ">"); } - + private void innerTableStart() { - w.print(linePrefix + linePrefix + "" + linePostfix); + w.print(linePrefix + linePrefix + "" + - opt.shape.extraColumn() + "" + linePostfix); + w.print(linePrefix + linePrefix + "
" + + linePostfix); } - + /** * Start the first inner table of a class. */ private void firstInnerTableStart(Options opt) { - w.print(linePrefix + linePrefix + "" + opt.shape.extraColumn() + - "" + linePostfix); } /** * End the first inner table of a class. */ private void firstInnerTableEnd(Options opt) { - w.print(linePrefix + linePrefix + "
" + linePostfix); + w.print(linePrefix + linePrefix + "" + opt.shape.extraColumn() + + "" + linePostfix); + w.print(linePrefix + linePrefix + "
" + linePostfix); } - + private void innerTableEnd() { - w.print(linePrefix + linePrefix + "
" + opt.shape.extraColumn() + "" + linePostfix); } private void tableLine(Align align, String text) { - w.print(linePrefix + linePrefix // - + " " // - + text // MAY contain markup! - + " " + linePostfix); + w.print(linePrefix + linePrefix // + + " " // + + text // MAY contain markup! + + " " + linePostfix); } private static class FieldRelationInfo { - ClassDoc cd; - boolean multiple; + ClassDoc cd; + boolean multiple; - public FieldRelationInfo(ClassDoc cd, boolean multiple) { - this.cd = cd; - this.multiple = multiple; - } + public FieldRelationInfo(ClassDoc 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..ccb67e0 100644 --- a/src/main/java/org/umlgraph/doclet/ClassInfo.java +++ b/src/main/java/org/umlgraph/doclet/ClassInfo.java @@ -23,8 +23,9 @@ import java.util.HashMap; import java.util.Map; /** - * 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 +37,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(); 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); + 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); + 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..d89df36 100644 --- a/src/main/java/org/umlgraph/doclet/ClassMatcher.java +++ b/src/main/java/org/umlgraph/doclet/ClassMatcher.java @@ -20,19 +20,19 @@ package org.umlgraph.doclet; import com.sun.javadoc.ClassDoc; /** - * 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); - + /** - * Returns the options for the specified class. + * Returns the options for the specified class. */ public boolean matches(String name); } diff --git a/src/main/java/org/umlgraph/doclet/ContextMatcher.java b/src/main/java/org/umlgraph/doclet/ContextMatcher.java index cc59d00..626fab0 100644 --- a/src/main/java/org/umlgraph/doclet/ContextMatcher.java +++ b/src/main/java/org/umlgraph/doclet/ContextMatcher.java @@ -38,6 +38,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 @@ -54,25 +55,26 @@ public class ContextMatcher implements ClassMatcher { /** * 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); + 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 +82,119 @@ 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 (ClassDoc cd : root.classes()) { + if (pattern.matcher(cd.toString()).matches()) { + matched.add(cd); + addToGraph(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; + // 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) */ public boolean matches(ClassDoc cd) { - if (keepParentHide && opt.matchesHideExpression(cd.toString())) - return false; + if (keepParentHide && opt.matchesHideExpression(cd.toString())) + 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) */ public boolean matches(String name) { - if (pattern.matcher(name).matches()) - return true; + 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 (ClassDoc 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(RootDoc 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..48e9737 100755 --- a/src/main/java/org/umlgraph/doclet/ContextView.java +++ b/src/main/java/org/umlgraph/doclet/ContextView.java @@ -14,6 +14,7 @@ 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 * */ @@ -28,93 +29,89 @@ 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, ClassDoc cd, RootDoc root, Options parent) throws IOException { + this.cd = cd; + String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() + ".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.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()))); + 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 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; + 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(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; + 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 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"; + 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, String className) { - if (!(matcher.matches(className) || opt.matchesIncludeExpression(className))) - opt.setOption(HIDE_OPTIONS); + 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..f9bcc0d 100644 --- a/src/main/java/org/umlgraph/doclet/Font.java +++ b/src/main/java/org/umlgraph/doclet/Font.java @@ -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..e39518d 100644 --- a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java +++ b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java @@ -6,8 +6,8 @@ import com.sun.javadoc.ClassDoc; import com.sun.javadoc.RootDoc; /** - * 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 { @@ -15,28 +15,28 @@ public class InterfaceMatcher implements ClassMatcher { protected Pattern pattern; public InterfaceMatcher(RootDoc root, Pattern pattern) { - this.root = root; - this.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()); + // 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(String name) { - ClassDoc cd = root.classNamed(name); - return cd == null ? false : matches(cd); + ClassDoc cd = root.classNamed(name); + return cd == null ? false : matches(cd); } } diff --git a/src/main/java/org/umlgraph/doclet/OptionProvider.java b/src/main/java/org/umlgraph/doclet/OptionProvider.java index 8287780..151d2b7 100644 --- a/src/main/java/org/umlgraph/doclet/OptionProvider.java +++ b/src/main/java/org/umlgraph/doclet/OptionProvider.java @@ -20,8 +20,8 @@ package org.umlgraph.doclet; import com.sun.javadoc.ClassDoc; /** - * 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 { /** @@ -51,7 +51,8 @@ public interface OptionProvider { /** * 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..7018da8 100644 --- a/src/main/java/org/umlgraph/doclet/Options.java +++ b/src/main/java/org/umlgraph/doclet/Options.java @@ -44,6 +44,7 @@ import com.sun.javadoc.Tag; /** * Represent the program options + * * @version $Revision$ * @author Diomidis Spinellis */ @@ -51,7 +52,7 @@ public class Options implements Cloneable, OptionProvider { // reused often, especially in UmlGraphDoc, worth creating just once and reusing private static final Pattern allPattern = Pattern.compile(".*"); protected static final String DEFAULT_EXTERNAL_APIDOC = "http://docs.oracle.com/javase/7/docs/api/"; - + // instance fields List hidePatterns = new ArrayList(); List includePatterns = new ArrayList(); @@ -97,14 +98,13 @@ public class Options implements Cloneable, OptionProvider { double rankSep = 0.5; public String outputDirectory = null; /* - * Numeric values are preferable to symbolic here. - * Symbolic reportedly fail on MacOSX, and also are - * more difficult to verify with XML tools. + * Numeric values are preferable to symbolic here. Symbolic reportedly fail on + * MacOSX, and also are more difficult to verify with XML tools. */ /** Guillemot left (open) */ - String guilOpen = "«"; // « \u00ab + String guilOpen = "«"; // « \u00ab /** Guillemot right (close) */ - String guilClose = "»"; // » \u00bb + String guilClose = "»"; // » \u00bb boolean inferRelationships = false; boolean inferDependencies = false; boolean collapsibleDiagrams = false; @@ -119,8 +119,10 @@ public class Options implements Cloneable, OptionProvider { // internal option, used by UMLDoc to generate relative links between classes boolean relativeLinksForSourcePackages = false; // internal option, used by UMLDoc to force strict matching on the class names - // and avoid problems with packages in the template declaration making UmlGraph hide - // classes outside of them (for example, class gr.spinellis.Foo + // and avoid problems with packages in the template declaration making UmlGraph + // hide + // classes outside of them (for example, class gr.spinellis.Foo // would have been hidden by the hide pattern "java.*" // TODO: consider making this standard behaviour boolean strictMatching = false; @@ -130,555 +132,534 @@ public class Options implements Cloneable, OptionProvider { } @Override - public Object clone() { - Options clone = null; - try { - clone = (Options) super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Cannot clone?!?", e); // Should not happen - } - // deep clone the hide and collection patterns - clone.hidePatterns = new ArrayList(hidePatterns); - clone.includePatterns = new ArrayList(includePatterns); - clone.collPackages= new ArrayList(collPackages); - clone.apiDocMap = new HashMap(apiDocMap); - return clone; + public Object clone() { + Options clone = null; + try { + clone = (Options) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Cannot clone?!?", e); // Should not happen + } + // deep clone the hide and collection patterns + clone.hidePatterns = new ArrayList(hidePatterns); + clone.includePatterns = new ArrayList(includePatterns); + clone.collPackages = new ArrayList(collPackages); + clone.apiDocMap = new HashMap(apiDocMap); + return clone; } /** Most complete output */ public void setAll() { - showAttributes = true; - showEnumerations = true; - showEnumConstants = true; - showOperations = true; - showConstructors = true; - showVisibility = true; - showType = true; + showAttributes = true; + showEnumerations = true; + showEnumConstants = true; + showOperations = true; + showConstructors = true; + showVisibility = true; + showType = true; } - + /** * Match strings, ignoring leading -, -!, and !. * - * @param given Given string + * @param given Given string * @param expect Expected string * @return {@code true} on success */ protected static boolean matchOption(String given, String expect) { - return matchOption(given, expect, false); + return matchOption(given, expect, false); } /** * Match strings, ignoring leading -, -!, and !. * - * @param given Given string - * @param expect Expected string + * @param given Given string + * @param expect Expected string * @param negative May be negative * @return {@code true} on success */ protected static boolean matchOption(String given, String expect, boolean negative) { - int begin = 0, end = given.length(); - if (begin < end && given.charAt(begin) == '-') - ++begin; - if (negative && begin < end && given.charAt(begin) == '!') - ++begin; - return expect.length() == end - begin && expect.regionMatches(0, given, begin, end - begin); + int begin = 0, end = given.length(); + if (begin < end && given.charAt(begin) == '-') + ++begin; + if (negative && begin < end && given.charAt(begin) == '!') + ++begin; + return expect.length() == end - begin && expect.regionMatches(0, given, begin, end - begin); } /** - * Return the number of arguments associated with the specified option. - * The return value includes the actual option. - * Will return 0 if the option is not supported. + * Return the number of arguments associated with the specified option. The + * return value includes the actual option. Will return 0 if the option is not + * supported. */ public static int optionLength(String option) { - if(matchOption(option, "qualify", true) || - matchOption(option, "qualifyGenerics", true) || - matchOption(option, "hideGenerics", true) || - matchOption(option, "horizontal", true) || - matchOption(option, "all") || - matchOption(option, "attributes", true) || - matchOption(option, "enumconstants", true) || - matchOption(option, "operations", true) || - matchOption(option, "enumerations", true) || - matchOption(option, "constructors", true) || - matchOption(option, "visibility", true) || - matchOption(option, "types", true) || - matchOption(option, "autosize", true) || - matchOption(option, "commentname", true) || - matchOption(option, "nodefontabstractitalic", true) || - matchOption(option, "postfixpackage", true) || - matchOption(option, "noguillemot", true) || - matchOption(option, "views", true) || - matchOption(option, "inferrel", true) || - matchOption(option, "useimports", true) || - matchOption(option, "collapsible", true) || - matchOption(option, "inferdep", true) || - matchOption(option, "inferdepinpackage", true) || - matchOption(option, "hideprivateinner", true) || - matchOption(option, "compact", true)) + if (matchOption(option, "qualify", true) || matchOption(option, "qualifyGenerics", true) + || matchOption(option, "hideGenerics", true) || matchOption(option, "horizontal", true) + || matchOption(option, "all") || matchOption(option, "attributes", true) + || matchOption(option, "enumconstants", true) || matchOption(option, "operations", true) + || matchOption(option, "enumerations", true) || matchOption(option, "constructors", true) + || matchOption(option, "visibility", true) || matchOption(option, "types", true) + || matchOption(option, "autosize", true) || matchOption(option, "commentname", true) + || matchOption(option, "nodefontabstractitalic", true) || matchOption(option, "postfixpackage", true) + || matchOption(option, "noguillemot", true) || matchOption(option, "views", true) + || matchOption(option, "inferrel", true) || matchOption(option, "useimports", true) + || matchOption(option, "collapsible", true) || matchOption(option, "inferdep", true) + || matchOption(option, "inferdepinpackage", true) || matchOption(option, "hideprivateinner", true) + || matchOption(option, "compact", true)) return 1; - else if(matchOption(option, "nodefillcolor") || - matchOption(option, "nodefontcolor") || - matchOption(option, "nodefontsize") || - matchOption(option, "nodefontname") || - matchOption(option, "nodefontclasssize") || - matchOption(option, "nodefontclassname") || - matchOption(option, "nodefonttagsize") || - matchOption(option, "nodefonttagname") || - matchOption(option, "nodefontpackagesize") || - matchOption(option, "nodefontpackagename") || - matchOption(option, "edgefontcolor") || - matchOption(option, "edgecolor") || - matchOption(option, "edgefontsize") || - matchOption(option, "edgefontname") || - matchOption(option, "shape") || - matchOption(option, "output") || - matchOption(option, "outputencoding") || - matchOption(option, "bgcolor") || - matchOption(option, "hide") || - matchOption(option, "include") || - matchOption(option, "apidocroot") || - matchOption(option, "apidocmap") || - matchOption(option, "d") || - matchOption(option, "view") || - matchOption(option, "inferreltype") || - matchOption(option, "inferdepvis") || - matchOption(option, "collpackages") || - matchOption(option, "nodesep") || - matchOption(option, "ranksep") || - matchOption(option, "dotexecutable") || - matchOption(option, "link")) - return 2; - else if(matchOption(option, "contextPattern") || - matchOption(option, "linkoffline")) + else if (matchOption(option, "nodefillcolor") || matchOption(option, "nodefontcolor") + || matchOption(option, "nodefontsize") || matchOption(option, "nodefontname") + || matchOption(option, "nodefontclasssize") || matchOption(option, "nodefontclassname") + || matchOption(option, "nodefonttagsize") || matchOption(option, "nodefonttagname") + || matchOption(option, "nodefontpackagesize") || matchOption(option, "nodefontpackagename") + || matchOption(option, "edgefontcolor") || matchOption(option, "edgecolor") + || matchOption(option, "edgefontsize") || matchOption(option, "edgefontname") + || matchOption(option, "shape") || matchOption(option, "output") + || matchOption(option, "outputencoding") || matchOption(option, "bgcolor") + || matchOption(option, "hide") || matchOption(option, "include") || matchOption(option, "apidocroot") + || matchOption(option, "apidocmap") || matchOption(option, "d") || matchOption(option, "view") + || matchOption(option, "inferreltype") || matchOption(option, "inferdepvis") + || matchOption(option, "collpackages") || matchOption(option, "nodesep") + || matchOption(option, "ranksep") || matchOption(option, "dotexecutable") + || matchOption(option, "link")) + return 2; + else if (matchOption(option, "contextPattern") || matchOption(option, "linkoffline")) return 3; else return 0; } - + /** Set the options based on a single option and its arguments */ void setOption(String[] opt) { - if(!matchOption(opt[0], "hide") && optionLength(opt[0]) > opt.length) { - System.err.println("Skipping option '" + opt[0] + "', missing argument"); - return; - } - boolean dash = opt[0].length() > 1 && opt[0].charAt(0) == '-'; - boolean positive = !(opt[0].length() > 1 && opt[0].charAt(dash ? 1 : 0) == '!'); - - if(matchOption(opt[0], "qualify", true)) { - showQualified = positive; - } else if(matchOption(opt[0], "qualifyGenerics", true)) { - showQualifiedGenerics = positive; - } else if(matchOption(opt[0], "hideGenerics", true)) { - hideGenerics = positive; - } else if(matchOption(opt[0], "horizontal", true)) { - horizontal = positive; - } else if(matchOption(opt[0], "attributes", true)) { - showAttributes = positive; - } else if(matchOption(opt[0], "enumconstants", true)) { - showEnumConstants = positive; - } else if(matchOption(opt[0], "operations", true)) { - showOperations = positive; - } else if(matchOption(opt[0], "enumerations", true)) { - showEnumerations = positive; - } else if(matchOption(opt[0], "constructors", true)) { - showConstructors = positive; - } else if(matchOption(opt[0], "visibility", true)) { - showVisibility = positive; - } else if(matchOption(opt[0], "types", true)) { - showType = positive; - } else if(matchOption(opt[0], "autoSize", true)) { - autoSize = positive; - } else if(matchOption(opt[0], "commentname", true)) { - showComment = positive; - } else if(matchOption(opt[0], "all")) { - setAll(); - } else if(matchOption(opt[0], "bgcolor", true)) { - bgColor = positive ? opt[1] : null; - } else if(matchOption(opt[0], "edgecolor", true)) { - edgeColor = positive ? opt[1] : "black"; - } else if(matchOption(opt[0], "edgefontcolor", true)) { - edgeFontColor = positive ? opt[1] : "black"; - } else if(matchOption(opt[0], "edgefontname", true)) { - edgeFontName = positive ? opt[1] : Font.DEFAULT_FONT; - } else if(matchOption(opt[0], "edgefontsize", true)) { - edgeFontSize = positive ? Double.parseDouble(opt[1]) : 10; - } else if(matchOption(opt[0], "nodefontcolor", true)) { - nodeFontColor = positive ? opt[1] : "black"; - } else if(matchOption(opt[0], "nodefontname", true)) { - nodeFontName = positive ? opt[1] : Font.DEFAULT_FONT; - } else if(matchOption(opt[0], "nodefontabstractitalic", true)) { - nodeFontAbstractItalic = positive; - } else if(matchOption(opt[0], "nodefontsize", true)) { - nodeFontSize = positive ? Double.parseDouble(opt[1]) : 10; - } else if(matchOption(opt[0], "nodefontclassname", true)) { - nodeFontClassName = positive ? opt[1] : null; - } else if(matchOption(opt[0], "nodefontclasssize", true)) { - nodeFontClassSize = positive ? Double.parseDouble(opt[1]) : -1; - } else if(matchOption(opt[0], "nodefonttagname", true)) { - nodeFontTagName = positive ? opt[1] : null; - } else if(matchOption(opt[0], "nodefonttagsize", true)) { - nodeFontTagSize = positive ? Double.parseDouble(opt[1]) : -1; - } else if(matchOption(opt[0], "nodefontpackagename", true)) { - nodeFontPackageName = positive ? opt[1] : null; - } else if(matchOption(opt[0], "nodefontpackagesize", true)) { - nodeFontPackageSize = positive ? Double.parseDouble(opt[1]) : -1; - } else if(matchOption(opt[0], "nodefillcolor", true)) { - nodeFillColor = positive ? opt[1] : null; - } else if(matchOption(opt[0], "shape", true)) { - shape = positive ? Shape.of(opt[1]) : Shape.CLASS; - } else if(matchOption(opt[0], "output", true)) { - outputFileName = positive ? opt[1] : "graph.dot"; - } else if(matchOption(opt[0], "outputencoding", true)) { - outputEncoding = positive ? opt[1] : "ISO-8859-1"; - } else if(matchOption(opt[0], "hide", true)) { - if (positive) { - if (opt.length == 1) { - hidePatterns.clear(); - hidePatterns.add(allPattern); - } else { - try { - hidePatterns.add(Pattern.compile(opt[1])); - } catch (PatternSyntaxException e) { - System.err.println("Skipping invalid pattern " + opt[1]); - } - } - } else - hidePatterns.clear(); - } else if(matchOption(opt[0], "include", true)) { - if (positive) { - try { - includePatterns.add(Pattern.compile(opt[1])); - } catch (PatternSyntaxException e) { - System.err.println("Skipping invalid pattern " + opt[1]); - } - } else - includePatterns.clear(); - } else if(matchOption(opt[0], "apidocroot", true)) { - apiDocRoot = positive ? fixApiDocRoot(opt[1]) : null; - } else if(matchOption(opt[0], "apidocmap", true)) { - if (positive) - setApiDocMapFile(opt[1]); - else - apiDocMap.clear(); - } else if(matchOption(opt[0], "noguillemot", true)) { - guilOpen = positive ? "<<" : "\u00ab"; - guilClose = positive ? ">>" : "\u00bb"; - } else if (matchOption(opt[0], "view", true)) { - viewName = positive ? opt[1] : null; - } else if (matchOption(opt[0], "views", true)) { - findViews = positive; - } else if (matchOption(opt[0], "d", true)) { - outputDirectory = positive ? opt[1] : null; - } else if(matchOption(opt[0], "inferrel", true)) { - inferRelationships = positive; - } else if(matchOption(opt[0], "inferreltype", true)) { - if (positive) { - try { - inferRelationshipType = RelationType.valueOf(opt[1].toUpperCase()); - } catch(IllegalArgumentException e) { - System.err.println("Unknown association type " + opt[1]); - } - } else - inferRelationshipType = RelationType.NAVASSOC; - } else if(matchOption(opt[0], "inferdepvis", true)) { - if (positive) { - try { - Visibility vis = Visibility.valueOf(opt[1].toUpperCase()); - inferDependencyVisibility = vis; - } catch(IllegalArgumentException e) { - System.err.println("Ignoring invalid visibility specification for " + - "dependency inference: " + opt[1]); - } - } else - inferDependencyVisibility = Visibility.PRIVATE; - } else if(matchOption(opt[0], "collapsible", true)) { - collapsibleDiagrams = positive; - } else if(matchOption(opt[0], "inferdep", true)) { - inferDependencies = positive; - } else if(matchOption(opt[0], "inferdepinpackage", true)) { - inferDepInPackage = positive; - } else if (matchOption(opt[0], "hideprivateinner", true)) { - hidePrivateInner = positive; - } else if(matchOption(opt[0], "useimports", true)) { - useImports = positive; - } else if (matchOption(opt[0], "collpackages", true)) { - if (positive) { - try { - collPackages.add(Pattern.compile(opt[1])); - } catch (PatternSyntaxException e) { - System.err.println("Skipping invalid pattern " + opt[1]); - } - } else - collPackages.clear(); - } else if (matchOption(opt[0], "compact", true)) { - compact = positive; - } else if (matchOption(opt[0], "postfixpackage", true)) { - postfixPackage = positive; - } else if (matchOption(opt[0], "link")) { - addApiDocRoots(opt[1]); - } else if (matchOption(opt[0], "linkoffline")) { - addApiDocRootsOffline(opt[1], opt[2]); - } else if(matchOption(opt[0], "contextPattern")) { - RelationDirection d; RelationType rt; - try { - d = RelationDirection.valueOf(opt[2].toUpperCase()); - if(opt[1].equalsIgnoreCase("all")) { - contextRelationPattern = new RelationPattern(d); - } else { - rt = RelationType.valueOf(opt[1].toUpperCase()); - contextRelationPattern.addRelation(rt, d); - } - } catch(IllegalArgumentException e) { - - } - - } else if (matchOption(opt[0], "nodesep", true)) { - try { - nodeSep = positive ? Double.parseDouble(opt[1]) : 0.25; - } catch (NumberFormatException e) { - System.err.println("Skipping invalid nodesep " + opt[1]); - } - } else if (matchOption(opt[0], "ranksep", true)) { - try { - rankSep = positive ? Double.parseDouble(opt[1]) : 0.5; - } catch (NumberFormatException e) { - System.err.println("Skipping invalid ranksep " + opt[1]); - } - } else if (matchOption(opt[0], "dotexecutable")) { - dotExecutable = opt[1]; - } else - ; // Do nothing, javadoc will handle the option or complain, if needed. + if (!matchOption(opt[0], "hide") && optionLength(opt[0]) > opt.length) { + System.err.println("Skipping option '" + opt[0] + "', missing argument"); + return; + } + boolean dash = opt[0].length() > 1 && opt[0].charAt(0) == '-'; + boolean positive = !(opt[0].length() > 1 && opt[0].charAt(dash ? 1 : 0) == '!'); + + if (matchOption(opt[0], "qualify", true)) { + showQualified = positive; + } else if (matchOption(opt[0], "qualifyGenerics", true)) { + showQualifiedGenerics = positive; + } else if (matchOption(opt[0], "hideGenerics", true)) { + hideGenerics = positive; + } else if (matchOption(opt[0], "horizontal", true)) { + horizontal = positive; + } else if (matchOption(opt[0], "attributes", true)) { + showAttributes = positive; + } else if (matchOption(opt[0], "enumconstants", true)) { + showEnumConstants = positive; + } else if (matchOption(opt[0], "operations", true)) { + showOperations = positive; + } else if (matchOption(opt[0], "enumerations", true)) { + showEnumerations = positive; + } else if (matchOption(opt[0], "constructors", true)) { + showConstructors = positive; + } else if (matchOption(opt[0], "visibility", true)) { + showVisibility = positive; + } else if (matchOption(opt[0], "types", true)) { + showType = positive; + } else if (matchOption(opt[0], "autoSize", true)) { + autoSize = positive; + } else if (matchOption(opt[0], "commentname", true)) { + showComment = positive; + } else if (matchOption(opt[0], "all")) { + setAll(); + } else if (matchOption(opt[0], "bgcolor", true)) { + bgColor = positive ? opt[1] : null; + } else if (matchOption(opt[0], "edgecolor", true)) { + edgeColor = positive ? opt[1] : "black"; + } else if (matchOption(opt[0], "edgefontcolor", true)) { + edgeFontColor = positive ? opt[1] : "black"; + } else if (matchOption(opt[0], "edgefontname", true)) { + edgeFontName = positive ? opt[1] : Font.DEFAULT_FONT; + } else if (matchOption(opt[0], "edgefontsize", true)) { + edgeFontSize = positive ? Double.parseDouble(opt[1]) : 10; + } else if (matchOption(opt[0], "nodefontcolor", true)) { + nodeFontColor = positive ? opt[1] : "black"; + } else if (matchOption(opt[0], "nodefontname", true)) { + nodeFontName = positive ? opt[1] : Font.DEFAULT_FONT; + } else if (matchOption(opt[0], "nodefontabstractitalic", true)) { + nodeFontAbstractItalic = positive; + } else if (matchOption(opt[0], "nodefontsize", true)) { + nodeFontSize = positive ? Double.parseDouble(opt[1]) : 10; + } else if (matchOption(opt[0], "nodefontclassname", true)) { + nodeFontClassName = positive ? opt[1] : null; + } else if (matchOption(opt[0], "nodefontclasssize", true)) { + nodeFontClassSize = positive ? Double.parseDouble(opt[1]) : -1; + } else if (matchOption(opt[0], "nodefonttagname", true)) { + nodeFontTagName = positive ? opt[1] : null; + } else if (matchOption(opt[0], "nodefonttagsize", true)) { + nodeFontTagSize = positive ? Double.parseDouble(opt[1]) : -1; + } else if (matchOption(opt[0], "nodefontpackagename", true)) { + nodeFontPackageName = positive ? opt[1] : null; + } else if (matchOption(opt[0], "nodefontpackagesize", true)) { + nodeFontPackageSize = positive ? Double.parseDouble(opt[1]) : -1; + } else if (matchOption(opt[0], "nodefillcolor", true)) { + nodeFillColor = positive ? opt[1] : null; + } else if (matchOption(opt[0], "shape", true)) { + shape = positive ? Shape.of(opt[1]) : Shape.CLASS; + } else if (matchOption(opt[0], "output", true)) { + outputFileName = positive ? opt[1] : "graph.dot"; + } else if (matchOption(opt[0], "outputencoding", true)) { + outputEncoding = positive ? opt[1] : "ISO-8859-1"; + } else if (matchOption(opt[0], "hide", true)) { + if (positive) { + if (opt.length == 1) { + hidePatterns.clear(); + hidePatterns.add(allPattern); + } else { + try { + hidePatterns.add(Pattern.compile(opt[1])); + } catch (PatternSyntaxException e) { + System.err.println("Skipping invalid pattern " + opt[1]); + } + } + } else + hidePatterns.clear(); + } else if (matchOption(opt[0], "include", true)) { + if (positive) { + try { + includePatterns.add(Pattern.compile(opt[1])); + } catch (PatternSyntaxException e) { + System.err.println("Skipping invalid pattern " + opt[1]); + } + } else + includePatterns.clear(); + } else if (matchOption(opt[0], "apidocroot", true)) { + apiDocRoot = positive ? fixApiDocRoot(opt[1]) : null; + } else if (matchOption(opt[0], "apidocmap", true)) { + if (positive) + setApiDocMapFile(opt[1]); + else + apiDocMap.clear(); + } else if (matchOption(opt[0], "noguillemot", true)) { + guilOpen = positive ? "<<" : "\u00ab"; + guilClose = positive ? ">>" : "\u00bb"; + } else if (matchOption(opt[0], "view", true)) { + viewName = positive ? opt[1] : null; + } else if (matchOption(opt[0], "views", true)) { + findViews = positive; + } else if (matchOption(opt[0], "d", true)) { + outputDirectory = positive ? opt[1] : null; + } else if (matchOption(opt[0], "inferrel", true)) { + inferRelationships = positive; + } else if (matchOption(opt[0], "inferreltype", true)) { + if (positive) { + try { + inferRelationshipType = RelationType.valueOf(opt[1].toUpperCase()); + } catch (IllegalArgumentException e) { + System.err.println("Unknown association type " + opt[1]); + } + } else + inferRelationshipType = RelationType.NAVASSOC; + } else if (matchOption(opt[0], "inferdepvis", true)) { + if (positive) { + try { + Visibility vis = Visibility.valueOf(opt[1].toUpperCase()); + inferDependencyVisibility = vis; + } catch (IllegalArgumentException e) { + System.err.println( + "Ignoring invalid visibility specification for " + "dependency inference: " + opt[1]); + } + } else + inferDependencyVisibility = Visibility.PRIVATE; + } else if (matchOption(opt[0], "collapsible", true)) { + collapsibleDiagrams = positive; + } else if (matchOption(opt[0], "inferdep", true)) { + inferDependencies = positive; + } else if (matchOption(opt[0], "inferdepinpackage", true)) { + inferDepInPackage = positive; + } else if (matchOption(opt[0], "hideprivateinner", true)) { + hidePrivateInner = positive; + } else if (matchOption(opt[0], "useimports", true)) { + useImports = positive; + } else if (matchOption(opt[0], "collpackages", true)) { + if (positive) { + try { + collPackages.add(Pattern.compile(opt[1])); + } catch (PatternSyntaxException e) { + System.err.println("Skipping invalid pattern " + opt[1]); + } + } else + collPackages.clear(); + } else if (matchOption(opt[0], "compact", true)) { + compact = positive; + } else if (matchOption(opt[0], "postfixpackage", true)) { + postfixPackage = positive; + } else if (matchOption(opt[0], "link")) { + addApiDocRoots(opt[1]); + } else if (matchOption(opt[0], "linkoffline")) { + addApiDocRootsOffline(opt[1], opt[2]); + } else if (matchOption(opt[0], "contextPattern")) { + RelationDirection d; + RelationType rt; + try { + d = RelationDirection.valueOf(opt[2].toUpperCase()); + if (opt[1].equalsIgnoreCase("all")) { + contextRelationPattern = new RelationPattern(d); + } else { + rt = RelationType.valueOf(opt[1].toUpperCase()); + contextRelationPattern.addRelation(rt, d); + } + } catch (IllegalArgumentException e) { + + } + + } else if (matchOption(opt[0], "nodesep", true)) { + try { + nodeSep = positive ? Double.parseDouble(opt[1]) : 0.25; + } catch (NumberFormatException e) { + System.err.println("Skipping invalid nodesep " + opt[1]); + } + } else if (matchOption(opt[0], "ranksep", true)) { + try { + rankSep = positive ? Double.parseDouble(opt[1]) : 0.5; + } catch (NumberFormatException e) { + System.err.println("Skipping invalid ranksep " + opt[1]); + } + } else if (matchOption(opt[0], "dotexecutable")) { + dotExecutable = opt[1]; + } else + ; // Do nothing, javadoc will handle the option or complain, if needed. } /** - * Adds api doc roots from a link. The folder reffered by the link should contain a package-list - * file that will be parsed in order to add api doc roots to this configuration + * Adds api doc roots from a link. The folder reffered by the link should + * contain a package-list file that will be parsed in order to add api doc roots + * to this configuration + * * @param packageListUrl */ private void addApiDocRoots(String packageListUrl) { - BufferedReader br = null; - packageListUrl = fixApiDocRoot(packageListUrl); - try { - URL url = new URL(packageListUrl + "/package-list"); - br = new BufferedReader(new InputStreamReader(url.openStream())); - String line; - while((line = br.readLine()) != null) { - line = line + "."; - Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*"); - apiDocMap.put(pattern, packageListUrl); - } - } catch(IOException e) { - System.err.println("Errors happened while accessing the package-list file at " - + packageListUrl); - } finally { - if(br != null) - try { - br.close(); - } catch (IOException e) {} - } - + BufferedReader br = null; + packageListUrl = fixApiDocRoot(packageListUrl); + try { + URL url = new URL(packageListUrl + "/package-list"); + br = new BufferedReader(new InputStreamReader(url.openStream())); + String line; + while ((line = br.readLine()) != null) { + line = line + "."; + Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*"); + apiDocMap.put(pattern, packageListUrl); + } + } catch (IOException e) { + System.err.println("Errors happened while accessing the package-list file at " + packageListUrl); + } finally { + if (br != null) + try { + br.close(); + } catch (IOException e) { + } + } + } /** - * Adds api doc roots from an offline link. - * The folder specified by packageListUrl should contain the package-list associed with the docUrl folder. - * @param docUrl folder containing the javadoc + * Adds api doc roots from an offline link. The folder specified by + * packageListUrl should contain the package-list associed with the docUrl + * folder. + * + * @param docUrl folder containing the javadoc * @param packageListUrl folder containing the package-list */ private void addApiDocRootsOffline(String docUrl, String packageListUrl) { - BufferedReader br = null; - packageListUrl = fixApiDocRoot(packageListUrl); - try { - URL url = new URL(packageListUrl + "/package-list"); - br = new BufferedReader(new InputStreamReader(url.openStream())); - String line; - while((line = br.readLine()) != null) { - line = line + "."; - Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*"); - apiDocMap.put(pattern, fixApiDocRoot(docUrl)); - } - } catch(IOException e) { - System.err.println("Unable to access the package-list file at " + packageListUrl); - } finally { - if (br != null) - try { - br.close(); - } catch (IOException e) {} - } + BufferedReader br = null; + packageListUrl = fixApiDocRoot(packageListUrl); + try { + URL url = new URL(packageListUrl + "/package-list"); + br = new BufferedReader(new InputStreamReader(url.openStream())); + String line; + while ((line = br.readLine()) != null) { + line = line + "."; + Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*"); + apiDocMap.put(pattern, fixApiDocRoot(docUrl)); + } + } catch (IOException e) { + System.err.println("Unable to access the package-list file at " + packageListUrl); + } finally { + if (br != null) + try { + br.close(); + } catch (IOException e) { + } + } } /** - * Loads the property file referred by apiDocMapFileName and fills the apiDocMap - * accordingly + * Loads the property file referred by apiDocMapFileName and fills + * the apiDocMap accordingly + * * @param apiDocMapFileName */ void setApiDocMapFile(String apiDocMapFileName) { - try { - InputStream is = new FileInputStream(apiDocMapFileName); - Properties userMap = new Properties(); - userMap.load(is); - for (Map.Entry mapEntry : userMap.entrySet()) { - try { - String thisRoot = (String) mapEntry.getValue(); - if (thisRoot != null) { - thisRoot = fixApiDocRoot(thisRoot); - apiDocMap.put(Pattern.compile((String) mapEntry.getKey()), thisRoot); - } else { - System.err.println("No URL for pattern " + mapEntry.getKey()); - } - } catch (PatternSyntaxException e) { - System.err.println("Skipping bad pattern " + mapEntry.getKey()); - } - } - } catch (FileNotFoundException e) { - System.err.println("File " + apiDocMapFileName + " was not found: " + e); - } catch (IOException e) { - System.err.println("Error reading the property api map file " + apiDocMapFileName - + ": " + e); - } + try { + InputStream is = new FileInputStream(apiDocMapFileName); + Properties userMap = new Properties(); + userMap.load(is); + for (Map.Entry mapEntry : userMap.entrySet()) { + try { + String thisRoot = (String) mapEntry.getValue(); + if (thisRoot != null) { + thisRoot = fixApiDocRoot(thisRoot); + apiDocMap.put(Pattern.compile((String) mapEntry.getKey()), thisRoot); + } else { + System.err.println("No URL for pattern " + mapEntry.getKey()); + } + } catch (PatternSyntaxException e) { + System.err.println("Skipping bad pattern " + mapEntry.getKey()); + } + } + } catch (FileNotFoundException e) { + System.err.println("File " + apiDocMapFileName + " was not found: " + e); + } catch (IOException e) { + System.err.println("Error reading the property api map file " + apiDocMapFileName + ": " + e); + } } - + /** - * Returns the appropriate URL "root" for an external class name. It will - * match the class name against the regular expressions specified in the - * apiDocMap; if a match is found, the associated URL - * will be returned. + * Returns the appropriate URL "root" for an external class name. It will match + * the class name against the regular expressions specified in the + * apiDocMap; if a match is found, the associated URL will be + * returned. *

- * NOTE: The match order of the match attempts is the one specified by the - * constructor of the api doc root, so it depends on the order of "-link" and "-apiDocMap" - * parameters. + * NOTE: The match order of the match attempts is the one specified by + * the constructor of the api doc root, so it depends on the order of "-link" + * and "-apiDocMap" parameters. */ public String getApiDocRoot(String className) { - if(apiDocMap.isEmpty()) - apiDocMap.put(Pattern.compile(".*"), DEFAULT_EXTERNAL_APIDOC); - - for (Map.Entry mapEntry : apiDocMap.entrySet()) { - if (mapEntry.getKey().matcher(className).matches()) - return mapEntry.getValue(); - } - return null; + if (apiDocMap.isEmpty()) + apiDocMap.put(Pattern.compile(".*"), DEFAULT_EXTERNAL_APIDOC); + + for (Map.Entry mapEntry : apiDocMap.entrySet()) { + if (mapEntry.getKey().matcher(className).matches()) + return mapEntry.getValue(); + } + return null; } - + /** Trim and append a file separator to the string */ private String fixApiDocRoot(String str) { - if (str == null) - return null; - String fixed = str.trim(); - if (fixed.isEmpty()) - return ""; - if (File.separatorChar != '/') - fixed = fixed.replace(File.separatorChar, '/'); - if (!fixed.endsWith("/")) - fixed = fixed + "/"; - return fixed; + if (str == null) + return null; + String fixed = str.trim(); + if (fixed.isEmpty()) + return ""; + if (File.separatorChar != '/') + fixed = fixed.replace(File.separatorChar, '/'); + if (!fixed.endsWith("/")) + fixed = fixed + "/"; + return fixed; } /** Set the options based on the command line parameters */ public void setOptions(String[][] options) { - for (String s[] : options) - setOption(s); + for (String s[] : options) + setOption(s); } - /** Set the options based on the tag elements of the ClassDoc parameter */ public void setOptions(Doc p) { - if (p == null) - return; + if (p == null) + return; - for (Tag tag : p.tags("opt")) - setOption(StringUtil.tokenize(tag.text())); + for (Tag tag : p.tags("opt")) + setOption(StringUtil.tokenize(tag.text())); } /** - * Check if the supplied string matches an entity specified - * with the -hide parameter. + * Check if the supplied string matches an entity specified with the -hide + * parameter. + * * @return true if the string matches. */ public boolean matchesHideExpression(String s) { - for (Pattern hidePattern : hidePatterns) { - // micro-optimization because the "all pattern" is heavily used in UmlGraphDoc - if(hidePattern == allPattern) - return true; - - Matcher m = hidePattern.matcher(s); - if (strictMatching ? m.matches() : m.find()) - return true; - } - return false; + for (Pattern hidePattern : hidePatterns) { + // micro-optimization because the "all pattern" is heavily used in UmlGraphDoc + if (hidePattern == allPattern) + return true; + + Matcher m = hidePattern.matcher(s); + if (strictMatching ? m.matches() : m.find()) + return true; + } + return false; } - + /** - * Check if the supplied string matches an entity specified - * with the -include parameter. + * Check if the supplied string matches an entity specified with the -include + * parameter. + * * @return true if the string matches. */ public boolean matchesIncludeExpression(String s) { - for (Pattern includePattern : includePatterns) { - Matcher m = includePattern.matcher(s); - if (strictMatching ? m.matches() : m.find()) - return true; - } - return false; + for (Pattern includePattern : includePatterns) { + Matcher m = includePattern.matcher(s); + if (strictMatching ? m.matches() : m.find()) + return true; + } + return false; } /** - * Check if the supplied string matches an entity specified - * with the -collpackages parameter. + * Check if the supplied string matches an entity specified with the + * -collpackages parameter. + * * @return true if the string matches. */ public boolean matchesCollPackageExpression(String s) { - for (Pattern collPattern : collPackages) { - Matcher m = collPattern.matcher(s); - if (strictMatching ? m.matches() : m.find()) - return true; - } - return false; + for (Pattern collPattern : collPackages) { + Matcher m = collPattern.matcher(s); + if (strictMatching ? m.matches() : m.find()) + return true; + } + return false; } - - // ---------------------------------------------------------------- + + // ---------------------------------------------------------------- // OptionProvider methods - // ---------------------------------------------------------------- - + // ---------------------------------------------------------------- + public Options getOptionsFor(ClassDoc cd) { - Options localOpt = getGlobalOptions(); - localOpt.setOptions(cd); - return localOpt; + Options localOpt = getGlobalOptions(); + localOpt.setOptions(cd); + return localOpt; } public Options getOptionsFor(String name) { - return getGlobalOptions(); + return getGlobalOptions(); } public Options getGlobalOptions() { - return (Options) clone(); + return (Options) clone(); } public void overrideForClass(Options opt, ClassDoc cd) { - // nothing to do + // nothing to do } public void overrideForClass(Options opt, String className) { - // nothing to do + // nothing to do } public String getDisplayName() { - return "general class diagram"; + return "general class diagram"; } - + public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("UMLGRAPH OPTIONS\n"); - for(Field f : this.getClass().getDeclaredFields()) { - if(!Modifier.isStatic(f.getModifiers())) { - f.setAccessible(true); - try { - sb.append(f.getName() + ":" + f.get(this) + "\n"); - } catch (Exception e) { - } - } - } - return sb.toString(); + StringBuilder sb = new StringBuilder(); + sb.append("UMLGRAPH OPTIONS\n"); + for (Field f : this.getClass().getDeclaredFields()) { + if (!Modifier.isStatic(f.getModifiers())) { + f.setAccessible(true); + try { + sb.append(f.getName() + ":" + f.get(this) + "\n"); + } catch (Exception e) { + } + } + } + return sb.toString(); } } diff --git a/src/main/java/org/umlgraph/doclet/PackageMatcher.java b/src/main/java/org/umlgraph/doclet/PackageMatcher.java index 99bd7ff..ecc3002 100644 --- a/src/main/java/org/umlgraph/doclet/PackageMatcher.java +++ b/src/main/java/org/umlgraph/doclet/PackageMatcher.java @@ -7,19 +7,19 @@ public class PackageMatcher implements ClassMatcher { protected PackageDoc packageDoc; public PackageMatcher(PackageDoc packageDoc) { - super(); - this.packageDoc = packageDoc; + super(); + this.packageDoc = packageDoc; } public boolean matches(ClassDoc cd) { - return cd.containingPackage().equals(packageDoc); + return cd.containingPackage().equals(packageDoc); } public boolean matches(String name) { - for (ClassDoc cd : packageDoc.allClasses()) - if (cd.qualifiedName().equals(name)) - return true; - return false; + for (ClassDoc cd : packageDoc.allClasses()) + if (cd.qualifiedName().equals(name)) + return true; + return false; } } diff --git a/src/main/java/org/umlgraph/doclet/PackageView.java b/src/main/java/org/umlgraph/doclet/PackageView.java index c66bf6d..83af937 100755 --- a/src/main/java/org/umlgraph/doclet/PackageView.java +++ b/src/main/java/org/umlgraph/doclet/PackageView.java @@ -12,6 +12,7 @@ import com.sun.javadoc.RootDoc; * single {@linkplain ClassMatcher}, and provides some extra configuration such * as output path configuration (and it is specified in code rather than in * javadoc comments). + * * @author wolf * */ @@ -25,57 +26,57 @@ public class PackageView implements OptionProvider { private Options opt; public PackageView(String outputFolder, PackageDoc pd, RootDoc root, OptionProvider parent) { - this.parent = parent; - this.pd = pd; - this.matcher = new PackageMatcher(pd); - this.opt = parent.getGlobalOptions(); - this.opt.setOptions(pd); - this.outputPath = pd.name().replace('.', '/') + "/" + pd.name() + ".dot"; + this.parent = parent; + this.pd = pd; + this.matcher = new PackageMatcher(pd); + this.opt = parent.getGlobalOptions(); + this.opt.setOptions(pd); + this.outputPath = pd.name().replace('.', '/') + "/" + pd.name() + ".dot"; } public String getDisplayName() { - return "Package view for package " + pd; + return "Package view for package " + pd; } public Options getGlobalOptions() { - Options go = parent.getGlobalOptions(); + Options go = parent.getGlobalOptions(); - go.setOption(new String[] { "output", outputPath }); - go.setOption(HIDE); + go.setOption(new String[] { "output", outputPath }); + go.setOption(HIDE); - return go; + return go; } public Options getOptionsFor(ClassDoc cd) { - Options go = parent.getGlobalOptions(); - overrideForClass(go, cd); - return go; + Options go = parent.getGlobalOptions(); + overrideForClass(go, cd); + return go; } public Options getOptionsFor(String name) { - Options go = parent.getGlobalOptions(); - overrideForClass(go, name); - return go; + Options go = parent.getGlobalOptions(); + overrideForClass(go, name); + return go; } public void overrideForClass(Options opt, ClassDoc cd) { - opt.setOptions(cd); - boolean inPackage = matcher.matches(cd); - if (inPackage) - opt.showQualified = false; - boolean included = inPackage || this.opt.matchesIncludeExpression(cd.qualifiedName()); - if (!included || this.opt.matchesHideExpression(cd.qualifiedName())) - opt.setOption(HIDE); + opt.setOptions(cd); + boolean inPackage = matcher.matches(cd); + if (inPackage) + opt.showQualified = false; + boolean included = inPackage || this.opt.matchesIncludeExpression(cd.qualifiedName()); + if (!included || this.opt.matchesHideExpression(cd.qualifiedName())) + opt.setOption(HIDE); } public void overrideForClass(Options opt, String className) { - opt.showQualified = false; - boolean inPackage = matcher.matches(className); - if (inPackage) - opt.showQualified = false; - boolean included = inPackage || this.opt.matchesIncludeExpression(className); - if (!included || this.opt.matchesHideExpression(className)) - opt.setOption(HIDE); + opt.showQualified = false; + boolean inPackage = matcher.matches(className); + if (inPackage) + opt.showQualified = false; + boolean included = inPackage || this.opt.matchesIncludeExpression(className); + if (!included || this.opt.matchesHideExpression(className)) + opt.setOption(HIDE); } } diff --git a/src/main/java/org/umlgraph/doclet/PatternMatcher.java b/src/main/java/org/umlgraph/doclet/PatternMatcher.java index 78a9135..a809db6 100644 --- a/src/main/java/org/umlgraph/doclet/PatternMatcher.java +++ b/src/main/java/org/umlgraph/doclet/PatternMatcher.java @@ -23,21 +23,22 @@ import com.sun.javadoc.ClassDoc; /** * Matches classes performing a regular expression match on the qualified class * name + * * @author wolf */ public class PatternMatcher implements ClassMatcher { Pattern pattern; public PatternMatcher(Pattern pattern) { - this.pattern = pattern; + this.pattern = pattern; } public boolean matches(ClassDoc cd) { - return matches(cd.toString()); + return matches(cd.toString()); } public boolean matches(String name) { - return pattern.matcher(name).matches(); + return pattern.matcher(name).matches(); } } diff --git a/src/main/java/org/umlgraph/doclet/RelationDirection.java b/src/main/java/org/umlgraph/doclet/RelationDirection.java index 4d3595c..dca8b4b 100644 --- a/src/main/java/org/umlgraph/doclet/RelationDirection.java +++ b/src/main/java/org/umlgraph/doclet/RelationDirection.java @@ -9,32 +9,35 @@ public enum RelationDirection { /** * Adds the current direction + * * @param d * @return */ public RelationDirection sum(RelationDirection d) { - // Handle same and nones first: - return (this == d || d == NONE) ? this : // - this == NONE ? d : BOTH; // They are different and not none. + // Handle same and nones first: + return (this == d || d == NONE) ? this : // + this == NONE ? d : BOTH; // They are different and not none. } /** - * Returns true if this direction "contains" the specified one, that is, - * either it's equal to it, or this direction is {@link #BOTH} + * Returns true if this direction "contains" the specified one, that is, either + * it's equal to it, or this direction is {@link #BOTH} + * * @param d * @return */ public boolean contains(RelationDirection d) { - return this == BOTH ? true : (d == this); + return this == BOTH ? true : (d == this); } /** - * Inverts the direction of the relation. Turns IN into OUT and vice-versa, NONE and BOTH - * are not changed + * Inverts the direction of the relation. Turns IN into OUT and vice-versa, NONE + * and BOTH are not changed + * * @return */ public RelationDirection inverse() { - return this == IN ? OUT : this == OUT ? IN : this; + return this == IN ? OUT : this == OUT ? IN : this; } }; diff --git a/src/main/java/org/umlgraph/doclet/RelationPattern.java b/src/main/java/org/umlgraph/doclet/RelationPattern.java index 4fa994e..db9b31e 100644 --- a/src/main/java/org/umlgraph/doclet/RelationPattern.java +++ b/src/main/java/org/umlgraph/doclet/RelationPattern.java @@ -2,6 +2,7 @@ package org.umlgraph.doclet; /** * A map from relation types to directions + * * @author wolf * */ @@ -13,38 +14,41 @@ public class RelationPattern { /** * Creates a new pattern using the same direction for every relation kind + * * @param defaultDirection The direction used to initialize this pattern */ public RelationPattern(RelationDirection defaultDirection) { - directions = new RelationDirection[RelationType.values().length]; - for (int i = 0; i < directions.length; i++) { - directions[i] = defaultDirection; - } + directions = new RelationDirection[RelationType.values().length]; + for (int i = 0; i < directions.length; i++) { + directions[i] = defaultDirection; + } } /** * Adds, eventually merging, a direction for the specified relation type + * * @param relationType * @param direction */ public void addRelation(RelationType relationType, RelationDirection direction) { - int idx = relationType.ordinal(); - directions[idx] = directions[idx].sum(direction); + int idx = relationType.ordinal(); + directions[idx] = directions[idx].sum(direction); } /** - * Returns true if this patterns matches at least the direction of one - * of the relations in the other relation patterns. Matching is defined - * by {@linkplain RelationDirection#contains(RelationDirection)} + * Returns true if this patterns matches at least the direction of one of the + * relations in the other relation patterns. Matching is defined by + * {@linkplain RelationDirection#contains(RelationDirection)} + * * @param relationPattern * @return */ public boolean matchesOne(RelationPattern relationPattern) { - for (int i = 0; i < directions.length; i++) { - if (directions[i].contains(relationPattern.directions[i])) - return true; - } - return false; + for (int i = 0; i < directions.length; i++) { + if (directions[i].contains(relationPattern.directions[i])) + return true; + } + return false; } } diff --git a/src/main/java/org/umlgraph/doclet/RelationType.java b/src/main/java/org/umlgraph/doclet/RelationType.java index 09f33a1..33b645d 100644 --- a/src/main/java/org/umlgraph/doclet/RelationType.java +++ b/src/main/java/org/umlgraph/doclet/RelationType.java @@ -27,8 +27,8 @@ public enum RelationType { /** Enum constructors must be private */ private RelationType(String style, boolean backorder) { - this.lower = toString().toLowerCase(); - this.style = style; - this.backorder = backorder; + this.lower = toString().toLowerCase(); + this.style = style; + this.backorder = backorder; } } diff --git a/src/main/java/org/umlgraph/doclet/Shape.java b/src/main/java/org/umlgraph/doclet/Shape.java index 54a8cdd..31df88c 100644 --- a/src/main/java/org/umlgraph/doclet/Shape.java +++ b/src/main/java/org/umlgraph/doclet/Shape.java @@ -45,10 +45,10 @@ public enum Shape { /** Initialize the lookup index */ static { - for (Shape s : Shape.values()) { - index.put(s.name(), s); - index.put(s.name().toLowerCase(Locale.ROOT), s); - } + for (Shape s : Shape.values()) { + index.put(s.name(), s); + index.put(s.name().toLowerCase(Locale.ROOT), s); + } } /** @@ -59,25 +59,25 @@ public enum Shape { * @return Shape */ public static Shape of(String s) { - Shape shp = index.get(s); - if (shp != null) - return shp; - System.err.println("Ignoring invalid shape: " + s); - return CLASS; + Shape shp = index.get(s); + if (shp != null) + return shp; + System.err.println("Ignoring invalid shape: " + s); + return CLASS; } /** Enum constructor, must be private! */ private Shape(String style) { - this.style = style; + this.style = style; } /** Return the table border required for the shape */ public String extraColumn() { - return this == Shape.ACTIVECLASS ? ("") : ""; + return this == Shape.ACTIVECLASS ? ("") : ""; } /** Return the cell border required for the shape */ public String cellBorder() { - return this == CLASS || this == ACTIVECLASS ? "1" : "0"; + return this == CLASS || this == ACTIVECLASS ? "1" : "0"; } } diff --git a/src/main/java/org/umlgraph/doclet/StringUtil.java b/src/main/java/org/umlgraph/doclet/StringUtil.java index e122e0b..1aee85b 100644 --- a/src/main/java/org/umlgraph/doclet/StringUtil.java +++ b/src/main/java/org/umlgraph/doclet/StringUtil.java @@ -31,37 +31,37 @@ import java.util.regex.Pattern; class StringUtil { /** Tokenize string s into an array */ public static String[] tokenize(String s) { - ArrayList r = new ArrayList(); - String remain = s; - int n = 0, pos; + ArrayList r = new ArrayList(); + String remain = s; + int n = 0, pos; - remain = remain.trim(); - while (remain.length() > 0) { - if (remain.startsWith("\"")) { - // Field in quotes - pos = remain.indexOf('"', 1); - if (pos == -1) - break; - r.add(remain.substring(1, pos)); - if (pos + 1 < remain.length()) - pos++; - } else { - // Space-separated field - pos = remain.indexOf(' ', 0); - if (pos == -1) { - r.add(remain); - remain = ""; - } else - r.add(remain.substring(0, pos)); - } - remain = remain.substring(pos + 1); - remain = remain.trim(); - // - is used as a placeholder for empy fields - if (r.get(n).equals("-")) - r.set(n, ""); - n++; - } - return r.toArray(new String[0]); + remain = remain.trim(); + while (remain.length() > 0) { + if (remain.startsWith("\"")) { + // Field in quotes + pos = remain.indexOf('"', 1); + if (pos == -1) + break; + r.add(remain.substring(1, pos)); + if (pos + 1 < remain.length()) + pos++; + } else { + // Space-separated field + pos = remain.indexOf(' ', 0); + if (pos == -1) { + r.add(remain); + remain = ""; + } else + r.add(remain.substring(0, pos)); + } + remain = remain.substring(pos + 1); + remain = remain.trim(); + // - is used as a placeholder for empy fields + if (r.get(n).equals("-")) + r.set(n, ""); + n++; + } + return r.toArray(new String[0]); } private final static Pattern ESCAPE_BASIC_XML = Pattern.compile("[&<>]"); @@ -71,47 +71,47 @@ class StringUtil { * HTML entity code. */ public static String escape(String s) { - if (ESCAPE_BASIC_XML.matcher(s).find()) { - StringBuilder sb = new StringBuilder(s); - for (int i = 0; i < sb.length();) { - switch (sb.charAt(i)) { - case '&': - sb.replace(i, i + 1, "&"); - i += "&".length(); - break; - case '<': - sb.replace(i, i + 1, "<"); - i += "<".length(); - break; - case '>': - sb.replace(i, i + 1, ">"); - i += ">".length(); - break; - default: - i++; - } - } - return sb.toString(); - } else - return s; + if (ESCAPE_BASIC_XML.matcher(s).find()) { + StringBuilder sb = new StringBuilder(s); + for (int i = 0; i < sb.length();) { + switch (sb.charAt(i)) { + case '&': + sb.replace(i, i + 1, "&"); + i += "&".length(); + break; + case '<': + sb.replace(i, i + 1, "<"); + i += "<".length(); + break; + case '>': + sb.replace(i, i + 1, ">"); + i += ">".length(); + break; + default: + i++; + } + } + return sb.toString(); + } else + return s; } /** * Convert embedded newlines into HTML line breaks */ public static String htmlNewline(String s) { - if (s.indexOf('\n') == -1) - return s; + if (s.indexOf('\n') == -1) + return s; - StringBuilder sb = new StringBuilder(s); - for (int i = 0; i < sb.length();) { - if (sb.charAt(i) == '\n') { - sb.replace(i, i + 1, "
"); - i += "
".length(); - } else - i++; - } - return sb.toString(); + StringBuilder sb = new StringBuilder(s); + for (int i = 0; i < sb.length();) { + if (sb.charAt(i) == '\n') { + sb.replace(i, i + 1, "
"); + i += "
".length(); + } else + i++; + } + return sb.toString(); } /** @@ -119,22 +119,22 @@ class StringUtil { * characters. */ public static String guillemize(Options opt, String s) { - StringBuilder r = new StringBuilder(s); - for (int i = 0; i < r.length();) - switch (r.charAt(i)) { - case '<': - r.replace(i, i + 1, opt.guilOpen); - i += opt.guilOpen.length(); - break; - case '>': - r.replace(i, i + 1, opt.guilClose); - i += opt.guilClose.length(); - break; - default: - i++; - break; - } - return r.toString(); + StringBuilder r = new StringBuilder(s); + for (int i = 0; i < r.length();) + switch (r.charAt(i)) { + case '<': + r.replace(i, i + 1, opt.guilOpen); + i += opt.guilOpen.length(); + break; + case '>': + r.replace(i, i + 1, opt.guilClose); + i += opt.guilClose.length(); + break; + default: + i++; + break; + } + return r.toString(); } /** @@ -144,45 +144,45 @@ class StringUtil { * @return the wrapped String. */ public static String guilWrap(Options opt, String str) { - return opt.guilOpen + str + opt.guilClose; + return opt.guilOpen + str + opt.guilClose; } /** Removes the template specs from a class name. */ public static String removeTemplate(String name) { - int openIdx = name.indexOf('<'); - if (openIdx == -1) - return name; - StringBuilder buf = new StringBuilder(name.length()); - for (int i = 0, depth = 0; i < name.length(); i++) { - char c = name.charAt(i); - if (c == '<') - depth++; - else if (c == '>') - depth--; - else if (depth == 0) - buf.append(c); - } - return buf.toString(); + int openIdx = name.indexOf('<'); + if (openIdx == -1) + return name; + StringBuilder buf = new StringBuilder(name.length()); + for (int i = 0, depth = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (c == '<') + depth++; + else if (c == '>') + depth--; + else if (depth == 0) + buf.append(c); + } + return buf.toString(); } public static String buildRelativePathFromClassNames(String contextPackageName, String classPackageName) { - // path, relative to the root, of the destination class - String[] contextClassPath = contextPackageName.split("\\."); - String[] currClassPath = classPackageName.split("\\."); + // path, relative to the root, of the destination class + String[] contextClassPath = contextPackageName.split("\\."); + String[] currClassPath = classPackageName.split("\\."); - // compute relative path between the context and the destination - // ... first, compute common part - int i = 0, e = Math.min(contextClassPath.length, currClassPath.length); - while (i < e && contextClassPath[i].equals(currClassPath[i])) - i++; - // ... go up with ".." to reach the common root - StringBuilder buf = new StringBuilder(classPackageName.length()); - for (int j = i; j < contextClassPath.length; j++) - buf.append("../"); - // ... go down from the common root to the destination - for (int j = i; j < currClassPath.length; j++) - buf.append(currClassPath[j]).append('/'); // Always use HTML seperators - return buf.toString(); + // compute relative path between the context and the destination + // ... first, compute common part + int i = 0, e = Math.min(contextClassPath.length, currClassPath.length); + while (i < e && contextClassPath[i].equals(currClassPath[i])) + i++; + // ... go up with ".." to reach the common root + StringBuilder buf = new StringBuilder(classPackageName.length()); + for (int j = i; j < contextClassPath.length; j++) + buf.append("../"); + // ... go down from the common root to the destination + for (int j = i; j < currClassPath.length; j++) + buf.append(currClassPath[j]).append('/'); // Always use HTML seperators + return buf.toString(); } /** @@ -199,24 +199,24 @@ class StringUtil { * @return Splitting point (Either referring to a dot, or -1) */ public static int splitPackageClass(String className) { - int gen = className.indexOf('<'); // Begin before generics. - int end = gen >= 0 ? gen : className.length(); - int start = className.lastIndexOf('.', end); - // No package name special cases: - if (start < 0) - return gen >= 0 || className.isEmpty() ? -1 // - : Character.isLowerCase(className.charAt(0)) ? end : -1; - int split = end; - while (true) { - if (Character.isLowerCase(className.charAt(start + 1))) - return split; - split = start; // Continue, this looks like a class name. - if (start < 0) - return -1; - start = className.lastIndexOf('.', start - 1); - } + int gen = className.indexOf('<'); // Begin before generics. + int end = gen >= 0 ? gen : className.length(); + int start = className.lastIndexOf('.', end); + // No package name special cases: + if (start < 0) + return gen >= 0 || className.isEmpty() ? -1 // + : Character.isLowerCase(className.charAt(0)) ? end : -1; + int split = end; + while (true) { + if (Character.isLowerCase(className.charAt(start + 1))) + return split; + split = start; // Continue, this looks like a class name. + if (start < 0) + return -1; + start = className.lastIndexOf('.', start - 1); + } } - + /** * Format a double to a string. *

@@ -226,6 +226,6 @@ class StringUtil { * @return Formatted value */ public static String fmt(double val) { - return val == Math.round(val) ? Long.toString((long) val) : Double.toString(val); + return val == Math.round(val) ? Long.toString((long) val) : Double.toString(val); } } diff --git a/src/main/java/org/umlgraph/doclet/SubclassMatcher.java b/src/main/java/org/umlgraph/doclet/SubclassMatcher.java index 999b823..f9282c6 100644 --- a/src/main/java/org/umlgraph/doclet/SubclassMatcher.java +++ b/src/main/java/org/umlgraph/doclet/SubclassMatcher.java @@ -6,8 +6,8 @@ import com.sun.javadoc.ClassDoc; import com.sun.javadoc.RootDoc; /** - * Matches every class that extends (directly or indirectly) a class - * matched by the regular expression provided. + * Matches every class that extends (directly or indirectly) a class matched by + * the regular expression provided. */ public class SubclassMatcher implements ClassMatcher { @@ -15,22 +15,22 @@ public class SubclassMatcher implements ClassMatcher { protected Pattern pattern; public SubclassMatcher(RootDoc root, Pattern pattern) { - this.root = root; - this.pattern = pattern; + this.root = root; + this.pattern = pattern; } public boolean matches(ClassDoc cd) { - // if it's the class we're looking for return - if(pattern.matcher(cd.toString()).matches()) - return true; - - // recurse on supeclass, if available - return cd.superclass() == null ? false : matches(cd.superclass()); + // if it's the class we're looking for return + if (pattern.matcher(cd.toString()).matches()) + return true; + + // recurse on supeclass, if available + return cd.superclass() == null ? false : matches(cd.superclass()); } public boolean matches(String name) { - ClassDoc cd = root.classNamed(name); - return cd == null ? false : matches(cd); + ClassDoc cd = root.classNamed(name); + return cd == null ? false : matches(cd); } } diff --git a/src/main/java/org/umlgraph/doclet/UMLOptions.java b/src/main/java/org/umlgraph/doclet/UMLOptions.java index b707110..a221134 100644 --- a/src/main/java/org/umlgraph/doclet/UMLOptions.java +++ b/src/main/java/org/umlgraph/doclet/UMLOptions.java @@ -2,8 +2,10 @@ package org.umlgraph.doclet; /** * Options for UMLGraph class diagram generation + * * @opt operations * @opt visibility * @hidden */ -public class UMLOptions {} \ No newline at end of file +public class UMLOptions { +} \ No newline at end of file diff --git a/src/main/java/org/umlgraph/doclet/UmlGraph.java b/src/main/java/org/umlgraph/doclet/UmlGraph.java index edd3825..5bc08a0 100644 --- a/src/main/java/org/umlgraph/doclet/UmlGraph.java +++ b/src/main/java/org/umlgraph/doclet/UmlGraph.java @@ -31,6 +31,7 @@ import com.sun.javadoc.RootDoc; /** * Doclet API implementation + * * @depend - - - OptionProvider * @depend - - - Options * @depend - - - View @@ -50,139 +51,138 @@ public class UmlGraph { /** Entry point through javadoc */ public static boolean start(RootDoc root) throws IOException { - Options opt = buildOptions(root); - root.printNotice("UMLGraph doclet version " + Version.VERSION + " started"); + Options opt = buildOptions(root); + root.printNotice("UMLGraph doclet version " + Version.VERSION + " started"); - View[] views = buildViews(opt, root, root); - if(views == null) - return false; - if (views.length == 0) - buildGraph(root, opt, null); - else - for (int i = 0; i < views.length; i++) - buildGraph(root, views[i], null); - return true; + View[] views = buildViews(opt, root, root); + if (views == null) + return false; + if (views.length == 0) + buildGraph(root, opt, null); + else + for (int i = 0; i < views.length; i++) + buildGraph(root, views[i], null); + return true; } public static void main(String args[]) { - PrintWriter err = new PrintWriter(System.err); - com.sun.tools.javadoc.Main.execute(programName, - err, err, err, docletName, args); + PrintWriter err = new PrintWriter(System.err); + com.sun.tools.javadoc.Main.execute(programName, err, err, err, docletName, args); } public static Options getCommentOptions() { - return commentOptions; + return commentOptions; } /** - * Creates the base Options object. - * This contains both the options specified on the command - * line and the ones specified in the UMLOptions class, if available. - * Also create the globally accessible commentOptions object. + * Creates the base Options object. This contains both the options specified on + * the command line and the ones specified in the UMLOptions class, if + * available. Also create the globally accessible commentOptions object. */ public static Options buildOptions(RootDoc root) { - commentOptions = new Options(); - commentOptions.setOptions(root.options()); - commentOptions.setOptions(findClass(root, "UMLNoteOptions")); - commentOptions.shape = Shape.NOTE; + commentOptions = new Options(); + commentOptions.setOptions(root.options()); + commentOptions.setOptions(findClass(root, "UMLNoteOptions")); + commentOptions.shape = Shape.NOTE; - Options opt = new Options(); - opt.setOptions(root.options()); - opt.setOptions(findClass(root, "UMLOptions")); - return opt; + Options opt = new Options(); + opt.setOptions(root.options()); + opt.setOptions(findClass(root, "UMLOptions")); + return opt; } /** Return the ClassDoc for the specified class; null if not found. */ private static ClassDoc findClass(RootDoc root, String name) { - ClassDoc[] classes = root.classes(); - for (ClassDoc cd : classes) - if(cd.name().equals(name)) - return cd; - return null; + ClassDoc[] classes = root.classes(); + for (ClassDoc cd : classes) + if (cd.name().equals(name)) + return cd; + return null; } /** * Builds and outputs a single graph according to the view overrides */ public static void buildGraph(RootDoc root, OptionProvider op, Doc contextDoc) throws IOException { - if(getCommentOptions() == null) - buildOptions(root); - Options opt = op.getGlobalOptions(); - root.printNotice("Building " + op.getDisplayName()); - ClassDoc[] classes = root.classes(); + if (getCommentOptions() == null) + buildOptions(root); + Options opt = op.getGlobalOptions(); + root.printNotice("Building " + op.getDisplayName()); + ClassDoc[] classes = root.classes(); - ClassGraph c = new ClassGraph(root, op, contextDoc); - c.prologue(); - for (ClassDoc cd : classes) - c.printClass(cd, true); - for (ClassDoc cd : classes) - c.printRelations(cd); - if(opt.inferRelationships) - for (ClassDoc cd : classes) - c.printInferredRelations(cd); - if(opt.inferDependencies) - for (ClassDoc cd : classes) - c.printInferredDependencies(cd); + ClassGraph c = new ClassGraph(root, op, contextDoc); + c.prologue(); + for (ClassDoc cd : classes) + c.printClass(cd, true); + for (ClassDoc cd : classes) + c.printRelations(cd); + if (opt.inferRelationships) + for (ClassDoc cd : classes) + c.printInferredRelations(cd); + if (opt.inferDependencies) + for (ClassDoc cd : classes) + c.printInferredDependencies(cd); - c.printExtraClasses(root); - c.epilogue(); + c.printExtraClasses(root); + c.epilogue(); } /** * Builds the views according to the parameters on the command line - * @param opt The options - * @param srcRootDoc The RootDoc for the source classes - * @param viewRootDoc The RootDoc for the view classes (may be - * different, or may be the same as the srcRootDoc) + * + * @param opt The options + * @param srcRootDoc The RootDoc for the source classes + * @param viewRootDoc The RootDoc for the view classes (may be different, or may + * be the same as the srcRootDoc) */ public static View[] buildViews(Options opt, RootDoc srcRootDoc, RootDoc viewRootDoc) { - if (opt.viewName != null) { - ClassDoc viewClass = viewRootDoc.classNamed(opt.viewName); - if(viewClass == null) { - System.out.println("View " + opt.viewName + " not found! Exiting without generating any output."); - return null; - } - if(viewClass.tags("view").length == 0) { - System.out.println(viewClass + " is not a view!"); - return null; - } - if(viewClass.isAbstract()) { - System.out.println(viewClass + " is an abstract view, no output will be generated!"); - return null; - } - return new View[] { buildView(srcRootDoc, viewClass, opt) }; - } else if (opt.findViews) { - List views = new ArrayList(); - ClassDoc[] classes = viewRootDoc.classes(); + if (opt.viewName != null) { + ClassDoc viewClass = viewRootDoc.classNamed(opt.viewName); + if (viewClass == null) { + System.out.println("View " + opt.viewName + " not found! Exiting without generating any output."); + return null; + } + if (viewClass.tags("view").length == 0) { + System.out.println(viewClass + " is not a view!"); + return null; + } + if (viewClass.isAbstract()) { + System.out.println(viewClass + " is an abstract view, no output will be generated!"); + return null; + } + return new View[] { buildView(srcRootDoc, viewClass, opt) }; + } else if (opt.findViews) { + List views = new ArrayList(); + ClassDoc[] classes = viewRootDoc.classes(); - // find view classes - for (int i = 0; i < classes.length; i++) - if (classes[i].tags("view").length > 0 && !classes[i].isAbstract()) - views.add(buildView(srcRootDoc, classes[i], opt)); + // find view classes + for (int i = 0; i < classes.length; i++) + if (classes[i].tags("view").length > 0 && !classes[i].isAbstract()) + views.add(buildView(srcRootDoc, classes[i], opt)); - return views.toArray(new View[views.size()]); - } else - return new View[0]; + return views.toArray(new View[views.size()]); + } else + return new View[0]; } /** * Builds a view along with its parent views, recursively */ private static View buildView(RootDoc root, ClassDoc viewClass, OptionProvider provider) { - ClassDoc superClass = viewClass.superclass(); - if(superClass == null || superClass.tags("view").length == 0) - return new View(root, viewClass, provider); + ClassDoc superClass = viewClass.superclass(); + if (superClass == null || superClass.tags("view").length == 0) + return new View(root, viewClass, provider); - return new View(root, viewClass, buildView(root, superClass, provider)); + return new View(root, viewClass, buildView(root, superClass, provider)); } /** Option checking */ public static int optionLength(String option) { - return Options.optionLength(option); + return Options.optionLength(option); } /** Indicate the language version we support */ public static LanguageVersion languageVersion() { - return LanguageVersion.JAVA_1_5; + return LanguageVersion.JAVA_1_5; } } diff --git a/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java b/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java index dca406f..a22c021 100644 --- a/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java +++ b/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java @@ -23,6 +23,7 @@ import com.sun.tools.doclets.standard.Standard; /** * Chaining doclet that runs the standart Javadoc doclet first, and on success, * runs the generation of dot files by UMLGraph + * * @author wolf * * @depend - - - WrappedClassDoc @@ -30,81 +31,81 @@ import com.sun.tools.doclets.standard.Standard; */ public class UmlGraphDoc { /** - * Option check, forwards options to the standard doclet, if that one refuses them, - * they are sent to UmlGraph + * Option check, forwards options to the standard doclet, if that one refuses + * them, they are sent to UmlGraph */ public static int optionLength(String option) { - int result = Standard.optionLength(option); - if (result != 0) - return result; - else - return UmlGraph.optionLength(option); + int result = Standard.optionLength(option); + if (result != 0) + return result; + else + return UmlGraph.optionLength(option); } /** * Standard doclet entry point + * * @param root * @return */ public static boolean start(RootDoc root) { - root.printNotice("UmlGraphDoc version " + Version.VERSION + ", running the standard doclet"); - Standard.start(root); - root.printNotice("UmlGraphDoc version " + Version.VERSION + ", altering javadocs"); - try { - String outputFolder = findOutputPath(root.options()); + root.printNotice("UmlGraphDoc version " + Version.VERSION + ", running the standard doclet"); + Standard.start(root); + root.printNotice("UmlGraphDoc version " + Version.VERSION + ", altering javadocs"); + try { + String outputFolder = findOutputPath(root.options()); - Options opt = UmlGraph.buildOptions(root); - opt.setOptions(root.options()); - // in javadoc enumerations are always printed - opt.showEnumerations = true; - opt.relativeLinksForSourcePackages = true; - // enable strict matching for hide expressions - opt.strictMatching = true; + Options opt = UmlGraph.buildOptions(root); + opt.setOptions(root.options()); + // in javadoc enumerations are always printed + opt.showEnumerations = true; + opt.relativeLinksForSourcePackages = true; + // enable strict matching for hide expressions + opt.strictMatching = true; // root.printNotice(opt.toString()); - generatePackageDiagrams(root, opt, outputFolder); - generateContextDiagrams(root, opt, outputFolder); - } catch(Throwable t) { - root.printWarning("Error: " + t.toString()); - t.printStackTrace(); - return false; - } - return true; + generatePackageDiagrams(root, opt, outputFolder); + generateContextDiagrams(root, opt, outputFolder); + } catch (Throwable t) { + root.printWarning("Error: " + t.toString()); + t.printStackTrace(); + return false; + } + return true; } /** * Standand doclet entry + * * @return */ public static LanguageVersion languageVersion() { - return Standard.languageVersion(); + return Standard.languageVersion(); } /** - * Generates the package diagrams for all of the packages that contain classes among those - * returned by RootDoc.class() + * Generates the package diagrams for all of the packages that contain classes + * among those returned by RootDoc.class() */ - private static void generatePackageDiagrams(RootDoc root, Options opt, String outputFolder) - throws IOException { - Set packages = new HashSet(); - for (ClassDoc classDoc : root.classes()) { - PackageDoc packageDoc = classDoc.containingPackage(); - if(!packages.contains(packageDoc.name())) { - packages.add(packageDoc.name()); - OptionProvider view = new PackageView(outputFolder, packageDoc, root, opt); - UmlGraph.buildGraph(root, view, packageDoc); - runGraphviz(opt.dotExecutable, outputFolder, packageDoc.name(), packageDoc.name(), root); - alterHtmlDocs(opt, outputFolder, packageDoc.name(), packageDoc.name(), - "package-summary.html", Pattern.compile("()|(

packages = new HashSet(); + for (ClassDoc classDoc : root.classes()) { + PackageDoc packageDoc = classDoc.containingPackage(); + if (!packages.contains(packageDoc.name())) { + packages.add(packageDoc.name()); + OptionProvider view = new PackageView(outputFolder, packageDoc, root, opt); + UmlGraph.buildGraph(root, view, packageDoc); + runGraphviz(opt.dotExecutable, outputFolder, packageDoc.name(), packageDoc.name(), root); + alterHtmlDocs(opt, outputFolder, packageDoc.name(), packageDoc.name(), "package-summary.html", + Pattern.compile("()|(

classDocs = new TreeSet(new Comparator() { public int compare(ClassDoc cd1, ClassDoc cd2) { return cd1.name().compareTo(cd2.name()); @@ -113,158 +114,143 @@ public class UmlGraphDoc { for (ClassDoc classDoc : root.classes()) classDocs.add(classDoc); - ContextView view = null; - for (ClassDoc classDoc : classDocs) { - try { - if(view == null) - view = new ContextView(outputFolder, classDoc, root, opt); - else - view.setContextCenter(classDoc); - UmlGraph.buildGraph(root, view, classDoc); - runGraphviz(opt.dotExecutable, outputFolder, classDoc.containingPackage().name(), classDoc.name(), root); - alterHtmlDocs(opt, outputFolder, classDoc.containingPackage().name(), classDoc.name(), - classDoc.name() + ".html", Pattern.compile(".*(Class|Interface|Enum) " + classDoc.name() + ".*") , root); - } catch (Exception e) { - throw new RuntimeException("Error generating " + classDoc.name(), e); - } - } + ContextView view = null; + for (ClassDoc classDoc : classDocs) { + try { + if (view == null) + view = new ContextView(outputFolder, classDoc, root, opt); + else + view.setContextCenter(classDoc); + UmlGraph.buildGraph(root, view, classDoc); + runGraphviz(opt.dotExecutable, outputFolder, classDoc.containingPackage().name(), classDoc.name(), + root); + alterHtmlDocs(opt, outputFolder, classDoc.containingPackage().name(), classDoc.name(), + classDoc.name() + ".html", + Pattern.compile(".*(Class|Interface|Enum) " + classDoc.name() + ".*"), root); + } catch (Exception e) { + throw new RuntimeException("Error generating " + classDoc.name(), e); + } + } } /** - * Runs Graphviz dot building both a diagram (in png format) and a client side map for it. + * Runs Graphviz dot building both a diagram (in png format) and a client side + * map for it. */ - private static void runGraphviz(String dotExecutable, String outputFolder, String packageName, String name, RootDoc root) { - if (dotExecutable == null) { - dotExecutable = "dot"; - } - File dotFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".dot"); - File svgFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".svg"); + private static void runGraphviz(String dotExecutable, String outputFolder, String packageName, String name, + RootDoc root) { + if (dotExecutable == null) { + dotExecutable = "dot"; + } + File dotFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".dot"); + File svgFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".svg"); - try { - Process p = Runtime.getRuntime().exec(new String [] { - dotExecutable, - "-Tsvg", - "-o", - svgFile.getAbsolutePath(), - dotFile.getAbsolutePath() - }); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream())); - String line; - while((line = reader.readLine()) != null) - root.printWarning(line); - int result = p.waitFor(); - if (result != 0) - root.printWarning("Errors running Graphviz on " + dotFile); - } catch (Exception e) { - e.printStackTrace(); - System.err.println("Ensure that dot is in your path and that its path does not contain spaces"); - } + try { + Process p = Runtime.getRuntime().exec(new String[] { dotExecutable, "-Tsvg", "-o", + svgFile.getAbsolutePath(), dotFile.getAbsolutePath() }); + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + String line; + while ((line = reader.readLine()) != null) + root.printWarning(line); + int result = p.waitFor(); + if (result != 0) + root.printWarning("Errors running Graphviz on " + dotFile); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Ensure that dot is in your path and that its path does not contain spaces"); + } } - //Format string for the uml image div tag. - private static final String UML_DIV_TAG = - "
" + - "" + - "
"; - - private static final String UML_AUTO_SIZED_DIV_TAG = - "
" + - "" + - "
"; - + // Format string for the uml image div tag. + private static final String UML_DIV_TAG = "
" + + "" + + "
"; + + private static final String UML_AUTO_SIZED_DIV_TAG = "
" + + "" + + "
"; + private static final String EXPANDABLE_UML_STYLE = "font-family: Arial,Helvetica,sans-serif;font-size: 1.5em; display: block; width: 250px; height: 20px; background: #009933; padding: 5px; text-align: center; border-radius: 8px; color: white; font-weight: bold;"; - //Format string for the java script tag. - private static final String EXPANDABLE_UML = - "\n" + - "
\n" + - " \n" + - " %2$s \n" + - "
"; - + // Format string for the java script tag. + private static final String EXPANDABLE_UML = "\n" + "
\n" + + " \n" + " %2$s \n" + "
"; + /** - * Takes an HTML file, looks for the first instance of the specified insertion point, and - * inserts the diagram image reference and a client side map in that point. + * Takes an HTML file, looks for the first instance of the specified insertion + * point, and inserts the diagram image reference and a client side map in that + * point. */ private static void alterHtmlDocs(Options opt, String outputFolder, String packageName, String className, - String htmlFileName, Pattern insertPointPattern, RootDoc root) throws IOException { - // setup files - File output = new File(outputFolder, packageName.replace(".", "/")); - File htmlFile = new File(output, htmlFileName); - File alteredFile = new File(htmlFile.getAbsolutePath() + ".uml"); - if (!htmlFile.exists()) { - System.err.println("Expected file not found: " + htmlFile.getAbsolutePath()); - return; - } + String htmlFileName, Pattern insertPointPattern, RootDoc root) throws IOException { + // setup files + File output = new File(outputFolder, packageName.replace(".", "/")); + File htmlFile = new File(output, htmlFileName); + File alteredFile = new File(htmlFile.getAbsolutePath() + ".uml"); + if (!htmlFile.exists()) { + System.err.println("Expected file not found: " + htmlFile.getAbsolutePath()); + return; + } - // parse & rewrite - BufferedWriter writer = null; - BufferedReader reader = null; - boolean matched = false; - try { - writer = new BufferedWriter(new OutputStreamWriter(new - FileOutputStream(alteredFile), opt.outputEncoding)); - reader = new BufferedReader(new InputStreamReader(new - FileInputStream(htmlFile), opt.outputEncoding)); + // parse & rewrite + BufferedWriter writer = null; + BufferedReader reader = null; + boolean matched = false; + try { + writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(alteredFile), opt.outputEncoding)); + reader = new BufferedReader(new InputStreamReader(new FileInputStream(htmlFile), opt.outputEncoding)); - String line; - while ((line = reader.readLine()) != null) { - writer.write(line); - writer.newLine(); - if (!matched && insertPointPattern.matcher(line).matches()) { - matched = true; - - String tag; - if (opt.autoSize) - tag = String.format(UML_AUTO_SIZED_DIV_TAG, className); - else - tag = String.format(UML_DIV_TAG, className); - if (opt.collapsibleDiagrams) - tag = String.format(EXPANDABLE_UML, tag, "Show UML class diagram", "Hide UML class diagram"); - writer.write(""); - writer.newLine(); - writer.write(tag); - writer.newLine(); - } - } - } finally { - if (writer != null) - writer.close(); - if (reader != null) - reader.close(); - } + String line; + while ((line = reader.readLine()) != null) { + writer.write(line); + writer.newLine(); + if (!matched && insertPointPattern.matcher(line).matches()) { + matched = true; - // if altered, delete old file and rename new one to the old file name - if (matched) { - htmlFile.delete(); - alteredFile.renameTo(htmlFile); - } else { - root.printNotice("Warning, could not find a line that matches the pattern '" + insertPointPattern.pattern() - + "'.\n Class diagram reference not inserted"); - alteredFile.delete(); - } + String tag; + if (opt.autoSize) + tag = String.format(UML_AUTO_SIZED_DIV_TAG, className); + else + tag = String.format(UML_DIV_TAG, className); + if (opt.collapsibleDiagrams) + tag = String.format(EXPANDABLE_UML, tag, "Show UML class diagram", "Hide UML class diagram"); + writer.write(""); + writer.newLine(); + writer.write(tag); + writer.newLine(); + } + } + } finally { + if (writer != null) + writer.close(); + if (reader != null) + reader.close(); + } + + // if altered, delete old file and rename new one to the old file name + if (matched) { + htmlFile.delete(); + alteredFile.renameTo(htmlFile); + } else { + root.printNotice("Warning, could not find a line that matches the pattern '" + + insertPointPattern.pattern() + "'.\n Class diagram reference not inserted"); + alteredFile.delete(); + } } /** * Returns the output path specified on the javadoc options */ private static String findOutputPath(String[][] options) { - for (int i = 0; i < options.length; i++) { - if (options[i][0].equals("-d")) - return options[i][1]; - } - return "."; + for (int i = 0; i < options.length; i++) { + if (options[i][0].equals("-d")) + return options[i][1]; + } + return "."; } } diff --git a/src/main/java/org/umlgraph/doclet/Version.java b/src/main/java/org/umlgraph/doclet/Version.java index 03f857d..36dab1a 100644 --- a/src/main/java/org/umlgraph/doclet/Version.java +++ b/src/main/java/org/umlgraph/doclet/Version.java @@ -1,4 +1,6 @@ /* Automatically generated file */ package org.umlgraph.doclet; -class Version { public static String VERSION = "R5_7_2-60-g0e99a6";} - \ No newline at end of file + +class Version { + public static String VERSION = "R5_7_2-60-g0e99a6"; +} diff --git a/src/main/java/org/umlgraph/doclet/View.java b/src/main/java/org/umlgraph/doclet/View.java index 8659a1c..f3b2dd4 100644 --- a/src/main/java/org/umlgraph/doclet/View.java +++ b/src/main/java/org/umlgraph/doclet/View.java @@ -32,6 +32,7 @@ import com.sun.javadoc.Tag; * will lead to the creation of a UML class diagram. Multiple views can be * defined on the same source tree, effectively allowing to create multiple * class diagram out of it. + * * @author wolf * * @depend - - - Options @@ -53,121 +54,116 @@ public class View implements OptionProvider { * Builds a view given the class that contains its definition */ public View(RootDoc root, ClassDoc c, OptionProvider provider) { - this.viewDoc = c; - this.provider = provider; - this.root = root; - Tag[] tags = c.tags(); - ClassMatcher currMatcher = null; - // parse options, get the global ones, and build a map of the - // pattern matched overrides - globalOptions = new ArrayList(); - for (int i = 0; i < tags.length; i++) { - if (tags[i].name().equals("@match")) { - currMatcher = buildMatcher(tags[i].text()); - if(currMatcher != null) { - optionOverrides.put(currMatcher, new ArrayList()); - } - } else if (tags[i].name().equals("@opt")) { - String[] opts = StringUtil.tokenize(tags[i].text()); - opts[0] = "-" + opts[0]; - if (currMatcher == null) { - globalOptions.add(opts); - } else { - optionOverrides.get(currMatcher).add(opts); - } - } - } + this.viewDoc = c; + this.provider = provider; + this.root = root; + Tag[] tags = c.tags(); + ClassMatcher currMatcher = null; + // parse options, get the global ones, and build a map of the + // pattern matched overrides + globalOptions = new ArrayList(); + for (int i = 0; i < tags.length; i++) { + if (tags[i].name().equals("@match")) { + currMatcher = buildMatcher(tags[i].text()); + if (currMatcher != null) { + optionOverrides.put(currMatcher, new ArrayList()); + } + } else if (tags[i].name().equals("@opt")) { + String[] opts = StringUtil.tokenize(tags[i].text()); + opts[0] = "-" + opts[0]; + if (currMatcher == null) { + globalOptions.add(opts); + } else { + optionOverrides.get(currMatcher).add(opts); + } + } + } } - + /** * Factory method that builds the appropriate matcher for @match tags */ private ClassMatcher buildMatcher(String tagText) { - // check there are at least @match and a parameter - String[] strings = StringUtil.tokenize(tagText); - if (strings.length < 2) { - System.err.println("Skipping uncomplete @match tag, type missing: " + tagText + " in view " + viewDoc); - return null; - } - - try { - if (strings[0].equals("class")) { - return new PatternMatcher(Pattern.compile(strings[1])); - } else if (strings[0].equals("context")) { - return new ContextMatcher(root, Pattern.compile(strings[1]), getGlobalOptions(), - false); - } else if (strings[0].equals("outgoingContext")) { - return new ContextMatcher(root, Pattern.compile(strings[1]), getGlobalOptions(), - false); - } else if (strings[0].equals("interface")) { - return new InterfaceMatcher(root, Pattern.compile(strings[1])); - } else if (strings[0].equals("subclass")) { - return new SubclassMatcher(root, Pattern.compile(strings[1])); - } else { - System.err.println("Skipping @match tag, unknown match type, in view " + viewDoc); - } - } catch (PatternSyntaxException pse) { - System.err.println("Skipping @match tag due to invalid regular expression '" + tagText - + "'" + " in view " + viewDoc); - } catch (Exception e) { - System.err.println("Skipping @match tag due to an internal error '" + tagText - + "'" + " in view " + viewDoc); - e.printStackTrace(); - } - return null; - } - + // check there are at least @match and a parameter + String[] strings = StringUtil.tokenize(tagText); + if (strings.length < 2) { + System.err.println("Skipping uncomplete @match tag, type missing: " + tagText + " in view " + viewDoc); + return null; + } - // ---------------------------------------------------------------- + try { + if (strings[0].equals("class")) { + return new PatternMatcher(Pattern.compile(strings[1])); + } else if (strings[0].equals("context")) { + return new ContextMatcher(root, Pattern.compile(strings[1]), getGlobalOptions(), false); + } else if (strings[0].equals("outgoingContext")) { + return new ContextMatcher(root, Pattern.compile(strings[1]), getGlobalOptions(), false); + } else if (strings[0].equals("interface")) { + return new InterfaceMatcher(root, Pattern.compile(strings[1])); + } else if (strings[0].equals("subclass")) { + return new SubclassMatcher(root, Pattern.compile(strings[1])); + } else { + System.err.println("Skipping @match tag, unknown match type, in view " + viewDoc); + } + } catch (PatternSyntaxException pse) { + System.err.println("Skipping @match tag due to invalid regular expression '" + tagText + "'" + " in view " + viewDoc); + } catch (Exception e) { + System.err.println("Skipping @match tag due to an internal error '" + tagText + "'" + " in view " + viewDoc); + e.printStackTrace(); + } + return null; + } + + // ---------------------------------------------------------------- // OptionProvider methods - // ---------------------------------------------------------------- + // ---------------------------------------------------------------- public Options getOptionsFor(ClassDoc cd) { - Options localOpt = getGlobalOptions(); - overrideForClass(localOpt, cd); - localOpt.setOptions(cd); - return localOpt; + Options localOpt = getGlobalOptions(); + overrideForClass(localOpt, cd); + localOpt.setOptions(cd); + return localOpt; } public Options getOptionsFor(String name) { - Options localOpt = getGlobalOptions(); - overrideForClass(localOpt, name); - return localOpt; + Options localOpt = getGlobalOptions(); + overrideForClass(localOpt, name); + return localOpt; } public Options getGlobalOptions() { - Options go = provider.getGlobalOptions(); - - boolean outputSet = false; - for (String[] opts : globalOptions) { - if (Options.matchOption(opts[0], "output")) - outputSet = true; - go.setOption(opts); - } - if (!outputSet) - go.setOption(new String[] { "output", viewDoc.name() + ".dot" }); - - return go; + Options go = provider.getGlobalOptions(); + + boolean outputSet = false; + for (String[] opts : globalOptions) { + if (Options.matchOption(opts[0], "output")) + outputSet = true; + go.setOption(opts); + } + if (!outputSet) + go.setOption(new String[] { "output", viewDoc.name() + ".dot" }); + + return go; } public void overrideForClass(Options opt, ClassDoc cd) { - provider.overrideForClass(opt, cd); - for (ClassMatcher cm : optionOverrides.keySet()) - if(cm.matches(cd)) - for (String[] override : optionOverrides.get(cm)) - opt.setOption(override); + provider.overrideForClass(opt, cd); + for (ClassMatcher cm : optionOverrides.keySet()) + if (cm.matches(cd)) + for (String[] override : optionOverrides.get(cm)) + opt.setOption(override); } public void overrideForClass(Options opt, String className) { - provider.overrideForClass(opt, className); - for (ClassMatcher cm : optionOverrides.keySet()) - if(cm.matches(className)) - for (String[] override : optionOverrides.get(cm)) - opt.setOption(override); + provider.overrideForClass(opt, className); + for (ClassMatcher cm : optionOverrides.keySet()) + if (cm.matches(className)) + for (String[] override : optionOverrides.get(cm)) + opt.setOption(override); } public String getDisplayName() { - return "view " + viewDoc.name(); + return "view " + viewDoc.name(); } } diff --git a/src/main/java/org/umlgraph/doclet/Visibility.java b/src/main/java/org/umlgraph/doclet/Visibility.java index 408a551..831d64b 100644 --- a/src/main/java/org/umlgraph/doclet/Visibility.java +++ b/src/main/java/org/umlgraph/doclet/Visibility.java @@ -23,6 +23,7 @@ import com.sun.javadoc.ProgramElementDoc; /** * Enumerates the possible visibilities in a Java program. For brevity, package * private visibility is referred as PACKAGE. + * * @author wolf */ public enum Visibility { @@ -31,17 +32,17 @@ public enum Visibility { final public String symbol; private Visibility(String symbol) { - this.symbol = symbol; + this.symbol = symbol; } public static Visibility get(ProgramElementDoc doc) { - if (doc.isPrivate()) - return PRIVATE; - else if (doc.isPackagePrivate()) - return PACKAGE; - else if (doc.isProtected()) - return PROTECTED; - else - return PUBLIC; + if (doc.isPrivate()) + return PRIVATE; + else if (doc.isPackagePrivate()) + return PACKAGE; + else if (doc.isProtected()) + return PROTECTED; + else + return PUBLIC; } } From cd8c3629a1726249eeee4991cf8daee48dfffb73 Mon Sep 17 00:00:00 2001 From: Laurent SCHOELENS Date: Fri, 24 Mar 2023 11:13:30 +0100 Subject: [PATCH 3/4] version running jdk9 --- .gitignore | 1 + pom.xml | 94 ++- src/main/java/module-info.java | 6 + .../java/org/umlgraph/doclet/ClassGraph.java | 634 ++++++++-------- .../java/org/umlgraph/doclet/ClassInfo.java | 8 +- .../org/umlgraph/doclet/ClassMatcher.java | 6 +- .../org/umlgraph/doclet/ContextMatcher.java | 59 +- .../java/org/umlgraph/doclet/ContextView.java | 62 +- src/main/java/org/umlgraph/doclet/Font.java | 2 +- .../org/umlgraph/doclet/InterfaceMatcher.java | 34 +- src/main/java/org/umlgraph/doclet/Option.java | 53 ++ .../org/umlgraph/doclet/OptionProvider.java | 15 +- .../java/org/umlgraph/doclet/Options.java | 679 ++++++++++++++++-- .../org/umlgraph/doclet/PackageMatcher.java | 28 +- .../java/org/umlgraph/doclet/PackageView.java | 47 +- .../org/umlgraph/doclet/PatternMatcher.java | 8 +- .../java/org/umlgraph/doclet/StringUtil.java | 30 +- .../org/umlgraph/doclet/SubclassMatcher.java | 25 +- .../java/org/umlgraph/doclet/TagScanner.java | 121 ++++ .../java/org/umlgraph/doclet/UmlGraph.java | 203 ++++-- .../java/org/umlgraph/doclet/UmlGraphDoc.java | 229 +++--- src/main/java/org/umlgraph/doclet/View.java | 72 +- .../java/org/umlgraph/doclet/Visibility.java | 19 +- .../umlgraph/doclet/util/CommentHelper.java | 168 +++++ .../org/umlgraph/doclet/util/ElementUtil.java | 159 ++++ .../org/umlgraph/doclet/util/TagUtil.java | 50 ++ 26 files changed, 2091 insertions(+), 721 deletions(-) create mode 100644 src/main/java/module-info.java create mode 100644 src/main/java/org/umlgraph/doclet/Option.java create mode 100644 src/main/java/org/umlgraph/doclet/TagScanner.java create mode 100644 src/main/java/org/umlgraph/doclet/util/CommentHelper.java create mode 100644 src/main/java/org/umlgraph/doclet/util/ElementUtil.java create mode 100644 src/main/java/org/umlgraph/doclet/util/TagUtil.java diff --git a/.gitignore b/.gitignore index 3f39de9..82dfc86 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.ipr *.iws *.swp +.idea .settings .project .classpath diff --git a/pom.xml b/pom.xml index 6c9edd3..f87b650 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ umlgraph jar UMLGraph - 5.7.3-SNAPSHOT + 6.0.0-SNAPSHOT Declarative Drawing of UML Diagrams http://www.spinellis.gr/umlgraph @@ -40,19 +40,9 @@ org.sonatype.oss oss-parent - 7 + 9 - - - sun.jdk - tools - 1.5.0 - system - ${java.home}/../lib/tools.jar - - - @@ -63,15 +53,16 @@ maven-compiler-plugin - 2.3.2 + 3.10.1 - 1.5 - 1.5 + 9 + 9 - + + org.apache.maven.plugins maven-jar-plugin - 2.3.1 + 3.3.0 @@ -84,18 +75,18 @@ org.apache.maven.plugins maven-release-plugin - 2.5 + 3.0.0-M7 - true - false - release - deploy + true + false + release + deploy org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.2.1 attach-sources @@ -108,36 +99,39 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9 + 3.5.0 - - - depend - X - - - hidden - X - - - opt - X - - + + + depend + X + + + hidden + X + + + opt + X + + org.umlgraph.doclet.UmlGraphDoc ${project.build.directory}${file.separator}${project.build.finalName}.jar - -inferrel - -inferdep - -autosize - -collapsible - -hide java.* - -collpackages - -qualify - -postfixpackage - -nodefontsize 9 - -nodefontpackagesize 7 - -link http://docs.oracle.com/javase/7/docs/jdk/api/javadoc/doclet/ - -link http://download.oracle.com/javase/7/docs/api/ + + + + + + + + + + + + + + + true diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..ef3e773 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module org.umlgraph.doclet { + exports org.umlgraph.doclet; + opens org.umlgraph.doclet; + + requires transitive jdk.javadoc; +} \ No newline at end of file diff --git a/src/main/java/org/umlgraph/doclet/ClassGraph.java b/src/main/java/org/umlgraph/doclet/ClassGraph.java index 83552ea..8acdc97 100644 --- a/src/main/java/org/umlgraph/doclet/ClassGraph.java +++ b/src/main/java/org/umlgraph/doclet/ClassGraph.java @@ -37,27 +37,40 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.ConstructorDoc; -import com.sun.javadoc.Doc; -import com.sun.javadoc.FieldDoc; -import com.sun.javadoc.MethodDoc; -import com.sun.javadoc.PackageDoc; -import com.sun.javadoc.Parameter; -import com.sun.javadoc.ParameterizedType; -import com.sun.javadoc.ProgramElementDoc; -import com.sun.javadoc.RootDoc; -import com.sun.javadoc.Tag; -import com.sun.javadoc.Type; -import com.sun.javadoc.TypeVariable; -import com.sun.javadoc.WildcardType; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.NoType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +import org.umlgraph.doclet.util.ElementUtil; +import org.umlgraph.doclet.util.TagUtil; + +import com.sun.source.util.DocTrees; + +import jdk.javadoc.doclet.DocletEnvironment; /** * Class graph generation engine @@ -71,6 +84,7 @@ import com.sun.javadoc.WildcardType; * @author Diomidis Spinellis */ class ClassGraph { + enum Align { LEFT, CENTER, RIGHT; @@ -81,20 +95,23 @@ class ClassGraph { } }; - protected Map classnames = new HashMap(); - protected Set rootClasses; - protected Map rootClassdocs = new HashMap(); + private final Elements elementUtils; + private final DocTrees docTrees; + private final Types types; + private final JavaFileManager fileManager; + protected Map classnames = new HashMap<>(); + protected Set rootClasses; + protected Map rootClassdocs = new HashMap<>(); protected OptionProvider optionProvider; protected PrintWriter w; - protected ClassDoc collectionClassDoc; - protected ClassDoc mapClassDoc; + protected TypeElement collectionClassDoc; + protected TypeElement mapClassDoc; protected String linePostfix; protected String linePrefix; // used only when generating context class diagrams in UMLDoc, to generate the - // proper - // relative links to other classes in the image map - protected final String contextPackageName; + // proper relative links to other classes in the image map + protected final Name contextPackageName; /** * Create a new ClassGraph. @@ -111,29 +128,36 @@ class ClassGraph { * @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) { + public ClassGraph(DocletEnvironment root, OptionProvider optionProvider, Element contextDoc) { this.optionProvider = optionProvider; - this.collectionClassDoc = root.classNamed("java.util.Collection"); - this.mapClassDoc = root.classNamed("java.util.Map"); + 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 (ClassDoc classDoc : root.classes()) { - rootClasses.add(classDoc.qualifiedName()); - rootClassdocs.put(classDoc.qualifiedName(), classDoc); + 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 ClassDoc) - contextPackageName = ((ClassDoc) contextDoc).containingPackage().name(); - else if (contextDoc instanceof PackageDoc) - contextPackageName = ((PackageDoc) contextDoc).name(); - else + 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"; @@ -141,18 +165,20 @@ class ClassGraph { } /** 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); + private String qualifiedName(Options opt, Name className) { + if (opt.hideGenerics) { + className = removeTemplate(elementUtils, className); + } // 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); + 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) { + 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); @@ -181,99 +207,85 @@ class ClassGraph { /** * Print the visibility adornment of element e prefixed by any stereotypes */ - private String visibility(Options opt, ProgramElementDoc e) { + 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[]) { + private String parameter(Options opt, List params) { 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) + 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) { + private String type(Options opt, TypeMirror t, boolean generics) { return ((generics ? opt.showQualifiedGenerics : opt.showQualified) ? // - t.qualifiedTypeName() : t.typeName()) // - + (opt.hideGenerics ? "" : typeParameters(opt, t.asParameterizedType())); + 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) + private String typeParameters(Options opt, DeclaredType t) { + if (t == null || t.getTypeArguments() == null || t.getTypeArguments().isEmpty()) { 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) + List 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")) + private String typeAnnotation(Options opt, TypeMirror t) { + if (t.getKind() == TypeKind.VOID) { return ""; - return " : " + type(opt, t, false) + t.dimension(); + } + 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)) + 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.name(); - if (opt.showType) - att += typeAnnotation(opt, f.type()); + String att = visibility(opt, f) + f.getSimpleName(); + if (opt.showType) { + att += typeAnnotation(opt, f.asType()); + } 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; - } - /** Print the class's operations m */ - private boolean operations(Options opt, MethodDoc m[]) { + private boolean operations(Options opt, List m) { boolean printed = false; - for (MethodDoc md : m) { - if (hidden(md)) + for (ExecutableElement md : m) { + if (hidden(md)) { continue; + } // Filter-out static initializer method - if (md.name().equals("") && md.isStatic() && md.isPackagePrivate()) + 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) + md.name() + // - (opt.showType ? "(" + parameter(opt, md.parameters()) + ")" + typeAnnotation(opt, md.returnType()) - : "()"); - tableLine(Align.LEFT, (md.isAbstract() ? Font.ABSTRACT : Font.NORMAL).wrap(opt, op)); + 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); @@ -284,12 +296,15 @@ class ClassGraph { /** Print the common class node's properties */ private void nodeProperties(Options opt) { Options def = opt.getGlobalOptions(); - if (opt.nodeFontName != def.nodeFontName) + if (opt.nodeFontName != def.nodeFontName) { w.print(",fontname=\"" + opt.nodeFontName + "\""); - if (opt.nodeFontColor != def.nodeFontColor) + } + if (opt.nodeFontColor != def.nodeFontColor) { w.print(",fontcolor=\"" + opt.nodeFontColor + "\""); - if (opt.nodeFontSize != def.nodeFontSize) + } + if (opt.nodeFontSize != def.nodeFontSize) { w.print(",fontsize=" + fmt(opt.nodeFontSize)); + } w.print(opt.shape.style); w.println("];"); } @@ -302,15 +317,16 @@ class ClassGraph { * @param prevterm the termination string for the previous element * @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) + private void tagvalue(Options opt, Element c) { + List tags = TagUtil.getTag(docTrees, c, "tagvalue"); + if (tags.isEmpty()) { return; + } - for (Tag tag : tags) { - String t[] = tokenize(tag.text()); + for (String tag : tags) { + String t[] = tokenize(tag); if (t.length != 2) { - System.err.println("@tagvalue expects two fields: " + tag.text()); + System.err.println("@tagvalue expects two fields: " + tag); continue; } tableLine(Align.RIGHT, Font.TAG.wrap(opt, "{" + t[0] + " = " + t[1] + "}")); @@ -318,14 +334,17 @@ class ClassGraph { } /** - * 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()); + 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.text()); + System.err.println("@stereotype expects one field: " + tag); continue; } tableLine(align, guilWrap(opt, t[0])); @@ -333,25 +352,27 @@ class ClassGraph { } /** 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) + 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(c instanceof ClassDoc ? (ClassDoc) c : c.containingClass()); - return opt.matchesHideExpression(c.toString()) // - || (opt.hidePrivateInner && c instanceof ClassDoc && c.isPrivate() - && ((ClassDoc) c).containingClass() != null); + } + 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) { + protected ClassInfo getClassInfo(Name className, boolean create) { return getClassInfo(null, className, create); } - protected ClassInfo getClassInfo(ClassDoc cd, String className, boolean create) { - className = removeTemplate(className); + 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) @@ -366,8 +387,7 @@ class ClassGraph { * 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); + private boolean hidden(CharSequence className) { ClassInfo ci = classnames.get(className); return ci != null ? ci.hidden : optionProvider.getOptionsFor(className).matchesHideExpression(className); } @@ -379,39 +399,42 @@ class ClassGraph { * RootDoc.classes(), this information is used to properly compute relative * links in diagrams for UMLDoc */ - public String printClass(ClassDoc c, boolean rootClass) { + public String printClass(TypeElement 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) + 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); + externalTableStart(opt, c.getQualifiedName(), url); firstInnerTableStart(opt); - if (c.isInterface()) + if (c.getKind() == ElementKind.INTERFACE) { tableLine(Align.CENTER, guilWrap(opt, "interface")); - if (c.isEnum()) + } + if (c.getKind() == ElementKind.ENUM) { 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); + 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(c.commentText())))); - else if (opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) { + 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))); @@ -430,45 +453,49 @@ class ClassGraph { if (showMembers) { if (opt.showAttributes) { innerTableStart(); - FieldDoc[] fields = c.fields(); + List fields = ElementUtil.getFields(c); // if there are no fields, print an empty line to generate proper HTML - if (fields.length == 0) + if (fields.size() == 0) { tableLine(Align.LEFT, ""); - else - attributes(opt, c.fields()); + } else { + attributes(opt, fields); + } innerTableEnd(); - } else if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) { + } 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.isEnum() && opt.showEnumConstants) { + if (c.getKind() == ElementKind.ENUM && opt.showEnumConstants) { innerTableStart(); - FieldDoc[] ecs = c.enumConstants(); + List ecs = ElementUtil.getEnumConstants(c); // if there are no constants, print an empty line to generate proper HTML - if (ecs.length == 0) { + if (ecs.size() == 0) { tableLine(Align.LEFT, ""); } else { - for (FieldDoc fd : c.enumConstants()) { - tableLine(Align.LEFT, fd.name()); + for (VariableElement fd : ecs) { + tableLine(Align.LEFT, fd.getSimpleName()); } } innerTableEnd(); } - if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) { + if (c.getKind() != ElementKind.ENUM && (opt.showConstructors || opt.showOperations)) { innerTableStart(); boolean printedLines = false; - if (opt.showConstructors) - printedLines |= operations(opt, c.constructors()); - if (opt.showOperations) - printedLines |= operations(opt, c.methods()); + if (opt.showConstructors) { + printedLines |= operations(opt, ElementUtil.getConstructors(c)); + } + if (opt.showOperations) { + printedLines |= operations(opt, ElementUtil.getMethods(c)); + } - if (!printedLines) + if (!printedLines) { // if there are no operations nor constructors, // print an empty line to generate proper HTML tableLine(Align.LEFT, ""); + } innerTableEnd(); } @@ -480,13 +507,14 @@ class ClassGraph { // If needed, add a note for this node int ni = 0; - for (Tag t : c.tags("note")) { + 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.qualifiedName(), url); + externalTableStart(UmlGraph.getCommentOptions(), c.getQualifiedName(), url); innerTableStart(); - tableLine(Align.LEFT, Font.CLASS.wrap(UmlGraph.getCommentOptions(), htmlNewline(escape(t.text())))); + tableLine(Align.LEFT, Font.CLASS.wrap(UmlGraph.getCommentOptions(), htmlNewline(escape(t)))); innerTableEnd(); externalTableEnd(); nodeProperties(UmlGraph.getCommentOptions()); @@ -505,25 +533,29 @@ class ClassGraph { * @param from the source class * @param edgetype the dot edge specification */ - private void allRelation(Options opt, RelationType rt, ClassDoc from) { + private void allRelation(Options opt, RelationType rt, TypeElement from) { String tagname = rt.lower; - for (Tag tag : from.tags(tagname)) { - String t[] = tokenize(tag.text()); // l-src label l-dst target + 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.text()); + + " expects four fields (l-src label l-dst target): " + tag); return; } - ClassDoc to = from.findClass(t[3]); + TypeElement to = elementUtils.getTypeElement(t[3]); if (to != null) { - if (hidden(to)) + if (hidden(to)) { continue; + } relation(opt, rt, from, to, t[0], t[1], t[2]); } else { - if (hidden(t[3])) + Name t3 = elementUtils.getName(t[3]); + if (hidden(t3)) { continue; - relation(opt, rt, from, from.toString(), to, t[3], t[0], t[1], t[2]); + } + relation(opt, rt, from, from.getQualifiedName(), to, t3, t[0], t[1], t[2]); } } } @@ -536,7 +568,7 @@ class ClassGraph { * @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, + 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) + "\"" : ""; @@ -579,30 +611,43 @@ class ClassGraph { * @param from the source class * @param to the destination class */ - private void relation(Options opt, RelationType rt, ClassDoc from, ClassDoc to, String tailLabel, String label, + private void relation(Options opt, RelationType rt, TypeElement from, TypeElement to, String tailLabel, String label, String headLabel) { - relation(opt, rt, from, from.toString(), to, to.toString(), tailLabel, label, 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 + 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) - Type s = c.superclassType(); - ClassDoc sc = s != null && !s.qualifiedTypeName().equals(Object.class.getName()) ? s.asClassDoc() : null; - if (sc != null && !c.isEnum() && !hidden(sc)) + 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) - for (Tag tag : c.tags("extends")) - if (!hidden(tag.text())) - relation(opt, RelationType.EXTENDS, c, c.findClass(tag.text()), null, null, null); + 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 (Type iface : c.interfaceTypes()) { - ClassDoc ic = iface.asClassDoc(); - if (!hidden(ic)) + 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); @@ -615,13 +660,14 @@ class ClassGraph { } /** 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) { + public void printExtraClasses(DocletEnvironment root) { + Set names = new HashSet<>(classnames.keySet()); + for (Name className : names) { ClassInfo info = getClassInfo(className, true); - if (info.nodePrinted) + if (info.nodePrinted) { continue; - ClassDoc c = root.classNamed(className); + } + TypeElement c = elementUtils.getTypeElement(className); if (c != null) { printClass(c, false); continue; @@ -660,26 +706,31 @@ class ClassGraph { * * @param classes */ - public void printInferredRelations(ClassDoc c) { + public void printInferredRelations(TypeElement c) { // check if the source is excluded from inference - if (hidden(c)) + if (hidden(c)) { return; + } - Options opt = optionProvider.getOptionsFor(c); + Options opt = optionProvider.getOptionsFor(docTrees, c); - for (FieldDoc field : c.fields(false)) { - if (hidden(field)) + for (VariableElement field : ElementUtil.getFields(c)) { + if (hidden(field)) { continue; + } // skip statics - if (field.isStatic()) + if (field.getModifiers().contains(Modifier.STATIC)) { continue; + } // skip primitives FieldRelationInfo fri = getFieldRelationInfo(field); - if (fri == null) + if (fri == null) { continue; + } // check if the destination is excluded from inference - if (hidden(fri.cd)) + 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()); @@ -695,9 +746,18 @@ class ClassGraph { * deprecation warning, which is output, because the imported classed are an * implementation detail. */ - @SuppressWarnings("deprecation") - ClassDoc[] importedClasses(ClassDoc c) { - return c.importedClasses(); + 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(); } /** @@ -707,58 +767,65 @@ class ClassGraph { * * @param classes */ - public void printInferredDependencies(ClassDoc c) { - if (hidden(c)) + public void printInferredDependencies(TypeElement c) { + if (hidden(c)) { return; + } - Options opt = optionProvider.getOptionsFor(c); - Set types = new HashSet(); + Options opt = optionProvider.getOptionsFor(docTrees, 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()); + 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 (FieldDoc field : filterByVisibility(c.fields(false), opt.inferDependencyVisibility)) { - types.add(field.type()); + for (VariableElement field : filterByVisibility(ElementUtil.getFields(c), opt.inferDependencyVisibility)) { + types.add(field.asType()); } } // see if there are some type parameters - if (c.asParameterizedType() != null) { - ParameterizedType pt = c.asParameterizedType(); - types.addAll(Arrays.asList(pt.typeArguments())); + if (c.asType() instanceof DeclaredType) { + DeclaredType pt = (DeclaredType) c.asType(); + types.addAll(pt.getTypeArguments()); } // see if type parameters extend something - for (TypeVariable tv : c.typeParameters()) { - if (tv.bounds().length > 0) - types.addAll(Arrays.asList(tv.bounds())); + 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(Arrays.asList(importedClasses(c))); + if (opt.useImports) { + types.addAll(importedClasses(c)); + } // compute dependencies - for (Type type : types) { + for (TypeMirror 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())) + 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 - ClassDoc fc = type.asClassDoc(); - if (hidden(fc)) + 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 && c.containingPackage().equals(fc.containingPackage())) + 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()); @@ -773,95 +840,97 @@ class ClassGraph { * 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); + private List filterByVisibility(List docs, Visibility visibility) { + if (visibility == Visibility.PRIVATE) { + return docs; + } - List filtered = new ArrayList(); + List filtered = new ArrayList<>(); for (T doc : docs) { - if (Visibility.get(doc).compareTo(visibility) > 0) + if (Visibility.get(doc).compareTo(visibility) > 0) { filtered.add(doc); + } } return filtered; } - private FieldRelationInfo getFieldRelationInfo(FieldDoc field) { - Type type = field.type(); - if (type.isPrimitive() || type instanceof WildcardType || type instanceof TypeVariable) + private FieldRelationInfo getFieldRelationInfo(VariableElement field) { + TypeMirror type = field.asType(); + if (type.getKind().isPrimitive() || type.getKind() == TypeKind.WILDCARD || type.getKind() == TypeKind.TYPEVAR) { 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); + 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 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.length == 2 && !argTypes[1].isPrimitive()) - return new FieldRelationInfo(argTypes[1].asClassDoc(), true); + if (argTypes != null && argTypes.size() == 2 && !argTypes.get(1).getKind().isPrimitive()) { + return new FieldRelationInfo(ElementUtil.getTypeElement(argTypes.get(1)), true); + } } - return new FieldRelationInfo(type.asClassDoc(), false); + return new FieldRelationInfo(ElementUtil.getTypeElement(type), false); } - 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(); + private List 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 (Type pti : pt.interfaceTypes()) { - Type[] result = getInterfaceTypeArguments(iface, pti); - if (result != null) + for (TypeMirror pti : ElementUtil.getInterfacesTypes(iface)) { + List result = getInterfaceTypeArguments(iface, pti); + if (result != null) { return result; + } + } + if (ElementUtil.getSuperclassType(pt) != null) { + return getInterfaceTypeArguments(iface, ElementUtil.getSuperclassType(pt)); } - 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; } /** Convert the class name into a corresponding URL */ - public String classToUrl(ClassDoc cd, boolean rootClass) { + public String classToUrl(TypeElement 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()); + 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); + public String classToUrl(Name className) { + TypeElement classDoc = rootClassdocs.get(className); if (classDoc != null) { String docRoot = optionProvider.getGlobalOptions().apiDocRoot; - if (docRoot == null) + 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(); + .append(ElementUtil.getPackageOf(elementUtils, classDoc).getQualifiedName().toString().replace('.', '/')) // + .append('/').append(classDoc.getSimpleName()).append(".html").toString(); } String docRoot = optionProvider.getGlobalOptions().getApiDocRoot(className); - if (docRoot == null) + 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('/'); + 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(); } @@ -875,16 +944,17 @@ class ClassGraph { Options opt = optionProvider.getGlobalOptions(); OutputStream os; - if (opt.outputFileName.equals("-")) + if (opt.outputFileName.equals("-")) { os = System.out; - else { + } 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()) + if (file.getParentFile() != null && !file.getParentFile().exists()) { file.getParentFile().mkdirs(); + } os = new FileOutputStream(file); } @@ -900,10 +970,12 @@ class ClassGraph { w.println(linePrefix + "nodesep=" + opt.nodeSep + ";"); w.println(linePrefix + "ranksep=" + opt.rankSep + ";"); - if (opt.horizontal) + if (opt.horizontal) { w.println(linePrefix + "rankdir=LR;"); - if (opt.bgColor != null) + } + if (opt.bgColor != null) { w.println(linePrefix + "bgcolor=\"" + opt.bgColor + "\";\n"); + } } /** Dot epilogue */ @@ -913,7 +985,7 @@ class ClassGraph { w.close(); } - private void externalTableStart(Options opt, String name, String url) { + private void externalTableStart(Options opt, Name name, String url) { String bgcolor = opt.nodeFillColor == null ? "" : (" bgcolor=\"" + opt.nodeFillColor + "\""); String href = url == null ? "" : (" href=\"" + url + "\" target=\"_parent\""); w.print("<" + opt.shape.extraColumn() + "" + linePostfix); } - private void tableLine(Align align, String text) { + private void tableLine(Align align, CharSequence text) { w.print(linePrefix + linePrefix // + "
" // + text // MAY contain markup! @@ -956,10 +1028,10 @@ class ClassGraph { } private static class FieldRelationInfo { - ClassDoc cd; + TypeElement cd; boolean multiple; - public FieldRelationInfo(ClassDoc cd, boolean multiple) { + public FieldRelationInfo(TypeElement cd, boolean multiple) { this.cd = cd; this.multiple = multiple; } diff --git a/src/main/java/org/umlgraph/doclet/ClassInfo.java b/src/main/java/org/umlgraph/doclet/ClassInfo.java index ccb67e0..5939a0c 100644 --- a/src/main/java/org/umlgraph/doclet/ClassInfo.java +++ b/src/main/java/org/umlgraph/doclet/ClassInfo.java @@ -22,6 +22,8 @@ package org.umlgraph.doclet; import java.util.HashMap; import java.util.Map; +import javax.lang.model.element.Name; + /** * Class's dot-compatible alias name (for fully qualified class names) and * printed information @@ -42,7 +44,7 @@ class ClassInfo { * classes linked with a bi-directional relation , and the ones referred by a * directed relation */ - Map relatedClasses = new HashMap(); + Map relatedClasses = new HashMap<>(); ClassInfo(boolean h) { hidden = h; @@ -50,7 +52,7 @@ class ClassInfo { classNumber++; } - public void addRelation(String dest, RelationType rt, RelationDirection d) { + public void addRelation(Name dest, RelationType rt, RelationDirection d) { RelationPattern ri = relatedClasses.get(dest); if (ri == null) { ri = new RelationPattern(RelationDirection.NONE); @@ -59,7 +61,7 @@ class ClassInfo { ri.addRelation(rt, d); } - public RelationPattern getRelation(String dest) { + public RelationPattern getRelation(CharSequence dest) { return relatedClasses.get(dest); } diff --git a/src/main/java/org/umlgraph/doclet/ClassMatcher.java b/src/main/java/org/umlgraph/doclet/ClassMatcher.java index d89df36..294fcfa 100644 --- a/src/main/java/org/umlgraph/doclet/ClassMatcher.java +++ b/src/main/java/org/umlgraph/doclet/ClassMatcher.java @@ -17,7 +17,7 @@ package org.umlgraph.doclet; -import com.sun.javadoc.ClassDoc; +import javax.lang.model.element.TypeElement; /** * A ClassMatcher is used to check if a class definition matches a specific @@ -29,10 +29,10 @@ public interface ClassMatcher { /** * Returns the options for the specified class. */ - public boolean matches(ClassDoc cd); + public boolean matches(TypeElement cd); /** * Returns the options for the specified class. */ - public boolean matches(String name); + public boolean matches(CharSequence name); } diff --git a/src/main/java/org/umlgraph/doclet/ContextMatcher.java b/src/main/java/org/umlgraph/doclet/ContextMatcher.java index 626fab0..eafdf1b 100644 --- a/src/main/java/org/umlgraph/doclet/ContextMatcher.java +++ b/src/main/java/org/umlgraph/doclet/ContextMatcher.java @@ -26,8 +26,10 @@ import java.util.List; import java.util.Set; import java.util.regex.Pattern; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.RootDoc; +import jdk.javadoc.doclet.DocletEnvironment; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; /** * Matches classes that are directly connected to one of the classes matched by @@ -46,11 +48,11 @@ import com.sun.javadoc.RootDoc; public class ContextMatcher implements ClassMatcher { ClassGraphHack cg; Pattern pattern; - List matched; - Set visited = new HashSet(); + List matched; + Set visited = new HashSet<>(); /** The options will be used to decide on inference */ Options opt; - RootDoc root; + DocletEnvironment root; boolean keepParentHide; /** @@ -64,7 +66,7 @@ public class ContextMatcher implements ClassMatcher { * the context * @throws IOException */ - public ContextMatcher(RootDoc root, Pattern pattern, Options options, boolean keepParentHide) throws IOException { + public ContextMatcher(DocletEnvironment root, Pattern pattern, Options options, boolean keepParentHide) throws IOException { this.pattern = pattern; this.root = root; this.keepParentHide = keepParentHide; @@ -89,11 +91,11 @@ public class ContextMatcher implements ClassMatcher { // build up the classgraph printing the relations for all of the // classes that make up the "center" of this context this.pattern = pattern; - matched = new ArrayList(); - for (ClassDoc cd : root.classes()) { - if (pattern.matcher(cd.toString()).matches()) { - matched.add(cd); - addToGraph(cd); + matched = new ArrayList<>(); + for (Element cd : root.getIncludedElements()) { + if (cd instanceof TypeElement && pattern.matcher(cd.toString()).matches()) { + matched.add((TypeElement) cd); + addToGraph((TypeElement) cd); } } } @@ -105,31 +107,36 @@ public class ContextMatcher implements ClassMatcher { * * @param cd */ - private void addToGraph(ClassDoc cd) { + private void addToGraph(TypeElement cd) { // avoid adding twice the same class, but don't rely on cg.getClassInfo // since there are other ways to add a classInfor than printing the class - if (visited.contains(cd.toString())) + if (visited.contains(cd.toString())) { return; + } visited.add(cd.toString()); cg.printClass(cd, false); cg.printRelations(cd); - if (opt.inferRelationships) + if (opt.inferRelationships) { cg.printInferredRelations(cd); - if (opt.inferDependencies) + } + if (opt.inferDependencies) { cg.printInferredDependencies(cd); + } } /** - * @see org.umlgraph.doclet.ClassMatcher#matches(com.sun.javadoc.ClassDoc) + * @see org.umlgraph.doclet.ClassMatcher#matches(TypeElement) */ - public boolean matches(ClassDoc cd) { - if (keepParentHide && opt.matchesHideExpression(cd.toString())) + public boolean matches(TypeElement cd) { + if (keepParentHide && opt.matchesHideExpression(cd.getQualifiedName())) { return false; + } // if the class is matched, it's in by default. - if (matched.contains(cd)) + if (matched.contains(cd)) { return true; + } // otherwise, add the class to the graph and see if it's associated // with any of the matched classes using the classgraph hack @@ -138,16 +145,18 @@ public class ContextMatcher implements ClassMatcher { } /** - * @see org.umlgraph.doclet.ClassMatcher#matches(java.lang.String) + * @see org.umlgraph.doclet.ClassMatcher#matches(CharSequence) */ - public boolean matches(String name) { - if (pattern.matcher(name).matches()) + public boolean matches(CharSequence name) { + if (pattern.matcher(name).matches()) { return true; + } - for (ClassDoc mcd : matched) { + for (TypeElement mcd : matched) { RelationPattern rp = cg.getClassInfo(mcd, true).getRelation(name); - if (rp != null && opt.contextRelationPattern.matchesOne(rp)) + if (rp != null && opt.contextRelationPattern.matchesOne(rp)) { return true; + } } return false; } @@ -162,7 +171,7 @@ public class ContextMatcher implements ClassMatcher { */ private static class ClassGraphHack extends ClassGraph { - public ClassGraphHack(RootDoc root, OptionProvider optionProvider) throws IOException { + public ClassGraphHack(DocletEnvironment root, OptionProvider optionProvider) throws IOException { super(root, optionProvider, null); prologue(); } diff --git a/src/main/java/org/umlgraph/doclet/ContextView.java b/src/main/java/org/umlgraph/doclet/ContextView.java index 48e9737..5202315 100755 --- a/src/main/java/org/umlgraph/doclet/ContextView.java +++ b/src/main/java/org/umlgraph/doclet/ContextView.java @@ -3,8 +3,14 @@ package org.umlgraph.doclet; import java.io.IOException; import java.util.regex.Pattern; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.RootDoc; +import jdk.javadoc.doclet.DocletEnvironment; + +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.TypeElement; + +import org.umlgraph.doclet.util.ElementUtil; + +import com.sun.source.util.DocTrees; /** * A view designed for UMLDoc, filters out everything that it's not directly @@ -20,7 +26,8 @@ import com.sun.javadoc.RootDoc; */ public class ContextView implements OptionProvider { - private ClassDoc cd; + private TypeElement cd; + private DocletEnvironment root; private ContextMatcher matcher; private Options globalOptions; private Options myGlobalOptions; @@ -29,9 +36,12 @@ public class ContextView implements OptionProvider { private Options packageOptions; private static final String[] HIDE_OPTIONS = new String[] { "hide" }; - public ContextView(String outputFolder, ClassDoc cd, RootDoc root, Options parent) throws IOException { + public ContextView(String outputFolder, TypeElement cd, DocletEnvironment root, Options parent) throws IOException { this.cd = cd; - String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() + ".dot"; + this.root = root; + ModuleElement md = ElementUtil.getModuleOf(root, cd); + String pathPrefix = Runtime.version().major() > 10 && md != null ? md.getQualifiedName().toString() + "/" : ""; + String outputPath = pathPrefix + ElementUtil.getPackageOf(root, cd).getQualifiedName().toString().replace('.', '/') + "/" + cd.getSimpleName() + ".dot"; // setup options statically, so that we won't need to change them so // often @@ -51,13 +61,15 @@ public class ContextView implements OptionProvider { this.centerOptions.nodeFillColor = "lemonChiffon"; this.centerOptions.showQualified = false; - this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.toString())), myGlobalOptions, true); + this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.getQualifiedName().toString())), myGlobalOptions, true); } - public void setContextCenter(ClassDoc contextCenter) { + public void setContextCenter(TypeElement contextCenter) { this.cd = contextCenter; - String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() + ".dot"; + ModuleElement md = ElementUtil.getModuleOf(root, contextCenter); + String pathPrefix = Runtime.version().major() > 10 && md != null ? md.getQualifiedName().toString() + "/" : ""; + String outputPath = pathPrefix + ElementUtil.getPackageOf(root, cd).getQualifiedName().toString().replace('.', '/') + "/" + cd.getSimpleName() + ".dot"; this.myGlobalOptions.setOption(new String[] { "output", outputPath }); matcher.setContextCenter(Pattern.compile(Pattern.quote(cd.toString()))); } @@ -70,14 +82,14 @@ public class ContextView implements OptionProvider { return myGlobalOptions; } - public Options getOptionsFor(ClassDoc cd) { + public Options getOptionsFor(DocTrees dt, TypeElement cd) { Options opt; - if (globalOptions.matchesHideExpression(cd.qualifiedName()) - || !(matcher.matches(cd) || globalOptions.matchesIncludeExpression(cd.qualifiedName()))) { + if (globalOptions.matchesHideExpression(cd.getQualifiedName()) + || !(matcher.matches(cd) || globalOptions.matchesIncludeExpression(cd.getQualifiedName()))) { opt = hideOptions; } else if (cd.equals(this.cd)) { opt = centerOptions; - } else if (cd.containingPackage().equals(this.cd.containingPackage())) { + } else if (root.getElementUtils().getPackageOf(cd).equals(root.getElementUtils().getPackageOf(this.cd))) { opt = packageOptions; } else { opt = globalOptions; @@ -87,31 +99,35 @@ public class ContextView implements OptionProvider { return optionClone; } - public Options getOptionsFor(String name) { + public Options getOptionsFor(CharSequence name) { Options opt; - if (!matcher.matches(name)) + if (!matcher.matches(name)) { opt = hideOptions; - else if (name.equals(cd.name())) + } else if (name.equals(cd.getQualifiedName())) { opt = centerOptions; - else + } else { opt = globalOptions; + } Options optionClone = (Options) opt.clone(); overrideForClass(optionClone, name); return optionClone; } - public void overrideForClass(Options opt, ClassDoc cd) { - opt.setOptions(cd); - if (opt.matchesHideExpression(cd.qualifiedName()) - || !(matcher.matches(cd) || opt.matchesIncludeExpression(cd.qualifiedName()))) + public void overrideForClass(Options opt, TypeElement cd) { + opt.setOptions(root.getDocTrees(), cd); + if (opt.matchesHideExpression(cd.getQualifiedName()) + || !(matcher.matches(cd) || opt.matchesIncludeExpression(cd.getQualifiedName()))) { opt.setOption(HIDE_OPTIONS); - if (cd.equals(this.cd)) + } + if (cd.equals(this.cd)) { opt.nodeFillColor = "lemonChiffon"; + } } - public void overrideForClass(Options opt, String className) { - if (!(matcher.matches(className) || opt.matchesIncludeExpression(className))) + public void overrideForClass(Options opt, CharSequence className) { + if (!(matcher.matches(className) || opt.matchesIncludeExpression(className))) { opt.setOption(HIDE_OPTIONS); + } } } diff --git a/src/main/java/org/umlgraph/doclet/Font.java b/src/main/java/org/umlgraph/doclet/Font.java index f9bcc0d..0e2b0a7 100644 --- a/src/main/java/org/umlgraph/doclet/Font.java +++ b/src/main/java/org/umlgraph/doclet/Font.java @@ -4,7 +4,7 @@ package org.umlgraph.doclet; * Class to represent a font for graphviz. *

* This is a fairly complicated model, because it is rather an API into graphviz - * formatting strings rather than a standalone thing.

Some fonts (edge, node, * abstract) are set on the top level elements, whereas others (class name, tag, * package) are inserted as {@code } tags, and these can be omitted if not * set. Inheritance of properties then happens in graphviz. diff --git a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java index e39518d..a4a6f13 100644 --- a/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java +++ b/src/main/java/org/umlgraph/doclet/InterfaceMatcher.java @@ -2,8 +2,13 @@ package org.umlgraph.doclet; import java.util.regex.Pattern; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.RootDoc; +import jdk.javadoc.doclet.DocletEnvironment; + +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import org.umlgraph.doclet.util.ElementUtil; /** * Matches every class that implements (directly or indirectly) an interfaces @@ -11,31 +16,36 @@ import com.sun.javadoc.RootDoc; */ public class InterfaceMatcher implements ClassMatcher { - protected RootDoc root; + protected DocletEnvironment root; protected Pattern pattern; - public InterfaceMatcher(RootDoc root, Pattern pattern) { + public InterfaceMatcher(DocletEnvironment root, Pattern pattern) { this.root = root; this.pattern = pattern; } - public boolean matches(ClassDoc cd) { + public boolean matches(TypeElement cd) { // if it's the interface we're looking for, match - if (cd.isInterface() && pattern.matcher(cd.toString()).matches()) + if (cd.getKind() == ElementKind.INTERFACE && pattern.matcher(cd.toString()).matches()) { return true; + } // for each interface, recurse, since classes and interfaces // are treated the same in the doclet API - for (ClassDoc iface : cd.interfaces()) - if (matches(iface)) + for (TypeMirror type : cd.getInterfaces()) { + TypeElement iType = ElementUtil.getTypeElement(type); + if (iType != null && matches(iType)) { return true; + } + } - // recurse on supeclass, if available - return cd.superclass() == null ? false : matches(cd.superclass()); + // recurse on superclass, if available + TypeElement scd = ElementUtil.getTypeElement(cd.getSuperclass()); + return scd == null ? false : matches(scd); } - public boolean matches(String name) { - ClassDoc cd = root.classNamed(name); + public boolean matches(CharSequence name) { + TypeElement cd = root.getElementUtils().getTypeElement(name); return cd == null ? false : matches(cd); } diff --git a/src/main/java/org/umlgraph/doclet/Option.java b/src/main/java/org/umlgraph/doclet/Option.java new file mode 100644 index 0000000..e419601 --- /dev/null +++ b/src/main/java/org/umlgraph/doclet/Option.java @@ -0,0 +1,53 @@ +package org.umlgraph.doclet; + +import java.util.List; + +import jdk.javadoc.doclet.Doclet; + +/** + * A base class for declaring options. Subtypes for specific options should + * implement the {@link #process(String,List) process} method to handle + * instances of the option found on the command line. + */ +public abstract class Option implements Doclet.Option { + private final String name; + private final int argumentCount; + private final String description; + private final String parameters; + + public Option(String name, boolean hasArg, String description, String parameters) { + this(name, hasArg ? 1 : 0, description, parameters); + } + + public Option(String name, int argumentCount, String description, String parameters) { + this.name = name; + this.argumentCount = argumentCount; + this.description = description; + this.parameters = parameters; + } + + @Override + public int getArgumentCount() { + return argumentCount; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Kind getKind() { + return Kind.STANDARD; + } + + @Override + public List getNames() { + return List.of(name); + } + + @Override + public String getParameters() { + return argumentCount == 0 ? "" : parameters; + } +} \ No newline at end of file diff --git a/src/main/java/org/umlgraph/doclet/OptionProvider.java b/src/main/java/org/umlgraph/doclet/OptionProvider.java index 151d2b7..e3f8e89 100644 --- a/src/main/java/org/umlgraph/doclet/OptionProvider.java +++ b/src/main/java/org/umlgraph/doclet/OptionProvider.java @@ -17,22 +17,23 @@ package org.umlgraph.doclet; -import com.sun.javadoc.ClassDoc; +import javax.lang.model.element.TypeElement; + +import com.sun.source.util.DocTrees; /** - * A factory class that builds Options object for general use or for a specific - * class + * A factory class that builds Options object for general use or for a specific class */ public interface OptionProvider { /** * Returns the options for the specified class. */ - public Options getOptionsFor(ClassDoc cd); + public Options getOptionsFor(DocTrees dt, TypeElement cd); /** * Returns the options for the specified class. */ - public Options getOptionsFor(String name); + public Options getOptionsFor(CharSequence name); /** * Returns the global options (the class independent definition) @@ -42,12 +43,12 @@ public interface OptionProvider { /** * Gets a base Options and applies the overrides for the specified class */ - public void overrideForClass(Options opt, ClassDoc cd); + public void overrideForClass(Options opt, TypeElement cd); /** * Gets a base Options and applies the overrides for the specified class */ - public void overrideForClass(Options opt, String className); + public void overrideForClass(Options opt, CharSequence className); /** * Returns user displayable name for this option provider. diff --git a/src/main/java/org/umlgraph/doclet/Options.java b/src/main/java/org/umlgraph/doclet/Options.java index 7018da8..7b5cc98 100644 --- a/src/main/java/org/umlgraph/doclet/Options.java +++ b/src/main/java/org/umlgraph/doclet/Options.java @@ -29,18 +29,26 @@ import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.Doc; -import com.sun.javadoc.Tag; +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; + +import org.umlgraph.doclet.util.TagUtil; + +import com.sun.source.util.DocTrees; + +import jdk.javadoc.doclet.Doclet; /** * Represent the program options @@ -49,13 +57,550 @@ import com.sun.javadoc.Tag; * @author Diomidis Spinellis */ public class Options implements Cloneable, OptionProvider { + + public final Set OPTIONS = Set.of( + new Option("--d", true, "Specify the output directory (defaults to the current directory).", "") { + @Override + public boolean process(String option, List arguments) { + outputDirectory = arguments.get(0); + return true; + } + }, + new Option("-qualify", false, "Produce fully-qualified class names.", null) { + @Override + public boolean process(String option, List arguments) { + showQualified = true; + return true; + } + }, + new Option("-qualifyGenerics", false, "Use fully-qualified class names in Java generics.", null) { + @Override + public boolean process(String option, List arguments) { + showQualifiedGenerics = true; + return true; + } + }, + new Option("-hideGenerics", false, "FIXME Missing doc", null) { + @Override + public boolean process(String option, List arguments) { + hideGenerics = true; + return true; + } + }, + new Option("-horizontal", false, "Layout the graph in the horizontal direction.", null) { + @Override + public boolean process(String option, List arguments) { + horizontal = true; + return true; + } + }, + new Option("-attributes", false, "Show class attributes (Java fields)", null) { + @Override + public boolean process(String option, List arguments) { + showAttributes = true; + return true; + } + }, + new Option("-enumconstants", false, "When showing enumerations, also show the values they can take", null) { + @Override + public boolean process(String option, List arguments) { + showEnumConstants = true; + return true; + } + }, + new Option("-operations", false, "Show class operations (Java methods)", null) { + @Override + public boolean process(String option, List arguments) { + showOperations = true; + return true; + } + }, + new Option("-enumerations", false, "Show enumarations as separate stereotyped primitive types", null) { + @Override + public boolean process(String option, List arguments) { + showEnumerations = true; + return true; + } + }, + new Option("-constructors", false, "Show a class's constructors", null) { + @Override + public boolean process(String option, List arguments) { + showConstructors = true; + return true; + } + }, + new Option("-visibility", false, "Adorn class elements according to their visibility (private, public, protected, package)", null) { + @Override + public boolean process(String option, List arguments) { + showVisibility = true; + return true; + } + }, + new Option("-types", false, "Add type information to attributes and operations", null) { + @Override + public boolean process(String option, List arguments) { + showType = true; + return true; + } + }, + new Option("-autosize", false, "Fits generated graph to the width of the page/window. Defaults to true.", null) { + @Override + public boolean process(String option, List arguments) { + autoSize = true; + return true; + } + }, + new Option("-commentname", false, "Name the element using the text in the javadoc comment, instead of the name of its class.", null) { + @Override + public boolean process(String option, List arguments) { + showComment = true; + return true; + } + }, + new Option("-all", false, "Same as -attributes -operations -visibility -types -enumerations -enumconstants", null) { + @Override + public boolean process(String option, List arguments) { + setAll(); + return true; + } + }, + new Option("--bgcolor", true, "Specify the graph's background color.", "") { + @Override + public boolean process(String option, List arguments) { + bgColor = arguments.get(0); + return true; + } + }, + new Option("--edgecolor", true, "Specify the color for drawing edges.", "") { + @Override + public boolean process(String option, List arguments) { + edgeColor = arguments.get(0); + return true; + } + }, + new Option("--edgefontcolor", true, "Specify the font color to use for edge labels.", "") { + @Override + public boolean process(String option, List arguments) { + edgeFontColor = arguments.get(0); + return true; + } + }, + new Option("--edgefontname", true, "Specify the font name to use for edge labels.", "") { + @Override + public boolean process(String option, List arguments) { + edgeFontName = arguments.get(0); + return true; + } + }, + new Option("-edgefontsize", true, "Specify the font size to use for edge labels.", "") { + @Override + public boolean process(String option, List arguments) { + edgeFontSize = Double.parseDouble(arguments.get(0)); + return true; + } + }, + new Option("-nodefontcolor", true, "Specify the font color to use inside nodes", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontColor = arguments.get(0); + return true; + } + }, + new Option("-nodefontname", true, "Specify the font name to use inside nodes", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontName = arguments.get(0); + return true; + } + }, + new Option("-nodefontabstractitalic", false, "FIXME no documentation", null) { + @Override + public boolean process(String option, List arguments) { + nodeFontAbstractItalic = true; + return true; + } + }, + new Option("-nodefontsize", true, "Specify the font size to use inside nodes", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontSize = Double.parseDouble(arguments.get(0)); + return true; + } + }, + new Option("-nodefontclassname", true, "Specify the font name to use for the class names", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontClassName = arguments.get(0); + return true; + } + }, + new Option("-nodefontclasssize", true, "Specify the font size to use for the class names.", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontClassSize = Double.parseDouble(arguments.get(0)); + return true; + } + }, + new Option("-nodefonttagname", true, "Specify the font name to use for the tag names.", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontTagName = arguments.get(0); + return true; + } + }, + new Option("-nodefonttagsize", true, "Specify the font size to use for the tag names", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontTagSize = Double.parseDouble(arguments.get(0)); + return true; + } + }, + new Option("-nodefontpackagename", true, "Specify the font name to use for the package names (used only when the package name is postfixed, see -postfixpackage).", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontPackageName = arguments.get(0); + return true; + } + }, + new Option("-nodefontpackagesize", true, "Specify the font size to use for the package names (used only when it package name is postfixed, see -postfixpackage).", "") { + @Override + public boolean process(String option, List arguments) { + nodeFontPackageSize = Double.parseDouble(arguments.get(0)); + return true; + } + }, + new Option("-nodefillcolor", true, "Specify the color to use to fill the shapes", "") { + @Override + public boolean process(String option, List arguments) { + nodeFillColor = arguments.get(0); + return true; + } + }, + new Option("-shape", true, "Specify the shape to use for the rendered element(s).\n" + + " The following UML shapes are available: class (default), node," + + " component, package, collaboration, usecase, activeclass", "") { + @Override + public boolean process(String option, List arguments) { + shape = Shape.of(arguments.get(0)); + return true; + } + }, + new Option("-output", true, "Specify the output file (default graph.dot).\n" + + "If the output directory is provided, -output can only specify a file name,\n" + + "otherwise a full path is accepted as well.\n" + + "If the filename specified is a dash, then the results are printed on the\n" + + "standard output, and can be directly piped into dot.\n" + + "Note that, in order to avoid javadoc messages to contaminate\n" + + "UMLGraph's output, you must execute UMLGraph directly as a jar,\n" + + "not through javadoc.", "") { + @Override + public boolean process(String option, List arguments) { + outputFileName = arguments.get(0); + return true; + } + }, + new Option("-outputencoding", true, "Specify the output encoding character set (default UTF-8).\n" + + "When using dot to generate SVG diagrams you should specify\n" + + "UTF-8 as the output encoding, to have guillemots correctly\n" + + "appearing in the resulting SVG.", "") { + @Override + public boolean process(String option, List arguments) { + outputEncoding = arguments.get(0); + return true; + } + }, + new Option("--hide", true, "Specify entities to hide from the graph." + + " Matching is done using a non-anchored regular match." + + " For instance, \"-hide (Big|\\.)Widget\" would hide \"com.foo.widgets.Widget\" and " + + " \"com.foo.widgets.BigWidget\". Can also be used without arguments, " + + " in this case it will hide everything (useful in the context of views " + + " to selectively unhide some portions of the graph, see the view chapter for " + + " further details).", "") { + @Override + public boolean process(String option, List arguments) { + if (arguments == null || arguments.isEmpty()) { + hidePatterns.clear(); + hidePatterns.add(allPattern); + } else { + try { + hidePatterns.add(Pattern.compile(arguments.get(0))); + } catch (PatternSyntaxException e) { + System.err.println("Skipping invalid pattern " + arguments.get(0)); + } + } + return true; + } + }, + new Option("--include", true, "Match classes to include with a non-anchored match. This is weaker than\n" + + " the -hide option, but can be used to include classes from foreign packages\n" + + " in the package view (which would by default filter to only include package members).", "") { + @Override + public boolean process(String option, List arguments) { + try { + includePatterns.add(Pattern.compile(arguments.get(0))); + } catch (PatternSyntaxException e) { + System.err.println("Skipping invalid pattern " + arguments.get(0)); + } + return true; + } + }, + new Option("-apidocroot", true, "Specify the URL that should be used as the \"root\" for local classes.\n" + + "This URL will be used as a prefix, to which the page name for the local class or\n" + + "package will be appended (following the JavaDoc convention).\n" + + "For example, if the value http://www.acme.org/apidocs is\n" + + "provided, the class org.acme.util.MyClass will be mapped to the URL\n" + + "http://www.acme.org/apidocs/org/acme/util/MyClass.html.\n" + + "This URL will then be added to .dot diagram and can be surfaced in the\n" + + "final class diagram by setting the output to SVG, or by creating an HTML page\n" + + "that associates the diagram static image (a .gif or .png) with a client-side\n" + + "image map.", "") { + @Override + public boolean process(String option, List arguments) { + apiDocRoot = fixApiDocRoot(arguments.get(0)); + return true; + } + }, + new Option("-apidocmap", true, "Specify the file name of the URL mapping table. \n" + + "The is a standard Java property file, where the property name is a regular\n" + + "expression (as defined in the java.util.regex package) and the property value is\n" + + "an URL \"root\" as described above.\n" + + "This table is used to resolved external class names (class names that do not\n" + + "belong to the current package being processed by UMLGraph). If no file is provided,\n" + + "external classes will just be mapped to the on-line Java API documentation.", "") { + @Override + public boolean process(String option, List arguments) { + setApiDocMapFile(arguments.get(0)); + return true; + } + }, + new Option("-noguillemot", false, "Specify that guillemot characters should not be used to denote " + + "special terms like \"interface\" and stereotype names." + + "This is used on some platforms to circumvent problems associated with displaying non-ASCII characters.", null) { + @Override + public boolean process(String option, List arguments) { + guilOpen = "<<"; + guilClose = ">>"; + return true; + } + }, + new Option("-view", true, "Specify the fully qualified name of a class that contains\n" + + " a view definition. Only the class diagram specified by this view will be generated. \n" + + "
See the views chapter for more details.", "") { + @Override + public boolean process(String option, List arguments) { + viewName = arguments.get(0); + return true; + } + }, + new Option("-views", false, "Generate a class diagram for every view found in the source path..", null) { + @Override + public boolean process(String option, List arguments) { + findViews = true; + return true; + } + }, + new Option("-inferrel", false, "Try to automatically infer relationships between classes by inspecting " + + "field values. See the class diagram inference chapter for further details. Disabled by default.", null) { + @Override + public boolean process(String option, List arguments) { + inferRelationships = true; + return true; + } + }, + new Option("-inferreltype", true, "The type of relationship inferred when -inferrel is activated. \n" + + "Defaults to \"navassoc\" (see the class modelling chapter for a list of relationship types).", "") { + @Override + public boolean process(String option, List arguments) { + try { + inferRelationshipType = RelationType.valueOf(arguments.get(0).toUpperCase()); + } catch (IllegalArgumentException e) { + System.err.println("Unknown association type " + arguments.get(0)); + } + return true; + } + }, + new Option("-inferdepvis", true, "Specifies the lowest visibility level of elements used to infer\n" + + "dependencies among classes. Possible values are private, package, protected, public, in this\n" + + "order. The default value is private. Use higher levels to limit the number of inferred dependencies", "") { + @Override + public boolean process(String option, List arguments) { + try { + inferDependencyVisibility = Visibility.valueOf(arguments.get(0).toUpperCase()); + } catch (IllegalArgumentException e) { + System.err.println("Ignoring invalid visibility specification for " + "dependency inference: " + arguments.get(0)); + } + return true; + } + }, + new Option("-collapsible", false, "Enhance the javadoc HTML files containing UML diagrams with Javascript " + + "that provides a link for showing the (initially collapsed) diagrams.", null) { + @Override + public boolean process(String option, List arguments) { + collapsibleDiagrams = true; + return true; + } + }, + new Option("-inferdep", false, "Try to automatically infer dependencies between classes by inspecting " + + "methods and fields. See the class diagram inference chapter for more details. Disabled by default.", null) { + @Override + public boolean process(String option, List arguments) { + inferDependencies = true; + return true; + } + }, + new Option("-inferdepinpackage", false, "Enable or disable dependency inference among classes in the\n" + + "same package. This option is disabled by default, because classes in the same package are supposed\n" + + "to be related anyway, and also because there's no working mechanism to actually detect all\n" + + "of these dependencies since imports are not required to use classes in the same package.", null) { + @Override + public boolean process(String option, List arguments) { + inferDepInPackage = true; + return true; + } + }, + new Option("-hideprivateinner", false, "FIXME NO DOC", null) { + @Override + public boolean process(String option, List arguments) { + hidePrivateInner = true; + return true; + } + }, + new Option("-useimports", false, "Will also use imports to infer dependencies. \n" + + "Disabled by default, since it does not work properly if there are multiple\n" + + "classes in the same source file (will add dependencies to every class in\n" + + "the source file).", null) { + @Override + public boolean process(String option, List arguments) { + System.err.println("useimports has been set but will have no effect since not (yet) available"); + useImports = true; + return true; + } + }, + new Option("--collpackages", true, "Specify the classes that will be treated as " + + "containers for one to many relationships when inference is enabled. " + + "Matching is done using a non-anchored regular match. Empty by default.", "") { + @Override + public boolean process(String option, List arguments) { + try { + collPackages.add(Pattern.compile(arguments.get(0))); + } catch (PatternSyntaxException e) { + System.err.println("Skipping invalid pattern " + arguments.get(0)); + } + return true; + } + }, + new Option("-compact", false, "Generate compact dot files, that is, print HTML labels\n" + + "in a single line instead of \"pretty printing\" them. Useful if the dot file\n" + + "has to be manipulated by an automated tool\n" + + "(e.g., the UMLGraph regression test suite).", null) { + @Override + public boolean process(String option, List arguments) { + compact = true; + return true; + } + }, + new Option("-postfixpackage", false, "When using qualified class names, put the package name in the line after the class name, in order to reduce the width of class nodes.", null) { + @Override + public boolean process(String option, List arguments) { + postfixPackage = true; + return true; + } + }, + + new Option("-hideGenerics", false, "?", null) { + @Override + public boolean process(String option, List arguments) { + hideGenerics = true; + return true; + } + }, + new Option("--link", true, "A clone of the standard doclet\n" + + "-link\n" + + "option, allows UMLGraph to generate links from class symbols to their external javadoc\n" + + "documentation (image maps are automatically generated in UMLGraphDoc, you'll have to generate them\n" + + "manually with graphviz if using UMLGraph).", "") { + @Override + public boolean process(String option, List arguments) { + System.err.println("add -link option with args : " + arguments.get(0)); + addApiDocRoots(arguments.get(0)); + return true; + } + }, + new Option("-linkoffline", 2, "Specify\n" + + "links to javadoc-generated documentation for external referenced classes.\n" + + "The -linkoffline option takes two arguments:\n" + + "the first for the string to be embedded in the href\n" + + "links, the second telling it where to find the package-list.\n" + + "Example:\n" + + "

\n"
+                + "-linkoffline http://developer.android.com/reference file:/home/doc/android/\n"
+                + "
\n" + + "See the javadoc documentation for more details.", " ") { + @Override + public boolean process(String option, List arguments) { + addApiDocRootsOffline(arguments.get(0), arguments.get(1)); + return true; + } + }, + new Option("-contextPattern", 2, "FIXME MISSING DOC.", " ") { + @Override + public boolean process(String option, List arguments) { + RelationDirection d; + RelationType rt; + try { + d = RelationDirection.valueOf(arguments.get(1).toUpperCase()); + if ("all".equalsIgnoreCase(arguments.get(0))) { + contextRelationPattern = new RelationPattern(d); + } else { + rt = RelationType.valueOf(arguments.get(0).toUpperCase()); + contextRelationPattern.addRelation(rt, d); + } + } catch (IllegalArgumentException e) { + + } + return true; + } + }, + new Option("-nodesep", true, "Specify the horizontal separation between the class nodes (0.25 by default). Decreasing this can make a diagram more compact.", "") { + @Override + public boolean process(String option, List arguments) { + try { + nodeSep = Double.parseDouble(arguments.get(0)); + } catch (NumberFormatException e) { + System.err.println("Skipping invalid nodesep " + arguments.get(0)); + } + return true; + } + }, + new Option("-ranksep", true, "Specify the vertical separation between the class nodes (0.5 by default). Decreasing this can make a diagram more compact.", "") { + @Override + public boolean process(String option, List arguments) { + try { + rankSep = Double.parseDouble(arguments.get(0)); + } catch (NumberFormatException e) { + System.err.println("Skipping invalid ranksep " + arguments.get(0)); + } + return true; + } + }, + new Option("-dotexecutable", true, "Specify the path of the dot executable.", "") { + @Override + public boolean process(String option, List arguments) { + dotExecutable = arguments.get(0); + return true; + } + } + + + ); + // reused often, especially in UmlGraphDoc, worth creating just once and reusing private static final Pattern allPattern = Pattern.compile(".*"); protected static final String DEFAULT_EXTERNAL_APIDOC = "http://docs.oracle.com/javase/7/docs/api/"; // instance fields - List hidePatterns = new ArrayList(); - List includePatterns = new ArrayList(); + List hidePatterns = new ArrayList<>(); + List includePatterns = new ArrayList<>(); boolean showQualified = false; boolean showQualifiedGenerics = false; boolean hideGenerics = false; @@ -87,8 +632,8 @@ public class Options implements Cloneable, OptionProvider { Shape shape = Shape.CLASS; String bgColor = null; public String outputFileName = "graph.dot"; - String outputEncoding = "ISO-8859-1"; // TODO: default to UTF-8 now? - Map apiDocMap = new HashMap(); + String outputEncoding = StandardCharsets.UTF_8.name(); + Map apiDocMap = new HashMap<>(); String apiDocRoot = null; boolean postfixPackage = false; boolean useGuillemot = true; @@ -96,7 +641,7 @@ public class Options implements Cloneable, OptionProvider { String viewName = null; double nodeSep = 0.25; double rankSep = 0.5; - public String outputDirectory = null; + public String outputDirectory = "."; /* * Numeric values are preferable to symbolic here. Symbolic reportedly fail on * MacOSX, and also are more difficult to verify with XML tools. @@ -113,26 +658,23 @@ public class Options implements Cloneable, OptionProvider { Visibility inferDependencyVisibility = Visibility.PRIVATE; boolean inferDepInPackage = false; RelationType inferRelationshipType = RelationType.NAVASSOC; - private List collPackages = new ArrayList(); + private List collPackages = new ArrayList<>(); boolean compact = false; boolean hidePrivateInner = false; // internal option, used by UMLDoc to generate relative links between classes boolean relativeLinksForSourcePackages = false; // internal option, used by UMLDoc to force strict matching on the class names - // and avoid problems with packages in the template declaration making UmlGraph - // hide - // classes outside of them (for example, class gr.spinellis.Foo + // and avoid problems with packages in the template declaration making UmlGraph hide + // classes outside of them (for example, class gr.spinellis.Foo // would have been hidden by the hide pattern "java.*" - // TODO: consider making this standard behaviour - boolean strictMatching = false; + boolean strictMatching = true; String dotExecutable = "dot"; Options() { } @Override - public Object clone() { + public Options clone() { Options clone = null; try { clone = (Options) super.clone(); @@ -140,10 +682,10 @@ public class Options implements Cloneable, OptionProvider { throw new RuntimeException("Cannot clone?!?", e); // Should not happen } // deep clone the hide and collection patterns - clone.hidePatterns = new ArrayList(hidePatterns); - clone.includePatterns = new ArrayList(includePatterns); - clone.collPackages = new ArrayList(collPackages); - clone.apiDocMap = new HashMap(apiDocMap); + clone.hidePatterns = new ArrayList<>(hidePatterns); + clone.includePatterns = new ArrayList<>(includePatterns); + clone.collPackages = new ArrayList<>(collPackages); + clone.apiDocMap = new HashMap<>(apiDocMap); return clone; } @@ -159,7 +701,7 @@ public class Options implements Cloneable, OptionProvider { } /** - * Match strings, ignoring leading -, -!, and !. + * Match strings, ignoring leading -, --, -!, and !. * * @param given Given string * @param expect Expected string @@ -170,7 +712,7 @@ public class Options implements Cloneable, OptionProvider { } /** - * Match strings, ignoring leading -, -!, and !. + * Match strings, ignoring leading -, --, -!, and !. * * @param given Given string * @param expect Expected string @@ -179,10 +721,16 @@ public class Options implements Cloneable, OptionProvider { */ protected static boolean matchOption(String given, String expect, boolean negative) { int begin = 0, end = given.length(); - if (begin < end && given.charAt(begin) == '-') + if (begin < end && given.charAt(begin) == '-') { ++begin; - if (negative && begin < end && given.charAt(begin) == '!') + } + // double dashed option + if (begin < end && given.charAt(begin) == '-') { ++begin; + } + if (negative && begin < end && given.charAt(begin) == '!') { + ++begin; + } return expect.length() == end - begin && expect.regionMatches(0, given, begin, end - begin); } @@ -204,10 +752,10 @@ public class Options implements Cloneable, OptionProvider { || matchOption(option, "inferrel", true) || matchOption(option, "useimports", true) || matchOption(option, "collapsible", true) || matchOption(option, "inferdep", true) || matchOption(option, "inferdepinpackage", true) || matchOption(option, "hideprivateinner", true) - || matchOption(option, "compact", true)) + || matchOption(option, "compact", true)) { return 1; - else if (matchOption(option, "nodefillcolor") || matchOption(option, "nodefontcolor") + } else if (matchOption(option, "nodefillcolor") || matchOption(option, "nodefontcolor") || matchOption(option, "nodefontsize") || matchOption(option, "nodefontname") || matchOption(option, "nodefontclasssize") || matchOption(option, "nodefontclassname") || matchOption(option, "nodefonttagsize") || matchOption(option, "nodefonttagname") @@ -221,12 +769,13 @@ public class Options implements Cloneable, OptionProvider { || matchOption(option, "inferreltype") || matchOption(option, "inferdepvis") || matchOption(option, "collpackages") || matchOption(option, "nodesep") || matchOption(option, "ranksep") || matchOption(option, "dotexecutable") - || matchOption(option, "link")) + || matchOption(option, "link")) { return 2; - else if (matchOption(option, "contextPattern") || matchOption(option, "linkoffline")) + } else if (matchOption(option, "contextPattern") || matchOption(option, "linkoffline")) { return 3; - else + } else { return 0; + } } /** Set the options based on a single option and its arguments */ @@ -436,7 +985,7 @@ public class Options implements Cloneable, OptionProvider { BufferedReader br = null; packageListUrl = fixApiDocRoot(packageListUrl); try { - URL url = new URL(packageListUrl + "/package-list"); + URL url = new URL(packageListUrl + "package-list"); br = new BufferedReader(new InputStreamReader(url.openStream())); String line; while ((line = br.readLine()) != null) { @@ -447,11 +996,12 @@ public class Options implements Cloneable, OptionProvider { } catch (IOException e) { System.err.println("Errors happened while accessing the package-list file at " + packageListUrl); } finally { - if (br != null) + if (br != null) { try { br.close(); } catch (IOException e) { } + } } } @@ -468,7 +1018,7 @@ public class Options implements Cloneable, OptionProvider { BufferedReader br = null; packageListUrl = fixApiDocRoot(packageListUrl); try { - URL url = new URL(packageListUrl + "/package-list"); + URL url = new URL(packageListUrl + "package-list"); br = new BufferedReader(new InputStreamReader(url.openStream())); String line; while ((line = br.readLine()) != null) { @@ -479,11 +1029,12 @@ public class Options implements Cloneable, OptionProvider { } catch (IOException e) { System.err.println("Unable to access the package-list file at " + packageListUrl); } finally { - if (br != null) + if (br != null) { try { br.close(); } catch (IOException e) { } + } } } @@ -528,44 +1079,47 @@ public class Options implements Cloneable, OptionProvider { * the constructor of the api doc root, so it depends on the order of "-link" * and "-apiDocMap" parameters. */ - public String getApiDocRoot(String className) { - if (apiDocMap.isEmpty()) + public String getApiDocRoot(Name className) { + if (apiDocMap.isEmpty()) { apiDocMap.put(Pattern.compile(".*"), DEFAULT_EXTERNAL_APIDOC); + } for (Map.Entry mapEntry : apiDocMap.entrySet()) { - if (mapEntry.getKey().matcher(className).matches()) + if (mapEntry.getKey().matcher(className).matches()) { return mapEntry.getValue(); + } } return null; } /** Trim and append a file separator to the string */ private String fixApiDocRoot(String str) { - if (str == null) + if (str == null) { return null; + } String fixed = str.trim(); - if (fixed.isEmpty()) + if (fixed.isEmpty()) { return ""; - if (File.separatorChar != '/') + } + if (File.separatorChar != '/') { fixed = fixed.replace(File.separatorChar, '/'); - if (!fixed.endsWith("/")) + } + if (!fixed.endsWith("/")) { fixed = fixed + "/"; + } return fixed; } - /** Set the options based on the command line parameters */ - public void setOptions(String[][] options) { - for (String s[] : options) - setOption(s); - } - /** Set the options based on the tag elements of the ClassDoc parameter */ - public void setOptions(Doc p) { - if (p == null) + public void setOptions(DocTrees docTrees, Element p) { + if (p == null) { return; + } - for (Tag tag : p.tags("opt")) - setOption(StringUtil.tokenize(tag.text())); + List tags = TagUtil.getTag(docTrees, p, "opt"); + for (String tag : tags) { + setOption(StringUtil.tokenize(tag)); + } } /** @@ -574,15 +1128,17 @@ public class Options implements Cloneable, OptionProvider { * * @return true if the string matches. */ - public boolean matchesHideExpression(String s) { + public boolean matchesHideExpression(CharSequence s) { for (Pattern hidePattern : hidePatterns) { // micro-optimization because the "all pattern" is heavily used in UmlGraphDoc - if (hidePattern == allPattern) + if (hidePattern == allPattern) { return true; + } Matcher m = hidePattern.matcher(s); - if (strictMatching ? m.matches() : m.find()) + if (strictMatching ? m.matches() : m.find()) { return true; + } } return false; } @@ -593,11 +1149,12 @@ public class Options implements Cloneable, OptionProvider { * * @return true if the string matches. */ - public boolean matchesIncludeExpression(String s) { + public boolean matchesIncludeExpression(CharSequence s) { for (Pattern includePattern : includePatterns) { Matcher m = includePattern.matcher(s); - if (strictMatching ? m.matches() : m.find()) + if (strictMatching ? m.matches() : m.find()) { return true; + } } return false; } @@ -608,7 +1165,7 @@ public class Options implements Cloneable, OptionProvider { * * @return true if the string matches. */ - public boolean matchesCollPackageExpression(String s) { + public boolean matchesCollPackageExpression(CharSequence s) { for (Pattern collPattern : collPackages) { Matcher m = collPattern.matcher(s); if (strictMatching ? m.matches() : m.find()) @@ -621,13 +1178,13 @@ public class Options implements Cloneable, OptionProvider { // OptionProvider methods // ---------------------------------------------------------------- - public Options getOptionsFor(ClassDoc cd) { + public Options getOptionsFor(DocTrees docTrees, TypeElement cd) { Options localOpt = getGlobalOptions(); - localOpt.setOptions(cd); + localOpt.setOptions(docTrees, cd); return localOpt; } - public Options getOptionsFor(String name) { + public Options getOptionsFor(CharSequence name) { return getGlobalOptions(); } @@ -635,11 +1192,11 @@ public class Options implements Cloneable, OptionProvider { return (Options) clone(); } - public void overrideForClass(Options opt, ClassDoc cd) { + public void overrideForClass(Options opt, TypeElement cd) { // nothing to do } - public void overrideForClass(Options opt, String className) { + public void overrideForClass(Options opt, CharSequence className) { // nothing to do } diff --git a/src/main/java/org/umlgraph/doclet/PackageMatcher.java b/src/main/java/org/umlgraph/doclet/PackageMatcher.java index ecc3002..6a2d268 100644 --- a/src/main/java/org/umlgraph/doclet/PackageMatcher.java +++ b/src/main/java/org/umlgraph/doclet/PackageMatcher.java @@ -1,24 +1,32 @@ package org.umlgraph.doclet; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.PackageDoc; +import jdk.javadoc.doclet.DocletEnvironment; + +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import org.umlgraph.doclet.util.ElementUtil; public class PackageMatcher implements ClassMatcher { - protected PackageDoc packageDoc; + protected DocletEnvironment root; + protected PackageElement packageDoc; - public PackageMatcher(PackageDoc packageDoc) { - super(); + public PackageMatcher(DocletEnvironment root, PackageElement packageDoc) { + this.root = root; this.packageDoc = packageDoc; } - public boolean matches(ClassDoc cd) { - return cd.containingPackage().equals(packageDoc); + public boolean matches(TypeElement cd) { + return packageDoc.equals(ElementUtil.getPackageOf(root, cd)); } - public boolean matches(String name) { - for (ClassDoc cd : packageDoc.allClasses()) - if (cd.qualifiedName().equals(name)) + public boolean matches(CharSequence name) { + for (Element cd : packageDoc.getEnclosedElements()) { + if (cd instanceof TypeElement && ((TypeElement) cd).getQualifiedName().equals(name)) { return true; + } + } return false; } diff --git a/src/main/java/org/umlgraph/doclet/PackageView.java b/src/main/java/org/umlgraph/doclet/PackageView.java index 83af937..e528e8a 100755 --- a/src/main/java/org/umlgraph/doclet/PackageView.java +++ b/src/main/java/org/umlgraph/doclet/PackageView.java @@ -1,8 +1,14 @@ package org.umlgraph.doclet; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.PackageDoc; -import com.sun.javadoc.RootDoc; +import jdk.javadoc.doclet.DocletEnvironment; + +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import org.umlgraph.doclet.util.ElementUtil; + +import com.sun.source.util.DocTrees; /** * A view designed for UMLDoc, filters out everything that it's not contained in @@ -19,19 +25,23 @@ import com.sun.javadoc.RootDoc; public class PackageView implements OptionProvider { private static final String[] HIDE = new String[] { "hide" }; - private PackageDoc pd; + private PackageElement pd; private OptionProvider parent; private ClassMatcher matcher; private String outputPath; private Options opt; + private DocTrees docTrees; - public PackageView(String outputFolder, PackageDoc pd, RootDoc root, OptionProvider parent) { + public PackageView(String outputFolder, PackageElement pd, DocletEnvironment root, OptionProvider parent) { this.parent = parent; this.pd = pd; - this.matcher = new PackageMatcher(pd); + this.docTrees = root.getDocTrees(); + this.matcher = new PackageMatcher(root, pd); this.opt = parent.getGlobalOptions(); - this.opt.setOptions(pd); - this.outputPath = pd.name().replace('.', '/') + "/" + pd.name() + ".dot"; + this.opt.setOptions(docTrees, pd); + ModuleElement md = ElementUtil.getModuleOf(root, pd); + String pathPrefix = Runtime.version().major() > 10 && md != null ? md.getQualifiedName().toString() + "/" : ""; + this.outputPath = pathPrefix + pd.getQualifiedName().toString().replace('.', '/') + "/" + pd.getSimpleName().toString() + ".dot"; } public String getDisplayName() { @@ -47,36 +57,39 @@ public class PackageView implements OptionProvider { return go; } - public Options getOptionsFor(ClassDoc cd) { + public Options getOptionsFor(DocTrees dt, TypeElement cd) { Options go = parent.getGlobalOptions(); overrideForClass(go, cd); return go; } - public Options getOptionsFor(String name) { + public Options getOptionsFor(CharSequence name) { Options go = parent.getGlobalOptions(); overrideForClass(go, name); return go; } - public void overrideForClass(Options opt, ClassDoc cd) { - opt.setOptions(cd); + public void overrideForClass(Options opt, TypeElement cd) { + opt.setOptions(docTrees, cd); boolean inPackage = matcher.matches(cd); - if (inPackage) + if (inPackage) { opt.showQualified = false; - boolean included = inPackage || this.opt.matchesIncludeExpression(cd.qualifiedName()); - if (!included || this.opt.matchesHideExpression(cd.qualifiedName())) + } + boolean included = inPackage || this.opt.matchesIncludeExpression(cd.getQualifiedName()); + if (!included || this.opt.matchesHideExpression(cd.getQualifiedName())) { opt.setOption(HIDE); + } } - public void overrideForClass(Options opt, String className) { + public void overrideForClass(Options opt, CharSequence className) { opt.showQualified = false; boolean inPackage = matcher.matches(className); if (inPackage) opt.showQualified = false; boolean included = inPackage || this.opt.matchesIncludeExpression(className); - if (!included || this.opt.matchesHideExpression(className)) + if (!included || this.opt.matchesHideExpression(className)) { opt.setOption(HIDE); + } } } diff --git a/src/main/java/org/umlgraph/doclet/PatternMatcher.java b/src/main/java/org/umlgraph/doclet/PatternMatcher.java index a809db6..7e5c4b5 100644 --- a/src/main/java/org/umlgraph/doclet/PatternMatcher.java +++ b/src/main/java/org/umlgraph/doclet/PatternMatcher.java @@ -18,7 +18,7 @@ package org.umlgraph.doclet; import java.util.regex.Pattern; -import com.sun.javadoc.ClassDoc; +import javax.lang.model.element.TypeElement; /** * Matches classes performing a regular expression match on the qualified class @@ -33,11 +33,11 @@ public class PatternMatcher implements ClassMatcher { this.pattern = pattern; } - public boolean matches(ClassDoc cd) { - return matches(cd.toString()); + public boolean matches(TypeElement cd) { + return matches(cd.getQualifiedName()); } - public boolean matches(String name) { + public boolean matches(CharSequence name) { return pattern.matcher(name).matches(); } diff --git a/src/main/java/org/umlgraph/doclet/StringUtil.java b/src/main/java/org/umlgraph/doclet/StringUtil.java index 1aee85b..441e4d9 100644 --- a/src/main/java/org/umlgraph/doclet/StringUtil.java +++ b/src/main/java/org/umlgraph/doclet/StringUtil.java @@ -19,6 +19,9 @@ package org.umlgraph.doclet; +import javax.lang.model.element.Name; +import javax.lang.model.util.Elements; + import java.util.*; import java.util.regex.Pattern; @@ -148,10 +151,11 @@ class StringUtil { } /** Removes the template specs from a class name. */ - public static String removeTemplate(String name) { - int openIdx = name.indexOf('<'); - if (openIdx == -1) + public static Name removeTemplate(Elements elements, Name name) { + int openIdx = name.toString().indexOf('<'); + if (openIdx == -1) { return name; + } StringBuilder buf = new StringBuilder(name.length()); for (int i = 0, depth = 0; i < name.length(); i++) { char c = name.charAt(i); @@ -162,26 +166,29 @@ class StringUtil { else if (depth == 0) buf.append(c); } - return buf.toString(); + return elements.getName(buf.toString()); } - public static String buildRelativePathFromClassNames(String contextPackageName, String classPackageName) { + public static String buildRelativePathFromClassNames(Name contextPackageName, String classPackageName) { // path, relative to the root, of the destination class - String[] contextClassPath = contextPackageName.split("\\."); + String[] contextClassPath = contextPackageName.toString().split("\\."); String[] currClassPath = classPackageName.split("\\."); // compute relative path between the context and the destination // ... first, compute common part int i = 0, e = Math.min(contextClassPath.length, currClassPath.length); - while (i < e && contextClassPath[i].equals(currClassPath[i])) + while (i < e && contextClassPath[i].equals(currClassPath[i])) { i++; + } // ... go up with ".." to reach the common root StringBuilder buf = new StringBuilder(classPackageName.length()); - for (int j = i; j < contextClassPath.length; j++) + for (int j = i; j < contextClassPath.length; j++) { buf.append("../"); + } // ... go down from the common root to the destination - for (int j = i; j < currClassPath.length; j++) + for (int j = i; j < currClassPath.length; j++) { buf.append(currClassPath[j]).append('/'); // Always use HTML seperators + } return buf.toString(); } @@ -195,10 +202,11 @@ class StringUtil { * users adhere to Java conventions, of beginning package names with a lowercase * letter. * - * @param className + * @param name * @return Splitting point (Either referring to a dot, or -1) */ - public static int splitPackageClass(String className) { + public static int splitPackageClass(CharSequence name) { + String className = name.toString(); int gen = className.indexOf('<'); // Begin before generics. int end = gen >= 0 ? gen : className.length(); int start = className.lastIndexOf('.', end); diff --git a/src/main/java/org/umlgraph/doclet/SubclassMatcher.java b/src/main/java/org/umlgraph/doclet/SubclassMatcher.java index f9282c6..acbabb4 100644 --- a/src/main/java/org/umlgraph/doclet/SubclassMatcher.java +++ b/src/main/java/org/umlgraph/doclet/SubclassMatcher.java @@ -2,8 +2,11 @@ package org.umlgraph.doclet; import java.util.regex.Pattern; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.RootDoc; +import jdk.javadoc.doclet.DocletEnvironment; + +import javax.lang.model.element.TypeElement; + +import org.umlgraph.doclet.util.ElementUtil; /** * Matches every class that extends (directly or indirectly) a class matched by @@ -11,25 +14,27 @@ import com.sun.javadoc.RootDoc; */ public class SubclassMatcher implements ClassMatcher { - protected RootDoc root; + protected DocletEnvironment root; protected Pattern pattern; - public SubclassMatcher(RootDoc root, Pattern pattern) { + public SubclassMatcher(DocletEnvironment root, Pattern pattern) { this.root = root; this.pattern = pattern; } - public boolean matches(ClassDoc cd) { + public boolean matches(TypeElement cd) { // if it's the class we're looking for return - if (pattern.matcher(cd.toString()).matches()) + if (pattern.matcher(cd.toString()).matches()) { return true; + } - // recurse on supeclass, if available - return cd.superclass() == null ? false : matches(cd.superclass()); + // recurse on superclass, if available + TypeElement scd = ElementUtil.getTypeElement(cd.getSuperclass()); + return scd == null ? false : matches(scd); } - public boolean matches(String name) { - ClassDoc cd = root.classNamed(name); + public boolean matches(CharSequence name) { + TypeElement cd = root.getElementUtils().getTypeElement(name); return cd == null ? false : matches(cd); } diff --git a/src/main/java/org/umlgraph/doclet/TagScanner.java b/src/main/java/org/umlgraph/doclet/TagScanner.java new file mode 100644 index 0000000..420eca8 --- /dev/null +++ b/src/main/java/org/umlgraph/doclet/TagScanner.java @@ -0,0 +1,121 @@ +package org.umlgraph.doclet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.umlgraph.doclet.util.CommentHelper; + +import com.sun.source.doctree.*; +import com.sun.source.util.SimpleDocTreeVisitor; + +/** + * A visitor to gather the block tags found in a comment. + */ +public class TagScanner extends SimpleDocTreeVisitor { + + private final Map> tags; + + public TagScanner(Map> tags) { + this.tags = tags; + } + + @Override + public Void visitDocComment(DocCommentTree tree, Void p) { + if (tree == null) { + return null; + } + String body = CommentHelper.getText(tree.getFullBody()); + tags.put("comment", new ArrayList<>(List.of(body))); + return visit(tree.getBlockTags(), null); + } + + @Override + public Void visitAuthor(AuthorTree node, Void p) { + String name = node.getTagName(); + String content = node.getName().toString(); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitDeprecated(DeprecatedTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getBody()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitProvides(ProvidesTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getDescription()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitSince(SinceTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getBody()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitVersion(VersionTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getBody()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitParam(ParamTree node, Void p) { + String name = node.getTagName(); + String content = node.getName().toString() + " " + CommentHelper.getText(node.getDescription()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitReturn(ReturnTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getDescription()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitSee(SeeTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getReference()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitThrows(ThrowsTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getDescription()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + @Override + public Void visitUses(UsesTree node, Void p) { + String name = node.getTagName(); + String content = CommentHelper.getText(node.getDescription()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } + + + @Override + public Void visitUnknownBlockTag(UnknownBlockTagTree tree, Void p) { + String name = tree.getTagName(); + String content = CommentHelper.getText(tree.getContent()); + tags.computeIfAbsent(name, n -> new ArrayList<>()).add(content); + return null; + } +} \ No newline at end of file diff --git a/src/main/java/org/umlgraph/doclet/UmlGraph.java b/src/main/java/org/umlgraph/doclet/UmlGraph.java index 5bc08a0..f14d76b 100644 --- a/src/main/java/org/umlgraph/doclet/UmlGraph.java +++ b/src/main/java/org/umlgraph/doclet/UmlGraph.java @@ -20,14 +20,26 @@ package org.umlgraph.doclet; import java.io.IOException; -import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Set; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.Doc; -import com.sun.javadoc.LanguageVersion; -import com.sun.javadoc.RootDoc; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Reporter; +import jdk.javadoc.doclet.StandardDoclet; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; + +import org.umlgraph.doclet.util.ElementUtil; +import org.umlgraph.doclet.util.TagUtil; /** * Doclet API implementation @@ -41,7 +53,7 @@ import com.sun.javadoc.RootDoc; * @version $Revision$ * @author Diomidis Spinellis */ -public class UmlGraph { +public class UmlGraph implements Doclet { private static final String programName = "UmlGraph"; private static final String docletName = "org.umlgraph.doclet.UmlGraph"; @@ -49,25 +61,66 @@ public class UmlGraph { /** Options used for commenting nodes */ private static Options commentOptions; - /** Entry point through javadoc */ - public static boolean start(RootDoc root) throws IOException { - Options opt = buildOptions(root); - root.printNotice("UMLGraph doclet version " + Version.VERSION + " started"); - View[] views = buildViews(opt, root, root); - if (views == null) - return false; - if (views.length == 0) - buildGraph(root, opt, null); - else - for (int i = 0; i < views.length; i++) - buildGraph(root, views[i], null); - return true; + private Locale locale; + private Reporter reporter; + private Options options; + private StandardDoclet standard; + + @Override + public void init(Locale locale, Reporter reporter) { + this.locale = locale; + this.reporter = reporter; + this.options = new Options(); + this.standard = new StandardDoclet(); } - public static void main(String args[]) { - PrintWriter err = new PrintWriter(System.err); - com.sun.tools.javadoc.Main.execute(programName, err, err, err, docletName, args); + @Override + public String getName() { + return docletName; + } + + @Override + public Set getSupportedOptions() { + Set options = new HashSet<>(standard.getSupportedOptions()); + for (Doclet.Option opt : this.options.OPTIONS) { + options.add(opt); + } + return options; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return standard.getSupportedSourceVersion(); + } + + /** + * Standard doclet entry point + * + * @param root + * @return + */ + @Override + /** Entry point through javadoc */ + public boolean run(DocletEnvironment root) { + reporter.print(Diagnostic.Kind.NOTE, "UMLGraph doclet version " + Version.VERSION + " started"); + + List views = buildViews(options, root, root); + if (views == null) { + return false; + } + try { + if (views.isEmpty()) { + buildGraph(reporter, root, options, null, null); + } else { + for (View view : views) { + buildGraph(reporter, root, options, view, null); + } + } + } catch (IOException ioe) { + reporter.print(Diagnostic.Kind.ERROR, "UMLGraph doclet error : " + ioe); + } + return true; } public static Options getCommentOptions() { @@ -79,49 +132,62 @@ public class UmlGraph { * the command line and the ones specified in the UMLOptions class, if * available. Also create the globally accessible commentOptions object. */ - public static Options buildOptions(RootDoc root) { - commentOptions = new Options(); - commentOptions.setOptions(root.options()); - commentOptions.setOptions(findClass(root, "UMLNoteOptions")); + public static Options buildOptions(DocletEnvironment root, Options o) { + commentOptions = o.clone(); + commentOptions.setOptions(root.getDocTrees(), findClass(root, "UMLNoteOptions")); commentOptions.shape = Shape.NOTE; - Options opt = new Options(); - opt.setOptions(root.options()); - opt.setOptions(findClass(root, "UMLOptions")); + Options opt = o.clone(); + opt.setOptions(root.getDocTrees(), findClass(root, "UMLOptions")); return opt; } /** Return the ClassDoc for the specified class; null if not found. */ - private static ClassDoc findClass(RootDoc root, String name) { - ClassDoc[] classes = root.classes(); - for (ClassDoc cd : classes) - if (cd.name().equals(name)) - return cd; + private static TypeElement findClass(DocletEnvironment root, String name) { + Set classes = root.getIncludedElements(); + for (Element element : classes) { + if (element instanceof TypeElement && ((TypeElement) element).getQualifiedName().toString().equals(name)) { + return (TypeElement) element; + } + } return null; } /** * Builds and outputs a single graph according to the view overrides */ - public static void buildGraph(RootDoc root, OptionProvider op, Doc contextDoc) throws IOException { - if (getCommentOptions() == null) - buildOptions(root); + public static void buildGraph(Reporter reporter, DocletEnvironment root, Options options, OptionProvider op, Element contextDoc) throws IOException { + if (getCommentOptions() == null) { + buildOptions(root, options); + } Options opt = op.getGlobalOptions(); - root.printNotice("Building " + op.getDisplayName()); - ClassDoc[] classes = root.classes(); + reporter.print(Diagnostic.Kind.NOTE, "Building " + op.getDisplayName()); + Set elements = root.getIncludedElements(); + Set classes = new HashSet<>(); + for (Element element : elements) { + if (element instanceof TypeElement) { + classes.add((TypeElement) element); + } + } ClassGraph c = new ClassGraph(root, op, contextDoc); c.prologue(); - for (ClassDoc cd : classes) + for (TypeElement cd : classes) { c.printClass(cd, true); - for (ClassDoc cd : classes) + } + for (TypeElement cd : classes) { c.printRelations(cd); - if (opt.inferRelationships) - for (ClassDoc cd : classes) + } + if (opt.inferRelationships) { + for (TypeElement cd : classes) { c.printInferredRelations(cd); - if (opt.inferDependencies) - for (ClassDoc cd : classes) + } + } + if (opt.inferDependencies) { + for (TypeElement cd : classes) { c.printInferredDependencies(cd); + } + } c.printExtraClasses(root); c.epilogue(); @@ -135,54 +201,51 @@ public class UmlGraph { * @param viewRootDoc The RootDoc for the view classes (may be different, or may * be the same as the srcRootDoc) */ - public static View[] buildViews(Options opt, RootDoc srcRootDoc, RootDoc viewRootDoc) { + public static List buildViews(Options opt, DocletEnvironment srcRootDoc, DocletEnvironment viewRootDoc) { if (opt.viewName != null) { - ClassDoc viewClass = viewRootDoc.classNamed(opt.viewName); + TypeElement viewClass = findClass(viewRootDoc, opt.viewName); if (viewClass == null) { System.out.println("View " + opt.viewName + " not found! Exiting without generating any output."); return null; } - if (viewClass.tags("view").length == 0) { + if (TagUtil.getTag(viewRootDoc, viewClass, "view").isEmpty()) { System.out.println(viewClass + " is not a view!"); return null; } - if (viewClass.isAbstract()) { + if (viewClass.getModifiers().contains(Modifier.ABSTRACT)) { System.out.println(viewClass + " is an abstract view, no output will be generated!"); return null; } - return new View[] { buildView(srcRootDoc, viewClass, opt) }; + return List.of(buildView(srcRootDoc, viewClass, opt)); } else if (opt.findViews) { - List views = new ArrayList(); - ClassDoc[] classes = viewRootDoc.classes(); + List views = new ArrayList<>(); + Set classes = viewRootDoc.getIncludedElements(); // find view classes - for (int i = 0; i < classes.length; i++) - if (classes[i].tags("view").length > 0 && !classes[i].isAbstract()) - views.add(buildView(srcRootDoc, classes[i], opt)); + for (Element elmt : classes) { + if (!(elmt instanceof TypeElement)) { + continue; + } + TypeElement element = (TypeElement) elmt; + if (TagUtil.getTag(viewRootDoc, element, "view").size() > 0 && !element.getModifiers().contains(Modifier.ABSTRACT)) { + views.add(buildView(srcRootDoc, element, opt)); + } + } - return views.toArray(new View[views.size()]); + return views; } else - return new View[0]; + return Collections.emptyList(); } /** * Builds a view along with its parent views, recursively */ - private static View buildView(RootDoc root, ClassDoc viewClass, OptionProvider provider) { - ClassDoc superClass = viewClass.superclass(); - if (superClass == null || superClass.tags("view").length == 0) + private static View buildView(DocletEnvironment root, TypeElement viewClass, OptionProvider provider) { + TypeElement superClass = ElementUtil.getSuperclass(viewClass); + if (superClass == null || TagUtil.getTag(root, superClass, "view").isEmpty()) { return new View(root, viewClass, provider); + } return new View(root, viewClass, buildView(root, superClass, provider)); } - - /** Option checking */ - public static int optionLength(String option) { - return Options.optionLength(option); - } - - /** Indicate the language version we support */ - public static LanguageVersion languageVersion() { - return LanguageVersion.JAVA_1_5; - } } diff --git a/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java b/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java index a22c021..6a8f796 100644 --- a/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java +++ b/src/main/java/org/umlgraph/doclet/UmlGraphDoc.java @@ -3,22 +3,32 @@ package org.umlgraph.doclet; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.FileOutputStream; import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStreamReader; -import java.util.HashSet; -import java.util.TreeSet; +import java.io.OutputStreamWriter; import java.util.Comparator; +import java.util.HashSet; +import java.util.Locale; import java.util.Set; +import java.util.TreeSet; import java.util.regex.Pattern; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.LanguageVersion; -import com.sun.javadoc.PackageDoc; -import com.sun.javadoc.RootDoc; -import com.sun.tools.doclets.standard.Standard; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; + +import org.umlgraph.doclet.util.ElementUtil; + +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Reporter; +import jdk.javadoc.doclet.StandardDoclet; /** * Chaining doclet that runs the standart Javadoc doclet first, and on success, @@ -29,17 +39,42 @@ import com.sun.tools.doclets.standard.Standard; * @depend - - - WrappedClassDoc * @depend - - - WrappedRootDoc */ -public class UmlGraphDoc { - /** - * Option check, forwards options to the standard doclet, if that one refuses - * them, they are sent to UmlGraph - */ - public static int optionLength(String option) { - int result = Standard.optionLength(option); - if (result != 0) - return result; - else - return UmlGraph.optionLength(option); +public class UmlGraphDoc implements Doclet { + + private Locale locale; + private Reporter reporter; + private Options options; + private StandardDoclet standard; + + public UmlGraphDoc() { + this.options = new Options(); + this.standard = new StandardDoclet(); + } + + @Override + public void init(Locale locale, Reporter reporter) { + this.locale = locale; + this.reporter = reporter; + this.standard.init(locale, reporter); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + @Override + public Set getSupportedOptions() { + Set options = new HashSet<>(standard.getSupportedOptions()); + for (Doclet.Option opt : this.options.OPTIONS) { + options.add(opt); + } + return options; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return standard.getSupportedSourceVersion(); } /** @@ -48,56 +83,53 @@ public class UmlGraphDoc { * @param root * @return */ - public static boolean start(RootDoc root) { - root.printNotice("UmlGraphDoc version " + Version.VERSION + ", running the standard doclet"); - Standard.start(root); - root.printNotice("UmlGraphDoc version " + Version.VERSION + ", altering javadocs"); + @Override + public boolean run(DocletEnvironment root) { + reporter.print(Diagnostic.Kind.NOTE, "UmlGraphDoc version " + Version.VERSION + ", running the standard doclet"); + standard.run(root); + reporter.print(Diagnostic.Kind.NOTE, "UmlGraphDoc version " + Version.VERSION + ", altering javadocs"); try { - String outputFolder = findOutputPath(root.options()); - - Options opt = UmlGraph.buildOptions(root); - opt.setOptions(root.options()); + Options opt = UmlGraph.buildOptions(root, this.options); // in javadoc enumerations are always printed opt.showEnumerations = true; opt.relativeLinksForSourcePackages = true; // enable strict matching for hide expressions opt.strictMatching = true; -// root.printNotice(opt.toString()); + reporter.print(Diagnostic.Kind.NOTE, opt.toString()); - generatePackageDiagrams(root, opt, outputFolder); - generateContextDiagrams(root, opt, outputFolder); + generatePackageDiagrams(root, reporter, opt, this.options.outputDirectory); + generateContextDiagrams(root, reporter, opt, this.options.outputDirectory); } catch (Throwable t) { - root.printWarning("Error: " + t.toString()); + reporter.print(Diagnostic.Kind.WARNING, "Error: " + t.toString()); t.printStackTrace(); return false; } return true; } - /** - * Standand doclet entry - * - * @return - */ - public static LanguageVersion languageVersion() { - return Standard.languageVersion(); - } - /** * Generates the package diagrams for all of the packages that contain classes * among those returned by RootDoc.class() */ - private static void generatePackageDiagrams(RootDoc root, Options opt, String outputFolder) throws IOException { - Set packages = new HashSet(); - for (ClassDoc classDoc : root.classes()) { - PackageDoc packageDoc = classDoc.containingPackage(); - if (!packages.contains(packageDoc.name())) { - packages.add(packageDoc.name()); + private static void generatePackageDiagrams(DocletEnvironment root, Reporter reporter, Options opt, String outputFolder) throws IOException { + Set packages = new HashSet<>(); + for (Element classDoc : root.getIncludedElements()) { + PackageElement packageDoc = null; + if (classDoc instanceof PackageElement) { + packageDoc = (PackageElement) classDoc; + } else if (classDoc instanceof TypeElement) { + packageDoc = ElementUtil.getPackageOf(root, classDoc); + } else { + continue; + } + if (!packages.contains(packageDoc.getSimpleName())) { + reporter.print(Diagnostic.Kind.WARNING, "Package processed " + packageDoc.getQualifiedName()); + packages.add(packageDoc.getSimpleName()); OptionProvider view = new PackageView(outputFolder, packageDoc, root, opt); - UmlGraph.buildGraph(root, view, packageDoc); - runGraphviz(opt.dotExecutable, outputFolder, packageDoc.name(), packageDoc.name(), root); - alterHtmlDocs(opt, outputFolder, packageDoc.name(), packageDoc.name(), "package-summary.html", - Pattern.compile("()|(

)|(

classDocs = new TreeSet(new Comparator() { - public int compare(ClassDoc cd1, ClassDoc cd2) { - return cd1.name().compareTo(cd2.name()); + private static void generateContextDiagrams(DocletEnvironment root, Reporter reporter, Options opt, String outputFolder) throws IOException { + Set classDocs = new TreeSet<>(new Comparator<>() { + public int compare(TypeElement cd1, TypeElement cd2) { + return cd1.getSimpleName().toString().compareTo(cd2.getSimpleName().toString()); } }); - for (ClassDoc classDoc : root.classes()) - classDocs.add(classDoc); + for (Element classDoc : root.getIncludedElements()) { + if (classDoc instanceof TypeElement) { + classDocs.add((TypeElement) classDoc); + } + } ContextView view = null; - for (ClassDoc classDoc : classDocs) { + for (TypeElement classDoc : classDocs) { + reporter.print(Diagnostic.Kind.WARNING, "Class processed " + classDoc.getQualifiedName()); try { - if (view == null) + if (view == null) { view = new ContextView(outputFolder, classDoc, root, opt); - else + } else { view.setContextCenter(classDoc); - UmlGraph.buildGraph(root, view, classDoc); - runGraphviz(opt.dotExecutable, outputFolder, classDoc.containingPackage().name(), classDoc.name(), - root); - alterHtmlDocs(opt, outputFolder, classDoc.containingPackage().name(), classDoc.name(), - classDoc.name() + ".html", - Pattern.compile(".*(Class|Interface|Enum) " + classDoc.name() + ".*"), root); + } + UmlGraph.buildGraph(reporter, root, opt, view, classDoc); + runGraphviz(opt.dotExecutable, outputFolder, ElementUtil.getModuleOf(root, classDoc), ElementUtil.getPackageOf(root, classDoc).getQualifiedName(), classDoc.getSimpleName(), reporter); + alterHtmlDocs(opt, outputFolder, ElementUtil.getModuleOf(root, classDoc), ElementUtil.getPackageOf(root, classDoc).getQualifiedName(), classDoc.getSimpleName(), + classDoc.getSimpleName() + ".html", + Pattern.compile(".*(Class|Interface|Enum) " + classDoc.getSimpleName() + ".*"), reporter); } catch (Exception e) { - throw new RuntimeException("Error generating " + classDoc.name(), e); + throw new RuntimeException("Error generating " + classDoc.getSimpleName(), e); } } } @@ -137,24 +173,29 @@ public class UmlGraphDoc { * Runs Graphviz dot building both a diagram (in png format) and a client side * map for it. */ - private static void runGraphviz(String dotExecutable, String outputFolder, String packageName, String name, - RootDoc root) { + private static void runGraphviz(String dotExecutable, String outputFolder, ModuleElement module, Name packageName, Name name, Reporter reporter) { if (dotExecutable == null) { dotExecutable = "dot"; } - File dotFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".dot"); - File svgFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".svg"); + String fileName = packageName.toString().replace(".", "/") + "/" + name; + if (Runtime.version().major() > 10 && module != null) { + fileName = module.getQualifiedName().toString() + '/' + fileName; + } + File dotFile = new File(outputFolder, fileName + ".dot"); + File svgFile = new File(outputFolder, fileName + ".svg"); try { Process p = Runtime.getRuntime().exec(new String[] { dotExecutable, "-Tsvg", "-o", svgFile.getAbsolutePath(), dotFile.getAbsolutePath() }); BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream())); String line; - while ((line = reader.readLine()) != null) - root.printWarning(line); + while ((line = reader.readLine()) != null) { + reporter.print(Diagnostic.Kind.WARNING, line); + } int result = p.waitFor(); - if (result != 0) - root.printWarning("Errors running Graphviz on " + dotFile); + if (result != 0) { + reporter.print(Diagnostic.Kind.WARNING, "Errors running Graphviz on " + dotFile); + } } catch (Exception e) { e.printStackTrace(); System.err.println("Ensure that dot is in your path and that its path does not contain spaces"); @@ -186,10 +227,11 @@ public class UmlGraphDoc { * point, and inserts the diagram image reference and a client side map in that * point. */ - private static void alterHtmlDocs(Options opt, String outputFolder, String packageName, String className, - String htmlFileName, Pattern insertPointPattern, RootDoc root) throws IOException { + private static void alterHtmlDocs(Options opt, String outputFolder, ModuleElement module, Name packageName, Name className, + String htmlFileName, Pattern insertPointPattern, Reporter reporter) throws IOException { // setup files - File output = new File(outputFolder, packageName.replace(".", "/")); + String prefix = Runtime.version().major() > 10 && module != null ? module.getQualifiedName().toString() + '/' : ""; + File output = new File(outputFolder, prefix + packageName.toString().replace(".", "/")); File htmlFile = new File(output, htmlFileName); File alteredFile = new File(htmlFile.getAbsolutePath() + ".uml"); if (!htmlFile.exists()) { @@ -213,12 +255,14 @@ public class UmlGraphDoc { matched = true; String tag; - if (opt.autoSize) + if (opt.autoSize) { tag = String.format(UML_AUTO_SIZED_DIV_TAG, className); - else + } else { tag = String.format(UML_DIV_TAG, className); - if (opt.collapsibleDiagrams) + } + if (opt.collapsibleDiagrams) { tag = String.format(EXPANDABLE_UML, tag, "Show UML class diagram", "Hide UML class diagram"); + } writer.write(""); writer.newLine(); writer.write(tag); @@ -226,10 +270,12 @@ public class UmlGraphDoc { } } } finally { - if (writer != null) + if (writer != null) { writer.close(); - if (reader != null) + } + if (reader != null) { reader.close(); + } } // if altered, delete old file and rename new one to the old file name @@ -237,20 +283,9 @@ public class UmlGraphDoc { htmlFile.delete(); alteredFile.renameTo(htmlFile); } else { - root.printNotice("Warning, could not find a line that matches the pattern '" - + insertPointPattern.pattern() + "'.\n Class diagram reference not inserted"); + reporter.print(Diagnostic.Kind.NOTE, "Warning, could not find a line that matches the pattern '" + + insertPointPattern.pattern() + "'.\n Class diagram reference not inserted"); alteredFile.delete(); } } - - /** - * Returns the output path specified on the javadoc options - */ - private static String findOutputPath(String[][] options) { - for (int i = 0; i < options.length; i++) { - if (options[i][0].equals("-d")) - return options[i][1]; - } - return "."; - } } diff --git a/src/main/java/org/umlgraph/doclet/View.java b/src/main/java/org/umlgraph/doclet/View.java index f3b2dd4..b58ecbe 100644 --- a/src/main/java/org/umlgraph/doclet/View.java +++ b/src/main/java/org/umlgraph/doclet/View.java @@ -23,9 +23,12 @@ import java.util.Map; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.RootDoc; -import com.sun.javadoc.Tag; +import javax.lang.model.element.TypeElement; + +import org.umlgraph.doclet.util.TagUtil; + +import jdk.javadoc.doclet.DocletEnvironment; +import com.sun.source.util.DocTrees; /** * Contains the definition of a View. A View is a set of option overrides that @@ -44,32 +47,35 @@ import com.sun.javadoc.Tag; * */ public class View implements OptionProvider { - Map> optionOverrides = new LinkedHashMap>(); - ClassDoc viewDoc; + Map> optionOverrides = new LinkedHashMap<>(); + TypeElement viewDoc; OptionProvider provider; List globalOptions; - RootDoc root; + DocletEnvironment root; /** * Builds a view given the class that contains its definition */ - public View(RootDoc root, ClassDoc c, OptionProvider provider) { + public View(DocletEnvironment root, TypeElement c, OptionProvider provider) { this.viewDoc = c; this.provider = provider; this.root = root; - Tag[] tags = c.tags(); + Map> tags = TagUtil.getTags(root, viewDoc); ClassMatcher currMatcher = null; // parse options, get the global ones, and build a map of the // pattern matched overrides globalOptions = new ArrayList(); - for (int i = 0; i < tags.length; i++) { - if (tags[i].name().equals("@match")) { - currMatcher = buildMatcher(tags[i].text()); + if (tags.get("match") != null) { + for (String text : tags.get("match")) { + currMatcher = buildMatcher(text); if (currMatcher != null) { - optionOverrides.put(currMatcher, new ArrayList()); + optionOverrides.put(currMatcher, new ArrayList<>()); } - } else if (tags[i].name().equals("@opt")) { - String[] opts = StringUtil.tokenize(tags[i].text()); + } + } + if (tags.get("opt") != null) { + for (String text : tags.get("opt")) { + String[] opts = StringUtil.tokenize(text); opts[0] = "-" + opts[0]; if (currMatcher == null) { globalOptions.add(opts); @@ -118,14 +124,14 @@ public class View implements OptionProvider { // OptionProvider methods // ---------------------------------------------------------------- - public Options getOptionsFor(ClassDoc cd) { + public Options getOptionsFor(DocTrees dt, TypeElement cd) { Options localOpt = getGlobalOptions(); overrideForClass(localOpt, cd); - localOpt.setOptions(cd); + localOpt.setOptions(dt, cd); return localOpt; } - public Options getOptionsFor(String name) { + public Options getOptionsFor(CharSequence name) { Options localOpt = getGlobalOptions(); overrideForClass(localOpt, name); return localOpt; @@ -136,34 +142,42 @@ public class View implements OptionProvider { boolean outputSet = false; for (String[] opts : globalOptions) { - if (Options.matchOption(opts[0], "output")) + if (Options.matchOption(opts[0], "output")) { outputSet = true; + } go.setOption(opts); } - if (!outputSet) - go.setOption(new String[] { "output", viewDoc.name() + ".dot" }); + if (!outputSet) { + go.setOption(new String[] { "output", viewDoc.getSimpleName() + ".dot" }); + } return go; } - public void overrideForClass(Options opt, ClassDoc cd) { + public void overrideForClass(Options opt, TypeElement cd) { provider.overrideForClass(opt, cd); - for (ClassMatcher cm : optionOverrides.keySet()) - if (cm.matches(cd)) - for (String[] override : optionOverrides.get(cm)) + for (ClassMatcher cm : optionOverrides.keySet()) { + if (cm.matches(cd)) { + for (String[] override : optionOverrides.get(cm)) { opt.setOption(override); + } + } + } } - public void overrideForClass(Options opt, String className) { + public void overrideForClass(Options opt, CharSequence className) { provider.overrideForClass(opt, className); - for (ClassMatcher cm : optionOverrides.keySet()) - if (cm.matches(className)) - for (String[] override : optionOverrides.get(cm)) + for (ClassMatcher cm : optionOverrides.keySet()) { + if (cm.matches(className)) { + for (String[] override : optionOverrides.get(cm)) { opt.setOption(override); + } + } + } } public String getDisplayName() { - return "view " + viewDoc.name(); + return "view " + viewDoc.getSimpleName(); } } diff --git a/src/main/java/org/umlgraph/doclet/Visibility.java b/src/main/java/org/umlgraph/doclet/Visibility.java index 831d64b..5804de7 100644 --- a/src/main/java/org/umlgraph/doclet/Visibility.java +++ b/src/main/java/org/umlgraph/doclet/Visibility.java @@ -18,7 +18,10 @@ */ package org.umlgraph.doclet; -import com.sun.javadoc.ProgramElementDoc; +import java.util.Set; + +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; /** * Enumerates the possible visibilities in a Java program. For brevity, package @@ -35,14 +38,16 @@ public enum Visibility { this.symbol = symbol; } - public static Visibility get(ProgramElementDoc doc) { - if (doc.isPrivate()) + public static Visibility get(Element doc) { + Set mods = doc.getModifiers(); + if (mods.contains(Modifier.PRIVATE)) { return PRIVATE; - else if (doc.isPackagePrivate()) - return PACKAGE; - else if (doc.isProtected()) + } else if (mods.contains(Modifier.PROTECTED)) { return PROTECTED; - else + } else if (mods.contains(Modifier.PUBLIC)) { return PUBLIC; + } else { + return PACKAGE; + } } } diff --git a/src/main/java/org/umlgraph/doclet/util/CommentHelper.java b/src/main/java/org/umlgraph/doclet/util/CommentHelper.java new file mode 100644 index 0000000..a3ac18f --- /dev/null +++ b/src/main/java/org/umlgraph/doclet/util/CommentHelper.java @@ -0,0 +1,168 @@ +package org.umlgraph.doclet.util; + +import static com.sun.source.doctree.DocTree.Kind.CODE; + +import java.util.List; + +import com.sun.source.doctree.AttributeTree; +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.EndElementTree; +import com.sun.source.doctree.EntityTree; +import com.sun.source.doctree.LinkTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.ReferenceTree; +import com.sun.source.doctree.SeeTree; +import com.sun.source.doctree.SerialTree; +import com.sun.source.doctree.StartElementTree; +import com.sun.source.doctree.TextTree; +import com.sun.source.doctree.UnknownBlockTagTree; +import com.sun.source.doctree.ValueTree; +import com.sun.source.doctree.AttributeTree.ValueKind; +import com.sun.source.util.SimpleDocTreeVisitor; + +public class CommentHelper { + public static final String SPACER = " "; + + public static String getText(List list) { + StringBuilder sb = new StringBuilder(); + for (DocTree dt : list) { + sb.append(getText0(dt)); + } + return sb.toString(); + } + + public static String getText(DocTree dt) { + return getText0(dt).toString(); + } + + private static StringBuilder getText0(DocTree dt) { + final StringBuilder sb = new StringBuilder(); + new SimpleDocTreeVisitor() { + @Override + public Void visitAttribute(AttributeTree node, Void p) { + sb.append(SPACER).append(node.getName()); + if (node.getValueKind() == ValueKind.EMPTY) { + return null; + } + + sb.append("="); + String quote; + switch (node.getValueKind()) { + case DOUBLE: + quote = "\""; + break; + case SINGLE: + quote = "\'"; + break; + default: + quote = ""; + break; + } + sb.append(quote); + node.getValue().stream().forEach((dt) -> { + dt.accept(this, null); + }); + sb.append(quote); + return null; + } + + @Override + public Void visitEndElement(EndElementTree node, Void p) { + sb.append(""); + return null; + } + + @Override + public Void visitEntity(EntityTree node, Void p) { + sb.append(node.toString()); + return null; + } + + @Override + public Void visitLink(LinkTree node, Void p) { + if (node.getReference() == null) { + return null; + } + + node.getReference().accept(this, null); + node.getLabel().stream().forEach((dt) -> { + dt.accept(this, null); + }); + return null; + } + + @Override + public Void visitLiteral(LiteralTree node, Void p) { + if (node.getKind() == CODE) { + sb.append("<").append(node.getKind().tagName).append(">"); + } + sb.append(node.getBody().toString()); + if (node.getKind() == CODE) { + sb.append(""); + } + return null; + } + + @Override + public Void visitReference(ReferenceTree node, Void p) { + sb.append(node.getSignature()); + return null; + } + + @Override + public Void visitSee(SeeTree node, Void p) { + node.getReference().stream().forEach((dt) -> { + dt.accept(this, null); + }); + return null; + } + + @Override + public Void visitSerial(SerialTree node, Void p) { + node.getDescription().stream().forEach((dt) -> { + dt.accept(this, null); + }); + return null; + } + + @Override + public Void visitStartElement(StartElementTree node, Void p) { + sb.append("<"); + sb.append(node.getName()); + node.getAttributes().stream().forEach((dt) -> { + dt.accept(this, null); + }); + sb.append((node.isSelfClosing() ? "/>" : ">")); + return null; + } + + @Override + public Void visitText(TextTree node, Void p) { + sb.append(node.getBody()); + return null; + } + + @Override + public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) { + node.getContent().stream().forEach((dt) -> { + dt.accept(this, null); + }); + return null; + } + + @Override + public Void visitValue(ValueTree node, Void p) { + return node.getReference().accept(this, null); + } + + @Override + protected Void defaultAction(DocTree node, Void p) { + sb.append(node.toString()); + return null; + } + }.visit(dt, null); + return sb; + } +} diff --git a/src/main/java/org/umlgraph/doclet/util/ElementUtil.java b/src/main/java/org/umlgraph/doclet/util/ElementUtil.java new file mode 100644 index 0000000..e00c884 --- /dev/null +++ b/src/main/java/org/umlgraph/doclet/util/ElementUtil.java @@ -0,0 +1,159 @@ +package org.umlgraph.doclet.util; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +import jdk.javadoc.doclet.DocletEnvironment; + +public class ElementUtil { + + public static CharSequence getSimpleName(Types types, TypeMirror t) { + if (t instanceof ArrayType) { + return getSimpleName(types, ((ArrayType) t).getComponentType()) + "[]"; + } + if (t.getKind().isPrimitive()) { + return t.getKind().name().toLowerCase(); + } + Element element = types.asElement(t); + if (element != null) { + return element.getSimpleName(); + } + return ""; + } + + public static CharSequence getQualifiedName(Types types, TypeMirror t) { + if (t instanceof ArrayType) { + return getQualifiedName(types, ((ArrayType) t).getComponentType()) + "[]"; + } + if (t.getKind().isPrimitive()) { + return t.getKind().name().toLowerCase(); + } + Element element = types.asElement(t); + if (element instanceof TypeElement) { + return ((TypeElement) element).getQualifiedName(); + } + return ""; + } + + public static TypeElement containingTypeElement(Element c) { + if (c instanceof VariableElement) { + Element enclosing = c.getEnclosingElement(); + return enclosing instanceof TypeElement ? (TypeElement) enclosing : null; + } else if (c instanceof ExecutableElement) { + Element enclosing = c.getEnclosingElement(); + return enclosing instanceof TypeElement ? (TypeElement) enclosing : null; + } + if (c == null) { + System.err.println("containingElement will return null for null element"); + return null; + } + System.err.println("containingElement will return null for kind " + c.getKind() + " class " + c.getClass() + " name " + c.getSimpleName()); + return null; + } + + public static TypeElement getTypeElement(TypeMirror clazz) { + if (clazz == null || clazz.getKind() == TypeKind.NONE) { + return null; + } + if (clazz instanceof ArrayType) { + return getTypeElement(((ArrayType) clazz).getComponentType()); + } + if (!(clazz instanceof DeclaredType)) { + return null; + } + Element scd = ((DeclaredType) clazz).asElement(); + return scd instanceof TypeElement ? (TypeElement) scd : null; + } + + public static TypeMirror getSuperclassType(DeclaredType pt) { + return pt.asElement() instanceof TypeElement ? ((TypeElement) pt.asElement()).getSuperclass() : null; + } + + public static TypeElement getSuperclass(TypeElement element) { + return getTypeElement(element.getSuperclass()); + } + + public static List getInterfacesTypes(TypeElement element) { + return element.getInterfaces(); + } + + public static List getInterfaces(TypeElement element) { + List interfaces = getInterfacesTypes(element); + return interfaces.stream().map(i -> getTypeElement(i)).filter(i -> i != null).collect(Collectors.toList()); + } + + public static ModuleElement getModuleOf(DocletEnvironment root, Element element) { + return getModuleOf(root.getElementUtils(), element); + } + + public static ModuleElement getModuleOf(Elements elements, Element element) { + if (element == null) { + return null; + } + if (element instanceof ModuleElement) { + return (ModuleElement) element; + } + return elements.getModuleOf(element); + } + + public static PackageElement getPackageOf(DocletEnvironment root, Element element) { + return getPackageOf(root.getElementUtils(), element); + } + + public static PackageElement getPackageOf(Elements elements, Element element) { + if (element == null) { + return null; + } + if (element instanceof PackageElement) { + return (PackageElement) element; + } + return elements.getPackageOf(element); + } + + public static List getFields(TypeElement element) { + List enclosed = element.getEnclosedElements(); + return ElementFilter.fieldsIn(enclosed).stream().filter(v -> v.getKind() == ElementKind.FIELD).collect(Collectors.toList()); + } + + public static List getEnumConstants(TypeElement element) { + List enclosed = element.getEnclosedElements(); + return ElementFilter.fieldsIn(enclosed).stream().filter(v -> v.getKind() == ElementKind.ENUM_CONSTANT).collect(Collectors.toList()); + } + + public static List getMethods(TypeElement element) { + List enclosed = element.getEnclosedElements(); + return ElementFilter.methodsIn(enclosed); + } + + public static List getConstructors(TypeElement element) { + List enclosed = element.getEnclosedElements(); + return ElementFilter.constructorsIn(enclosed); + } + + public static String dimensions(TypeMirror type) { + StringBuilder sb = new StringBuilder(); + + // First append root component type + TypeMirror t = type; + while (t.getKind() == TypeKind.ARRAY) { + sb.append("[]"); + t = ((ArrayType) t).getComponentType(); + } + return sb.toString(); + } +} diff --git a/src/main/java/org/umlgraph/doclet/util/TagUtil.java b/src/main/java/org/umlgraph/doclet/util/TagUtil.java new file mode 100644 index 0000000..e1c1c02 --- /dev/null +++ b/src/main/java/org/umlgraph/doclet/util/TagUtil.java @@ -0,0 +1,50 @@ +package org.umlgraph.doclet.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.Element; + +import org.umlgraph.doclet.TagScanner; + +import com.sun.source.doctree.DocCommentTree; +import com.sun.source.util.DocTrees; + +import jdk.javadoc.doclet.DocletEnvironment; + +public class TagUtil { + + public static Map> getTags(DocletEnvironment root, Element c) { + return getTags(root.getDocTrees(), c); + } + + public static Map> getTags(DocTrees docTrees, Element c) { + DocCommentTree tree = docTrees.getDocCommentTree(c); + Map> tagsByName = new HashMap<>(); + new TagScanner(tagsByName).visitDocComment(tree, null); + return tagsByName; + } + + public static String getComment(DocletEnvironment root, Element c) { + return getComment(root.getDocTrees(), c); + } + + public static String getComment(DocTrees docTrees, Element c) { + List comments = getTag(docTrees, c, "comment"); + if (comments == null || comments.isEmpty()) { + return ""; + } + return comments.get(0); + } + + public static List getTag(DocletEnvironment root, Element c, String tagName) { + return getTag(root.getDocTrees(), c, tagName); + } + + public static List getTag(DocTrees docTrees, Element c, String tagName) { + List tags = getTags(docTrees, c).get(tagName); + return tags == null ? Collections.emptyList() : tags; + } +} From d273b6a3efe82bf19b571050840cdb7fa97c952b Mon Sep 17 00:00:00 2001 From: Laurent SCHOELENS Date: Fri, 24 Mar 2023 16:11:29 +0100 Subject: [PATCH 4/4] fix junit dep / jdk11+ build --- pom.xml | 10 ++++++ .../java/org/umlgraph/doclet/Options.java | 9 +---- .../java/org/umlgraph/doclet/UmlGraph.java | 10 ++++-- .../java/org/umlgraph/test/BasicTest.java | 32 +++++++++++------ src/test/java/org/umlgraph/test/RunDoc.java | 13 +++++-- src/test/java/org/umlgraph/test/RunOne.java | 12 +++++-- .../java/org/umlgraph/test/UmlDocTest.java | 36 ++++++++++++------- 7 files changed, 83 insertions(+), 39 deletions(-) diff --git a/pom.xml b/pom.xml index f87b650..7a9184d 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,15 @@ oss-parent 9 + + + + junit + junit + 4.13.2 + test + + @@ -132,6 +141,7 @@ + false true diff --git a/src/main/java/org/umlgraph/doclet/Options.java b/src/main/java/org/umlgraph/doclet/Options.java index 7b5cc98..f5b4175 100644 --- a/src/main/java/org/umlgraph/doclet/Options.java +++ b/src/main/java/org/umlgraph/doclet/Options.java @@ -285,7 +285,7 @@ public class Options implements Cloneable, OptionProvider { return true; } }, - new Option("-output", true, "Specify the output file (default graph.dot).\n" + 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" @@ -507,13 +507,6 @@ public class Options implements Cloneable, OptionProvider { } }, - new Option("-hideGenerics", false, "?", null) { - @Override - public boolean process(String option, List arguments) { - hideGenerics = true; - return true; - } - }, new Option("--link", true, "A clone of the standard doclet\n" + "-link\n" + "option, allows UMLGraph to generate links from class symbols to their external javadoc\n" diff --git a/src/main/java/org/umlgraph/doclet/UmlGraph.java b/src/main/java/org/umlgraph/doclet/UmlGraph.java index f14d76b..dba68e5 100644 --- a/src/main/java/org/umlgraph/doclet/UmlGraph.java +++ b/src/main/java/org/umlgraph/doclet/UmlGraph.java @@ -66,13 +66,16 @@ public class UmlGraph implements Doclet { private Reporter reporter; private Options options; private StandardDoclet standard; + + public UmlGraph() { + this.options = new Options(); + this.standard = new StandardDoclet(); + } @Override public void init(Locale locale, Reporter reporter) { this.locale = locale; this.reporter = reporter; - this.options = new Options(); - this.standard = new StandardDoclet(); } @Override @@ -233,8 +236,9 @@ public class UmlGraph implements Doclet { } return views; - } else + } else { return Collections.emptyList(); + } } /** diff --git a/src/test/java/org/umlgraph/test/BasicTest.java b/src/test/java/org/umlgraph/test/BasicTest.java index 28f1cad..4da8d5c 100644 --- a/src/test/java/org/umlgraph/test/BasicTest.java +++ b/src/test/java/org/umlgraph/test/BasicTest.java @@ -26,6 +26,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.tools.DocumentationTool; +import javax.tools.ToolProvider; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.umlgraph.doclet.UmlGraph; + /** * UmlGraph regression tests * @author wolf @@ -41,12 +49,15 @@ public class BasicTest { static PrintWriter pw = new PrintWriter(System.out); - public static void main(String[] args) throws IOException { + @Test + @Ignore + public void test() throws IOException { List differences = new ArrayList(); File outFolder = new File(testDestFolder); - if (!outFolder.exists()) + if (!outFolder.exists()) { outFolder.mkdirs(); + } TestUtils.cleanFolder(outFolder, true); @@ -64,14 +75,14 @@ public class BasicTest { pw.println(); pw.println(); pw.flush(); - System.exit(differences.size() > 0 ? 1 : 0); + Assert.assertEquals("Some differences found " + differences.size(), 0, differences.size()); } private static void performViewTests(List differences, File outFolder) throws IOException { - String[] options = new String[] { "-docletpath", "build", "-private", "-d", - outFolder.getAbsolutePath(), "-sourcepath", "testdata/java", "-compact", - "-subpackages", "gr.spinellis", "-views" }; + String[] options = new String[] { "--docletpath=build", "-private", + "--d=\"" + outFolder.getAbsolutePath() + "\"", "--sourcepath=\"testdata/java\"", "-compact", + "--subpackages=\"gr.spinellis\"", "-views" }; runDoclet(options); List viewFiles = new ArrayList(); @@ -118,8 +129,8 @@ public class BasicTest { dotFile.delete(); File refFile = new File(testRefFolder, outFileName); String javaPath = new File(testSourceFolder, javaFiles[i]).getAbsolutePath(); - String[] options = new String[] { "-docletpath", "build", "-hide", "Hidden", - "-compact", "-private", "-d", testDestFolder, "-output", outFileName, javaPath }; + String[] options = new String[] { "--docletpath=build", "--hide=\"Hidden\"", + "-compact", "-private", "--d=\"" + testDestFolder + "\"", "--output=\"" + outFileName + "\"", javaPath }; runDoclet(options); compare(differences, dotFile, refFile); @@ -128,8 +139,9 @@ public class BasicTest { } private static void runDoclet(String[] options) { - com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw, - "org.umlgraph.doclet.UmlGraph", options); + DocumentationTool systemDocumentationTool = ToolProvider.getSystemDocumentationTool(); + DocumentationTool.DocumentationTask task = systemDocumentationTool.getTask(pw, null, null, UmlGraph.class, Arrays.asList(options), null); + task.call(); } private static void compare(List differences, File dotFile, File refFile) diff --git a/src/test/java/org/umlgraph/test/RunDoc.java b/src/test/java/org/umlgraph/test/RunDoc.java index 3a7f525..0431732 100644 --- a/src/test/java/org/umlgraph/test/RunDoc.java +++ b/src/test/java/org/umlgraph/test/RunDoc.java @@ -21,6 +21,12 @@ package org.umlgraph.test; import java.io.File; import java.io.PrintWriter; +import java.util.Arrays; + +import javax.tools.DocumentationTool; +import javax.tools.ToolProvider; + +import org.umlgraph.doclet.UmlGraphDoc; public class RunDoc { @@ -34,14 +40,15 @@ public class RunDoc { File outFolder = new File(docFolder); if (!outFolder.exists()) outFolder.mkdirs(); - String[] options = new String[] { "-docletpath", "build", "-private", "-d", docFolder, + String[] options = new String[] { "-docletpath", "build", "-private", "--d", docFolder, "-sourcepath", sourcesFolder, "-subpackages", "gr.spinellis" }; runDoclet(options); } private static void runDoclet(String[] options) { - com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw, - "org.umlgraph.doclet.UmlGraphDoc", options); + DocumentationTool systemDocumentationTool = ToolProvider.getSystemDocumentationTool(); + DocumentationTool.DocumentationTask task = systemDocumentationTool.getTask(pw, null, null, UmlGraphDoc.class, Arrays.asList(options), null); + task.call(); } } diff --git a/src/test/java/org/umlgraph/test/RunOne.java b/src/test/java/org/umlgraph/test/RunOne.java index e2f7b8c..7e98f1b 100644 --- a/src/test/java/org/umlgraph/test/RunOne.java +++ b/src/test/java/org/umlgraph/test/RunOne.java @@ -21,6 +21,12 @@ package org.umlgraph.test; import java.io.File; import java.io.PrintWriter; +import java.util.Arrays; + +import javax.tools.DocumentationTool; +import javax.tools.ToolProvider; + +import org.umlgraph.doclet.UmlGraph; public class RunOne { @@ -53,8 +59,8 @@ public class RunOne { } private static void runDoclet(String[] options) { - com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw, "org.umlgraph.doclet.UmlGraph", options); + DocumentationTool systemDocumentationTool = ToolProvider.getSystemDocumentationTool(); + DocumentationTool.DocumentationTask task = systemDocumentationTool.getTask(pw, null, null, UmlGraph.class, Arrays.asList(options), null); + task.call(); } - - } diff --git a/src/test/java/org/umlgraph/test/UmlDocTest.java b/src/test/java/org/umlgraph/test/UmlDocTest.java index d470b4f..c185efc 100644 --- a/src/test/java/org/umlgraph/test/UmlDocTest.java +++ b/src/test/java/org/umlgraph/test/UmlDocTest.java @@ -25,6 +25,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.tools.DocumentationTool; +import javax.tools.ToolProvider; + +import org.junit.Ignore; +import org.junit.Test; +import org.umlgraph.doclet.UmlGraphDoc; + /** * UmlGraphDoc doclet regression tests * @author wolf @@ -41,7 +48,9 @@ public class UmlDocTest { static PrintWriter pw = new PrintWriter(System.out); - public static void main(String[] args) throws IOException { + @Test + @Ignore + public void test() throws IOException { List differences = new ArrayList(); File outFolder = new File(testDestFolder); @@ -68,10 +77,11 @@ public class UmlDocTest { private static void runTest(List differences) throws IOException { File outFolder = new File(testDestFolder); - String[] options = new String[] { "-docletpath", "build", "-private", "-d", - outFolder.getAbsolutePath(), "-sourcepath", testSourceFolder, "-compact", + File srcFolder = new File(testSourceFolder); + String[] options = new String[] { "-private", "--d=\"" + outFolder.getAbsolutePath() + "\"", + "--source-path=\"" + srcFolder.getAbsolutePath() + "\"", "-compact", "-subpackages", "gr.spinellis", "-inferrel", "-inferdep", "-qualify", - "-postfixpackage", "-collpackages", "java.util.*" }; + "-postfixpackage", "--collpackages=\"java.util.*\"" }; runDoclet(options); compareDocletOutputs(differences, new File(testRefFolder), new File(testDestFolder)); @@ -119,8 +129,9 @@ public class UmlDocTest { differences.add(out.getName() + " is different from the reference"); } else { - if (!TestUtils.textFilesEquals(pw, ref, out)) + if (!TestUtils.textFilesEquals(pw, ref, out)) { differences.add(out.getName() + " is different from the reference"); + } } i++; j++; @@ -146,13 +157,14 @@ public class UmlDocTest { * @param options */ private static void runDoclet(String[] options) { - pw.print("Run javadoc -doclet " + doclet); - for (String o : options) - pw.print(" " + o); - pw.println(); - com.sun.tools.javadoc.Main.execute("UMLDoc test", pw, pw, pw, - doclet, options); - System.exit(0); + pw.print("Run javadoc -doclet " + doclet); + for (String o : options) { + pw.print(" " + o); + } + pw.println(); + DocumentationTool systemDocumentationTool = ToolProvider.getSystemDocumentationTool(); + DocumentationTool.DocumentationTask task = systemDocumentationTool.getTask(pw, null, null, UmlGraphDoc.class, Arrays.asList(options), null); + task.call(); } }