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:
Andrea Aime 2006-01-28 10:13:17 +00:00
parent a96f721897
commit 5423bea7eb
12 changed files with 792 additions and 90 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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