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