KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > eclipse > editors > XmlScanner


1 /*
2  * Copyright (c) 2003 The Visigoth Software Society. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowledgement:
19  * "This product includes software developed by the
20  * Visigoth Software Society (http://www.visigoths.org/)."
21  * Alternately, this acknowledgement may appear in the software
22  * itself, if and wherever such third-party acknowledgements
23  * normally appear.
24  *
25  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names
26  * of the project contributors may be used to endorse or promote
27  * products derived from this software without prior written
28  * permission. For written permission, please contact
29  * visigoths@visigoths.org.
30  *
31  * 5. Products derived from this software may not be called
32  * "FreeMarker" or "Visigoth" nor may "FreeMarker" or "Visigoth"
33  * appear in their names without prior written permission of the
34  * Visigoth Software Society.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Visigoth Software Society. For more
52  * information on the Visigoth Software Society, please see
53  * http://www.visigoths.org/
54  */

55
56 package freemarker.eclipse.editors;
57
58 import java.util.Vector JavaDoc;
59
60 import org.eclipse.jface.text.rules.ICharacterScanner;
61 import org.eclipse.jface.text.rules.IRule;
62 import org.eclipse.jface.text.rules.IToken;
63 import org.eclipse.jface.text.rules.MultiLineRule;
64 import org.eclipse.jface.text.rules.RuleBasedScanner;
65 import org.eclipse.jface.text.rules.Token;
66 import org.eclipse.jface.text.rules.WhitespaceRule;
67
68 /**
69  * The XML tag scanner. This class handles the lexical scanning
70  * of the XML tag partitions, separating out FreeMarker constructs
71  * into their own tokens (with their own colors).
72  *
73  * @version $Id: version $
74  * @author <a HREF="mailto:stephan@chaquotay.net">Stephan Mueller</a>
75  * @author <a HREF="mailto:per&#64;percederberg.net">Per Cederberg</a>
76  */

77 public class XmlScanner extends RuleBasedScanner {
78
79     /**
80      * The stack of initiated character sequences. When a quote or a
81      * FreeMarker directive is encountered, the starting character
82      * sequence is added to the stack. The stack can then be used to
83      * check for a matching end sequence. It is also used to ensure
84      * proper nesting of attributes and directives.
85      */

86     private Vector JavaDoc stack = new Vector JavaDoc();
87
88     /**
89      * Creates a new XML tag scanner.
90      *
91      * @param manager the color manager to use
92      */

93     public XmlScanner(ITokenManager manager) {
94         Vector JavaDoc rules = new Vector JavaDoc();
95         IRule[] result;
96         IToken comment;
97         IToken directive;
98         IToken interpolation;
99         IToken string;
100         
101         // Retrieve tokens
102
string = manager.getStringToken();
103         comment = manager.getCommentToken();
104         directive = manager.getDirectiveToken();
105         interpolation = manager.getInterpolationToken();
106
107         // Add rules for FreeMarker constructs
108
rules.add(new MultiLineRule("<#--", "-->", comment));
109         rules.add(new XmlDirectiveRule(directive));
110         rules.add(new InterpolationRule(interpolation));
111
112         // Add rule for XML attributes
113
rules.add(new XmlAttributeRule(string));
114
115         // Add generic whitespace rule.
116
rules.add(new WhitespaceRule(new WhitespaceDetector()));
117
118         // Set scanner rules
119
result = new IRule[rules.size()];
120         rules.copyInto(result);
121         setRules(result);
122     }
123
124     /**
125      * Returns the top character sequence in the stack.
126      *
127      * @return the top character sequence in the stack, or
128      * null if the stack is empty
129      */

130     protected String JavaDoc topSequence() {
131         if (stack.isEmpty()) {
132             return null;
133         } else {
134             return (String JavaDoc) stack.lastElement();
135         }
136     }
137
138     /**
139      * Adds a new character sequence to the top of the stack.
140      *
141      * @param str the character sequence to add
142      */

143     protected void pushSequence(String JavaDoc str) {
144         stack.add(str);
145     }
146
147     /**
148      * Removes the top character sequence from the stack.
149      */

150     protected void popSequence() {
151         if (stack.size() > 0) {
152             stack.remove(stack.size() - 1);
153         }
154     }
155
156     /**
157      * A simple XML attribute value rule. This rule is used to match
158      * XML attribute values. It interrupts and resumes matching for
159      * FreeMarker directives and interpolations.
160      *
161      * @version $Id: version $
162      * @author <a HREF="mailto:stephan@chaquotay.net">Stephan Mueller</a>
163      * @author <a HREF="mailto:per&#64;percederberg.net">Per Cederberg</a>
164      */

165     protected class XmlAttributeRule implements IRule {
166
167         /**
168          * The token to return for this rule.
169          */

170         private final IToken token;
171
172         /**
173          * Creates a new XML attribute rule.
174          *
175          * @param token the token to use for this rule
176          */

177         public XmlAttributeRule(IToken token) {
178             this.token = token;
179         }
180
181         /**
182          * Checks if character is an XML quote character.
183          *
184          * @param c the character to check
185          *
186          * @return true if the character is a quote character, or
187          * false otherwise
188          */

189         private boolean isQuoteChar(char c) {
190             return c == '"' || c == '\'';
191         }
192
193         /*
194          * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
195          */

196         public IToken evaluate(ICharacterScanner scanner) {
197             String JavaDoc start;
198             int c;
199
200             // Check for quote start (if not already read)
201
start = topSequence();
202             if (start == null || !isQuoteChar(start.charAt(0))) {
203                 c = scanner.read();
204                 if (c == EOF || !isQuoteChar((char) c)) {
205                     scanner.unread();
206                     return Token.UNDEFINED;
207                 }
208                 pushSequence(String.valueOf((char) c));
209                 start = topSequence();
210             }
211
212             // Read until quote end, interpolation or (user-)directive
213
do {
214                 c = scanner.read();
215                 if (c == '$' || c == '#') {
216                     c = scanner.read();
217                     scanner.unread();
218                     if (c == '{') {
219                         scanner.unread();
220                         return token;
221                     }
222                     c = '$';
223                 } else if (c == '<') {
224                     c = scanner.read();
225                     if (c == '/') {
226                         c = scanner.read();
227                         scanner.unread();
228                     }
229                     scanner.unread();
230                     if (c == '#' || c == '@') {
231                         scanner.unread();
232                         return token;
233                     }
234                     c = '<';
235                 }
236             } while (c != EOF && c != start.charAt(0));
237
238             // Remove from stack and return result
239
popSequence();
240             return token;
241         }
242     }
243
244     /**
245      * A simple FreeMarker directive rule. This rule is used to match
246      * FreeMarker directives withing XML partitions. It interrupts
247      * and resumes matching for FreeMarker comments.
248      *
249      * @version $Id: version $
250      * @author <a HREF="mailto:per&#64;percederberg.net">Per Cederberg</a>
251      */

252     protected class XmlDirectiveRule implements IRule {
253
254         /**
255          * The token to return for this rule.
256          */

257         private final IToken token;
258
259         /**
260          * Creates a new XML attribute rule.
261          *
262          * @param token the token to use for this rule
263          */

264         public XmlDirectiveRule(IToken token) {
265             this.token = token;
266         }
267
268         /*
269          * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
270          */

271         public IToken evaluate(ICharacterScanner scanner) {
272             String JavaDoc start;
273             int parens = 0;
274             int c;
275
276             // Check for directive start (if not already read)
277
start = topSequence();
278             if (start == null || !start.equals("<#")) {
279                 if (!FreemarkerTools.readDirectiveStart(scanner)) {
280                     return Token.UNDEFINED;
281                 }
282                 pushSequence("<#");
283             }
284
285             // Read until directive end or comment
286
do {
287                 c = scanner.read();
288                 if (c == '<') {
289                     scanner.unread();
290                     if (FreemarkerTools.readCommentStart(scanner)) {
291                         scanner.unread();
292                         scanner.unread();
293                         scanner.unread();
294                         scanner.unread();
295                         return token;
296                     }
297                     c = scanner.read();
298                 } else if (c == '"') {
299                     FreemarkerTools.readQuoteEnd(scanner);
300                 } else if (c == '(') {
301                     parens++;
302                 } else if (c == ')') {
303                     parens--;
304                 }
305             } while (c != EOF && (c != '>' || parens > 0));
306
307             // Remove from stack and return result
308
popSequence();
309             return token;
310         }
311     }
312 }
313
Popular Tags