Files repocopied to org/umlgraph

This commit is contained in:
Diomidis Spinellis 2007-11-27 07:40:28 +00:00
parent 6e5dabef48
commit 15b5b303bf
31 changed files with 0 additions and 4818 deletions

View File

@ -1 +0,0 @@
Version.java

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class's dot-comaptible alias name (for fully qualified class names)
* and printed information
* @version $Revision$
* @author <a href="http://www.spinellis.gr">Diomidis Spinellis</a>
*/
class ClassInfo {
private static int classNumber;
/** Alias name for the class */
String name;
/** True if the class class node has been printed */
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
*/
Map<String, RelationPattern> relatedClasses = new HashMap<String, RelationPattern>();
ClassInfo(boolean p, boolean h) {
nodePrinted = p;
hidden = h;
name = "c" + (new Integer(classNumber)).toString();
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);
}
public RelationPattern getRelation(String dest) {
return relatedClasses.get(dest);
}
/** Start numbering from zero. */
public static void reset() {
classNumber = 0;
}
}

View File

@ -1,39 +0,0 @@
/*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.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
* @author wolf
*/
public interface ClassMatcher {
/**
* Returns the options for the specified class.
*/
public boolean matches(ClassDoc cd);
/**
* Returns the options for the specified class.
*/
public boolean matches(String name);
}

View File

@ -1,202 +0,0 @@
/*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
/**
* Matches classes that are directly connected to one of the classes matched by
* the regual expression specified. The context center is computed by regex
* lookup. Depending on the specified Options, inferred relations and
* dependencies will be used as well.
* <p>
* 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
*/
public class ContextMatcher implements ClassMatcher {
ClassGraphHack cg;
Pattern pattern;
List<ClassDoc> matched;
Set<String> visited = new HashSet<String>();
Options opt;
RootDoc root;
boolean keepParentHide;
/**
* 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 opt The options will be used to decide on inference
* @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 fullContext If true, all the classes related to the context
* center will be included, otherwise it will match only
* the classes referred with an outgoing relation from
* the context center
* @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);
setContextCenter(pattern);
}
/**
* Can be used to setup a different pattern for this context matcher.
* <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);
}
}
}
/**
* Adds the specified class to the internal class graph along with its
* relations and depencies, 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;
visited.add(cd.toString());
cg.printClass(cd, false);
cg.printRelations(cd);
if (opt.inferRelationships) {
cg.printInferredRelations(cd);
}
if (opt.inferDependencies) {
cg.printInferredDependencies(cd);
}
}
/**
* @see gr.spinellis.umlgraph.doclet.ClassMatcher#matches(com.sun.javadoc.ClassDoc)
*/
public boolean matches(ClassDoc cd) {
if (keepParentHide && opt.matchesHideExpression(cd.toString()))
return false;
// 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());
}
/**
* @see gr.spinellis.umlgraph.doclet.ClassMatcher#matches(java.lang.String)
*/
public boolean matches(String name) {
if (pattern.matcher(name).matches())
return true;
for (ClassDoc mcd : matched) {
String mcName = mcd.toString();
ClassInfo ciMatched = cg.getClassInfo(mcName);
RelationPattern rp = ciMatched.getRelation(name);
if (ciMatched != null && 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.
* @author wolf
*
*/
private static class ClassGraphHack extends ClassGraph {
public ClassGraphHack(RootDoc root, OptionProvider optionProvider) throws IOException {
super(root, optionProvider, null);
prologue();
}
public void prologue() throws IOException {
w = new PrintWriter(new DevNullWriter());
}
}
/**
* Simple dev/null imitation
* @author wolf
*/
private static class DevNullWriter extends Writer {
public void write(char[] cbuf, int off, int len) throws IOException {
// nothing to do
}
public void flush() throws IOException {
// nothing to do
}
public void close() throws IOException {
// nothing to do
}
}
}

View File

@ -1,109 +0,0 @@
package gr.spinellis.umlgraph.doclet;
import java.io.IOException;
import java.util.regex.Pattern;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
/**
* A view designed for UMLDoc, filters out everything that it's not directly
* connected to the center class of the context.
* <p>
* As such, can be viewed as a simplified version of a {@linkplain View} using a
* 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
*
*/
public class ContextView implements OptionProvider {
private ClassDoc cd;
private ContextMatcher matcher;
private Options globalOptions;
private Options myGlobalOptions;
private Options hideOptions;
private Options centerOptions;
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";
// 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;
this.myGlobalOptions = parent.getGlobalOptions();
this.myGlobalOptions.setOption(new String[] { "-output", outputPath });
this.myGlobalOptions.setOption(HIDE_OPTIONS);
this.hideOptions = parent.getGlobalOptions();
this.hideOptions.setOption(HIDE_OPTIONS);
this.centerOptions = parent.getGlobalOptions();
this.centerOptions.nodeFillColor = "lemonChiffon";
this.centerOptions.showQualified = false;
this.matcher = new ContextMatcher(root, Pattern.compile(cd.qualifiedName()),
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(cd.toString()));
}
public String getDisplayName() {
return "Context view for class " + cd;
}
public Options getGlobalOptions() {
return myGlobalOptions;
}
public Options getOptionsFor(ClassDoc cd) {
if (globalOptions.matchesHideExpression(cd.toString()) || !matcher.matches(cd)) {
return hideOptions;
} else if (cd.equals(this.cd)) {
return centerOptions;
} else if(cd.containingPackage().equals(this.cd.containingPackage())){
return packageOptions;
} else {
return globalOptions;
}
}
public Options getOptionsFor(String name) {
if (!matcher.matches(name))
return hideOptions;
else if (name.equals(cd.name()))
return centerOptions;
else
return globalOptions;
}
public void overrideForClass(Options opt, ClassDoc cd) {
if (opt.matchesHideExpression(cd.toString()) || !matcher.matches(cd))
opt.setOption(HIDE_OPTIONS);
if (cd.equals(this.cd))
opt.nodeFillColor = "lemonChiffon";
}
public void overrideForClass(Options opt, String className) {
if (!matcher.matches(className))
opt.setOption(HIDE_OPTIONS);
}
}

View File

@ -1,48 +0,0 @@
package gr.spinellis.umlgraph.doclet;
import java.util.regex.Pattern;
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.
*/
public class InterfaceMatcher implements ClassMatcher {
protected RootDoc root;
protected Pattern pattern;
public InterfaceMatcher(RootDoc root, 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
if(cd.superclass() != null)
return matches(cd.superclass());
return false;
}
public boolean matches(String name) {
ClassDoc cd = root.classNamed(name);
if(cd == null)
return false;
return matches(cd);
}
}

View File

@ -1,58 +0,0 @@
/*
* Contibuted by Andrea Aime
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import com.sun.javadoc.ClassDoc;
/**
* A factory class that builds Options object for general use or for a
* specific class
*/
public interface OptionProvider {
/**
* Returns the options for the specified class.
*/
public Options getOptionsFor(ClassDoc cd);
/**
* Returns the options for the specified class.
*/
public Options getOptionsFor(String name);
/**
* Returns the global options (the class independent definition)
*/
public Options getGlobalOptions();
/**
* Gets a base Options and applies the overrides for the specified class
*/
public void overrideForClass(Options opt, ClassDoc cd);
/**
* Gets a base Options and applies the overrides for the specified class
*/
public void overrideForClass(Options opt, String className);
/**
* Returns user displayable name for this option provider.
* <p>Will be used to provide progress feedback on the console
*/
public String getDisplayName();
}

View File

@ -1,699 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Tag;
/**
* Represent the program options
* @version $Revision$
* @author <a href="http://www.spinellis.gr">Diomidis Spinellis</a>
*/
public class Options implements Cloneable, OptionProvider {
// dot's font platform dependence workaround
private static String defaultFont;
private static String defaultItalicFont;
// reused often, especially in UmlGraphDoc, worth creating just once and reusing
private static final Pattern allPattern = Pattern.compile(".*");
protected static final String DEFAULT_EXTERNAL_APIDOC = "http://java.sun.com/j2se/1.4.2/docs/api/";
static {
// use an appropriate font depending on the current operating system
// (on windows graphviz is unable to locate "Helvetica-Oblique"
if(System.getProperty("os.name").toLowerCase().contains("windows")) {
defaultFont = "arial";
defaultItalicFont = "ariali";
} else {
defaultFont = "Helvetica";
defaultItalicFont = "Helvetica-Oblique";
}
}
// instance fields
Vector<Pattern> hidePatterns;
boolean showQualified;
boolean showAttributes;
boolean showEnumerations;
boolean showEnumConstants;
boolean showOperations;
boolean showConstructors;
boolean showVisibility;
boolean horizontal;
boolean showType;
String edgeFontName;
String edgeFontColor;
String edgeColor;
double edgeFontSize;
String nodeFontName;
String nodeFontAbstractName;
String nodeFontColor;
double nodeFontSize;
String nodeFillColor;
double nodeFontClassSize;
String nodeFontClassName;
String nodeFontClassAbstractName;
double nodeFontTagSize;
String nodeFontTagName;
double nodeFontPackageSize;
String nodeFontPackageName;
String bgColor;
public String outputFileName;
String outputEncoding;
Map<Pattern, String> apiDocMap;
String apiDocRoot;
boolean postfixPackage;
boolean useGuillemot;
boolean findViews;
String viewName;
public String outputDirectory;
/** Guillemot left (open) */
String guilOpen = "&laquo;"; // "\u00ab";
/** Guillemot right (close) */
String guilClose = "&raquo;"; // "\u00bb";
boolean inferRelationships;
boolean inferDependencies;
RelationPattern contextRelationPattern;
boolean useImports;
Visibility inferDependencyVisibility;
boolean inferDepInPackage;
RelationType inferRelationshipType;
private Vector<Pattern> collPackages;
boolean compact;
// internal option, used by UMLDoc to generate relative links between classes
boolean relativeLinksForSourcePackages;
// internal option, used by UMLDoc to force strict matching on the class names
// and avoid problems with packages in the template declaration making UmlGraph hide
// classes outside of them (for example, class gr.spinellis.Foo<T extends java.io.Serializable>
// would have been hidden by the hide pattern "java.*"
// TODO: consider making this standard behaviour
boolean strictMatching;
Options() {
showQualified = false;
showAttributes = false;
showEnumConstants = false;
showOperations = false;
showVisibility = false;
showEnumerations = false;
showConstructors = false;
showType = false;
edgeFontName = defaultFont;
edgeFontColor = "black";
edgeColor = "black";
edgeFontSize = 10;
nodeFontColor = "black";
nodeFontName = defaultFont;
nodeFontAbstractName = defaultItalicFont;
nodeFontSize = 10;
nodeFontClassSize = -1;
nodeFontClassName = null;
nodeFontClassAbstractName = null;
nodeFontTagSize = -1;
nodeFontTagName = null;
nodeFontPackageSize = -1;
nodeFontPackageName = null;
nodeFillColor = null;
bgColor = null;
outputFileName = "graph.dot";
outputDirectory= null;
outputEncoding = "ISO-8859-1";
hidePatterns = new Vector<Pattern>();
apiDocMap = new HashMap<Pattern, String>();
apiDocRoot = null;
postfixPackage = false;
useGuillemot = true;
findViews = false;
viewName = null;
contextRelationPattern = new RelationPattern(RelationDirection.BOTH);
inferRelationships = false;
inferDependencies = false;
inferDependencyVisibility = Visibility.PRIVATE;
inferDepInPackage = false;
useImports = false;
inferRelationshipType = RelationType.NAVASSOC;
collPackages = new Vector<Pattern>();
compact = false;
relativeLinksForSourcePackages = false;
}
public Object clone() {
Options clone = null;
try {
clone = (Options) super.clone();
} catch (CloneNotSupportedException e) {
// Should not happen
}
// deep clone the hide and collection patterns
clone.hidePatterns = new Vector<Pattern>(hidePatterns);
clone.collPackages= new Vector<Pattern>(collPackages);
clone.apiDocMap = new HashMap<Pattern, String>(apiDocMap);
return clone;
}
/** Most complete output */
public void setAll() {
showAttributes = true;
showEnumerations = true;
showEnumConstants = true;
showOperations = true;
showConstructors = true;
showVisibility = true;
showType = true;
}
/**
* Return the number of arguments associated with the specified option.
* The return value includes the actual option.
* Will return 0 if the option is not supported.
*/
public static int optionLength(String option) {
if(option.equals("-qualify") ||
option.equals("-horizontal") ||
option.equals("-attributes") ||
option.equals("-operations") ||
option.equals("-constructors") ||
option.equals("-visibility") ||
option.equals("-types") ||
option.equals("-all") ||
option.equals("-postfixpackage") ||
option.equals("-noguillemot") ||
option.equals("-enumconstants") ||
option.equals("-enumerations") ||
option.equals("-views") ||
option.equals("-inferrel") ||
option.equals("-useimports") ||
option.equals("-inferdep") ||
option.equals("-inferdepinpackage") ||
option.equals("-compact"))
return 1;
else if(option.equals("-nodefillcolor") ||
option.equals("-nodefontcolor") ||
option.equals("-nodefontsize") ||
option.equals("-nodefontname") ||
option.equals("-nodefontabstractname") ||
option.equals("-nodefontclasssize") ||
option.equals("-nodefontclassname") ||
option.equals("-nodefontclassabstractname") ||
option.equals("-nodefonttagsize") ||
option.equals("-nodefonttagname") ||
option.equals("-nodefontpackagesize") ||
option.equals("-nodefontpackagename") ||
option.equals("-edgefontcolor") ||
option.equals("-edgecolor") ||
option.equals("-edgefontsize") ||
option.equals("-edgefontname") ||
option.equals("-output") ||
option.equals("-outputencoding") ||
option.equals("-bgcolor") ||
option.equals("-hide") ||
option.equals("-apidocroot") ||
option.equals("-apidocmap") ||
option.equals("-d") ||
option.equals("-view") ||
option.equals("-inferreltype") ||
option.equals("-inferdepvis") ||
option.equals("-collpackages") ||
option.equals("-link"))
return 2;
else if(option.equals("-contextPattern"))
return 3;
else
return 0;
}
/** Set the options based on a single option and its arguments */
void setOption(String[] opt) {
if(!opt[0].equals("-hide") && optionLength(opt[0]) > opt.length) {
System.err.println("Skipping option '" + opt[0] + "', missing argument");
return;
}
if(opt[0].equals("-qualify")) {
showQualified = true;
} else if (opt[0].equals("-!qualify")) {
showQualified = false;
} else if(opt[0].equals("-horizontal")) {
horizontal = true;
} else if (opt[0].equals("-!horizontal")) {
horizontal = false;
} else if(opt[0].equals("-attributes")) {
showAttributes = true;
} else if (opt[0].equals("-!attributes")) {
showAttributes = false;
} else if(opt[0].equals("-enumconstants")) {
showEnumConstants = true;
} else if (opt[0].equals("-!enumconstants")) {
showEnumConstants = false;
} else if(opt[0].equals("-operations")) {
showOperations = true;
} else if (opt[0].equals("-!operations")) {
showOperations = false;
} else if(opt[0].equals("-enumerations")) {
showEnumerations = true;
} else if (opt[0].equals("-!enumerations")) {
showEnumerations = false;
} else if(opt[0].equals("-constructors")) {
showConstructors = true;
} else if (opt[0].equals("-!constructors")) {
showConstructors = false;
} else if(opt[0].equals("-visibility")) {
showVisibility = true;
} else if (opt[0].equals("-!visibility")) {
showVisibility = false;
} else if(opt[0].equals("-types")) {
showType = true;
} else if (opt[0].equals("-!types")) {
showType = false;
} else if(opt[0].equals("-all")) {
setAll();
} else if(opt[0].equals("-bgcolor")) {
bgColor = opt[1];
} else if (opt[0].equals("-!bgcolor")) {
bgColor = null;
} else if(opt[0].equals("-edgecolor")) {
edgeColor = opt[1];
} else if (opt[0].equals("-!edgecolor")) {
edgeColor = "black";
} else if(opt[0].equals("-edgefontcolor")) {
edgeFontColor = opt[1];
} else if (opt[0].equals("-!edgefontcolor")) {
edgeFontColor = "black";
} else if(opt[0].equals("-edgefontname")) {
edgeFontName = opt[1];
} else if (opt[0].equals("-!edgefontname")) {
edgeFontName = defaultFont;
} else if(opt[0].equals("-edgefontsize")) {
edgeFontSize = Integer.parseInt(opt[1]);
} else if (opt[0].equals("-!edgefontsize")) {
edgeFontSize = 10;
} else if(opt[0].equals("-nodefontcolor")) {
nodeFontColor = opt[1];
} else if (opt[0].equals("-!nodefontcolor")) {
nodeFontColor = "black";
} else if(opt[0].equals("-nodefontname")) {
nodeFontName = opt[1];
} else if (opt[0].equals("-!nodefontname")) {
nodeFontName = defaultFont;
} else if(opt[0].equals("-nodefontabstractname")) {
nodeFontAbstractName = opt[1];
} else if (opt[0].equals("-!nodefontabstractname")) {
nodeFontAbstractName = defaultItalicFont;
} else if(opt[0].equals("-nodefontsize")) {
nodeFontSize = Integer.parseInt(opt[1]);
} else if (opt[0].equals("-!nodefontsize")) {
nodeFontSize = 10;
} else if(opt[0].equals("-nodefontclassname")) {
nodeFontClassName = opt[1];
} else if (opt[0].equals("-!nodefontclassname")) {
nodeFontClassName = null;
} else if(opt[0].equals("-nodefontclassabstractname")) {
nodeFontClassAbstractName = opt[1];
} else if (opt[0].equals("-!nodefontclassabstractname")) {
nodeFontClassAbstractName = null;
} else if(opt[0].equals("-nodefontclasssize")) {
nodeFontClassSize = Integer.parseInt(opt[1]);
} else if (opt[0].equals("-!nodefontclasssize")) {
nodeFontClassSize = -1;
} else if(opt[0].equals("-nodefonttagname")) {
nodeFontTagName = opt[1];
} else if (opt[0].equals("-!nodefonttagname")) {
nodeFontTagName = null;
} else if(opt[0].equals("-nodefonttagsize")) {
nodeFontTagSize = Integer.parseInt(opt[1]);
} else if (opt[0].equals("-!nodefonttagsize")) {
nodeFontTagSize = -1;
} else if(opt[0].equals("-nodefontpackagename")) {
nodeFontPackageName = opt[1];
} else if (opt[0].equals("-!nodefontpackagename")) {
nodeFontPackageName = null;
} else if(opt[0].equals("-nodefontpackagesize")) {
nodeFontPackageSize = Integer.parseInt(opt[1]);
} else if (opt[0].equals("-!nodefontpackagesize")) {
nodeFontPackageSize = -1;
} else if(opt[0].equals("-nodefillcolor")) {
nodeFillColor = opt[1];
} else if (opt[0].equals("-!nodefillcolor")) {
nodeFillColor = null;
} else if(opt[0].equals("-output")) {
outputFileName = opt[1];
} else if (opt[0].equals("-!output")) {
outputFileName = "graph.dot";
} else if(opt[0].equals("-outputencoding")) {
outputEncoding = opt[1];
} else if (opt[0].equals("-!outputencoding")) {
outputEncoding = "ISO-8859-1";
} else if(opt[0].equals("-hide")) {
if(opt.length == 1) {
hidePatterns.clear();
hidePatterns.add(allPattern);
} else {
try {
hidePatterns.add(Pattern.compile(opt[1]));
} catch (PatternSyntaxException e) {
System.err.println("Skipping invalid pattern " + opt[1]);
}
}
} else if (opt[0].equals("-!hide")) {
hidePatterns.clear();
} else if(opt[0].equals("-apidocroot")) {
apiDocRoot = fixApiDocRoot(opt[1]);
} else if (opt[0].equals("-!apidocroot")) {
apiDocRoot = null;
} else if(opt[0].equals("-apidocmap")) {
setApiDocMapFile(opt[1]);
} else if (opt[0].equals("-!apidocmap")) {
apiDocMap.clear();
} else if(opt[0].equals("-noguillemot")) {
guilOpen = "&lt;&lt;";
guilClose = "&gt;&gt;";
} else if (opt[0].equals("-!noguillemot")) {
guilOpen = "\u00ab";
guilClose = "\u00bb";
} else if (opt[0].equals("-view")) {
viewName = opt[1];
} else if (opt[0].equals("-!view")) {
viewName = null;
} else if (opt[0].equals("-views")) {
findViews = true;
} else if (opt[0].equals("-!views")) {
findViews = false;
} else if (opt[0].equals("-d")) {
outputDirectory = opt[1];
} else if (opt[0].equals("-!d")) {
outputDirectory = null;
} else if(opt[0].equals("-inferrel")) {
inferRelationships = true;
} else if(opt[0].equals("-!inferrel")) {
inferRelationships = false;
} else if(opt[0].equals("-inferreltype")) {
try {
inferRelationshipType = RelationType.valueOf(opt[1].toUpperCase());
} catch(IllegalArgumentException e) {
System.err.println("Unknown association type " + opt[1]);
}
} else if(opt[0].equals("-!inferreltype")) {
inferRelationshipType = RelationType.NAVASSOC;
} else if(opt[0].equals("-inferdepvis")) {
try {
Visibility vis = Visibility.valueOf(opt[1].toUpperCase());
inferDependencyVisibility = vis;
} catch(IllegalArgumentException e) {
System.err.println("Ignoring invalid visibility specification for " +
"dependency inference: " + opt[1]);
}
} else if(opt[0].equals("-!inferdepvis")) {
inferDependencyVisibility = Visibility.PRIVATE;
} else if(opt[0].equals("-inferdep")) {
inferDependencies = true;
} else if(opt[0].equals("-!inferdep")) {
inferDependencies = false;
} else if(opt[0].equals("-inferdepinpackage")) {
inferDepInPackage = true;
} else if(opt[0].equals("-!inferdepinpackage")) {
inferDepInPackage = false;
} else if(opt[0].equals("-useimports")) {
useImports = true;
} else if(opt[0].equals("-!useimports")) {
useImports = false;
} else if (opt[0].equals("-collpackages")) {
try {
collPackages.add(Pattern.compile(opt[1]));
} catch (PatternSyntaxException e) {
System.err.println("Skipping invalid pattern " + opt[1]);
}
} else if (opt[0].equals("-!collpackages")) {
collPackages.clear();
} else if (opt[0].equals("-compact")) {
compact = true;
} else if (opt[0].equals("-!compact")) {
compact = false;
} else if (opt[0].equals("-postfixpackage")) {
postfixPackage = true;
} else if (opt[0].equals("-!postfixpackage")) {
postfixPackage = false;
} else if (opt[0].equals("-link")) {
addApiDocRoots(opt[1]);
} else if(opt[0].equals("-contextPattern")) {
RelationDirection d; RelationType rt;
try {
d = RelationDirection.valueOf(opt[2].toUpperCase());
if(opt[1].equalsIgnoreCase("all")) {
contextRelationPattern = new RelationPattern(d);
} else {
rt = RelationType.valueOf(opt[1].toUpperCase());
contextRelationPattern.addRelation(rt, d);
}
} catch(IllegalArgumentException e) {
}
} else
; // Do nothing, javadoc will handle the option or complain, if
// needed.
}
/**
* Adds api doc roots from a link. The folder reffered by the link should contain a package-list
* file that will be parsed in order to add api doc roots to this configuration
* @param packageListUrl
*/
private void addApiDocRoots(String packageListUrl) {
BufferedReader br = null;
packageListUrl = fixApiDocRoot(packageListUrl);
try {
URL url = new URL(packageListUrl + "/package-list");
br = new BufferedReader(new InputStreamReader(url.openStream()));
String line = null;
while((line = br.readLine()) != null) {
line = line + ".";
Pattern pattern = Pattern.compile(line.replace(".", "\\.") + "[^\\.]*");
apiDocMap.put(pattern, packageListUrl);
}
} catch(IOException e) {
System.err.println("Errors happened while accessing the package-list file at "
+ packageListUrl);
} finally {
if(br != null)
try {
br.close();
} catch (IOException e) {}
}
}
/**
* Loads the property file referred by <code>apiDocMapFileName</code> and fills the apiDocMap
* accordingly
* @param apiDocMapFileName
*/
void setApiDocMapFile(String apiDocMapFileName) {
try {
InputStream is = new FileInputStream(apiDocMapFileName);
Properties userMap = new Properties();
userMap.load(is);
for (Map.Entry mapEntry : userMap.entrySet()) {
try {
Pattern regex = Pattern.compile((String) mapEntry.getKey());
String thisRoot = (String) mapEntry.getValue();
if (thisRoot != null) {
thisRoot = fixApiDocRoot(thisRoot);
apiDocMap.put(regex, thisRoot);
} else {
System.err.println("No URL for pattern " + mapEntry.getKey());
}
} catch (PatternSyntaxException e) {
System.err.println("Skipping bad pattern " + mapEntry.getKey());
}
}
} catch (FileNotFoundException e) {
System.err.println("File " + apiDocMapFileName + " was not found: " + e);
} catch (IOException e) {
System.err.println("Error reading the property api map file " + apiDocMapFileName
+ ": " + e);
}
}
/**
* Returns the appropriate URL "root" for an external class name. It will
* match the class name against the regular expressions specified in the
* <code>apiDocMap</code>; if a match is found, the associated URL
* will be returned.
*
* <b>NOTE:</b> The match order of the match attempts is the one specified by the
* constructor of the api doc root, so it depends on the order of "-link" and "-apiDocMap"
* parameters.
*/
public String getApiDocRoot(String className) {
if(apiDocMap.isEmpty())
apiDocMap.put(Pattern.compile(".*"), DEFAULT_EXTERNAL_APIDOC);
for (Map.Entry<Pattern, String> mapEntry : apiDocMap.entrySet()) {
Pattern regex = mapEntry.getKey();
Matcher matcher = regex.matcher(className);
if (matcher.matches())
return mapEntry.getValue();
}
return null;
}
/** Trim and append a file separator to the string */
private String fixApiDocRoot(String str) {
String fixed = null;
if (str != null) {
fixed = str.trim();
if (fixed.length() > 0) {
if (!File.separator.equals("/"))
fixed = fixed.replace(File.separator.charAt(0), '/');
if (!fixed.endsWith("/"))
fixed = fixed + "/";
}
}
return fixed;
}
/** Set the options based on the command line parameters */
public void setOptions(String[][] options) {
for (String s[] : options)
setOption(s);
}
/** Set the options based on the tag elements of the ClassDoc parameter */
public void setOptions(ClassDoc p) {
if (p == null)
return;
for (Tag tag : p.tags("opt")) {
String[] opt = StringUtil.tokenize(tag.text());
opt[0] = "-" + opt[0];
setOption(opt);
}
}
/**
* Check if the supplied string matches an entity specified
* with the -hide parameter.
* @return true if the string matches.
*/
public boolean matchesHideExpression(String s) {
for (Pattern hidePattern : hidePatterns) {
// micro-optimization because the "all pattern" is heavily used in UmlGraphDoc
if(hidePattern == allPattern)
return true;
Matcher m = hidePattern.matcher(s);
if (strictMatching) {
if (m.matches()) {
return true;
}
} else if (m.find()) {
return true;
}
}
return false;
}
/**
* Check if the supplied string matches an entity specified
* with the -hide parameter.
* @return true if the string matches.
*/
public boolean matchesCollPackageExpression(String s) {
for (Pattern collPattern : collPackages) {
Matcher m = collPattern.matcher(s);
if (strictMatching) {
if (m.matches()) {
return true;
}
} else if (m.find()) {
return true;
}
}
return false;
}
// ----------------------------------------------------------------
// OptionProvider methods
// ----------------------------------------------------------------
public Options getOptionsFor(ClassDoc cd) {
Options localOpt = getGlobalOptions();
localOpt.setOptions(cd);
return localOpt;
}
public Options getOptionsFor(String name) {
return getGlobalOptions();
}
public Options getGlobalOptions() {
return (Options) clone();
}
public void overrideForClass(Options opt, ClassDoc cd) {
// nothing to do
}
public void overrideForClass(Options opt, String className) {
// nothing to do
}
public String getDisplayName() {
return "general class diagram";
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("UMLGRAPH OPTIONS\n");
for(Field f : this.getClass().getDeclaredFields()) {
if(!Modifier.isStatic(f.getModifiers())) {
f.setAccessible(true);
try {
sb.append(f.getName() + ":" + f.get(this) + "\n");
} catch (Exception e) {
}
}
}
return sb.toString();
}
}

View File

@ -1,26 +0,0 @@
package gr.spinellis.umlgraph.doclet;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
public class PackageMatcher implements ClassMatcher {
protected PackageDoc packageDoc;
public PackageMatcher(PackageDoc packageDoc) {
super();
this.packageDoc = packageDoc;
}
public boolean matches(ClassDoc cd) {
return cd.containingPackage().equals(packageDoc);
}
public boolean matches(String name) {
for (ClassDoc cd : packageDoc.allClasses()) {
if (cd.qualifiedName().equals(name))
return true;
}
return false;
}
}

View File

@ -1,69 +0,0 @@
package gr.spinellis.umlgraph.doclet;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.RootDoc;
/**
* A view designed for UMLDoc, filters out everything that it's not contained in
* the specified package.
* <p>
* As such, can be viewed as a simplified version of a {@linkplain View} using a
* 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
*
*/
public class PackageView implements OptionProvider {
private PackageDoc pd;
private OptionProvider parent;
private ClassMatcher matcher;
private String outputPath;
public PackageView(String outputFolder, PackageDoc pd, RootDoc root, OptionProvider parent) {
this.parent = parent;
this.pd = pd;
this.matcher = new PackageMatcher(pd);
this.outputPath = pd.name().replace('.', '/') + "/" + pd.name() + ".dot";
}
public String getDisplayName() {
return "Package view for package " + pd;
}
public Options getGlobalOptions() {
Options go = parent.getGlobalOptions();
go.setOption(new String[] { "-output", outputPath });
go.setOption(new String[] { "-hide" });
return go;
}
public Options getOptionsFor(ClassDoc cd) {
Options go = parent.getGlobalOptions();
overrideForClass(go, cd);
return go;
}
public Options getOptionsFor(String name) {
Options go = parent.getGlobalOptions();
overrideForClass(go, name);
return go;
}
public void overrideForClass(Options opt, ClassDoc cd) {
opt.showQualified = false;
if (!matcher.matches(cd) || parent.getGlobalOptions().matchesHideExpression(cd.name()))
opt.setOption(new String[] { "-hide" });
}
public void overrideForClass(Options opt, String className) {
opt.showQualified = false;
if (!matcher.matches(className))
opt.setOption(new String[] { "-hide" });
}
}

View File

@ -1,46 +0,0 @@
/*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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;
}
public boolean matches(ClassDoc cd) {
return matches(cd.toString());
}
public boolean matches(String name) {
Matcher matcher = pattern.matcher(name);
return matcher.matches();
}
}

View File

@ -1,51 +0,0 @@
package gr.spinellis.umlgraph.doclet;
/**
* The possibile directions of a relation given a reference class (used in
* context diagrams)
*/
public enum RelationDirection {
NONE, IN, OUT, BOTH;
/**
* Adds the current direction
* @param d
* @return
*/
public RelationDirection sum(RelationDirection d) {
if (this == NONE)
return d;
if ((this == IN && d == OUT) || (this == OUT && d == IN) || this == BOTH || d == BOTH)
return BOTH;
return this;
}
/**
* 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) {
if (this == BOTH)
return true;
else
return d == this;
}
/**
* Inverts the direction of the relation. Turns IN into OUT and vice-versa, NONE and BOTH
* are not changed
* @return
*/
public RelationDirection inverse() {
if (this == IN)
return OUT;
else if (this == OUT)
return IN;
else
return this;
}
};

View File

@ -1,50 +0,0 @@
package gr.spinellis.umlgraph.doclet;
/**
* A map from relation types to directions
* @author wolf
*
*/
public class RelationPattern {
/**
* A map from RelationType (indexes) to Direction objects
*/
RelationDirection[] directions;
/**
* 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;
}
}
/**
* 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);
}
/**
* 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;
}
}

View File

@ -1,10 +0,0 @@
package gr.spinellis.umlgraph.doclet;
/**
* The type of relation that links two entities
* @author wolf
*
*/
public enum RelationType {
ASSOC, NAVASSOC, HAS, COMPOSED, DEPEND, EXTENDS, IMPLEMENTS;
}

View File

@ -1,66 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.util.*;
/**
* String utility functions
* @version $Revision$
* @author <a href="http://www.spinellis.gr">Diomidis Spinellis</a>
*/
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;
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]);
}
}

View File

@ -1,41 +0,0 @@
package gr.spinellis.umlgraph.doclet;
import java.util.regex.Pattern;
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.
*/
public class SubclassMatcher implements ClassMatcher {
protected RootDoc root;
protected Pattern pattern;
public SubclassMatcher(RootDoc root, 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
if(cd.superclass() != null)
return matches(cd.superclass());
return false;
}
public boolean matches(String name) {
ClassDoc cd = root.classNamed(name);
if(cd == null)
return false;
return matches(cd);
}
}

View File

@ -1,9 +0,0 @@
package gr.spinellis.umlgraph.doclet;
/**
* Options for UMLGraph class diagram generation
* @opt operations
* @opt visibility
* @hidden
*/
public class UMLOptions {}

View File

@ -1,170 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.LanguageVersion;
import com.sun.javadoc.RootDoc;
/**
* Doclet API implementation
* @depend - - - OptionProvider
* @depend - - - Options
* @depend - - - View
* @depend - - - ClassGraph
* @depend - - - Version
*
* @version $Revision$
* @author <a href="http://www.spinellis.gr">Diomidis Spinellis</a>
*/
public class UmlGraph {
private static final String programName = "UmlGraph";
private static final String docletName = "gr.spinellis.umlgraph.doclet.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");
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);
}
/**
* Creates the base Options object, that contains both the options specified on the command
* line and the ones specified in the UMLOptions class, if available.
*/
public static Options buildOptions(RootDoc root) {
Options opt = new Options();
opt.setOptions(root.options());
opt.setOptions(findUMLOptions(root));
return opt;
}
private static ClassDoc findUMLOptions(RootDoc root) {
ClassDoc[] classes = root.classes();
for (ClassDoc cd : classes)
if(cd.name().equals("UMLOptions"))
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 {
Options opt = op.getGlobalOptions();
root.printNotice("Building " + op.getDisplayName());
ClassDoc[] classes = root.classes();
ClassGraph c = new ClassGraph(root, op, contextDoc);
c.prologue();
for (int i = 0; i < classes.length; i++)
c.printClass(classes[i], true);
for (int i = 0; i < classes.length; i++)
c.printRelations(classes[i]);
if(opt.inferRelationships)
c.printInferredRelations(classes);
if(opt.inferDependencies)
c.printInferredDependencies(classes);
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)
*/
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();
// 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];
}
/**
* 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);
return new View(root, viewClass, buildView(root, superClass, provider));
}
/** Option checking */
public static int optionLength(String option) {
return Options.optionLength(option);
}
/** Indicate the language version we support */
public static LanguageVersion languageVersion() {
return LanguageVersion.JAVA_1_5;
}
}

View File

@ -1,242 +0,0 @@
package gr.spinellis.umlgraph.doclet;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.LanguageVersion;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.RootDoc;
import com.sun.tools.doclets.standard.Standard;
/**
* Chaining doclet that runs the standart Javadoc doclet first, and on success,
* runs the generation of dot files by UMLGraph
* @author wolf
*
* @depend - - - WrappedClassDoc
* @depend - - - WrappedRootDoc
*/
public class UmlGraphDoc {
/**
* Option check, forwards options to the standard doclet, if that one refuses them,
* they are sent to UmlGraph
*/
public static int optionLength(String option) {
int result = Standard.optionLength(option);
if (result != 0)
return result;
else
return UmlGraph.optionLength(option);
}
/**
* 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());
Options opt = new Options();
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());
root = new WrappedRootDoc(root);
generatePackageDiagrams(root, opt, outputFolder);
generateContextDiagrams(root, opt, outputFolder);
} catch(Throwable t) {
root.printWarning("Error!");
root.printWarning(t.toString());
t.printStackTrace();
return false;
}
return true;
}
/**
* Standand doclet entry
* @return
*/
public static LanguageVersion languageVersion() {
return Standard.languageVersion();
}
/**
* Generates the package diagrams for all of the packages that contain classes among those
* returned by RootDoc.class()
*/
private static void generatePackageDiagrams(RootDoc root, Options opt, String outputFolder)
throws IOException {
Set<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(outputFolder, packageDoc.name(), packageDoc.name(), root);
alterHtmlDocs(outputFolder, packageDoc.name(), packageDoc.name(),
"package-summary.html", Pattern.compile("</H2>"), root);
}
}
}
/**
* Generates the context diagram for a single class
*/
private static void generateContextDiagrams(RootDoc root, Options opt, String outputFolder)
throws IOException {
ContextView view = null;
for (ClassDoc classDoc : root.classes()) {
if(view == null)
view = new ContextView(outputFolder, classDoc, root, opt);
else
view.setContextCenter(classDoc);
UmlGraph.buildGraph(root, view, classDoc);
runGraphviz(outputFolder, classDoc.containingPackage().name(), classDoc.name(), root);
alterHtmlDocs(outputFolder, classDoc.containingPackage().name(), classDoc.name(),
classDoc.name() + ".html", Pattern.compile("(Class|Interface|Enum) " + classDoc.name() + ".*") , root);
}
}
/**
* Runs Graphviz dot building both a diagram (in png format) and a client side map for it.
* <p>
* At the moment, it assumes dot.exe is in the classpahth
*/
private static void runGraphviz(String outputFolder, String packageName, String name, RootDoc root) {
File dotFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".dot");
File pngFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".png");
File mapFile = new File(outputFolder, packageName.replace(".", "/") + "/" + name + ".map");
try {
Process p = Runtime.getRuntime().exec(new String [] {
"dot",
"-Tcmapx",
"-o",
mapFile.getAbsolutePath(),
"-Tpng",
"-o",
pngFile.getAbsolutePath(),
dotFile.getAbsolutePath()
});
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = null;
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();
}
}
/**
* 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(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");
File mapFile = new File(output, className + ".map");
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 {
int BUFSIZE = (int) Math.pow(2, 20); // more or less one megabyte
writer = new BufferedWriter(new FileWriter(alteredFile), BUFSIZE);
reader = new BufferedReader(new FileReader(htmlFile));
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
if (!matched && insertPointPattern.matcher(line).matches()) {
matched = true;
if (mapFile.exists())
insertClientSideMap(mapFile, writer);
else
root.printWarning("Could not find map file " + mapFile);
writer.write("<div align=\"center\"><img src=\"" + className
+ ".png\" alt=\"Package class diagram package " + className
+ "\" usemap=\"#G\" border=0/></a></div>");
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();
}
}
/**
* Reads the map file and outputs in to the specified writer
* @throws IOException
*/
private static void insertClientSideMap(File mapFile, BufferedWriter writer) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(mapFile));
String line = null;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} finally {
if (reader != null)
reader.close();
}
}
/**
* 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 ".";
}
}

View File

@ -1,180 +0,0 @@
/*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
/**
* Contains the definition of a View. A View is a set of option overrides that
* 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
* @depend - - - ClassMatcher
* @depend - - - InterfaceMatcher
* @depend - - - PatternMatcher
* @depend - - - SubclassMatcher
* @depend - - - ContextMatcher
*
*/
public class View implements OptionProvider {
Map<ClassMatcher, List<String[]>> optionOverrides = new LinkedHashMap<ClassMatcher, List<String[]>>();
ClassDoc viewDoc;
OptionProvider provider;
List<String[]> globalOptions;
RootDoc root;
/**
* 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);
}
}
}
}
/**
* 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;
}
// ----------------------------------------------------------------
// OptionProvider methods
// ----------------------------------------------------------------
public Options getOptionsFor(ClassDoc cd) {
Options localOpt = getGlobalOptions();
overrideForClass(localOpt, cd);
localOpt.setOptions(cd);
return localOpt;
}
public Options getOptionsFor(String name) {
Options localOpt = getGlobalOptions();
overrideForClass(localOpt, name);
return localOpt;
}
public Options getGlobalOptions() {
Options go = provider.getGlobalOptions();
boolean outputSet = false;
for (String[] opts : globalOptions) {
if (opts[0].equals("-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);
}
}
}
}
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);
}
}
}
}
public String getDisplayName() {
return "view " + viewDoc.name();
}
}

View File

@ -1,44 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
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 {
PRIVATE, PACKAGE, PROTECTED, PUBLIC;
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;
}
}

View File

@ -1,362 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.AnnotationTypeDoc;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ConstructorDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.ParamTag;
import com.sun.javadoc.ParameterizedType;
import com.sun.javadoc.SeeTag;
import com.sun.javadoc.SourcePosition;
import com.sun.javadoc.Tag;
import com.sun.javadoc.Type;
import com.sun.javadoc.TypeVariable;
import com.sun.javadoc.WildcardType;
/**
* A ClassDoc wrapper that caches answer to the most common requests performed
* by UMLGraph, considerably improving the overall UMLDoc performance (ClassDoc
* computes most of the results for more fine grained information at each call).
* <p>
* Unfortunately this has a side effect, since it breaks the equals() call between
* plain ClassDoc instances and WrappedClassDoc ones, so use it with due care.
* <p>
* In particular, don't provide WrappedClassDoc instances to the standard doclet.
* @author wolf
*
*/
public class WrappedClassDoc implements ClassDoc {
ClassDoc wrapped;
String toString;
String name;
Tag[] tags;
public WrappedClassDoc(ClassDoc wrapped) {
this.wrapped = wrapped;
}
public AnnotationDesc[] annotations() {
return wrapped.annotations();
}
public AnnotationTypeDoc asAnnotationTypeDoc() {
return wrapped.asAnnotationTypeDoc();
}
public ClassDoc asClassDoc() {
return wrapped.asClassDoc();
}
public ParameterizedType asParameterizedType() {
return wrapped.asParameterizedType();
}
public TypeVariable asTypeVariable() {
return wrapped.asTypeVariable();
}
public WildcardType asWildcardType() {
return wrapped.asWildcardType();
}
public String commentText() {
return wrapped.commentText();
}
public int compareTo(Object arg0) {
if (arg0 instanceof WrappedClassDoc) {
WrappedClassDoc other = (WrappedClassDoc) arg0;
return wrapped.compareTo(other.wrapped);
}
return wrapped.compareTo(arg0);
}
public ConstructorDoc[] constructors() {
return wrapped.constructors();
}
public ConstructorDoc[] constructors(boolean arg0) {
return wrapped.constructors(arg0);
}
public ClassDoc containingClass() {
return wrapped.containingClass();
}
public PackageDoc containingPackage() {
return wrapped.containingPackage();
}
public boolean definesSerializableFields() {
return wrapped.definesSerializableFields();
}
public String dimension() {
return wrapped.dimension();
}
public FieldDoc[] enumConstants() {
return wrapped.enumConstants();
}
public FieldDoc[] fields() {
return wrapped.fields();
}
public FieldDoc[] fields(boolean arg0) {
return wrapped.fields(arg0);
}
public ClassDoc findClass(String arg0) {
return wrapped.findClass(arg0);
}
public Tag[] firstSentenceTags() {
return wrapped.firstSentenceTags();
}
public String getRawCommentText() {
return wrapped.getRawCommentText();
}
/** @deprecated */
public @Deprecated ClassDoc[] importedClasses() {
return wrapped.importedClasses();
}
/** @deprecated */
public @Deprecated PackageDoc[] importedPackages() {
return wrapped.importedPackages();
}
public Tag[] inlineTags() {
return wrapped.inlineTags();
}
public ClassDoc[] innerClasses() {
return wrapped.innerClasses();
}
public ClassDoc[] innerClasses(boolean arg0) {
return wrapped.innerClasses(arg0);
}
public ClassDoc[] interfaces() {
return wrapped.interfaces();
}
public Type[] interfaceTypes() {
return wrapped.interfaceTypes();
}
public boolean isAbstract() {
return wrapped.isAbstract();
}
public boolean isAnnotationType() {
return wrapped.isAnnotationType();
}
public boolean isAnnotationTypeElement() {
return wrapped.isAnnotationTypeElement();
}
public boolean isClass() {
return wrapped.isClass();
}
public boolean isConstructor() {
return wrapped.isConstructor();
}
public boolean isEnum() {
return wrapped.isEnum();
}
public boolean isEnumConstant() {
return wrapped.isEnumConstant();
}
public boolean isError() {
return wrapped.isError();
}
public boolean isException() {
return wrapped.isException();
}
public boolean isExternalizable() {
return wrapped.isExternalizable();
}
public boolean isField() {
return wrapped.isField();
}
public boolean isFinal() {
return wrapped.isFinal();
}
public boolean isIncluded() {
return wrapped.isIncluded();
}
public boolean isInterface() {
return wrapped.isInterface();
}
public boolean isMethod() {
return wrapped.isMethod();
}
public boolean isOrdinaryClass() {
return wrapped.isOrdinaryClass();
}
public boolean isPackagePrivate() {
return wrapped.isPackagePrivate();
}
public boolean isPrimitive() {
return wrapped.isPrimitive();
}
public boolean isPrivate() {
return wrapped.isPrivate();
}
public boolean isProtected() {
return wrapped.isProtected();
}
public boolean isPublic() {
return wrapped.isPublic();
}
public boolean isSerializable() {
return wrapped.isSerializable();
}
public boolean isStatic() {
return wrapped.isStatic();
}
public MethodDoc[] methods() {
return wrapped.methods();
}
public MethodDoc[] methods(boolean arg0) {
return wrapped.methods(arg0);
}
public String modifiers() {
return wrapped.modifiers();
}
public int modifierSpecifier() {
return wrapped.modifierSpecifier();
}
public String name() {
if (name == null)
name = wrapped.name();
return name;
}
public SourcePosition position() {
return wrapped.position();
}
public String qualifiedName() {
return wrapped.qualifiedName();
}
public String qualifiedTypeName() {
return wrapped.qualifiedTypeName();
}
public SeeTag[] seeTags() {
return wrapped.seeTags();
}
public FieldDoc[] serializableFields() {
return wrapped.serializableFields();
}
public MethodDoc[] serializationMethods() {
return wrapped.serializationMethods();
}
public void setRawCommentText(String arg0) {
wrapped.setRawCommentText(arg0);
}
public String simpleTypeName() {
return wrapped.simpleTypeName();
}
public boolean subclassOf(ClassDoc arg0) {
return wrapped.subclassOf(arg0);
}
public ClassDoc superclass() {
return wrapped.superclass();
}
public Type superclassType() {
return wrapped.superclassType();
}
public Tag[] tags() {
if (tags == null)
tags = wrapped.tags();
return tags;
}
public Tag[] tags(String arg0) {
return wrapped.tags(arg0);
}
public String toString() {
if (toString == null) {
toString = wrapped.toString();
}
return toString;
}
public String typeName() {
return wrapped.typeName();
}
public TypeVariable[] typeParameters() {
return wrapped.typeParameters();
}
public ParamTag[] typeParamTags() {
return wrapped.typeParamTags();
}
}

View File

@ -1,192 +0,0 @@
/*
* Create a graphviz graph based on the classes in the specified java
* source files.
*
* (C) Copyright 2002-2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.doclet;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.SeeTag;
import com.sun.javadoc.SourcePosition;
import com.sun.javadoc.Tag;
/**
* RootDoc wrapper that provides WrappedClassDoc instances instead of plain ClassDoc in order
* to optimize the overall performance of UMLDoc.
* @author wolf
*/
public class WrappedRootDoc implements RootDoc {
RootDoc wrapped;
WrappedClassDoc[] wrappedClassDocs;
public WrappedRootDoc(RootDoc wrapped) {
this.wrapped = wrapped;
ClassDoc[] classes = wrapped.classes();
wrappedClassDocs = new WrappedClassDoc[classes.length];
for (int i = 0; i < classes.length; i++) {
wrappedClassDocs[i] = new WrappedClassDoc(classes[i]);
}
}
public ClassDoc[] classes() {
return wrappedClassDocs;
}
public ClassDoc classNamed(String arg0) {
return wrapped.classNamed(arg0);
}
public String commentText() {
return wrapped.commentText();
}
public int compareTo(Object arg0) {
return wrapped.compareTo(arg0);
}
public Tag[] firstSentenceTags() {
return wrapped.firstSentenceTags();
}
public String getRawCommentText() {
return wrapped.getRawCommentText();
}
public Tag[] inlineTags() {
return wrapped.inlineTags();
}
public boolean isAnnotationType() {
return wrapped.isAnnotationType();
}
public boolean isAnnotationTypeElement() {
return wrapped.isAnnotationTypeElement();
}
public boolean isClass() {
return wrapped.isClass();
}
public boolean isConstructor() {
return wrapped.isConstructor();
}
public boolean isEnum() {
return wrapped.isEnum();
}
public boolean isEnumConstant() {
return wrapped.isEnumConstant();
}
public boolean isError() {
return wrapped.isError();
}
public boolean isException() {
return wrapped.isException();
}
public boolean isField() {
return wrapped.isField();
}
public boolean isIncluded() {
return wrapped.isIncluded();
}
public boolean isInterface() {
return wrapped.isInterface();
}
public boolean isMethod() {
return wrapped.isMethod();
}
public boolean isOrdinaryClass() {
return wrapped.isOrdinaryClass();
}
public String name() {
return wrapped.name();
}
public String[][] options() {
return wrapped.options();
}
public PackageDoc packageNamed(String arg0) {
return wrapped.packageNamed(arg0);
}
public SourcePosition position() {
return wrapped.position();
}
public void printError(SourcePosition arg0, String arg1) {
wrapped.printError(arg0, arg1);
}
public void printError(String arg0) {
wrapped.printError(arg0);
}
public void printNotice(SourcePosition arg0, String arg1) {
wrapped.printNotice(arg0, arg1);
}
public void printNotice(String arg0) {
wrapped.printNotice(arg0);
}
public void printWarning(SourcePosition arg0, String arg1) {
wrapped.printWarning(arg0, arg1);
}
public void printWarning(String arg0) {
wrapped.printWarning(arg0);
}
public SeeTag[] seeTags() {
return wrapped.seeTags();
}
public void setRawCommentText(String arg0) {
wrapped.setRawCommentText(arg0);
}
public ClassDoc[] specifiedClasses() {
return wrapped.specifiedClasses();
}
public PackageDoc[] specifiedPackages() {
return wrapped.specifiedPackages();
}
public Tag[] tags() {
return wrapped.tags();
}
public Tag[] tags(String arg0) {
return wrapped.tags(arg0);
}
}

View File

@ -1,152 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* UmlGraph regression tests
* @author wolf
*
*/
public class BasicTest {
static String testSourceFolder = "testdata/java";
static String testDestFolder = "testdata/dot-out";
static String testRefFolder = "testdata/dot-ref";
static PrintWriter pw = new PrintWriter(System.out);
public static void main(String[] args) throws IOException {
List<String> differences = new ArrayList<String>();
File outFolder = new File(testDestFolder);
if (!outFolder.exists())
outFolder.mkdirs();
TestUtils.cleanFolder(outFolder, true);
// don't use windows specific fonts
System.setProperty("os.name", "generic");
// run tests
performBasicTests(differences);
performViewTests(differences, outFolder);
if (differences.size() > 0) {
pw.println("ERROR, some files are not structurally equal or some files are missing:");
for (String className : differences) {
pw.println(className);
}
} else {
pw.println("GOOD, all files are structurally equal");
}
pw.println();
pw.println();
pw.flush();
}
private static void performViewTests(List<String> differences, File outFolder)
throws IOException {
String[] options = new String[] { "-docletpath", "build", "-private", "-d",
outFolder.getAbsolutePath(), "-sourcepath", "testdata/java", "-compact",
"-subpackages", "gr.spinellis", "-views" };
runDoclet(options);
List<String> viewFiles = new ArrayList<String>();
viewFiles.addAll(getViewList(new File(testSourceFolder, "gr/spinellis/basic/views")));
viewFiles.addAll(getViewList(new File(testSourceFolder, "gr/spinellis/context/views")));
viewFiles.addAll(getViewList(new File(testSourceFolder, "gr/spinellis/iface/views")));
viewFiles.addAll(getViewList(new File(testSourceFolder, "gr/spinellis/subclass/views")));
for (String fileName : viewFiles) {
String viewName = fileName.substring(0, fileName.length() - 5);
File dotFile = new File(testDestFolder, viewName + ".dot");
File refFile = new File(testRefFolder, viewName + ".dot");
if (viewName.contains("Abstract")) {
// make sure abstract views are not generated
if (dotFile.exists()) {
pw.println("Error, abstract view " + viewName + " has been generated");
differences.add(dotFile.getName() + " should not be there");
}
} else {
compare(differences, dotFile, refFile);
}
}
}
private static List<String> getViewList(File viewFolder) {
if (!viewFolder.exists())
throw new RuntimeException("The folder " + viewFolder.getAbsolutePath()
+ " does not exists.");
else if (!viewFolder.isDirectory())
throw new RuntimeException(viewFolder.getAbsolutePath() + " is not a folder!.");
else if (!viewFolder.canRead())
throw new RuntimeException("The folder " + viewFolder.getAbsolutePath()
+ " cannot be read.");
return Arrays.asList(viewFolder.list(new SimpleFileFilter(".java")));
}
private static boolean performBasicTests(List<String> differences) throws IOException {
String[] javaFiles = new File(testSourceFolder).list(new SimpleFileFilter(".java"));
boolean equal = true;
for (int i = 0; i < javaFiles.length; i++) {
String javaFileName = javaFiles[i].substring(0, javaFiles[i].length() - 5);
String outFileName = javaFileName + ".dot";
File dotFile = new File(testDestFolder, outFileName);
dotFile.delete();
File refFile = new File(testRefFolder, outFileName);
String javaPath = new File(testSourceFolder, javaFiles[i]).getAbsolutePath();
String[] options = new String[] { "-docletpath", "build", "-hide", "Hidden",
"-compact", "-private", "-d", testDestFolder, "-output", outFileName, javaPath };
runDoclet(options);
compare(differences, dotFile, refFile);
}
return equal;
}
private static void runDoclet(String[] options) {
com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw,
"gr.spinellis.umlgraph.doclet.UmlGraph", options);
}
private static void compare(List<String> differences, File dotFile, File refFile)
throws IOException {
if (!dotFile.exists()) {
pw.println("Error, output file " + dotFile + " has not been generated");
differences.add(dotFile.getName() + " has not been generated");
} else if (!refFile.exists()) {
pw.println("Error, reference file " + refFile + " is not available");
differences.add(refFile.getName() + " reference is not available");
} else if (!TestUtils.dotFilesEqual(pw, dotFile.getAbsolutePath(), refFile
.getAbsolutePath())) {
differences.add(dotFile.getName() + " is different from the reference");
}
}
}

View File

@ -1,308 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class DotDiff {
private List<DotNode> nodes1 = new ArrayList<DotNode>();
private List<DotNode> nodes2 = new ArrayList<DotNode>();
private List<DotArc> arcs1 = new ArrayList<DotArc>();
private List<DotArc> arcs2 = new ArrayList<DotArc>();
private List<String> extraLines1 = new ArrayList<String>();
private List<String> extraLines2 = new ArrayList<String>();
/**
* Builds a dot differ on the two files
*
* @param dotFirst
* @param dotSecond
* @throws IOException
*/
public DotDiff(File dotFirst, File dotSecond) throws IOException {
// gather the lines
List<String> lines1 = readGraphLines(dotFirst);
List<String> lines2 = readGraphLines(dotSecond);
// parse the lines
extraLines1 = parseLines(lines1, nodes1, arcs1);
extraLines2 = parseLines(lines2, nodes2, arcs2);
// diff extra lines
for (Iterator<String> it = extraLines1.iterator(); it.hasNext();) {
if (extraLines2.remove(it.next()))
it.remove();
}
// diff nodes
for (Iterator<DotNode> it = nodes1.iterator(); it.hasNext();) {
if (nodes2.remove(it.next()))
it.remove();
}
// diff arcs
for (Iterator<DotArc> it = arcs1.iterator(); it.hasNext();) {
if (arcs2.remove(it.next()))
it.remove();
}
}
/**
* Returns true if the dot files are structurally equal, that is, if every
* non comment and non header line of the first file appears in the second,
* and otherwise.
*
* @return
*/
public boolean graphEquals() {
return (extraLines1.size() + extraLines2.size() + nodes1.size() + nodes2.size()
+ arcs1.size() + arcs2.size()) == 0;
}
public List<DotArc> getArcs1() {
return arcs1;
}
public List<DotArc> getArcs2() {
return arcs2;
}
public List<String> getExtraLines1() {
return extraLines1;
}
public List<String> getExtraLines2() {
return extraLines2;
}
public List<DotNode> getNodes1() {
return nodes1;
}
public List<DotNode> getNodes2() {
return nodes2;
}
/**
* Reads all relevant lines from the dot file
*
* @param dotFile
* @return
* @throws IOException
*/
private List<String> readGraphLines(File dotFile) throws IOException {
List<String> lines = new ArrayList<String>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(dotFile));
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (graphDefinitionLine(line))
lines.add(line);
}
} finally {
if (br != null)
br.close();
}
return lines;
}
/**
* Tells if a line is relevant or not (unrelevant lines are headers,
* comments, "digraf G {" and the closing "}" (to simplify matters we assume
* the file is properly structured).
*
* @param line
* @return
*/
private boolean graphDefinitionLine(String line) {
return !(line.startsWith("#") || line.startsWith("//") || line.equals("digraph G {") || line
.equals("}"));
}
private List<String> parseLines(List<String> lines, List<DotNode> nodeList, List<DotArc> arcs)
throws IOException {
List<String> extraLines = new ArrayList<String>();
List<String> arcLines = new ArrayList<String>();
Map<String, DotNode> nodes = new HashMap<String, DotNode>();
for (String line : lines) {
int openBrackedIdx = line.indexOf('[');
int closedBracketIdx = line.lastIndexOf(']');
int arrowIdx = line.indexOf("->");
if (openBrackedIdx < 0 && closedBracketIdx < 0 || line.startsWith("edge")
|| line.startsWith("node"))
extraLines.add(line);
else if (arrowIdx > 0 && arrowIdx < openBrackedIdx) { // that's an arc
arcLines.add(line);
} else { // that's a node
String attributes = line.substring(openBrackedIdx + 1, closedBracketIdx);
Map<String, String> attMap = parseAttributes(attributes);
String name = line.substring(0, openBrackedIdx - 1).trim();
String label = attMap.get("label");
DotNode node = new DotNode(name, label, attMap, line);
nodes.put(name, node);
nodeList.add(node);
}
}
for (String line : arcLines) {
int openBrackedIdx = line.indexOf('[');
int closedBracketIdx = line.lastIndexOf(']');
String attributes = line.substring(openBrackedIdx + 1, closedBracketIdx);
String[] names = line.substring(0, openBrackedIdx).split("->");
DotNode from = nodes.get(names[0].trim());
DotNode to = nodes.get(names[1].trim());
if (from == null) {
from = new DotNode(names[0], "", new HashMap<String, String>(), "");
}
if (to == null) {
to = new DotNode(names[1], "", new HashMap<String, String>(), "");
}
arcs.add(new DotArc(from, to, parseAttributes(attributes), line));
}
return extraLines;
}
private Map<String, String> parseAttributes(String attributes) throws IOException {
Map<String, String> map = new HashMap<String, String>();
StreamTokenizer st = new StreamTokenizer(new StringReader(attributes));
st.wordChars('_', '_');
st.wordChars('\\', '\\');
st.wordChars('\'', '\'');
int tokenType;
boolean isValue = false;
String attName = null;
String token = null;
while ((tokenType = st.nextToken()) != StreamTokenizer.TT_EOF) {
switch (tokenType) {
case StreamTokenizer.TT_NUMBER:
token = "" + st.nval;
break;
case StreamTokenizer.TT_WORD:
case '"':
case '\'':
token = st.sval;
break;
}
tokenType = st.nextToken();
if (isValue) {
map.put(attName, token);
isValue = false;
} else {
attName = token;
isValue = true;
}
}
return map;
}
private static class DotNode {
String name;
String label;
Map<String, String> attributes;
String line;
public DotNode(String name, String label, Map<String, String> attributes, String line) {
this.name = name;
this.label = label.replace("\n", "\\n");
this.attributes = attributes;
this.line = line.replace("\n", "\\n");
}
public boolean equals(Object other) {
if (!(other instanceof DotNode))
return false;
DotNode on = (DotNode) other;
if (label == null) // anonymous node
return on.label == null && on.name.equals(name) && on.attributes.equals(attributes);
else
return on.label.equals(label) && on.attributes.equals(attributes);
}
public int hashCode() {
return name.hashCode() + 17 * attributes.hashCode();
}
public String toString() {
return "Node: " + label + "; " + line;
}
}
private static class DotArc {
DotNode from;
DotNode to;
Map<String, String> attributes;
String line;
public DotArc(DotNode from, DotNode to, Map<String, String> attributes, String line) {
this.from = from;
this.to = to;
this.attributes = attributes;
this.line = line.replace("\n", "\\n");
}
public boolean equals(Object other) {
if (!(other instanceof DotArc))
return false;
DotArc oa = (DotArc) other;
return oa.from.equals(from) && oa.to.equals(to) && oa.attributes.equals(attributes);
}
public int hashCode() {
return from.hashCode() + 17 * (to.hashCode() + 17 * attributes.hashCode());
}
public String toString() {
return "Arc: " + from.label + " -> " + to.label + "; " + line;
}
}
}

View File

@ -1,48 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.File;
import java.io.PrintWriter;
public class RunDoc {
static String sourcesFolder = "src";
static String docFolder = "javadoc";
static PrintWriter pw = new PrintWriter(System.out);
public static void main(String[] args) {
File outFolder = new File(docFolder);
if (!outFolder.exists())
outFolder.mkdirs();
String[] options = new String[] { "-docletpath", "build", "-private", "-d", docFolder,
"-sourcepath", sourcesFolder, "-subpackages", "gr.spinellis" };
runDoclet(options);
}
private static void runDoclet(String[] options) {
com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw,
"gr.spinellis.umlgraph.doclet.UmlGraphDoc", options);
}
}

View File

@ -1,61 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.File;
import java.io.PrintWriter;
public class RunOne {
static String testSourceFolder = "testdata/java/";
static String testDestFolder = "testdata/dot-out";
static PrintWriter pw = new PrintWriter(System.out);
public static void main(String[] args) {
File outFolder = new File(testDestFolder);
if (!outFolder.exists())
outFolder.mkdirs();
// runView("gr.spinellis.views.ViewChildEmpty");
runSingleClass("TestHiddenOp");
}
public static void runView(String viewClass) {
String[] options = new String[] { "-docletpath", "build", "-private", "-d",
testDestFolder, "-sourcepath", "testdata/java", "-subpackages",
"gr.spinellis", "-view", viewClass};
runDoclet(options);
}
public static void runSingleClass(String className) {
String[] options = new String[] { "-docletpath", "build", "-hide", "Hidden",
"-private", "-d", testDestFolder, "-output", className + ".dot", testSourceFolder + className + ".java"};
runDoclet(options);
}
private static void runDoclet(String[] options) {
com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw, "gr.spinellis.umlgraph.doclet.UmlGraph", options);
}
}

View File

@ -1,35 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.File;
import java.io.FilenameFilter;
class SimpleFileFilter implements FilenameFilter {
protected String extension;
public SimpleFileFilter(String ext) {
this.extension = ext;
}
public boolean accept(File dir, String name) {
return name.endsWith(extension);
}
}

View File

@ -1,136 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
/**
* Collection of utility methods used by the test classes
* @author wolf
*
*/
public class TestUtils {
/**
* Simple text file diffing: will tell you if two text files are line by
* line equals, and will stop at the first difference found.
* @throws IOException
*/
public static boolean textFilesEquals(PrintWriter pw, File refTextFile, File outTextFile)
throws IOException {
BufferedReader refReader = null, outReader = null;
String refLine = null, outFile = null;
try {
pw.println("Performing diff:");
pw.println("out: " + outTextFile.getAbsolutePath());
pw.println("ref: " + refTextFile.getAbsolutePath());
refReader = new BufferedReader(new FileReader(refTextFile));
outReader = new BufferedReader(new FileReader(outTextFile));
// line by line scan, exit when one file ends or lines are not
// equal (and pass over the "Generated by javadoc ..." comments)
for (;;) {
refLine = refReader.readLine();
outFile = outReader.readLine();
if (refLine == null || outFile == null) {
break;
} else if (refLine.startsWith("<!-- Generated by javadoc ")) {
if (!outFile.startsWith("<!-- Generated by javadoc "))
break;
} else if (!refLine.equals(outFile)) {
break;
}
}
} finally {
if (refReader != null)
refReader.close();
if (outReader != null)
outReader.close();
}
// they were equals if both files ended at the same time
boolean equal = refLine == null && outFile == null;
if (equal)
pw.print("File contents are equal");
else
pw.println("Differences found\nref: " + refLine + "\nout: " + outFile);
pw.println();
pw.println();
return equal;
}
public static boolean dotFilesEqual(PrintWriter pw, String dotPath, String refPath)
throws IOException {
pw.println("Performing diff:\nout:" + dotPath + "\nref:" + refPath);
DotDiff differ = new DotDiff(new File(dotPath), new File(refPath));
boolean equal = differ.graphEquals();
if (equal) {
pw.println("File contents are structurally equal");
} else {
pw.println("File contents are structurally not equal");
printList(pw, "# Lines in out but not in ref", differ.getExtraLines1());
printList(pw, "# Lines in ref but not in out", differ.getExtraLines2());
printList(pw, "# Nodes in out but not in ref", differ.getNodes1());
printList(pw, "# Nodes in ref but not in out", differ.getNodes2());
printList(pw, "# Arcs in out but not in ref", differ.getArcs1());
printList(pw, "# Arcs in ref but not in out", differ.getArcs2());
}
pw.println("\n\n");
return equal;
}
public static void printList(PrintWriter pw, String message, List extraOut) {
if (extraOut.size() > 0) {
pw.println(message);
for (Object o : extraOut) {
pw.println(o);
}
}
}
/**
* Deletes the content of the folder, eventually in a recursive way (but
* avoids deleting eventual .cvsignore files and CVS folders)
*/
public static void cleanFolder(File folder, boolean recurse) {
for (File f : folder.listFiles()) {
if (f.isDirectory() && !f.getName().equals("CVS")) {
if (recurse) {
cleanFolder(f, true);
if (f.list().length == 0)
f.delete();
}
} else if (!f.getName().equals(".cvsignore")) {
f.delete();
}
}
}
}

View File

@ -1,160 +0,0 @@
/*
* UmlGraph class diagram testing framework
*
* Contibuted by Andrea Aime
* (C) Copyright 2005 Diomidis Spinellis
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id$
*
*/
package gr.spinellis.umlgraph.test;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* UmlGraphDoc doclet regression tests
* @author wolf
*
*/
public class UmlDocTest {
static final String testSourceFolder = "testdata/umldoc-src";
static final String testDestFolder = "testdata/umldoc-out";
static final String testRefFolder = "testdata/umldoc-ref";
static final String doclet = "gr.spinellis.umlgraph.doclet.UmlGraphDoc";
static PrintWriter pw = new PrintWriter(System.out);
public static void main(String[] args) throws IOException {
List<String> differences = new ArrayList<String>();
File outFolder = new File(testDestFolder);
if (!outFolder.exists())
outFolder.mkdirs();
TestUtils.cleanFolder(outFolder, true);
// don't use windows specific fonts
System.setProperty("os.name", "generic");
// run tests
runTest(differences);
if (differences.size() > 0) {
pw.println("ERROR, some files are not structurally equal or some files are missing:");
for (String className : differences) {
pw.println(className);
}
} else {
pw.println("GOOD, all files are structurally equal");
}
pw.println();
pw.println();
pw.flush();
}
private static void runTest(List<String> differences) throws IOException {
File outFolder = new File(testDestFolder);
String[] options = new String[] { "-docletpath", "build", "-private", "-d",
outFolder.getAbsolutePath(), "-sourcepath", testSourceFolder, "-compact",
"-subpackages", "gr.spinellis", "-inferrel", "-inferdep", "-qualify",
"-postfixpackage", "-collpackages", "java.util.*" };
runDoclet(options);
compareDocletOutputs(differences, new File(testRefFolder), new File(testDestFolder));
}
/**
* Ensures that reference and output have the same contents in terms of:
* <ul>
* <li> html files </li>
* <li> dot files </li>
* <li> folders </li>
* </ul>
* @throws IOException
*/
private static void compareDocletOutputs(List<String> differences, File refFolder,
File outFolder) throws IOException {
if(refFolder.getName().equals("CVS"))
return;
if (!refFolder.exists() || !refFolder.isDirectory())
throw new IllegalArgumentException("Reference does not exists or is not a folder: "
+ refFolder.getAbsolutePath());
if (!outFolder.exists() || !outFolder.isDirectory())
throw new IllegalArgumentException("Output does not exists or is not a folder: "
+ outFolder.getAbsolutePath());
// get elements and sort
String[] refFiles = refFolder.list();
String[] outFiles = refFolder.list();
Arrays.sort(refFiles);
Arrays.sort(outFiles);
// parallel scan (mergesort inspired)
int i = 0, j = 0;
while (i < refFiles.length && j < outFiles.length) {
File ref = new File(refFolder, refFiles[i]);
File out = new File(outFolder, outFiles[j]);
int compare = refFiles[i].compareTo(outFiles[i]);
if (compare == 0) {
String refName = ref.getName().toLowerCase();
if (ref.isDirectory()) {
compareDocletOutputs(differences, ref, out);
} else if (refName.endsWith(".dot")) {
if (!TestUtils.dotFilesEqual(pw, ref.getAbsolutePath(), out.getAbsolutePath()))
differences.add(out.getName() + " is different from the reference");
} else {
if (!TestUtils.textFilesEquals(pw, ref, out))
differences.add(out.getName() + " is different from the reference");
}
i++;
j++;
} else if (compare < 0) {
differences.add("Reference file/folder not found in output: "
+ ref.getAbsolutePath());
i++;
} else {
j++;
}
}
// all ref files remaining are missing ones
while (i < refFiles.length) {
File ref = new File(refFolder, refFiles[i]);
differences.add("Reference file/folder not found in output: " + ref.getAbsolutePath());
i++;
}
}
/**
* Runs the UmlGraphDoc doclet
* @param options
*/
private static void runDoclet(String[] options) {
pw.print("Run javadoc -doclet " + doclet);
for (String o : options)
pw.print(o + " ");
pw.println();
com.sun.tools.javadoc.Main.execute("UMLDoc test", pw, pw, pw,
doclet, options);
}
}