Source for org.jfree.xml.parser.RootXmlReadHandler

   1: /* ========================================================================
   2:  * JCommon : a free general purpose class library for the Java(tm) platform
   3:  * ========================================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jcommon/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
  22:  * USA.
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * -----------------------
  28:  * RootXmlReadHandler.java
  29:  * -----------------------
  30:  * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
  31:  *
  32:  * Original Author:  Thomas Morgner;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * $Id: RootXmlReadHandler.java,v 1.9 2008/09/10 09:20:16 mungady Exp $
  36:  *
  37:  * Changes (from 25-Nov-2003)
  38:  * --------------------------
  39:  * 25-Nov-2003 : Added Javadocs (DG);
  40:  * 22-Feb-2005 : Fixed a bug when ending nested tags with the same tagname.
  41:  */
  42: package org.jfree.xml.parser;
  43: 
  44: import java.awt.BasicStroke;
  45: import java.awt.Color;
  46: import java.awt.Font;
  47: import java.awt.GradientPaint;
  48: import java.awt.Insets;
  49: import java.awt.Paint;
  50: import java.awt.RenderingHints;
  51: import java.awt.Stroke;
  52: import java.awt.geom.Point2D;
  53: import java.awt.geom.Rectangle2D;
  54: import java.util.ArrayList;
  55: import java.util.HashMap;
  56: import java.util.LinkedList;
  57: import java.util.List;
  58: import java.util.Stack;
  59: import java.util.Vector;
  60: 
  61: import org.jfree.util.ObjectUtilities;
  62: import org.jfree.xml.FrontendDefaultHandler;
  63: import org.jfree.xml.ParseException;
  64: import org.jfree.xml.ElementDefinitionException;
  65: import org.jfree.xml.parser.coretypes.BasicStrokeReadHandler;
  66: import org.jfree.xml.parser.coretypes.ColorReadHandler;
  67: import org.jfree.xml.parser.coretypes.FontReadHandler;
  68: import org.jfree.xml.parser.coretypes.GenericReadHandler;
  69: import org.jfree.xml.parser.coretypes.GradientPaintReadHandler;
  70: import org.jfree.xml.parser.coretypes.InsetsReadHandler;
  71: import org.jfree.xml.parser.coretypes.ListReadHandler;
  72: import org.jfree.xml.parser.coretypes.Point2DReadHandler;
  73: import org.jfree.xml.parser.coretypes.Rectangle2DReadHandler;
  74: import org.jfree.xml.parser.coretypes.RenderingHintsReadHandler;
  75: import org.jfree.xml.parser.coretypes.StringReadHandler;
  76: import org.jfree.xml.util.ManualMappingDefinition;
  77: import org.jfree.xml.util.MultiplexMappingDefinition;
  78: import org.jfree.xml.util.MultiplexMappingEntry;
  79: import org.jfree.xml.util.ObjectFactory;
  80: import org.jfree.xml.util.SimpleObjectFactory;
  81: import org.xml.sax.Attributes;
  82: import org.xml.sax.SAXException;
  83: 
  84: /**
  85:  * A base root SAX handler.
  86:  */
  87: public abstract class RootXmlReadHandler extends FrontendDefaultHandler {
  88: 
  89:     /** The current handlers. */
  90:     private Stack currentHandlers;
  91: 
  92:     /** ??. */
  93:     private Stack outerScopes;
  94: 
  95:     /** The root handler. */
  96:     private XmlReadHandler rootHandler;
  97: 
  98:     /** The object registry. */
  99:     private HashMap objectRegistry;
 100: 
 101:     /** Maps classes to handlers. */
 102:     private SimpleObjectFactory classToHandlerMapping;
 103: 
 104:     private boolean rootHandlerInitialized;
 105: 
 106:     /**
 107:      * Creates a new root SAX handler.
 108:      */
 109:     public RootXmlReadHandler() {
 110:         this.objectRegistry = new HashMap();
 111:         this.classToHandlerMapping = new SimpleObjectFactory();
 112:     }
 113: 
 114:     /**
 115:      * Adds the default mappings.
 116:      */
 117:     protected void addDefaultMappings () {
 118: 
 119:         final MultiplexMappingEntry[] paintEntries = new MultiplexMappingEntry[2];
 120:         paintEntries[0] = new MultiplexMappingEntry("color", Color.class.getName());
 121:         paintEntries[1] = new MultiplexMappingEntry("gradientPaint", GradientPaint.class.getName());
 122:         addMultiplexMapping(Paint.class, "type", paintEntries);
 123:         addManualMapping(Color.class, ColorReadHandler.class);
 124:         addManualMapping(GradientPaint.class, GradientPaintReadHandler.class);
 125: 
 126:         final MultiplexMappingEntry[] point2DEntries = new MultiplexMappingEntry[2];
 127:         point2DEntries[0] = new MultiplexMappingEntry("float", Point2D.Float.class.getName());
 128:         point2DEntries[1] = new MultiplexMappingEntry("double", Point2D.Double.class.getName());
 129:         addMultiplexMapping(Point2D.class, "type", point2DEntries);
 130:         addManualMapping(Point2D.Float.class, Point2DReadHandler.class);
 131:         addManualMapping(Point2D.Double.class, Point2DReadHandler.class);
 132: 
 133:         final MultiplexMappingEntry[] rectangle2DEntries = new MultiplexMappingEntry[2];
 134:         rectangle2DEntries[0] = new MultiplexMappingEntry(
 135:             "float", Rectangle2D.Float.class.getName()
 136:         );
 137:         rectangle2DEntries[1] = new MultiplexMappingEntry(
 138:             "double", Rectangle2D.Double.class.getName()
 139:         );
 140:         addMultiplexMapping(Rectangle2D.class, "type", rectangle2DEntries);
 141:         addManualMapping(Rectangle2D.Float.class, Rectangle2DReadHandler.class);
 142:         addManualMapping(Rectangle2D.Double.class, Rectangle2DReadHandler.class);
 143: 
 144:         // Handle list types
 145:         final MultiplexMappingEntry[] listEntries = new MultiplexMappingEntry[4];
 146:         listEntries[0] = new MultiplexMappingEntry("array-list", ArrayList.class.getName());
 147:         listEntries[1] = new MultiplexMappingEntry("linked-list", LinkedList.class.getName());
 148:         listEntries[2] = new MultiplexMappingEntry("vector", Vector.class.getName());
 149:         listEntries[3] = new MultiplexMappingEntry("stack", Stack.class.getName());
 150:         addMultiplexMapping(List.class, "type", listEntries);
 151:         addManualMapping(LinkedList.class, ListReadHandler.class);
 152:         addManualMapping(Vector.class, ListReadHandler.class);
 153:         addManualMapping(ArrayList.class, ListReadHandler.class);
 154:         addManualMapping(Stack.class, ListReadHandler.class);
 155: 
 156:         final MultiplexMappingEntry[] strokeEntries = new MultiplexMappingEntry[1];
 157:         strokeEntries[0] = new MultiplexMappingEntry("basic", BasicStroke.class.getName());
 158:         addMultiplexMapping(Stroke.class, "type", strokeEntries);
 159:         addManualMapping(BasicStroke.class, BasicStrokeReadHandler.class);
 160: 
 161:         addManualMapping(Font.class, FontReadHandler.class);
 162:         addManualMapping(Insets.class, InsetsReadHandler.class);
 163:         addManualMapping(RenderingHints.class, RenderingHintsReadHandler.class);
 164:         addManualMapping(String.class, StringReadHandler.class);
 165:     }
 166: 
 167:     /**
 168:      * Returns the object factory.
 169:      *
 170:      * @return The object factory.
 171:      */
 172:     public abstract ObjectFactory getFactoryLoader();
 173: 
 174:     /**
 175:      * Adds a mapping between a class and the handler for the class.
 176:      *
 177:      * @param classToRead  the class.
 178:      * @param handler  the handler class.
 179:      */
 180:     protected void addManualMapping(final Class classToRead, final Class handler) {
 181:         if (handler == null) {
 182:             throw new NullPointerException("handler must not be null.");
 183:         }
 184:         if (classToRead == null) {
 185:             throw new NullPointerException("classToRead must not be null.");
 186:         }
 187:         if (!XmlReadHandler.class.isAssignableFrom(handler)) {
 188:             throw new IllegalArgumentException("The given handler is no XmlReadHandler.");
 189:         }
 190:         this.classToHandlerMapping.addManualMapping
 191:             (new ManualMappingDefinition(classToRead, handler.getName(), null));
 192:     }
 193: 
 194:     /**
 195:      * Adds a multiplex mapping.
 196:      *
 197:      * @param baseClass  the base class.
 198:      * @param typeAttr  the type attribute.
 199:      * @param mdef  the mapping entry.
 200:      */
 201:     protected void addMultiplexMapping(final Class baseClass,
 202:                                        final String typeAttr,
 203:                                        final MultiplexMappingEntry[] mdef) {
 204: 
 205:         this.classToHandlerMapping.addMultiplexMapping(
 206:             new MultiplexMappingDefinition(baseClass, typeAttr, mdef)
 207:         );
 208:     }
 209: 
 210:     /**
 211:      * Adds an object to the registry.
 212:      *
 213:      * @param key  the key.
 214:      * @param value  the object.
 215:      */
 216:     public void setHelperObject(final String key, final Object value) {
 217:         if (value == null) {
 218:             this.objectRegistry.remove(key);
 219:         }
 220:         else {
 221:             this.objectRegistry.put(key, value);
 222:         }
 223:     }
 224: 
 225:     /**
 226:      * Returns an object from the registry.
 227:      *
 228:      * @param key  the key.
 229:      *
 230:      * @return The object.
 231:      */
 232:     public Object getHelperObject(final String key) {
 233:         return this.objectRegistry.get(key);
 234:     }
 235: 
 236:     /**
 237:      * Creates a SAX handler for the specified class.
 238:      *
 239:      * @param classToRead  the class.
 240:      * @param tagName  the tag name.
 241:      * @param atts  the attributes.
 242:      *
 243:      * @return a SAX handler.
 244:      *
 245:      * @throws XmlReaderException if there is a problem with the reader.
 246:      */
 247:     public XmlReadHandler createHandler(final Class classToRead, final String tagName, final Attributes atts)
 248:         throws XmlReaderException {
 249: 
 250:         final XmlReadHandler retval = findHandlerForClass(classToRead, atts, new ArrayList());
 251:         if (retval == null) {
 252:             throw new NullPointerException("Unable to find handler for class: " + classToRead);
 253:         }
 254:         retval.init(this, tagName);
 255:         return retval;
 256:     }
 257: 
 258:     /**
 259:      * Finds a handler for the specified class.
 260:      *
 261:      * @param classToRead  the class to be read.
 262:      * @param atts  the attributes.
 263:      * @param history  the history.
 264:      *
 265:      * @return A handler for the specified class.
 266:      *
 267:      * @throws XmlReaderException if there is a problem with the reader.
 268:      */
 269:     private XmlReadHandler findHandlerForClass(final Class classToRead, final Attributes atts,
 270:                                                final ArrayList history)
 271:         throws XmlReaderException {
 272:         final ObjectFactory genericFactory = getFactoryLoader();
 273: 
 274:         if (history.contains(classToRead)) {
 275:             throw new IllegalStateException("Circular reference detected: " + history);
 276:         }
 277:         history.add(classToRead);
 278:         // check the manual mappings ...
 279:         ManualMappingDefinition manualDefinition =
 280:             this.classToHandlerMapping.getManualMappingDefinition(classToRead);
 281:         if (manualDefinition == null) {
 282:             manualDefinition = genericFactory.getManualMappingDefinition(classToRead);
 283:         }
 284:         if (manualDefinition != null) {
 285:             // Log.debug ("Locating handler for " + manualDefinition.getBaseClass());
 286:             return loadHandlerClass(manualDefinition.getReadHandler());
 287:         }
 288: 
 289:         // check whether a multiplexer is defined ...
 290:         // find multiplexer for this class...
 291:         MultiplexMappingDefinition mplex =
 292:             getFactoryLoader().getMultiplexDefinition(classToRead);
 293:         if (mplex == null) {
 294:             mplex = this.classToHandlerMapping.getMultiplexDefinition(classToRead);
 295:         }
 296:         if (mplex != null) {
 297:             final String attributeValue = atts.getValue(mplex.getAttributeName());
 298:             if (attributeValue == null) {
 299:                 throw new XmlReaderException(
 300:                     "Multiplexer type attribute is not defined: " + mplex.getAttributeName()
 301:                     + " for " + classToRead
 302:                 );
 303:             }
 304:             final MultiplexMappingEntry entry =
 305:                 mplex.getEntryForType(attributeValue);
 306:             if (entry == null) {
 307:                 throw new XmlReaderException(
 308:                     "Invalid type attribute value: " + mplex.getAttributeName() + " = "
 309:                     + attributeValue
 310:                 );
 311:             }
 312:             final Class c = loadClass(entry.getTargetClass());
 313:             if (!c.equals(mplex.getBaseClass())) {
 314:                 return findHandlerForClass(c, atts, history);
 315:             }
 316:         }
 317: 
 318:         // check for generic classes ...
 319:         // and finally try the generic handler matches ...
 320:         if (this.classToHandlerMapping.isGenericHandler(classToRead)) {
 321:             return new GenericReadHandler
 322:                 (this.classToHandlerMapping.getFactoryForClass(classToRead));
 323:         }
 324:         if (getFactoryLoader().isGenericHandler(classToRead)) {
 325:             return new GenericReadHandler
 326:                 (getFactoryLoader().getFactoryForClass(classToRead));
 327:         }
 328:         return null;
 329:     }
 330: 
 331:     /**
 332:      * Sets the root SAX handler.
 333:      *
 334:      * @param handler  the SAX handler.
 335:      */
 336:     protected void setRootHandler(final XmlReadHandler handler) {
 337:         this.rootHandler = handler;
 338:         this.rootHandlerInitialized = false;
 339:     }
 340: 
 341:     /**
 342:      * Returns the root SAX handler.
 343:      *
 344:      * @return the root SAX handler.
 345:      */
 346:     protected XmlReadHandler getRootHandler() {
 347:         return this.rootHandler;
 348:     }
 349: 
 350:     /**
 351:      * Start a new handler stack and delegate to another handler.
 352:      *
 353:      * @param handler  the handler.
 354:      * @param tagName  the tag name.
 355:      * @param attrs  the attributes.
 356:      *
 357:      * @throws XmlReaderException if there is a problem with the reader.
 358:      * @throws SAXException if there is a problem with the parser.
 359:      */
 360:     public void recurse(final XmlReadHandler handler, final String tagName, final Attributes attrs)
 361:         throws XmlReaderException, SAXException {
 362: 
 363:         this.outerScopes.push(this.currentHandlers);
 364:         this.currentHandlers = new Stack();
 365:         this.currentHandlers.push(handler);
 366:         handler.startElement(tagName, attrs);
 367: 
 368:     }
 369: 
 370:     /**
 371:      * Delegate to another handler.
 372:      *
 373:      * @param handler  the new handler.
 374:      * @param tagName  the tag name.
 375:      * @param attrs  the attributes.
 376:      *
 377:      * @throws XmlReaderException if there is a problem with the reader.
 378:      * @throws SAXException if there is a problem with the parser.
 379:      */
 380:     public void delegate(final XmlReadHandler handler, final String tagName, final Attributes attrs)
 381:         throws XmlReaderException, SAXException {
 382:         this.currentHandlers.push(handler);
 383:         handler.init(this, tagName);
 384:         handler.startElement(tagName, attrs);
 385:     }
 386: 
 387:     /**
 388:      * Hand control back to the previous handler.
 389:      *
 390:      * @param tagName  the tagname.
 391:      *
 392:      * @throws SAXException if there is a problem with the parser.
 393:      * @throws XmlReaderException if there is a problem with the reader.
 394:      */
 395:     public void unwind(final String tagName) throws SAXException, XmlReaderException {
 396:       // remove current handler from stack ..
 397:         this.currentHandlers.pop();
 398:         if (this.currentHandlers.isEmpty() && !this.outerScopes.isEmpty()) {
 399:             // if empty, but "recurse" had been called, then restore the old handler stack ..
 400:             // but do not end the recursed element ..
 401:             this.currentHandlers = (Stack) this.outerScopes.pop();
 402:         }
 403:         else if (!this.currentHandlers.isEmpty()) {
 404:             // if there are some handlers open, close them too (these handlers must be delegates)..
 405:             getCurrentHandler().endElement(tagName);
 406:         }
 407:     }
 408: 
 409:     /**
 410:      * Returns the current handler.
 411:      *
 412:      * @return The current handler.
 413:      */
 414:     protected XmlReadHandler getCurrentHandler() {
 415:         return (XmlReadHandler) this.currentHandlers.peek();
 416:     }
 417: 
 418:     /**
 419:      * Starts processing a document.
 420:      *
 421:      * @throws SAXException not in this implementation.
 422:      */
 423:     public void startDocument() throws SAXException {
 424:         this.outerScopes = new Stack();
 425:         this.currentHandlers = new Stack();
 426:         this.currentHandlers.push(this.rootHandler);
 427:     }
 428: 
 429:     /**
 430:      * Starts processing an element.
 431:      *
 432:      * @param uri  the URI.
 433:      * @param localName  the local name.
 434:      * @param qName  the qName.
 435:      * @param attributes  the attributes.
 436:      *
 437:      * @throws SAXException if there is a parsing problem.
 438:      */
 439:     public void startElement(final String uri, final String localName,
 440:                              final String qName, final Attributes attributes)
 441:         throws SAXException {
 442:         if (this.rootHandlerInitialized == false) {
 443:             this.rootHandler.init(this, qName);
 444:             this.rootHandlerInitialized = true;
 445:         }
 446: 
 447:         try {
 448:             getCurrentHandler().startElement(qName, attributes);
 449:         }
 450:         catch (XmlReaderException xre) {
 451:             throw new ParseException(xre, getLocator());
 452:         }
 453:     }
 454: 
 455:     /**
 456:      * Process character data.
 457:      *
 458:      * @param ch  the character buffer.
 459:      * @param start  the start index.
 460:      * @param length  the length of the character data.
 461:      *
 462:      * @throws SAXException if there is a parsing error.
 463:      */
 464:     public void characters(final char[] ch, final int start, final int length) throws SAXException {
 465:         try {
 466:             getCurrentHandler().characters(ch, start, length);
 467:         }
 468:         catch (SAXException se) {
 469:             throw se;
 470:         }
 471:         catch (Exception e) {
 472:             throw new ParseException(e, getLocator());
 473:         }
 474:     }
 475: 
 476:     /**
 477:      * Finish processing an element.
 478:      *
 479:      * @param uri  the URI.
 480:      * @param localName  the local name.
 481:      * @param qName  the qName.
 482:      *
 483:      * @throws SAXException if there is a parsing error.
 484:      */
 485:     public void endElement(final String uri, final String localName, final String qName)
 486:         throws SAXException {
 487:         try {
 488:             getCurrentHandler().endElement(qName);
 489:         }
 490:         catch (XmlReaderException xre) {
 491:             throw new ParseException(xre, getLocator());
 492:         }
 493:     }
 494: 
 495:     /**
 496:      * Loads the given class, and ignores all exceptions which may occur
 497:      * during the loading. If the class was invalid, null is returned instead.
 498:      *
 499:      * @param className the name of the class to be loaded.
 500:      * @return the class or null.
 501:      * @throws XmlReaderException if there is a reader error.
 502:      */
 503:     protected XmlReadHandler loadHandlerClass(final String className)
 504:         throws XmlReaderException {
 505:         try {
 506:             final Class c = loadClass(className);
 507:             return (XmlReadHandler) c.newInstance();
 508:         }
 509:         catch (Exception e) {
 510:             // ignore buggy classes for now ..
 511:             throw new XmlReaderException("LoadHanderClass: Unable to instantiate " + className, e);
 512:         }
 513:     }
 514: 
 515:     /**
 516:      * Loads the given class, and ignores all exceptions which may occur
 517:      * during the loading. If the class was invalid, null is returned instead.
 518:      *
 519:      * @param className the name of the class to be loaded.
 520:      * @return the class or null.
 521:      * @throws XmlReaderException if there is a reader error.
 522:      */
 523:     protected Class loadClass(final String className)
 524:         throws XmlReaderException {
 525:         if (className == null) {
 526:             throw new XmlReaderException("LoadHanderClass: Class name not defined");
 527:         }
 528:         try {
 529:             final Class c = ObjectUtilities.getClassLoader(getClass()).loadClass(className);
 530:             return c;
 531:         }
 532:         catch (Exception e) {
 533:             // ignore buggy classes for now ..
 534:             throw new XmlReaderException("LoadHanderClass: Unable to load " + className, e);
 535:         }
 536:     }
 537: 
 538:     /**
 539:      * Returns ???.
 540:      *
 541:      * @return ???.
 542:      *
 543:      * @throws SAXException ???.
 544:      */
 545:     public Object getResult () throws SAXException
 546:     {
 547:         if (this.rootHandler != null) {
 548:           try
 549:           {
 550:             return this.rootHandler.getObject();
 551:           }
 552:           catch (XmlReaderException e)
 553:           {
 554:             throw new ElementDefinitionException(e);
 555:           }
 556:         }
 557:         return null;
 558:     }
 559: }