KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > xml > generator > ModelBuilder


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  * ModelBuilder.java
29  * -----------------
30  * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
31  *
32  * Original Author: Thomas Morgner;
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  *
35  * $Id: ModelBuilder.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 21-Jun-2003 : Initial version (TM);
40  * 26-Nov-2003 : Updated header and Javadocs (DG);
41  *
42  */

43
44 package org.jfree.xml.generator;
45
46 import java.beans.BeanInfo JavaDoc;
47 import java.beans.IndexedPropertyDescriptor JavaDoc;
48 import java.beans.IntrospectionException JavaDoc;
49 import java.beans.Introspector JavaDoc;
50 import java.beans.PropertyDescriptor JavaDoc;
51 import java.lang.reflect.Method JavaDoc;
52 import java.lang.reflect.Modifier JavaDoc;
53 import java.util.ArrayList JavaDoc;
54 import java.util.Arrays JavaDoc;
55 import java.util.Iterator JavaDoc;
56 import java.util.Properties JavaDoc;
57
58 import org.jfree.util.HashNMap;
59 import org.jfree.xml.generator.model.ClassDescription;
60 import org.jfree.xml.generator.model.DescriptionModel;
61 import org.jfree.xml.generator.model.MultiplexMappingInfo;
62 import org.jfree.xml.generator.model.PropertyInfo;
63 import org.jfree.xml.generator.model.PropertyType;
64 import org.jfree.xml.generator.model.TypeInfo;
65 import org.jfree.xml.util.BasicTypeSupport;
66
67 /**
68  * A model builder. This class performs the work of creating a class description model from
69  * a set of source files.
70  */

71 public final class ModelBuilder {
72
73     /** The single instance. */
74     private static ModelBuilder instance;
75
76     /**
77      * Returns the single instance of this class.
78      *
79      * @return the single instance of this class.
80      */

81     public static ModelBuilder getInstance() {
82         if (instance == null) {
83             instance = new ModelBuilder();
84         }
85         return instance;
86     }
87
88     /** The handler mapping. */
89     private Properties JavaDoc handlerMapping;
90
91     /**
92      * Creates a single instance.
93      */

94     private ModelBuilder() {
95         this.handlerMapping = new Properties JavaDoc();
96     }
97
98     /**
99      * Adds attribute handlers.
100      *
101      * @param p the handlers.
102      */

103     public void addAttributeHandlers(final Properties JavaDoc p) {
104         this.handlerMapping.putAll(p);
105     }
106
107     /**
108      * Builds a model from the classes provided by the {@link SourceCollector}.
109      * <P>
110      * The {@link DescriptionGenerator} class invokes this.
111      *
112      * @param c the source collector.
113      * @param model the model under construction (<code>null</code> permitted).
114      *
115      * @return The completed model.
116      */

117     public DescriptionModel buildModel(final SourceCollector c, DescriptionModel model) {
118         
119         Class JavaDoc[] classes = c.getClasses();
120
121         if (model == null) {
122             model = new DescriptionModel();
123         }
124
125         while (classes.length != 0) {
126             classes = fillModel(classes, model);
127         }
128
129         fillSuperClasses(model);
130         // search for multiplexer classes
131

132         // first search all classes used in parameters and add them to
133
// our list of possible base classes
134
final Class JavaDoc[] baseClasses = findElementTypes(model);
135
136         final HashNMap classMap = new HashNMap();
137         for (int i = 0; i < baseClasses.length; i++) {
138
139             final Class JavaDoc base = baseClasses[i];
140
141             for (int j = 0; j < baseClasses.length; j++) {
142
143                 final Class JavaDoc child = baseClasses[j];
144                 if (Modifier.isAbstract(child.getModifiers())) {
145                     continue;
146                 }
147                 if (base.isAssignableFrom(child)) {
148                     classMap.add(base, child);
149                 }
150             }
151         }
152
153         // at this point, the keys of 'classMap' represent all required
154
// multiplexers, while the values assigned to these keys define the
155
// possible childs
156
final Iterator JavaDoc keys = classMap.keys();
157         while (keys.hasNext()) {
158             final Class JavaDoc base = (Class JavaDoc) keys.next();
159             final Class JavaDoc[] childs = (Class JavaDoc[]) classMap.toArray(base, new Class JavaDoc[0]);
160             if (childs.length < 2) {
161                 continue;
162             }
163
164             boolean isNew = false;
165             MultiplexMappingInfo mmi = model.getMappingModel().lookupMultiplexMapping(base);
166             final ArrayList JavaDoc typeInfoList;
167             if (mmi == null) {
168                 mmi = new MultiplexMappingInfo(base);
169                 typeInfoList = new ArrayList JavaDoc();
170                 isNew = true;
171             }
172             else {
173                 typeInfoList = new ArrayList JavaDoc(Arrays.asList(mmi.getChildClasses()));
174             }
175
176             for (int i = 0; i < childs.length; i++) {
177                 // the generic information is only added, if no other information
178
// is already present ...
179
final TypeInfo typeInfo = new TypeInfo(childs[i].getName(), childs[i]);
180                 if (!typeInfoList.contains(typeInfo)) {
181                     typeInfoList.add(typeInfo);
182                 }
183             }
184
185             mmi.setChildClasses((TypeInfo[]) typeInfoList.toArray(new TypeInfo[0]));
186             if (isNew) {
187                 model.getMappingModel().addMultiplexMapping(mmi);
188             }
189         }
190
191         // when resolving a class to an handler, the resolver first has to
192
// search for an multiplexer before searching for handlers. Otherwise
193
// non-abstract baseclasses will be found before the multiplexer can
194
// resolve the situation.
195
return model;
196     }
197
198     private Class JavaDoc[] findElementTypes(final DescriptionModel model) {
199         final ArrayList JavaDoc baseClasses = new ArrayList JavaDoc();
200
201         for (int i = 0; i < model.size(); i++) {
202             final ClassDescription cd = model.get(i);
203             if (!baseClasses.contains(cd.getObjectClass())) {
204                 baseClasses.add(cd.getObjectClass());
205             }
206
207             final PropertyInfo[] properties = cd.getProperties();
208             for (int p = 0; p < properties.length; p++) {
209                 // filter primitive types ... they cannot form a generalization
210
// relation
211
if (!properties[p].getPropertyType().equals(PropertyType.ELEMENT)) {
212                     continue;
213                 }
214                 final Class JavaDoc type = properties[p].getType();
215                 if (baseClasses.contains(type)) {
216                     continue;
217                 }
218                 // filter final classes, they too cannot have derived classes
219
if (Modifier.isFinal(type.getModifiers())) {
220                     continue;
221                 }
222                 baseClasses.add(type);
223             }
224         }
225         return (Class JavaDoc[]) baseClasses.toArray(new Class JavaDoc[baseClasses.size()]);
226     }
227
228     /**
229      * Fills the super class for all object descriptions of the model. The
230      * super class is only filled, if the object's super class is contained
231      * in the model.
232      *
233      * @param model the model which should get its superclasses updated.
234      */

235     private void fillSuperClasses(final DescriptionModel model) {
236         // Fill superclasses
237
for (int i = 0; i < model.size(); i++) {
238             final ClassDescription cd = model.get(i);
239             final Class JavaDoc parent = cd.getObjectClass().getSuperclass();
240             if (parent == null) {
241                 continue;
242             }
243             final ClassDescription superCD = model.get(parent);
244             if (superCD != null) {
245                 cd.setSuperClass(superCD.getObjectClass());
246             }
247         }
248     }
249
250     /**
251      * Updates the model to contain the given classes.
252      *
253      * @param classes a list of classes which should be part of the model.
254      * @param model the model which is updated
255      *
256      * @return A list of super classes which should also be contained in the model.
257      */

258     private Class JavaDoc[] fillModel(final Class JavaDoc[] classes, final DescriptionModel model) {
259         // first check all direct matches from the source collector.
260
// but make sure that we also detect external superclasses -
261
// we have to get all properties ...
262
final ArrayList JavaDoc superClasses = new ArrayList JavaDoc();
263         for (int i = 0; i < classes.length; i++) {
264
265             Class JavaDoc superClass = classes[i].getSuperclass();
266             if (superClass != null) {
267                 if (!Object JavaDoc.class.equals(superClass)
268                     && !contains(classes, superClass)
269                     && !superClasses.contains(superClass)) {
270                     superClasses.add(superClass);
271                 }
272             }
273             else {
274                 superClass = Object JavaDoc.class;
275             }
276
277             try {
278                 final BeanInfo JavaDoc bi = Introspector.getBeanInfo(classes[i], superClass);
279                 final ClassDescription parent = model.get(classes[i]);
280                 final ClassDescription cd = createClassDescription(bi, parent);
281                 if (cd != null) {
282                     model.addClassDescription(cd);
283                 }
284             }
285             catch (IntrospectionException JavaDoc ie) {
286                 // swallowed....
287
}
288         }
289         return (Class JavaDoc[]) superClasses.toArray(new Class JavaDoc[0]);
290     }
291
292     /**
293      * Creates a {@link ClassDescription} object for the specified bean info.
294      *
295      * @param beanInfo the bean info.
296      * @param parent the parent class description.
297      *
298      * @return The class description.
299      */

300     private ClassDescription createClassDescription (final BeanInfo JavaDoc beanInfo, final ClassDescription parent) {
301         final PropertyDescriptor JavaDoc[] props = beanInfo.getPropertyDescriptors();
302         final ArrayList JavaDoc properties = new ArrayList JavaDoc();
303         for (int i = 0; i < props.length; i++) {
304             final PropertyDescriptor JavaDoc propertyDescriptor = props[i];
305             PropertyInfo pi;
306             if (parent != null) {
307                 pi = parent.getProperty(propertyDescriptor.getName());
308                 if (pi != null) {
309                     // Property already found, don't touch it
310
// Log.info (new Log.SimpleMessage
311
// ("Ignore predefined property: ", propertyDescriptor.getName()));
312
properties.add(pi);
313                     continue;
314                 }
315             }
316
317             if (props[i] instanceof IndexedPropertyDescriptor JavaDoc) {
318                 // this would handle lists and array access. We don't support
319
// this in the direct approach. We will need some cheating:
320
// <Chart>
321
// <Subtitle-list>
322
// <title1 ..>
323
// <title2 ..>
324
// pi = createIndexedPropertyInfo((IndexedPropertyDescriptor) props[i]);
325
}
326             else {
327                 pi = createSimplePropertyInfo(props[i]);
328                 if (pi != null) {
329                     properties.add(pi);
330                 }
331             }
332         }
333
334         final PropertyInfo[] propArray = (PropertyInfo[])
335             properties.toArray(new PropertyInfo[properties.size()]);
336
337         final ClassDescription cd;
338         if (parent != null) {
339             cd = parent;
340         }
341         else {
342             cd = new ClassDescription(beanInfo.getBeanDescriptor().getBeanClass());
343             cd.setDescription(beanInfo.getBeanDescriptor().getShortDescription());
344         }
345
346         cd.setProperties(propArray);
347         return cd;
348     }
349
350     /**
351      * Checks, whether the given method can be called from the generic object factory.
352      *
353      * @param method the method descriptor
354      * @return true, if the method is not null and public, false otherwise.
355      */

356     public static boolean isValidMethod(final Method JavaDoc method) {
357         if (method == null) {
358             return false;
359         }
360         if (!Modifier.isPublic(method.getModifiers())) {
361             return false;
362         }
363         return true;
364     }
365
366     /**
367      * Creates a {@link PropertyInfo} object from a {@link PropertyDescriptor}.
368      *
369      * @param pd the property descriptor.
370      *
371      * @return the property info (<code>null</code> possible).
372      */

373     public PropertyInfo createSimplePropertyInfo(final PropertyDescriptor JavaDoc pd) {
374
375         final boolean readMethod = isValidMethod(pd.getReadMethod());
376         final boolean writeMethod = isValidMethod(pd.getWriteMethod());
377         if (!writeMethod || !readMethod) {
378             // a property is useless for our purposes without having a read or write method.
379
return null;
380         }
381
382         final PropertyInfo pi = new PropertyInfo(pd.getName(), pd.getPropertyType());
383         pi.setConstrained(pd.isConstrained());
384         pi.setDescription(pd.getShortDescription());
385         pi.setNullable(true);
386         pi.setPreserve(false);
387         pi.setReadMethodAvailable(readMethod);
388         pi.setWriteMethodAvailable(writeMethod);
389         pi.setXmlName(pd.getName());
390         if (isAttributeProperty(pd.getPropertyType())) {
391             pi.setPropertyType(PropertyType.ATTRIBUTE);
392             pi.setXmlHandler(getHandlerClass(pd.getPropertyType()));
393         }
394         else {
395             pi.setPropertyType(PropertyType.ELEMENT);
396         }
397         return pi;
398     }
399
400     /**
401      * Checks, whether the given class can be handled as attribute.
402      * All primitive types can be attributes as well as all types which have
403      * a custom attribute handler defined.
404      *
405      * @param c the class which should be checked
406      * @return true, if the class can be handled as attribute, false otherwise.
407      */

408     private boolean isAttributeProperty(final Class JavaDoc c) {
409         if (BasicTypeSupport.isBasicDataType(c)) {
410             return true;
411         }
412         return this.handlerMapping.containsKey(c.getName());
413     }
414
415     /**
416      * Returns the class name for the attribute handler for a property of the specified class.
417      *
418      * @param c the class for which to search an attribute handler
419      * @return the handler class or null, if this class cannot be handled
420      * as attribute.
421      */

422     private String JavaDoc getHandlerClass(final Class JavaDoc c) {
423         if (BasicTypeSupport.isBasicDataType(c)) {
424             final String JavaDoc handler = BasicTypeSupport.getHandlerClass(c);
425             if (handler != null) {
426                 return handler;
427             }
428         }
429         return this.handlerMapping.getProperty(c.getName());
430     }
431
432     /**
433      * Checks, whether the class <code>c</code> is contained in the given
434      * class array.
435      *
436      * @param cAll the list of all classes
437      * @param c the class to be searched
438      * @return true, if the class is contained in the array, false otherwise.
439      */

440     private boolean contains(final Class JavaDoc[] cAll, final Class JavaDoc c) {
441         for (int i = 0; i < cAll.length; i++) {
442             if (cAll[i].equals(c)) {
443                 return true;
444             }
445         }
446         return false;
447     }
448
449
450 // private PropertyInfo createIndexedPropertyInfo(IndexedPropertyDescriptor prop)
451
// {
452
//
453
// MethodInfo readMethod = createMethodInfo(prop.getIndexedReadMethod());
454
// MethodInfo writeMethod = createMethodInfo(prop.getIndexedWriteMethod());
455
// if (writeMethod == null)
456
// {
457
// return null;
458
// }
459
// IndexedPropertyInfo pi = new IndexedPropertyInfo(prop.getName());
460
// pi.setConstrained(prop.isConstrained());
461
// pi.setDescription(prop.getShortDescription());
462
// pi.setNullable(true);
463
// pi.setPreserve(false);
464
// pi.setType(prop.getIndexedPropertyType());
465
// pi.setReadMethod(readMethod);
466
// pi.setWriteMethod(writeMethod);
467
//
468
// TypeInfo keyInfo = new TypeInfo("index");
469
// keyInfo.setType(Integer.TYPE);
470
// keyInfo.setNullable(false);
471
// keyInfo.setConstrained(true); // throws indexoutofboundsexception
472
// keyInfo.setDescription("Generic index value");
473
// KeyDescription kd = new KeyDescription(new TypeInfo[]{keyInfo});
474
// pi.setKey(kd);
475
// return pi;
476
// }
477
}
478
Popular Tags