KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > xml > util > AbstractModelReader


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

43
44 package org.jfree.xml.util;
45
46 import java.io.BufferedInputStream JavaDoc;
47 import java.io.InputStream JavaDoc;
48 import java.net.URL JavaDoc;
49 import java.util.Stack JavaDoc;
50
51 import javax.xml.parsers.SAXParser JavaDoc;
52 import javax.xml.parsers.SAXParserFactory JavaDoc;
53
54 import org.jfree.util.Log;
55 import org.jfree.util.ObjectUtilities;
56 import org.jfree.xml.CommentHandler;
57 import org.jfree.xml.ElementDefinitionException;
58 import org.xml.sax.Attributes JavaDoc;
59 import org.xml.sax.InputSource JavaDoc;
60 import org.xml.sax.SAXException JavaDoc;
61 import org.xml.sax.XMLReader JavaDoc;
62 import org.xml.sax.helpers.DefaultHandler JavaDoc;
63
64 /**
65  * Loads the class model from an previously written xml file set.
66  * This class provides abstract methods which get called during the parsing
67  * (similiar to the SAX parsing, but slightly easier to code).
68  *
69  * This will need a rewrite in the future, when the structure is finished.
70  */

71 public abstract class AbstractModelReader {
72
73     /** The 'START' state. */
74     private static final int STATE_START = 0;
75     
76     /** The 'IN_OBJECT' state. */
77     private static final int IN_OBJECT = 1;
78
79     /** The 'IGNORE_OBJECT' state. */
80     private static final int IGNORE_OBJECT = 2;
81     
82     /** The 'MAPPING' state. */
83     private static final int MAPPING_STATE = 3;
84     
85     /** The 'CONSTRUCTOR' state. */
86     private static final int CONSTRUCTOR_STATE = 4;
87
88     /**
89      * The SAX2 callback implementation used for parsing the model xml files.
90      */

91     private class SAXModelHandler extends DefaultHandler JavaDoc {
92
93         /** The resource URL. */
94         private URL JavaDoc resource;
95
96         /** The current state. */
97         private int state;
98         
99         /** Open comments. */
100         private Stack JavaDoc openComments;
101         
102         /** Flag to track includes. */
103         private boolean isInclude;
104
105         /**
106          * Creates a new SAX handler for parsing the model.
107          *
108          * @param resource the resource URL.
109          * @param isInclude an include?
110          */

111         public SAXModelHandler(final URL JavaDoc resource, final boolean isInclude) {
112             if (resource == null) {
113                 throw new NullPointerException JavaDoc();
114             }
115             this.resource = resource;
116             this.openComments = new Stack JavaDoc();
117             this.isInclude = isInclude;
118         }
119
120         /**
121          * Receive notification of the start of an element.
122          *
123          * @param uri The Namespace URI, or the empty string if the
124          * element has no Namespace URI or if Namespace
125          * processing is not being performed.
126          * @param localName The local name (without prefix), or the
127          * empty string if Namespace processing is not being
128          * performed.
129          * @param qName The qualified name (with prefix), or the
130          * empty string if qualified names are not available.
131          * @param attributes The attributes attached to the element. If
132          * there are no attributes, it shall be an empty
133          * Attributes object.
134          * @exception SAXException Any SAX exception, possibly
135          * wrapping another exception.
136          *
137          * @see org.xml.sax.ContentHandler#startElement
138          */

139         public void startElement(final String JavaDoc uri, final String JavaDoc localName,
140                                  final String JavaDoc qName, final Attributes JavaDoc attributes)
141             throws SAXException JavaDoc {
142
143             setOpenComment(getCommentHandler().getComments());
144             this.openComments.push(getOpenComment());
145             setCloseComment(null);
146
147             try {
148
149                 if (!this.isInclude && qName.equals(ClassModelTags.OBJECTS_TAG)) {
150                     //Log.debug ("Open Comments: " + openComment);
151
startRootDocument();
152                     return;
153                 }
154
155                 if (getState() == STATE_START) {
156                     startRootElement(qName, attributes);
157                 }
158                 else if (getState() == IGNORE_OBJECT) {
159                     return;
160                 }
161                 else if (getState() == IN_OBJECT) {
162                     startObjectElement(qName, attributes);
163                 }
164                 else if (getState() == MAPPING_STATE) {
165                     if (!qName.equals(ClassModelTags.TYPE_TAG)) {
166                         throw new SAXException JavaDoc("Expected 'type' tag");
167                     }
168                     final String JavaDoc name = attributes.getValue(ClassModelTags.NAME_ATTR);
169                     final String JavaDoc target = attributes.getValue(ClassModelTags.CLASS_ATTR);
170                     handleMultiplexMapping(name, target);
171                 }
172                 else if (getState() == CONSTRUCTOR_STATE) {
173                     if (!qName.equals(ClassModelTags.PARAMETER_TAG)) {
174                         throw new SAXException JavaDoc("Expected 'parameter' tag");
175                     }
176                     final String JavaDoc parameterClass = attributes.getValue(ClassModelTags.CLASS_ATTR);
177                     final String JavaDoc tagName = attributes.getValue(ClassModelTags.PROPERTY_ATTR); // optional
178
handleConstructorDefinition(tagName, parameterClass);
179                 }
180             }
181             catch (ObjectDescriptionException e) {
182                 throw new SAXException JavaDoc(e);
183             }
184             finally {
185                 getCommentHandler().clearComments();
186             }
187         }
188
189         /**
190          * Receive notification of the end of an element.
191          *
192          * @param uri The Namespace URI, or the empty string if the
193          * element has no Namespace URI or if Namespace
194          * processing is not being performed.
195          * @param localName The local name (without prefix), or the
196          * empty string if Namespace processing is not being
197          * performed.
198          * @param qName The qualified name (with prefix), or the
199          * empty string if qualified names are not available.
200          * @exception SAXException Any SAX exception, possibly
201          * wrapping another exception.
202          * @see org.xml.sax.ContentHandler#endElement
203          */

204         public void endElement(final String JavaDoc uri, final String JavaDoc localName, final String JavaDoc qName)
205             throws SAXException JavaDoc {
206
207             setOpenComment((String JavaDoc[]) this.openComments.pop());
208             setCloseComment(getCommentHandler().getComments());
209
210             try {
211                 if (!this.isInclude && qName.equals(ClassModelTags.OBJECTS_TAG)) {
212                     endRootDocument();
213                     return;
214                 }
215
216                 if (qName.equals(ClassModelTags.OBJECT_TAG)) {
217                     if (getState() != IGNORE_OBJECT) {
218                         endObjectDefinition();
219                     }
220                     setState(STATE_START);
221                 }
222                 else if (qName.equals(ClassModelTags.MAPPING_TAG)) {
223                     setState(STATE_START);
224                     endMultiplexMapping();
225                 }
226                 else if (qName.equals(ClassModelTags.CONSTRUCTOR_TAG)) {
227                     if (getState() != IGNORE_OBJECT) {
228                         setState(IN_OBJECT);
229                     }
230                 }
231             }
232             catch (ObjectDescriptionException e) {
233                 throw new SAXException JavaDoc(e);
234             }
235             finally {
236                 getCommentHandler().clearComments();
237             }
238         }
239
240         /**
241          * Handles the start of an element within an object definition.
242          *
243          * @param qName The qualified name (with prefix), or the
244          * empty string if qualified names are not available.
245          * @param attributes The attributes attached to the element. If
246          * there are no attributes, it shall be an empty
247          * Attributes object.
248          * @throws ObjectDescriptionException if an error occured while
249          * handling this tag
250          */

251         private void startObjectElement(final String JavaDoc qName, final Attributes JavaDoc attributes)
252             throws ObjectDescriptionException {
253             if (qName.equals(ClassModelTags.CONSTRUCTOR_TAG)) {
254                 setState(CONSTRUCTOR_STATE);
255             }
256             else if (qName.equals(ClassModelTags.LOOKUP_PROPERTY_TAG)) {
257                 final String JavaDoc name = attributes.getValue(ClassModelTags.NAME_ATTR);
258                 final String JavaDoc lookupKey = attributes.getValue(ClassModelTags.LOOKUP_ATTR);
259                 handleLookupDefinition(name, lookupKey);
260             }
261             else if (qName.equals(ClassModelTags.IGNORED_PROPERTY_TAG)) {
262                 final String JavaDoc name = attributes.getValue(ClassModelTags.NAME_ATTR);
263                 handleIgnoredProperty(name);
264             }
265             else if (qName.equals(ClassModelTags.ELEMENT_PROPERTY_TAG)) {
266                 final String JavaDoc elementAtt = attributes.getValue(ClassModelTags.ELEMENT_ATTR);
267                 final String JavaDoc name = attributes.getValue(ClassModelTags.NAME_ATTR);
268                 handleElementDefinition(name, elementAtt);
269             }
270             else if (qName.equals(ClassModelTags.ATTRIBUTE_PROPERTY_TAG)) {
271                 final String JavaDoc name = attributes.getValue(ClassModelTags.NAME_ATTR);
272                 final String JavaDoc attribName = attributes.getValue(ClassModelTags.ATTRIBUTE_ATTR);
273                 final String JavaDoc handler = attributes.getValue(ClassModelTags.ATTRIBUTE_HANDLER_ATTR);
274                 handleAttributeDefinition(name, attribName, handler);
275             }
276         }
277
278         /**
279          * Handles the include or object tag.
280          *
281          * @param qName The qualified name (with prefix), or the
282          * empty string if qualified names are not available.
283          * @param attributes The attributes attached to the element. If
284          * there are no attributes, it shall be an empty
285          * Attributes object.
286          * @throws SAXException if an parser error occured
287          * @throws ObjectDescriptionException if an object model related
288          * error occured.
289          */

290         private void startRootElement(final String JavaDoc qName, final Attributes JavaDoc attributes)
291             throws SAXException JavaDoc, ObjectDescriptionException {
292
293             if (qName.equals(ClassModelTags.INCLUDE_TAG)) {
294                 if (this.isInclude) {
295                     Log.warn("Ignored nested include tag.");
296                     return;
297                 }
298                 final String JavaDoc src = attributes.getValue(ClassModelTags.SOURCE_ATTR);
299                 try {
300                     final URL JavaDoc url = new URL JavaDoc(this.resource, src);
301                     startIncludeHandling(url);
302                     parseXmlDocument(url, true);
303                     endIncludeHandling();
304                 }
305                 catch (Exception JavaDoc ioe) {
306                     throw new ElementDefinitionException
307                         (ioe, "Unable to include file from " + src);
308                 }
309             }
310             else if (qName.equals(ClassModelTags.OBJECT_TAG)) {
311                 setState(IN_OBJECT);
312                 final String JavaDoc className = attributes.getValue(ClassModelTags.CLASS_ATTR);
313                 String JavaDoc register = attributes.getValue(ClassModelTags.REGISTER_NAMES_ATTR);
314                 if (register != null && register.length() == 0) {
315                     register = null;
316                 }
317                 final boolean ignored = "true".equals(attributes.getValue(ClassModelTags.IGNORE_ATTR));
318                 if (!startObjectDefinition(className, register, ignored)) {
319                     setState(IGNORE_OBJECT);
320                 }
321             }
322             else if (qName.equals(ClassModelTags.MANUAL_TAG)) {
323                 final String JavaDoc className = attributes.getValue(ClassModelTags.CLASS_ATTR);
324                 final String JavaDoc readHandler = attributes.getValue(ClassModelTags.READ_HANDLER_ATTR);
325                 final String JavaDoc writeHandler = attributes.getValue(ClassModelTags.WRITE_HANDLER_ATTR);
326                 handleManualMapping(className, readHandler, writeHandler);
327             }
328             else if (qName.equals(ClassModelTags.MAPPING_TAG)) {
329                 setState(MAPPING_STATE);
330                 final String JavaDoc typeAttr = attributes.getValue(ClassModelTags.TYPE_ATTR);
331                 final String JavaDoc baseClass = attributes.getValue(ClassModelTags.BASE_CLASS_ATTR);
332                 startMultiplexMapping(baseClass, typeAttr);
333             }
334         }
335
336         /**
337          * Returns the current state.
338          *
339          * @return the state.
340          */

341         private int getState() {
342             return this.state;
343         }
344
345         /**
346          * Sets the current state.
347          *
348          * @param state the state.
349          */

350         private void setState(final int state) {
351             this.state = state;
352         }
353     }
354
355     /** The comment handler. */
356     private CommentHandler commentHandler;
357     
358     /** The close comments. */
359     private String JavaDoc[] closeComment;
360     
361     /** The open comments. */
362     private String JavaDoc[] openComment;
363
364     /**
365      * Default Constructor.
366      */

367     public AbstractModelReader() {
368         this.commentHandler = new CommentHandler();
369     }
370
371     /**
372      * Returns the comment handler.
373      *
374      * @return The comment handler.
375      */

376     protected CommentHandler getCommentHandler() {
377         return this.commentHandler;
378     }
379
380     /**
381      * Returns the close comment.
382      *
383      * @return The close comment.
384      */

385     protected String JavaDoc[] getCloseComment() {
386         return this.closeComment;
387     }
388
389     /**
390      * Returns the open comment.
391      *
392      * @return The open comment.
393      */

394     protected String JavaDoc[] getOpenComment() {
395         return this.openComment;
396     }
397
398     /**
399      * Sets the close comment.
400      *
401      * @param closeComment the close comment.
402      */

403     protected void setCloseComment(final String JavaDoc[] closeComment) {
404         this.closeComment = closeComment;
405     }
406
407     /**
408      * Sets the open comment.
409      *
410      * @param openComment the open comment.
411      */

412     protected void setOpenComment(final String JavaDoc[] openComment) {
413         this.openComment = openComment;
414     }
415
416     /**
417      * Parses an XML document at the given URL.
418      *
419      * @param resource the document URL.
420      *
421      * @throws ObjectDescriptionException ??
422      */

423     protected void parseXml(final URL JavaDoc resource) throws ObjectDescriptionException {
424         parseXmlDocument(resource, false);
425     }
426
427     /**
428      * Parses the given specification and loads all includes specified in the files.
429      * This implementation does not check for loops in the include files.
430      *
431      * @param resource the url of the xml specification.
432      * @param isInclude an include?
433      *
434      * @throws org.jfree.xml.util.ObjectDescriptionException if an error occured which prevented the
435      * loading of the specifications.
436      */

437     protected void parseXmlDocument(final URL JavaDoc resource, final boolean isInclude)
438         throws ObjectDescriptionException {
439         
440         try {
441             final InputStream JavaDoc in = new BufferedInputStream JavaDoc(resource.openStream());
442             final SAXParserFactory JavaDoc factory = SAXParserFactory.newInstance();
443             final SAXParser JavaDoc saxParser = factory.newSAXParser();
444             final XMLReader JavaDoc reader = saxParser.getXMLReader();
445
446             final SAXModelHandler handler = new SAXModelHandler(resource, isInclude);
447             try {
448                 reader.setProperty
449                     ("http://xml.org/sax/properties/lexical-handler",
450                         getCommentHandler());
451             }
452             catch (SAXException JavaDoc se) {
453                 Log.debug("Comments are not supported by this SAX implementation.");
454             }
455             reader.setContentHandler(handler);
456             reader.setDTDHandler(handler);
457             reader.setErrorHandler(handler);
458             reader.parse(new InputSource JavaDoc(in));
459             in.close();
460         }
461         catch (Exception JavaDoc e) {
462             // unable to init
463
Log.warn("Unable to load factory specifications", e);
464             throw new ObjectDescriptionException("Unable to load object factory specs.", e);
465         }
466     }
467
468     /**
469      * Start the root document.
470      */

471     protected void startRootDocument() {
472         // nothing required
473
}
474
475     /**
476      * End the root document.
477      */

478     protected void endRootDocument() {
479         // nothing required
480
}
481
482     /**
483      * Start handling an include.
484      *
485      * @param resource the URL.
486      */

487     protected void startIncludeHandling(final URL JavaDoc resource) {
488         // nothing required
489
}
490
491     /**
492      * End handling an include.
493      */

494     protected void endIncludeHandling() {
495         // nothing required
496
}
497
498     /**
499      * Callback method for ignored properties. Such properties get marked so that
500      * the information regarding these properties won't get lost.
501      *
502      * @param name the name of the ignored property.
503      */

504     protected void handleIgnoredProperty(final String JavaDoc name) {
505         // nothing required
506
}
507
508     /**
509      * Handles a manual mapping definition. The manual mapping maps specific
510      * read and write handlers to a given base class. Manual mappings always
511      * override any other definition.
512      *
513      * @param className the base class name
514      * @param readHandler the class name of the read handler
515      * @param writeHandler the class name of the write handler
516      * @return true, if the mapping was accepted, false otherwise.
517      * @throws ObjectDescriptionException if an unexpected error occured.
518      */

519     protected abstract boolean handleManualMapping(String JavaDoc className, String JavaDoc readHandler,
520         String JavaDoc writeHandler) throws ObjectDescriptionException;
521
522     /**
523      * Starts a object definition. The object definition collects all properties of
524      * an bean-class and defines, which constructor should be used when creating the
525      * class.
526      *
527      * @param className the class name of the defined object
528      * @param register the (optional) register name, to lookup and reference the object
529      * later.
530      * @param ignored ??.
531      *
532      * @return true, if the definition was accepted, false otherwise.
533      * @throws ObjectDescriptionException if an unexpected error occured.
534      */

535     protected abstract boolean startObjectDefinition(String JavaDoc className, String JavaDoc register,
536         boolean ignored) throws ObjectDescriptionException;
537
538     /**
539      * Handles an attribute definition. This method gets called after the object definition
540      * was started. The method will be called for every defined attribute property.
541      *
542      * @param name the name of the property
543      * @param attribName the xml-attribute name to use later.
544      * @param handlerClass the attribute handler class.
545      * @throws ObjectDescriptionException if an error occured.
546      */

547     protected abstract void handleAttributeDefinition(String JavaDoc name, String JavaDoc attribName,
548                                                       String JavaDoc handlerClass)
549         throws ObjectDescriptionException;
550
551     /**
552      * Handles an element definition. This method gets called after the object definition
553      * was started. The method will be called for every defined element property. Element
554      * properties are used to describe complex objects.
555      *
556      * @param name the name of the property
557      * @param element the xml-tag name for the child element.
558      * @throws ObjectDescriptionException if an error occurs.
559      */

560     protected abstract void handleElementDefinition(String JavaDoc name, String JavaDoc element)
561         throws ObjectDescriptionException;
562
563     /**
564      * Handles an lookup definition. This method gets called after the object definition
565      * was started. The method will be called for every defined lookup property. Lookup properties
566      * reference previously created object using the object's registry name.
567      *
568      * @param name the property name of the base object
569      * @param lookupKey the register key of the referenced object
570      * @throws ObjectDescriptionException if an error occured.
571      */

572     protected abstract void handleLookupDefinition(String JavaDoc name, String JavaDoc lookupKey)
573         throws ObjectDescriptionException;
574
575     /**
576      * Finializes the object definition.
577      *
578      * @throws ObjectDescriptionException if an error occures.
579      */

580     protected abstract void endObjectDefinition() throws ObjectDescriptionException;
581
582     /**
583      * Starts a multiplex mapping. Multiplex mappings are used to define polymorphic
584      * argument handlers. The mapper will collect all derived classes of the given
585      * base class and will select the corresponding mapping based on the given type
586      * attribute.
587      *
588      * @param className the base class name
589      * @param typeAttr the xml-attribute name containing the mapping key
590      */

591     protected abstract void startMultiplexMapping(String JavaDoc className, String JavaDoc typeAttr);
592
593     /**
594      * Defines an entry for the multiplex mapping. The new entry will be activated
595      * when the base mappers type attribute contains this <code>typename</code> and
596      * will resolve to the handler for the given classname.
597      *
598      * @param typeName the type value for this mapping.
599      * @param className the class name to which this mapping resolves.
600      * @throws ObjectDescriptionException if an error occurs.
601      */

602     protected abstract void handleMultiplexMapping(String JavaDoc typeName, String JavaDoc className)
603         throws ObjectDescriptionException;
604
605     /**
606      * Finializes the multiplexer mapping.
607      *
608      * @throws ObjectDescriptionException if an error occurs.
609      */

610     protected abstract void endMultiplexMapping() throws ObjectDescriptionException;
611
612     /**
613      * Handles a constructor definition. Only one constructor can be defined for
614      * a certain object type. The constructor will be filled using the given properties.
615      *
616      * @param propertyName the property name of the referenced local property
617      * @param parameterClass the parameter class for the parameter.
618      * @throws ObjectDescriptionException if an error occured.
619      */

620     protected abstract void handleConstructorDefinition(String JavaDoc propertyName, String JavaDoc parameterClass)
621         throws ObjectDescriptionException;
622
623     /**
624      * Loads the given class, and ignores all exceptions which may occur
625      * during the loading. If the class was invalid, null is returned instead.
626      *
627      * @param className the name of the class to be loaded.
628      * @return the class or null.
629      */

630     protected Class JavaDoc loadClass(final String JavaDoc className) {
631         if (className == null) {
632             return null;
633         }
634         if (className.startsWith("::")) {
635             return BasicTypeSupport.getClassRepresentation(className);
636         }
637         try {
638             return ObjectUtilities.getClassLoader(getClass()).loadClass(className);
639         }
640         catch (Exception JavaDoc e) {
641             // ignore buggy classes for now ..
642
Log.warn("Unable to load class", e);
643             return null;
644         }
645     }
646
647 }
648
Popular Tags