mirror of https://github.com/dspinellis/UMLGraph
Added new @match types, with an general interface, an implementation for each kind, and a factory method in the View class to build the right implementation. Changed View implementation to work agains ClassMatchers instead of Patterns
This commit is contained in:
parent
a96f721897
commit
5423bea7eb
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.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.
|
||||
* @author wolf
|
||||
*/
|
||||
public class ContextMatcher implements ClassMatcher {
|
||||
ClassGraphHack cg;
|
||||
Pattern pattern;
|
||||
ArrayList<ClassDoc> matched;
|
||||
Options opt;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws IOException
|
||||
*/
|
||||
public ContextMatcher(RootDoc root, Pattern pattern, Options options) throws IOException {
|
||||
this.pattern = pattern;
|
||||
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);
|
||||
|
||||
// build up the classgraph printing the relations for all of the
|
||||
// classes that make up the "center" of this context
|
||||
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) {
|
||||
cg.printClass(cd);
|
||||
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 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);
|
||||
if (ciMatched != null && ciMatched.isRelated(name))
|
||||
return true;
|
||||
ClassInfo ci = cg.getClassInfo(name);
|
||||
if (ci != null && ci.isRelated(mcName))
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,15 +18,14 @@
|
|||
package gr.spinellis.umlgraph.doclet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
|
@ -37,63 +36,83 @@ import com.sun.javadoc.Tag;
|
|||
* @author wolf
|
||||
*
|
||||
* @depend - - - Options
|
||||
* @depend - - - ClassMatcher
|
||||
* @depend - - - InterfaceMatcher
|
||||
* @depend - - - PatternMatcher
|
||||
* @depend - - - SubclassMatcher
|
||||
*
|
||||
*/
|
||||
class View implements OptionProvider {
|
||||
Map<Pattern, String[][]> optionOverrides = new LinkedHashMap<Pattern, String[][]>();
|
||||
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
|
||||
* @param c
|
||||
* @throws PatternSyntaxException
|
||||
*/
|
||||
public View(ClassDoc c, OptionProvider provider) throws PatternSyntaxException {
|
||||
public View(RootDoc root, ClassDoc c, OptionProvider provider) {
|
||||
this.viewDoc = c;
|
||||
this.provider = provider;
|
||||
this.root = root;
|
||||
Tag[] tags = c.tags();
|
||||
String currPattern = null;
|
||||
ClassMatcher currMatcher = null;
|
||||
// parse options, get the global ones, and build a map of the
|
||||
// pattern matched overrides
|
||||
List<String[]> patternOptions = new ArrayList<String[]>();
|
||||
globalOptions = new ArrayList<String[]>();
|
||||
for (int i = 0; i < tags.length; i++) {
|
||||
if (tags[i].name().equals("@match")) {
|
||||
// store the current pattern and its options
|
||||
if (currPattern != null) {
|
||||
String[][] options = patternOptions
|
||||
.toArray(new String[patternOptions.size()][]);
|
||||
optionOverrides.put(Pattern.compile(currPattern), options);
|
||||
currMatcher = buildMatcher(tags[i].text());
|
||||
if(currMatcher != null) {
|
||||
optionOverrides.put(currMatcher, new ArrayList<String[]>());
|
||||
}
|
||||
// start gathering data for the new patters
|
||||
String[] strings = StringUtil.tokenize(tags[i].text());
|
||||
if(strings.length < 2) {
|
||||
System.err.println("Skipping uncomplete @match tag, type missing. ");
|
||||
currPattern = null;
|
||||
} else if(!strings[0].equals("class")) {
|
||||
System.err.println("Skipping @match tag, unknown match type (only 'class' is supported for the moment). ");
|
||||
currPattern = null;
|
||||
} else {
|
||||
currPattern = strings[1];
|
||||
}
|
||||
patternOptions.clear();
|
||||
} else if (tags[i].name().equals("@opt")) {
|
||||
String[] opts = StringUtil.tokenize(tags[i].text());
|
||||
opts[0] = "-" + opts[0];
|
||||
if (currPattern == null) {
|
||||
if (currMatcher == null) {
|
||||
globalOptions.add(opts);
|
||||
} else {
|
||||
patternOptions.add(opts);
|
||||
optionOverrides.get(currMatcher).add(opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currPattern != null) {
|
||||
String[][] options = patternOptions.toArray(new String[patternOptions.size()][]);
|
||||
optionOverrides.put(Pattern.compile(currPattern), options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
} 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
|
||||
|
|
@ -129,24 +148,21 @@ class View implements OptionProvider {
|
|||
|
||||
public void overrideForClass(Options opt, ClassDoc cd) {
|
||||
provider.overrideForClass(opt, cd);
|
||||
overrideForClass_(opt, cd.toString());
|
||||
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);
|
||||
overrideForClass_(opt, className);
|
||||
}
|
||||
|
||||
private void overrideForClass_(Options opt, String className) {
|
||||
for (Iterator<Map.Entry<Pattern, String[][]>> iter = optionOverrides.entrySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
Map.Entry<Pattern, String[][]> mapEntry = iter.next();
|
||||
Pattern regex = mapEntry.getKey();
|
||||
Matcher matcher = regex.matcher(className);
|
||||
if (matcher.matches()) {
|
||||
String[][] overrides = mapEntry.getValue();
|
||||
for (int i = 0; i < overrides.length; i++) {
|
||||
opt.setOption(overrides[i]);
|
||||
for (ClassMatcher cm : optionOverrides.keySet()) {
|
||||
if(cm.matches(className)) {
|
||||
for (String[] override : optionOverrides.get(cm)) {
|
||||
opt.setOption(override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.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.
|
||||
* @author wolf
|
||||
*/
|
||||
public class ContextMatcher implements ClassMatcher {
|
||||
ClassGraphHack cg;
|
||||
Pattern pattern;
|
||||
ArrayList<ClassDoc> matched;
|
||||
Options opt;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws IOException
|
||||
*/
|
||||
public ContextMatcher(RootDoc root, Pattern pattern, Options options) throws IOException {
|
||||
this.pattern = pattern;
|
||||
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);
|
||||
|
||||
// build up the classgraph printing the relations for all of the
|
||||
// classes that make up the "center" of this context
|
||||
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) {
|
||||
cg.printClass(cd);
|
||||
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 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);
|
||||
if (ciMatched != null && ciMatched.isRelated(name))
|
||||
return true;
|
||||
ClassInfo ci = cg.getClassInfo(name);
|
||||
if (ci != null && ci.isRelated(mcName))
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,15 +18,14 @@
|
|||
package gr.spinellis.umlgraph.doclet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
|
@ -37,63 +36,83 @@ import com.sun.javadoc.Tag;
|
|||
* @author wolf
|
||||
*
|
||||
* @depend - - - Options
|
||||
* @depend - - - ClassMatcher
|
||||
* @depend - - - InterfaceMatcher
|
||||
* @depend - - - PatternMatcher
|
||||
* @depend - - - SubclassMatcher
|
||||
*
|
||||
*/
|
||||
class View implements OptionProvider {
|
||||
Map<Pattern, String[][]> optionOverrides = new LinkedHashMap<Pattern, String[][]>();
|
||||
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
|
||||
* @param c
|
||||
* @throws PatternSyntaxException
|
||||
*/
|
||||
public View(ClassDoc c, OptionProvider provider) throws PatternSyntaxException {
|
||||
public View(RootDoc root, ClassDoc c, OptionProvider provider) {
|
||||
this.viewDoc = c;
|
||||
this.provider = provider;
|
||||
this.root = root;
|
||||
Tag[] tags = c.tags();
|
||||
String currPattern = null;
|
||||
ClassMatcher currMatcher = null;
|
||||
// parse options, get the global ones, and build a map of the
|
||||
// pattern matched overrides
|
||||
List<String[]> patternOptions = new ArrayList<String[]>();
|
||||
globalOptions = new ArrayList<String[]>();
|
||||
for (int i = 0; i < tags.length; i++) {
|
||||
if (tags[i].name().equals("@match")) {
|
||||
// store the current pattern and its options
|
||||
if (currPattern != null) {
|
||||
String[][] options = patternOptions
|
||||
.toArray(new String[patternOptions.size()][]);
|
||||
optionOverrides.put(Pattern.compile(currPattern), options);
|
||||
currMatcher = buildMatcher(tags[i].text());
|
||||
if(currMatcher != null) {
|
||||
optionOverrides.put(currMatcher, new ArrayList<String[]>());
|
||||
}
|
||||
// start gathering data for the new patters
|
||||
String[] strings = StringUtil.tokenize(tags[i].text());
|
||||
if(strings.length < 2) {
|
||||
System.err.println("Skipping uncomplete @match tag, type missing. ");
|
||||
currPattern = null;
|
||||
} else if(!strings[0].equals("class")) {
|
||||
System.err.println("Skipping @match tag, unknown match type (only 'class' is supported for the moment). ");
|
||||
currPattern = null;
|
||||
} else {
|
||||
currPattern = strings[1];
|
||||
}
|
||||
patternOptions.clear();
|
||||
} else if (tags[i].name().equals("@opt")) {
|
||||
String[] opts = StringUtil.tokenize(tags[i].text());
|
||||
opts[0] = "-" + opts[0];
|
||||
if (currPattern == null) {
|
||||
if (currMatcher == null) {
|
||||
globalOptions.add(opts);
|
||||
} else {
|
||||
patternOptions.add(opts);
|
||||
optionOverrides.get(currMatcher).add(opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currPattern != null) {
|
||||
String[][] options = patternOptions.toArray(new String[patternOptions.size()][]);
|
||||
optionOverrides.put(Pattern.compile(currPattern), options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
} 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
|
||||
|
|
@ -129,24 +148,21 @@ class View implements OptionProvider {
|
|||
|
||||
public void overrideForClass(Options opt, ClassDoc cd) {
|
||||
provider.overrideForClass(opt, cd);
|
||||
overrideForClass_(opt, cd.toString());
|
||||
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);
|
||||
overrideForClass_(opt, className);
|
||||
}
|
||||
|
||||
private void overrideForClass_(Options opt, String className) {
|
||||
for (Iterator<Map.Entry<Pattern, String[][]>> iter = optionOverrides.entrySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
Map.Entry<Pattern, String[][]> mapEntry = iter.next();
|
||||
Pattern regex = mapEntry.getKey();
|
||||
Matcher matcher = regex.matcher(className);
|
||||
if (matcher.matches()) {
|
||||
String[][] overrides = mapEntry.getValue();
|
||||
for (int i = 0; i < overrides.length; i++) {
|
||||
opt.setOption(overrides[i]);
|
||||
for (ClassMatcher cm : optionOverrides.keySet()) {
|
||||
if(cm.matches(className)) {
|
||||
for (String[] override : optionOverrides.get(cm)) {
|
||||
opt.setOption(override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue