View Javadoc

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     //todo can the add(String) be moved up to StructuredContext???
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 }