1
2
3
4
5
6
7
8
9
10
11
12
13 package gov.nasa.pds.ltdt.parser;
14
15 import gov.nasa.pds.ltdt.label.statement.PrettyAttributeStatement;
16 import gov.nasa.pds.ltdt.label.statement.PrettyCommentStatement;
17 import gov.nasa.pds.ltdt.label.statement.PrettyGroupStatement;
18 import gov.nasa.pds.ltdt.label.statement.PrettyObjectStatement;
19 import gov.nasa.pds.ltdt.label.statement.Value;
20 import gov.nasa.pds.tools.label.MalformedSFDULabel;
21 import gov.nasa.pds.tools.label.SFDULabel;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.LineNumberReader;
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32
33 /***
34 * Class that parses a PDS label. This class does not do any semantic
35 * validation of values.
36 *
37 */
38 public class ParserLite {
39
40 private static final String COMMENTS_EXPRESSION = "///*.*//*/";
41 private static final String COMMENT_ID = "COMMENT-";
42 private LineNumberReader reader;
43 private boolean endStatementFound;
44 private boolean endOfFileReached;
45
46 public ParserLite() {
47 endStatementFound = false;
48 endOfFileReached = false;
49 }
50
51 /***
52 * Parse the PDS label
53 *
54 * @param label A stream representation of a PDS label.
55 * @return A list that can contain PrettyStatement objects, and
56 * string representations of a blank line, SFDU, or the END statement
57 * in a label.
58 *
59 * @throws IOException
60 */
61 public List parse(InputStream label) throws IOException {
62
63
64 List statements = new ArrayList();
65
66 label.mark(20);
67 List sfdus = consumeSFDU(label);
68 if(!sfdus.isEmpty()) {
69 String sfdu ="";
70 for(Iterator i=sfdus.iterator(); i.hasNext();) {
71
72 sfdu += i.next().toString();
73 }
74 statements.add(sfdu);
75
76 label.skip(2);
77 }
78 else
79 label.reset();
80
81 reader = new LineNumberReader(new InputStreamReader(label));
82
83 while( !(endStatementFound || endOfFileReached) ) {
84 List results = getNextLine();
85 for(Iterator i = results.iterator(); i.hasNext();) {
86 Object result = i.next();
87 if(result instanceof PrettyAttributeStatement) {
88 PrettyAttributeStatement stmt = (PrettyAttributeStatement) result;
89 String identifier = stmt.getIdentifier();
90 if(identifier.equals("OBJECT")) {
91 String value = stmt.getValue().toString();
92 PrettyObjectStatement object =
93 new PrettyObjectStatement(value + "_Line_" + reader.getLineNumber());
94 object = getNestedStatements(object);
95 statements.add(object);
96 }
97 else if(identifier.equals("GROUP")) {
98 String value = stmt.getValue().toString();
99 PrettyGroupStatement group =
100 new PrettyGroupStatement(value + "_Line_" + reader.getLineNumber());
101 group = getNestedStatements(group);
102 statements.add(group);
103 }
104 else
105 statements.add(stmt);
106 }
107 else
108 statements.add(result);
109 }
110 }
111
112 try {
113 sfdus = consumeFinalSFDU();
114 if(!sfdus.isEmpty()) {
115 String sfdu ="";
116 for(Iterator i=sfdus.iterator(); i.hasNext();) {
117 sfdu += i.next().toString();
118 }
119 statements.add(sfdu);
120 }
121 } catch(IOException i) {
122
123 } finally {
124 reader.close();
125 }
126 return statements;
127 }
128
129 /***
130 * Get the next line in a PDS label. Any comments found
131 * within a line gets parsed and stored first before attempting
132 * to find the next valid ODL statement (KEYWORD = VALUE).
133 *
134 * @return A list of statements.
135 *
136 * @throws IOException
137 */
138 public List getNextLine() throws IOException {
139 List result = new ArrayList();
140 String savedText = "";
141 String text = null;
142 boolean isStatement = false;
143
144 while(!isStatement) {
145 try {
146 if((text = reader.readLine()) == null) {
147 endOfFileReached = true;
148 throw new EndOfFileException("End of file reached.");
149 }
150 text = savedText + " " + text;
151 text = text.replaceAll("//s+", " ").trim();
152 if(savedText.equals("") && text.matches("")) {
153 throw new BlankLineFoundException("Blank Line found");
154 }
155
156
157 Pattern commentExpression = Pattern.compile(COMMENTS_EXPRESSION);
158 Matcher commentMatcher = commentExpression.matcher(text);
159 while(commentMatcher.find()) {
160 int start = commentMatcher.start();
161 int end = commentMatcher.end();
162 Pattern quotesExpression = Pattern.compile("\"");
163 Matcher quoteMatcher = quotesExpression.matcher(text.substring(0, start));
164 int quoteCount = 0;
165 while(quoteMatcher.find()) {
166 ++quoteCount;
167 }
168
169
170 if(quoteCount % 2 == 0) {
171
172 String id = COMMENT_ID + reader.getLineNumber();
173 String comment = text.substring(start+2, end-2).trim();
174 PrettyCommentStatement cs = new PrettyCommentStatement(-1, id, comment);
175 result.add(cs);
176 text = new StringBuffer(text).delete(start, end).toString();
177 }
178 }
179 if(text.trim().equals("END")) {
180 isStatement = true;
181 endStatementFound = true;
182 }
183 else {
184
185 String []statement = text.split("=", 2);
186 if(statement.length <= 1 || (statement[1].matches("//s+") || statement[1].matches("")))
187 throw new StatementNotFoundException("Statement not found");
188
189 String value = statement[1].trim();
190
191
192 if(value.matches("[\"{(].*")) {
193 checkValue(value);
194 }
195 isStatement = true;
196 }
197 } catch(EndOfFileException e) {
198 if(!("".equals(savedText)))
199 result.add(savedText);
200 break;
201 } catch(BadValueException be) {
202 savedText = text;
203 } catch (StatementNotFoundException se) {
204 savedText = text;
205 } catch (BlankLineFoundException e) {
206 result.add(" ");
207 }
208 }
209
210 if(isStatement)
211 result.add(parseStatement(text));
212
213 return result;
214 }
215
216
217 /***
218 * Parse a statement
219 *
220 * @param string A string representation of a valid ODL statement.
221 * @return an AttributeStatement object or null if no statement
222 * was found.
223 */
224 public PrettyAttributeStatement parseStatement(String string) {
225 PrettyAttributeStatement stmt = null;
226 String []statement = string.split("=", 2);
227 String identifier = statement[0].trim();
228 try {
229 Value value = new Value(statement[1].trim());
230 stmt = new PrettyAttributeStatement(identifier, value);
231 } catch(IndexOutOfBoundsException i) {
232 stmt = new PrettyAttributeStatement(identifier);
233 }
234 return stmt;
235 }
236
237 /***
238 * Gets the statements found within an OBJECT.
239 *
240 * @param object The parent OBJECT.
241 * @return The OBJECT, complete with its attributes.
242 *
243 * @throws IOException
244 */
245 private PrettyObjectStatement getNestedStatements(PrettyObjectStatement object)
246 throws IOException {
247 boolean endOfObject = false;
248
249 while(!endOfObject && !endOfFileReached) {
250 List results = getNextLine();
251 for(Iterator i=results.iterator(); i.hasNext();) {
252 Object result = i.next();
253 try {
254 PrettyAttributeStatement ps = (PrettyAttributeStatement) result;
255 if(ps.getIdentifier().equals("END_OBJECT"))
256 endOfObject = true;
257 else if(ps.getIdentifier().equals("OBJECT")) {
258 String identifier = ps.getValue().toString();
259 PrettyObjectStatement nestedObject =
260 new PrettyObjectStatement(identifier + "_Line_" + reader.getLineNumber());
261 nestedObject = getNestedStatements(nestedObject);
262 object.addStatement(nestedObject);
263 }
264 else if(ps.getIdentifier().equals("GROUP")) {
265 String identifier = ps.getValue().toString();
266 PrettyGroupStatement nestedGroup =
267 new PrettyGroupStatement(identifier + "_Line_" + reader.getLineNumber());
268 nestedGroup = getNestedStatements(nestedGroup);
269 object.addStatement(nestedGroup);
270 }
271 else
272 object.addStatement(ps);
273 } catch(ClassCastException c1) {
274 try {
275 PrettyCommentStatement cs = (PrettyCommentStatement) result;
276 object.addStatement(cs);
277 }catch(ClassCastException c2) {
278
279
280
281
282 if(!result.toString().matches("//s+"))
283 object.addStatement(new PrettyAttributeStatement(result.toString()));
284 }
285 }
286 }
287 }
288 return object;
289 }
290
291 /***
292 * Gets the statements found within a GROUP object.
293 *
294 * @param group The parent GROUP object.
295 * @return The GROUP object, complete with its attributes.
296 *
297 * @throws IOException
298 */
299 private PrettyGroupStatement getNestedStatements(PrettyGroupStatement group)
300 throws IOException {
301 boolean endOfGroup = false;
302
303 while(!endOfGroup && !endOfFileReached) {
304 List results = getNextLine();
305 for(Iterator i=results.iterator(); i.hasNext();) {
306 Object result = i.next();
307 try {
308 PrettyAttributeStatement ps = (PrettyAttributeStatement) result;
309
310
311 if(ps.getIdentifier().equals("OBJECT")) {
312 String identifier = ps.getValue().toString();
313 PrettyObjectStatement nestedObject =
314 new PrettyObjectStatement(identifier + "_Line_" + reader.getLineNumber());
315 nestedObject = getNestedStatements(nestedObject);
316 group.addStatement(nestedObject);
317 }
318 else if(ps.getIdentifier().equals("GROUP")) {
319 String identifier = ps.getValue().toString();
320 PrettyGroupStatement nestedGroup =
321 new PrettyGroupStatement(identifier + "_Line_" + reader.getLineNumber());
322 nestedGroup = getNestedStatements(nestedGroup);
323 group.addStatement(nestedGroup);
324 }
325 else if(ps.getIdentifier().equals("END_GROUP"))
326 endOfGroup = true;
327 else
328 group.addStatement(ps);
329
330 } catch(ClassCastException c1) {
331 try {
332 PrettyCommentStatement cs = (PrettyCommentStatement) result;
333 group.addStatement(cs);
334 } catch(ClassCastException c2) {
335
336
337
338
339 if(!result.toString().matches("//s+"))
340 group.addStatement(new PrettyAttributeStatement(result.toString()));
341 }
342 }
343 }
344 }
345 return group;
346 }
347
348 private List consumeSFDU(InputStream input) throws IOException {
349 List sfdus = new ArrayList();
350
351 byte[] sfduLabel = new byte[20];
352 int count = input.read(sfduLabel);
353 if (count == 20) {
354 try {
355 SFDULabel sfdu = new SFDULabel(sfduLabel);
356 if ("CCSD".equals(sfdu.getControlAuthorityId())) {
357 sfdus.add(sfdu);
358
359 input.read(sfduLabel);
360 sfdus.add(new SFDULabel(sfduLabel));
361 }
362 } catch (MalformedSFDULabel e) {
363
364 }
365 }
366 return sfdus;
367 }
368
369 /***
370 * Consumes the final SFDU statement, if it is found after the END
371 * statement.
372 *
373 * @return the SFDUs, if any.
374 * @throws IOException
375 */
376 private List consumeFinalSFDU() throws IOException {
377 List sfdus = new ArrayList();
378
379 char[] sfduLabel = new char[20];
380 int count = reader.read(sfduLabel);
381 if(count == 20) {
382 try {
383 SFDULabel sfdu = new SFDULabel(new String(sfduLabel).getBytes());
384 if("CCSD".equals(sfdu.getControlAuthorityId())) {
385 sfdus.add(sfdu);
386 reader.read(sfduLabel);
387 sfdus.add(new SFDULabel(new String(sfduLabel).getBytes()));
388 }
389 } catch (MalformedSFDULabel e) {
390
391 }
392 }
393 return sfdus;
394 }
395
396 /***
397 * Determines whether or not a value is "good". A value is good
398 * when the number of curly braces, parenthesis, and quotes
399 * are equal. Braces and parenthesis located inside of quotes
400 * are ignored.
401 *
402 * @param value
403 * @return true if the value is good.
404 * @throws BadValueException If
405 */
406 private boolean checkValue(String value) throws BadValueException {
407 boolean outsideQuotes = true;
408 int braces = 0;
409 int parenthesis = 0;
410
411 for(int i = 0; i < value.length(); i++) {
412 if(outsideQuotes) {
413 if(value.charAt(i) == '{')
414 ++braces;
415 else if(value.charAt(i) == '}')
416 --braces;
417 else if(value.charAt(i) == '(')
418 ++parenthesis;
419 else if(value.charAt(i) == ')')
420 --parenthesis;
421 }
422 if(value.charAt(i) == '"')
423 outsideQuotes = !outsideQuotes;
424 }
425 if((!outsideQuotes) || (parenthesis != 0) || (braces !=0) )
426 throw new BadValueException("Bad Value");
427
428 return true;
429 }
430 }