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.4 2005/10/18 13:33:53 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 23-Sep-2003 : Initial version (TM);
040: *
041: */
042:
043: package org.jfree.xml.util;
044:
045: import java.beans.BeanInfo;
046: import java.beans.IntrospectionException;
047: import java.beans.Introspector;
048: import java.beans.PropertyDescriptor;
049: import java.lang.reflect.Constructor;
050: import java.lang.reflect.Method;
051: import 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: */
057: public 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,
126: Object.class);
127: final PropertyDescriptor[] pd = chartBeaninfo
128: .getPropertyDescriptors();
129: for (int i = 0; i < pd.length; i++) {
130: this .propertyInfos.put(pd[i].getName(), pd[i]);
131: }
132: } catch (IntrospectionException ioe) {
133: throw new ObjectDescriptionException(
134: "This is an ugly solution right now ... dirty hack attack");
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(
181: final String propertyName) {
182: return (PropertyDescriptor) this .propertyInfos
183: .get(propertyName);
184: }
185:
186: /**
187: * Returns the class for a tag name.
188: *
189: * @param tagName the tag name.
190: *
191: * @return the class.
192: *
193: * @throws ObjectDescriptionException if there is a problem.
194: */
195: public Class getTypeForTagName(final String tagName)
196: throws ObjectDescriptionException {
197: final PropertyDefinition pdef = getPropertyDefinitionByTagName(tagName);
198: final PropertyDescriptor pdescr = getPropertyDescriptor(pdef
199: .getPropertyName());
200: if (pdescr == null) {
201: throw new ObjectDescriptionException("Invalid Definition: "
202: + pdef.getPropertyName());
203: }
204: return pdescr.getPropertyType();
205: }
206:
207: /**
208: * Returns true if there is a property definition for the specified property name.
209: *
210: * @param propertyName the property name.
211: *
212: * @return A boolean.
213: */
214: public boolean isPropertyDefinition(final String propertyName) {
215: for (int i = 0; i < this .propertyDefinitions.length; i++) {
216: final PropertyDefinition pdef = this .propertyDefinitions[i];
217: if (pdef.getPropertyName().equals(propertyName)) {
218: return true;
219: }
220: }
221: return false;
222: }
223:
224: /**
225: * Returns the property definition for the specified property name.
226: *
227: * @param propertyName the property name.
228: *
229: * @return the property definition.
230: *
231: * @throws ObjectDescriptionException if there is no such property for this object.
232: */
233: public PropertyDefinition getPropertyDefinitionByPropertyName(
234: final String propertyName)
235: throws ObjectDescriptionException {
236: for (int i = 0; i < this .propertyDefinitions.length; i++) {
237: final PropertyDefinition pdef = this .propertyDefinitions[i];
238: if (pdef.getPropertyName().equals(propertyName)) {
239: return pdef;
240: }
241: }
242: throw new ObjectDescriptionException(
243: "This property is not defined for this kind of object. : "
244: + propertyName);
245: }
246:
247: /**
248: * Returns a property definition for the specified tag name.
249: *
250: * @param tagName the tag name.
251: *
252: * @return the property definition.
253: *
254: * @throws ObjectDescriptionException if there is no such tag defined for this object.
255: */
256: public PropertyDefinition getPropertyDefinitionByTagName(
257: final String tagName) throws ObjectDescriptionException {
258: for (int i = 0; i < this .propertyDefinitions.length; i++) {
259: final PropertyDefinition pdef = this .propertyDefinitions[i];
260: if (pdef.getElementName().equals(tagName)) {
261: return pdef;
262: }
263: }
264: throw new ObjectDescriptionException(
265: "This tag is not defined for this kind of object. : "
266: + tagName);
267: }
268:
269: /**
270: * Returns the constructor definitions.
271: *
272: * @return the constructor definitions.
273: */
274: public ConstructorDefinition[] getConstructorDefinitions() {
275: return this .constructorDefinitions;
276: }
277:
278: /**
279: * Returns the attribute definitions.
280: *
281: * @return the attribute definitions.
282: */
283: public AttributeDefinition[] getAttributeDefinitions() {
284: return this .attributeDefinitions;
285: }
286:
287: /**
288: * Returns the property definitions.
289: *
290: * @return the property definitions.
291: */
292: public PropertyDefinition[] getPropertyDefinitions() {
293: return this .propertyDefinitions;
294: }
295:
296: /**
297: * Returns the property names.
298: *
299: * @return the property names.
300: */
301: public String[] getOrderedPropertyNames() {
302: return this .orderedPropertyNames;
303: }
304:
305: /**
306: * Returns the lookup definitions.
307: *
308: * @return the lookup definitions.
309: */
310: public LookupDefinition[] getLookupDefinitions() {
311: return this .lookupDefinitions;
312: }
313:
314: /**
315: * Returns the value of the specified property.
316: *
317: * @param name the property name.
318: *
319: * @return the property value.
320: */
321: public Object getProperty(final String name) {
322: return this .propertyValues.get(name);
323: }
324:
325: /**
326: * Creates an object according to the definition.
327: *
328: * @return the object.
329: *
330: * @throws ObjectDescriptionException if there is a problem with the object description.
331: */
332: public Object createObject() throws ObjectDescriptionException {
333: final Class[] cArgs = new Class[this .constructorDefinitions.length];
334: final Object[] oArgs = new Object[this .constructorDefinitions.length];
335: for (int i = 0; i < cArgs.length; i++) {
336: final ConstructorDefinition cDef = this .constructorDefinitions[i];
337: cArgs[i] = cDef.getType();
338: if (cDef.isNull()) {
339: oArgs[i] = null;
340: } else {
341: oArgs[i] = getProperty(cDef.getPropertyName());
342: }
343: }
344:
345: try {
346: final Constructor constr = this .baseClass
347: .getConstructor(cArgs);
348: final Object o = constr.newInstance(oArgs);
349: return o;
350: } catch (Exception e) {
351: throw new ObjectDescriptionException(
352: "Ugh! Constructor made a buuuh!", e);
353: }
354: }
355:
356: /**
357: * Sets a property value.
358: *
359: * @param propertyName the property name.
360: * @param value the property value.
361: *
362: * @throws ObjectDescriptionException if there is a problem with the object description.
363: */
364: public void setProperty(final String propertyName,
365: final Object value) throws ObjectDescriptionException {
366: final PropertyDescriptor pdesc = getPropertyDescriptor(propertyName);
367: if (pdesc == null) {
368: throw new ObjectDescriptionException("Unknown property "
369: + propertyName);
370: }
371:
372: if (!isAssignableOrPrimitive(pdesc.getPropertyType(), value
373: .getClass())) {
374: throw new ObjectDescriptionException("Invalid value: "
375: + pdesc.getPropertyType() + " vs. "
376: + value.getClass());
377: }
378:
379: this .propertyValues.put(propertyName, value);
380: }
381:
382: /**
383: * Returns <code>true</code> if the base type is a primitive or assignable from the value type.
384: *
385: * @param baseType the base class.
386: * @param valueType the value class.
387: *
388: * @return A boolean.
389: */
390: private boolean isAssignableOrPrimitive(final Class baseType,
391: final Class valueType) {
392: if (BasicTypeSupport.isBasicDataType(baseType)) {
393: return true;
394: }
395: // verbose stuff below *should* no longer be needed
396: return baseType.isAssignableFrom(valueType);
397: }
398:
399: /**
400: * Returns <code>true<code> if the specified property is...
401: *
402: * @param propertyName the property name.
403: *
404: * @return A boolean.
405: */
406: private boolean isConstructorProperty(final String propertyName) {
407: for (int i = 0; i < this .constructorDefinitions.length; i++) {
408: final ConstructorDefinition cDef = this .constructorDefinitions[i];
409: if (propertyName.equals(cDef.getPropertyName())) {
410: return true;
411: }
412: }
413: return false;
414: }
415:
416: /**
417: * Writes the properties for the object.
418: *
419: * @param object the object.
420: *
421: * @throws ObjectDescriptionException if there is a problem.
422: */
423: public void writeObjectProperties(final Object object)
424: throws ObjectDescriptionException {
425: // this assumes that the order of setting the attributes does not matter.
426: for (int i = 0; i < this .orderedPropertyNames.length; i++) {
427: try {
428: final String name = this .orderedPropertyNames[i];
429: if (isConstructorProperty(name)) {
430: continue;
431: }
432: final Object value = getProperty(name);
433: if (value == null) {
434: // do nothing if value is not defined ...
435: continue;
436: }
437: final PropertyDescriptor pdescr = getPropertyDescriptor(name);
438: final Method setter = pdescr.getWriteMethod();
439: setter.invoke(object, new Object[] { value });
440: } catch (Exception e) {
441: throw new ObjectDescriptionException(
442: "Failed to set properties." + getBaseClass(), e);
443: }
444: }
445: }
446:
447: /**
448: * Reads the properties.
449: *
450: * @param object the object.
451: *
452: * @throws ObjectDescriptionException if there is a problem.
453: */
454: public void readProperties(final Object object)
455: throws ObjectDescriptionException {
456: // this assumes that the order of setting the attributes does not matter.
457: for (int i = 0; i < this .orderedPropertyNames.length; i++) {
458: try {
459: final String name = this .orderedPropertyNames[i];
460: final PropertyDescriptor pdescr = getPropertyDescriptor(name);
461: if (pdescr == null) {
462: throw new IllegalStateException(
463: "No property defined: " + name);
464: }
465: final Method setter = pdescr.getReadMethod();
466: final Object value = setter.invoke(object,
467: new Object[0]);
468: if (value == null) {
469: // do nothing if value is not defined ... or null
470: continue;
471: }
472: setProperty(name, value);
473: } catch (Exception e) {
474: throw new ObjectDescriptionException(
475: "Failed to set properties.", e);
476: }
477: }
478: }
479:
480: /**
481: * Returns the base class.
482: *
483: * @return the base class.
484: */
485: public Class getBaseClass() {
486: return this.baseClass;
487: }
488:
489: }
|