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          */
PersistenceParser( InputStream inputStream, ExceptionListener exceptionListener, ClassLoader cl, XMLDecoder decoder)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 
startElement( String uri, String localName, String qName, Attributes attributes)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 
endElement(String uri, String localName, String qName)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          */
characters(char[] ch, int start, int length)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                  */
createHandler(ElementHandler parent)286                 AbstractElementHandler createHandler(ElementHandler parent);
287         }
288 
289         class BooleanHandlerCreator implements Creator
290         {
createHandler(ElementHandler parent)291                 public AbstractElementHandler createHandler(ElementHandler parent)
292                 {
293                         return new BooleanHandler(parent);
294                 }
295         }
296 
297         class ByteHandlerCreator implements Creator
298         {
createHandler(ElementHandler parent)299                 public AbstractElementHandler createHandler(ElementHandler parent)
300                 {
301                         return new ByteHandler(parent);
302                 }
303         }
304 
305         class ShortHandlerCreator implements Creator
306         {
createHandler(ElementHandler parent)307                 public AbstractElementHandler createHandler(ElementHandler parent)
308                 {
309                         return new ShortHandler(parent);
310                 }
311         }
312 
313         class IntHandlerCreator implements Creator
314         {
createHandler(ElementHandler parent)315                 public AbstractElementHandler createHandler(ElementHandler parent)
316                 {
317                         return new IntHandler(parent);
318                 }
319         }
320 
321         class LongHandlerCreator implements Creator
322         {
createHandler(ElementHandler parent)323                 public AbstractElementHandler createHandler(ElementHandler parent)
324                 {
325                         return new LongHandler(parent);
326                 }
327         }
328 
329         class FloatHandlerCreator implements Creator
330         {
createHandler(ElementHandler parent)331                 public AbstractElementHandler createHandler(ElementHandler parent)
332                 {
333                         return new FloatHandler(parent);
334                 }
335         }
336 
337         class DoubleHandlerCreator implements Creator
338         {
createHandler(ElementHandler parent)339                 public AbstractElementHandler createHandler(ElementHandler parent)
340                 {
341                         return new DoubleHandler(parent);
342                 }
343         }
344 
345         class CharHandlerCreator implements Creator
346         {
createHandler(ElementHandler parent)347                 public AbstractElementHandler createHandler(ElementHandler parent)
348                 {
349                         return new CharHandler(parent);
350                 }
351         }
352 
353         class StringHandlerCreator implements Creator
354         {
createHandler(ElementHandler parent)355                 public AbstractElementHandler createHandler(ElementHandler parent)
356                 {
357                         return new StringHandler(parent);
358                 }
359         }
360 
361         class JavaHandlerCreator implements Creator
362         {
createHandler(ElementHandler parent)363                 public AbstractElementHandler createHandler(ElementHandler parent)
364                 {
365                         return javaHandler;
366                 }
367         }
368 
369         class ObjectHandlerCreator implements Creator
370         {
createHandler(ElementHandler parent)371                 public AbstractElementHandler createHandler(ElementHandler parent)
372                 {
373                         return new ObjectHandler(parent);
374                 }
375         }
376 
377         class VoidHandlerCreator implements Creator
378         {
createHandler(ElementHandler parent)379                 public AbstractElementHandler createHandler(ElementHandler parent)
380                 {
381                         return new VoidHandler(parent);
382                 }
383         }
384 
385         class ClassHandlerCreator implements Creator
386         {
createHandler(ElementHandler parent)387                 public AbstractElementHandler createHandler(ElementHandler parent)
388                 {
389                         return new ClassHandler(parent);
390                 }
391         }
392 
393         class NullHandlerCreator implements Creator
394         {
createHandler(ElementHandler parent)395                 public AbstractElementHandler createHandler(ElementHandler parent)
396                 {
397                         return new NullHandler(parent);
398                 }
399         }
400 
401         class ArrayHandlerCreator implements Creator
402         {
createHandler(ElementHandler parent)403                 public AbstractElementHandler createHandler(ElementHandler parent)
404                 {
405                         return new ArrayHandler(parent);
406                 }
407         }
408 
409         /** Adds a decoded object to the Context. */
addParameterObject(Object o)410         public void addParameterObject(Object o) throws AssemblyException
411         {
412                 objects.add(o);
413         }
414 
notifyStatement(Context outerContext)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 
endContext(Context outerContext)421         public Object endContext(Context outerContext) throws AssemblyException
422         {
423                 return null;
424         }
425 
subContextFailed()426         public boolean subContextFailed()
427         {
428                 // failing of subcontexts is no problem for the mother of all contexts
429                 return false;
430         }
431 
set(int index, Object o)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 
get(int index)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 
getResult()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 
setId(String id)453         public void setId(String id)
454         {
455                 exceptionListener.exceptionThrown(new IllegalArgumentException("id attribute is not allowed for <java> tag."));
456         }
457 
getId()458         public String getId()
459         {
460                 // appears to have no id
461                 return null;
462         }
463 
isStatement()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 
setStatement(boolean b)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          */
iterator()480         public Iterator iterator()
481         {
482                 return objects.iterator();
483         }
484 
485 }
486