Source for gnu.java.beans.decoder.PersistenceParser

   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: }