/* * AbstractWingTransformer.java * * Version: $Revision: 3705 $ * * Date: $Date: 2009-04-11 17:02:24 +0000 (Sat, 11 Apr 2009) $ * * Copyright (c) 2002, Hewlett-Packard Company and Massachusetts * Institute of Technology. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Hewlett-Packard Company nor the name of the * Massachusetts Institute of Technology nor the names of their * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.dspace.app.xmlui.wing; import java.util.Stack; import org.apache.cocoon.transformation.AbstractTransformer; import org.dspace.app.xmlui.wing.element.Body; import org.dspace.app.xmlui.wing.element.Options; import org.dspace.app.xmlui.wing.element.PageMeta; import org.dspace.app.xmlui.wing.element.UserMeta; import org.dspace.app.xmlui.wing.element.WingDocument; import org.dspace.app.xmlui.wing.element.WingMergeableElement; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.NamespaceSupport; /** * This class handles receiving SAX events and translating them into DRI events. * These DRI events are then routed to the individual implementing components * where they fill in and construct the DRI document. The document they * construct is known as the feeder document, this is merged into the main * document that was generated from the previous component in the Cocoon * pipeline. The merge takes place in accordance with the DRI schema's rules for * merging two DRI documents. * * * @author Scott Phillips */ public abstract class AbstractWingTransformer extends AbstractTransformer implements WingTransformer { /** * Simple variable to indicate weather a new namespace context is needed. If * several namespaces are declared on the same attribute then they are * considered in the same 'context'. Each time an element is opened this * flag is reset to true, and each time a new namespace is declared it is * set to false. Using this information new contexts are opened * conservatively. */ private boolean needNewNamespaceContext = true; /** * The namespace support object keeps track of registered URI prefixes. This * is used by the WingElements so that they may attach the correctp prefix * when assigning elements to namespaces. */ private NamespaceSupport namespaces; /** * The feeder document is the document being merged into the main, * pre-existing document, that is the result of the previous Cocoon * component in the pipeline. */ private WingDocument feederDocument; /** * The wing context is where the namespace support is stored along with the * content and lexical handlers so that the wing elements can have access to * them when they perform their toSAX() method. */ private WingContext wingContext; /** * This is a stack to the current location in the merge while it is in * progress. */ private Stack<WingMergeableElement> stack; /** * Set up the transformer so that it can build a feeder Wing document and * merge it into the main document * * FIXME: Update document: - this method must be called to initialize the * framework. It must be called after the component's setup has been called * and the implementing object setup. * */ public void setupWing() throws WingException { this.wingContext = new WingContext(); this.wingContext.setLogger(this.getLogger()); this.wingContext.setComponentName(this.getComponentName()); this.wingContext.setObjectManager(this.getObjectManager()); feederDocument = this.createWingDocument(wingContext); this.stack = new Stack<WingMergeableElement>(); } /** * Receive notification of the beginning of a document. */ public void startDocument() throws SAXException { needNewNamespaceContext = true; namespaces = new NamespaceSupport(); super.startDocument(); } /** * Receive notification of the end of a document. */ public void endDocument() throws SAXException { wingContext.dispose(); super.endDocument(); } /** * Begin the scope of a prefix-URI Namespace mapping. * * @param prefix * The Namespace prefix being declared. * @param uri * The Namespace URI the prefix is mapped to. */ public void startPrefixMapping(String prefix, String uri) throws SAXException { if (needNewNamespaceContext) { namespaces.pushContext(); needNewNamespaceContext = false; } namespaces.declarePrefix(prefix, uri); super.startPrefixMapping(prefix, uri); } /** * End the scope of a prefix-URI mapping. * * @param prefix * The prefix that was being mapping. */ public void endPrefixMapping(String prefix) throws SAXException { if (!needNewNamespaceContext) { namespaces.popContext(); needNewNamespaceContext = true; } super.endPrefixMapping(prefix); } /** * Receive notification of the beginning of an element. * * @param namespaceURI * 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 raw XML 1.0 name (with prefix), or the empty string if raw * names are not available. * @param attributes * The attributes attached to the element. If there are no * attributes, it shall be an empty Attributes object. */ public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) throws SAXException { // Reset the namespace context flag. needNewNamespaceContext = true; try { if (stack == null) { throw new WingException("Stack not initialized."); } // Deal with the stack jump start issue of having a document all // ready on the stack. if (stack.size() == 0) { if (feederDocument.mergeEqual(namespaceURI, localName, qName, attributes)) { attributes = feederDocument.merge(attributes); stack.push(feederDocument); } else { throw new WingException( "Attempting to merge DRI documents but the source document is not compatable with the feeder document."); } } else if (stack.size() > 0) { WingMergeableElement peek = stack.peek(); WingMergeableElement child = null; if (peek != null) { child = peek.mergeChild(namespaceURI, localName, qName, attributes); } // Check if we should construct a new portion of the document. if (child instanceof UserMeta) { // Create the UserMeta this.addUserMeta((UserMeta) child); } else if (child instanceof PageMeta) { // Create the PageMeta this.addPageMeta((PageMeta) child); } else if (child instanceof Body) { // Create the Body this.addBody((Body) child); } else if (child instanceof Options) { // Create the Options this.addOptions((Options) child); } // Update any attributes of this merged element. if (child != null) attributes = child.merge(attributes); stack.push(child); } // Send off the event with nothing modified except for the // attributes (possibly) super.startElement(namespaceURI, localName, qName, attributes); } catch (SAXException saxe) { throw saxe; } catch (Exception e) { handleException(e); } } /** * Receive notification of the end of an element. * * @param namespaceURI * 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 raw XML 1.0 name (with prefix), or the empty string if raw * names are not available. */ public void endElement(String namespaceURI, String localName, String qName) throws SAXException { try { if (stack.size() > 0) { WingMergeableElement poped = stack.pop(); if (poped != null) { poped.toSAX(contentHandler, lexicalHandler, namespaces); poped.dispose(); } } // Send the event on unmodified super.endElement(namespaceURI, localName, qName); } catch (SAXException saxe) { throw saxe; } catch (Exception e) { handleException(e); } } /** * Handle exceptions that occurred during the document's creation. When * errors occur a SAX event is being processed it will be sent through this * method. This allows implementing classes to override this method for * specific error handling hooks. * * @param e * The thrown exception */ protected void handleException(Exception e) throws SAXException { throw new SAXException( "An error was incountered while processing the Wing based component: " + this.getClass().getName(), e); } /** * Construct a new WingDocument. * * @param wingContext * The current wing context this transformer is operating under. */ protected WingDocument createWingDocument(WingContext wingContext) throws WingException { return new WingDocument(wingContext); } /** Abstract implementations of WingTransformer */ public void addBody(Body body) throws Exception { // Do nothing } public void addOptions(Options options) throws Exception { // do nothing } public void addUserMeta(UserMeta userMeta) throws Exception { // Do nothing } public void addPageMeta(PageMeta pageMeta) throws Exception { // Do nothing } /** * Return the ObjectManager associated with this component. If no * objectManager needed then return null. */ public ObjectManager getObjectManager() { return null; } /** * Return the name of this component. Typicaly the name is just * the class name of the component. */ public String getComponentName() { return this.getClass().getName(); } /** * Return the default i18n message catalogue that should be used * when no others are specified. */ public static String getDefaultMessageCatalogue() { return "default"; } /** * This is a short cut method for creating a new message object, this * allows them to be created with one simple method call that uses * the default catalogue. * * @param key * The catalogue key used to look up a message. * @return A new message object. */ public static Message message(String key) { return message(getDefaultMessageCatalogue(), key); } /** * This is a short cut method for creating a new message object. This * version allows the callie to specify a particular catalogue overriding * the default catalogue supplied. * * @param catalogue * The catalogue where translations will be located. * @param key * The catalogue key used to look up a translation within the * catalogue. * @return A new message object. */ public static Message message(String catalogue, String key) { return new Message(catalogue, key); } /** * Recyle */ public void recycle() { this.namespaces = null; this.feederDocument = null; this.wingContext=null; this.stack =null; super.recycle(); } /** * Dispose */ public void dispose() { this.namespaces = null; this.feederDocument = null; this.wingContext=null; this.stack =null; //super.dispose(); super dosn't dispose. } }