/** * 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 org.apache.commons.jxpath.JXPathContext; import org.xchain.Command; import org.xchain.annotations.Attribute; import org.xchain.annotations.AttributeType; import org.xchain.annotations.Element; import org.xchain.framework.net.UrlFactory; import java.net.URL; import java.io.OutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.Writer; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.Result; import org.xml.sax.ContentHandler; import org.w3c.dom.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p>The <sax:result/> command adds result objects to a pipeline. This command can add several types of results, based on the attributes * defined on the command. To write output to a system id, add the system-id attribute to the command:</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * ... * <sax:result system-id="'file:/some/path'"> * </sax:pipeline> * </code> * * <p>To write output to a file path, add the path attribute:</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * ... * <sax:result path="'/some/path'"> * </sax:pipeline> * </code> * * <p>To write output to an object in the context, add the select attribute:</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * ... * <sax:result select="$result"> * </sax:pipeline> * </code> * * <p>The value of the select attribute must resovle to one of:</p> * <ul> * <li>javax.xml.transform.Result</li> * <li>org.xml.sax.ContentHandler</li> * <li>org.w3c.dom.Node</li> * <li>java.io.OutputStream</li> * <li>java.io.Writer</li> * <li>java.io.File</li> * </ul> * * @author Mike Moulton * @author Christian Trimble * @author Devon Tackett * @author Jason Rose * @author Josh Kennedy */ @Element(localName = "result") public abstract class ResultCommand implements Command { public static Logger log = LoggerFactory.getLogger(ResultCommand.class); /** * <p>DEPRICATED: Use system-id instead. The system id to send the output to.</p> * @param context the JXPathContext to evaluate against. */ @Attribute(localName = "systemId", type = AttributeType.JXPATH_VALUE) public abstract String getSystemIdDepricated(JXPathContext context) throws Exception; /** * <p>Returns true if the systemId attribute has been set.</p> * @return true if the systemId attribute has been set, false otherwise. */ public abstract boolean hasSystemIdDepricated(); /** * <p>The system id to send output to.</p> * @param context the JXPathContext to evaluate against. */ @Attribute(localName="system-id", type = AttributeType.JXPATH_VALUE) public abstract String getSystemId(JXPathContext context) throws Exception; /** * <p>Returns true if the system-id attribute has been set.</p> * @return true if the system-id attribute has been set, false otherwise. */ public abstract boolean hasSystemId(); /** * <p>The file path to send output to.</p> * @param context the JXPathContext to evaluate against. */ @Attribute(localName = "path", type = AttributeType.JXPATH_VALUE) public abstract String getPath(JXPathContext context) throws Exception; /** * <p>Returns true if the path attribute has been set.</p> * @return true if the path attribute has been set, false otherwise. */ public abstract boolean hasPath(); /** * <p>The object to send output to.</p> * @param context the JXPathContext to evaluate against. */ @Attribute(localName = "select", type = AttributeType.JXPATH_SELECT_SINGLE_NODE) public abstract Object getSelect(JXPathContext context) throws Exception; /** * <p>Returns true if the select attribute has been set.</p> * @return true if the select attribute has been set, false otherwise. */ public abstract boolean hasSelect(); /** * <p>Returns the result for the select attribute.</p> * @param context the JXPathContext to evaluate against. * @return the correct result object for the type of object selected from the context. */ public Result createResultForSelect(JXPathContext context) throws Exception { Object object = getSelect(context); if( object == null ) { throw new IllegalArgumentException("The selected object cannot be null."); } // if the object is a result, then use it. else if( object instanceof Result ) { return (Result) object; } // if the object is a stream, then create a stream source. else if( object instanceof OutputStream ) { return new StreamResult((OutputStream) object); } else if( object instanceof Writer ) { return new StreamResult((Writer) object); } else if( object instanceof File ) { return new StreamResult((File) object); } // if the object is a content handler, then create a sax result. else if( object instanceof ContentHandler ) { return new SAXResult((ContentHandler) object); } // if the result is a node, then create a dom result. else if( object instanceof Node ) { return new DOMResult((Node) object); } // we do not how to make a result for this object, so bail out. else { throw new IllegalArgumentException("The selected result object (" + object.getClass().getName() + ") is not a result object nor is it an output stream."); } } /** * <p>Returns the Result object for the system-id attribute.</p> * @param context the JXPathContext to evaluate against. * @return a stream result for the system id. */ public Result createResultForSystemId(JXPathContext context) throws Exception { // set the system id. //String systemId = getSystemId(context); String systemId = null; if( hasSystemId() ) { systemId = getSystemId(context); } else { systemId = getSystemIdDepricated(context); } // create a result object for the system id. URL url = UrlFactory.getInstance().newUrl(systemId); // create an output stream for this url. OutputStream out = url.openConnection().getOutputStream(); // create a stream result for the output stream. StreamResult streamResult = new StreamResult(); streamResult.setSystemId(systemId); streamResult.setOutputStream(out); return streamResult; } /** * <p>Returns the Result object for the path attribute.</p> * @param context the JXPathContext to evaluate against. * @return a stream result for the path specified. */ public Result createResultForPath(JXPathContext context) throws Exception { // set the system id. String path = getPath(context); // get the file object for the path. File file = new File(path); // create the directories leading up to the path. File parentFile = file.getParentFile(); if( !parentFile.exists() ) { parentFile.mkdirs(); } // make sure that the file also exists. file.createNewFile(); // create an output stream for this url. OutputStream out = new FileOutputStream(file); // create a stream result for the output stream. StreamResult streamResult = new StreamResult(); streamResult.setSystemId(file.toURL().toExternalForm()); streamResult.setOutputStream(out); return streamResult; } /** * <p>Builds the result object for this element and sets it on the current sax pipeline configuration.</p> */ public boolean execute(JXPathContext context) throws Exception { Result result = null; if( hasSelect() ) { result = createResultForSelect(context); } else if( hasSystemId() || hasSystemIdDepricated() ) { result = createResultForSystemId(context); } else if( hasPath() ) { result = createResultForPath(context); } else { throw new IllegalStateException("The system-id, path or select attribute must be set for the result tag."); } // set the result in the pipeline config's composite stage. PipelineCommand.getPipelineConfig().getCompositeStage().setResult(result); return false; } }