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.8 2005/10/18 13:32:52 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:     protected void addDefaultMappings () {
 115: 
 116:         final MultiplexMappingEntry[] paintEntries = new MultiplexMappingEntry[2];
 117:         paintEntries[0] = new MultiplexMappingEntry("color", Color.class.getName());
 118:         paintEntries[1] = new MultiplexMappingEntry("gradientPaint", GradientPaint.class.getName());
 119:         addMultiplexMapping(Paint.class, "type", paintEntries);
 120:         addManualMapping(Color.class, ColorReadHandler.class);
 121:         addManualMapping(GradientPaint.class, GradientPaintReadHandler.class);
 122: 
 123:         final MultiplexMappingEntry[] point2DEntries = new MultiplexMappingEntry[2];
 124:         point2DEntries[0] = new MultiplexMappingEntry("float", Point2D.Float.class.getName());
 125:         point2DEntries[1] = new MultiplexMappingEntry("double", Point2D.Double.class.getName());
 126:         addMultiplexMapping(Point2D.class, "type", point2DEntries);
 127:         addManualMapping(Point2D.Float.class, Point2DReadHandler.class);
 128:         addManualMapping(Point2D.Double.class, Point2DReadHandler.class);
 129: 
 130:         final MultiplexMappingEntry[] rectangle2DEntries = new MultiplexMappingEntry[2];
 131:         rectangle2DEntries[0] = new MultiplexMappingEntry(
 132:             "float", Rectangle2D.Float.class.getName()
 133:         );
 134:         rectangle2DEntries[1] = new MultiplexMappingEntry(
 135:             "double", Rectangle2D.Double.class.getName()
 136:         );
 137:         addMultiplexMapping(Rectangle2D.class, "type", rectangle2DEntries);
 138:         addManualMapping(Rectangle2D.Float.class, Rectangle2DReadHandler.class);
 139:         addManualMapping(Rectangle2D.Double.class, Rectangle2DReadHandler.class);
 140: 
 141:         // Handle list types
 142:         final MultiplexMappingEntry[] listEntries = new MultiplexMappingEntry[4];
 143:         listEntries[0] = new MultiplexMappingEntry("array-list", ArrayList.class.getName());
 144:         listEntries[1] = new MultiplexMappingEntry("linked-list", LinkedList.class.getName());
 145:         listEntries[2] = new MultiplexMappingEntry("vector", Vector.class.getName());
 146:         listEntries[3] = new MultiplexMappingEntry("stack", Stack.class.getName());
 147:         addMultiplexMapping(List.class, "type", listEntries);
 148:         addManualMapping(LinkedList.class, ListReadHandler.class);
 149:         addManualMapping(Vector.class, ListReadHandler.class);
 150:         addManualMapping(ArrayList.class, ListReadHandler.class);
 151:         addManualMapping(Stack.class, ListReadHandler.class);
 152: 
 153:         final MultiplexMappingEntry[] strokeEntries = new MultiplexMappingEntry[1];
 154:         strokeEntries[0] = new MultiplexMappingEntry("basic", BasicStroke.class.getName());
 155:         addMultiplexMapping(Stroke.class, "type", strokeEntries);
 156:         addManualMapping(BasicStroke.class, BasicStrokeReadHandler.class);
 157: 
 158:         addManualMapping(Font.class, FontReadHandler.class);
 159:         addManualMapping(Insets.class, InsetsReadHandler.class);
 160:         addManualMapping(RenderingHints.class, RenderingHintsReadHandler.class);
 161:         addManualMapping(String.class, StringReadHandler.class);
 162:     }
 163: 
 164:     /** 
 165:      * Returns the object factory. 
 166:      * 
 167:      * @return The object factory.
 168:      */
 169:     public abstract ObjectFactory getFactoryLoader();
 170: 
 171:     /**
 172:      * Adds a mapping between a class and the handler for the class.
 173:      *
 174:      * @param classToRead  the class.
 175:      * @param handler  the handler class.
 176:      */
 177:     protected void addManualMapping(final Class classToRead, final Class handler) {
 178:         if (handler == null) {
 179:             throw new NullPointerException("handler must not be null.");
 180:         }
 181:         if (classToRead == null) {
 182:             throw new NullPointerException("classToRead must not be null.");
 183:         }
 184:         if (!XmlReadHandler.class.isAssignableFrom(handler)) {
 185:             throw new IllegalArgumentException("The given handler is no XmlReadHandler.");
 186:         }
 187:         this.classToHandlerMapping.addManualMapping
 188:             (new ManualMappingDefinition(classToRead, handler.getName(), null));
 189:     }
 190: 
 191:     /**
 192:      * Adds a multiplex mapping.
 193:      * 
 194:      * @param baseClass  the base class.
 195:      * @param typeAttr  the type attribute.
 196:      * @param mdef  the mapping entry.
 197:      */
 198:     protected void addMultiplexMapping(final Class baseClass,
 199:                                        final String typeAttr,
 200:                                        final MultiplexMappingEntry[] mdef) {
 201:         
 202:         this.classToHandlerMapping.addMultiplexMapping(
 203:             new MultiplexMappingDefinition(baseClass, typeAttr, mdef)
 204:         );
 205:     }
 206: 
 207:     /**
 208:      * Adds an object to the registry.
 209:      * 
 210:      * @param key  the key.
 211:      * @param value  the object.
 212:      */
 213:     public void setHelperObject(final String key, final Object value) {
 214:         if (value == null) {
 215:             this.objectRegistry.remove(key);
 216:         }
 217:         else {
 218:             this.objectRegistry.put(key, value);
 219:         }
 220:     }
 221: 
 222:     /**
 223:      * Returns an object from the registry.
 224:      * 
 225:      * @param key  the key.
 226:      * 
 227:      * @return The object.
 228:      */
 229:     public Object getHelperObject(final String key) {
 230:         return this.objectRegistry.get(key);
 231:     }
 232: 
 233:     /**
 234:      * Creates a SAX handler for the specified class.
 235:      *
 236:      * @param classToRead  the class.
 237:      * @param tagName  the tag name.
 238:      * @param atts  the attributes.
 239:      *
 240:      * @return a SAX handler.
 241:      *
 242:      * @throws XmlReaderException if there is a problem with the reader.
 243:      */
 244:     public XmlReadHandler createHandler(final Class classToRead, final String tagName, final Attributes atts)
 245:         throws XmlReaderException {
 246: 
 247:         final XmlReadHandler retval = findHandlerForClass(classToRead, atts, new ArrayList());
 248:         if (retval == null) {
 249:             throw new NullPointerException("Unable to find handler for class: " + classToRead);
 250:         }
 251:         retval.init(this, tagName);
 252:         return retval;
 253:     }
 254: 
 255:     /**
 256:      * Finds a handler for the specified class.
 257:      * 
 258:      * @param classToRead  the class to be read.
 259:      * @param atts  the attributes.
 260:      * @param history  the history.
 261:      * 
 262:      * @return A handler for the specified class.
 263:      * 
 264:      * @throws XmlReaderException if there is a problem with the reader.
 265:      */
 266:     private XmlReadHandler findHandlerForClass(final Class classToRead, final Attributes atts,
 267:                                                final ArrayList history)
 268:         throws XmlReaderException {
 269:         final ObjectFactory genericFactory = getFactoryLoader();
 270: 
 271:         if (history.contains(classToRead)) {
 272:             throw new IllegalStateException("Circular reference detected: " + history);
 273:         }
 274:         history.add(classToRead);
 275:         // check the manual mappings ...
 276:         ManualMappingDefinition manualDefinition =
 277:             this.classToHandlerMapping.getManualMappingDefinition(classToRead);
 278:         if (manualDefinition == null) {
 279:             manualDefinition = genericFactory.getManualMappingDefinition(classToRead);
 280:         }
 281:         if (manualDefinition != null) {
 282:             // Log.debug ("Locating handler for " + manualDefinition.getBaseClass());
 283:             return loadHandlerClass(manualDefinition.getReadHandler());
 284:         }
 285: 
 286:         // check whether a multiplexer is defined ...
 287:         // find multiplexer for this class...
 288:         MultiplexMappingDefinition mplex =
 289:             getFactoryLoader().getMultiplexDefinition(classToRead);
 290:         if (mplex == null) {
 291:             mplex = this.classToHandlerMapping.getMultiplexDefinition(classToRead);
 292:         }
 293:         if (mplex != null) {
 294:             final String attributeValue = atts.getValue(mplex.getAttributeName());
 295:             if (attributeValue == null) {
 296:                 throw new XmlReaderException(
 297:                     "Multiplexer type attribute is not defined: " + mplex.getAttributeName() 
 298:                     + " for " + classToRead
 299:                 );
 300:             }
 301:             final MultiplexMappingEntry entry =
 302:                 mplex.getEntryForType(attributeValue);
 303:             if (entry == null) {
 304:                 throw new XmlReaderException(
 305:                     "Invalid type attribute value: " + mplex.getAttributeName() + " = " 
 306:                     + attributeValue
 307:                 );
 308:             }
 309:             final Class c = loadClass(entry.getTargetClass());
 310:             if (!c.equals(mplex.getBaseClass())) {
 311:                 return findHandlerForClass(c, atts, history);
 312:             }
 313:         }
 314: 
 315:         // check for generic classes ...
 316:         // and finally try the generic handler matches ...
 317:         if (this.classToHandlerMapping.isGenericHandler(classToRead)) {
 318:             return new GenericReadHandler
 319:                 (this.classToHandlerMapping.getFactoryForClass(classToRead));
 320:         }
 321:         if (getFactoryLoader().isGenericHandler(classToRead)) {
 322:             return new GenericReadHandler
 323:                 (getFactoryLoader().getFactoryForClass(classToRead));
 324:         }
 325:         return null;
 326:     }
 327: 
 328:     /**
 329:      * Sets the root SAX handler.
 330:      *
 331:      * @param handler  the SAX handler.
 332:      */
 333:     protected void setRootHandler(final XmlReadHandler handler) {
 334:         this.rootHandler = handler;
 335:         this.rootHandlerInitialized = false;
 336:     }
 337: 
 338:     /**
 339:      * Returns the root SAX handler.
 340:      *
 341:      * @return the root SAX handler.
 342:      */
 343:     protected XmlReadHandler getRootHandler() {
 344:         return this.rootHandler;
 345:     }
 346: 
 347:     /**
 348:      * Start a new handler stack and delegate to another handler.
 349:      *
 350:      * @param handler  the handler.
 351:      * @param tagName  the tag name.
 352:      * @param attrs  the attributes.
 353:      * 
 354:      * @throws XmlReaderException if there is a problem with the reader.
 355:      * @throws SAXException if there is a problem with the parser.
 356:      */
 357:     public void recurse(final XmlReadHandler handler, final String tagName, final Attributes attrs)
 358:         throws XmlReaderException, SAXException {
 359:         
 360:         this.outerScopes.push(this.currentHandlers);
 361:         this.currentHandlers = new Stack();
 362:         this.currentHandlers.push(handler);
 363:         handler.startElement(tagName, attrs);
 364:     
 365:     }
 366: 
 367:     /**
 368:      * Delegate to another handler.
 369:      * 
 370:      * @param handler  the new handler.
 371:      * @param tagName  the tag name.
 372:      * @param attrs  the attributes.
 373:      * 
 374:      * @throws XmlReaderException if there is a problem with the reader.
 375:      * @throws SAXException if there is a problem with the parser.
 376:      */
 377:     public void delegate(final XmlReadHandler handler, final String tagName, final Attributes attrs)
 378:         throws XmlReaderException, SAXException {
 379:         this.currentHandlers.push(handler);
 380:         handler.init(this, tagName);
 381:         handler.startElement(tagName, attrs);
 382:     }
 383: 
 384:     /**
 385:      * Hand control back to the previous handler.
 386:      * 
 387:      * @param tagName  the tagname.
 388:      * 
 389:      * @throws SAXException if there is a problem with the parser.
 390:      * @throws XmlReaderException if there is a problem with the reader.
 391:      */
 392:     public void unwind(final String tagName) throws SAXException, XmlReaderException {
 393:       // remove current handler from stack ..
 394:         this.currentHandlers.pop();
 395:         if (this.currentHandlers.isEmpty() && !this.outerScopes.isEmpty()) {
 396:             // if empty, but "recurse" had been called, then restore the old handler stack ..
 397:             // but do not end the recursed element ..
 398:             this.currentHandlers = (Stack) this.outerScopes.pop();
 399:         }
 400:         else if (!this.currentHandlers.isEmpty()) {
 401:             // if there are some handlers open, close them too (these handlers must be delegates)..
 402:             getCurrentHandler().endElement(tagName);
 403:         }
 404:     }
 405: 
 406:     /**
 407:      * Returns the current handler.
 408:      * 
 409:      * @return The current handler.
 410:      */
 411:     protected XmlReadHandler getCurrentHandler() {
 412:         return (XmlReadHandler) this.currentHandlers.peek();
 413:     }
 414: 
 415:     /**
 416:      * Starts processing a document.
 417:      * 
 418:      * @throws SAXException not in this implementation.
 419:      */
 420:     public void startDocument() throws SAXException {
 421:         this.outerScopes = new Stack();
 422:         this.currentHandlers = new Stack();
 423:         this.currentHandlers.push(this.rootHandler);
 424:     }
 425: 
 426:     /**
 427:      * Starts processing an element.
 428:      * 
 429:      * @param uri  the URI.
 430:      * @param localName  the local name.
 431:      * @param qName  the qName.
 432:      * @param attributes  the attributes.
 433:      * 
 434:      * @throws SAXException if there is a parsing problem.
 435:      */
 436:     public void startElement(final String uri, final String localName,
 437:                              final String qName, final Attributes attributes)
 438:         throws SAXException {
 439:         if (rootHandlerInitialized == false) {
 440:             rootHandler.init(this, qName);
 441:             rootHandlerInitialized = true;
 442:         }
 443: 
 444:         try {
 445:             getCurrentHandler().startElement(qName, attributes);
 446:         }
 447:         catch (XmlReaderException xre) {
 448:             throw new ParseException(xre, getLocator());
 449:         }
 450:     }
 451: 
 452:     /**
 453:      * Process character data.
 454:      * 
 455:      * @param ch  the character buffer.
 456:      * @param start  the start index.
 457:      * @param length  the length of the character data.
 458:      * 
 459:      * @throws SAXException if there is a parsing error.
 460:      */
 461:     public void characters(final char[] ch, final int start, final int length) throws SAXException {
 462:         try {
 463:             getCurrentHandler().characters(ch, start, length);
 464:         }
 465:         catch (SAXException se) {
 466:             throw se;
 467:         }
 468:         catch (Exception e) {
 469:             throw new ParseException(e, getLocator());
 470:         }
 471:     }
 472: 
 473:     /**
 474:      * Finish processing an element.
 475:      * 
 476:      * @param uri  the URI.
 477:      * @param localName  the local name.
 478:      * @param qName  the qName.
 479:      * 
 480:      * @throws SAXException if there is a parsing error.
 481:      */
 482:     public void endElement(final String uri, final String localName, final String qName)
 483:         throws SAXException {
 484:         try {
 485:             getCurrentHandler().endElement(qName);
 486:         }
 487:         catch (XmlReaderException xre) {
 488:             throw new ParseException(xre, getLocator());
 489:         }
 490:     }
 491: 
 492:     /**
 493:      * Loads the given class, and ignores all exceptions which may occur
 494:      * during the loading. If the class was invalid, null is returned instead.
 495:      *
 496:      * @param className the name of the class to be loaded.
 497:      * @return the class or null.
 498:      * @throws XmlReaderException if there is a reader error.
 499:      */
 500:     protected XmlReadHandler loadHandlerClass(final String className)
 501:         throws XmlReaderException {
 502:         try {
 503:             final Class c = loadClass(className);
 504:             return (XmlReadHandler) c.newInstance();
 505:         }
 506:         catch (Exception e) {
 507:             // ignore buggy classes for now ..
 508:             throw new XmlReaderException("LoadHanderClass: Unable to instantiate " + className, e);
 509:         }
 510:     }
 511: 
 512:     /**
 513:      * Loads the given class, and ignores all exceptions which may occur
 514:      * during the loading. If the class was invalid, null is returned instead.
 515:      *
 516:      * @param className the name of the class to be loaded.
 517:      * @return the class or null.
 518:      * @throws XmlReaderException if there is a reader error.
 519:      */
 520:     protected Class loadClass(final String className)
 521:         throws XmlReaderException {
 522:         if (className == null) {
 523:             throw new XmlReaderException("LoadHanderClass: Class name not defined");
 524:         }
 525:         try {
 526:             final Class c = ObjectUtilities.getClassLoader(getClass()).loadClass(className);
 527:             return c;
 528:         }
 529:         catch (Exception e) {
 530:             // ignore buggy classes for now ..
 531:             throw new XmlReaderException("LoadHanderClass: Unable to load " + className, e);
 532:         }
 533:     }
 534: 
 535:     public Object getResult () throws SAXException
 536:     {
 537:         if (this.rootHandler != null) {
 538:           try
 539:           {
 540:             return this.rootHandler.getObject();
 541:           }
 542:           catch (XmlReaderException e)
 543:           {
 544:             throw new ElementDefinitionException(e);
 545:           }
 546:         }
 547:         return null;
 548:     }
 549: }