1 package net.sf.quarrel.file;
2
3 import org.apache.commons.lang.StringUtils;
4 import org.apache.commons.logging.Log;
5 import org.apache.commons.logging.LogFactory;
6
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.LineNumberReader;
11 import java.util.Iterator;
12
13 /***
14 * This class is responsible for parsing an InputStream and reporting all
15 * discovered elements to the ElementCollector. The element collector will
16 * build a model from the values discovered. This will sperate the knowledge
17 * of the file structure from the object model used to represent it.
18 * <p/>
19 * There are some potential problems with this class right now.
20 * <ul>
21 * <li>it has more knowldge about UML than it should. Ideally, it would only
22 * know about idents and such. But right now, I'm not sure how to make it work
23 * without checking for key words</li>
24 * <li>the code is checking for an indent of zero to link the case
25 * of two class header on adjacent line. if I can fix the problem mentioned
26 * above, then this problem will probably be fixed as well.</li>
27 * </ul>
28 * </p>
29 */
30 public class StreamParser {
31
32 private final static Log LOG = LogFactory.getLog(StreamParser.class);
33
34 private final ElementCollector _elementCollector;
35 private InputStream _inputStream;
36
37 /***
38 * Create a new StreamParse for the specified InputStream. Call parse() to
39 * to perform the parsing and then call the elements() method to get the
40 * results.
41 *
42 * @param source the InputStream to parse
43 * @see #parse()
44 * @see #elements()
45 */
46 public StreamParser(final InputStream source) {
47 _elementCollector = new ElementCollector();
48 _inputStream = source;
49 }
50
51 /***
52 * Parses the InputStream provided and reports all discovered data to the
53 * collector.
54 *
55 * @param inputStream the InputStream to parse
56 * @throws IOException if there are any parsing errors
57 * @throws ParsingException if the file structure is not valid
58 * @deprecated use #parse() instead. this method will be made private.
59 */
60 private void parse(final InputStream inputStream)
61 throws IOException, ParsingException {
62
63 final InputStreamReader isr = new InputStreamReader(inputStream);
64 final LineNumberReader br = new LineNumberReader(isr);
65
66 int indent = 0;
67
68 String line;
69
70 boolean firstLine = true;
71
72 while ((line = br.readLine()) != null) {
73
74 if (StringUtils.isEmpty(line.trim())) {
75 continue;
76 }
77
78 final int lineNumber = br.getLineNumber();
79
80 LOG.debug("[" + lineNumber + "][" + determinIndent(line) + "]" + line);
81
82 if (firstLine) {
83 _elementCollector.push(line);
84 firstLine = false;
85 indent = determinIndent(line);
86 } else {
87
88 final int newIndent = determinIndent(line);
89
90 LOG.debug("newIndent = " + newIndent);
91 String value = line.trim();
92
93 if (newIndent > indent) {
94
95 final FeatureType type = FeatureType.getEnum(value);
96 if (type != null) {
97 _elementCollector.push(value);
98 } else {
99 _elementCollector.add(value);
100 }
101
102 } else if (newIndent == indent) {
103
104 if (indent == 0) {
105 _elementCollector.pop();
106 _elementCollector.push(value);
107 } else {
108 _elementCollector.add(value);
109 }
110
111 } else {
112
113 _elementCollector.pop();
114
115 final FeatureType type = FeatureType.getEnum(value);
116
117 if (type != null) {
118 _elementCollector.push(value);
119 } else {
120 _elementCollector.pop();
121 _elementCollector.push(value);
122 }
123
124 }
125
126 indent = newIndent;
127
128 }
129
130
131
132
133
134
135 }
136
137 _elementCollector.finish();
138
139 }
140
141 private int determinIndent(final String line) {
142
143 int i = 0;
144
145 while ((line.charAt(i)) == ' ') {
146 i++;
147 }
148
149 return i;
150 }
151
152 public void parse() throws IOException, ParsingException {
153 parse(_inputStream);
154 }
155
156 public Iterator elements() {
157 return _elementCollector.elements();
158 }
159 }