KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > language > markup > xsp > XSPExpressionParser


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.language.markup.xsp;
17
18 import org.xml.sax.SAXException JavaDoc;
19
20 /**
21  * Parse XSP expressions. Expressions are embedded in attribute="value" and text elements and are
22  * expanded by the
23  * {@link org.apache.cocoon.components.language.markup.xsp.XSPMarkupLanguage.PreProcessFilter PreProcessFilter}
24  * and have the form {#expression}. To prevent interpolation, use {##quote}, which results in the
25  * text {#quote}.
26  * An exception is thrown if the closing brace is missing.
27  * <p>
28  * The parser has a rudimentary understanding of expressions concerning
29  * nested braces and braces inside quoted strings and character constants.
30  * All valid Java, Javascript, and Python expressions can be used.
31  * <p>
32  * Example: &lt;h1&gt;Hello {#user.getName()}&lt;/h1&gt; &lt;img or
33  * SRC=&quot;image_{#image.getId()}&quot;/&gt;
34  * <p>
35  *
36  * @version SVN $Id: XSPExpressionParser.java 278684 2005-09-05 07:26:57Z anathaniel $
37  */

38 public class XSPExpressionParser {
39
40     /**
41      * Handler interface for parsed expressions and text fragments. The parser calls the handler to
42      * process these.
43      */

44     public static interface Handler {
45         public void handleText(char[] chars, int start, int length) throws SAXException JavaDoc;
46
47         public void handleExpression(char[] chars, int start, int length) throws SAXException JavaDoc;
48     }
49
50     /**
51      * Parser state.
52      */

53     protected static abstract class State {
54         /**
55          * Consume the next character
56          *
57          * @param parser The parser
58          * @param ch The character to consume
59          * @throws SAXException If there is an error in the expression
60          */

61         public abstract void consume(XSPExpressionParser parser, char ch) throws SAXException JavaDoc;
62
63         /**
64          * Finish processing. Default behaviour is to throw an expression. States that are legal end
65          * states must overwrite this method.
66          *
67          * @param parser The parser
68          * @throws SAXException It is illegal to finish processing in this state.
69          */

70         public void done(XSPExpressionParser parser) throws SAXException JavaDoc {
71             throw new SAXException JavaDoc("Incomplete XSP expression {#"+parser.getExpression());
72         }
73     }
74
75     /**
76      * Parser state in a quoted string.
77      */

78     protected static class QuotedState extends State {
79         private final char quote;
80
81         /**
82          * Create state to process quotes strings.
83          *
84          * @param quote The quote character to delimit strings
85          */

86         public QuotedState(char quote) {
87             this.quote = quote;
88         }
89
90         /**
91          * Consume the next character
92          *
93          * @param parser The parser
94          * @param ch The character to consume
95          * @throws SAXException If there is an error in the expression
96          */

97         public void consume(XSPExpressionParser parser, char ch) throws SAXException JavaDoc {
98             parser.append(ch);
99             if (ch == quote && !parser.isEscaped())
100                 parser.setState(EXPRESSION_STATE);
101             else if (ch == '\\')
102                 parser.setEscaped(!parser.isEscaped());
103             else
104                 parser.setEscaped(false);
105         }
106     }
107
108     /**
109      * The parser is parsing text.
110      */

111     protected static final State TEXT_STATE = new State() {
112         public void consume(XSPExpressionParser parser, char ch) throws SAXException JavaDoc {
113             switch (ch) {
114                 case '{':
115                     parser.setState(LBRACE_STATE);
116                     break;
117
118                 default:
119                     parser.append(ch);
120             }
121         }
122
123         /**
124          * Handle remaining text. It is legal to end in text mode.
125          *
126          * @see State#done(XSPExpressionParser)
127          */

128         public void done(XSPExpressionParser parser) throws SAXException JavaDoc {
129             parser.handleText();
130         }
131     };
132
133     /**
134      * The parser has encountered '{' in <code>{@link TEXT_STATE}</code>.
135      */

136     protected static final State LBRACE_STATE = new State() {
137         public void consume(XSPExpressionParser parser, char ch) throws SAXException JavaDoc {
138             switch (ch) {
139                 case '#':
140                     parser.setState(TEXT_HASH_STATE);
141                     break;
142
143                 default:
144                     parser.append('{');
145                     parser.append(ch);
146                     parser.setState(TEXT_STATE);
147             }
148         }
149
150         /**
151          * Handle remaining text. It is legal to end text with '{'.
152          *
153          * @see State#done(XSPExpressionParser)
154          */

155         public void done(XSPExpressionParser parser) throws SAXException JavaDoc {
156             // Append the pending '{'
157
parser.append('{');
158             parser.handleText();
159         }
160     };
161
162     /**
163      * The parser has encountered '#' in <code>{@link LBRACE_STATE}</code>.
164      */

165     protected static final State TEXT_HASH_STATE = new State() {
166         public void consume(XSPExpressionParser parser, char ch) throws SAXException JavaDoc {
167             switch (ch) {
168                 case '#':
169                     parser.append('{');
170                     parser.append('#');
171                     parser.setState(TEXT_STATE);
172                     break;
173
174                 default:
175                     parser.handleText();
176                     parser.initExpression();
177                     parser.setState(EXPRESSION_STATE);
178                     EXPRESSION_STATE.consume(parser, ch);
179             }
180         }
181     };
182
183     /**
184      * The parser is parsing an expression.
185      */

186     protected static final State EXPRESSION_STATE = new State() {
187         public void consume(XSPExpressionParser parser, char ch) throws SAXException JavaDoc {
188             switch (ch) {
189                 case '{':
190                     parser.incrNesting();
191                     parser.append(ch);
192                     break;
193
194                 case '}':
195                     if (parser.decrNesting() > 0) {
196                         parser.append(ch);
197                     }
198                     else {
199                         parser.handleExpression();
200                         parser.setState(TEXT_STATE);
201                     }
202                     break;
203
204                 case '"':
205                     parser.append(ch);
206                     parser.setState(EXPRESSION_STRING_STATE);
207                     break;
208
209                 case '\'':
210                     parser.append(ch);
211                     parser.setState(EXPRESSION_CHAR_STATE);
212                     break;
213
214                 case '\u00B4':
215                     parser.append(ch);
216                     parser.setState(EXPRESSION_SHELL_STATE);
217                     break;
218
219                 default:
220                     parser.append(ch);
221             }
222         }
223     };
224
225     /**
226      * The parser has encountered '"' in <code>{@link EXPRESSION_STATE}</code>
227      * to start a string constant.
228      */

229     protected static final State EXPRESSION_STRING_STATE = new QuotedState('"');
230
231     /**
232      * The parser has encountered '\'' in <code>{@link EXPRESSION_STATE}</code>
233      * to start a character constant.
234      */

235     protected static final State EXPRESSION_CHAR_STATE = new QuotedState('\'');
236
237     /**
238      * The parser has encountered '`' (Backtick, ASCII 0x60) in
239      * <code>{@link EXPRESSION_STATE}</code> to start a Python string constant.
240      */

241     protected static final State EXPRESSION_SHELL_STATE = new QuotedState('`');
242
243     /**
244      * The parser state
245      */

246     private State state = TEXT_STATE;
247
248     /**
249      * The nesting level of braces.
250      */

251     private int nesting = 0;
252
253     /**
254      * Flag whether previous character was a backslash to escape quotes.
255      */

256     private boolean escaped = false;
257
258     /**
259      * The handler for parsed text and expression fragments.
260      */

261     private Handler handler;
262
263     /**
264      * The buffer for the current text or expression fragment. We do our own StringBuffer here to
265      * save some allocations of char arrays for the handler.
266      */

267     private char[] buf = new char[256];
268
269     /**
270      * The current size of the fragment in the buffer.
271      */

272     private int bufSize;
273
274     /**
275      * The delty by which the buffer grows if it is too small.
276      */

277     private int bufGrow = 256;
278
279     /**
280      * Create a new <code>{@link XSPExpressionParser}</code>.
281      *
282      * @param handler The handler for parsed text and expression fragments.
283      */

284     public XSPExpressionParser(Handler handler) {
285         this.handler = handler;
286     }
287
288     /**
289      * Parses a character sequence.
290      *
291      * @param chars The character sequence to parse
292      * @throws SAXException If there is an error in the sequence.
293      */

294     public void consume(String JavaDoc chars) throws SAXException JavaDoc {
295         int end = chars.length();
296
297         for (int i = 0; i < end; ++i) {
298             char ch = chars.charAt(i);
299             state.consume(this, ch);
300         }
301     }
302
303     /**
304      * Parses part of a character array.
305      *
306      * @param chars The characters
307      * @param start The start position in the character array
308      * @param length The number of characters to parse
309      * @throws SAXException If there is an error in the sequence.
310      */

311     public void consume(char[] chars, int start, int length) throws SAXException JavaDoc {
312         int end = start + length;
313
314         for (int i = start; i < end; ++i) {
315             char ch = chars[i];
316             state.consume(this, ch);
317         }
318     }
319
320     /**
321      * Flushes the parser
322      *
323      * @throws SAXException If there is an error in the parsed text.
324      */

325     public void flush() throws SAXException JavaDoc {
326         state.done(this);
327         bufSize = 0;
328         state = TEXT_STATE;
329     }
330
331     protected State getState() {
332         return state;
333     }
334
335     protected void setState(State state) {
336         this.state = state;
337     }
338
339     protected void initExpression() {
340         nesting = 1;
341         escaped = false;
342     }
343
344     protected void incrNesting() {
345         nesting++;
346     }
347
348     protected int decrNesting() {
349         return --nesting;
350     }
351
352     protected void setEscaped(boolean escaped) {
353         this.escaped = escaped;
354     }
355
356     protected boolean isEscaped() {
357         return escaped;
358     }
359
360     protected String JavaDoc getExpression() {
361         return new String JavaDoc(buf, 0, bufSize);
362     }
363
364     protected void handleText() throws SAXException JavaDoc {
365         if (bufSize > 0) {
366             handler.handleText(buf, 0, bufSize);
367             bufSize = 0;
368         }
369     }
370
371     protected void handleExpression() throws SAXException JavaDoc {
372         if (bufSize == 0) {
373             throw new SAXException JavaDoc("Illegal empty expression.");
374         }
375
376         handler.handleExpression(buf, 0, bufSize);
377
378         bufSize = 0;
379     }
380
381     protected void append(char ch) {
382         if (bufSize + 1 >= buf.length) {
383             char[] newBuf = new char[buf.length + bufGrow];
384             System.arraycopy(buf, 0, newBuf, 0, buf.length);
385             buf = newBuf;
386         }
387
388         buf[bufSize] = ch;
389         ++bufSize;
390     }
391 }
392
Popular Tags