You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Weber Dietmar EXT <Di...@icn.siemens.de> on 2003/10/02 16:50:18 UTC

XTTransformer

Hello,

Cocoon 2.1 does not contain org.apache.cocoon.transformation.XTTransformer any more. Unfortunately because it is fast and resource sparing.
So I added it to my local environment. It had to be modified because of the modified source resolving (excalibur).

It works with j2sdk1.4.2_01,tomcat4.1.27,cocoon2.1.2 both on Win2000 and Solaris5.8(Spark).

Greetings.

-------------------------------------------------------------------------

/*

 ============================================================================
                   The Apache Software License, Version 1.1
 ============================================================================

 Copyright (C) 1999-2002 The Apache Software Foundation. All rights reserved.

 Redistribution and use in source and binary forms, with or without modifica-
 tion, are permitted provided that the following conditions are met:

 1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

 2. 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.

 3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

 4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    apache@apache.org.

 5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
 APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
 INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
 DING, 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.

 This software  consists of voluntary contributions made  by many individuals
 on  behalf of the Apache Software  Foundation and was  originally created by
 Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
 Software Foundation, please see <http://www.apache.org/>.

*/
package org.apache.cocoon.transformation;

import com.jclark.xsl.om.Name;
import com.jclark.xsl.om.Node;
import com.jclark.xsl.om.XSLException;
import com.jclark.xsl.sax.*;
import com.jclark.xsl.tr.Engine;
import com.jclark.xsl.tr.EngineImpl;
import com.jclark.xsl.tr.ParameterSet;
import com.jclark.xsl.tr.Sheet;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.logger.Loggable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.Modifiable;
import org.apache.cocoon.ProcessingException;
// dwe: now excalibur! import org.apache.cocoon.components.store.Store;
import org.apache.excalibur.store.Store;
import org.apache.excalibur.source.Source;
//import org.apache.cocoon.environment.SourceResolver;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceUtil;

import org.apache.cocoon.xml.DocumentHandlerAdapter;
import org.apache.cocoon.xml.DocumentHandlerWrapper;
import org.apache.cocoon.xml.XMLConsumer;
import org.apache.log.Logger;
import org.xml.sax.*;
import org.xml.sax.ext.LexicalHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * This Transformer use the XT processor.
 *
 * @author <a href="mailto:ssahuc@imediation.com">Sahuc Sebastien</a>
 * @version CVS $Id: XTTransformer.java,v 1.9 2002/02/22 07:03:56 cziegeler Exp $
 */
public class XTTransformer extends DocumentHandlerWrapper
implements Transformer, Composable, Loggable, Disposable {
    private Logger log;

    /** The component manager */
    private ComponentManager manager = null;

    /** The store service instance */
    private Store store = null;

    /** The XT Processor */
    private XTProcessor processor = null;

    /**The DocumentHandler */
    private DocumentHandler docHandler = null;

    public void setLogger(Logger logger) {
        if (this.log == null) {
            this.log = logger;
        }
    }

    /**
     * Set the current <code>ComponentManager</code> instance used by this
     * <code>Composable</code>.
     */
    public void compose(ComponentManager manager) {
        this.manager = manager;

        try {
            this.store = (Store) manager.lookup(Store.TRANSIENT_STORE);
        } catch (Exception e) {
            log.error("Could not find component for role "
                      + Store.TRANSIENT_STORE, e);
        }
    }

    /**
     * Set the <code>SourceResolver</code>, the <code>Dictionary</code> with
     * the object model, the source and sitemap
     * <code>Parameters</code> used to process the request.
     */
    public void setup(org.apache.cocoon.environment.SourceResolver resolver, Map objectModel, String src, Parameters par)
    throws SAXException, ProcessingException, IOException {
        // Check the stylesheet uri
        String xsluri = src;
        if (xsluri == null) {
            throw new ProcessingException("Stylesheet URI can't be null");
        }

        // Get the stylesheet from the Store if available,
        // otherwise load it and put it into the store for further request
        XTProcessor loaderprocessor = null;

        if (store != null) {
            loaderprocessor = (XTProcessor) store.get(xsluri);
            if (loaderprocessor != null) {
                loaderprocessor.setLogger(this.log);
                loaderprocessor.compose(this.manager);
            }
        }

        // If not in the store or if style sheet has changed, loads and stores it
        if (loaderprocessor == null || loaderprocessor.hasChanged()) {
            loaderprocessor= new XTProcessor();
            loaderprocessor.setLogger(this.log);
            loaderprocessor.compose(this.manager);
            SAXParser saxParser = null;
            try {
                saxParser = SAXParserFactory.newInstance().newSAXParser();
            } catch (ParserConfigurationException e) {
                log.error("XTTransformer.setup", e);
                new ProcessingException(e.getMessage(),e);
            }
            loaderprocessor.setParser(saxParser.getParser());
            Source source = resolver.resolveURI(xsluri);// dwe: resolve -> resolveURI; InputSource -> Source
            try {
                loaderprocessor.loadStylesheet(source);// dwe: InputSource -> Source
                if (store != null) store.store(xsluri, loaderprocessor);
            } finally {
// dwe               source.recycle();// this method was used by the previous xttransformer but is no longer exists!
//              source.dispose();// dwe: api-doc lists this method but new excalibur lib does not contain it!
            }
        }

        // Always clone the processor before using it,
        // Indeed 1 instance per thread is allowed
        this.processor = (XTProcessor) loaderprocessor.clone();
        this.processor.setLogger(this.log);
        this.processor.compose(this.manager);

        // Create the processor and set it as this documenthandler
        // FIXME (SS): set the correct SystemId to the XML inputSource
        DocHandler temp = new DocHandler(processor.createBuilder("XTSystemID"));
        temp.setLogger(this.log);
        this.docHandler = temp;
        this.setDocumentHandler(this.docHandler);
    }

    /**
     * Set the <code>XMLConsumer</code> that will receive XML data.
     * <br>
     * This method will simply call <code>setContentHandler(consumer)</code>
     * and <code>setLexicalHandler(consumer)</code>.
     */
    public void setConsumer(XMLConsumer consumer) {
        this.setContentHandler(consumer);
    }

    /**
     * Set the <code>ContentHandler</code> that will receive XML data.
     * <br>
     * Subclasses may retrieve this <code>ContentHandler</code> instance
     * accessing the protected <code>super.contentHandler</code> field.
     */
    public void setContentHandler(ContentHandler content) {
        this.processor.setDocumentHandler(new DocumentHandlerAdapter(content));
    }

    /**
     * Set the <code>LexicalHandler</code> that will receive XML data.
     * <br>
     * Subclasses may retrieve this <code>LexicalHandler</code> instance
     * accessing the protected <code>super.lexicalHandler</code> field.
     *
     * @exception IllegalStateException If the <code>LexicalHandler</code> or
     *                                  the <code>XMLConsumer</code> were
     *                                  already set.
     */
    public void setLexicalHandler(LexicalHandler lexical) {
    }

    /**
    * inner class DocumentHandler that delegates all SAX Events to the XT's builder.
    */
    class DocHandler implements DocumentHandler, DTDHandler, Loggable {
        protected Logger log;

        /**
        * The XT's DocumentHandler instance to which SAX events are forwarded
        */
        private XMLProcessorImpl.Builder builder = null;

        /**
        * The Document Handler delivered.
        */
        public DocHandler(XMLProcessorImpl.Builder builder) {
            this.builder = builder;
        }

    public void setLogger(Logger logger) {
        if (this.log == null) {
            this.log = logger;
        }
    }

        public void setDocumentLocator (Locator locator) {
            builder.setDocumentLocator(locator);
        }

        public void ignorableWhitespace (char[] ch, int start, int length) throws SAXException {
            builder.ignorableWhitespace (ch, start, length);
        }

        public void processingInstruction (String target, String data) throws SAXException {
            builder.processingInstruction (target, data);
        }

        public void startDocument () throws SAXException {
            builder.startDocument();
        }

        public void endDocument () throws SAXException {
            builder.endDocument();

            try {
                // We've finished with the source document.
                // Start processing it by passing the builder
                processor.process(builder.getRootNode());
            } catch (IOException ioe) {
                log.error("XTTransformer", ioe);
                throw new SAXException(ioe);
            }

        }

        public void startElement (String name, AttributeList atts) throws SAXException {
            builder.startElement (name, atts);
        }

        public void endElement (String name) throws SAXException {
            builder.endElement (name);
        }

        public void characters (char[] str, int index, int len) throws SAXException {
            builder.characters ( str, index, len);
        }

        public void notationDecl (String name, String publicId, String systemId) throws SAXException {
            builder.notationDecl (name, publicId, systemId);
        }

        public void unparsedEntityDecl (String name, String publicId, String systemId, String notationName) throws SAXException {
            builder.unparsedEntityDecl (name, publicId, systemId, notationName);
        }
    }

    /**
    * dispose
    */
    public void dispose()
    {
        this.manager.release(this.store);
    }
}

 /**
  * The XT processor.
  */

class XTProcessor implements Cloneable, ParameterSet, Modifiable, Loggable, Composable {

    protected Logger log;
    private XMLProcessorEx sheetLoader;
    private Parser sheetParser;
    private Sheet sheet;
    private Engine engine;
    private InputSource sheetSource;
    private ResultBase result;
    private DocumentHandler documentHandler;
    private ErrorHandler errorHandler;
    private HashMap params = new HashMap();
    private File xslFile;
    private long lastModified;
    private ComponentManager manager;

    public void setLogger(Logger logger) {
        if (this.log == null) {
            this.log = logger;
        }
    }

    public void compose(ComponentManager manager) {
        this.manager = manager;
    }

    /**
    * set the Parser that will parse the XSL style sheet
    */
    public void setParser(Parser sheetParser) {
        this.sheetParser = sheetParser;
        sheetLoader = new XMLProcessorImpl(sheetParser);
    }

    /**
    * set the DocumentHandler (Consumer) to which the XT Porcessor will
    * fire the result SAX events.
    */
    public void setDocumentHandler(DocumentHandler handler) {
        documentHandler = handler;
    }

    /**
    * set the ErrorHandler
    */
    public void setErrorHandler(ErrorHandler handler) {
        if (sheetParser != null)
            sheetParser.setErrorHandler(handler);
        if (sheetLoader != null)
            sheetLoader.setErrorHandler(handler);
        this.errorHandler = handler;
    }

    /**
    * Load the style sheet given its <code>InputSource</code>
    * Should be called after the setParser() and before the process()
    */
    public void loadStylesheet(Source source)// dwe: InputSource -> Source
      throws SAXException, IOException {
        // Set the xslFile for the caching mechanism
        URL url = null;
        InputSource sheetSource = null;//dwe: the former parameter; There is a field with the same name !
        SourceResolver resolver = null;
        try {
              resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);

//dwe patch
              this.xslFile = SourceUtil.getFile(source);//dwe
              sheetSource = new InputSource(source.getInputStream());
              sheetSource.setEncoding("iso-8859-1");// @TODO dwe: get from environment/configuration
//            url = SourceResolver.getURL(sheetSource.getSystemId());
//end dwe patch

        } catch (Exception e) {
            log.error("cannot obtain the SourceResolver", e);
            throw new SAXException ("cannot obtain the SourceResolver", e);
        } finally {
            if ( null != resolver ) {
//                resolver.release(configSource);
                manager.release(resolver);
            }
        }
//dwe already set       this.xslFile = new File(url.getFile());
        lastModified = xslFile.lastModified();

        // XT internal
        engine = new EngineImpl(sheetLoader, new ExtensionHandlerImpl());
        try {
            Node node = sheetLoader.load(sheetSource,
                       0,
                       engine.getSheetLoadContext(),
                       engine.getNameTable());
            sheet = engine.createSheet(node);
        } catch (XSLException e) {
            handleXSLException(e);
        }
    }

    /**
    * Create the XT's Builder which implements a DocumentHandler
    */
    public XMLProcessorImpl.Builder createBuilder (String systemId) {
        XMLProcessorImpl.Builder builder
                    = XMLProcessorImpl.createBuilder ( systemId, 0, sheet.getSourceLoadContext(),engine.getNameTable());
        return builder;
    }

    /**
    * Applies the Style sheet to the source root node.
    * the rootNode are taken from the builder retrieved with createBuilder() method,
    * and by calling on the builder object the method getRootNode() after the SAX events
    * have fed the builder.
    */
    public void process(Node root) throws SAXException, IOException {
        try {
            result = new MultiNamespaceResult(documentHandler, errorHandler);
            sheet.process(root, sheetLoader, this, result);
        } catch (XSLException e) {
            handleXSLException(e);
        }
    }

    void handleXSLException(XSLException e) throws SAXException, IOException {
        log.error("XTTransformer", e);
        String systemId = null;
        int lineNumber = -1;
        Node node = e.getNode();
        if (node != null) {
            URL url = node.getURL();
            if (url != null)
                systemId = url.toString();
            lineNumber = node.getLineNumber();
        }
        Exception wrapped = e.getException();
        String message = e.getMessage();
        if (systemId != null || lineNumber != -1)
            throw new SAXParseException(message,
                                        null,
                                        systemId,
                                        lineNumber,
                                        -1,
                                        wrapped);
        if (message == null) {
            if (wrapped instanceof SAXException)
                throw (SAXException)wrapped;
            if (wrapped instanceof IOException)
                throw (IOException)wrapped;
        }
        throw new SAXException(message, wrapped);
    }

    public Object clone() {
        try {
            XTProcessor cloned = (XTProcessor) super.clone();
            cloned.setLogger(this.log);
            cloned.params = (HashMap) cloned.params.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            log.error("unexpected CloneNotSupportedException", e);
            throw new Error("unexpected CloneNotSupportedException");
        }
    }

    public Object getParameter(Name name) {
        String nameString = name.getNamespace();
        if (nameString == null)
            nameString = name.getLocalPart();
        else
            nameString = (nameString
            + OutputMethodHandler.namespaceSeparator
            + name.getLocalPart());
        return params.get(nameString);
    }

   /**
   * Set XSL parameters
   */
    public void setParameter(String name, Object obj) {
        params.put(name, obj);
    }

    /**
    * implements interface <code>Modifiable</code>
    */
    public boolean modifiedSince(long date) {
        return(date < this.xslFile.lastModified());
    }

    /**
    * Checks if the style sheet file have changed since the construction
    * of <code>this</code> instance.
    */
    public boolean hasChanged() {
        if (this.xslFile == null) {
            return false;
        }
        return modifiedSince(this.lastModified);
    }
}