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 * GenericObjectFactory.java 029 * ------------------------- 030 * (C)opyright 2003-2005, by Thomas Morgner and Contributors. 031 * 032 * Original Author: Thomas Morgner; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * $Id: GenericObjectFactory.java,v 1.5 2011/10/17 20:08:22 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 23-Sep-2003 : Initial version (TM); 040 * 041 */ 042 043package org.jfree.xml.util; 044 045import java.beans.BeanInfo; 046import java.beans.IntrospectionException; 047import java.beans.Introspector; 048import java.beans.PropertyDescriptor; 049import java.lang.reflect.Constructor; 050import java.lang.reflect.Method; 051import java.util.HashMap; 052 053/** 054 * The generic object factory contains all methods necessary to collect 055 * the property values needed to produce a fully instantiated object. 056 */ 057public final class GenericObjectFactory { 058 059 /** Storage for the constructor definitions. */ 060 private final ConstructorDefinition[] constructorDefinitions; 061 062 /** Storage for the property definitions. */ 063 private final PropertyDefinition[] propertyDefinitions; 064 065 /** Storage for the lookup definitions. */ 066 private final LookupDefinition[] lookupDefinitions; 067 068 /** Storage for the attribute definitions. */ 069 private final AttributeDefinition[] attributeDefinitions; 070 071 /** The ordered property names. */ 072 private final String[] orderedPropertyNames; 073 074 /** Storage for property info. */ 075 private final HashMap propertyInfos; 076 077 /** Storage for property values. */ 078 private final HashMap propertyValues; 079 080 /** The base class. */ 081 private final Class baseClass; 082 083 /** The register name. */ 084 private final String registerName; 085 086 /** 087 * Creates a new generic object factory. 088 * 089 * @param c the class. 090 * @param registerName the (optional) name under which to register the class for 091 * any later lookup. 092 * @param constructors the constructor definitions. 093 * @param propertyDefinitions the property definitions. 094 * @param lookupDefinitions the lookup definitions. 095 * @param attributeDefinitions the attribute definitions. 096 * @param orderedPropertyNames the ordered property names. 097 * 098 * @throws ObjectDescriptionException if there is a problem. 099 */ 100 public GenericObjectFactory(final Class c, 101 final String registerName, 102 final ConstructorDefinition[] constructors, 103 final PropertyDefinition[] propertyDefinitions, 104 final LookupDefinition[] lookupDefinitions, 105 final AttributeDefinition[] attributeDefinitions, 106 final String[] orderedPropertyNames) 107 throws ObjectDescriptionException { 108 109 if (c == null) { 110 throw new NullPointerException("BaseClass cannot be null."); 111 } 112 this.baseClass = c; 113 this.registerName = registerName; 114 115 this.propertyInfos = new HashMap(); 116 this.propertyValues = new HashMap(); 117 118 this.constructorDefinitions = constructors; 119 this.propertyDefinitions = propertyDefinitions; 120 this.lookupDefinitions = lookupDefinitions; 121 this.attributeDefinitions = attributeDefinitions; 122 this.orderedPropertyNames = orderedPropertyNames; 123 124 try { 125 final BeanInfo chartBeaninfo = Introspector.getBeanInfo(c, Object.class); 126 final PropertyDescriptor[] pd = chartBeaninfo.getPropertyDescriptors(); 127 for (int i = 0; i < pd.length; i++) { 128 this.propertyInfos.put(pd[i].getName(), pd[i]); 129 } 130 } 131 catch (IntrospectionException ioe) { 132 throw new ObjectDescriptionException( 133 "This is an ugly solution right now ... dirty hack attack" 134 ); 135 } 136 } 137 138 /** 139 * A copy constructor. 140 * 141 * @param factory the factory to copy. 142 */ 143 private GenericObjectFactory (final GenericObjectFactory factory) { 144 this.baseClass = factory.baseClass; 145 this.propertyValues = new HashMap(); 146 this.orderedPropertyNames = factory.orderedPropertyNames; 147 this.constructorDefinitions = factory.constructorDefinitions; 148 this.propertyDefinitions = factory.propertyDefinitions; 149 this.attributeDefinitions = factory.attributeDefinitions; 150 this.propertyInfos = factory.propertyInfos; 151 this.registerName = factory.registerName; 152 this.lookupDefinitions = factory.lookupDefinitions; 153 } 154 155 /** 156 * Returns a copy of this instance. 157 * 158 * @return a copy of this instance. 159 */ 160 public GenericObjectFactory getInstance () { 161 return new GenericObjectFactory(this); 162 } 163 164 /** 165 * Returns the register name. 166 * 167 * @return the register name. 168 */ 169 public String getRegisterName() { 170 return this.registerName; 171 } 172 173 /** 174 * Returns a property descriptor. 175 * 176 * @param propertyName the property name. 177 * 178 * @return a property descriptor. 179 */ 180 private PropertyDescriptor getPropertyDescriptor(final String propertyName) { 181 return (PropertyDescriptor) this.propertyInfos.get(propertyName); 182 } 183 184 /** 185 * Returns the class for a tag name. 186 * 187 * @param tagName the tag name. 188 * 189 * @return the class. 190 * 191 * @throws ObjectDescriptionException if there is a problem. 192 */ 193 public Class getTypeForTagName(final String tagName) throws ObjectDescriptionException { 194 final PropertyDefinition pdef = getPropertyDefinitionByTagName(tagName); 195 final PropertyDescriptor pdescr = getPropertyDescriptor(pdef.getPropertyName()); 196 if (pdescr == null) { 197 throw new ObjectDescriptionException("Invalid Definition: " + pdef.getPropertyName()); 198 } 199 return pdescr.getPropertyType(); 200 } 201 202 /** 203 * Returns true if there is a property definition for the specified property name. 204 * 205 * @param propertyName the property name. 206 * 207 * @return A boolean. 208 */ 209 public boolean isPropertyDefinition (final String propertyName) { 210 for (int i = 0; i < this.propertyDefinitions.length; i++) { 211 final PropertyDefinition pdef = this.propertyDefinitions[i]; 212 if (pdef.getPropertyName().equals(propertyName)) { 213 return true; 214 } 215 } 216 return false; 217 } 218 219 /** 220 * Returns the property definition for the specified property name. 221 * 222 * @param propertyName the property name. 223 * 224 * @return the property definition. 225 * 226 * @throws ObjectDescriptionException if there is no such property for this object. 227 */ 228 public PropertyDefinition getPropertyDefinitionByPropertyName(final String propertyName) 229 throws ObjectDescriptionException { 230 for (int i = 0; i < this.propertyDefinitions.length; i++) { 231 final PropertyDefinition pdef = this.propertyDefinitions[i]; 232 if (pdef.getPropertyName().equals(propertyName)) { 233 return pdef; 234 } 235 } 236 throw new ObjectDescriptionException( 237 "This property is not defined for this kind of object. : " + propertyName 238 ); 239 } 240 241 /** 242 * Returns a property definition for the specified tag name. 243 * 244 * @param tagName the tag name. 245 * 246 * @return the property definition. 247 * 248 * @throws ObjectDescriptionException if there is no such tag defined for this object. 249 */ 250 public PropertyDefinition getPropertyDefinitionByTagName(final String tagName) 251 throws ObjectDescriptionException { 252 for (int i = 0; i < this.propertyDefinitions.length; i++) { 253 final PropertyDefinition pdef = this.propertyDefinitions[i]; 254 if (pdef.getElementName().equals(tagName)) { 255 return pdef; 256 } 257 } 258 throw new ObjectDescriptionException( 259 "This tag is not defined for this kind of object. : " + tagName 260 ); 261 } 262 263 /** 264 * Returns the constructor definitions. 265 * 266 * @return the constructor definitions. 267 */ 268 public ConstructorDefinition[] getConstructorDefinitions() { 269 return this.constructorDefinitions; 270 } 271 272 /** 273 * Returns the attribute definitions. 274 * 275 * @return the attribute definitions. 276 */ 277 public AttributeDefinition[] getAttributeDefinitions() { 278 return this.attributeDefinitions; 279 } 280 281 /** 282 * Returns the property definitions. 283 * 284 * @return the property definitions. 285 */ 286 public PropertyDefinition[] getPropertyDefinitions() { 287 return this.propertyDefinitions; 288 } 289 290 /** 291 * Returns the property names. 292 * 293 * @return the property names. 294 */ 295 public String[] getOrderedPropertyNames() { 296 return this.orderedPropertyNames; 297 } 298 299 /** 300 * Returns the lookup definitions. 301 * 302 * @return the lookup definitions. 303 */ 304 public LookupDefinition[] getLookupDefinitions() { 305 return this.lookupDefinitions; 306 } 307 308 /** 309 * Returns the value of the specified property. 310 * 311 * @param name the property name. 312 * 313 * @return the property value. 314 */ 315 public Object getProperty(final String name) { 316 return this.propertyValues.get(name); 317 } 318 319 /** 320 * Creates an object according to the definition. 321 * 322 * @return the object. 323 * 324 * @throws ObjectDescriptionException if there is a problem with the object description. 325 */ 326 public Object createObject() throws ObjectDescriptionException { 327 final Class[] cArgs = new Class[this.constructorDefinitions.length]; 328 final Object[] oArgs = new Object[this.constructorDefinitions.length]; 329 for (int i = 0; i < cArgs.length; i++) { 330 final ConstructorDefinition cDef = this.constructorDefinitions[i]; 331 cArgs[i] = cDef.getType(); 332 if (cDef.isNull()) { 333 oArgs[i] = null; 334 } 335 else { 336 oArgs[i] = getProperty(cDef.getPropertyName()); 337 } 338 } 339 340 try { 341 final Constructor constr = this.baseClass.getConstructor(cArgs); 342 final Object o = constr.newInstance(oArgs); 343 return o; 344 } 345 catch (Exception e) { 346 throw new ObjectDescriptionException("Ugh! Constructor made a buuuh!", e); 347 } 348 } 349 350 /** 351 * Sets a property value. 352 * 353 * @param propertyName the property name. 354 * @param value the property value. 355 * 356 * @throws ObjectDescriptionException if there is a problem with the object description. 357 */ 358 public void setProperty(final String propertyName, final Object value) 359 throws ObjectDescriptionException { 360 final PropertyDescriptor pdesc = getPropertyDescriptor(propertyName); 361 if (pdesc == null) { 362 throw new ObjectDescriptionException("Unknown property " + propertyName); 363 } 364 365 if (!isAssignableOrPrimitive(pdesc.getPropertyType(), value.getClass())) { 366 throw new ObjectDescriptionException( 367 "Invalid value: " + pdesc.getPropertyType() + " vs. " + value.getClass() 368 ); 369 } 370 371 this.propertyValues.put(propertyName, value); 372 } 373 374 /** 375 * Returns <code>true</code> if the base type is a primitive or assignable from the value type. 376 * 377 * @param baseType the base class. 378 * @param valueType the value class. 379 * 380 * @return A boolean. 381 */ 382 private boolean isAssignableOrPrimitive(final Class baseType, final Class valueType) { 383 if (BasicTypeSupport.isBasicDataType(baseType)) { 384 return true; 385 } 386 // verbose stuff below *should* no longer be needed 387 return baseType.isAssignableFrom(valueType); 388 } 389 390 /** 391 * Returns <code>true</code> if the specified property is... 392 * 393 * @param propertyName the property name. 394 * 395 * @return A boolean. 396 */ 397 private boolean isConstructorProperty(final String propertyName) { 398 for (int i = 0; i < this.constructorDefinitions.length; i++) { 399 final ConstructorDefinition cDef = this.constructorDefinitions[i]; 400 if (propertyName.equals(cDef.getPropertyName())) { 401 return true; 402 } 403 } 404 return false; 405 } 406 407 /** 408 * Writes the properties for the object. 409 * 410 * @param object the object. 411 * 412 * @throws ObjectDescriptionException if there is a problem. 413 */ 414 public void writeObjectProperties(final Object object) throws ObjectDescriptionException { 415 // this assumes that the order of setting the attributes does not matter. 416 for (int i = 0; i < this.orderedPropertyNames.length; i++) { 417 try { 418 final String name = this.orderedPropertyNames[i]; 419 if (isConstructorProperty(name)) { 420 continue; 421 } 422 final Object value = getProperty(name); 423 if (value == null) { 424 // do nothing if value is not defined ... 425 continue; 426 } 427 final PropertyDescriptor pdescr = getPropertyDescriptor(name); 428 final Method setter = pdescr.getWriteMethod(); 429 setter.invoke(object, new Object[]{value}); 430 } 431 catch (Exception e) { 432 throw new ObjectDescriptionException( 433 "Failed to set properties." + getBaseClass(), e 434 ); 435 } 436 } 437 } 438 439 /** 440 * Reads the properties. 441 * 442 * @param object the object. 443 * 444 * @throws ObjectDescriptionException if there is a problem. 445 */ 446 public void readProperties(final Object object) throws ObjectDescriptionException { 447 // this assumes that the order of setting the attributes does not matter. 448 for (int i = 0; i < this.orderedPropertyNames.length; i++) { 449 try { 450 final String name = this.orderedPropertyNames[i]; 451 final PropertyDescriptor pdescr = getPropertyDescriptor(name); 452 if (pdescr == null) { 453 throw new IllegalStateException("No property defined: " + name); 454 } 455 final Method setter = pdescr.getReadMethod(); 456 final Object value = setter.invoke(object, new Object[0]); 457 if (value == null) { 458 // do nothing if value is not defined ... or null 459 continue; 460 } 461 setProperty(name, value); 462 } 463 catch (Exception e) { 464 throw new ObjectDescriptionException("Failed to set properties.", e); 465 } 466 } 467 } 468 469 /** 470 * Returns the base class. 471 * 472 * @return the base class. 473 */ 474 public Class getBaseClass() { 475 return this.baseClass; 476 } 477 478}