Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/arranger/jarl/script/gsp/GSP.java


1   package com.arranger.jarl.script.gsp;
2   
3   import java.io.Reader;
4   import java.util.HashMap;
5   import java.util.Map;
6   
7   /**
8    * GSP General Script Parser.
9    */
10  public class GSP {
11      
12      protected static final int LL = 15;
13      protected static final int BUF_SIZE = 0x2000;
14      protected static final int BUF_MAX = BUF_SIZE - LL - 1;
15      
16      protected static final int STATE_BODY = 0;
17      protected static final int STATE_CODE = 1;
18      protected static final int STATE_EXPR = 2;
19      protected static final int STATE_DECL = 3;
20      protected static final int STATE_DIRV = 4;
21      protected static final int STATE_COMM = 5;
22      protected static final int STATE_TAG_START = 6;
23      protected static final int STATE_TAG_END = 7;
24      protected static final int STATE_TAG_ATTR = 8;
25  
26      /**
27       * Processes the specified the input stream with the specified language and compiler.
28       * 
29       * @param in Input stream to parse.
30       * @param lang Language with which to parse.
31       * @param compiler Compiler directing the parse.
32       */
33      public void parse(Reader in, IGSPLanguage lang, IGSPCompiler compiler) throws GSPException {
34          int state = STATE_BODY;
35          String prefix = null;
36          char attr = 0;
37  
38          // first char is always a space
39          char[] buf = new char[BUF_SIZE];
40          int cur = 0;
41          buf[cur] = ' ';
42          
43          int ch = 1;
44          int line = 1;
45          int column = 1;
46          
47          try {
48              ch = in.read();
49              
50              while (ch != -1) {
51                  switch (ch) {
52                      
53                      case '%':
54                          // start token
55                          if (state == STATE_BODY && buf[cur] == '<') {
56                              state = STATE_CODE;
57                              lang.print(buf, 1, cur - 1);
58                              cur = 0;
59                              buf[cur] = ' ';
60                              
61                              ch = in.read();
62                              column++;
63                              
64                              switch (ch) {
65                                  case '=':
66                                      state = STATE_EXPR;
67                                      break;
68                                  case '!':
69                                      state = STATE_DECL;
70                                      break;
71                                  case '@':
72                                      state = STATE_DIRV;
73                                      break;
74                                  case '-':
75                                      state = STATE_COMM;
76                                      break;
77                                  case '\r':
78                                      column = 0;
79                                      line++;
80                                      cur = append(buf, cur, lang, state, (char) ch);
81                                      break;
82                                  case '\n':
83                                      if (buf[cur] == '\r') {
84                                          column = 0;
85                                          line++;
86                                      }
87                                      cur = append(buf, cur, lang, state, (char) ch);
88                                      break;
89                                  default:
90                                      cur = append(buf, cur, lang, state, (char) ch);
91                              }
92                          
93                          // escape sequence
94                          } else if (buf[cur] == '\\') {
95                              cur--;
96                              cur = append(buf, cur, lang, state, (char) ch);
97                          
98                          // literal
99                          } else {
100                             cur = append(buf, cur, lang, state, (char) ch);
101                         }
102                         break;
103 
104 
105                     case ':':
106                         // tag start/end
107                         if (state == STATE_BODY) {
108                             boolean isPrefix = false;
109                             int cutoff = cur - LL;
110                             if (cutoff <= 0) {
111                                 cutoff = 1;
112                             }
113 
114                             // look for tag prefix start
115                             for (int i = cur; i >= cutoff; i--) {
116                                 int ch1 = buf[i];
117 
118                                 // colon not in a tag
119                                 if (ch1 == ' ' || ch1 == '>') {
120                                     break;
121 
122                                 } else if (ch1 == '<') {
123                                     // end tag
124                                     if (buf[i + 1] == '/') {
125                                         state = STATE_TAG_END;
126                                         prefix = new String(buf, i + 2, cur - i - 1);
127                                     // start tag
128                                     } else {
129                                         state = STATE_TAG_START;
130                                         prefix = new String(buf, i + 1, cur - i);
131                                     }
132 
133                                     lang.print(buf, 1, i - 1);
134                                     cur = 0;
135                                     buf[cur] = ' ';
136 
137                                     isPrefix = true;
138                                     break;
139                                 }
140                             }
141 
142                             // sometimes a colon is just a colon (iykwim)
143                             if (!isPrefix) {
144                                 cur = append(buf, cur, lang, state, (char) ch);
145                             }
146 
147                         // literal
148                         } else {
149                             cur = append(buf, cur, lang, state, (char) ch);
150                         }
151                         break;
152 
153 
154                     case '>':
155                         
156                         // escape sequence
157                         if (buf[cur] == '\\') {
158                             cur--;
159                             cur = append(buf, cur, lang, state, (char) ch);
160                         
161                         // literal
162                         } else if (state == STATE_BODY || state == STATE_TAG_ATTR) {
163                             cur = append(buf, cur, lang, state, (char) ch);
164 
165                         // end start tag
166                         } else if (state == STATE_TAG_START) {
167                             parseStartTag(buf, 1, cur + 1, lang, prefix);
168                             prefix = null;
169 
170                             state = STATE_BODY;
171                             cur = 0;
172                             buf[cur] = ' ';
173 
174                         // end end tag
175                         } else if (state == STATE_TAG_END) {
176                             lang.tagEnd(prefix, new String(buf, 1, cur));
177                             prefix = null;
178 
179                             state = STATE_BODY;
180                             cur = 0;
181                             buf[cur] = ' ';
182 
183                         // end token
184                         } else if (buf[cur] == '%') {
185                             // literal
186                             if (state == STATE_COMM && !(buf[cur - 1] == '-' && buf[cur - 2] == '-')) {
187                                 cur = append(buf, cur, lang, state, (char) ch);
188                             
189                             } else {
190                                 switch (state) {
191                                     case STATE_CODE:
192                                         lang.code(buf, 1, cur - 1);
193                                         break;
194                                     case STATE_EXPR:
195                                         lang.eval(buf, 1, cur - 1);
196                                         break;
197                                     case STATE_DECL:
198                                         lang.decl(buf, 1, cur - 1);
199                                         break;
200                                     case STATE_DIRV:
201                                         parseDirective(buf, 1, cur, lang, compiler);
202                                         break;
203                                     case STATE_COMM:
204                                         // skip
205                                         break;
206                                 }
207                                 state = STATE_BODY;
208                                 cur = 0;
209                                 buf[cur] = ' ';
210                             }
211                         
212                         // literal
213                         } else {
214                             cur = append(buf, cur, lang, state, (char) ch);
215                         }
216                         break;
217                     
218                     
219                     case '\r':
220                         column = 0;
221                         line++;
222                         cur = append(buf, cur, lang, state, (char) ch);
223                         break;
224                     
225                     
226                     case '\n':
227                         if (buf[cur] != '\r') {
228                             column = 0;
229                             line++;
230                         }
231                         cur = append(buf, cur, lang, state, (char) ch);
232                         break;
233 
234 
235 
236                     case '\"':
237                     case '\'':
238                         switch (state) {
239                             // switch to protected attr mode
240                             case STATE_TAG_START:
241                                 state = STATE_TAG_ATTR;
242                                 attr = (char) ch;
243                                 cur = append(buf, cur, lang, state, (char) ch);
244                                 break;
245 
246                             // switch out of protected attr mode
247                             case STATE_TAG_ATTR:
248                                 // only bust out if the quote matches & is not being escaped
249                                 if (attr == ch && buf[cur] != '\\') {
250                                     state = STATE_TAG_START;
251                                     attr = 0;
252                                 }
253                                 cur = append(buf, cur, lang, state, (char) ch);
254                                 break;
255 
256                             default:
257                                 cur = append(buf, cur, lang, state, (char) ch);
258                         }
259                         break;
260 
261 
262                     default:
263                         cur = append(buf, cur, lang, state, (char) ch);
264                 }
265                 
266                 ch = in.read();
267                 column++;
268             }
269             
270             // flush remaining
271             lang.print(buf, 1, cur);
272         
273         } catch (GSPException gspe) {
274             if (gspe.getLine() < 1) {
275                 gspe.setLine(line);
276             }
277             if (gspe.getColumn() < 1) {
278                 gspe.setColumn(column);
279             }
280             throw gspe;
281         
282         } catch (Exception e) {
283             throw new GSPException(e, line, column);
284         }
285     }
286     
287     /**
288      * Appends the specified character to the buffer.
289      * Flushes the buffer if it's almost full.
290      * 
291      * @param buf Buffer.
292      * @param cur Buffer cursor (current position).
293      * @param ch Character to append.
294      * @param lang GSP language.
295      * @param state Parser state.
296      * 
297      * @return New buffer cursor.
298      */
299     protected int append(char[] buf, int cur, IGSPLanguage lang, int state, char ch) throws GSPException {
300         // flush buf
301         if (cur > BUF_MAX) {
302             switch (state) {
303                 case STATE_BODY:
304                     lang.print(buf, 1, cur - LL);
305                     break;
306                 case STATE_CODE:
307                     lang.code(buf, 1, cur - LL);
308                     break;
309                 case STATE_DECL:
310                     lang.decl(buf, 1, cur - LL);
311                     break;
312                 case STATE_COMM:
313                     break;
314                 default:
315                     throw new GSPException("expr too long", 0, 0);
316             }
317             
318             // keep lookahead chars
319             System.arraycopy(buf, cur - LL + 1, buf, 1, LL);
320             cur = LL;
321         }
322         
323         // append char
324         buf[++cur] = ch;
325         
326         // new cursor
327         return cur;
328     }
329     
330     /**
331      * Passes the specified directive to the specified language.
332      * 
333      * @param buf Buffer containing directive.
334      * @param start Starting index of directive.
335      * @param length Length of directive.
336      * @param lang GSP language.
337      * @param compiler GSP compiler.
338      */
339     protected void parseDirective(char[] buf, int start, int length, IGSPLanguage lang, IGSPCompiler compiler) throws GSPException {
340         String directive = null;
341         Map params = new HashMap(0x0f);
342         
343         // skip leading whitespace
344         while (start < length && 
345         (buf[start] == ' ' || buf[start] == '\t' || buf[start] == '\r' || buf[start] == '\n')) {
346             start++;
347         }
348         
349         int end = start;
350 
351         // find directive name end
352         while (end < length &&
353         (buf[end] != ' ' && buf[end] != '\t' && buf[end] != '\r' && buf[end] != '\n')) {
354             end++;
355         }
356         
357         directive = new String(buf, start, end - start);
358         params = parseParameters(buf, end, length, params);
359         
360         if (!handleDirective(directive, params, lang, compiler)) {
361             lang.directive(directive, params);
362         }
363     }
364     
365     /**
366      * Handles the specified directive.
367      * 
368      * @param d Directive to handle.
369      * @param p Directive params.
370      * @param l Language.
371      * @param c Compiler.
372      * 
373      * @return true if directive was handled.
374      */
375     protected boolean handleDirective(String d, Map p, IGSPLanguage l, IGSPCompiler c) throws GSPException {
376         if ("include".equals(d)) {
377             String file = (String) p.get("file");
378             
379             try {
380                 Reader in = c.open(file);
381                 parse(in, l, c);
382             
383             } catch (GSPException gspe) {
384                 if (gspe.getFile() == null) {
385                     gspe.setFile(file);
386                 }
387                 throw gspe;
388             }
389             return true;
390         }
391         
392         return false;
393     }
394 
395     /**
396      * Passes the specified start tag to the specified language.
397      *
398      * @param buf Buffer containing directive.
399      * @param start Starting index of directive.
400      * @param length Length of directive.
401      * @param lang GSP language.
402      * @param prefix Tag prefix.
403      */
404     protected void parseStartTag(char[] buf, int start, int length, IGSPLanguage lang, String prefix) throws GSPException {
405         String name = null;
406         Map params = new HashMap(0x1f);
407         boolean empty = false;
408 
409         // test for empty tag (<p:n n='v' />)
410         if (buf[length - 1] == '/') {
411             empty = true;
412             length--;
413         }
414 
415         int end = start;
416 
417         // find tag name end
418         while (end < length &&
419         (buf[end] != ' ' && buf[end] != '\t' && buf[end] != '\r' && buf[end] != '\n')) {
420             end++;
421         }
422 
423         name = new String(buf, start, end - start);
424         params = parseParameters(buf, end, length, params);
425 
426         lang.tagStart(prefix, name, params);
427 
428         if (empty) {
429             lang.tagEnd(prefix, name);
430         }
431     }
432 
433     /**
434      * Parses attribute name="value" pairs out of the specified buffer.
435      *
436      * @param buf Buffer containing directive.
437      * @param start Starting index of directive.
438      * @param length Length of directive.
439      * @param params (optional) Map in which to store params.
440      *
441      * @return Map of parsed params.
442      */
443     protected Map parseParameters(char[] buf, int start, int length, Map params) {
444         if (params == null) {
445             params = new HashMap(0x20);
446         }
447 
448         String name = null;
449         String value = null;
450         int end = start;
451 
452         // find name start
453         while (end < length &&
454         (buf[end] == ' ' || buf[end] == '\t' || buf[end] == '\r' || buf[end] == '\n')) {
455             end++;
456         }
457 
458         while (end < length) {
459             start = end;
460 
461             // find name end
462             while (end < length &&
463             (buf[end] != '=' && buf[end] != ' ' && buf[end] != '\t' && buf[end] != '\r' && buf[end] != '\n')) {
464                 end++;
465             }
466 
467             name = new String(buf, start, end - start);
468 
469             start = end;
470 
471             // find value start
472             while (start < length &&
473             (buf[start] == '=' || buf[start] == ' ' || buf[start] == '\t' || buf[start] == '\r' || buf[start] == '\n')) {
474                 start++;
475             }
476 
477             boolean foundEnd = false;
478             end = start + 1;
479 
480             // find value end
481             while (end < length && !foundEnd) {
482                 switch (buf[end]) {
483                     case '\\':
484                         end++;
485                         break;
486                     case '\'':
487                     case '\"':
488                         if (buf[end] == buf[start]) {
489                             foundEnd = true;
490                         }
491                         break;
492                 }
493                 end++;
494             }
495 
496             value = new String(buf, start + 1, end - start - 2);
497 
498             // add to map
499             params.put(name, value);
500 
501             // find name start
502             while (end < length &&
503             (buf[end] == ' ' || buf[end] == '\t' || buf[end] == '\r' || buf[end] == '\n')) {
504                 end++;
505             }
506         }
507 
508         return params;
509     }
510 }