Introduced explicit enums for relation types and relation directions, added an option to specify the direction and type of relations included in the context (basically, a tool to build a map from relation type to direction), fixed a test that still contained the old -inferassoc intestead of -inferrel

This commit is contained in:
Andrea Aime 2006-07-02 16:23:39 +00:00
parent afa290cca5
commit 14b80c0eab
18 changed files with 385 additions and 127 deletions

View File

@ -20,8 +20,9 @@
package gr.spinellis.umlgraph.doclet;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class's dot-comaptible alias name (for fully qualified class names)
@ -42,7 +43,7 @@ class ClassInfo {
* all the classes linked with a bi-directional relation , and the ones
* referred by a directed relation
*/
Set<String> relatedClasses = new HashSet<String>();
Map<String, RelationPattern> relatedClasses = new HashMap<String, RelationPattern>();
ClassInfo(boolean p, boolean h) {
nodePrinted = p;
@ -51,12 +52,17 @@ class ClassInfo {
classNumber++;
}
public void addRelation(String dest) {
relatedClasses.add(dest);
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 boolean isRelated(String dest) {
return relatedClasses.contains(dest);
public RelationPattern getRelation(String dest) {
return relatedClasses.get(dest);
}
/** Start numbering from zero. */

View File

@ -32,13 +32,13 @@ 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.
* 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
* you
* @author wolf
*
* @depend - - - DevNullWriter
@ -61,6 +61,10 @@ public class ContextMatcher implements ClassMatcher {
* @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 {
@ -68,14 +72,14 @@ public class ContextMatcher implements ClassMatcher {
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"});
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>
@ -103,11 +107,12 @@ public class ContextMatcher implements ClassMatcher {
* @param cd
*/
private void addToGraph(ClassDoc cd) {
// avoid adding twice the same class, but don't rely on cg.getClassInfo since there
// 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()))
if (visited.contains(cd.toString()))
return;
visited.add(cd.toString());
cg.printClass(cd, false);
cg.printRelations(cd);
@ -146,10 +151,8 @@ public class ContextMatcher implements ClassMatcher {
for (ClassDoc mcd : matched) {
String mcName = mcd.toString();
ClassInfo ciMatched = cg.getClassInfo(mcName);
if (ciMatched != null && ciMatched.isRelated(name))
return true;
ClassInfo ci = cg.getClassInfo(name);
if (ci != null && ci.isRelated(mcName))
RelationPattern rp = ciMatched.getRelation(name);
if (ciMatched != null && rp != null && opt.contextRelationPattern.matchesOne(rp))
return true;
}
return false;

View File

@ -109,11 +109,12 @@ public class Options implements Cloneable, OptionProvider {
String guilClose = "&raquo;"; // "\u00bb";
boolean inferRelationships;
boolean inferDependencies;
RelationPattern contextRelationPattern;
boolean useImports;
Visibility inferDendencyVisibility;
boolean inferDepInPackage;
boolean verbose2;
String inferRelationshipType;
RelationType inferRelationshipType;
private Vector<Pattern> collPackages;
boolean compact;
// internal option, used by UMLDoc to generate relative links between classes
@ -161,12 +162,13 @@ public class Options implements Cloneable, OptionProvider {
useGuillemot = true;
findViews = false;
viewName = null;
contextRelationPattern = new RelationPattern(RelationDirection.BOTH);
inferRelationships = false;
inferDependencies = false;
inferDendencyVisibility = Visibility.PRIVATE;
inferDepInPackage = false;
useImports = false;
inferRelationshipType = "navassoc";
inferRelationshipType = RelationType.NAVASSOC;
collPackages = new Vector<Pattern>();
compact = false;
relativeLinksForSourcePackages = false;
@ -220,6 +222,7 @@ public class Options implements Cloneable, OptionProvider {
option.equals("-useimports") ||
option.equals("-inferdep") ||
option.equals("-inferdepinpackage") ||
option.equals("-fullContext") ||
option.equals("-verbose2") ||
option.equals("-compact"))
@ -251,9 +254,10 @@ public class Options implements Cloneable, OptionProvider {
option.equals("-inferreltype") ||
option.equals("-inferdepvis") ||
option.equals("-collpackages") ||
option.equals("-link")
)
option.equals("-link"))
return 2;
else if(option.equals("-contextPattern"))
return 3;
else
return 0;
}
@ -423,22 +427,23 @@ public class Options implements Cloneable, OptionProvider {
} else if(opt[0].equals("-!inferrel")) {
inferRelationships = false;
} else if(opt[0].equals("-inferreltype")) {
if(ClassGraph.associationMap.containsKey(opt[1])) {
inferRelationshipType = opt[1];
} else {
System.err.println("Unknown association type " + opt[1]);
}
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 = "navassoc";
inferRelationshipType = RelationType.NAVASSOC;
} else if(opt[0].equals("-inferdepvis")) {
Visibility vis = Visibility.parseVisibility(opt[1]);
if(vis == null)
System.err.println("Ignoring invalid visibility specification for dependency inference" + vis);
inferDendencyVisibility = Visibility.PRIVATE;
try {
Visibility vis = Visibility.valueOf(opt[1].toUpperCase());
inferDendencyVisibility = vis;
} catch(IllegalArgumentException e) {
System.err.println("Ignoring invalid visibility specification for " +
"dependency inference: " + opt[1]);
}
} else if(opt[0].equals("-!inferdepvis")) {
inferDendencyVisibility = Visibility.PRIVATE;
} else if(opt[0].equals("-!inferreltype")) {
inferRelationshipType = "navassoc";
} else if(opt[0].equals("-inferdep")) {
inferDependencies = true;
} else if(opt[0].equals("-!inferdep")) {
@ -469,6 +474,20 @@ public class Options implements Cloneable, OptionProvider {
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.

View File

@ -0,0 +1,51 @@
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

@ -0,0 +1,50 @@
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

@ -0,0 +1,10 @@
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

@ -97,6 +97,9 @@ public class View implements OptionProvider {
} 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")) {

View File

@ -28,13 +28,7 @@ import com.sun.javadoc.ProgramElementDoc;
*
*/
public enum Visibility {
PRIVATE("private"), PACKAGE("package"), PROTECTED("protected"), PUBLIC("public");
private String name;
private Visibility(String name) {
this.name = name;
}
PRIVATE, PACKAGE, PROTECTED, PUBLIC;
public static Visibility get(ProgramElementDoc doc) {
if (doc.isPrivate())
@ -47,12 +41,4 @@ public enum Visibility {
return PUBLIC;
}
public static Visibility parseVisibility(String vis) {
for (Visibility v : Visibility.values()) {
if (v.name.equalsIgnoreCase(vis))
return v;
}
return null;
}
}

View File

@ -20,8 +20,9 @@
package gr.spinellis.umlgraph.doclet;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class's dot-comaptible alias name (for fully qualified class names)
@ -42,7 +43,7 @@ class ClassInfo {
* all the classes linked with a bi-directional relation , and the ones
* referred by a directed relation
*/
Set<String> relatedClasses = new HashSet<String>();
Map<String, RelationPattern> relatedClasses = new HashMap<String, RelationPattern>();
ClassInfo(boolean p, boolean h) {
nodePrinted = p;
@ -51,12 +52,17 @@ class ClassInfo {
classNumber++;
}
public void addRelation(String dest) {
relatedClasses.add(dest);
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 boolean isRelated(String dest) {
return relatedClasses.contains(dest);
public RelationPattern getRelation(String dest) {
return relatedClasses.get(dest);
}
/** Start numbering from zero. */

View File

@ -32,13 +32,13 @@ 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.
* 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
* you
* @author wolf
*
* @depend - - - DevNullWriter
@ -61,6 +61,10 @@ public class ContextMatcher implements ClassMatcher {
* @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 {
@ -68,14 +72,14 @@ public class ContextMatcher implements ClassMatcher {
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"});
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>
@ -103,11 +107,12 @@ public class ContextMatcher implements ClassMatcher {
* @param cd
*/
private void addToGraph(ClassDoc cd) {
// avoid adding twice the same class, but don't rely on cg.getClassInfo since there
// 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()))
if (visited.contains(cd.toString()))
return;
visited.add(cd.toString());
cg.printClass(cd, false);
cg.printRelations(cd);
@ -146,10 +151,8 @@ public class ContextMatcher implements ClassMatcher {
for (ClassDoc mcd : matched) {
String mcName = mcd.toString();
ClassInfo ciMatched = cg.getClassInfo(mcName);
if (ciMatched != null && ciMatched.isRelated(name))
return true;
ClassInfo ci = cg.getClassInfo(name);
if (ci != null && ci.isRelated(mcName))
RelationPattern rp = ciMatched.getRelation(name);
if (ciMatched != null && rp != null && opt.contextRelationPattern.matchesOne(rp))
return true;
}
return false;

View File

@ -109,11 +109,12 @@ public class Options implements Cloneable, OptionProvider {
String guilClose = "&raquo;"; // "\u00bb";
boolean inferRelationships;
boolean inferDependencies;
RelationPattern contextRelationPattern;
boolean useImports;
Visibility inferDendencyVisibility;
boolean inferDepInPackage;
boolean verbose2;
String inferRelationshipType;
RelationType inferRelationshipType;
private Vector<Pattern> collPackages;
boolean compact;
// internal option, used by UMLDoc to generate relative links between classes
@ -161,12 +162,13 @@ public class Options implements Cloneable, OptionProvider {
useGuillemot = true;
findViews = false;
viewName = null;
contextRelationPattern = new RelationPattern(RelationDirection.BOTH);
inferRelationships = false;
inferDependencies = false;
inferDendencyVisibility = Visibility.PRIVATE;
inferDepInPackage = false;
useImports = false;
inferRelationshipType = "navassoc";
inferRelationshipType = RelationType.NAVASSOC;
collPackages = new Vector<Pattern>();
compact = false;
relativeLinksForSourcePackages = false;
@ -220,6 +222,7 @@ public class Options implements Cloneable, OptionProvider {
option.equals("-useimports") ||
option.equals("-inferdep") ||
option.equals("-inferdepinpackage") ||
option.equals("-fullContext") ||
option.equals("-verbose2") ||
option.equals("-compact"))
@ -251,9 +254,10 @@ public class Options implements Cloneable, OptionProvider {
option.equals("-inferreltype") ||
option.equals("-inferdepvis") ||
option.equals("-collpackages") ||
option.equals("-link")
)
option.equals("-link"))
return 2;
else if(option.equals("-contextPattern"))
return 3;
else
return 0;
}
@ -423,22 +427,23 @@ public class Options implements Cloneable, OptionProvider {
} else if(opt[0].equals("-!inferrel")) {
inferRelationships = false;
} else if(opt[0].equals("-inferreltype")) {
if(ClassGraph.associationMap.containsKey(opt[1])) {
inferRelationshipType = opt[1];
} else {
System.err.println("Unknown association type " + opt[1]);
}
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 = "navassoc";
inferRelationshipType = RelationType.NAVASSOC;
} else if(opt[0].equals("-inferdepvis")) {
Visibility vis = Visibility.parseVisibility(opt[1]);
if(vis == null)
System.err.println("Ignoring invalid visibility specification for dependency inference" + vis);
inferDendencyVisibility = Visibility.PRIVATE;
try {
Visibility vis = Visibility.valueOf(opt[1].toUpperCase());
inferDendencyVisibility = vis;
} catch(IllegalArgumentException e) {
System.err.println("Ignoring invalid visibility specification for " +
"dependency inference: " + opt[1]);
}
} else if(opt[0].equals("-!inferdepvis")) {
inferDendencyVisibility = Visibility.PRIVATE;
} else if(opt[0].equals("-!inferreltype")) {
inferRelationshipType = "navassoc";
} else if(opt[0].equals("-inferdep")) {
inferDependencies = true;
} else if(opt[0].equals("-!inferdep")) {
@ -469,6 +474,20 @@ public class Options implements Cloneable, OptionProvider {
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.

View File

@ -0,0 +1,51 @@
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

@ -0,0 +1,50 @@
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

@ -0,0 +1,10 @@
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

@ -97,6 +97,9 @@ public class View implements OptionProvider {
} 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")) {

View File

@ -28,13 +28,7 @@ import com.sun.javadoc.ProgramElementDoc;
*
*/
public enum Visibility {
PRIVATE("private"), PACKAGE("package"), PROTECTED("protected"), PUBLIC("public");
private String name;
private Visibility(String name) {
this.name = name;
}
PRIVATE, PACKAGE, PROTECTED, PUBLIC;
public static Visibility get(ProgramElementDoc doc) {
if (doc.isPrivate())
@ -47,12 +41,4 @@ public enum Visibility {
return PUBLIC;
}
public static Visibility parseVisibility(String vis) {
for (Visibility v : Visibility.values()) {
if (v.name.equalsIgnoreCase(vis))
return v;
}
return null;
}
}

View File

@ -1,7 +1,7 @@
#!/usr/local/bin/dot
#
# Class diagram
# Generated by UmlGraph version 4.1 (http://www.spinellis.gr/sw/umlgraph)
# Generated by UmlGraph version 4.4 (http://www.spinellis.gr/sw/umlgraph)
#
digraph G {
@ -23,27 +23,29 @@ digraph G {
c72:p -> c69:p [dir=back,arrowtail=empty];
//MyList extends MyFunnyList<A, B>
c69:p -> c70:p [dir=back,arrowtail=empty];
// A depend C
c66:p -> c68:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// A depend B
c66:p -> c67:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// B depend C
c67:p -> c68:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// B depend A
// A HAS B
c66:p -> c67:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=none, arrowtail=ediamond];
// A HAS B
c66:p -> c67:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=none, arrowtail=ediamond];
// A HAS C
c66:p -> c68:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=none, arrowtail=ediamond];
// C NAVASSOC A
c68:p -> c66:p [taillabel="", label="", headlabel="*", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open];
// C NAVASSOC B
c68:p -> c67:p [taillabel="", label="", headlabel="*", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open];
// C NAVASSOC D
c68:p -> c71:p [taillabel="", label="", headlabel="*", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open];
// C NAVASSOC C
c68:p -> c68:p [taillabel="", label="", headlabel="*", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open];
// C NAVASSOC java.util.List<E>
c68:p -> c73:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open];
// D NAVASSOC MyList
c71:p -> c70:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open];
// B DEPEND A
c67:p -> c66:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// C depend java.util.Map<K, V>
c68:p -> c73:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// C depend java.util.List<E>
c68:p -> c74:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// C depend java.util.ArrayList<E>
c68:p -> c72:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// D depend MyList
c71:p -> c70:p [taillabel="", label="", headlabel="", fontname="Helvetica", fontcolor="black", fontsize=10.0, color="black", arrowhead=open, style=dashed];
// java.util.Map<K, V>
c73 [label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="2" port="p" href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html"><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td> &laquo;interface&raquo; </td></tr><tr><td> Map&lt;K, V&gt; </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> size() : int </td></tr><tr><td align="left"> isEmpty() : boolean </td></tr><tr><td align="left"> containsKey(arg0 : Object) : boolean </td></tr><tr><td align="left"> containsValue(arg0 : Object) : boolean </td></tr><tr><td align="left"> get(arg0 : Object) : V </td></tr><tr><td align="left"> put(arg0 : K, arg1 : V) : V </td></tr><tr><td align="left"> remove(arg0 : Object) : V </td></tr><tr><td align="left"> putAll(arg0 : Map&lt;?, ?&gt;) </td></tr><tr><td align="left"> clear() </td></tr><tr><td align="left"> keySet() : Set&lt;K&gt; </td></tr><tr><td align="left"> values() : Collection&lt;V&gt; </td></tr><tr><td align="left"> entrySet() : Set&lt;Map.Entry&lt;K, V&gt;&gt; </td></tr><tr><td align="left"> equals(arg0 : Object) : boolean </td></tr><tr><td align="left"> hashCode() : int </td></tr></table></td></tr></table>>, fontname="Helvetica", fontcolor="black", fontsize=10.0];
// java.util.ArrayList<E>
c72 [label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="2" port="p" href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html"><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td> ArrayList&lt;E&gt; </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> serialVersionUID : long </td></tr><tr><td align="left"> elementData : E[] </td></tr><tr><td align="left"> size : int </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> trimToSize() </td></tr><tr><td align="left"> ensureCapacity(arg0 : int) </td></tr><tr><td align="left"> size() : int </td></tr><tr><td align="left"> isEmpty() : boolean </td></tr><tr><td align="left"> contains(arg0 : Object) : boolean </td></tr><tr><td align="left"> indexOf(arg0 : Object) : int </td></tr><tr><td align="left"> lastIndexOf(arg0 : Object) : int </td></tr><tr><td align="left"> clone() : Object </td></tr><tr><td align="left"> toArray() : Object[] </td></tr><tr><td align="left"> toArray(arg0 : T[]) : T[] </td></tr><tr><td align="left"> get(arg0 : int) : E </td></tr><tr><td align="left"> set(arg0 : int, arg1 : E) : E </td></tr><tr><td align="left"> add(arg0 : E) : boolean </td></tr><tr><td align="left"> add(arg0 : int, arg1 : E) </td></tr><tr><td align="left"> remove(arg0 : int) : E </td></tr><tr><td align="left"> remove(arg0 : Object) : boolean </td></tr><tr><td align="left"> fastRemove(arg0 : int) </td></tr><tr><td align="left"> clear() </td></tr><tr><td align="left"> addAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> addAll(arg0 : int, arg1 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> removeRange(arg0 : int, arg1 : int) </td></tr><tr><td align="left"> RangeCheck(arg0 : int) </td></tr><tr><td align="left"> writeObject(arg0 : ObjectOutputStream) </td></tr><tr><td align="left"> readObject(arg0 : ObjectInputStream) </td></tr></table></td></tr></table>>, fontname="Helvetica", fontcolor="black", fontsize=10.0];
// java.util.List<E>
c74 [label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="2" port="p" href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/List.html"><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td> &laquo;interface&raquo; </td></tr><tr><td> List&lt;E&gt; </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> size() : int </td></tr><tr><td align="left"> isEmpty() : boolean </td></tr><tr><td align="left"> contains(arg0 : Object) : boolean </td></tr><tr><td align="left"> iterator() : Iterator&lt;E&gt; </td></tr><tr><td align="left"> toArray() : Object[] </td></tr><tr><td align="left"> toArray(arg0 : T[]) : T[] </td></tr><tr><td align="left"> add(arg0 : E) : boolean </td></tr><tr><td align="left"> remove(arg0 : Object) : boolean </td></tr><tr><td align="left"> containsAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> addAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> addAll(arg0 : int, arg1 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> removeAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> retainAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> clear() </td></tr><tr><td align="left"> equals(arg0 : Object) : boolean </td></tr><tr><td align="left"> hashCode() : int </td></tr><tr><td align="left"> get(arg0 : int) : E </td></tr><tr><td align="left"> set(arg0 : int, arg1 : E) : E </td></tr><tr><td align="left"> add(arg0 : int, arg1 : E) </td></tr><tr><td align="left"> remove(arg0 : int) : E </td></tr><tr><td align="left"> indexOf(arg0 : Object) : int </td></tr><tr><td align="left"> lastIndexOf(arg0 : Object) : int </td></tr><tr><td align="left"> listIterator() : ListIterator&lt;E&gt; </td></tr><tr><td align="left"> listIterator(arg0 : int) : ListIterator&lt;E&gt; </td></tr><tr><td align="left"> subList(arg0 : int, arg1 : int) : List&lt;E&gt; </td></tr></table></td></tr></table>>, fontname="Helvetica", fontcolor="black", fontsize=10.0];
c73 [label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="2" port="p" href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/List.html"><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td> &laquo;interface&raquo; </td></tr><tr><td> List&lt;E&gt; </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> </td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="1"><tr><td align="left"> size() : int </td></tr><tr><td align="left"> isEmpty() : boolean </td></tr><tr><td align="left"> contains(arg0 : Object) : boolean </td></tr><tr><td align="left"> iterator() : Iterator&lt;E&gt; </td></tr><tr><td align="left"> toArray() : Object[] </td></tr><tr><td align="left"> toArray(arg0 : T[]) : T[] </td></tr><tr><td align="left"> add(arg0 : E) : boolean </td></tr><tr><td align="left"> remove(arg0 : Object) : boolean </td></tr><tr><td align="left"> containsAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> addAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> addAll(arg0 : int, arg1 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> removeAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> retainAll(arg0 : Collection&lt;?&gt;) : boolean </td></tr><tr><td align="left"> clear() </td></tr><tr><td align="left"> equals(arg0 : Object) : boolean </td></tr><tr><td align="left"> hashCode() : int </td></tr><tr><td align="left"> get(arg0 : int) : E </td></tr><tr><td align="left"> set(arg0 : int, arg1 : E) : E </td></tr><tr><td align="left"> add(arg0 : int, arg1 : E) </td></tr><tr><td align="left"> remove(arg0 : int) : E </td></tr><tr><td align="left"> indexOf(arg0 : Object) : int </td></tr><tr><td align="left"> lastIndexOf(arg0 : Object) : int </td></tr><tr><td align="left"> listIterator() : ListIterator&lt;E&gt; </td></tr><tr><td align="left"> listIterator(arg0 : int) : ListIterator&lt;E&gt; </td></tr><tr><td align="left"> subList(arg0 : int, arg1 : int) : List&lt;E&gt; </td></tr></table></td></tr></table>>, fontname="Helvetica", fontcolor="black", fontsize=10.0];
}

View File

@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.Map;
/**
* @opt inferassoc
* @opt inferrel
* @opt inferdep
* @opt inferdepinpackage
* @opt attributes
@ -17,7 +17,7 @@ import java.util.Map;
class UMLOptions {}
/**
* @opt inferassoctype has
* @opt inferreltype has
*/
class A {
B first;