KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > util > xml > ReflectiveParser


1 /*
2  * Copyright 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.util.xml;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.core.ext.UnableToCompleteException;
20 import com.google.gwt.util.tools.Utility;
21
22 import org.xml.sax.Attributes JavaDoc;
23 import org.xml.sax.InputSource JavaDoc;
24 import org.xml.sax.Locator JavaDoc;
25 import org.xml.sax.SAXException JavaDoc;
26 import org.xml.sax.XMLReader JavaDoc;
27 import org.xml.sax.helpers.DefaultHandler JavaDoc;
28
29 import java.io.IOException JavaDoc;
30 import java.io.Reader JavaDoc;
31 import java.util.Stack JavaDoc;
32
33 import javax.xml.parsers.ParserConfigurationException JavaDoc;
34 import javax.xml.parsers.SAXParser JavaDoc;
35 import javax.xml.parsers.SAXParserFactory JavaDoc;
36
37 /**
38  * Somewhat general-purpose SAX-style XML parser that uses reflection and calls
39  * into your "schema" classes. For example, the element
40  * <code>&lt;server-name&gt;</code> maps to the method
41  * <code>server_name</code>. Note that the mapping is one-way, hyphens become
42  * underscores, but then you don't really want to use underscores in XML tag
43  * names anyway, do you? Also, all mixed content text (that is, text inside
44  * elements) is ignored, so think attributes.
45  */

46 public final class ReflectiveParser {
47
48   private static final class Impl extends DefaultHandler JavaDoc {
49
50     private Locator JavaDoc locator;
51
52     private Reader JavaDoc reader;
53
54     private Stack JavaDoc schemaLevels = new Stack JavaDoc();
55
56     private Stack JavaDoc argStack = new Stack JavaDoc();
57
58     private Schema defaultSchema;
59
60     public void characters(char[] ch, int start, int length)
61         throws SAXException JavaDoc {
62       int lineNumber = locator.getLineNumber();
63
64       // Get the active schema level.
65
//
66
Schema schemaLevel = getTopSchemaLevel();
67
68       // Find the precomputed handler class info.
69
//
70
Class JavaDoc slc = schemaLevel.getClass();
71       HandlerClassInfo classInfo = HandlerClassInfo.getClassInfo(slc);
72       assert (classInfo != null); // would've thrown if unregistered
73
HandlerMethod method = classInfo.getTextMethod();
74       if (method == null) {
75         // This is okay. Nothing special to do.
76
//
77
return;
78       }
79
80       // Call the handler.
81
//
82
try {
83         final String JavaDoc text = String.valueOf(ch, start, length);
84         method.invokeText(lineNumber, text, schemaLevel);
85       } catch (UnableToCompleteException e) {
86         throw new SAXException JavaDoc(e);
87       }
88     }
89
90     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc elem)
91         throws SAXException JavaDoc {
92       int lineNumber = locator.getLineNumber();
93
94       // Get the active schema level.
95
//
96
Schema schemaLevel = popLevel();
97       if (schemaLevel == null) {
98         // This was an unexpected child, but we already informed the schema
99
// about it during startElement(), so we can just return.
100
//
101
return;
102       }
103
104       // Find the precomputed handler class info.
105
//
106
Class JavaDoc slc = schemaLevel.getClass();
107       HandlerClassInfo classInfo = HandlerClassInfo.getClassInfo(slc);
108       assert (classInfo != null); // would've thrown if unregistered
109
HandlerMethod method = classInfo.getEndMethod(elem);
110       if (method == null) {
111         // This is okay. Nothing special to do.
112
//
113
return;
114       }
115
116       Object JavaDoc[] args = getCurrentArgs();
117       if (args != null) {
118         // Call the handler using the same arguments we send to the "begin"
119
// handler.
120
//
121
try {
122           method.invokeEnd(lineNumber, elem, schemaLevel, args);
123         } catch (UnableToCompleteException e) {
124           throw new SAXException JavaDoc(e);
125         }
126       }
127     }
128
129     public void setDocumentLocator(Locator JavaDoc locator) {
130       this.locator = locator;
131     }
132
133     public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
134         String JavaDoc elemName, Attributes JavaDoc atts) throws SAXException JavaDoc {
135       int lineNumber = locator.getLineNumber();
136
137       // Get the active schema level.
138
//
139
Schema schemaLevel = getTopSchemaLevel();
140       if (schemaLevel == null) {
141         // This means that children should not appear at this level.
142
//
143
Schema nextToTop = getNextToTopSchemaLevel();
144
145         // Push another null since this child shouldn't have children either.
146
//
147
setArgsAndPushLevel(null, null);
148
149         // Inform the next-to-top schema level about this.
150
//
151
try {
152           nextToTop.onUnexpectedChild(lineNumber, elemName);
153         } catch (UnableToCompleteException e) {
154           throw new SAXException JavaDoc(e);
155         }
156
157         return;
158       }
159
160       // Find the precomputed handler class info.
161
//
162
Class JavaDoc slc = schemaLevel.getClass();
163       HandlerClassInfo classInfo = HandlerClassInfo.getClassInfo(slc);
164       HandlerMethod method = classInfo.getStartMethod(elemName);
165
166       if (method == null) {
167         // This is not okay. The schema has to at least have a stub
168
// to indicate that a particular tag is allowed.
169
//
170
try {
171           schemaLevel.onUnexpectedElement(lineNumber, elemName);
172         } catch (UnableToCompleteException e) {
173           throw new SAXException JavaDoc(e);
174         }
175
176         // Since we don't know about this element, assume it should not have
177
// children either.
178
//
179
setArgsAndPushLevel(null, null);
180
181         return;
182       }
183
184       HandlerArgs args = method.createArgs(schemaLevel, lineNumber, elemName);
185
186       // For each attribute found, try to match it up to a parameter.
187
//
188
for (int i = 0, n = atts.getLength(); i < n; ++i) {
189         String JavaDoc attrName = atts.getQName(i);
190         String JavaDoc attrValue = atts.getValue(i);
191
192         if (!args.setArg(attrName, attrValue)) {
193           // Inform the handler that the attribute was unknown.
194
//
195
try {
196             schemaLevel.onUnexpectedAttribute(lineNumber, elemName, attrName,
197                 attrValue);
198           } catch (UnableToCompleteException e) {
199             throw new SAXException JavaDoc(e);
200           }
201         }
202       }
203
204       // Check for unset parameters.
205
//
206
int missingCount = 0;
207       for (int i = 0, n = args.getArgCount(); i < n; ++i) {
208         if (!args.isArgSet(i)) {
209           // Inform the handler that the required attribute was not set.
210
// It might throw, but it also might not.
211
//
212
try {
213             schemaLevel.onMissingAttribute(lineNumber, elemName,
214                 args.getArgName(i));
215           } catch (UnableToCompleteException e) {
216             throw new SAXException JavaDoc(e);
217           }
218
219           ++missingCount;
220         }
221       }
222
223       if (missingCount > 0) {
224         // Do not invoke the handler.
225
//
226

227         // Assume that children shouldn't be recognized either since the
228
// handler wasn't invoked.
229
//
230
setArgsAndPushLevel(null, null);
231
232         return;
233       }
234
235       // Invoke the handler method, which will internally
236
// convert all the args to their respective parameter types
237
// (or warn if there is a problem doing so).
238
//
239
Object JavaDoc[] invokeArgs = new Object JavaDoc[method.getParamCount()];
240       Schema childSchemaLevel;
241       try {
242         childSchemaLevel = method.invokeBegin(lineNumber, elemName,
243             schemaLevel, args, invokeArgs);
244       } catch (UnableToCompleteException e) {
245         throw new SAXException JavaDoc(e);
246       }
247
248       // childSchemaLevel can be null and that's okay -- it means no children
249
// are expected. Same for invokeArgs[0] -- it means that the "begin"
250
// handler was not called, so neither will we call the "end" handler.
251
//
252
setArgsAndPushLevel(invokeArgs, childSchemaLevel);
253     }
254
255     private Object JavaDoc[] getCurrentArgs() {
256       return (Object JavaDoc[]) argStack.peek();
257     }
258
259     private Schema getNextToTopSchemaLevel() {
260       return (Schema) schemaLevels.get(schemaLevels.size() - 2);
261     }
262
263     private Schema getTopSchemaLevel() {
264       return (Schema) schemaLevels.peek();
265     }
266
267     private void parse(TreeLogger logger, Schema topSchema, Reader JavaDoc reader)
268         throws UnableToCompleteException {
269       // Set up the parentmost schema which is used to find default converters
270
// and handlers (but isn't actually on the schema stack.)
271
//
272
defaultSchema = new DefaultSchema(logger);
273
274       // Tell this schema level about the default schema, which is initialized
275
// with
276
// converters for basic types.
277
//
278
topSchema.setParent(defaultSchema);
279
280       // Make a slot for the document element's args.
281
//
282
argStack.push(null);
283
284       // Push the first schema.
285
//
286
setArgsAndPushLevel(null, topSchema);
287
288       Throwable JavaDoc caught = null;
289       try {
290         this.reader = reader;
291         SAXParser JavaDoc parser = SAXParserFactory.newInstance().newSAXParser();
292         InputSource JavaDoc inputSource = new InputSource JavaDoc(this.reader);
293         XMLReader JavaDoc xmlReader = parser.getXMLReader();
294         xmlReader.setContentHandler(this);
295         xmlReader.parse(inputSource);
296       } catch (SAXException JavaDoc e) {
297         // If it's an exception wrapped in a SAXException, rip off the outer SAX
298
// exception.
299
//
300
caught = e;
301
302         Exception JavaDoc inner = e.getException();
303         if (inner instanceof RuntimeException JavaDoc) {
304           throw (RuntimeException JavaDoc) inner;
305         } else if (inner != null) {
306           caught = inner;
307         }
308
309       } catch (ParserConfigurationException JavaDoc e) {
310         caught = e;
311       } catch (IOException JavaDoc e) {
312         caught = e;
313       } finally {
314         Utility.close(reader);
315       }
316
317       if (caught != null) {
318         Messages.XML_PARSE_FAILED.log(logger, caught);
319         throw new UnableToCompleteException();
320       }
321     }
322
323     private Schema popLevel() {
324       argStack.pop();
325       schemaLevels.pop();
326       return getTopSchemaLevel();
327     }
328
329     private void setArgsAndPushLevel(Object JavaDoc[] handlerArgs, Schema schemaLevel) {
330       // Set the args on the current schema level.
331
argStack.set(argStack.size() - 1, handlerArgs);
332       // A slot for the args at the childrens' depth.
333
argStack.push(null);
334       if (!schemaLevels.isEmpty()) {
335         // Tell this schema level about its parent.
336
//
337
Schema maybeParent = null;
338         for (int i = schemaLevels.size() - 1; i >= 0; --i) {
339           maybeParent = (Schema) schemaLevels.get(i);
340           if (maybeParent != null) {
341             break;
342           }
343         }
344         if (maybeParent == null) {
345           throw new IllegalStateException JavaDoc("Cannot find any parent schema");
346         }
347         if (schemaLevel != null) {
348           schemaLevel.setParent(maybeParent);
349         }
350       }
351       // The schema for children.
352
schemaLevels.push(schemaLevel);
353     }
354   }
355
356   public static void parse(TreeLogger logger, Schema schema, Reader JavaDoc reader)
357       throws UnableToCompleteException {
358
359     // Register the schema level.
360
//
361
registerSchemaLevel(schema.getClass());
362
363     // Do the parse.
364
//
365
Impl impl = new Impl();
366     impl.parse(logger, schema, reader);
367   }
368
369   /**
370    * Can safely register the same class recursively.
371    */

372   public static void registerSchemaLevel(Class JavaDoc schemaLevelClass) {
373     HandlerClassInfo.registerClass(schemaLevelClass);
374
375     // Try to register nested classes.
376
//
377
Class JavaDoc[] nested = schemaLevelClass.getDeclaredClasses();
378     for (int i = 0, n = nested.length; i < n; ++i) {
379       Class JavaDoc nestedClass = nested[i];
380       if (Schema.class.isAssignableFrom(nestedClass)) {
381         registerSchemaLevel(nestedClass);
382       }
383     }
384   }
385 }
386
Popular Tags