001: /*
002: * Contibuted by Andrea Aime
003: * (C) Copyright 2005 Diomidis Spinellis
004: *
005: * Permission to use, copy, and distribute this software and its
006: * documentation for any purpose and without fee is hereby granted,
007: * provided that the above copyright notice appear in all copies and that
008: * both that copyright notice and this permission notice appear in
009: * supporting documentation.
010: *
011: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
012: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
013: * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
014: *
015: * $Id: ContextMatcher.java,v 1.7 2007/11/27 09:04:22 dds Exp $
016: *
017: */
018:
019: package org.umlgraph.doclet;
020:
021: import java.io.IOException;
022: import java.io.PrintWriter;
023: import java.io.Writer;
024: import java.util.ArrayList;
025: import java.util.HashSet;
026: import java.util.List;
027: import java.util.Set;
028: import java.util.regex.Pattern;
029:
030: import com.sun.javadoc.ClassDoc;
031: import com.sun.javadoc.RootDoc;
032:
033: /**
034: * Matches classes that are directly connected to one of the classes matched by
035: * the regual expression specified. The context center is computed by regex
036: * lookup. Depending on the specified Options, inferred relations and
037: * dependencies will be used as well.
038: * <p>
039: * This class needs to perform quite a bit of computations in order to gather
040: * the network of class releationships, so you are allowed to reuse it should
041: * you
042: * @author wolf
043: *
044: * @depend - - - DevNullWriter
045: */
046: public class ContextMatcher implements ClassMatcher {
047: ClassGraphHack cg;
048: Pattern pattern;
049: List<ClassDoc> matched;
050: Set<String> visited = new HashSet<String>();
051: Options opt;
052: RootDoc root;
053: boolean keepParentHide;
054:
055: /**
056: * Builds the context matcher
057: * @param root The root doc returned by JavaDoc
058: * @param pattern The pattern that will match the "center" of this
059: * context
060: * @param opt The options will be used to decide on inference
061: * @param keepParentHide If true, parent option hide patterns will be
062: * preserved, so that classes hidden by the options won't
063: * be shown in the context
064: * @param fullContext If true, all the classes related to the context
065: * center will be included, otherwise it will match only
066: * the classes referred with an outgoing relation from
067: * the context center
068: * @throws IOException
069: */
070: public ContextMatcher(RootDoc root, Pattern pattern,
071: Options options, boolean keepParentHide) throws IOException {
072: this .pattern = pattern;
073: this .root = root;
074: this .keepParentHide = keepParentHide;
075: opt = (Options) options.clone();
076: opt.setOption(new String[] { "-!hide" });
077: opt.setOption(new String[] { "-!attributes" });
078: opt.setOption(new String[] { "-!operations" });
079: this .cg = new ClassGraphHack(root, opt);
080:
081: setContextCenter(pattern);
082: }
083:
084: /**
085: * Can be used to setup a different pattern for this context matcher.
086: * <p>
087: * This can be used to speed up subsequent matching with the same global
088: * options, since the class network informations will be reused.
089: * @param pattern
090: */
091: public void setContextCenter(Pattern pattern) {
092: // build up the classgraph printing the relations for all of the
093: // classes that make up the "center" of this context
094: this .pattern = pattern;
095: matched = new ArrayList<ClassDoc>();
096: for (ClassDoc cd : root.classes()) {
097: if (pattern.matcher(cd.toString()).matches()) {
098: matched.add(cd);
099: addToGraph(cd);
100: }
101: }
102: }
103:
104: /**
105: * Adds the specified class to the internal class graph along with its
106: * relations and depencies, eventually inferring them, according to the
107: * Options specified for this matcher
108: * @param cd
109: */
110: private void addToGraph(ClassDoc cd) {
111: // avoid adding twice the same class, but don't rely on cg.getClassInfo
112: // since there
113: // are other ways to add a classInfor than printing the class
114: if (visited.contains(cd.toString()))
115: return;
116:
117: visited.add(cd.toString());
118: cg.printClass(cd, false);
119: cg.printRelations(cd);
120: if (opt.inferRelationships) {
121: cg.printInferredRelations(cd);
122: }
123: if (opt.inferDependencies) {
124: cg.printInferredDependencies(cd);
125: }
126: }
127:
128: /**
129: * @see org.umlgraph.doclet.ClassMatcher#matches(com.sun.javadoc.ClassDoc)
130: */
131: public boolean matches(ClassDoc cd) {
132: if (keepParentHide && opt.matchesHideExpression(cd.toString()))
133: return false;
134:
135: // if the class is matched, it's in by default.
136: if (matched.contains(cd))
137: return true;
138:
139: // otherwise, add the class to the graph and see if it's associated
140: // with any of the matched classes using the classgraph hack
141: addToGraph(cd);
142: return matches(cd.toString());
143: }
144:
145: /**
146: * @see org.umlgraph.doclet.ClassMatcher#matches(java.lang.String)
147: */
148: public boolean matches(String name) {
149: if (pattern.matcher(name).matches())
150: return true;
151:
152: for (ClassDoc mcd : matched) {
153: String mcName = mcd.toString();
154: ClassInfo ciMatched = cg.getClassInfo(mcName);
155: RelationPattern rp = ciMatched.getRelation(name);
156: if (ciMatched != null && rp != null
157: && opt.contextRelationPattern.matchesOne(rp))
158: return true;
159: }
160: return false;
161: }
162:
163: /**
164: * A quick hack to compute class dependencies reusing ClassGraph but
165: * without generating output. Will be removed once the ClassGraph class
166: * will be split into two classes for graph computation and output
167: * generation.
168: * @author wolf
169: *
170: */
171: private static class ClassGraphHack extends ClassGraph {
172:
173: public ClassGraphHack(RootDoc root,
174: OptionProvider optionProvider) throws IOException {
175: super (root, optionProvider, null);
176: prologue();
177: }
178:
179: public void prologue() throws IOException {
180: w = new PrintWriter(new DevNullWriter());
181: }
182:
183: }
184:
185: /**
186: * Simple dev/null imitation
187: * @author wolf
188: */
189: private static class DevNullWriter extends Writer {
190:
191: public void write(char[] cbuf, int off, int len)
192: throws IOException {
193: // nothing to do
194: }
195:
196: public void flush() throws IOException {
197: // nothing to do
198: }
199:
200: public void close() throws IOException {
201: // nothing to do
202: }
203:
204: }
205:
206: }
|