You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by cz...@apache.org on 2007/02/26 14:35:31 UTC
svn commit: r511800 - in
/cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt:
XSLTProcessor.java XSLTProcessorErrorListener.java
XSLTProcessorException.java XSLTProcessorImpl.java
Author: cziegeler
Date: Mon Feb 26 05:35:28 2007
New Revision: 511800
URL: http://svn.apache.org/viewvc?view=rev&rev=511800
Log:
First version of an xslt processor bean
Added:
cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java (with props)
cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java (with props)
cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java (with props)
cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java (with props)
Added: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java?view=auto&rev=511800
==============================================================================
--- cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java (added)
+++ cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java Mon Feb 26 05:35:28 2007
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.xslt;
+
+import java.util.Map;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceValidity;
+import org.xml.sax.XMLFilter;
+
+/**
+ * This is the interface of the XSLT processor.
+ *
+ * @version $Id$
+ */
+public interface XSLTProcessor {
+
+ public static class TransformerHandlerAndValidity {
+
+ private final TransformerHandler transformerHandler;
+ private final SourceValidity transformerValidity;
+
+ protected TransformerHandlerAndValidity( final TransformerHandler transformerHandler,
+ final SourceValidity transformerValidity ) {
+ this.transformerHandler = transformerHandler;
+ this.transformerValidity = transformerValidity;
+ }
+
+ public TransformerHandler getTransfomerHandler() {
+ return transformerHandler;
+ }
+
+ public SourceValidity getTransfomerValidity() {
+ return transformerValidity;
+ }
+ }
+
+ /**
+ * <p>Return a <code>TransformerHandler</code> for a given
+ * stylesheet {@link Source}. This can be used in a pipeline to
+ * handle the transformation of a stream of SAX events. See {@link
+ * org.apache.cocoon.transformation.TraxTransformer#setConsumer} for
+ * an example of how to use this method.
+ *
+ * <p>The additional <code>filter</code> argument, if it's not
+ * <code>null</code>, is inserted in the chain SAX events as an XML
+ * filter during the parsing or the source document.
+ *
+ * <p>This method caches the Templates object with meta information
+ * (modification time and list of included stylesheets) and performs
+ * a reparsing only if this changes.
+ *
+ * @param stylesheet a {@link Source} value
+ * @param filter a {@link XMLFilter} value
+ * @return a {@link TransformerHandler} value
+ * @exception XSLTProcessorException if an error occurs
+ */
+ TransformerHandler getTransformerHandler( Source stylesheet, XMLFilter filter )
+ throws XSLTProcessorException;
+
+ /**
+ * <p>Return a {@link TransformerHandler} and
+ * <code>SourceValidity</code> for a given stylesheet
+ * {@link Source}. This can be used in a pipeline to
+ * handle the transformation of a stream of SAX events. See {@link
+ * org.apache.cocoon.transformation.TraxTransformer#setConsumer} for
+ * an example of how to use this method.
+ *
+ * <p>The additional <code>filter</code> argument, if it's not
+ * <code>null</code>, is inserted in the chain SAX events as an XML
+ * filter during the parsing or the source document.
+ *
+ * <p>This method caches the Templates object with meta information
+ * (modification time and list of included stylesheets) and performs
+ * a reparsing only if this changes.
+ *
+ * @param stylesheet a {@link Source} value
+ * @param filter a {@link XMLFilter} value
+ * @return a <code>TransformerHandlerAndValidity</code> value
+ * @exception XSLTProcessorException if an error occurs
+ */
+ TransformerHandlerAndValidity getTransformerHandlerAndValidity( Source stylesheet, XMLFilter filter )
+ throws XSLTProcessorException;
+
+ /**
+ * Same as {@link #getTransformerHandler(Source,XMLFilter)}, with
+ * <code>filter</code> set to <code>null</code>.
+ *
+ * @param stylesheet a {@link Source} value
+ * @return a {@link TransformerHandler} value
+ * @exception XSLTProcessorException if an error occurs
+ */
+ TransformerHandler getTransformerHandler( Source stylesheet )
+ throws XSLTProcessorException;
+
+ /**
+ * Same as {@link #getTransformerHandlerAndValidity(Source,XMLFilter)}, with
+ * <code>filter</code> set to <code>null</code>.
+ *
+ * @param stylesheet a {@link Source} value
+ * @return a {@link TransformerHandlerAndValidity} value
+ * @exception XSLTProcessorException if an error occurs
+ */
+ TransformerHandlerAndValidity getTransformerHandlerAndValidity( Source stylesheet )
+ throws XSLTProcessorException;
+
+ /**
+ * Applies an XSLT stylesheet to an XML document. The source and
+ * stylesheet documents are specified as {@link Source}
+ * objects. The result of the transformation is placed in
+ * {@link Result}, which should be properly initialized before
+ * invoking this method. Any additional parameters passed in
+ * {@link Map params} will become arguments to the stylesheet.
+ *
+ * @param source a {@link Source} value
+ * @param stylesheet a {@link Source} value
+ * @param params a <code>Map</code>
+ * @param result a <code>Result</code> value
+ * @exception XSLTProcessorException if an error occurs
+ */
+ void transform( Source source, Source stylesheet, Map params, Result result )
+ throws XSLTProcessorException;
+}
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessor.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java?view=auto&rev=511800
==============================================================================
--- cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java (added)
+++ cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java Mon Feb 26 05:35:28 2007
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.xslt;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+import org.apache.cocoon.util.location.Location;
+import org.apache.cocoon.util.location.LocationUtils;
+import org.apache.commons.logging.Log;
+
+/**
+ * A smart error listener for <code>javax.xml.tranform</code> that does its best to provide
+ * useful error messages.
+ *
+ * @version $Id$
+ */
+public class XSLTProcessorErrorListener implements ErrorListener{
+
+ private final Log logger;
+ private final String uri;
+
+ /** The exception we had from warning() */
+ private TransformerException warningEx;
+
+ /** The exception we had from error() or fatalError() */
+ private TransformerException exception;
+
+ public XSLTProcessorErrorListener(Log logger, String uri) {
+ this.logger = logger;
+ this.uri = uri;
+ }
+
+ /**
+ * Get the exception that was catched by this listener, if any.
+ *
+ * @return the exception
+ */
+ public Throwable getThrowable() {
+ if (exception == null) {
+ return null;
+ }
+
+ Location loc = LocationUtils.getLocation(exception);
+ if (LocationUtils.isKnown(loc)) {
+ // Has a location: don't loose this precious information!
+ return exception;
+ }
+
+ // No location: if it's just a wrapper, consider only the wrapped exception
+ if (exception.getCause() != null) {
+ return exception.getCause();
+ }
+
+ // That's the actual exception!
+ return exception;
+ }
+
+ public void warning(TransformerException ex) throws TransformerException {
+ // TODO: We may want here to allow some special formatting of the messages, such as
+ // "DEBUG:A debug message" or "INFO:Transforming <foo> in mode 'bar'" to use the different
+ // log levels. This can include also deprecation logs for system-defined stylesheets
+ // using "DEPRECATED:WARN:Styling 'foo' is replaced by 'bar'".
+
+ if (logger.isWarnEnabled()) {
+ Location loc = LocationUtils.getLocation(ex);
+ logger.warn(ex.getMessage() + " at "+ loc == null ? uri : loc.toString());
+ }
+ // Keep the warning (see below)
+ warningEx = ex;
+ }
+
+ public void error(TransformerException ex) throws TransformerException {
+
+ // If we had a warning previoulsy, and the current exception has no cause, then use the warning.
+ // This is how Xalan behaves on <xsl:message terminate="yes">: it first issues a warning with all
+ // the useful information, then a useless "stylesheed directed termination" error.
+ if (warningEx != null && ex.getCause() == null) {
+ ex = warningEx;
+ }
+ warningEx = null;
+
+ // Keep the exception for later use.
+ exception = ex;
+ // and rethrow it
+ throw ex;
+ }
+
+ public void fatalError(TransformerException ex) throws TransformerException {
+ if (warningEx != null && ex.getCause() == null) {
+ ex = warningEx;
+ }
+ warningEx = null;
+
+ exception = ex;
+ throw ex;
+ }
+}
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorErrorListener.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java?view=auto&rev=511800
==============================================================================
--- cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java (added)
+++ cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java Mon Feb 26 05:35:28 2007
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.xslt;
+
+/**
+ * This exception is thrown by the XSLTProcessor. It will wrap any exceptions
+ * thrown throughout the processing process.
+ *
+ * @version $Id$
+ */
+public class XSLTProcessorException
+ extends Exception {
+
+ public XSLTProcessorException( final String message ) {
+ super( message );
+ }
+
+ public XSLTProcessorException( final String message,
+ final Throwable throwable ) {
+ super( message, throwable );
+ }
+}
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorException.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java?view=auto&rev=511800
==============================================================================
--- cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java (added)
+++ cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java Mon Feb 26 05:35:28 2007
@@ -0,0 +1,604 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.xslt;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TemplatesHandler;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.components.source.util.SourceUtil;
+import org.apache.cocoon.core.xml.SAXParser;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.impl.validity.AggregatedValidity;
+import org.apache.excalibur.store.Store;
+import org.apache.excalibur.xml.sax.XMLizable;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+
+/**
+ * Adaptation of Excalibur's XSLTProcessor implementation to allow for better
+ * error reporting. This is a bean implementation that can be configured in
+ * a spring context.
+ *
+ * @version $Id$
+ */
+public class XSLTProcessorImpl
+ extends AbstractLogEnabled
+ implements XSLTProcessor, URIResolver {
+
+ /** The store service instance */
+ protected Store store;
+
+ /** The configured transformer factory to use */
+ protected String transformerFactory;
+
+ /** The trax TransformerFactory this component uses */
+ protected SAXTransformerFactory factory;
+
+ /** Is incremental processing turned on? (default for Xalan: no) */
+ protected boolean incrementalProcessing;
+
+ /** Resolver used to resolve XSLT document() calls, imports and includes */
+ protected SourceResolver resolver;
+
+ /** Check included stylesheets */
+ protected boolean checkIncludes;
+
+ /** Map of pairs of System ID's / validities of the included stylesheets */
+ protected Map includesMap = new HashMap();
+
+ protected SAXParser saxParser;
+
+ /**
+ * Initialize this component.
+ */
+ public void init() throws Exception {
+ this.factory = getTransformerFactory(this.transformerFactory);
+ }
+
+ public void setStore(Store store) {
+ this.store = store;
+ }
+
+ public void setIncrementalProcessing(boolean incrementalProcessing) {
+ this.incrementalProcessing = incrementalProcessing;
+ }
+
+ public void setResolver(SourceResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public void setCheckIncludes(boolean checkIncludes) {
+ this.checkIncludes = checkIncludes;
+ }
+
+ public void setSaxParser(SAXParser saxParser) {
+ this.saxParser = saxParser;
+ }
+
+ /**
+ * Set the transformer factory used by this component
+ */
+ public void setTransformerFactory(final String classname) {
+ this.transformerFactory = classname;
+ }
+
+ /**
+ * @see org.apache.excalibur.xml.xslt.XSLTProcessor#getTransformerHandler(org.apache.excalibur.source.Source)
+ */
+ public TransformerHandler getTransformerHandler(final Source stylesheet) throws XSLTProcessorException {
+ return getTransformerHandler(stylesheet, null);
+ }
+
+ /**
+ * @see org.apache.excalibur.xml.xslt.XSLTProcessor#getTransformerHandler(org.apache.excalibur.source.Source,
+ * org.xml.sax.XMLFilter)
+ */
+ public TransformerHandler getTransformerHandler(final Source stylesheet, final XMLFilter filter) throws XSLTProcessorException {
+ final XSLTProcessor.TransformerHandlerAndValidity validity = getTransformerHandlerAndValidity(stylesheet, filter);
+ return validity.getTransfomerHandler();
+ }
+
+ /**
+ * @see org.apache.excalibur.xml.xslt.XSLTProcessor#getTransformerHandlerAndValidity(org.apache.excalibur.source.Source)
+ */
+ public TransformerHandlerAndValidity getTransformerHandlerAndValidity(final Source stylesheet) throws XSLTProcessorException {
+ return getTransformerHandlerAndValidity(stylesheet, null);
+ }
+
+ /**
+ * @see org.apache.excalibur.xml.xslt.XSLTProcessor#getTransformerHandlerAndValidity(org.apache.excalibur.source.Source, org.xml.sax.XMLFilter)
+ */
+ public TransformerHandlerAndValidity getTransformerHandlerAndValidity(Source stylesheet, XMLFilter filter) throws XSLTProcessorException {
+
+ final String id = stylesheet.getURI();
+ TransformerHandlerAndValidity handlerAndValidity;
+
+ try {
+ handlerAndValidity = getTemplates(stylesheet, id);
+ if (handlerAndValidity != null) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Reusing Templates for " + id);
+ }
+ return handlerAndValidity;
+ }
+ } catch(Exception e) {
+ throw new XSLTProcessorException("Error retrieving template", e);
+ }
+
+ XSLTProcessorErrorListener errorListener = new XSLTProcessorErrorListener(getLogger(), stylesheet.getURI());
+ try{
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Creating new Templates for " + id);
+ }
+
+ factory.setErrorListener(errorListener);
+
+ // Create a Templates ContentHandler to handle parsing of the
+ // stylesheet.
+ TemplatesHandler templatesHandler = factory.newTemplatesHandler();
+
+ // Set the system ID for the template handler since some
+ // TrAX implementations (XSLTC) rely on this in order to obtain
+ // a meaningful identifier for the Templates instances.
+ templatesHandler.setSystemId(id);
+ if (filter != null) {
+ filter.setContentHandler(templatesHandler);
+ }
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Source = " + stylesheet + ", templatesHandler = " + templatesHandler);
+ }
+
+ // Initialize List for included validities
+ SourceValidity validity = stylesheet.getValidity();
+ if (validity != null && checkIncludes) {
+ includesMap.put(id, new ArrayList());
+ }
+
+ try {
+ // Process the stylesheet.
+ sourceToSAX(stylesheet, filter != null ? (ContentHandler) filter : (ContentHandler) templatesHandler);
+
+ // Get the Templates object (generated during the parsing of
+ // the stylesheet) from the TemplatesHandler.
+ final Templates template = templatesHandler.getTemplates();
+
+ if (null == template) {
+ throw new XSLTProcessorException("Unable to create templates for stylesheet: " + stylesheet.getURI());
+ }
+
+ // Must set base for Xalan stylesheet.
+ // Otherwise document('') in logicsheet causes NPE.
+ Class clazz = template.getClass();
+ if (clazz.getName().equals("org.apache.xalan.templates.StylesheetRoot")) {
+ Method method = clazz.getMethod("setHref", new Class[]{String.class});
+ method.invoke(template, new Object[]{id});
+ }
+
+ putTemplates(template, stylesheet, id);
+
+ // Create transformer handler
+ final TransformerHandler handler = factory.newTransformerHandler(template);
+ handler.getTransformer().setErrorListener(new XSLTProcessorErrorListener(getLogger(), stylesheet.getURI()));
+ handler.getTransformer().setURIResolver(this);
+
+ // Create aggregated validity
+ AggregatedValidity aggregated = null;
+ if (validity != null && checkIncludes) {
+ List includes = (List) includesMap.get(id);
+ if (includes != null) {
+ aggregated = new AggregatedValidity();
+ aggregated.add(validity);
+ for (int i = includes.size() - 1; i >= 0; i--) {
+ aggregated.add((SourceValidity) ((Object[]) includes.get(i))[1]);
+ }
+ validity = aggregated;
+ }
+ }
+
+ // Create result
+ handlerAndValidity = new MyTransformerHandlerAndValidity(handler, validity);
+ } finally {
+ if (checkIncludes)
+ includesMap.remove(id);
+ }
+
+ return handlerAndValidity;
+ } catch (Exception e) {
+ Throwable realEx = errorListener.getThrowable();
+ if (realEx == null) realEx = e;
+
+ if (realEx instanceof RuntimeException) {
+ throw (RuntimeException)realEx;
+ }
+
+ if (realEx instanceof XSLTProcessorException) {
+ throw (XSLTProcessorException)realEx;
+ }
+
+ throw new XSLTProcessorException("Exception when creating Transformer from " + stylesheet.getURI(), realEx);
+ }
+ }
+
+ private void sourceToSAX(Source source, ContentHandler handler)
+ throws SAXException, IOException, SourceException, ProcessingException {
+ if (source instanceof XMLizable) {
+ ((XMLizable) source).toSAX(handler);
+ } else {
+ this.saxParser.parse(SourceUtil.getInputSource(source), handler);
+ }
+ }
+
+ /**
+ * @see org.apache.cocoon.components.xslt.XSLTProcessor#transform(org.apache.excalibur.source.Source, org.apache.excalibur.source.Source, java.util.Map, javax.xml.transform.Result)
+ */
+ public void transform(final Source source, final Source stylesheet, final Map params, final Result result) throws XSLTProcessorException {
+ try {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug(
+ "Transform source = " + source + ", stylesheet = " + stylesheet + ", parameters = " + params + ", result = " + result);
+ }
+ final TransformerHandler handler = getTransformerHandler(stylesheet);
+ if (params != null) {
+ final Transformer transformer = handler.getTransformer();
+ transformer.clearParameters();
+ final Iterator i = params.entrySet().iterator();
+ while ( i.hasNext() ) {
+ final Map.Entry current = (Map.Entry)i.next();
+ transformer.setParameter(current.getKey().toString(), current.getValue());
+ }
+ }
+
+ handler.setResult(result);
+ sourceToSAX(source, handler);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Transform done");
+ }
+ } catch (SAXException e) {
+ // Unwrapping the exception will "remove" the real cause with
+ // never Xalan versions and makes the exception message unusable
+ final String message = "Error in running Transformation";
+ throw new XSLTProcessorException(message, e);
+ /*
+ * if( e.getException() == null ) { final String message = "Error in
+ * running Transformation"; throw new XSLTProcessorException(
+ * message, e ); } else { final String message = "Got SAXException.
+ * Rethrowing cause exception."; getLogger().debug( message, e );
+ * throw new XSLTProcessorException( "Error in running
+ * Transformation", e.getException() ); }
+ */
+ } catch (Exception e) {
+ final String message = "Error in running Transformation";
+ throw new XSLTProcessorException(message, e);
+ }
+ }
+
+ /**
+ * Get the TransformerFactory associated with the given classname. If the
+ * class can't be found or the given class doesn't implement the required
+ * interface, the default factory is returned.
+ */
+ private SAXTransformerFactory getTransformerFactory(String factoryName) {
+ SAXTransformerFactory _factory;
+
+ if (null == factoryName) {
+ _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+ } else {
+ try {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null) {
+ loader = getClass().getClassLoader();
+ }
+ _factory = (SAXTransformerFactory) loader.loadClass(factoryName).newInstance();
+ } catch (ClassNotFoundException cnfe) {
+ getLogger().error("Cannot find the requested TrAX factory '" + factoryName + "'. Using default TrAX Transformer Factory instead.");
+ if (factory != null)
+ return factory;
+ _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+ } catch (ClassCastException cce) {
+ getLogger().error(
+ "The indicated class '" + factoryName
+ + "' is not a TrAX Transformer Factory. Using default TrAX Transformer Factory instead.");
+ if (factory != null)
+ return factory;
+ _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+ } catch (Exception e) {
+ getLogger().error(
+ "Error found loading the requested TrAX Transformer Factory '" + factoryName
+ + "'. Using default TrAX Transformer Factory instead.");
+ if (factory != null)
+ return factory;
+ _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+ }
+ }
+
+ _factory.setErrorListener(new XSLTProcessorErrorListener(getLogger(), null));
+ _factory.setURIResolver(this);
+
+ // FIXME (SM): implementation-specific parameter passing should be
+ // made more extensible.
+ if (_factory.getClass().getName().equals("org.apache.xalan.processor.TransformerFactoryImpl")) {
+ _factory.setAttribute("http://xml.apache.org/xalan/features/incremental", Boolean.valueOf(incrementalProcessing));
+ }
+ // SAXON 8 will not report errors unless version warning is set to false.
+ if (_factory.getClass().getName().equals("net.sf.saxon.TransformerFactoryImpl")) {
+ _factory.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE);
+ }
+
+ return _factory;
+ }
+
+ private TransformerHandlerAndValidity getTemplates(Source stylesheet, String id)
+ throws IOException, TransformerException {
+ if (this.store == null) {
+ return null;
+ }
+
+ // we must augment the template ID with the factory classname since one
+ // transformer implementation cannot handle the instances of a
+ // template created by another one.
+ String key = "XSLTTemplate: " + id + '(' + factory.getClass().getName() + ')';
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("getTemplates: stylesheet " + id);
+ }
+
+ SourceValidity newValidity = stylesheet.getValidity();
+
+ // Only stylesheets with validity are stored
+ if (newValidity == null) {
+ // Remove an old template
+ store.remove(key);
+ return null;
+ }
+
+ // Stored is an array of the templates and the caching time and list of
+ // includes
+ Object[] templateAndValidityAndIncludes = (Object[]) store.get(key);
+ if (templateAndValidityAndIncludes == null) {
+ // Templates not found in cache
+ return null;
+ }
+
+ // Check template modification time
+ SourceValidity storedValidity = (SourceValidity) templateAndValidityAndIncludes[1];
+ int valid = storedValidity.isValid();
+ boolean isValid;
+ if (valid == 0) {
+ valid = storedValidity.isValid(newValidity);
+ isValid = (valid == 1);
+ } else {
+ isValid = (valid == 1);
+ }
+ if (!isValid) {
+ store.remove(key);
+ return null;
+ }
+
+ // Check includes
+ if (checkIncludes) {
+ AggregatedValidity aggregated = null;
+ List includes = (List) templateAndValidityAndIncludes[2];
+ if (includes != null) {
+ aggregated = new AggregatedValidity();
+ aggregated.add(storedValidity);
+
+ for (int i = includes.size() - 1; i >= 0; i--) {
+ // Every include stored as pair of source ID and validity
+ Object[] pair = (Object[]) includes.get(i);
+ storedValidity = (SourceValidity) pair[1];
+ aggregated.add(storedValidity);
+
+ valid = storedValidity.isValid();
+ isValid = false;
+ if (valid == 0) {
+ Source includedSource = null;
+ try {
+ includedSource = resolver.resolveURI((String) pair[0]);
+ SourceValidity included = includedSource.getValidity();
+ if (included != null) {
+ valid = storedValidity.isValid(included);
+ isValid = (valid == 1);
+ }
+ } finally {
+ resolver.release(includedSource);
+ }
+ } else {
+ isValid = (valid == 1);
+ }
+ if (!isValid) {
+ store.remove(key);
+ return null;
+ }
+ }
+ storedValidity = aggregated;
+ }
+ }
+
+ TransformerHandler handler = factory.newTransformerHandler((Templates) templateAndValidityAndIncludes[0]);
+ handler.getTransformer().setErrorListener(new XSLTProcessorErrorListener(getLogger(), stylesheet.getURI()));
+ handler.getTransformer().setURIResolver(this);
+ return new MyTransformerHandlerAndValidity(handler, storedValidity);
+ }
+
+ private void putTemplates(Templates templates, Source stylesheet, String id) throws IOException {
+ if (this.store == null) {
+ return;
+ }
+ // we must augment the template ID with the factory classname since one
+ // transformer implementation cannot handle the instances of a
+ // template created by another one.
+ String key = "XSLTTemplate: " + id + '(' + factory.getClass().getName() + ')';
+
+ // only stylesheets with a last modification date are stored
+ SourceValidity validity = stylesheet.getValidity();
+ if (null != validity) {
+ // Stored is an array of the template and the current time
+ Object[] templateAndValidityAndIncludes = new Object[3];
+ templateAndValidityAndIncludes[0] = templates;
+ templateAndValidityAndIncludes[1] = validity;
+ if (checkIncludes) {
+ templateAndValidityAndIncludes[2] = includesMap.get(id);
+ }
+ store.store(key, templateAndValidityAndIncludes);
+ }
+ }
+
+ /**
+ * Called by the processor when it encounters an xsl:include, xsl:import, or
+ * document() function.
+ *
+ * @param href
+ * An href attribute, which may be relative or absolute.
+ * @param base
+ * The base URI in effect when the href attribute was
+ * encountered.
+ *
+ * @return A Source object, or null if the href cannot be resolved, and the
+ * processor should try to resolve the URI itself.
+ *
+ * @throws TransformerException
+ * if an error occurs when trying to resolve the URI.
+ */
+ public javax.xml.transform.Source resolve(String href, String base) throws TransformerException {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("resolve(href = " + href + ", base = " + base + "); resolver = " + resolver);
+ }
+
+ Source xslSource = null;
+ try {
+ if (base == null || href.indexOf(":") > 1) {
+ // Null base - href must be an absolute URL
+ xslSource = resolver.resolveURI(href);
+ } else if (href.length() == 0) {
+ // Empty href resolves to base
+ xslSource = resolver.resolveURI(base);
+ } else {
+ // is the base a file or a real m_url
+ if (!base.startsWith("file:")) {
+ int lastPathElementPos = base.lastIndexOf('/');
+ if (lastPathElementPos == -1) {
+ // this should never occur as the base should
+ // always be protocol:/....
+ return null; // we can't resolve this
+ } else {
+ xslSource = resolver.resolveURI(base.substring(0, lastPathElementPos) + "/" + href);
+ }
+ } else {
+ File parent = new File(base.substring(5));
+ File parent2 = new File(parent.getParentFile(), href);
+ xslSource = resolver.resolveURI(parent2.toURL().toExternalForm());
+ }
+ }
+
+ InputSource is = getInputSource(xslSource);
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("xslSource = " + xslSource + ", system id = " + xslSource.getURI());
+ }
+
+ if (checkIncludes) {
+ // Populate included validities
+ List includes = (List) includesMap.get(base);
+ if (includes != null) {
+ SourceValidity included = xslSource.getValidity();
+ if (included != null) {
+ includes.add(new Object[] { xslSource.getURI(), xslSource.getValidity() });
+ } else {
+ // One of the included stylesheets is not cacheable
+ includesMap.remove(base);
+ }
+ }
+ }
+
+ return new StreamSource(is.getByteStream(), is.getSystemId());
+ } catch (SourceException e) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return null", e);
+ }
+
+ // CZ: To obtain the same behaviour as when the resource is
+ // transformed by the XSLT Transformer we should return null here.
+ return null;
+ } catch (java.net.MalformedURLException mue) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return null", mue);
+ }
+
+ return null;
+ } catch (IOException ioe) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return null", ioe);
+ }
+
+ return null;
+ } finally {
+ resolver.release(xslSource);
+ }
+ }
+
+ /**
+ * Return a new <code>InputSource</code> object that uses the
+ * <code>InputStream</code> and the system ID of the <code>Source</code>
+ * object.
+ *
+ * @throws IOException
+ * if I/O error occured.
+ */
+ private static InputSource getInputSource(final Source source) throws IOException, SourceException {
+ final InputSource newObject = new InputSource(source.getInputStream());
+ newObject.setSystemId(source.getURI());
+ return newObject;
+ }
+
+ /**
+ * Subclass to allow for instanciation, as for some unknown reason the
+ * constructor is protected....
+ */
+ public static class MyTransformerHandlerAndValidity extends TransformerHandlerAndValidity {
+
+ protected MyTransformerHandlerAndValidity(TransformerHandler handler, SourceValidity validity) {
+ super(handler, validity);
+ }
+ }
+}
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cocoon/trunk/core/cocoon-pipeline/cocoon-pipeline-components/src/main/java/org/apache/cocoon/components/xslt/XSLTProcessorImpl.java
------------------------------------------------------------------------------
svn:keywords = Id