1 package net.sf.quarrel.file;
2
3 import org.apache.commons.collections.CollectionUtils;
4 import org.apache.commons.collections.IteratorUtils;
5 import org.apache.commons.logging.Log;
6 import org.apache.commons.logging.LogFactory;
7
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.HashMap;
11 import java.util.Iterator;
12 import java.util.Map;
13 import java.util.Stack;
14
15 /***
16 * Responsible for building an intermediate model from the contents of an
17 * InputStream. The StreamParser class will read the stream and feed the
18 * contents to ElementCollector.
19 */
20 public final class ElementCollector {
21
22 /***
23 * The log object for this class.
24 */
25 private final Log LOG = LogFactory.getLog(ElementCollector.class);
26
27 /***
28 * Stack used to keep track of the where we are in the model structure
29 */
30 private final Stack _stack = new Stack();
31
32 /***
33 * Current Context object.
34 */
35 private Context _context = null;
36
37 /***
38 * The set of FileElements discovered
39 */
40 private final Collection _elements = new ArrayList();
41
42 /***
43 * Returns an iterator over the set of elements that have been collector.
44 *
45 * @return an Iterator for the collector elements
46 */
47 public Iterator elements() {
48 return IteratorUtils.unmodifiableIterator(_elements.iterator());
49 }
50
51 /***
52 * Pushes a new compartment into the collector using the specified name
53 *
54 * @param name the name of the compartment to be added
55 */
56 public void push(final String name) {
57
58 LOG.debug("push(" + name + ")");
59
60 if (_context == null) {
61 _context = ContextFactory.create(name);
62 } else {
63 final Context subcontext = _context.subcontext(name);
64 _stack.push(_context);
65 _context = subcontext;
66 }
67
68 }
69
70 /***
71 * Brings the state of the collector to a completed state.
72 */
73 public void finish() {
74
75 while (_stack.size() > 0) {
76 pop();
77 }
78
79 if (_context != null) {
80 _elements.add(_context);
81 _context = null;
82 }
83
84 }
85
86 /***
87 * Adds a line to the current compartment
88 *
89 * @param line the String to add to the current compartment
90 */
91 public void add(final String line) {
92 LOG.debug("add(" + line + ")");
93 _context.add(line);
94 }
95
96 /***
97 * Pop the current compartment off the stack.
98 */
99 public void pop() {
100 LOG.debug("pop()");
101 if (_stack.size() > 0) {
102 _context = (Context) _stack.pop();
103 } else {
104 _elements.add(_context);
105 _context = null;
106 }
107
108 }
109
110 /***
111 * Abstraction of the file model. In this way, the state of the model
112 * ensures that data is accumulated in the correct way w/o loads of
113 * if statements and such.
114 */
115 private static interface Context {
116
117 /***
118 * Adds a subelement to the current context
119 * @param s
120 */
121 public void add(String s);
122
123 /***
124 * creates a new subcontext to the current context
125 * @param s
126 * @return
127 */
128 public Context subcontext(final String s);
129 }
130
131 /***
132 * Helper class to create the appropriate context type based on the line
133 * passed in. This is mainly to differentiate between each of the
134 * structured element types.
135 */
136 private static class ContextFactory {
137
138 public static Context create(final String s) {
139
140 final String[] strings = s.split(" ");
141
142 final String type = strings[0];
143
144 Context context;
145
146 if ("class".equalsIgnoreCase(type)) {
147 context = new ClassContext(s);
148 } else if ("interface".equalsIgnoreCase(type)) {
149 context = new InterfaceContext(s);
150 } else {
151 throw new IllegalArgumentException("Unsupported keyword [" + type + "]");
152 }
153
154 return context;
155
156 }
157
158 }
159
160 /***
161 * Handles the common functionality about all structured elements (i.e.
162 * classes and interfaces.
163 */
164 private static abstract class StructuredContext
165 implements Context, FileElement {
166
167
168 private final String _name;
169
170 private final ElementType _type;
171
172 private final Map _features = new HashMap();
173
174 public StructuredContext(final String s, final ElementType type) {
175 final String[] strings = s.split(" ");
176 _name = strings[1];
177 _type = type;
178 }
179
180 public String getName() {
181 return _name;
182 }
183
184 public ElementType getElementType() {
185 return _type;
186 }
187
188 /***
189 * {@inheritDoc}
190 */
191 public Context subcontext(final String s) {
192
193 final FeatureType type = FeatureType.getEnum(s);
194
195 Collection collection = (Collection) _features.get(type);
196
197 if (collection == null) {
198 collection = new ArrayList();
199 _features.put(type, collection);
200 }
201
202 return new CollectionContext(collection, type);
203
204 }
205
206 /***
207 * {@inheritDoc}
208 */
209 public Collection getFeatures(FeatureType type) {
210
211 Collection features = (Collection) _features.get(type);
212
213 if (features == null) {
214 features = CollectionUtils.EMPTY_COLLECTION;
215 }
216
217 return features;
218
219 }
220
221 }
222
223 /***
224 * This class manages the addition of a feature to a collection.
225 */
226 private static class CollectionContext implements Context {
227
228 private final Collection _collection;
229 private final FeatureType _type;
230
231 public CollectionContext(final Collection collection,
232 final FeatureType type) {
233 _collection = collection;
234 _type = type;
235 }
236
237 /***
238 * {@inheritDoc}
239 */
240 public void add(String s) {
241 final Feature feature = new Feature(_type, s);
242 _collection.add(feature);
243 }
244
245 /***
246 * {@inheritDoc}
247 */
248 public Context subcontext(final String s) {
249 throw new IllegalStateException();
250 }
251
252 }
253
254
255
256 private static class InterfaceContext
257 extends StructuredContext {
258
259 public InterfaceContext(String s) {
260 super(s, ElementType.INTERFACE);
261 }
262
263 /***
264 * {@inheritDoc}
265 */
266 public void add(String s) {
267
268 }
269
270 }
271
272 private static class ClassContext
273 extends StructuredContext {
274
275 public ClassContext(String s) {
276 super(s, ElementType.CLASS);
277 }
278
279 /***
280 * {@inheritDoc}
281 */
282 public void add(String s) {
283
284 }
285
286 }
287
288 }