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