/** * Copyright 2011 meltmedia * * Licensed 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.xchain.namespaces.sax; import java.util.LinkedList; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamSource; import org.xchain.Locatable; import org.apache.commons.jxpath.JXPathContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xchain.annotations.Attribute; import org.xchain.annotations.AttributeType; import org.xchain.annotations.Element; import org.xchain.framework.lifecycle.XmlFactoryLifecycle; import org.xchain.framework.sax.TransformerHandlerStage; import org.xchain.impl.ChainImpl; import org.xml.sax.Locator; import javax.xml.namespace.QName; /** * <p>The <sax:transformer/> adds a transform to a sax pipeline. Currently, XChains supports both XSLT and STX transformations. To add a transform to * a pipeline, include a <sax:transformer/> element between the source and result of a <sax:pipeline/> element.</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * <sax:source .../> * ... * <sax:transformer system-id="'relative-uri-of-template'"/> * ... * <sax:result .../> * </sax:pipeline> * </code> * * <p>Parameters can be passed to a template by including <sax:parameter/> elements inside the <sax:transformer/> element.</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * <sax:source .../> * ... * <sax:transformer system-id="'relative-uri-of-template'"> * <sax:parameter name="'name'" value="'value'"/> * </sax:transformer> * ... * <sax:result .../> * </sax:pipeline> * </code> * * <p>Since the transformer element is a command, you can optionally include a template by adding a conditional element around it. For example, you can add a template based on some test * using the $lt;xchain:if/> element.</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * <sax:source .../> * ... * <xchain:if test="$test"/> * <sax:transformer system-id="'relative-uri-of-template'"/> * <:/xcahin:if> * ... * <sax:result .../> * </sax:pipeline> * </code> * * @author Mike Moulton * @author Christian Trimble * @author Josh Kennedy */ @Element(localName="transformer") public abstract class TransformerCommand extends ChainImpl implements Locatable { /** The log for the transformer command. */ private static Logger log = LoggerFactory.getLogger(TransformerCommand.class); /** * The thread local stack of transformers currently being used by this transformer. */ protected static ThreadLocal<LinkedList<Transformer>> transformerThreadLocal = new ThreadLocal<LinkedList<Transformer>>(); /** * <p>The system id of the stylesheet.</p> * @param context the JXPathContext to evaluate against. */ @Attribute(localName="system-id", type=AttributeType.JXPATH_VALUE) public abstract String getSystemId( JXPathContext context ) throws Exception; public boolean execute( JXPathContext context ) throws Exception { if( log.isDebugEnabled() ) { log.debug("Creating transformer stage for system id '"+getSystemId( context )+"'."); } Templates templates = null; TransformerHandler transformerHandler = null; Transformer transformer = null; try { // create the transformer handler for the templates object. transformerHandler = XmlFactoryLifecycle.newTransformerHandler(java.net.URI.create(getLocator().getSystemId()).resolve(getSystemId(context)).toString()); // get the transformer from the transformer handler object. transformer = transformerHandler.getTransformer(); transformer.setURIResolver(PipelineCommand.getPipelineConfig().getUriResolver()); if( PipelineCommand.getPipelineConfig().getErrorListener() != null ) { transformer.setErrorListener(PipelineCommand.getPipelineConfig().getErrorListener()); } } catch( Exception e ) { if( log.isErrorEnabled() ) { log.error("Could not create transformer for system id '"+getSystemId( context )+"' due to an exception.", e); } throw e; } try { // push the transformer on the stack. pushCurrentTransformer( transformer ); // allow the child templates to configure the transformer. super.execute( context ); } finally { // pop the current transformer. popCurrentTransformer(); } if( log.isDebugEnabled() ) { log.debug("Adding transformer to compositeStage."); } if( log.isDebugEnabled() ) { log.debug("Adding transformer to stage."); } // add the transformer to the pipeline. PipelineCommand.getPipelineConfig().getCompositeStage().addStage(new TransformerHandlerStage(transformerHandler)); // return false, allowing other pipelines to execute. return false; } /** * <p>Returns the current transfrormer stack. The current transformer holds transformers that are being configured by the current thread.</p> * @return the stack of transformers being configured by the current thread. */ public static LinkedList<Transformer> getCurrentTransformerStack() { LinkedList<Transformer> currentTransformerStack = transformerThreadLocal.get(); if( currentTransformerStack == null ) { currentTransformerStack = new LinkedList<Transformer>(); transformerThreadLocal.set(currentTransformerStack); } return currentTransformerStack; } /** * <p>Pushes a transformer onto the stack of transfromers for this thread.</p> * @param transformer the transformer to push onto the current transfromer stack. */ public static void pushCurrentTransformer( Transformer transformer ) { getCurrentTransformerStack().addFirst( transformer ); } /** * <p>Pops the current transformer off of the stack of transfromers for this thread.</p> * @return the transformer that was on the top of the current transformer stack. */ public static Transformer popCurrentTransformer() { return getCurrentTransformerStack().removeFirst(); } /** * <p>Returns the current transformer on the top of the stack, without removing it from the stack.</p> * @return the current transformer for this thread. */ public static Transformer getCurrentTransformer() { return getCurrentTransformerStack().getFirst(); } }