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: * ObjectFactoryLoader.java
029: * ------------------------
030: * (C) Copyright 2002-2005, by Thomas Morgner and Contributors.
031: *
032: * Original Author: Thomas Morgner;
033: * Contributor(s): -;
034: *
035: * $Id: ObjectFactoryLoader.java,v 1.4 2005/10/18 13:33:53 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 24-Sep-2003: Initial version
040: *
041: */
042:
043: package org.jfree.xml.util;
044:
045: import java.net.URL;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.HashMap;
049: import java.util.Iterator;
050:
051: import org.jfree.util.Log;
052: import org.jfree.xml.attributehandlers.AttributeHandler;
053:
054: /**
055: * The object factory loader loads the xml specification for the generic
056: * handlers. The specification may be distributed over multiple files.
057: * <p>
058: * This class provides the model management for the reader and writer.
059: * The instantiation of the handlers is done elsewhere.
060: *
061: * @author TM
062: */
063: public class ObjectFactoryLoader extends AbstractModelReader implements
064: ObjectFactory {
065:
066: /** Maps classes to GenericObjectFactory instances. */
067: private HashMap objectMappings;
068:
069: /** Manual mappings. */
070: private HashMap manualMappings;
071:
072: /** Multiplex mappings. */
073: private HashMap multiplexMappings;
074:
075: /** The target class. */
076: private Class target;
077:
078: /** The register name. */
079: private String registerName;
080:
081: /** The property definition. */
082: private ArrayList propertyDefinition;
083:
084: /** The attribute definition. */
085: private ArrayList attributeDefinition;
086:
087: /** The constructor definition. */
088: private ArrayList constructorDefinition;
089:
090: /** The lookup definitions. */
091: private ArrayList lookupDefinitions;
092:
093: /** The ordered names. */
094: private ArrayList orderedNames;
095:
096: /** The base class. */
097: private String baseClass;
098:
099: /** The attribute name. */
100: private String attributeName;
101:
102: /** The multiplex entries. */
103: private ArrayList multiplexEntries;
104:
105: /**
106: * Creates a new object factory loader for the given base file.
107: *
108: * @param resourceName the URL of the initial specification file.
109: *
110: * @throws ObjectDescriptionException if the file could not be parsed.
111: */
112: public ObjectFactoryLoader(final URL resourceName)
113: throws ObjectDescriptionException {
114: this .objectMappings = new HashMap();
115: this .manualMappings = new HashMap();
116: this .multiplexMappings = new HashMap();
117: parseXml(resourceName);
118: rebuildSuperClasses();
119: }
120:
121: private void rebuildSuperClasses()
122: throws ObjectDescriptionException {
123: this .propertyDefinition = new ArrayList();
124: this .attributeDefinition = new ArrayList();
125: this .constructorDefinition = new ArrayList();
126: this .lookupDefinitions = new ArrayList();
127: this .orderedNames = new ArrayList();
128:
129: final HashMap newObjectDescriptions = new HashMap();
130: final Iterator it = this .objectMappings.keySet().iterator();
131: while (it.hasNext()) {
132: final Object key = it.next();
133: final GenericObjectFactory gef = (GenericObjectFactory) this .objectMappings
134: .get(key);
135: performSuperClassUpdate(gef);
136:
137: final PropertyDefinition[] propertyDefs = (PropertyDefinition[]) this .propertyDefinition
138: .toArray(new PropertyDefinition[0]);
139: final LookupDefinition[] lookupDefs = (LookupDefinition[]) this .lookupDefinitions
140: .toArray(new LookupDefinition[0]);
141: final AttributeDefinition[] attribDefs = (AttributeDefinition[]) this .attributeDefinition
142: .toArray(new AttributeDefinition[0]);
143: final ConstructorDefinition[] constructorDefs = (ConstructorDefinition[]) this .constructorDefinition
144: .toArray(new ConstructorDefinition[0]);
145: final String[] orderedNamesDefs = (String[]) this .orderedNames
146: .toArray(new String[0]);
147:
148: final GenericObjectFactory objectFactory = new GenericObjectFactory(
149: gef.getBaseClass(), gef.getRegisterName(),
150: constructorDefs, propertyDefs, lookupDefs,
151: attribDefs, orderedNamesDefs);
152: newObjectDescriptions.put(key, objectFactory);
153:
154: this .propertyDefinition.clear();
155: this .attributeDefinition.clear();
156: this .constructorDefinition.clear();
157: this .lookupDefinitions.clear();
158: this .orderedNames.clear();
159: }
160:
161: this .objectMappings.clear();
162: this .objectMappings = newObjectDescriptions;
163:
164: this .propertyDefinition = null;
165: this .attributeDefinition = null;
166: this .constructorDefinition = null;
167: this .lookupDefinitions = null;
168: this .orderedNames = null;
169: }
170:
171: private void performSuperClassUpdate(final GenericObjectFactory gef) {
172: // first handle the super classes, ...
173: final Class super Class = gef.getBaseClass().getSuperclass();
174: if (super Class != null && !super Class.equals(Object.class)) {
175: final GenericObjectFactory super Gef = (GenericObjectFactory) this .objectMappings
176: .get(super Class);
177: if (super Gef != null) {
178: performSuperClassUpdate(super Gef);
179: }
180: }
181:
182: // and finally append all local properties ...
183: this .propertyDefinition.addAll(Arrays.asList(gef
184: .getPropertyDefinitions()));
185: this .attributeDefinition.addAll(Arrays.asList(gef
186: .getAttributeDefinitions()));
187: this .constructorDefinition.addAll(Arrays.asList(gef
188: .getConstructorDefinitions()));
189: this .lookupDefinitions.addAll(Arrays.asList(gef
190: .getLookupDefinitions()));
191: this .orderedNames.addAll(Arrays.asList(gef
192: .getOrderedPropertyNames()));
193: }
194:
195: /**
196: * Starts a object definition. The object definition collects all properties of
197: * an bean-class and defines, which constructor should be used when creating the
198: * class.
199: *
200: * @param className the class name of the defined object
201: * @param register the (optional) register name, to lookup and reference the object later.
202: * @param ignore ignore?
203: *
204: * @return true, if the definition was accepted, false otherwise.
205: * @throws ObjectDescriptionException if an unexpected error occured.
206: */
207: protected boolean startObjectDefinition(final String className,
208: final String register, final boolean ignore)
209: throws ObjectDescriptionException {
210:
211: if (ignore) {
212: return false;
213: }
214: this .target = loadClass(className);
215: if (this .target == null) {
216: Log.warn(new Log.SimpleMessage("Failed to load class ",
217: className));
218: return false;
219: }
220: this .registerName = register;
221: this .propertyDefinition = new ArrayList();
222: this .attributeDefinition = new ArrayList();
223: this .constructorDefinition = new ArrayList();
224: this .lookupDefinitions = new ArrayList();
225: this .orderedNames = new ArrayList();
226: return true;
227: }
228:
229: /**
230: * Handles an attribute definition. This method gets called after the object definition
231: * was started. The method will be called for every defined attribute property.
232: *
233: * @param name the name of the property
234: * @param attribName the xml-attribute name to use later.
235: * @param handlerClass the attribute handler class.
236: * @throws ObjectDescriptionException if an error occured.
237: */
238: protected void handleAttributeDefinition(final String name,
239: final String attribName, final String handlerClass)
240: throws ObjectDescriptionException {
241: final AttributeHandler handler = loadAttributeHandler(handlerClass);
242: this .orderedNames.add(name);
243: this .attributeDefinition.add(new AttributeDefinition(name,
244: attribName, handler));
245: }
246:
247: /**
248: * Handles an element definition. This method gets called after the object definition
249: * was started. The method will be called for every defined element property. Element
250: * properties are used to describe complex objects.
251: *
252: * @param name the name of the property
253: * @param element the xml-tag name for the child element.
254: * @throws ObjectDescriptionException if an error occurs.
255: */
256: protected void handleElementDefinition(final String name,
257: final String element) throws ObjectDescriptionException {
258: this .orderedNames.add(name);
259: this .propertyDefinition.add(new PropertyDefinition(name,
260: element));
261: }
262:
263: /**
264: * Handles an lookup definition. This method gets called after the object definition
265: * was started. The method will be called for every defined lookup property. Lookup properties
266: * reference previously created object using the object's registry name.
267: *
268: * @param name the property name of the base object
269: * @param lookupKey the register key of the referenced object
270: * @throws ObjectDescriptionException if an error occured.
271: */
272: protected void handleLookupDefinition(final String name,
273: final String lookupKey) throws ObjectDescriptionException {
274: final LookupDefinition ldef = new LookupDefinition(name,
275: lookupKey);
276: this .orderedNames.add(name);
277: this .lookupDefinitions.add(ldef);
278: }
279:
280: /**
281: * Finializes the object definition.
282: *
283: * @throws ObjectDescriptionException if an error occures.
284: */
285: protected void endObjectDefinition()
286: throws ObjectDescriptionException {
287:
288: final PropertyDefinition[] propertyDefs = (PropertyDefinition[]) this .propertyDefinition
289: .toArray(new PropertyDefinition[0]);
290: final LookupDefinition[] lookupDefs = (LookupDefinition[]) this .lookupDefinitions
291: .toArray(new LookupDefinition[0]);
292: final AttributeDefinition[] attribDefs = (AttributeDefinition[]) this .attributeDefinition
293: .toArray(new AttributeDefinition[0]);
294: final ConstructorDefinition[] constructorDefs = (ConstructorDefinition[]) this .constructorDefinition
295: .toArray(new ConstructorDefinition[0]);
296: final String[] orderedNamesDefs = (String[]) this .orderedNames
297: .toArray(new String[0]);
298:
299: final GenericObjectFactory objectFactory = new GenericObjectFactory(
300: this .target, this .registerName, constructorDefs,
301: propertyDefs, lookupDefs, attribDefs, orderedNamesDefs);
302: this .objectMappings.put(this .target, objectFactory);
303: }
304:
305: /**
306: * Handles a constructor definition. Only one constructor can be defined for
307: * a certain object type. The constructor will be filled using the given properties.
308: *
309: * @param propertyName the property name of the referenced local property
310: * @param parameterClass the parameter class for the parameter.
311: */
312: protected void handleConstructorDefinition(
313: final String propertyName, final String parameterClass) {
314: final Class c = loadClass(parameterClass);
315: this .orderedNames.add(propertyName);
316: this .constructorDefinition.add(new ConstructorDefinition(
317: propertyName, c));
318: }
319:
320: /**
321: * Handles a manual mapping definition. The manual mapping maps specific
322: * read and write handlers to a given base class. Manual mappings always
323: * override any other definition.
324: *
325: * @param className the base class name
326: * @param readHandler the class name of the read handler
327: * @param writeHandler the class name of the write handler
328: * @return true, if the mapping was accepted, false otherwise.
329: * @throws ObjectDescriptionException if an unexpected error occured.
330: */
331: protected boolean handleManualMapping(final String className,
332: final String readHandler, final String writeHandler)
333: throws ObjectDescriptionException {
334:
335: if (!this .manualMappings.containsKey(className)) {
336: final Class loadedClass = loadClass(className);
337: this .manualMappings.put(loadedClass,
338: new ManualMappingDefinition(loadedClass,
339: readHandler, writeHandler));
340: return true;
341: }
342: return false;
343: }
344:
345: /**
346: * Starts a multiplex mapping. Multiplex mappings are used to define polymorphic
347: * argument handlers. The mapper will collect all derived classes of the given
348: * base class and will select the corresponding mapping based on the given type
349: * attribute.
350: *
351: * @param className the base class name
352: * @param typeAttr the xml-attribute name containing the mapping key
353: */
354: protected void startMultiplexMapping(final String className,
355: final String typeAttr) {
356: this .baseClass = className;
357: this .attributeName = typeAttr;
358: this .multiplexEntries = new ArrayList();
359: }
360:
361: /**
362: * Defines an entry for the multiplex mapping. The new entry will be activated
363: * when the base mappers type attribute contains this <code>typename</code> and
364: * will resolve to the handler for the given classname.
365: *
366: * @param typeName the type value for this mapping.
367: * @param className the class name to which this mapping resolves.
368: * @throws ObjectDescriptionException if an error occurs.
369: */
370: protected void handleMultiplexMapping(final String typeName,
371: final String className) throws ObjectDescriptionException {
372: this .multiplexEntries.add(new MultiplexMappingEntry(typeName,
373: className));
374: }
375:
376: /**
377: * Finializes the multiplexer mapping.
378: *
379: * @throws ObjectDescriptionException if an error occurs.
380: */
381: protected void endMultiplexMapping()
382: throws ObjectDescriptionException {
383: final MultiplexMappingEntry[] mappings = (MultiplexMappingEntry[]) this .multiplexEntries
384: .toArray(new MultiplexMappingEntry[0]);
385: final Class c = loadClass(this .baseClass);
386: this .multiplexMappings.put(c, new MultiplexMappingDefinition(c,
387: this .attributeName, mappings));
388: this .multiplexEntries = null;
389: }
390:
391: /**
392: * Loads an instantiates the attribute handler specified by the given
393: * class name.
394: *
395: * @param attribute the attribute handlers classname.
396: * @return the created attribute handler instance
397: * @throws ObjectDescriptionException if the handler could not be loaded.
398: */
399: private AttributeHandler loadAttributeHandler(final String attribute)
400: throws ObjectDescriptionException {
401:
402: final Class c = loadClass(attribute);
403: try {
404: return (AttributeHandler) c.newInstance();
405: } catch (Exception e) {
406: throw new ObjectDescriptionException(
407: "Invalid attribute handler specified: " + attribute);
408: }
409: }
410:
411: /**
412: * Checks, whether the factory has a description for the given class.
413: *
414: * @param c the class to be handled by the factory.
415: * @return true, if an description exists for the given class, false otherwise.
416: */
417: public boolean isGenericHandler(final Class c) {
418: return this .objectMappings.containsKey(c);
419: }
420:
421: /**
422: * Returns a factory instance for the given class. The factory is independent
423: * from all previously generated instances.
424: *
425: * @param c the class
426: * @return the object factory.
427: */
428: public GenericObjectFactory getFactoryForClass(final Class c) {
429: final GenericObjectFactory factory = (GenericObjectFactory) this .objectMappings
430: .get(c);
431: if (factory == null) {
432: return null;
433: }
434: return factory.getInstance();
435: }
436:
437: /**
438: * Returns the manual mapping definition for the given class, or null, if
439: * not manual definition exists.
440: *
441: * @param c the class for which to check the existence of the definition
442: * @return the manual mapping definition or null.
443: */
444: public ManualMappingDefinition getManualMappingDefinition(
445: final Class c) {
446: return (ManualMappingDefinition) this .manualMappings.get(c);
447: }
448:
449: /**
450: * Returns the multiplex definition for the given class, or null, if no
451: * such definition exists.
452: *
453: * @param c the class for which to check the existence of the multiplexer
454: * @return the multiplexer for the class, or null if no multiplexer exists.
455: */
456: public MultiplexMappingDefinition getMultiplexDefinition(
457: final Class c) {
458: final MultiplexMappingDefinition definition = (MultiplexMappingDefinition) this.multiplexMappings
459: .get(c);
460: return definition;
461: }
462:
463: }
|