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 * DefaultModelReader.java
029 * -----------------------
030 * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
031 *
032 * Original Author: Thomas Morgner;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: DefaultModelReader.java,v 1.2 2005/10/18 13:32:20 mungady Exp $
036 *
037 * Changes
038 * -------
039 * 12-Nov-2003 : Initial version (TM);
040 * 26-Nov-2003 : Updated header and Javadocs (DG);
041 *
042 */
043
044 package org.jfree.xml.generator;
045
046 import java.beans.BeanInfo;
047 import java.beans.IntrospectionException;
048 import java.beans.Introspector;
049 import java.beans.PropertyDescriptor;
050 import java.io.File;
051 import java.io.IOException;
052 import java.net.URL;
053 import java.util.ArrayList;
054
055 import org.jfree.io.IOUtils;
056 import org.jfree.xml.generator.model.ClassDescription;
057 import org.jfree.xml.generator.model.Comments;
058 import org.jfree.xml.generator.model.DescriptionModel;
059 import org.jfree.xml.generator.model.IgnoredPropertyInfo;
060 import org.jfree.xml.generator.model.ManualMappingInfo;
061 import org.jfree.xml.generator.model.MultiplexMappingInfo;
062 import org.jfree.xml.generator.model.PropertyInfo;
063 import org.jfree.xml.generator.model.PropertyType;
064 import org.jfree.xml.generator.model.TypeInfo;
065 import org.jfree.xml.util.AbstractModelReader;
066 import org.jfree.xml.util.ObjectDescriptionException;
067
068 /**
069 * A reader for the class model.
070 */
071 public class DefaultModelReader extends AbstractModelReader {
072
073 /** A model containing classes and the corresponding class descriptions. */
074 private DescriptionModel model;
075
076 /** The class description under construction. */
077 private ClassDescription currentClassDescription;
078
079 /** Information about the class being processed. */
080 private BeanInfo currentBeanInfo;
081
082 /** The base URL. */
083 private URL baseURL;
084
085 /** The source. */
086 private String source;
087
088 /** The multiplex mapping info. */
089 private MultiplexMappingInfo multiplexInfo;
090
091 /** The multiplex type info.*/
092 private ArrayList multiplexTypeInfos;
093
094 /** Storage for the properties of the current class. */
095 private ArrayList propertyList;
096
097 /** Storage for the constructors of the current class. */
098 private ArrayList constructorList;
099
100 /**
101 * Creates a new model reader.
102 */
103 public DefaultModelReader() {
104 super();
105 }
106
107 /**
108 * Loads a description model.
109 *
110 * @param file the file name.
111 *
112 * @return A description model.
113 *
114 * @throws IOException if there is an I/O problem.
115 * @throws ObjectDescriptionException if there is a problem reading the object descriptions.
116 */
117 public synchronized DescriptionModel load(final String file) throws IOException,
118 ObjectDescriptionException {
119
120 this.model = new DescriptionModel();
121 this.baseURL = new File (file).toURL();
122 parseXml(this.baseURL);
123 fillSuperClasses();
124 return this.model;
125
126 }
127
128 /**
129 * Iterates through all the class descriptions in the model, setting the superclass
130 * attribute in all cases where the superclass definitions are contained in the model.
131 */
132 protected void fillSuperClasses() {
133 for (int i = 0; i < this.model.size(); i++) {
134 final ClassDescription cd = this.model.get(i);
135 final Class parent = cd.getObjectClass().getSuperclass();
136 if (parent == null) {
137 continue;
138 }
139 final ClassDescription superCD = this.model.get(parent);
140 if (superCD != null) {
141 cd.setSuperClass(superCD.getObjectClass());
142 }
143 }
144 }
145
146 /**
147 * Begin processing an object definition element.
148 *
149 * @param className the class name.
150 * @param register the register name (<code>null</code> permitted).
151 * @param ignore ??
152 *
153 * @return <code>true</code> if the class is available, and <code>false</code> otherwise.
154 */
155 protected boolean startObjectDefinition(final String className, final String register, final boolean ignore) {
156 final Class c = loadClass(className);
157 if (c == null) {
158 return false;
159 }
160 this.currentClassDescription = new ClassDescription(c);
161 this.currentClassDescription.setPreserve(ignore);
162 this.currentClassDescription.setRegisterKey(register);
163 try {
164 this.currentBeanInfo = Introspector.getBeanInfo(c, Object.class);
165 }
166 catch (IntrospectionException ie) {
167 return false;
168 }
169 this.propertyList = new java.util.ArrayList();
170 this.constructorList = new java.util.ArrayList();
171 return true;
172 }
173
174 /**
175 * Finishes processing an object definition (sets the constructor and property info for the
176 * class description, and adds the class description to the model).
177 *
178 * @throws ObjectDescriptionException if there is a problem with the object description.
179 */
180 protected void endObjectDefinition() throws ObjectDescriptionException {
181 final PropertyInfo[] pis = (PropertyInfo[])
182 this.propertyList.toArray(new PropertyInfo[this.propertyList.size()]);
183 this.currentClassDescription.setProperties(pis);
184
185 final TypeInfo[] tis = (TypeInfo[])
186 this.constructorList.toArray(new TypeInfo[this.constructorList.size()]);
187
188 this.currentClassDescription.setConstructorDescription(tis);
189 this.currentClassDescription.setComments
190 (new Comments(getOpenComment(), getCloseComment()));
191 this.currentClassDescription.setSource(this.source);
192
193 this.model.addClassDescription(this.currentClassDescription);
194
195 this.propertyList = null;
196 this.currentBeanInfo = null;
197 this.currentClassDescription = null;
198 }
199
200 /**
201 * Handles the description of an attribute within an object definition.
202 *
203 * @param name the name.
204 * @param attribName the attribute name.
205 * @param handlerClass the fully qualified class name for the attribute handler.
206 *
207 * @throws ObjectDescriptionException if there is a problem with the object description.
208 */
209 protected void handleAttributeDefinition(final String name, final String attribName, final String handlerClass)
210 throws ObjectDescriptionException {
211
212 final PropertyInfo propertyInfo = ModelBuilder.getInstance().createSimplePropertyInfo
213 (getPropertyDescriptor(name));
214
215 if (propertyInfo == null) {
216 throw new ObjectDescriptionException("Unable to load property " + name);
217 }
218
219 propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
220 propertyInfo.setPropertyType(PropertyType.ATTRIBUTE);
221 propertyInfo.setXmlName(attribName);
222 propertyInfo.setXmlHandler(handlerClass);
223 this.propertyList.add(propertyInfo);
224 }
225
226 /**
227 * Handles the constructor definition.
228 *
229 * @param tagName the tag name.
230 * @param parameterClass the parameter class.
231 *
232 * @throws ObjectDescriptionException if there is a problem with the object description.
233 */
234 protected void handleConstructorDefinition(final String tagName, final String parameterClass)
235 throws ObjectDescriptionException {
236
237 final Class c = loadClass(parameterClass);
238 if (c == null) {
239 throw new ObjectDescriptionException("Failed to load class " + parameterClass);
240 }
241 final TypeInfo ti = new TypeInfo(tagName, c);
242 ti.setComments(new Comments(getOpenComment(), getCloseComment()));
243 this.constructorList.add (ti);
244 }
245
246 /**
247 * Handles the description of an element within an object definition.
248 *
249 * @param name the property name.
250 * @param element the element name.
251 *
252 * @throws ObjectDescriptionException if there is a problem with the object description.
253 */
254 protected void handleElementDefinition(final String name, final String element)
255 throws ObjectDescriptionException {
256
257 final PropertyInfo propertyInfo = ModelBuilder.getInstance().createSimplePropertyInfo
258 (getPropertyDescriptor(name));
259
260 if (propertyInfo == null) {
261 throw new ObjectDescriptionException("Unable to load property " + name);
262 }
263
264 propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
265 propertyInfo.setPropertyType(PropertyType.ELEMENT);
266 propertyInfo.setXmlName(element);
267 propertyInfo.setXmlHandler(null);
268 this.propertyList.add(propertyInfo);
269
270 }
271
272 /**
273 * Handles a lookup definition.
274 *
275 * @param name the name.
276 * @param lookupKey the lookup key.
277 *
278 * @throws ObjectDescriptionException if there is a problem with the object description.
279 */
280 protected void handleLookupDefinition(final String name, final String lookupKey)
281 throws ObjectDescriptionException {
282 final PropertyInfo propertyInfo = ModelBuilder.getInstance().createSimplePropertyInfo
283 (getPropertyDescriptor(name));
284
285 if (propertyInfo == null) {
286 throw new ObjectDescriptionException("Unable to load property " + name);
287 }
288
289 propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
290 propertyInfo.setPropertyType(PropertyType.LOOKUP);
291 propertyInfo.setXmlName(lookupKey);
292 propertyInfo.setXmlHandler(null);
293 this.propertyList.add(propertyInfo);
294 }
295
296 /**
297 * Returns a property descriptor for the named property, or <code>null</code> if there is
298 * no descriptor with the given name.
299 *
300 * @param propertyName the property name.
301 *
302 * @return a property descriptor.
303 */
304 protected PropertyDescriptor getPropertyDescriptor(final String propertyName) {
305 final PropertyDescriptor[] pds = this.currentBeanInfo.getPropertyDescriptors();
306 for (int i = 0; i < pds.length; i++) {
307 if (pds[i].getName().equals(propertyName)) {
308 return pds[i];
309 }
310 }
311 return null;
312 }
313
314 /**
315 * Handles an ignored property.
316 *
317 * @param name the name.
318 */
319 protected void handleIgnoredProperty(final String name) {
320 final IgnoredPropertyInfo propertyInfo = new IgnoredPropertyInfo(name);
321 propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
322 this.propertyList.add(propertyInfo);
323 }
324
325 /**
326 * Handles a manual mapping.
327 *
328 * @param className the class name.
329 * @param readHandler the read handler.
330 * @param writeHandler the write handler.
331 *
332 * @return A boolean.
333 *
334 * @throws ObjectDescriptionException if there is a problem with the object description.
335 */
336 protected boolean handleManualMapping(final String className, final String readHandler, final String writeHandler)
337 throws ObjectDescriptionException {
338
339 final ManualMappingInfo manualMappingInfo =
340 new ManualMappingInfo(loadClass(className),
341 loadClass(readHandler), loadClass(writeHandler));
342 manualMappingInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
343 manualMappingInfo.setSource(this.source);
344 this.model.getMappingModel().addManualMapping(manualMappingInfo);
345 return true;
346 }
347
348 /**
349 * Start a multiplex mapping.
350 *
351 * @param className the class name.
352 * @param typeAttr the type.
353 */
354 protected void startMultiplexMapping(final String className, final String typeAttr) {
355 this.multiplexInfo = new MultiplexMappingInfo(loadClass(className), typeAttr);
356 this.multiplexInfo.setSource(this.source);
357 this.multiplexTypeInfos = new ArrayList();
358 }
359
360 /**
361 * Handles a multiplex mapping.
362 *
363 * @param typeName the type name.
364 * @param className the class name.
365 *
366 * @throws ObjectDescriptionException if there is a problem with the object description.
367 */
368 protected void handleMultiplexMapping(final String typeName, final String className)
369 throws ObjectDescriptionException {
370 final TypeInfo info = new TypeInfo(typeName, loadClass(className));
371 info.setComments(new Comments(getOpenComment(), getCloseComment()));
372 this.multiplexTypeInfos.add (info);
373 }
374
375 /**
376 * Ends a multiplex mapping.
377 *
378 * @throws ObjectDescriptionException if there is a problem with the object description.
379 */
380 protected void endMultiplexMapping() throws ObjectDescriptionException {
381 final TypeInfo[] typeInfos = (TypeInfo[]) this.multiplexTypeInfos.toArray(
382 new TypeInfo[this.multiplexTypeInfos.size()]
383 );
384 this.multiplexInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
385 this.multiplexInfo.setChildClasses(typeInfos);
386 this.model.getMappingModel().addMultiplexMapping(this.multiplexInfo);
387 this.multiplexInfo = null;
388 }
389
390 /**
391 * Starts include handling.
392 *
393 * @param resource the URL.
394 */
395 protected void startIncludeHandling(final URL resource) {
396 this.source = IOUtils.getInstance().createRelativeURL(resource, this.baseURL);
397 this.model.addSource(this.source);
398 this.model.addIncludeComment(
399 this.source, new Comments(getOpenComment(), getCloseComment())
400 );
401 }
402
403 /**
404 * Ends include handling.
405 */
406 protected void endIncludeHandling() {
407 this.source = "";
408 }
409
410 /**
411 * Starts the root document.
412 */
413 protected void startRootDocument() {
414 this.source = "";
415 }
416
417 /**
418 * Ends the root document.
419 */
420 protected void endRootDocument() {
421 this.model.setModelComments(new Comments(getOpenComment(), getCloseComment()));
422 }
423 }