Improve class info handling.

This commit is contained in:
Erich Schubert 2018-10-26 00:45:32 +02:00
parent c7d8648352
commit dc18c503e9
4 changed files with 127 additions and 157 deletions

View File

@ -312,26 +312,17 @@ class ClassGraph {
/** Return true if c has a @hidden tag associated with it */ /** Return true if c has a @hidden tag associated with it */
private boolean hidden(ProgramElementDoc c) { private boolean hidden(ProgramElementDoc c) {
Tag tags[] = c.tags("hidden"); return c.tags("hidden").length > 0 || c.tags("view").length > 0 || //
if (tags.length > 0) optionProvider.getOptionsFor(c instanceof ClassDoc ? (ClassDoc) c : c.containingClass()) //
return true; .matchesHideExpression(c.toString());
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());
} }
protected ClassInfo getClassInfo(String className) { protected ClassInfo getClassInfo(String className) {
return classnames.get(removeTemplate(className)); return classnames.get(removeTemplate(className));
} }
private ClassInfo newClassInfo(String className, boolean printed, boolean hidden) { private ClassInfo newClassInfo(String className, boolean hidden) {
ClassInfo ci = new ClassInfo(printed, hidden); ClassInfo ci = new ClassInfo(hidden);
classnames.put(removeTemplate(className), ci); classnames.put(removeTemplate(className), ci);
return ci; return ci;
} }
@ -352,139 +343,121 @@ class ClassGraph {
* relative links in diagrams for UMLDoc * relative links in diagrams for UMLDoc
*/ */
public String printClass(ClassDoc c, boolean rootClass) { public String printClass(ClassDoc c, boolean rootClass) {
ClassInfo ci;
boolean toPrint;
Options opt = optionProvider.getOptionsFor(c);
String className = c.toString(); String className = c.toString();
if ((ci = getClassInfo(className)) != null) ClassInfo ci = getClassInfo(className);
toPrint = !ci.nodePrinted; ci = ci != null ? ci : newClassInfo(className, hidden(c));
else { if(ci.nodePrinted || ci.hidden)
toPrint = true; return ci.name;
ci = newClassInfo(className, true, hidden(c)); Options opt = optionProvider.getOptionsFor(c);
} if (c.isEnum() && !opt.showEnumerations)
if (toPrint && !hidden(c) && (!c.isEnum() || opt.showEnumerations)) { return ci.name;
// Associate classname's alias // Associate classname's alias
String r = className; w.println("\t// " + className);
w.println("\t// " + r); // Create label
// Create label w.print("\t" + ci.name + " [label=");
w.print("\t" + ci.name + " [label=");
boolean showMembers = boolean showMembers =
(opt.showAttributes && c.fields().length > 0) || (opt.showAttributes && c.fields().length > 0) ||
(c.isEnum() && opt.showEnumConstants && c.enumConstants().length > 0) || (c.isEnum() && opt.showEnumConstants && c.enumConstants().length > 0) ||
(opt.showOperations && c.methods().length > 0) || (opt.showOperations && c.methods().length > 0) ||
(opt.showConstructors && c.constructors().length > 0); (opt.showConstructors && c.constructors().length > 0);
externalTableStart(opt, c.qualifiedName(), classToUrl(c, rootClass));
firstInnerTableStart(opt); externalTableStart(opt, c.qualifiedName(), classToUrl(c, rootClass));
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());
if (!printedLines) firstInnerTableStart(opt);
// if there are no operations nor constructors, if (c.isInterface())
// print an empty line to generate proper HTML tableLine(Align.CENTER, guilWrap(opt, "interface"));
tableLine(Align.LEFT, ""); if (c.isEnum())
tableLine(Align.CENTER, guilWrap(opt, "enumeration"));
innerTableEnd(); stereotype(opt, c, Align.CENTER);
} Font font = c.isAbstract() && !c.isInterface() ? Font.CLASS_ABSTRACT : Font.CLASS;
} String qualifiedName = qualifiedName(opt, className);
externalTableEnd(); int startTemplate = qualifiedName.indexOf('<');
w.print(", URL=\"" + classToUrl(c, rootClass) + "\""); int idx = qualifiedName.lastIndexOf('.', startTemplate < 0 ? qualifiedName.length() - 1 : startTemplate);
nodeProperties(opt); if (opt.showComment)
tableLine(Align.LEFT, Font.CLASS.wrap(opt, htmlNewline(escape(c.commentText()))));
// If needed, add a note for this node else if (opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
int ni = 0; String packageName = qualifiedName.substring(0, idx);
for (Tag t : c.tags("note")) { String cn = qualifiedName.substring(idx + 1);
String noteName = "n" + ni + "c" + ci.name; tableLine(Align.CENTER, font.wrap(opt, escape(cn)));
w.print("\t// Note annotation\n"); tableLine(Align.CENTER, Font.PACKAGE.wrap(opt, packageName));
w.print("\t" + noteName + " [label="); } else {
externalTableStart(UmlGraph.getCommentOptions(), c.qualifiedName(), classToUrl(c, rootClass)); tableLine(Align.CENTER, font.wrap(opt, escape(qualifiedName)));
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; tagvalue(opt, c);
} firstInnerTableEnd(opt);
private String getNodeName(ClassDoc c) { /*
String className = c.toString(); * Warning: The boolean expressions guarding innerTableStart()
ClassInfo ci = getClassInfo(className); * in this block, should match those in the code block above
if (ci == null) * marked: "Calculate the number of innerTable rows we will emmit"
ci = newClassInfo(className, false, hidden(c)); */
return ci.name; if (showMembers) {
} if (opt.showAttributes) {
innerTableStart();
/** Return a class's internal name */ FieldDoc[] fields = c.fields();
private String getNodeName(String c) { // if there are no fields, print an empty line to generate proper HTML
ClassInfo ci = getClassInfo(c); 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) if (!printedLines)
ci = newClassInfo(c, false, false); // 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; return ci.name;
} }
@ -564,7 +537,10 @@ class ClassGraph {
* whose outline is rendered through an inner table. * whose outline is rendered through an inner table.
*/ */
private String relationNode(ClassDoc c) { 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. /** Return the full name of a relation's node c.
@ -575,7 +551,9 @@ class ClassGraph {
*/ */
private String relationNode(ClassDoc c, String cName) { private String relationNode(ClassDoc c, String cName) {
Options opt = c == null ? optionProvider.getOptionsFor(cName) : optionProvider.getOptionsFor(c); 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 */ /** 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 if (hidden(c) || c.name().equals("")) // avoid phantom classes, they may pop up when the source uses annotations
return; return;
String className = c.toString(); String className = c.toString();
// Print generalization (through the Java superclass) // Print generalization (through the Java superclass)
Type s = c.superclassType(); Type s = c.superclassType();
if (s != null && ClassDoc sc = s != null && !s.qualifiedTypeName().equals(Object.class.getName()) ? s.asClassDoc() : null;
!s.toString().equals("java.lang.Object") && if (sc != null && !c.isEnum() && !hidden(sc)) {
!c.isEnum() &&
!hidden(s.asClassDoc())) {
ClassDoc sc = s.asClassDoc();
w.println("\t//" + c + " extends " + s + "\n" + w.println("\t//" + c + " extends " + s + "\n" +
"\t" + relationNode(sc) + " -> " + relationNode(c) + "\t" + relationNode(sc) + " -> " + relationNode(c) +
" [" + RelationType.EXTENDS.style + "];"); " [" + RelationType.EXTENDS.style + "];");

View File

@ -43,8 +43,7 @@ class ClassInfo {
*/ */
Map<String, RelationPattern> relatedClasses = new HashMap<String, RelationPattern>(); Map<String, RelationPattern> relatedClasses = new HashMap<String, RelationPattern>();
ClassInfo(boolean p, boolean h) { ClassInfo(boolean h) {
nodePrinted = p;
hidden = h; hidden = h;
name = "c" + classNumber; name = "c" + classNumber;
classNumber++; classNumber++;

View File

@ -97,26 +97,23 @@ public class ContextMatcher implements ClassMatcher {
/** /**
* Adds the specified class to the internal class graph along with its * 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 * Options specified for this matcher
* @param cd * @param cd
*/ */
private void addToGraph(ClassDoc cd) { private void addToGraph(ClassDoc cd) {
// avoid adding twice the same class, but don't rely on cg.getClassInfo // avoid adding twice the same class, but don't rely on cg.getClassInfo
// since there // since there are other ways to add a classInfor than printing the class
// are other ways to add a classInfor than printing the class
if (visited.contains(cd.toString())) if (visited.contains(cd.toString()))
return; return;
visited.add(cd.toString()); visited.add(cd.toString());
cg.printClass(cd, false); cg.printClass(cd, false);
cg.printRelations(cd); cg.printRelations(cd);
if (opt.inferRelationships) { if (opt.inferRelationships)
cg.printInferredRelations(cd); cg.printInferredRelations(cd);
} if (opt.inferDependencies)
if (opt.inferDependencies) {
cg.printInferredDependencies(cd); cg.printInferredDependencies(cd);
}
} }
/** /**

View File

@ -52,7 +52,7 @@ public class ContextView implements OptionProvider {
this.centerOptions.nodeFillColor = "lemonChiffon"; this.centerOptions.nodeFillColor = "lemonChiffon";
this.centerOptions.showQualified = false; this.centerOptions.showQualified = false;
this.matcher = new ContextMatcher(root, Pattern.compile(cd.qualifiedName()), this.matcher = new ContextMatcher(root, Pattern.compile(Pattern.quote(cd.toString())),
myGlobalOptions, true); myGlobalOptions, true);
} }
@ -62,7 +62,7 @@ public class ContextView implements OptionProvider {
String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name() String outputPath = cd.containingPackage().name().replace('.', '/') + "/" + cd.name()
+ ".dot"; + ".dot";
this.myGlobalOptions.setOption(new String[] { "output", outputPath }); this.myGlobalOptions.setOption(new String[] { "output", outputPath });
matcher.setContextCenter(Pattern.compile(cd.toString())); matcher.setContextCenter(Pattern.compile(Pattern.quote(cd.toString())));
} }
public String getDisplayName() { public String getDisplayName() {