org.jfree.xml.util.AbstractModelReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcommon Show documentation
Show all versions of jcommon Show documentation
JCommon is a free general purpose Java class library that is used in
several projects at www.jfree.org, including JFreeChart and
JFreeReport.
/* ========================================================================
* JCommon : a free general purpose class library for the Java(tm) platform
* ========================================================================
*
* (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jcommon/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------------------
* AbstractModelReader.java
* ------------------------
* (C)opyright 2003-2005, by Thomas Morgner and Contributors.
*
* Original Author: Thomas Morgner;
* Contributor(s): David Gilbert (for Object Refinery Limited);
*
* $Id: AbstractModelReader.java,v 1.8 2005/10/18 13:33:53 mungady Exp $
*
* Changes
* -------
* 12-Nov-2003 : Initial version
* 25-Nov-2003 : Updated header (DG);
*
*/
package org.jfree.xml.util;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Stack;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.jfree.util.Log;
import org.jfree.util.ObjectUtilities;
import org.jfree.xml.CommentHandler;
import org.jfree.xml.ElementDefinitionException;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
* Loads the class model from an previously written xml file set.
* This class provides abstract methods which get called during the parsing
* (similiar to the SAX parsing, but slightly easier to code).
*
* This will need a rewrite in the future, when the structure is finished.
*/
public abstract class AbstractModelReader {
/** The 'START' state. */
private static final int STATE_START = 0;
/** The 'IN_OBJECT' state. */
private static final int IN_OBJECT = 1;
/** The 'IGNORE_OBJECT' state. */
private static final int IGNORE_OBJECT = 2;
/** The 'MAPPING' state. */
private static final int MAPPING_STATE = 3;
/** The 'CONSTRUCTOR' state. */
private static final int CONSTRUCTOR_STATE = 4;
/**
* The SAX2 callback implementation used for parsing the model xml files.
*/
private class SAXModelHandler extends DefaultHandler {
/** The resource URL. */
private URL resource;
/** The current state. */
private int state;
/** Open comments. */
private Stack openComments;
/** Flag to track includes. */
private boolean isInclude;
/**
* Creates a new SAX handler for parsing the model.
*
* @param resource the resource URL.
* @param isInclude an include?
*/
public SAXModelHandler(final URL resource, final boolean isInclude) {
if (resource == null) {
throw new NullPointerException();
}
this.resource = resource;
this.openComments = new Stack();
this.isInclude = isInclude;
}
/**
* Receive notification of the start of an element.
*
* @param uri The Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed.
* @param localName The local name (without prefix), or the
* empty string if Namespace processing is not being
* performed.
* @param qName The qualified name (with prefix), or the
* empty string if qualified names are not available.
* @param attributes The attributes attached to the element. If
* there are no attributes, it shall be an empty
* Attributes object.
* @exception SAXException Any SAX exception, possibly
* wrapping another exception.
*
* @see org.xml.sax.ContentHandler#startElement
*/
public void startElement(final String uri, final String localName,
final String qName, final Attributes attributes)
throws SAXException {
setOpenComment(getCommentHandler().getComments());
this.openComments.push(getOpenComment());
setCloseComment(null);
try {
if (!this.isInclude && qName.equals(ClassModelTags.OBJECTS_TAG)) {
//Log.debug ("Open Comments: " + openComment);
startRootDocument();
return;
}
if (getState() == STATE_START) {
startRootElement(qName, attributes);
}
else if (getState() == IGNORE_OBJECT) {
return;
}
else if (getState() == IN_OBJECT) {
startObjectElement(qName, attributes);
}
else if (getState() == MAPPING_STATE) {
if (!qName.equals(ClassModelTags.TYPE_TAG)) {
throw new SAXException("Expected 'type' tag");
}
final String name = attributes.getValue(ClassModelTags.NAME_ATTR);
final String target = attributes.getValue(ClassModelTags.CLASS_ATTR);
handleMultiplexMapping(name, target);
}
else if (getState() == CONSTRUCTOR_STATE) {
if (!qName.equals(ClassModelTags.PARAMETER_TAG)) {
throw new SAXException("Expected 'parameter' tag");
}
final String parameterClass = attributes.getValue(ClassModelTags.CLASS_ATTR);
final String tagName = attributes.getValue(ClassModelTags.PROPERTY_ATTR); // optional
handleConstructorDefinition(tagName, parameterClass);
}
}
catch (ObjectDescriptionException e) {
throw new SAXException(e);
}
finally {
getCommentHandler().clearComments();
}
}
/**
* Receive notification of the end of an element.
*
* @param uri The Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed.
* @param localName The local name (without prefix), or the
* empty string if Namespace processing is not being
* performed.
* @param qName The qualified name (with prefix), or the
* empty string if qualified names are not available.
* @exception SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#endElement
*/
public void endElement(final String uri, final String localName, final String qName)
throws SAXException {
setOpenComment((String[]) this.openComments.pop());
setCloseComment(getCommentHandler().getComments());
try {
if (!this.isInclude && qName.equals(ClassModelTags.OBJECTS_TAG)) {
endRootDocument();
return;
}
if (qName.equals(ClassModelTags.OBJECT_TAG)) {
if (getState() != IGNORE_OBJECT) {
endObjectDefinition();
}
setState(STATE_START);
}
else if (qName.equals(ClassModelTags.MAPPING_TAG)) {
setState(STATE_START);
endMultiplexMapping();
}
else if (qName.equals(ClassModelTags.CONSTRUCTOR_TAG)) {
if (getState() != IGNORE_OBJECT) {
setState(IN_OBJECT);
}
}
}
catch (ObjectDescriptionException e) {
throw new SAXException(e);
}
finally {
getCommentHandler().clearComments();
}
}
/**
* Handles the start of an element within an object definition.
*
* @param qName The qualified name (with prefix), or the
* empty string if qualified names are not available.
* @param attributes The attributes attached to the element. If
* there are no attributes, it shall be an empty
* Attributes object.
* @throws ObjectDescriptionException if an error occured while
* handling this tag
*/
private void startObjectElement(final String qName, final Attributes attributes)
throws ObjectDescriptionException {
if (qName.equals(ClassModelTags.CONSTRUCTOR_TAG)) {
setState(CONSTRUCTOR_STATE);
}
else if (qName.equals(ClassModelTags.LOOKUP_PROPERTY_TAG)) {
final String name = attributes.getValue(ClassModelTags.NAME_ATTR);
final String lookupKey = attributes.getValue(ClassModelTags.LOOKUP_ATTR);
handleLookupDefinition(name, lookupKey);
}
else if (qName.equals(ClassModelTags.IGNORED_PROPERTY_TAG)) {
final String name = attributes.getValue(ClassModelTags.NAME_ATTR);
handleIgnoredProperty(name);
}
else if (qName.equals(ClassModelTags.ELEMENT_PROPERTY_TAG)) {
final String elementAtt = attributes.getValue(ClassModelTags.ELEMENT_ATTR);
final String name = attributes.getValue(ClassModelTags.NAME_ATTR);
handleElementDefinition(name, elementAtt);
}
else if (qName.equals(ClassModelTags.ATTRIBUTE_PROPERTY_TAG)) {
final String name = attributes.getValue(ClassModelTags.NAME_ATTR);
final String attribName = attributes.getValue(ClassModelTags.ATTRIBUTE_ATTR);
final String handler = attributes.getValue(ClassModelTags.ATTRIBUTE_HANDLER_ATTR);
handleAttributeDefinition(name, attribName, handler);
}
}
/**
* Handles the include or object tag.
*
* @param qName The qualified name (with prefix), or the
* empty string if qualified names are not available.
* @param attributes The attributes attached to the element. If
* there are no attributes, it shall be an empty
* Attributes object.
* @throws SAXException if an parser error occured
* @throws ObjectDescriptionException if an object model related
* error occured.
*/
private void startRootElement(final String qName, final Attributes attributes)
throws SAXException, ObjectDescriptionException {
if (qName.equals(ClassModelTags.INCLUDE_TAG)) {
if (this.isInclude) {
Log.warn("Ignored nested include tag.");
return;
}
final String src = attributes.getValue(ClassModelTags.SOURCE_ATTR);
try {
final URL url = new URL(this.resource, src);
startIncludeHandling(url);
parseXmlDocument(url, true);
endIncludeHandling();
}
catch (Exception ioe) {
throw new ElementDefinitionException
(ioe, "Unable to include file from " + src);
}
}
else if (qName.equals(ClassModelTags.OBJECT_TAG)) {
setState(IN_OBJECT);
final String className = attributes.getValue(ClassModelTags.CLASS_ATTR);
String register = attributes.getValue(ClassModelTags.REGISTER_NAMES_ATTR);
if (register != null && register.length() == 0) {
register = null;
}
final boolean ignored = "true".equals(attributes.getValue(ClassModelTags.IGNORE_ATTR));
if (!startObjectDefinition(className, register, ignored)) {
setState(IGNORE_OBJECT);
}
}
else if (qName.equals(ClassModelTags.MANUAL_TAG)) {
final String className = attributes.getValue(ClassModelTags.CLASS_ATTR);
final String readHandler = attributes.getValue(ClassModelTags.READ_HANDLER_ATTR);
final String writeHandler = attributes.getValue(ClassModelTags.WRITE_HANDLER_ATTR);
handleManualMapping(className, readHandler, writeHandler);
}
else if (qName.equals(ClassModelTags.MAPPING_TAG)) {
setState(MAPPING_STATE);
final String typeAttr = attributes.getValue(ClassModelTags.TYPE_ATTR);
final String baseClass = attributes.getValue(ClassModelTags.BASE_CLASS_ATTR);
startMultiplexMapping(baseClass, typeAttr);
}
}
/**
* Returns the current state.
*
* @return the state.
*/
private int getState() {
return this.state;
}
/**
* Sets the current state.
*
* @param state the state.
*/
private void setState(final int state) {
this.state = state;
}
}
/** The comment handler. */
private CommentHandler commentHandler;
/** The close comments. */
private String[] closeComment;
/** The open comments. */
private String[] openComment;
/**
* Default Constructor.
*/
public AbstractModelReader() {
this.commentHandler = new CommentHandler();
}
/**
* Returns the comment handler.
*
* @return The comment handler.
*/
protected CommentHandler getCommentHandler() {
return this.commentHandler;
}
/**
* Returns the close comment.
*
* @return The close comment.
*/
protected String[] getCloseComment() {
return this.closeComment;
}
/**
* Returns the open comment.
*
* @return The open comment.
*/
protected String[] getOpenComment() {
return this.openComment;
}
/**
* Sets the close comment.
*
* @param closeComment the close comment.
*/
protected void setCloseComment(final String[] closeComment) {
this.closeComment = closeComment;
}
/**
* Sets the open comment.
*
* @param openComment the open comment.
*/
protected void setOpenComment(final String[] openComment) {
this.openComment = openComment;
}
/**
* Parses an XML document at the given URL.
*
* @param resource the document URL.
*
* @throws ObjectDescriptionException ??
*/
protected void parseXml(final URL resource) throws ObjectDescriptionException {
parseXmlDocument(resource, false);
}
/**
* Parses the given specification and loads all includes specified in the files.
* This implementation does not check for loops in the include files.
*
* @param resource the url of the xml specification.
* @param isInclude an include?
*
* @throws org.jfree.xml.util.ObjectDescriptionException if an error occured which prevented the
* loading of the specifications.
*/
protected void parseXmlDocument(final URL resource, final boolean isInclude)
throws ObjectDescriptionException {
try {
final InputStream in = new BufferedInputStream(resource.openStream());
final SAXParserFactory factory = SAXParserFactory.newInstance();
final SAXParser saxParser = factory.newSAXParser();
final XMLReader reader = saxParser.getXMLReader();
final SAXModelHandler handler = new SAXModelHandler(resource, isInclude);
try {
reader.setProperty
("http://xml.org/sax/properties/lexical-handler",
getCommentHandler());
}
catch (SAXException se) {
Log.debug("Comments are not supported by this SAX implementation.");
}
reader.setContentHandler(handler);
reader.setDTDHandler(handler);
reader.setErrorHandler(handler);
reader.parse(new InputSource(in));
in.close();
}
catch (Exception e) {
// unable to init
Log.warn("Unable to load factory specifications", e);
throw new ObjectDescriptionException("Unable to load object factory specs.", e);
}
}
/**
* Start the root document.
*/
protected void startRootDocument() {
// nothing required
}
/**
* End the root document.
*/
protected void endRootDocument() {
// nothing required
}
/**
* Start handling an include.
*
* @param resource the URL.
*/
protected void startIncludeHandling(final URL resource) {
// nothing required
}
/**
* End handling an include.
*/
protected void endIncludeHandling() {
// nothing required
}
/**
* Callback method for ignored properties. Such properties get marked so that
* the information regarding these properties won't get lost.
*
* @param name the name of the ignored property.
*/
protected void handleIgnoredProperty(final String name) {
// nothing required
}
/**
* Handles a manual mapping definition. The manual mapping maps specific
* read and write handlers to a given base class. Manual mappings always
* override any other definition.
*
* @param className the base class name
* @param readHandler the class name of the read handler
* @param writeHandler the class name of the write handler
* @return true, if the mapping was accepted, false otherwise.
* @throws ObjectDescriptionException if an unexpected error occured.
*/
protected abstract boolean handleManualMapping(String className, String readHandler,
String writeHandler) throws ObjectDescriptionException;
/**
* Starts a object definition. The object definition collects all properties of
* an bean-class and defines, which constructor should be used when creating the
* class.
*
* @param className the class name of the defined object
* @param register the (optional) register name, to lookup and reference the object
* later.
* @param ignored ??.
*
* @return true, if the definition was accepted, false otherwise.
* @throws ObjectDescriptionException if an unexpected error occured.
*/
protected abstract boolean startObjectDefinition(String className, String register,
boolean ignored) throws ObjectDescriptionException;
/**
* Handles an attribute definition. This method gets called after the object definition
* was started. The method will be called for every defined attribute property.
*
* @param name the name of the property
* @param attribName the xml-attribute name to use later.
* @param handlerClass the attribute handler class.
* @throws ObjectDescriptionException if an error occured.
*/
protected abstract void handleAttributeDefinition(String name, String attribName,
String handlerClass)
throws ObjectDescriptionException;
/**
* Handles an element definition. This method gets called after the object definition
* was started. The method will be called for every defined element property. Element
* properties are used to describe complex objects.
*
* @param name the name of the property
* @param element the xml-tag name for the child element.
* @throws ObjectDescriptionException if an error occurs.
*/
protected abstract void handleElementDefinition(String name, String element)
throws ObjectDescriptionException;
/**
* Handles an lookup definition. This method gets called after the object definition
* was started. The method will be called for every defined lookup property. Lookup properties
* reference previously created object using the object's registry name.
*
* @param name the property name of the base object
* @param lookupKey the register key of the referenced object
* @throws ObjectDescriptionException if an error occured.
*/
protected abstract void handleLookupDefinition(String name, String lookupKey)
throws ObjectDescriptionException;
/**
* Finializes the object definition.
*
* @throws ObjectDescriptionException if an error occures.
*/
protected abstract void endObjectDefinition() throws ObjectDescriptionException;
/**
* Starts a multiplex mapping. Multiplex mappings are used to define polymorphic
* argument handlers. The mapper will collect all derived classes of the given
* base class and will select the corresponding mapping based on the given type
* attribute.
*
* @param className the base class name
* @param typeAttr the xml-attribute name containing the mapping key
*/
protected abstract void startMultiplexMapping(String className, String typeAttr);
/**
* Defines an entry for the multiplex mapping. The new entry will be activated
* when the base mappers type attribute contains this typename
and
* will resolve to the handler for the given classname.
*
* @param typeName the type value for this mapping.
* @param className the class name to which this mapping resolves.
* @throws ObjectDescriptionException if an error occurs.
*/
protected abstract void handleMultiplexMapping(String typeName, String className)
throws ObjectDescriptionException;
/**
* Finializes the multiplexer mapping.
*
* @throws ObjectDescriptionException if an error occurs.
*/
protected abstract void endMultiplexMapping() throws ObjectDescriptionException;
/**
* Handles a constructor definition. Only one constructor can be defined for
* a certain object type. The constructor will be filled using the given properties.
*
* @param propertyName the property name of the referenced local property
* @param parameterClass the parameter class for the parameter.
* @throws ObjectDescriptionException if an error occured.
*/
protected abstract void handleConstructorDefinition(String propertyName, String parameterClass)
throws ObjectDescriptionException;
/**
* Loads the given class, and ignores all exceptions which may occur
* during the loading. If the class was invalid, null is returned instead.
*
* @param className the name of the class to be loaded.
* @return the class or null.
*/
protected Class loadClass(final String className) {
if (className == null) {
return null;
}
if (className.startsWith("::")) {
return BasicTypeSupport.getClassRepresentation(className);
}
try {
return ObjectUtilities.getClassLoader(getClass()).loadClass(className);
}
catch (Exception e) {
// ignore buggy classes for now ..
Log.warn("Unable to load class", e);
return null;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy