| /* 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; |
| } |
| } |