View Javadoc

1   package net.sf.jstp;
2   
3   import java.util.List;
4   import java.util.ArrayList;
5   import java.io.*;
6   
7   // not the fastest parser on earth
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)//attributes. not exactly as spec, but fine for our purpose
92          {
93              int B=blank();
94              String attrName = name();
95              if(attrName.length()==0) //no more attributes
96                  break;
97              else //a new attribute. blank is required in front of it
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             //each line is a seperate text element
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     // == exception wrapper =================================
226     // use unchecked exception wrappers internally
227     // unwrap to check exception at published interface
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     // == reader =================================
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 }