diff --git a/doc/ack.xml b/doc/ack.xml
index aa7dc8a..e23805a 100644
--- a/doc/ack.xml
+++ b/doc/ack.xml
@@ -46,6 +46,7 @@ UMLGraph would not be in its current state without their contributions.
Soraya Santana de la Fe
Jonathan R. Santos
Jan Schlüter
+Erich Schubert
Sebastian Setzer
Jörn Guy Süß
Andreas Studer
diff --git a/doc/cd-opt.xml b/doc/cd-opt.xml
index 505457b..bdee2bb 100644
--- a/doc/cd-opt.xml
+++ b/doc/cd-opt.xml
@@ -1,9 +1,10 @@
-A number of options contol the operation of UMLGraph
+A number of options control the operation of UMLGraph
class diagram generator.
These can be specified on the command line, and most can also
-be specified through javadoc tags within the diagram, affecting all or some elements.
+be specified through javadoc @opt tags within the diagram, affecting all or some elements.
+Most have a negated version by prefixing an exclamation mark.
What Gets Drawn
@@ -32,6 +33,7 @@ be specified through javadoc tags within the diagram, affecting all or some elem
in the package view (which would by default filter to only include package members).
- -operations
- Show class operations (Java methods)
- -qualify
- Produce fully-qualified class names.
+- -qualifyGenerics
- Use fully-qualified class names in Java generics.
- -types
- Add type information to attributes and operations
- -view
- Specify the fully qualified name of a class that contains
a view definition. Only the class diagram specified by this view will be generated.
diff --git a/doc/ver.xml b/doc/ver.xml
index 683e43e..f89f6eb 100644
--- a/doc/ver.xml
+++ b/doc/ver.xml
@@ -7,6 +7,12 @@
- The
@opt include option can be used to include classes from
foreign packages in the package view (unless hidden with @opt hide).
- You can now also use
@opt in package-info.java.
+- Added
-qualifyGenerics option to separately control the inclusion of
+package names for the class (-qualify) and its generics. In particular
+this works well with -qualify -postfixpackage (and -!qualifyGenerics).
+To get the old -qualify behavior, you need to add -qualifyGenerics,
+the default is off, and it will not inherit from -qualify.
+- Better handling of complex names with generics and inner classes such as
pkg.Outer<T>.Inner.
diff --git a/src/main/java/org/umlgraph/doclet/ClassGraph.java b/src/main/java/org/umlgraph/doclet/ClassGraph.java
index 7d3d557..378cea7 100644
--- a/src/main/java/org/umlgraph/doclet/ClassGraph.java
+++ b/src/main/java/org/umlgraph/doclet/ClassGraph.java
@@ -129,24 +129,34 @@ class ClassGraph {
/** Return the class's name, possibly by stripping the leading path */
- private String qualifiedName(Options opt, String r) {
- if (!opt.showQualified) {
- // Create readable string by stripping leading path
- for (;;) {
- int dotpos = r.lastIndexOf('.');
- if (dotpos == -1) break; // Work done!
- /*
- * Change all occurences of
- * "p1.p2.myClass" into
- * "myClass"
- */
- int start = dotpos;
- while (start > 0 && Character.isJavaIdentifierPart(r.charAt(start - 1)))
- start--;
- r = r.substring(0, start) + r.substring(dotpos + 1);
+ private static String qualifiedName(Options opt, String r) {
+ // Nothing to do:
+ if (opt.showQualified && (opt.showQualifiedGenerics || r.indexOf('<') < 0))
+ return r;
+ StringBuilder buf = new StringBuilder(r.length());
+ int last = 0, depth = 0;
+ boolean strip = !opt.showQualified;
+ for (int i = 0; i < r.length();) {
+ char c = r.charAt(i++);
+ // The last condition prevents losing the dot in A.B
+ if ((c == '.' || c == '$') && strip && last + 1 < i)
+ last = i; // skip
+ if (Character.isJavaIdentifierPart(c))
+ continue;
+ // Handle nesting of generics
+ if (c == '<') {
+ ++depth;
+ strip = !opt.showQualifiedGenerics;
+ } else if (c == '>' && --depth == 0)
+ strip = !opt.showQualified;
+ if (last < i) {
+ buf.append(r, last, i);
+ last = i;
}
}
- return r;
+ if (last < r.length())
+ buf.append(r, last, r.length());
+ return buf.toString();
}
/**
@@ -263,9 +273,9 @@ class ClassGraph {
}
/** Print a a basic type t */
- private String type(Options opt, Type t) {
+ private String type(Options opt, Type t, boolean generics) {
String type;
- if (opt.showQualified)
+ if (generics ? opt.showQualifiedGenerics : opt.showQualified)
type = t.qualifiedTypeName();
else
type = t.typeName();
@@ -281,7 +291,7 @@ class ClassGraph {
Type args[] = t.typeArguments();
tp += "<";
for (int i = 0; i < args.length; i++) {
- tp += type(opt, args[i]);
+ tp += type(opt, args[i], true);
if (i != args.length - 1)
tp += ", ";
}
@@ -291,11 +301,10 @@ class ClassGraph {
/** Annotate an field/argument with its type t */
private String typeAnnotation(Options opt, Type t) {
- String ta = "";
if (t.typeName().equals("void"))
- return ta;
- ta += " : ";
- ta += type(opt, t);
+ return "";
+ String ta = " : ";
+ ta += type(opt, t, false);
ta += t.dimension();
return ta;
}
@@ -506,16 +515,12 @@ class ClassGraph {
Font font = c.isAbstract() && !c.isInterface() ? Font.CLASS_ABSTRACT : Font.CLASS;
String qualifiedName = qualifiedName(opt, r);
int startTemplate = qualifiedName.indexOf('<');
- int idx;
- if(startTemplate < 0)
- idx = qualifiedName.lastIndexOf('.');
- else
- idx = qualifiedName.lastIndexOf('.', startTemplate);
+ int idx = qualifiedName.lastIndexOf('.', startTemplate < 0 ? qualifiedName.length() - 1 : startTemplate);
if (opt.showComment)
tableLine(Align.LEFT, htmlNewline(escape(c.commentText())), opt, Font.CLASS);
else if(opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
String packageName = qualifiedName.substring(0, idx);
- String cn = className.substring(idx + 1);
+ String cn = qualifiedName.substring(idx + 1);
tableLine(Align.CENTER, escape(cn), opt, font);
tableLine(Align.CENTER, packageName, opt, Font.PACKAGE);
} else {
@@ -781,14 +786,16 @@ class ClassGraph {
w.print("\t" + info.name + "[label=");
externalTableStart(opt, className, classToUrl(className));
innerTableStart();
- int idx = className.lastIndexOf(".");
- if(opt.postfixPackage && idx > 0 && idx < (className.length() - 1)) {
- String packageName = className.substring(0, idx);
- String cn = className.substring(idx + 1);
+ 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, escape(cn), opt, Font.CLASS);
tableLine(Align.CENTER, packageName, opt, Font.PACKAGE);
} else {
- tableLine(Align.CENTER, escape(className), opt, Font.CLASS);
+ tableLine(Align.CENTER, escape(qualifiedName), opt, Font.CLASS);
}
innerTableEnd();
externalTableEnd();
diff --git a/src/main/java/org/umlgraph/doclet/Options.java b/src/main/java/org/umlgraph/doclet/Options.java
index c9a3dea..4efa0e7 100644
--- a/src/main/java/org/umlgraph/doclet/Options.java
+++ b/src/main/java/org/umlgraph/doclet/Options.java
@@ -71,6 +71,7 @@ public class Options implements Cloneable, OptionProvider {
List hidePatterns;
List includePatterns;
boolean showQualified;
+ boolean showQualifiedGenerics;
boolean showAttributes;
boolean showEnumerations;
boolean showEnumConstants;
@@ -141,6 +142,7 @@ public class Options implements Cloneable, OptionProvider {
Options() {
showQualified = false;
+ showQualifiedGenerics = false;
showAttributes = false;
showEnumConstants = false;
showOperations = false;
@@ -303,6 +305,10 @@ public class Options implements Cloneable, OptionProvider {
showQualified = true;
} else if (opt[0].equals("-!qualify")) {
showQualified = false;
+ } else if(opt[0].equals("-qualifyGenerics")) {
+ showQualifiedGenerics = true;
+ } else if (opt[0].equals("-!qualifyGenerics")) {
+ showQualifiedGenerics = false;
} else if(opt[0].equals("-horizontal")) {
horizontal = true;
} else if (opt[0].equals("-!horizontal")) {