1 package net.sf.jstp;
2
3 import java.util.List;
4 import java.util.ArrayList;
5 import java.io.*;
6
7
8 public class JstpParser
9 {
10 public JstpParser(Reader input)
11 {
12 initReader(input, 4);
13 }
14
15 public List elements = new ArrayList();
16
17 public void parse() throws IOException, JstpParserException
18 {
19 try
20 {
21 parseJstp();
22 }
23 catch(IOExceptionWrapper e)
24 {
25 throw (IOException)e.getCause();
26 }
27 catch (JstpParserExceptionWrapper e)
28 {
29 throw (JstpParserException)e.getCause();
30 }
31 }
32
33 void parseJstp()
34 {
35 while(!eof())
36 {
37 parseElement();
38 elements.add(element);
39 }
40 }
41
42 JstpElement element = null;
43 void parseElement()
44 {
45 if(lookahead("<%--"))
46 parseComment();
47 else if(lookahead("<%@"))
48 parseDirective();
49 else if(lookahead("<%!"))
50 parseDeclaration();
51 else if(lookahead("<%="))
52 parseExpression();
53 else if(lookahead("<%"))
54 parseScriptlet();
55 else
56 parseText();
57 }
58
59 void newElement(int type)
60 {
61 JstpElement newElement = new JstpElement(type, row, col);
62 if(element!=null)
63 {
64 element.next = newElement;
65 newElement.prev = element;
66 }
67 element = newElement;
68 }
69
70 void parseComment()
71 {
72 newElement(JstpElement.TYPE_COMMENT);
73
74 consume("<%--");
75 while(!lookahead("--%>"))
76 {
77 element.content.append(next());
78 }
79 consume("--%>");
80 }
81
82 void parseDirective()
83 {
84 newElement(JstpElement.TYPE_DIRECTIVE);
85
86 consume("<%@");
87
88 blank();
89 element.directiveName = name();
90
91 while(true)
92 {
93 int B=blank();
94 String attrName = name();
95 if(attrName.length()==0)
96 break;
97 else
98 if(B==0)
99 parserError("whitespace expected before attribute "+attrName);
100
101 blank();
102 if(next() != '=' )
103 parserError("missing '='");
104 blank();
105
106 String attrValue = string();
107
108 element.directiveAttributes.put(attrName, attrValue);
109 }
110
111 if(lookahead("%>"))
112 consume("%>");
113 else
114 parserError("parser error in directive '"+element.directiveName+"'");
115 }
116
117 void parseDeclaration()
118 {
119 newElement(JstpElement.TYPE_DECLARATION);
120 consume("<%!");
121 parseRestOfDeclExprScr();
122 }
123 void parseExpression()
124 {
125 newElement(JstpElement.TYPE_EXPRESSION);
126 consume("<%=");
127 parseRestOfDeclExprScr();
128 }
129 void parseScriptlet()
130 {
131 newElement(JstpElement.TYPE_SCRIPTLET);
132 consume("<%");
133 parseRestOfDeclExprScr();
134 }
135
136 void parseRestOfDeclExprScr()
137 {
138 while(!lookahead("%>"))
139 {
140 if(lookahead("%//>"))
141 {
142 consume("%//>");
143 element.content.append("%>");
144 }
145 else
146 element.content.append(next());
147 }
148 consume("%>");
149 }
150
151
152 void parseText()
153 {
154 newElement(JstpElement.TYPE_TEXT);
155 while(!eof() && !lookahead("<%"))
156 {
157
158 if(lookahead("\r\n"))
159 {
160 element.content.append(next()).append(next());
161 break;
162 }
163 else if(lookahead("\r") || lookahead("\n"))
164 {
165 element.content.append(next());
166 break;
167 }
168 else if(lookahead("<//%"))
169 {
170 consume("<//%");
171 element.content.append("<%");
172 }
173 else
174 element.content.append(next());
175 }
176 }
177
178 int blank()
179 {
180 for(int i=0; ;i++)
181 {
182 if(Character.isSpace(peek()))
183 next();
184 else
185 return i;
186 }
187 }
188
189 String name()
190 {
191 StringBuffer buf = new StringBuffer();
192 while(Character.isLetter(peek()) || '-'==peek())
193 {
194 buf.append(next());
195 }
196 return buf.toString();
197 }
198
199 String string()
200 {
201 String delim = null;
202 if(lookahead("\""))
203 delim = "\"";
204 else if(lookahead("\'"))
205 delim = "\'";
206 else
207 parserError("\" or \' expected at start of attribute value");
208
209 StringBuffer buf = new StringBuffer();
210
211 consume(delim);
212 while(!lookahead(delim))
213 {
214 buf.append(next());
215 }
216 consume(delim);
217
218 return buf.toString();
219 }
220
221
222
223
224
225
226
227
228 static class IOExceptionWrapper extends RuntimeException
229 {
230 public IOExceptionWrapper(IOException e){ super(e); }
231 }
232 static class JstpParserExceptionWrapper extends RuntimeException
233 {
234 public JstpParserExceptionWrapper(JstpParserException e){ super(e); }
235 }
236 void parserError(String message) throws JstpParserExceptionWrapper
237 {
238 throw new JstpParserExceptionWrapper(new JstpParserException(message, row, col));
239 }
240
241
242
243 PushbackReader pbr;
244 char[] buf;
245 void initReader(Reader reader, int lookahead)
246 {
247 this.pbr = new PushbackReader(reader, lookahead);
248 this.buf = new char[lookahead];
249 }
250
251 boolean eof()
252 {
253 try
254 {
255 int c = pbr.read();
256 if(c==-1)
257 return true;
258
259 pbr.unread(c);
260 return false;
261 }
262 catch (IOException e)
263 {
264 throw new IOExceptionWrapper(e);
265 }
266 }
267
268 boolean _r = false;
269 int row = 1;
270 int col = 1;
271 void count(int x)
272 {
273 for(int i=0; i<x; i++)
274 {
275 char c = buf[i];
276 boolean r = (c=='\r');
277 if( c=='\n' || (_r && r) )
278 {
279 row = row+1;
280 col = 1;
281 }
282 else if( _r && !r )
283 {
284 row = row+1;
285 col = 2;
286 }
287 else
288 {
289 col = col + 1;
290 }
291 _r = r;
292 }
293 }
294
295 char next()
296 {
297 try
298 {
299 int n = pbr.read(buf, 0, 1);
300 if(n==-1)
301 unexpectedEOF();
302
303 count(1);
304 return buf[0];
305 }
306 catch (IOException e)
307 {
308 throw new IOExceptionWrapper(e);
309 }
310 }
311
312 char peek()
313 {
314 try
315 {
316 int n = pbr.read(buf, 0, 1);
317 if(n==-1)
318 unexpectedEOF();
319 pbr.unread(buf, 0, 1);
320
321 return buf[0];
322 }
323 catch (IOException e)
324 {
325 throw new IOExceptionWrapper(e);
326 }
327 }
328
329 void unexpectedEOF() throws JstpParserExceptionWrapper
330 {
331 if(element!=null && element.type!=JstpElement.TYPE_TEXT)
332 parserError("unclosed "+element.getTypeStartToken()+" at "+element.row+":"+element.col);
333 else
334 parserError("unexpected EOF");
335 }
336
337 boolean lookahead(String s)
338 {
339 try
340 {
341 int n = pbr.read(buf, 0, s.length());
342 if(n==-1)
343 return false;
344 pbr.unread(buf, 0, n);
345
346 if(n!=s.length())
347 return false;
348
349 for(int i=0; i<n; i++)
350 if( buf[i] != s.charAt(i) )
351 return false;
352
353 return true;
354 }
355 catch (IOException e)
356 {
357 throw new IOExceptionWrapper(e);
358 }
359 }
360
361 void consume(String s)
362 {
363 try
364 {
365 int n = pbr.read(buf, 0, s.length());
366
367 if(n!=s.length())
368 throw new Error("assertion fails");
369
370 count(n);
371
372 for(int i=0; i<n; i++)
373 if( buf[i] != s.charAt(i) )
374 throw new Error("assertion fails");
375 }
376 catch (IOException e)
377 {
378 throw new IOExceptionWrapper(e);
379 }
380 }
381
382 }