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: View.java,v 1.12 2007/11/27 09:04:22 dds Exp $
016: *
017: */
018: package org.umlgraph.doclet;
019:
020: import java.util.ArrayList;
021: import java.util.LinkedHashMap;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.regex.Pattern;
025: import java.util.regex.PatternSyntaxException;
026:
027: import com.sun.javadoc.ClassDoc;
028: import com.sun.javadoc.RootDoc;
029: import com.sun.javadoc.Tag;
030:
031: /**
032: * Contains the definition of a View. A View is a set of option overrides that
033: * will lead to the creation of a UML class diagram. Multiple views can be
034: * defined on the same source tree, effectively allowing to create multiple
035: * class diagram out of it.
036: * @author wolf
037: *
038: * @depend - - - Options
039: * @depend - - - ClassMatcher
040: * @depend - - - InterfaceMatcher
041: * @depend - - - PatternMatcher
042: * @depend - - - SubclassMatcher
043: * @depend - - - ContextMatcher
044: *
045: */
046: public class View implements OptionProvider {
047: Map<ClassMatcher, List<String[]>> optionOverrides = new LinkedHashMap<ClassMatcher, List<String[]>>();
048: ClassDoc viewDoc;
049: OptionProvider provider;
050: List<String[]> globalOptions;
051: RootDoc root;
052:
053: /**
054: * Builds a view given the class that contains its definition
055: */
056: public View(RootDoc root, ClassDoc c, OptionProvider provider) {
057: this .viewDoc = c;
058: this .provider = provider;
059: this .root = root;
060: Tag[] tags = c.tags();
061: ClassMatcher currMatcher = null;
062: // parse options, get the global ones, and build a map of the
063: // pattern matched overrides
064: globalOptions = new ArrayList<String[]>();
065: for (int i = 0; i < tags.length; i++) {
066: if (tags[i].name().equals("@match")) {
067: currMatcher = buildMatcher(tags[i].text());
068: if (currMatcher != null) {
069: optionOverrides.put(currMatcher,
070: new ArrayList<String[]>());
071: }
072: } else if (tags[i].name().equals("@opt")) {
073: String[] opts = StringUtil.tokenize(tags[i].text());
074: opts[0] = "-" + opts[0];
075: if (currMatcher == null) {
076: globalOptions.add(opts);
077: } else {
078: optionOverrides.get(currMatcher).add(opts);
079: }
080: }
081: }
082: }
083:
084: /**
085: * Factory method that builds the appropriate matcher for @match tags
086: */
087: private ClassMatcher buildMatcher(String tagText) {
088: // check there are at least @match <type> and a parameter
089: String[] strings = StringUtil.tokenize(tagText);
090: if (strings.length < 2) {
091: System.err
092: .println("Skipping uncomplete @match tag, type missing: "
093: + tagText + " in view " + viewDoc);
094: return null;
095: }
096:
097: try {
098: if (strings[0].equals("class")) {
099: return new PatternMatcher(Pattern.compile(strings[1]));
100: } else if (strings[0].equals("context")) {
101: return new ContextMatcher(root, Pattern
102: .compile(strings[1]), getGlobalOptions(), false);
103: } else if (strings[0].equals("outgoingContext")) {
104: return new ContextMatcher(root, Pattern
105: .compile(strings[1]), getGlobalOptions(), false);
106: } else if (strings[0].equals("interface")) {
107: return new InterfaceMatcher(root, Pattern
108: .compile(strings[1]));
109: } else if (strings[0].equals("subclass")) {
110: return new SubclassMatcher(root, Pattern
111: .compile(strings[1]));
112: } else {
113: System.err
114: .println("Skipping @match tag, unknown match type, in view "
115: + viewDoc);
116: }
117: } catch (PatternSyntaxException pse) {
118: System.err
119: .println("Skipping @match tag due to invalid regular expression '"
120: + tagText + "'" + " in view " + viewDoc);
121: } catch (Exception e) {
122: System.err
123: .println("Skipping @match tag due to an internal error '"
124: + tagText + "'" + " in view " + viewDoc);
125: e.printStackTrace();
126: }
127: return null;
128: }
129:
130: // ----------------------------------------------------------------
131: // OptionProvider methods
132: // ----------------------------------------------------------------
133:
134: public Options getOptionsFor(ClassDoc cd) {
135: Options localOpt = getGlobalOptions();
136: overrideForClass(localOpt, cd);
137: localOpt.setOptions(cd);
138: return localOpt;
139: }
140:
141: public Options getOptionsFor(String name) {
142: Options localOpt = getGlobalOptions();
143: overrideForClass(localOpt, name);
144: return localOpt;
145: }
146:
147: public Options getGlobalOptions() {
148: Options go = provider.getGlobalOptions();
149:
150: boolean outputSet = false;
151: for (String[] opts : globalOptions) {
152: if (opts[0].equals("-output"))
153: outputSet = true;
154: go.setOption(opts);
155: }
156: if (!outputSet)
157: go.setOption(new String[] { "-output",
158: viewDoc.name() + ".dot" });
159:
160: return go;
161: }
162:
163: public void overrideForClass(Options opt, ClassDoc cd) {
164: provider.overrideForClass(opt, cd);
165: for (ClassMatcher cm : optionOverrides.keySet()) {
166: if (cm.matches(cd)) {
167: for (String[] override : optionOverrides.get(cm)) {
168: opt.setOption(override);
169: }
170: }
171: }
172: }
173:
174: public void overrideForClass(Options opt, String className) {
175: provider.overrideForClass(opt, className);
176: for (ClassMatcher cm : optionOverrides.keySet()) {
177: if (cm.matches(className)) {
178: for (String[] override : optionOverrides.get(cm)) {
179: opt.setOption(override);
180: }
181: }
182: }
183: }
184:
185: public String getDisplayName() {
186: return "view " + viewDoc.name();
187: }
188:
189: }
|