Source code: gnu/java/beans/decoder/PersistenceParser.java
1 /* gnu.java.beans.PersistenceParser
2 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.java.beans.decoder;
39
40 import java.beans.ExceptionListener;
41 import java.beans.XMLDecoder;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.util.HashMap;
45 import java.util.Iterator;
46 import java.util.LinkedList;
47 import java.util.List;
48
49 import javax.xml.parsers.ParserConfigurationException;
50 import javax.xml.parsers.SAXParser;
51 import javax.xml.parsers.SAXParserFactory;
52
53 import org.xml.sax.Attributes;
54 import org.xml.sax.SAXException;
55 import org.xml.sax.helpers.DefaultHandler;
56
57 /** The PersistenceParser parses an XML data stream and delegates actions to ElementHandler
58 * instances. The parser catches and recovers from all exception which reside from wrong usage
59 * of attributes and tags.
60 *
61 * @author Robert Schuster
62 */
63 public class PersistenceParser extends DefaultHandler implements Context
64 {
65 /** The ExceptionListener instance which is informed of non-critical parsing exceptions.
66 */
67 private ExceptionListener exceptionListener;
68
69 /** When an element was not usable all elements inside it should be skipped.
70 * This is done by skipping startElement() and endElement() invocations whenever
71 * this value is above 0.
72 */
73 private int skipElement;
74
75 /** Stores the Creator instances which can instantiate the appropriate handler implementation
76 * for a given element.
77 */
78 private HashMap handlerCreators = new HashMap();
79
80 /** Denotes the current ElementHandler. To avoid checking for null-values it is pre-assigned
81 * with a DummyHandler instance which must not be used but acts as a root element.
82 */
83 private ElementHandler currentHandler;
84
85 /** The real root element that stores all objects created during parsing.
86 * Package-private to avoid an accessor method.
87 */
88 JavaHandler javaHandler;
89
90 /** Stores the decoded objects. */
91 private List objects = new LinkedList();
92
93 /** The XMLDecoder instance that started this PersistenceParser */
94 private XMLDecoder decoder;
95
96 /** Creates a PersistenceParser which reads XML data from the given InputStream, reports
97 * exceptions to ExceptionListener instance, stores resulting object in the DecoderContext
98 * and uses the given ClassLoader to resolve classes.
99 *
100 * @param inputStream
101 * @param exceptionListener
102 * @param decoderContext
103 * @param cl
104 */
105 public PersistenceParser(
106 InputStream inputStream,
107 ExceptionListener exceptionListener,
108 ClassLoader cl,
109 XMLDecoder decoder)
110 {
111
112 this.exceptionListener = exceptionListener;
113 this.decoder = decoder;
114
115 DummyHandler dummyHandler = new DummyHandler();
116 currentHandler = dummyHandler;
117 javaHandler = new JavaHandler(dummyHandler, this, cl);
118
119 SAXParserFactory factory = SAXParserFactory.newInstance();
120
121 SAXParser parser;
122 try
123 {
124 parser = factory.newSAXParser();
125 }
126 catch (ParserConfigurationException pce)
127 {
128 // should not happen when a parser is available because we did
129 // not request any requirements on the XML parser
130 throw (InternalError) new InternalError(
131 "No SAX Parser available.").initCause(
132 pce);
133 }
134 catch (SAXException saxe)
135 {
136 // should not happen when a parser is available because we did
137 // not request any requirements on the XML parser
138 throw (InternalError) new InternalError(
139 "No SAX Parser available.").initCause(
140 saxe);
141 }
142
143 // prepares a map of Creator instances which can instantiate a handler which is
144 // appropriate for the tag that is used as a key for the Creator
145 handlerCreators.put("java", new JavaHandlerCreator());
146
147 // calls methods (properties), constructors, access fields
148 handlerCreators.put("object", new ObjectHandlerCreator());
149 handlerCreators.put("void", new VoidHandlerCreator());
150
151 handlerCreators.put("array", new ArrayHandlerCreator());
152
153 // these handler directly create an Object (or null)
154 handlerCreators.put("class", new ClassHandlerCreator());
155 handlerCreators.put("null", new NullHandlerCreator());
156
157 handlerCreators.put("char", new CharHandlerCreator());
158 handlerCreators.put("string", new StringHandlerCreator());
159 handlerCreators.put("boolean", new BooleanHandlerCreator());
160 handlerCreators.put("byte", new ByteHandlerCreator());
161 handlerCreators.put("short", new ShortHandlerCreator());
162 handlerCreators.put("int", new IntHandlerCreator());
163 handlerCreators.put("long", new LongHandlerCreator());
164 handlerCreators.put("float", new FloatHandlerCreator());
165 handlerCreators.put("double", new DoubleHandlerCreator());
166
167 // parses the data and sends all exceptions to the ExceptionListener
168 try
169 {
170 parser.parse(inputStream, this);
171 }
172 catch (SAXException saxe)
173 {
174 exceptionListener.exceptionThrown(
175 new IllegalArgumentException("XML data not well-formed."));
176 }
177 catch (IOException ioe)
178 {
179 exceptionListener.exceptionThrown(ioe);
180 }
181 }
182
183 public void startElement(
184 String uri,
185 String localName,
186 String qName,
187 Attributes attributes)
188 throws SAXException
189 {
190 /* The element is skipped if
191 * a) the current handler has already failed or a previous error occured
192 * which makes all children obsolete
193 */
194 if (currentHandler.hasFailed() || skipElement > 0)
195 {
196 exceptionListener.exceptionThrown(
197 new IllegalArgumentException(
198 "Element unusable due to previous error: " + qName));
199
200 skipElement++;
201
202 return;
203 }
204
205 /* b) Subelements are not allowed within the current ElementHandler.
206 */
207 if (!currentHandler.isSubelementAllowed(qName))
208 {
209 exceptionListener.exceptionThrown(
210 new IllegalArgumentException(
211 "Element is not allowed here: " + qName));
212
213 skipElement++;
214
215 return;
216 }
217
218 /* c) The tag name is not a key in the map of Creator instances. This means that
219 * either the XML data is of a newer version or simply contains a miss-spelled element.
220 */
221 if (!handlerCreators.containsKey(qName))
222 {
223 exceptionListener.exceptionThrown(
224 new IllegalArgumentException(
225 "Element unusable because tag is unknown: " + qName));
226
227 skipElement++;
228
229 return;
230 }
231
232 // creates a new handler for the new element
233 AbstractElementHandler handler =
234 ((Creator) handlerCreators.get(qName)).createHandler(
235 currentHandler);
236
237 // makes it the current handler to receive character data
238 currentHandler = handler;
239
240 // starts the handler
241 currentHandler.start(attributes, exceptionListener);
242 }
243
244 public void endElement(String uri, String localName, String qName)
245 throws SAXException
246 {
247 // skips processing the current handler if we are parsing an element
248 // which was marked invalid (in startElement() )
249 if (skipElement > 0)
250 {
251 skipElement--;
252 return;
253 }
254
255 // invokes the handler's finishing method
256 currentHandler.end(exceptionListener);
257
258 // removes the current handler and reactivates its parent
259 currentHandler = currentHandler.getParent();
260 }
261
262 /** Transfers character data to the current handler
263 */
264 public void characters(char[] ch, int start, int length)
265 throws SAXException
266 {
267 // prevents sending character data of invalid elements
268 if (skipElement > 0)
269 return;
270
271 currentHandler.characters(ch, start, length);
272 }
273
274 /** Creator interface provided a mechanism to instantiate ElementHandler instances
275 * for the appropriate tag.
276 *
277 * @author Robert Schuster
278 */
279 interface Creator
280 {
281 /** Creates an ElementHandler instance using the given ElementHandler as its parent.
282 *
283 * @param parent The parent ElementHandler of the result.
284 * @return A new ElementHandler instance.
285 */
286 AbstractElementHandler createHandler(ElementHandler parent);
287 }
288
289 class BooleanHandlerCreator implements Creator
290 {
291 public AbstractElementHandler createHandler(ElementHandler parent)
292 {
293 return new BooleanHandler(parent);
294 }
295 }
296
297 class ByteHandlerCreator implements Creator
298 {
299 public AbstractElementHandler createHandler(ElementHandler parent)
300 {
301 return new ByteHandler(parent);
302 }
303 }
304
305 class ShortHandlerCreator implements Creator
306 {
307 public AbstractElementHandler createHandler(ElementHandler parent)
308 {
309 return new ShortHandler(parent);
310 }
311 }
312
313 class IntHandlerCreator implements Creator
314 {
315 public AbstractElementHandler createHandler(ElementHandler parent)
316 {
317 return new IntHandler(parent);
318 }
319 }
320
321 class LongHandlerCreator implements Creator
322 {
323 public AbstractElementHandler createHandler(ElementHandler parent)
324 {
325 return new LongHandler(parent);
326 }
327 }
328
329 class FloatHandlerCreator implements Creator
330 {
331 public AbstractElementHandler createHandler(ElementHandler parent)
332 {
333 return new FloatHandler(parent);
334 }
335 }
336
337 class DoubleHandlerCreator implements Creator
338 {
339 public AbstractElementHandler createHandler(ElementHandler parent)
340 {
341 return new DoubleHandler(parent);
342 }
343 }
344
345 class CharHandlerCreator implements Creator
346 {
347 public AbstractElementHandler createHandler(ElementHandler parent)
348 {
349 return new CharHandler(parent);
350 }
351 }
352
353 class StringHandlerCreator implements Creator
354 {
355 public AbstractElementHandler createHandler(ElementHandler parent)
356 {
357 return new StringHandler(parent);
358 }
359 }
360
361 class JavaHandlerCreator implements Creator
362 {
363 public AbstractElementHandler createHandler(ElementHandler parent)
364 {
365 return javaHandler;
366 }
367 }
368
369 class ObjectHandlerCreator implements Creator
370 {
371 public AbstractElementHandler createHandler(ElementHandler parent)
372 {
373 return new ObjectHandler(parent);
374 }
375 }
376
377 class VoidHandlerCreator implements Creator
378 {
379 public AbstractElementHandler createHandler(ElementHandler parent)
380 {
381 return new VoidHandler(parent);
382 }
383 }
384
385 class ClassHandlerCreator implements Creator
386 {
387 public AbstractElementHandler createHandler(ElementHandler parent)
388 {
389 return new ClassHandler(parent);
390 }
391 }
392
393 class NullHandlerCreator implements Creator
394 {
395 public AbstractElementHandler createHandler(ElementHandler parent)
396 {
397 return new NullHandler(parent);
398 }
399 }
400
401 class ArrayHandlerCreator implements Creator
402 {
403 public AbstractElementHandler createHandler(ElementHandler parent)
404 {
405 return new ArrayHandler(parent);
406 }
407 }
408
409 /** Adds a decoded object to the Context. */
410 public void addParameterObject(Object o) throws AssemblyException
411 {
412 objects.add(o);
413 }
414
415 public void notifyStatement(Context outerContext) throws AssemblyException
416 {
417 // can be ignored because theis Context does not react to statement and expressions
418 // differently
419 }
420
421 public Object endContext(Context outerContext) throws AssemblyException
422 {
423 return null;
424 }
425
426 public boolean subContextFailed()
427 {
428 // failing of subcontexts is no problem for the mother of all contexts
429 return false;
430 }
431
432 public void set(int index, Object o) throws AssemblyException
433 {
434 // not supported
435 throw new AssemblyException(
436 new IllegalArgumentException("Set method is not allowed in decoder context."));
437 }
438
439 public Object get(int index) throws AssemblyException
440 {
441 // not supported
442 throw new AssemblyException(
443 new IllegalArgumentException("Get method is not allowed in decoder context."));
444 }
445
446 public Object getResult()
447 {
448 // returns the XMLDecoder instance which is requested by child contexts this way.
449 // That is needed to invoke methods on the decoder.
450 return decoder;
451 }
452
453 public void setId(String id)
454 {
455 exceptionListener.exceptionThrown(new IllegalArgumentException("id attribute is not allowed for <java> tag."));
456 }
457
458 public String getId()
459 {
460 // appears to have no id
461 return null;
462 }
463
464 public boolean isStatement()
465 {
466 // this context is a statement by definition because it never returns anything to a parent because
467 // there is no such parent (DummyContext does not count!)
468 return true;
469 }
470
471 public void setStatement(boolean b)
472 {
473 // ignores that because this Context is always a statement
474 }
475
476 /** Returns an Iterator instance which returns the decoded objects.
477 *
478 * This method is used by the XMLDecoder directly.
479 */
480 public Iterator iterator()
481 {
482 return objects.iterator();
483 }
484
485 }