From dc18c503e924d4b9c41b6de3b073b44fcb910907 Mon Sep 17 00:00:00 2001 From: Erich Schubert Date: Fri, 26 Oct 2018 00:45:32 +0200 Subject: [PATCH] Improve class info handling. --- .../java/org/umlgraph/doclet/ClassGraph.java | 266 ++++++++---------- .../java/org/umlgraph/doclet/ClassInfo.java | 3 +- .../org/umlgraph/doclet/ContextMatcher.java | 11 +- .../java/org/umlgraph/doclet/ContextView.java | 4 +- 4 files changed, 127 insertions(+), 157 deletions(-) diff --git a/src/main/java/org/umlgraph/doclet/ClassGraph.java b/src/main/java/org/umlgraph/doclet/ClassGraph.java index ec1865a..ce6b9e0 100644 --- a/src/main/java/org/umlgraph/doclet/ClassGraph.java +++ b/src/main/java/org/umlgraph/doclet/ClassGraph.java @@ -312,26 +312,17 @@ class ClassGraph { /** Return true if c has a @hidden tag associated with it */ private boolean hidden(ProgramElementDoc c) { - Tag tags[] = c.tags("hidden"); - if (tags.length > 0) - return true; - tags = c.tags("view"); - if (tags.length > 0) - return true; - Options opt; - if(c instanceof ClassDoc) - opt = optionProvider.getOptionsFor((ClassDoc) c); - else - opt = optionProvider.getOptionsFor(c.containingClass()); - return opt.matchesHideExpression(c.toString()); + return c.tags("hidden").length > 0 || c.tags("view").length > 0 || // + optionProvider.getOptionsFor(c instanceof ClassDoc ? (ClassDoc) c : c.containingClass()) // + .matchesHideExpression(c.toString()); } - + protected ClassInfo getClassInfo(String className) { return classnames.get(removeTemplate(className)); } - - private ClassInfo newClassInfo(String className, boolean printed, boolean hidden) { - ClassInfo ci = new ClassInfo(printed, hidden); + + private ClassInfo newClassInfo(String className, boolean hidden) { + ClassInfo ci = new ClassInfo(hidden); classnames.put(removeTemplate(className), ci); return ci; } @@ -352,139 +343,121 @@ class ClassGraph { * relative links in diagrams for UMLDoc */ public String printClass(ClassDoc c, boolean rootClass) { - ClassInfo ci; - boolean toPrint; - Options opt = optionProvider.getOptionsFor(c); - String className = c.toString(); - if ((ci = getClassInfo(className)) != null) - toPrint = !ci.nodePrinted; - else { - toPrint = true; - ci = newClassInfo(className, true, hidden(c)); - } - if (toPrint && !hidden(c) && (!c.isEnum() || opt.showEnumerations)) { - // Associate classname's alias - String r = className; - w.println("\t// " + r); - // Create label - w.print("\t" + ci.name + " [label="); + ClassInfo ci = getClassInfo(className); + ci = ci != null ? ci : newClassInfo(className, hidden(c)); + if(ci.nodePrinted || ci.hidden) + return ci.name; + Options opt = optionProvider.getOptionsFor(c); + if (c.isEnum() && !opt.showEnumerations) + return ci.name; + // Associate classname's alias + w.println("\t// " + className); + // Create label + w.print("\t" + ci.name + " [label="); - boolean showMembers = + 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); - - externalTableStart(opt, c.qualifiedName(), classToUrl(c, rootClass)); - 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, r); - int startTemplate = qualifiedName.indexOf('<'); - int idx = qualifiedName.lastIndexOf('.', startTemplate < 0 ? qualifiedName.length() - 1 : startTemplate); - 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); - - 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()); + externalTableStart(opt, c.qualifiedName(), classToUrl(c, rootClass)); - if (!printedLines) - // if there are no operations nor constructors, - // print an empty line to generate proper HTML - tableLine(Align.LEFT, ""); - - innerTableEnd(); - } - } - externalTableEnd(); - w.print(", URL=\"" + classToUrl(c, rootClass) + "\""); - 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("\t// Note annotation\n"); - w.print("\t" + noteName + " [label="); - externalTableStart(UmlGraph.getCommentOptions(), c.qualifiedName(), classToUrl(c, rootClass)); - innerTableStart(); - tableLine(Align.LEFT, Font.CLASS.wrap(UmlGraph.getCommentOptions(), htmlNewline(escape(t.text())))); - innerTableEnd(); - externalTableEnd(); - nodeProperties(UmlGraph.getCommentOptions()); - w.print("\t" + noteName + " -> " + relationNode(c) + "[arrowhead=none];\n"); - ni++; - } - ci.nodePrinted = true; + 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 startTemplate = qualifiedName.indexOf('<'); + int idx = qualifiedName.lastIndexOf('.', startTemplate < 0 ? qualifiedName.length() - 1 : startTemplate); + 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))); } - return ci.name; - } + tagvalue(opt, c); + firstInnerTableEnd(opt); - private String getNodeName(ClassDoc c) { - String className = c.toString(); - ClassInfo ci = getClassInfo(className); - if (ci == null) - ci = newClassInfo(className, false, hidden(c)); - return ci.name; - } - - /** Return a class's internal name */ - private String getNodeName(String c) { - ClassInfo ci = getClassInfo(c); + /* + * 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 (ci == null) - ci = newClassInfo(c, false, false); + if (!printedLines) + // if there are no operations nor constructors, + // print an empty line to generate proper HTML + tableLine(Align.LEFT, ""); + + innerTableEnd(); + } + } + externalTableEnd(); + w.print(", URL=\"" + classToUrl(c, rootClass) + "\""); + 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("\t// Note annotation\n"); + w.print("\t" + noteName + " [label="); + externalTableStart(UmlGraph.getCommentOptions(), c.qualifiedName(), classToUrl(c, rootClass)); + innerTableStart(); + tableLine(Align.LEFT, Font.CLASS.wrap(UmlGraph.getCommentOptions(), htmlNewline(escape(t.text())))); + innerTableEnd(); + externalTableEnd(); + nodeProperties(UmlGraph.getCommentOptions()); + w.print("\t" + noteName + " -> " + relationNode(c) + "[arrowhead=none];\n"); + ni++; + } + ci.nodePrinted = true; return ci.name; } @@ -564,7 +537,10 @@ class ClassGraph { * whose outline is rendered through an inner table. */ private String relationNode(ClassDoc c) { - return getNodeName(c) + optionProvider.getOptionsFor(c).shape.landingPort(); + String className = c.toString(); + ClassInfo ci = getClassInfo(className); + return (ci != null ? ci : newClassInfo(className, hidden(c))).name // + + optionProvider.getOptionsFor(c).shape.landingPort(); } /** Return the full name of a relation's node c. @@ -575,7 +551,9 @@ class ClassGraph { */ private String relationNode(ClassDoc c, String cName) { Options opt = c == null ? optionProvider.getOptionsFor(cName) : optionProvider.getOptionsFor(c); - return getNodeName(cName) + opt.shape.landingPort(); + ClassInfo ci = getClassInfo(cName); + return (ci != null ? ci : newClassInfo(cName, false)).name // + + opt.shape.landingPort(); } /** Print a class's relations */ @@ -584,14 +562,10 @@ class ClassGraph { if (hidden(c) || c.name().equals("")) // avoid phantom classes, they may pop up when the source uses annotations return; String className = c.toString(); - // Print generalization (through the Java superclass) Type s = c.superclassType(); - if (s != null && - !s.toString().equals("java.lang.Object") && - !c.isEnum() && - !hidden(s.asClassDoc())) { - ClassDoc sc = s.asClassDoc(); + ClassDoc sc = s != null && !s.qualifiedTypeName().equals(Object.class.getName()) ? s.asClassDoc() : null; + if (sc != null && !c.isEnum() && !hidden(sc)) { w.println("\t//" + c + " extends " + s + "\n" + "\t" + relationNode(sc) + " -> " + relationNode(c) + " [" + RelationType.EXTENDS.style + "];"); diff --git a/src/main/java/org/umlgraph/doclet/ClassInfo.java b/src/main/java/org/umlgraph/doclet/ClassInfo.java index 28c7c4a..3c8eedb 100644 --- a/src/main/java/org/umlgraph/doclet/ClassInfo.java +++ b/src/main/java/org/umlgraph/doclet/ClassInfo.java @@ -43,8 +43,7 @@ class ClassInfo { */ Map relatedClasses = new HashMap(); - ClassInfo(boolean p, boolean h) { - nodePrinted = p; + ClassInfo(boolean h) { hidden = h; name = "c" + classNumber; classNumber++; diff --git a/src/main/java/org/umlgraph/doclet/ContextMatcher.java b/src/main/java/org/umlgraph/doclet/ContextMatcher.java index c3a05d5..b81a898 100644 --- a/src/main/java/org/umlgraph/doclet/ContextMatcher.java +++ b/src/main/java/org/umlgraph/doclet/ContextMatcher.java @@ -97,26 +97,23 @@ public class ContextMatcher implements ClassMatcher { /** * Adds the specified class to the internal class graph along with its - * relations and depencies, eventually inferring them, according to the + * 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 + // 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) { + if (opt.inferRelationships) cg.printInferredRelations(cd); - } - if (opt.inferDependencies) { + if (opt.inferDependencies) cg.printInferredDependencies(cd); - } } /** diff --git a/src/main/java/org/umlgraph/doclet/ContextView.java b/src/main/java/org/umlgraph/doclet/ContextView.java index 1693e8f..040725e 100755 --- a/src/main/java/org/umlgraph/doclet/ContextView.java +++ b/src/main/java/org/umlgraph/doclet/ContextView.java @@ -52,7 +52,7 @@ public class ContextView implements OptionProvider { this.centerOptions.nodeFillColor = "lemonChiffon"; this.centerOptions.showQualified = false; - this.matcher = new ContextMatcher(root, Pattern.compile(cd.qualifiedName()), + this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.toString())), myGlobalOptions, true); } @@ -62,7 +62,7 @@ public class ContextView implements OptionProvider { String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() + ".dot"; this.myGlobalOptions.setOption(new String[] { "output", outputPath }); - matcher.setContextCenter(Pattern.compile(cd.toString())); + matcher.setContextCenter(Pattern.compile(Pattern.quote(cd.toString()))); } public String getDisplayName() {