reformat classes

This commit is contained in:
Laurent SCHOELENS 2023-03-21 09:15:55 +01:00
parent d80597d547
commit c8f805bdef
24 changed files with 1942 additions and 1957 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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 <a href="http://www.spinellis.gr">Diomidis Spinellis</a>
*/
@ -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<String, RelationPattern> relatedClasses = new HashMap<String, RelationPattern>();
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;
}
}

View File

@ -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);
}

View File

@ -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 {
* <p>
* 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<ClassDoc>();
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<ClassDoc>();
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
}
}

View File

@ -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);
}
}

View File

@ -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("<font");
if (face != null)
buf.append(" face=\"").append(face).append('"');
if (size > 0)
buf.append(" point-size=\"").append(size).append('"');
buf.append('>');
}
if (italic)
buf.append("<i>");
buf.append(text);
if (italic)
buf.append("</i>");
if (face != null || size > 0)
buf.append("</font>");
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("<font");
if (face != null)
buf.append(" face=\"").append(face).append('"');
if (size > 0)
buf.append(" point-size=\"").append(size).append('"');
buf.append('>');
}
if (italic)
buf.append("<i>");
buf.append(text);
if (italic)
buf.append("</i>");
if (face != null || size > 0)
buf.append("</font>");
return buf.toString();
}
}

View File

@ -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);
}
}

View File

@ -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.
* <p>Will be used to provide progress feedback on the console
* <p>
* Will be used to provide progress feedback on the console
*/
public String getDisplayName();
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
};

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 ? ("<td rowspan=\"10\"></td>") : "";
return this == Shape.ACTIVECLASS ? ("<td rowspan=\"10\"></td>") : "";
}
/** 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";
}
}

View File

@ -31,37 +31,37 @@ import java.util.regex.Pattern;
class StringUtil {
/** Tokenize string s into an array */
public static String[] tokenize(String s) {
ArrayList<String> r = new ArrayList<String>();
String remain = s;
int n = 0, pos;
ArrayList<String> r = new ArrayList<String>();
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, "&amp;");
i += "&amp;".length();
break;
case '<':
sb.replace(i, i + 1, "&lt;");
i += "&lt;".length();
break;
case '>':
sb.replace(i, i + 1, "&gt;");
i += "&gt;".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, "&amp;");
i += "&amp;".length();
break;
case '<':
sb.replace(i, i + 1, "&lt;");
i += "&lt;".length();
break;
case '>':
sb.replace(i, i + 1, "&gt;");
i += "&gt;".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, "<br/>");
i += "<br/>".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, "<br/>");
i += "<br/>".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 <code>String</code>.
*/
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.
* <p>
@ -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);
}
}

View File

@ -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);
}
}

View File

@ -2,8 +2,10 @@ package org.umlgraph.doclet;
/**
* Options for UMLGraph class diagram generation
*
* @opt operations
* @opt visibility
* @hidden
*/
public class UMLOptions {}
public class UMLOptions {
}

View File

@ -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<View> views = new ArrayList<View>();
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<View> views = new ArrayList<View>();
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;
}
}

View File

@ -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<String> packages = new HashSet<String>();
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("(</[Hh]2>)|(<h1 title=\"Package\").*"), root);
}
}
private static void generatePackageDiagrams(RootDoc root, Options opt, String outputFolder) throws IOException {
Set<String> packages = new HashSet<String>();
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("(</[Hh]2>)|(<h1 title=\"Package\").*"), root);
}
}
}
/**
* Generates the context diagram for a single class
*/
private static void generateContextDiagrams(RootDoc root, Options opt, String outputFolder)
throws IOException {
private static void generateContextDiagrams(RootDoc root, Options opt, String outputFolder) throws IOException {
Set<ClassDoc> classDocs = new TreeSet<ClassDoc>(new Comparator<ClassDoc>() {
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 =
"<div align=\"center\">" +
"<object width=\"100%%\" height=\"100%%\" type=\"image/svg+xml\" data=\"%1$s.svg\" alt=\"Package class diagram package %1$s\" border=0></object>" +
"</div>";
private static final String UML_AUTO_SIZED_DIV_TAG =
"<div align=\"center\">" +
"<object type=\"image/svg+xml\" data=\"%1$s.svg\" alt=\"Package class diagram package %1$s\" border=0></object>" +
"</div>";
// Format string for the uml image div tag.
private static final String UML_DIV_TAG = "<div align=\"center\">"
+ "<object width=\"100%%\" height=\"100%%\" type=\"image/svg+xml\" data=\"%1$s.svg\" alt=\"Package class diagram package %1$s\" border=0></object>"
+ "</div>";
private static final String UML_AUTO_SIZED_DIV_TAG = "<div align=\"center\">"
+ "<object type=\"image/svg+xml\" data=\"%1$s.svg\" alt=\"Package class diagram package %1$s\" border=0></object>"
+ "</div>";
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 =
"<script type=\"text/javascript\">\n" +
"function show() {\n" +
" document.getElementById(\"uml\").innerHTML = \n" +
" \'<a style=\"" + EXPANDABLE_UML_STYLE + "\" href=\"javascript:hide()\">%3$s</a>\' +\n" +
" \'%1$s\';\n" +
"}\n" +
"function hide() {\n" +
" document.getElementById(\"uml\").innerHTML = \n" +
" \'<a style=\"" + EXPANDABLE_UML_STYLE + "\" href=\"javascript:show()\">%2$s</a>\' ;\n" +
"}\n" +
"</script>\n" +
"<div id=\"uml\" >\n" +
" <a href=\"javascript:show()\">\n" +
" <a style=\"" + EXPANDABLE_UML_STYLE + "\" href=\"javascript:show()\">%2$s</a> \n" +
"</div>";
// Format string for the java script tag.
private static final String EXPANDABLE_UML = "<script type=\"text/javascript\">\n" + "function show() {\n"
+ " document.getElementById(\"uml\").innerHTML = \n" + " \'<a style=\"" + EXPANDABLE_UML_STYLE
+ "\" href=\"javascript:hide()\">%3$s</a>\' +\n" + " \'%1$s\';\n" + "}\n" + "function hide() {\n"
+ " document.getElementById(\"uml\").innerHTML = \n" + " \'<a style=\"" + EXPANDABLE_UML_STYLE
+ "\" href=\"javascript:show()\">%2$s</a>\' ;\n" + "}\n" + "</script>\n" + "<div id=\"uml\" >\n"
+ " <a href=\"javascript:show()\">\n" + " <a style=\"" + EXPANDABLE_UML_STYLE
+ "\" href=\"javascript:show()\">%2$s</a> \n" + "</div>";
/**
* 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("<!-- UML diagram added by UMLGraph version " +
Version.VERSION +
" (http://www.spinellis.gr/umlgraph/) -->");
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("<!-- UML diagram added by UMLGraph version " + Version.VERSION + " (http://www.spinellis.gr/umlgraph/) -->");
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 ".";
}
}

View File

@ -1,4 +1,6 @@
/* Automatically generated file */
package org.umlgraph.doclet;
class Version { public static String VERSION = "R5_7_2-60-g0e99a6";}
class Version {
public static String VERSION = "R5_7_2-60-g0e99a6";
}

View File

@ -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<String[]>();
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<String[]>());
}
} 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<String[]>();
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<String[]>());
}
} 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 <type> 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 <type> 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();
}
}

View File

@ -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;
}
}