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: * RootXmlWriteHandler.java
029: * ------------------------
030: * (C) Copyright 2002-2005, by Object Refinery Limited.
031: *
032: * Original Author: Peter Becker;
033: * Contributor(s): -;
034: *
035: * $Id: RootXmlWriteHandler.java,v 1.5 2005/10/18 13:35:06 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 23-Dec-2003 : Added missing Javadocs (DG);
040: *
041: */
042: package org.jfree.xml.writer;
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.io.IOException;
055: import java.util.ArrayList;
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.util.ManualMappingDefinition;
063: import org.jfree.xml.util.MultiplexMappingDefinition;
064: import org.jfree.xml.util.MultiplexMappingEntry;
065: import org.jfree.xml.util.ObjectFactory;
066: import org.jfree.xml.util.SimpleObjectFactory;
067: import org.jfree.xml.writer.coretypes.BasicStrokeWriteHandler;
068: import org.jfree.xml.writer.coretypes.ColorWriteHandler;
069: import org.jfree.xml.writer.coretypes.FontWriteHandler;
070: import org.jfree.xml.writer.coretypes.GenericWriteHandler;
071: import org.jfree.xml.writer.coretypes.GradientPaintWriteHandler;
072: import org.jfree.xml.writer.coretypes.InsetsWriteHandler;
073: import org.jfree.xml.writer.coretypes.ListWriteHandler;
074: import org.jfree.xml.writer.coretypes.Point2DWriteHandler;
075: import org.jfree.xml.writer.coretypes.Rectangle2DWriteHandler;
076: import org.jfree.xml.writer.coretypes.RenderingHintsWriteHandler;
077:
078: /**
079: * A root handler for writing objects to XML format.
080: */
081: public abstract class RootXmlWriteHandler {
082:
083: /** A map containg the manual mappings. */
084: private SimpleObjectFactory classToHandlerMapping;
085:
086: /**
087: * Creates a new RootXmlWrite handler with the default mappings enabled.
088: */
089: public RootXmlWriteHandler() {
090: this .classToHandlerMapping = new SimpleObjectFactory();
091:
092: // set up handling for Paint objects
093: final MultiplexMappingEntry[] paintEntries = new MultiplexMappingEntry[2];
094: paintEntries[0] = new MultiplexMappingEntry("color",
095: Color.class.getName());
096: paintEntries[1] = new MultiplexMappingEntry("gradientPaint",
097: GradientPaint.class.getName());
098: addMultiplexMapping(Paint.class, "type", paintEntries);
099: addManualMapping(GradientPaint.class,
100: GradientPaintWriteHandler.class);
101: addManualMapping(Color.class, ColorWriteHandler.class);
102:
103: // set up handling for Point2D objects
104: final MultiplexMappingEntry[] point2DEntries = new MultiplexMappingEntry[2];
105: point2DEntries[0] = new MultiplexMappingEntry("float",
106: Point2D.Float.class.getName());
107: point2DEntries[1] = new MultiplexMappingEntry("double",
108: Point2D.Double.class.getName());
109: addMultiplexMapping(Point2D.class, "type", point2DEntries);
110: addManualMapping(Point2D.Float.class, Point2DWriteHandler.class);
111: addManualMapping(Point2D.Double.class,
112: Point2DWriteHandler.class);
113:
114: // set up handling for Stroke objects
115: final MultiplexMappingEntry[] strokeEntries = new MultiplexMappingEntry[1];
116: strokeEntries[0] = new MultiplexMappingEntry("basic",
117: BasicStroke.class.getName());
118: addMultiplexMapping(Stroke.class, "type", strokeEntries);
119: addManualMapping(BasicStroke.class,
120: BasicStrokeWriteHandler.class);
121:
122: // set up handling for Rectangle2D objects
123: final MultiplexMappingEntry[] rectangle2DEntries = new MultiplexMappingEntry[2];
124: rectangle2DEntries[0] = new MultiplexMappingEntry("float",
125: Rectangle2D.Float.class.getName());
126: rectangle2DEntries[1] = new MultiplexMappingEntry("double",
127: Rectangle2D.Double.class.getName());
128: addMultiplexMapping(Rectangle2D.class, "type",
129: rectangle2DEntries);
130: addManualMapping(Rectangle2D.Float.class,
131: Rectangle2DWriteHandler.class);
132: addManualMapping(Rectangle2D.Double.class,
133: Rectangle2DWriteHandler.class);
134:
135: // set up handling for List objects
136: final MultiplexMappingEntry[] listEntries = new MultiplexMappingEntry[4];
137: listEntries[0] = new MultiplexMappingEntry("array-list",
138: ArrayList.class.getName());
139: listEntries[1] = new MultiplexMappingEntry("linked-list",
140: LinkedList.class.getName());
141: listEntries[2] = new MultiplexMappingEntry("vector",
142: Vector.class.getName());
143: listEntries[3] = new MultiplexMappingEntry("stack", Stack.class
144: .getName());
145: addMultiplexMapping(List.class, "type", listEntries);
146: addManualMapping(LinkedList.class, ListWriteHandler.class);
147: addManualMapping(Vector.class, ListWriteHandler.class);
148: addManualMapping(ArrayList.class, ListWriteHandler.class);
149: addManualMapping(Stack.class, ListWriteHandler.class);
150:
151: // handle all other direct mapping types
152: addManualMapping(RenderingHints.class,
153: RenderingHintsWriteHandler.class);
154: addManualMapping(Insets.class, InsetsWriteHandler.class);
155: addManualMapping(Font.class, FontWriteHandler.class);
156: }
157:
158: /**
159: * Returns the object factory.
160: *
161: * @return the object factory.
162: */
163: protected abstract ObjectFactory getFactoryLoader();
164:
165: /**
166: * Adds a new manual mapping to this handler.
167: *
168: * This method provides support for the manual mapping. The manual mapping
169: * will become active before the multiplexers were queried. This facility
170: * could be used to override the model definition.
171: *
172: * @param classToWrite the class, which should be handled
173: * @param handler the write handler implementation for that class.
174: */
175: protected void addManualMapping(final Class classToWrite,
176: final Class handler) {
177: if (handler == null) {
178: throw new NullPointerException("handler must not be null.");
179: }
180: if (classToWrite == null) {
181: throw new NullPointerException(
182: "classToWrite must not be null.");
183: }
184: if (!XmlWriteHandler.class.isAssignableFrom(handler)) {
185: throw new IllegalArgumentException(
186: "The given handler is no XmlWriteHandler.");
187: }
188:
189: this .classToHandlerMapping
190: .addManualMapping(new ManualMappingDefinition(
191: classToWrite, null, handler.getName()));
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 entries.
200: */
201: protected void addMultiplexMapping(final Class baseClass,
202: final String typeAttr, final MultiplexMappingEntry[] mdef) {
203:
204: this .classToHandlerMapping
205: .addMultiplexMapping(new MultiplexMappingDefinition(
206: baseClass, typeAttr, mdef));
207:
208: }
209:
210: /**
211: * Tries to find the mapping for the given class. This will first check
212: * the manual mapping and then try to use the object factory to resolve
213: * the class parameter into a write handler.
214: *
215: * @param classToWrite the class for which to find a handler.
216: * @return the write handler, never null.
217: * @throws XMLWriterException if no handler could be found for the given class.
218: */
219: protected XmlWriteHandler getMapping(final Class classToWrite)
220: throws XMLWriterException {
221:
222: if (classToWrite == null) {
223: throw new NullPointerException("ClassToWrite is null.");
224: }
225:
226: // search direct matches, first the direct definitions ...
227: ManualMappingDefinition manualMapping = this .classToHandlerMapping
228: .getManualMappingDefinition(classToWrite);
229: if (manualMapping == null) {
230: // search the manual mappings from the xml file.
231: manualMapping = getFactoryLoader()
232: .getManualMappingDefinition(classToWrite);
233: }
234: if (manualMapping != null) {
235: return loadHandlerClass(manualMapping.getWriteHandler());
236: }
237:
238: // multiplexer definitions can be safely ignored here, as they are used to
239: // map parent classes to more specific child classes. In this case, we already
240: // know the child class and can look up the handler directly.
241:
242: // of course we have to check for multiplexers later, so that we can apply
243: // the mutiplex-attributes.
244:
245: // and finally try the generic handler matches ...
246: if (this .classToHandlerMapping.isGenericHandler(classToWrite)) {
247: return new GenericWriteHandler(this .classToHandlerMapping
248: .getFactoryForClass(classToWrite));
249: }
250: if (getFactoryLoader().isGenericHandler(classToWrite)) {
251: return new GenericWriteHandler(getFactoryLoader()
252: .getFactoryForClass(classToWrite));
253: }
254:
255: throw new XMLWriterException("Unable to handle " + classToWrite);
256: }
257:
258: /**
259: * Writes the given object with the specified tagname. This method will
260: * do nothing, if the given object is null.
261: *
262: * @param tagName the tagname for the xml-element containing the object
263: * definition. The tagname must not be null.
264: * @param object the object which should be written.
265: * @param baseClass the base class.
266: * @param writer the xml writer used to write the content, never null.
267: *
268: * @throws IOException if an IOException occures.
269: * @throws XMLWriterException if an object model related error occures during
270: * the writing.
271: */
272: public void write(final String tagName, final Object object,
273: final Class baseClass, final XMLWriter writer)
274: throws IOException, XMLWriterException {
275: if (object == null) {
276: return;
277: }
278: if (tagName == null) {
279: throw new NullPointerException(
280: "RootXmlWriteHandler.write(..) : tagName is null");
281: }
282: if (writer == null) {
283: throw new NullPointerException(
284: "RootXmlWriteHandler.write(..) : writer is null");
285: }
286: if (!baseClass.isInstance(object)) {
287: throw new ClassCastException("Object is no instance of "
288: + baseClass);
289: }
290: final Class classToWrite = object.getClass();
291: final XmlWriteHandler handler = getMapping(classToWrite);
292: handler.setRootHandler(this );
293:
294: String attributeName = null;
295: String attributeValue = null;
296:
297: // find multiplexer for this class...
298: MultiplexMappingDefinition mplex = getFactoryLoader()
299: .getMultiplexDefinition(baseClass);
300: if (mplex == null) {
301: mplex = this .classToHandlerMapping
302: .getMultiplexDefinition(baseClass);
303: }
304: if (mplex != null) {
305: final MultiplexMappingEntry entry = mplex
306: .getEntryForClass(classToWrite.getName());
307: if (entry != null) {
308: attributeName = mplex.getAttributeName();
309: attributeValue = entry.getAttributeValue();
310: } else {
311: throw new XMLWriterException(
312: "Unable to find child mapping for multiplexer "
313: + baseClass + " to child "
314: + classToWrite);
315: }
316: }
317:
318: handler.write(tagName, object, writer, attributeName,
319: attributeValue);
320: writer.allowLineBreak();
321: }
322:
323: /**
324: * Loads the given class, and ignores all exceptions which may occur
325: * during the loading. If the class was invalid, null is returned instead.
326: *
327: * @param className the name of the class to be loaded.
328: * @return the class or null.
329: *
330: * @throws XMLWriterException if there is a writer exception.
331: */
332: protected XmlWriteHandler loadHandlerClass(final String className)
333: throws XMLWriterException {
334: if (className == null) {
335: throw new XMLWriterException(
336: "LoadHanderClass: Class name not defined");
337: }
338: try {
339: final Class c = ObjectUtilities.getClassLoader(getClass())
340: .loadClass(className);
341: return (XmlWriteHandler) c.newInstance();
342: } catch (Exception e) {
343: // ignore buggy classes for now ..
344: throw new XMLWriterException(
345: "LoadHanderClass: Unable to instantiate "
346: + className, e);
347: }
348: }
349:
350: }
|