Source for org.jfree.xml.util.ObjectFactoryLoader

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