001: /* ========================================================================
002: * JCommon : a free general purpose class library for the Java(tm) platform
003: * ========================================================================
004: *
005: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jcommon/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * -----------------------
028: * RootXmlReadHandler.java
029: * -----------------------
030: * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
031: *
032: * Original Author: Thomas Morgner;
033: * Contributor(s): David Gilbert (for Object Refinery Limited);
034: *
035: * $Id: RootXmlReadHandler.java,v 1.8 2005/10/18 13:32:52 mungady Exp $
036: *
037: * Changes (from 25-Nov-2003)
038: * --------------------------
039: * 25-Nov-2003 : Added Javadocs (DG);
040: * 22-Feb-2005 : Fixed a bug when ending nested tags with the same tagname.
041: */
042: package org.jfree.xml.parser;
043:
044: import java.awt.BasicStroke;
045: import java.awt.Color;
046: import java.awt.Font;
047: import java.awt.GradientPaint;
048: import java.awt.Insets;
049: import java.awt.Paint;
050: import java.awt.RenderingHints;
051: import java.awt.Stroke;
052: import java.awt.geom.Point2D;
053: import java.awt.geom.Rectangle2D;
054: import java.util.ArrayList;
055: import java.util.HashMap;
056: import java.util.LinkedList;
057: import java.util.List;
058: import java.util.Stack;
059: import java.util.Vector;
060:
061: import org.jfree.util.ObjectUtilities;
062: import org.jfree.xml.FrontendDefaultHandler;
063: import org.jfree.xml.ParseException;
064: import org.jfree.xml.ElementDefinitionException;
065: import org.jfree.xml.parser.coretypes.BasicStrokeReadHandler;
066: import org.jfree.xml.parser.coretypes.ColorReadHandler;
067: import org.jfree.xml.parser.coretypes.FontReadHandler;
068: import org.jfree.xml.parser.coretypes.GenericReadHandler;
069: import org.jfree.xml.parser.coretypes.GradientPaintReadHandler;
070: import org.jfree.xml.parser.coretypes.InsetsReadHandler;
071: import org.jfree.xml.parser.coretypes.ListReadHandler;
072: import org.jfree.xml.parser.coretypes.Point2DReadHandler;
073: import org.jfree.xml.parser.coretypes.Rectangle2DReadHandler;
074: import org.jfree.xml.parser.coretypes.RenderingHintsReadHandler;
075: import org.jfree.xml.parser.coretypes.StringReadHandler;
076: import org.jfree.xml.util.ManualMappingDefinition;
077: import org.jfree.xml.util.MultiplexMappingDefinition;
078: import org.jfree.xml.util.MultiplexMappingEntry;
079: import org.jfree.xml.util.ObjectFactory;
080: import org.jfree.xml.util.SimpleObjectFactory;
081: import org.xml.sax.Attributes;
082: import org.xml.sax.SAXException;
083:
084: /**
085: * A base root SAX handler.
086: */
087: public abstract class RootXmlReadHandler extends FrontendDefaultHandler {
088:
089: /** The current handlers. */
090: private Stack currentHandlers;
091:
092: /** ??. */
093: private Stack outerScopes;
094:
095: /** The root handler. */
096: private XmlReadHandler rootHandler;
097:
098: /** The object registry. */
099: 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",
118: Color.class.getName());
119: paintEntries[1] = new MultiplexMappingEntry("gradientPaint",
120: GradientPaint.class.getName());
121: addMultiplexMapping(Paint.class, "type", paintEntries);
122: addManualMapping(Color.class, ColorReadHandler.class);
123: addManualMapping(GradientPaint.class,
124: GradientPaintReadHandler.class);
125:
126: final MultiplexMappingEntry[] point2DEntries = new MultiplexMappingEntry[2];
127: point2DEntries[0] = new MultiplexMappingEntry("float",
128: Point2D.Float.class.getName());
129: point2DEntries[1] = new MultiplexMappingEntry("double",
130: Point2D.Double.class.getName());
131: addMultiplexMapping(Point2D.class, "type", point2DEntries);
132: addManualMapping(Point2D.Float.class, Point2DReadHandler.class);
133: addManualMapping(Point2D.Double.class, Point2DReadHandler.class);
134:
135: final MultiplexMappingEntry[] rectangle2DEntries = new MultiplexMappingEntry[2];
136: rectangle2DEntries[0] = new MultiplexMappingEntry("float",
137: Rectangle2D.Float.class.getName());
138: rectangle2DEntries[1] = new MultiplexMappingEntry("double",
139: Rectangle2D.Double.class.getName());
140: addMultiplexMapping(Rectangle2D.class, "type",
141: rectangle2DEntries);
142: addManualMapping(Rectangle2D.Float.class,
143: Rectangle2DReadHandler.class);
144: addManualMapping(Rectangle2D.Double.class,
145: Rectangle2DReadHandler.class);
146:
147: // Handle list types
148: final MultiplexMappingEntry[] listEntries = new MultiplexMappingEntry[4];
149: listEntries[0] = new MultiplexMappingEntry("array-list",
150: ArrayList.class.getName());
151: listEntries[1] = new MultiplexMappingEntry("linked-list",
152: LinkedList.class.getName());
153: listEntries[2] = new MultiplexMappingEntry("vector",
154: Vector.class.getName());
155: listEntries[3] = new MultiplexMappingEntry("stack", Stack.class
156: .getName());
157: addMultiplexMapping(List.class, "type", listEntries);
158: addManualMapping(LinkedList.class, ListReadHandler.class);
159: addManualMapping(Vector.class, ListReadHandler.class);
160: addManualMapping(ArrayList.class, ListReadHandler.class);
161: addManualMapping(Stack.class, ListReadHandler.class);
162:
163: final MultiplexMappingEntry[] strokeEntries = new MultiplexMappingEntry[1];
164: strokeEntries[0] = new MultiplexMappingEntry("basic",
165: BasicStroke.class.getName());
166: addMultiplexMapping(Stroke.class, "type", strokeEntries);
167: addManualMapping(BasicStroke.class,
168: BasicStrokeReadHandler.class);
169:
170: addManualMapping(Font.class, FontReadHandler.class);
171: addManualMapping(Insets.class, InsetsReadHandler.class);
172: addManualMapping(RenderingHints.class,
173: RenderingHintsReadHandler.class);
174: addManualMapping(String.class, StringReadHandler.class);
175: }
176:
177: /**
178: * Returns the object factory.
179: *
180: * @return The object factory.
181: */
182: public abstract ObjectFactory getFactoryLoader();
183:
184: /**
185: * Adds a mapping between a class and the handler for the class.
186: *
187: * @param classToRead the class.
188: * @param handler the handler class.
189: */
190: protected void addManualMapping(final Class classToRead,
191: final Class handler) {
192: if (handler == null) {
193: throw new NullPointerException("handler must not be null.");
194: }
195: if (classToRead == null) {
196: throw new NullPointerException(
197: "classToRead must not be null.");
198: }
199: if (!XmlReadHandler.class.isAssignableFrom(handler)) {
200: throw new IllegalArgumentException(
201: "The given handler is no XmlReadHandler.");
202: }
203: this .classToHandlerMapping
204: .addManualMapping(new ManualMappingDefinition(
205: classToRead, handler.getName(), null));
206: }
207:
208: /**
209: * Adds a multiplex mapping.
210: *
211: * @param baseClass the base class.
212: * @param typeAttr the type attribute.
213: * @param mdef the mapping entry.
214: */
215: protected void addMultiplexMapping(final Class baseClass,
216: final String typeAttr, final MultiplexMappingEntry[] mdef) {
217:
218: this .classToHandlerMapping
219: .addMultiplexMapping(new MultiplexMappingDefinition(
220: baseClass, typeAttr, mdef));
221: }
222:
223: /**
224: * Adds an object to the registry.
225: *
226: * @param key the key.
227: * @param value the object.
228: */
229: public void setHelperObject(final String key, final Object value) {
230: if (value == null) {
231: this .objectRegistry.remove(key);
232: } else {
233: this .objectRegistry.put(key, value);
234: }
235: }
236:
237: /**
238: * Returns an object from the registry.
239: *
240: * @param key the key.
241: *
242: * @return The object.
243: */
244: public Object getHelperObject(final String key) {
245: return this .objectRegistry.get(key);
246: }
247:
248: /**
249: * Creates a SAX handler for the specified class.
250: *
251: * @param classToRead the class.
252: * @param tagName the tag name.
253: * @param atts the attributes.
254: *
255: * @return a SAX handler.
256: *
257: * @throws XmlReaderException if there is a problem with the reader.
258: */
259: public XmlReadHandler createHandler(final Class classToRead,
260: final String tagName, final Attributes atts)
261: throws XmlReaderException {
262:
263: final XmlReadHandler retval = findHandlerForClass(classToRead,
264: atts, new ArrayList());
265: if (retval == null) {
266: throw new NullPointerException(
267: "Unable to find handler for class: " + classToRead);
268: }
269: retval.init(this , tagName);
270: return retval;
271: }
272:
273: /**
274: * Finds a handler for the specified class.
275: *
276: * @param classToRead the class to be read.
277: * @param atts the attributes.
278: * @param history the history.
279: *
280: * @return A handler for the specified class.
281: *
282: * @throws XmlReaderException if there is a problem with the reader.
283: */
284: private XmlReadHandler findHandlerForClass(final Class classToRead,
285: final Attributes atts, final ArrayList history)
286: throws XmlReaderException {
287: final ObjectFactory genericFactory = getFactoryLoader();
288:
289: if (history.contains(classToRead)) {
290: throw new IllegalStateException(
291: "Circular reference detected: " + history);
292: }
293: history.add(classToRead);
294: // check the manual mappings ...
295: ManualMappingDefinition manualDefinition = this .classToHandlerMapping
296: .getManualMappingDefinition(classToRead);
297: if (manualDefinition == null) {
298: manualDefinition = genericFactory
299: .getManualMappingDefinition(classToRead);
300: }
301: if (manualDefinition != null) {
302: // Log.debug ("Locating handler for " + manualDefinition.getBaseClass());
303: return loadHandlerClass(manualDefinition.getReadHandler());
304: }
305:
306: // check whether a multiplexer is defined ...
307: // find multiplexer for this class...
308: MultiplexMappingDefinition mplex = getFactoryLoader()
309: .getMultiplexDefinition(classToRead);
310: if (mplex == null) {
311: mplex = this .classToHandlerMapping
312: .getMultiplexDefinition(classToRead);
313: }
314: if (mplex != null) {
315: final String attributeValue = atts.getValue(mplex
316: .getAttributeName());
317: if (attributeValue == null) {
318: throw new XmlReaderException(
319: "Multiplexer type attribute is not defined: "
320: + mplex.getAttributeName() + " for "
321: + classToRead);
322: }
323: final MultiplexMappingEntry entry = mplex
324: .getEntryForType(attributeValue);
325: if (entry == null) {
326: throw new XmlReaderException(
327: "Invalid type attribute value: "
328: + mplex.getAttributeName() + " = "
329: + attributeValue);
330: }
331: final Class c = loadClass(entry.getTargetClass());
332: if (!c.equals(mplex.getBaseClass())) {
333: return findHandlerForClass(c, atts, history);
334: }
335: }
336:
337: // check for generic classes ...
338: // and finally try the generic handler matches ...
339: if (this .classToHandlerMapping.isGenericHandler(classToRead)) {
340: return new GenericReadHandler(this .classToHandlerMapping
341: .getFactoryForClass(classToRead));
342: }
343: if (getFactoryLoader().isGenericHandler(classToRead)) {
344: return new GenericReadHandler(getFactoryLoader()
345: .getFactoryForClass(classToRead));
346: }
347: return null;
348: }
349:
350: /**
351: * Sets the root SAX handler.
352: *
353: * @param handler the SAX handler.
354: */
355: protected void setRootHandler(final XmlReadHandler handler) {
356: this .rootHandler = handler;
357: this .rootHandlerInitialized = false;
358: }
359:
360: /**
361: * Returns the root SAX handler.
362: *
363: * @return the root SAX handler.
364: */
365: protected XmlReadHandler getRootHandler() {
366: return this .rootHandler;
367: }
368:
369: /**
370: * Start a new handler stack and delegate to another handler.
371: *
372: * @param handler the handler.
373: * @param tagName the tag name.
374: * @param attrs the attributes.
375: *
376: * @throws XmlReaderException if there is a problem with the reader.
377: * @throws SAXException if there is a problem with the parser.
378: */
379: public void recurse(final XmlReadHandler handler,
380: final String tagName, final Attributes attrs)
381: throws XmlReaderException, SAXException {
382:
383: this .outerScopes.push(this .currentHandlers);
384: this .currentHandlers = new Stack();
385: this .currentHandlers.push(handler);
386: handler.startElement(tagName, attrs);
387:
388: }
389:
390: /**
391: * Delegate to another handler.
392: *
393: * @param handler the new handler.
394: * @param tagName the tag name.
395: * @param attrs the attributes.
396: *
397: * @throws XmlReaderException if there is a problem with the reader.
398: * @throws SAXException if there is a problem with the parser.
399: */
400: public void delegate(final XmlReadHandler handler,
401: final String tagName, final Attributes attrs)
402: throws XmlReaderException, SAXException {
403: this .currentHandlers.push(handler);
404: handler.init(this , tagName);
405: handler.startElement(tagName, attrs);
406: }
407:
408: /**
409: * Hand control back to the previous handler.
410: *
411: * @param tagName the tagname.
412: *
413: * @throws SAXException if there is a problem with the parser.
414: * @throws XmlReaderException if there is a problem with the reader.
415: */
416: public void unwind(final String tagName) throws SAXException,
417: XmlReaderException {
418: // remove current handler from stack ..
419: this .currentHandlers.pop();
420: if (this .currentHandlers.isEmpty()
421: && !this .outerScopes.isEmpty()) {
422: // if empty, but "recurse" had been called, then restore the old handler stack ..
423: // but do not end the recursed element ..
424: this .currentHandlers = (Stack) this .outerScopes.pop();
425: } else if (!this .currentHandlers.isEmpty()) {
426: // if there are some handlers open, close them too (these handlers must be delegates)..
427: getCurrentHandler().endElement(tagName);
428: }
429: }
430:
431: /**
432: * Returns the current handler.
433: *
434: * @return The current handler.
435: */
436: protected XmlReadHandler getCurrentHandler() {
437: return (XmlReadHandler) this .currentHandlers.peek();
438: }
439:
440: /**
441: * Starts processing a document.
442: *
443: * @throws SAXException not in this implementation.
444: */
445: public void startDocument() throws SAXException {
446: this .outerScopes = new Stack();
447: this .currentHandlers = new Stack();
448: this .currentHandlers.push(this .rootHandler);
449: }
450:
451: /**
452: * Starts processing an element.
453: *
454: * @param uri the URI.
455: * @param localName the local name.
456: * @param qName the qName.
457: * @param attributes the attributes.
458: *
459: * @throws SAXException if there is a parsing problem.
460: */
461: public void startElement(final String uri, final String localName,
462: final String qName, final Attributes attributes)
463: throws SAXException {
464: if (rootHandlerInitialized == false) {
465: rootHandler.init(this , qName);
466: rootHandlerInitialized = true;
467: }
468:
469: try {
470: getCurrentHandler().startElement(qName, attributes);
471: } catch (XmlReaderException xre) {
472: throw new ParseException(xre, getLocator());
473: }
474: }
475:
476: /**
477: * Process character data.
478: *
479: * @param ch the character buffer.
480: * @param start the start index.
481: * @param length the length of the character data.
482: *
483: * @throws SAXException if there is a parsing error.
484: */
485: public void characters(final char[] ch, final int start,
486: final int length) throws SAXException {
487: try {
488: getCurrentHandler().characters(ch, start, length);
489: } catch (SAXException se) {
490: throw se;
491: } catch (Exception e) {
492: throw new ParseException(e, getLocator());
493: }
494: }
495:
496: /**
497: * Finish processing an element.
498: *
499: * @param uri the URI.
500: * @param localName the local name.
501: * @param qName the qName.
502: *
503: * @throws SAXException if there is a parsing error.
504: */
505: public void endElement(final String uri, final String localName,
506: final String qName) throws SAXException {
507: try {
508: getCurrentHandler().endElement(qName);
509: } catch (XmlReaderException xre) {
510: throw new ParseException(xre, getLocator());
511: }
512: }
513:
514: /**
515: * Loads the given class, and ignores all exceptions which may occur
516: * during the loading. If the class was invalid, null is returned instead.
517: *
518: * @param className the name of the class to be loaded.
519: * @return the class or null.
520: * @throws XmlReaderException if there is a reader error.
521: */
522: protected XmlReadHandler loadHandlerClass(final String className)
523: throws XmlReaderException {
524: try {
525: final Class c = loadClass(className);
526: return (XmlReadHandler) c.newInstance();
527: } catch (Exception e) {
528: // ignore buggy classes for now ..
529: throw new XmlReaderException(
530: "LoadHanderClass: Unable to instantiate "
531: + className, e);
532: }
533: }
534:
535: /**
536: * Loads the given class, and ignores all exceptions which may occur
537: * during the loading. If the class was invalid, null is returned instead.
538: *
539: * @param className the name of the class to be loaded.
540: * @return the class or null.
541: * @throws XmlReaderException if there is a reader error.
542: */
543: protected Class loadClass(final String className)
544: throws XmlReaderException {
545: if (className == null) {
546: throw new XmlReaderException(
547: "LoadHanderClass: Class name not defined");
548: }
549: try {
550: final Class c = ObjectUtilities.getClassLoader(getClass())
551: .loadClass(className);
552: return c;
553: } catch (Exception e) {
554: // ignore buggy classes for now ..
555: throw new XmlReaderException(
556: "LoadHanderClass: Unable to load " + className, e);
557: }
558: }
559:
560: public Object getResult() throws SAXException {
561: if (this .rootHandler != null) {
562: try {
563: return this .rootHandler.getObject();
564: } catch (XmlReaderException e) {
565: throw new ElementDefinitionException(e);
566: }
567: }
568: return null;
569: }
570: }
|