1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49:
50:
59:
60:
61: public class CSSParser
62: {
63:
64:
67: private CSSScanner scanner;
68:
69:
72: private CSSParserCallback callback;
73:
74:
77: private int lookahead;
78:
79:
82: private String error;
83:
84:
89: public CSSParser(Reader in, CSSParserCallback cb)
90: {
91: scanner = new CSSScanner(in);
92: callback = cb;
93: lookahead = -1;
94: }
95:
96:
101: public void parse()
102: throws IOException
103: {
104: boolean success = parseStylesheet();
105: if (! success)
106: {
107: throw new CSSParserException(error);
108: }
109: }
110:
111:
119: private boolean parseStylesheet()
120: throws IOException
121: {
122: int token = peekToken();
123: while (token != CSSScanner.EOF && (token == CSSScanner.CDC
124: || token == CSSScanner.CDO || token == CSSScanner.S
125: || parseStatement()))
126: {
127: if (token == CSSScanner.CDC || token == CSSScanner.CDO
128: || token == CSSScanner.S)
129: readToken();
130: token = peekToken();
131: }
132:
133: return token == CSSScanner.EOF;
134: }
135:
136:
143: private boolean parseStatement()
144: throws IOException
145: {
146: return parseRuleset() || parseAtRule();
147: }
148:
149:
157: private boolean parseRuleset()
158: throws IOException
159: {
160: StringBuilder selector = new StringBuilder();
161: parseSelector(selector);
162: StringTokenizer selSplitter =
163: new StringTokenizer(selector.toString(), ",");
164: Selector[] sels = new Selector[selSplitter.countTokens()];
165: for (int i = 0; selSplitter.hasMoreTokens(); i++)
166: {
167: String sel = selSplitter.nextToken().trim();
168: sels[i] = new Selector(sel);
169: }
170: callback.startStatement(sels);
171:
172: int token;
173: do
174: {
175: token = readToken();
176: } while (token == CSSScanner.S);
177: boolean ret = true;
178:
179: if (token == CSSScanner.CURLY_LEFT)
180: {
181:
182: do
183: {
184: token = readToken();
185: } while (token == CSSScanner.S);
186: lookahead = token;
187:
188:
189: boolean decl = parseDeclaration();
190: token = peekToken();
191: while (token == CSSScanner.SEMICOLON)
192: {
193: readToken();
194:
195: do
196: {
197: token = readToken();
198: } while (token == CSSScanner.S);
199: lookahead = token;
200:
201:
202: parseDeclaration();
203: token = peekToken();
204: }
205: if (token != CSSScanner.CURLY_RIGHT)
206: {
207: error = "Expected right curly brace";
208: ret = false;
209: }
210: else
211: {
212: readToken();
213:
214: do
215: {
216: token = readToken();
217: } while (token == CSSScanner.S);
218: lookahead = token;
219: callback.endStatement();
220: }
221: }
222: else
223: {
224: ret = false;
225: error = "Expected left curly brace";
226: }
227: return ret;
228: }
229:
230:
238: private boolean parseDeclaration()
239: throws IOException
240: {
241:
242: int token = readToken();
243: if (token == CSSScanner.DELIM)
244: token = readToken();
245:
246: boolean ret = true;
247:
248:
249: String property = null;
250: if (token == CSSScanner.IDENT)
251: {
252: property = new String(scanner.parseBuffer, 0, scanner.tokenEnd);
253:
254: do
255: {
256: token = readToken();
257: } while (token == CSSScanner.S);
258:
259:
260: if (token == CSSScanner.DELIM && scanner.parseBuffer[0] == ':')
261: {
262:
263: do
264: {
265: token = readToken();
266: } while (token == CSSScanner.S);
267: lookahead = token;
268:
269: StringBuilder value = new StringBuilder();
270: if (parseValue(value))
271: {
272: callback.declaration(property, value.toString().trim());
273: }
274: else
275: {
276: ret = false;
277: error = "Error while reading the property value";
278: }
279: }
280: else
281: {
282: ret = false;
283: error = "Expected colon to separate property and value";
284: }
285:
286: }
287: else
288: {
289: lookahead = token;
290: ret = false;
291: error = "Expected IDENT token for property";
292: }
293: return ret;
294: }
295:
296:
306: private boolean parseValue(StringBuilder s)
307: throws IOException
308: {
309:
310: boolean success = parseAny(s);
311: while (parseAny(s))
312: ;
313:
314: return success;
315: }
316:
317:
327: private boolean parseSelector(StringBuilder sel)
328: throws IOException
329: {
330:
331: boolean ret = parseAny(sel);
332: if (ret)
333: {
334: while (parseAny(sel))
335: ;
336: }
337: return ret;
338: }
339:
340:
351: private boolean parseAny(StringBuilder s)
352: throws IOException
353: {
354: int token = peekToken();
355: boolean ret = false;
356: if (token == CSSScanner.IDENT || token == CSSScanner.NUMBER
357: || token == CSSScanner.PERCENTAGE || token == CSSScanner.DIMENSION
358: || token == CSSScanner.STRING || token == CSSScanner.DELIM
359: || token == CSSScanner.URI || token == CSSScanner.HASH
360: || token == CSSScanner.UNICODE_RANGE || token == CSSScanner.INCLUDES
361: || token == CSSScanner.DASHMATCH)
362: {
363: if (s != null)
364: s.append(scanner.parseBuffer, 0, scanner.tokenEnd);
365: readToken();
366: ret = true;
367: }
368: else if (token == CSSScanner.FUNCTION)
369: System.err.println("Implement parseAny for FUNCTION");
370: else if (token == CSSScanner.PAREN_LEFT)
371: System.err.println("Implement parseAny for (");
372: else if (token == CSSScanner.BRACE_LEFT)
373: System.err.println("Implement parseAny for [");
374:
375:
376: token = peekToken();
377: while (token == CSSScanner.S)
378: {
379: if (s != null)
380: s.append(scanner.parseBuffer, 0, scanner.tokenEnd);
381: readToken();
382: token = peekToken();
383: }
384: return ret;
385: }
386:
387:
395: private boolean parseAtRule()
396: throws IOException
397: {
398:
399: return false;
400: }
401:
402:
407: private int readToken()
408: throws IOException
409: {
410: int token;
411: if (lookahead == -1)
412: {
413: do
414: {
415: token = scanner.nextToken();
416: } while (token == CSSScanner.COMMENT);
417: }
418: else
419: {
420: token = lookahead;
421: lookahead = -1;
422: }
423: return token;
424: }
425:
426:
432: private int peekToken()
433: throws IOException
434: {
435: int token;
436: if (lookahead == -1)
437: {
438: do
439: {
440: token = scanner.nextToken();
441: } while (token == CSSScanner.COMMENT);
442: lookahead = token;
443: }
444: else
445: token = lookahead;
446: return token;
447: }
448:
449:
454: public static void main(String[] args)
455: {
456: try
457: {
458: InputStream in;
459: if (args.length > 0)
460: {
461: File file = new File(args[0]);
462: in = new FileInputStream(file);
463: }
464: else
465: {
466: String name = "/javax/swing/text/html/default.css";
467: in = CSSScanner.class.getResourceAsStream(name);
468: }
469: BufferedInputStream bin = new BufferedInputStream(in);
470: InputStreamReader r = new InputStreamReader(bin);
471: CSSParserCallback cb = new CSSParserCallback()
472: {
473: public void startStatement(Selector[] selector)
474: {
475: System.out.print("startStatement: ");
476: for (int i = 0; i < selector.length; i++)
477: {
478: System.out.print(selector[i]);
479: if (i < selector.length - 1)
480: System.out.print(',');
481: else
482: System.out.println();
483: }
484: }
485: public void endStatement()
486: {
487: System.out.println("endStatement");
488: }
489: public void declaration(String property, String value)
490: {
491: System.out.println("declaration: " + property + ", " + value);
492: }
493: };
494: CSSParser p = new CSSParser(r, cb);
495: p.parse();
496: }
497: catch (IOException ex)
498: {
499: ex.printStackTrace();
500: }
501: }
502:
503: }