| /* gnu.java.beans.decoder.AbstractElementHandler | |
| Copyright (C) 2004 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.beans.decoder; | |
| import java.beans.ExceptionListener; | |
| import org.xml.sax.Attributes; | |
| /** ElementHandler manages a Context instance and interacts with | |
| * its parent and child handlers. | |
| * | |
| * @author Robert Schuster | |
| */ | |
| abstract class AbstractElementHandler implements ElementHandler | |
| { | |
| /** The Context instance of this handler. The instance is available after the startElement() | |
| * method was called. Otherwise the handler is marked as failed. | |
| */ | |
| private Context context; | |
| /** The parent handler. */ | |
| private ElementHandler parent; | |
| /** Stores whether this handler is marked as failed. */ | |
| private boolean hasFailed; | |
| /** Stores the character data which is contained in the body of the XML tag. */ | |
| private StringBuffer buffer = new StringBuffer(); | |
| /** Stores whether this ElementHandler can have subelements. The information for this is taken from | |
| * javabeans.dtd which can be found here: | |
| * <a href="http://java.sun.com/products/jfc/tsc/articles/persistence3/">Java Persistence Article</a> | |
| */ | |
| private boolean allowsSubelements; | |
| /** Creates a new ElementHandler with the given ElementHandler instance | |
| * as parent. | |
| * | |
| * @param parentHandler The parent handler. | |
| */ | |
| protected AbstractElementHandler(ElementHandler parentHandler, | |
| boolean allowsSubs) | |
| { | |
| parent = parentHandler; | |
| allowsSubelements = allowsSubs; | |
| } | |
| /** Evaluates the attributes and creates a Context instance. | |
| * If the creation of the Context instance fails the ElementHandler | |
| * is marked as failed which may affect the parent handler other. | |
| * | |
| * @param attributes Attributes of the XML tag. | |
| */ | |
| public final void start(Attributes attributes, | |
| ExceptionListener exceptionListener) | |
| { | |
| try | |
| { | |
| // lets the subclass create the appropriate Context instance | |
| context = startElement(attributes, exceptionListener); | |
| } | |
| catch (AssemblyException pe) | |
| { | |
| Throwable t = pe.getCause(); | |
| if (t instanceof Exception) | |
| exceptionListener.exceptionThrown((Exception) t); | |
| else | |
| throw new InternalError("Unexpected Throwable type in AssemblerException. Please file a bug report."); | |
| notifyContextFailed(); | |
| return; | |
| } | |
| } | |
| /** Analyses the content of the Attributes instance and creates a Context | |
| * object accordingly. | |
| * An AssemblerException is thrown when the Context instance could not | |
| * be created. | |
| * | |
| * @param attributes Attributes of the XML tag. | |
| * @return A Context instance. | |
| * @throws AssemblerException when Context instance could not be created. | |
| */ | |
| protected abstract Context startElement(Attributes attributes, ExceptionListener exceptionListener) | |
| throws AssemblyException; | |
| /** Post-processes the Context. | |
| */ | |
| public final void end(ExceptionListener exceptionListener) | |
| { | |
| // skips processing if the handler is marked as failed (because the Context | |
| // is then invalid or may not exist at all) | |
| if (!hasFailed) | |
| { | |
| try | |
| { | |
| // note: the order of operations is very important here | |
| // sends the stored character data to the Context | |
| endElement(buffer.toString()); | |
| // reports to the parent handler if this handler's Context is a | |
| // statement (returning no value BACK to the parent's Context) | |
| if (context.isStatement()) | |
| { | |
| // This may create a valid result in the parent's Context | |
| // or let it fail | |
| parent.notifyStatement(exceptionListener); | |
| // skips any further processing if the parent handler is now marked | |
| // as failed | |
| if (parent.hasFailed()) | |
| return; | |
| } | |
| // processes the Context and stores the result | |
| putObject(context.getId(), context.endContext(parent.getContext())); | |
| // transfers the Context's results to the parent's Context | |
| // if it is an expression (rather than a statement) | |
| if (! context.isStatement()) | |
| parent.getContext().addParameterObject(context.getResult()); | |
| } | |
| catch (AssemblyException pe) | |
| { | |
| // notifies that an exception was thrown in this handler's Context | |
| Throwable t = pe.getCause(); | |
| if (t instanceof Exception) | |
| exceptionListener.exceptionThrown((Exception) t); | |
| else | |
| throw (InternalError) new InternalError("Severe problem while decoding XML data.") | |
| .initCause(t); | |
| // marks the handler as failed | |
| notifyContextFailed(); | |
| } | |
| } | |
| } | |
| /** Notifies the handler's Context that its child Context will not return | |
| * a value back. Some Context variants need this information to know when | |
| * a method or a constructor call can be made. | |
| * | |
| * This method is called by a child handler. | |
| */ | |
| public void notifyStatement(ExceptionListener exceptionListener) | |
| { | |
| try | |
| { | |
| // propagates to parent handler first to generate objects | |
| // needed by this Context instance | |
| if(context.isStatement()) | |
| { | |
| parent.notifyStatement(exceptionListener); | |
| } | |
| // Some Context instances do stuff which can fail now. If that | |
| // happens this handler is marked as failed. | |
| context.notifyStatement(parent.getContext()); | |
| } | |
| catch (AssemblyException ae) | |
| { | |
| // notifies that an exception was thrown in this handler's Context | |
| Throwable t = ae.getCause(); | |
| if (t instanceof Exception) | |
| exceptionListener.exceptionThrown((Exception) t); | |
| else | |
| throw (InternalError) new InternalError("Severe problem while decoding XML data.") | |
| .initCause(t); | |
| // marks the handler as failed | |
| notifyContextFailed(); | |
| } | |
| } | |
| /** Marks this and any depending parent handlers as failed. Which means that on their end | |
| * no result is calculated. | |
| * | |
| * When a handler has failed no more handlers are accepted within it. | |
| */ | |
| public final void notifyContextFailed() | |
| { | |
| hasFailed = true; | |
| // marks the parent handler as failed if its Context | |
| // is affected by the failure of this handler's Context | |
| if (parent.getContext().subContextFailed()) | |
| parent.notifyContextFailed(); | |
| } | |
| /** Returns whether this handler has failed. | |
| * | |
| * This is used to skip child elements. | |
| * | |
| * @return Whether this handler has failed. | |
| */ | |
| public final boolean hasFailed() | |
| { | |
| return hasFailed; | |
| } | |
| /** Processes the character data when the element ends. | |
| * | |
| * The default implementation does nothing for convenience. | |
| * | |
| * @param characters | |
| * @throws AssemblerException | |
| */ | |
| protected void endElement(String characters) throws AssemblyException | |
| { | |
| // XXX: throw an exception when unexpected character data is available? | |
| } | |
| /** Adds characters from the body of the XML tag to the buffer. | |
| * | |
| * @param ch | |
| * @param start | |
| * @param length | |
| * @throws SAXException | |
| */ | |
| public final void characters(char[] ch, int start, int length) | |
| { | |
| // simply appends character data | |
| buffer.append(ch, start, length); | |
| } | |
| /** Stores an object globally under a unique id. If the id is | |
| * null the object is not stored. | |
| * | |
| * @param objectId | |
| * @param o | |
| */ | |
| public void putObject(String objectId, Object o) | |
| { | |
| if (objectId != null) | |
| parent.putObject(objectId, o); | |
| } | |
| /** Returns a previously stored object. If the id is null the | |
| * result is null, too. | |
| * | |
| * @param objectId | |
| * @return Returns a previously stored object or null. | |
| */ | |
| public Object getObject(String objectId) throws AssemblyException | |
| { | |
| return objectId == null ? null : parent.getObject(objectId); | |
| } | |
| /** Returns the Class instance as if called Class.forName() but | |
| * uses a ClassLoader given by the user. | |
| * | |
| * @param className | |
| * @return | |
| * @throws ClassNotFoundException | |
| */ | |
| public Class instantiateClass(String className) | |
| throws ClassNotFoundException | |
| { | |
| return parent.instantiateClass(className); | |
| } | |
| public final boolean isSubelementAllowed(String subElementName) | |
| { | |
| return allowsSubelements && ! subElementName.equals("java"); | |
| } | |
| public final Context getContext() | |
| { | |
| return context; | |
| } | |
| public final ElementHandler getParent() | |
| { | |
| return parent; | |
| } | |
| } |