/* * The Apache Software License, Version 1.1 * * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, 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 "Xalan" 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 (INCLUDING, 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 based on software copyright (c) 1999, Lotus * Development Corporation., http://www.lotus.com. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.xalan.xslt; import org.apache.xpath.*; import org.apache.xpath.compiler.XPathParser; import org.apache.xpath.compiler.Compiler; import org.apache.xalan.xpath.XObject; import org.apache.xalan.xpath.XString; import org.apache.xalan.xpath.XNodeSet; import org.apache.xalan.xpath.XBoolean; import org.apache.xalan.xpath.XNumber; import org.apache.xalan.xpath.XNull; import org.w3c.dom.*; import org.w3c.dom.traversal.NodeIterator; import org.xml.sax.ContentHandler; import org.xml.sax.*; import org.xml.sax.helpers.*; import org.xml.sax.ext.*; import org.xml.sax.helpers.ParserAdapter; import org.apache.xml.serialize.XMLSerializer; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.Templates; import javax.xml.transform.Source; import javax.xml.transform.SourceLocator; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.ErrorListener; import java.util.*; import java.net.*; import java.io.*; import java.lang.reflect.*; import org.apache.xalan.templates.Stylesheet; //import org.apache.xalan.templates.StylesheetRoot; import org.apache.xalan.xpath.xml.XMLParserLiaison; import org.apache.xalan.xpath.xml.ProblemListenerDefault; import org.apache.xalan.xpath.xml.ProblemListener; import org.apache.xalan.templates.StylesheetComposed; import org.apache.xalan.transformer.TransformerImpl; import org.apache.xalan.processor.StylesheetHandler; import org.apache.xalan.processor.TransformerFactoryImpl; //import org.apache.xalan.processor.ProcessorStylesheet; //import org.apache.xalan.xslt.StylesheetSpec; import org.apache.xalan.trace.*; import org.apache.xalan.res.XSLTErrorResources; import org.apache.xalan.res.XSLMessages; import org.apache.xml.utils.PrefixResolverDefault; import org.apache.xml.utils.TreeWalker; import org.apache.xml.utils.QName; import org.apache.xml.utils.DefaultErrorHandler; import org.apache.xalan.transformer.TransformerHandlerImpl; // Imported JAVA API for XML Parsing 1.0 classes import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; /** * <meta name="usage" content="advanced"/> * The Xalan workhorse -- Collaborates with the XML parser liaison, the DOM, * and the XPath engine, to transform a source tree of nodes into a result tree * according to instructions and templates specified by a stylesheet tree. * We suggest you use one of the * static XSLTProcessorFactory getProcessor() methods to instantiate the processor * and return an interface that greatly simplifies the process of manipulating * XSLTEngineImpl. * * <p>The methods <code>process(...)</code> are the primary public entry points. * The best way to perform transformations is to use the * {@link org.apache.xalan.xslt.XSLTProcessor#process(org.apache.xalan.xslt.XSLTInputSource, org.apache.xalan.xslt.XSLTInputSource, org.apache.xalan.xslt.XSLTResultTarget)} method, * but you may use any of process methods defined in XSLTEngineImpl.</p> * * <p>Please note that this class is not safe per instance over multiple * threads. If you are in a multithreaded environment, you should * keep a pool of these objects, or create a new one each time. In a * multithreaded environment, the right way to do things is to create a * StylesheetRoot via processStylesheet, and then reuse this object * over multiple threads.</p> * * <p>If you reuse the processor instance, you should call reset() between transformations.</p> * @see XSLTProcessorFactory * @see XSLTProcessor * @deprecated This compatibility layer will be removed in later releases. */ public class XSLTEngineImpl implements XSLTProcessor { //private Processor m_processor; private TransformerFactory m_tfactory; private TransformerImpl m_transformerImpl; private DOM2Helper m_liaison; private String m_outputFileName; private DocumentHandler m_documentHandler = null; private ProblemListenerDefault m_problemListener; private Hashtable m_stylesheetParams; StylesheetRoot m_stylesheetRoot = null; Vector m_evalList = null; boolean m_needToEval = false; /* * If this is true, then the diag function will * be called. */ private boolean m_traceTemplateChildren = false; /* * If this is true, then the simple tracing of templates * will be performed. */ private boolean m_traceTemplates = false; /* * If this is true, then diagnostics of each select * will be performed. */ boolean m_traceSelects = false; /* * A stream to print diagnostics to. */ java.io.PrintWriter m_diagnosticsPrintWriter = null; /* For diagnostics */ Hashtable m_durationsTable = new Hashtable(); /** * A XSLMessages instance capable of producing user messages. */ private static XSLMessages m_XSLMessages = new XSLMessages(); /** * Construct an XSLT processor that uses the default DTM (Document Table Model) liaison * and XML parser. As a general rule, you should use XSLTProcessorFactory to create an * instance of this class and provide access to the instance via the XSLTProcessor interface. * * @see XSLTProcessorFactory * @see XSLTProcessor */ protected XSLTEngineImpl() throws org.xml.sax.SAXException { m_tfactory = TransformerFactory.newInstance(); m_problemListener = new ProblemListenerDefault(); //m_liaison = (DOM2Helper)createLiaison(); } /** * Construct an XSLT processor that uses the the given parser liaison. * As a general rule, you should use XSLTProcessorFactory to create an * instance of this class and provide access to the instance via the XSLTProcessor interface. * * @see XSLTProcessorFactory * @see XSLTProcessor */ public XSLTEngineImpl(String liaisonClassName) throws SAXException { m_tfactory = TransformerFactory.newInstance(); m_problemListener = new ProblemListenerDefault(); try { m_liaison = (DOM2Helper)(Class.forName(liaisonClassName).newInstance()); //org.apache.xpath.XPathContext xctxt = this.getTransformer().getXPathContext(); //xctxt.setDOMHelper(m_liaison); } catch (ClassNotFoundException e1) { throw new SAXException("XML Liaison class " + liaisonClassName + " specified but not found", e1); } catch (IllegalAccessException e2) { throw new SAXException("XML Liaison class " + liaisonClassName + " found but cannot be loaded", e2); } catch (InstantiationException e3) { throw new SAXException("XML Liaison class " + liaisonClassName + " loaded but cannot be instantiated (no empty public constructor?)", e3); } catch (ClassCastException e4) { throw new SAXException("XML Liaison class " + liaisonClassName + " does not implement DOM2Helper", e4); } } /** * Construct an XSL processor that uses the the given parser liaison. * As a general rule, you should use XSLTProcessorFactory to create an * instance of this class and provide access to the instance via the XSLTProcessor interface. * * @param XMLParserLiaison A liaison to an XML parser. * * @see org.apache.xalan.xpath.xml.XMLParserLiaison * @see XSLTProcessorFactory * @see XSLTProcessor */ public XSLTEngineImpl(XMLParserLiaison parserLiaison) throws org.xml.sax.SAXException { m_tfactory = TransformerFactory.newInstance(); m_problemListener = new ProblemListenerDefault(); m_liaison = (DOM2Helper)parserLiaison; // org.apache.xpath.XPathContext xctxt = this.getTransformer().getXPathContext(); //xctxt.setDOMHelper(m_liaison); } /** * Construct an XSLT processor that can call back to the XML parser, in order to handle * included files and the like. * * @param XMLParserLiaison A liaison to an XML parser. * * @see org.apache.xalan.xpath.xml.XMLParserLiaison * @see XSLTProcessorFactory * @see XSLTProcessor */ XSLTEngineImpl(XMLParserLiaison parserLiaison, XPathFactory xpathFactory) throws SAXException { m_tfactory = TransformerFactory.newInstance(); m_problemListener = new ProblemListenerDefault(); m_liaison = (DOM2Helper)parserLiaison; // org.apache.xpath.XPathContext xctxt = this.getTransformer().getXPathContext(); //xctxt.setDOMHelper(m_liaison); } /** * Get a Liaison class */ public XMLParserLiaison createLiaison() throws org.xml.sax.SAXException { return new org.apache.xalan.xpath.xml.XMLParserLiaisonDefault(); } /** * Reset the state. This needs to be called after a process() call * is invoked, if the processor is to be used again. */ public void reset() { if (m_transformerImpl != null) m_transformerImpl.reset(); m_stylesheetParams = null; } /** * Transform the source tree to the output in the given * result tree target. As a general rule, we recommend you use the * {@link org.apache.xalan.xslt.XSLTProcessor#process(org.apache.xalan.xslt.XSLTInputSource, org.apache.xalan.xslt.XSLTInputSource, org.apache.xalan.xslt.XSLTResultTarget)} method. * @param inputSource The input source. * @param stylesheetSource The stylesheet source. May be null if source has a xml-stylesheet PI. * @param outputTarget The output source tree. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void process( XSLTInputSource inputSource, XSLTInputSource stylesheetSource, XSLTResultTarget outputTarget) throws SAXException { try { Boolean totalTimeID = new Boolean(true); pushTime(totalTimeID); Node sourceTree = null; Templates templates = null; if (m_needToEval) { Node node = null; if (null != stylesheetSource) { Source ssSource = stylesheetSource.getSourceObject(); if(ssSource instanceof DOMSource) { node = ((DOMSource)ssSource).getNode(); } } if (null == node) { node = new DOM2Helper().createDocument() ; } for (int i=0; i< m_evalList.size(); i++) { String name = (String)m_evalList.elementAt(i); String expression = (String)m_stylesheetParams.get(name); try{ org.apache.xpath.objects.XObject val = org.apache.xpath.XPathAPI.eval(node, expression); m_stylesheetParams.put(name, val); } catch(TransformerException te) { throw new SAXException(te); } } m_needToEval = false; m_evalList = null; } sourceTree = getSourceTreeFromInput(inputSource); if(null != stylesheetSource) { try{ templates = m_tfactory.newTemplates(stylesheetSource.getSourceObject()); } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } else if( null != inputSource) { if(null != sourceTree) { String stylesheetURI = null; Stack hrefs = new Stack(); for(Node child=sourceTree.getFirstChild(); null != child; child=child.getNextSibling()) { if(Node.PROCESSING_INSTRUCTION_NODE == child.getNodeType()) { ProcessingInstruction pi = (ProcessingInstruction)child; if(pi.getNodeName().equals("xml-stylesheet") || pi.getNodeName().equals("xml:stylesheet")) { boolean isOK = true; StringTokenizer tokenizer = new StringTokenizer(pi.getNodeValue(), " \t="); while(tokenizer.hasMoreTokens()) { if(tokenizer.nextToken().equals("type")) { String typeVal = tokenizer.nextToken(); typeVal = typeVal.substring(1, typeVal.length()-1); if(!typeVal.equals("text/xsl") && !typeVal.equals("text/xml") && !typeVal.equals("application/xml+xslt")) { isOK = false; } } } if(isOK) { tokenizer = new StringTokenizer(pi.getNodeValue(), " \t="); while(tokenizer.hasMoreTokens()) { if(tokenizer.nextToken().equals("href")) { stylesheetURI = tokenizer.nextToken(); stylesheetURI = stylesheetURI.substring(1, stylesheetURI.length()-1); hrefs.push(stylesheetURI); } } } } } } // end for(int i = 0; i < nNodes; i++) boolean isRoot = true; Stylesheet prevStylesheet = null; while(!hrefs.isEmpty()) { Stylesheet stylesheet = getStylesheetFromPIURL((String)hrefs.pop(), sourceTree, (null != inputSource) ? inputSource.getSystemId() : null, isRoot); if(false == isRoot) { prevStylesheet.setImport((StylesheetComposed)stylesheet); } prevStylesheet = stylesheet; isRoot = false; } } } else { error(XSLTErrorResources.ER_NO_INPUT_STYLESHEET); //"Stylesheet input was not specified!"); } if(null == templates) { if (m_stylesheetRoot != null) templates = m_stylesheetRoot.getObject(); else { error(XSLTErrorResources.ER_FAILED_PROCESS_STYLESHEET); //"Failed to process stylesheet!"); return; } } if(null != templates) { try{ m_transformerImpl = (TransformerImpl)templates.newTransformer(); if (m_problemListener != null) m_transformerImpl.setErrorListener(m_problemListener); // if (m_liaison != null) // m_transformerImpl.getXPathContext().setDOMHelper(m_liaison); } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } if (m_stylesheetParams != null) { Enumeration keys = m_stylesheetParams.keys(); while (keys.hasMoreElements()) { String name = (String)keys.nextElement(); Object value = m_stylesheetParams.get(name); m_transformerImpl.setParameter(name, null, value); } } try{ m_transformerImpl.transform(new DOMSource(sourceTree), outputTarget.getResultObject()); } catch (TransformerException te) { throw new SAXException(te); } if(null != m_diagnosticsPrintWriter) { displayDuration("Total time", totalTimeID); } } } catch(MalformedURLException mue) { error(XSLTErrorResources.ERROR0000, new Object[] {mue.getMessage()}, mue); // throw se; } catch(FileNotFoundException fnfe) { error(XSLTErrorResources.ERROR0000, new Object[] {fnfe.getMessage()}, fnfe); // throw se; } catch(IOException ioe) { error(XSLTErrorResources.ERROR0000, new Object[] {ioe.getMessage()}, ioe); // throw se; } catch(SAXException se) { error(XSLTErrorResources.ER_SAX_EXCEPTION, se); //"SAX Exception", se); // throw se; } } /** * Bottleneck the creation of the stylesheet for derivation purposes. */ public StylesheetRoot createStylesheetRoot(String baseIdentifier) throws MalformedURLException, FileNotFoundException, IOException, SAXException { try{ if (baseIdentifier == null) return new StylesheetRoot(this, baseIdentifier); Source inSource = new XSLTInputSource(baseIdentifier).getSourceObject(); Templates templates = m_tfactory.newTemplates(inSource); StylesheetRoot stylesheet = new StylesheetRoot((org.apache.xalan.templates.StylesheetRoot)templates); return stylesheet; } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } /** * Bottleneck the creation of the stylesheet for derivation purposes. */ StylesheetRoot createStylesheetRoot(String baseIdentifier, XSLTInputSource source) throws MalformedURLException, FileNotFoundException, IOException, SAXException { try{ Source inSource = source.getSourceObject(); Templates templates = m_tfactory.newTemplates(inSource); StylesheetRoot stylesheet = new StylesheetRoot((org.apache.xalan.templates.StylesheetRoot)templates); return stylesheet; } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } /** * Given a URI to an XSL stylesheet, * Compile the stylesheet into an internal representation. * This calls reset() before processing if the stylesheet root has been set * to non-null. * @param xmldocURLString The URL to the input XML document. * @return The compiled stylesheet object. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public StylesheetRoot processStylesheet(XSLTInputSource stylesheetSource) throws SAXException { try{ if(null != ((TransformerFactoryImpl)m_tfactory).newTemplatesHandler().getTemplates()) reset(); } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } String xslIdentifier = ((null == stylesheetSource) || (null == stylesheetSource.getSystemId())) ? "Input XSL" : stylesheetSource.getSystemId(); // In case we have a fragment identifier, go ahead and // try and parse the XML here. m_stylesheetRoot = null; try { StylesheetHandler stylesheetProcessor = new StylesheetHandler((TransformerFactoryImpl)m_tfactory); //this, m_stylesheetRoot); Source ssSource = stylesheetSource.getSourceObject(); if(ssSource instanceof DOMSource) { if(((DOMSource)ssSource).getNode() instanceof StylesheetRoot) { m_stylesheetRoot = (StylesheetRoot)((DOMSource)ssSource).getNode(); } else { stylesheetProcessor.setSystemId(stylesheetSource.getSystemId()); TreeWalker tw = new TreeWalker(stylesheetProcessor, new org.apache.xpath.DOM2Helper()); tw.traverse(((DOMSource)ssSource).getNode()); m_stylesheetRoot = new StylesheetRoot(stylesheetProcessor.getStylesheetRoot()); } } else { if(null != m_liaison) { // DOM2Helper liaison = (DOM2Helper)(Class.forName(liaisonClassName).newInstance()); m_liaison.parse(SAXSource.sourceToInputSource(ssSource)); DOMSource source = new DOMSource(m_liaison.getDocument()); Templates templates = m_tfactory.newTemplates(source); m_stylesheetRoot = new StylesheetRoot( (org.apache.xalan.templates.StylesheetRoot)templates); } else { m_stylesheetRoot = createStylesheetRoot(stylesheetSource.getSystemId(), stylesheetSource); } addTraceListenersToStylesheet(); stylesheetProcessor.pushStylesheet(m_stylesheetRoot.getObject()); diag("========= Parsing "+xslIdentifier+" =========="); pushTime(xslIdentifier); //String liaisonClassName = System.getProperty("org.apache.xalan.source.liaison"); if(null != m_diagnosticsPrintWriter) displayDuration("Parse of "+xslIdentifier, xslIdentifier); } } catch(Exception e) { error(XSLTErrorResources.ER_COULDNT_PARSE_DOC, new Object[] {xslIdentifier}, e); //"Could not parse "+xslIdentifier+" document!", e); } return m_stylesheetRoot; } /** * Given a URI to an XSL stylesheet, * Compile the stylesheet into an internal representation. * This calls reset() before processing if the stylesheet root has been set * to non-null. * @param xmldocURLString The URL to the input XML document. * @return The compiled stylesheet object. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public StylesheetRoot processStylesheet(String xsldocURLString) throws SAXException { try { XSLTInputSource input = new XSLTInputSource(getURLFromString(xsldocURLString, null).toString()); return processStylesheet(input); } catch(SAXException se) { error(XSLTErrorResources.ER_PROCESSSTYLESHEET_NOT_SUCCESSFUL, se); //"processStylesheet not succesfull!", se); return null; // shut up compiler } } /** * Set the stylesheet for this processor. If this is set, then the * process calls that take only the input .xml will use * this instead of looking for a stylesheet PI. Also, * setting the stylesheet is needed if you are going * to use the processor as a SAX DocumentHandler. */ public void setStylesheet(StylesheetRoot stylesheetRoot) { m_stylesheetRoot = stylesheetRoot; org.apache.xalan.templates.StylesheetRoot sr = stylesheetRoot.getObject(); if (m_transformerImpl == null) m_transformerImpl = (TransformerImpl)sr.newTransformer(); m_transformerImpl.setStylesheet(sr); if (m_problemListener != null) m_transformerImpl.setErrorListener(m_problemListener); } /** * Get the current stylesheet for this processor. */ public StylesheetRoot getStylesheet() { return m_stylesheetRoot; } /** * <meta name="usage" content="internal"/> * Get the filename of the output document, if it was set. * This is for use by multiple output documents, to determine * the base directory for the output document. It needs to * be set by the caller. */ public String getOutputFileName() { return m_outputFileName; } /** * <meta name="usage" content="internal"/> * Set the filename of the output document. * This is for use by multiple output documents, to determine * the base directory for the output document. It needs to * be set by the caller. */ public void setOutputFileName(String filename) { m_outputFileName = filename; } /** * Given an input source, get the source tree. */ public Node getSourceTreeFromInput(XSLTInputSource inputSource) throws org.xml.sax.SAXException { Node sourceTree = null; String xmlIdentifier = ((null == inputSource) || (null == inputSource.getSystemId())) ? "Input XML" : inputSource.getSystemId(); Source iSource = inputSource.getSourceObject(); if(iSource instanceof DOMSource) { //if(getXMLProcessorLiaison() instanceof org.apache.xalan.xpath.dtm.DTMLiaison) // error(XSLTErrorResources.ER_CANT_USE_DTM_FOR_INPUT); sourceTree = ((DOMSource)iSource).getNode(); } else { // In case we have a fragment identifier, go ahead and // try and parse the XML here. try { diag("========= Parsing "+xmlIdentifier+" =========="); pushTime(xmlIdentifier); //String liaisonClassName = System.getProperty("org.apache.xalan.source.liaison"); if(null != m_liaison) { //DOM2Helper liaison = (DOM2Helper)(Class.forName(liaisonClassName).newInstance()); m_liaison.parse(SAXSource.sourceToInputSource(iSource)); if(null != m_diagnosticsPrintWriter) displayDuration("Parse of "+xmlIdentifier, xmlIdentifier); sourceTree = m_liaison.getDocument(); } else { try { // Use an implementation of the JAVA API for XML Parsing 1.0 to // create a DOM Document node to contain the result. DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); dfactory.setNamespaceAware(true); dfactory.setValidating(false); DocumentBuilder docBuilder = dfactory.newDocumentBuilder(); sourceTree = docBuilder.parse(xmlIdentifier); } catch (ParserConfigurationException pce) { error(XSLTErrorResources.ER_COULDNT_PARSE_DOC, new Object[] {xmlIdentifier}, pce); } } } catch(Exception e) { // Unwrap exception if((e instanceof SAXException) && (null != ((SAXException)e).getException())) { // ((SAXException)e).getException().printStackTrace(); e = ((SAXException)e).getException(); } sourceTree = null; // shutup compiler error(XSLTErrorResources.ER_COULDNT_PARSE_DOC, new Object[] {xmlIdentifier}, e); //"Could not parse "+xmlIdentifier+" document!", e); } } return sourceTree; } /** * Get Stylesheet from PI URL * * @param xslURLString a valid URI to an XSL stylesheet. * @param fragbase Document fragment Node. * @param xmlBaseIdent Base URI to resolve stylesheet URL * @param isRoot Flag indicating if root node */ Stylesheet getStylesheetFromPIURL(String xslURLString, Node fragBase, String xmlBaseIdent, boolean isRoot) throws SAXException, MalformedURLException, FileNotFoundException, IOException { Stylesheet stylesheet = null; String[] stringHolder = { null}; xslURLString = xslURLString.trim(); int fragIndex = xslURLString.indexOf('#'); String fragID = null; Document stylesheetDoc; if(fragIndex == 0) { diag("Locating stylesheet from fragment identifier..."); fragID = xslURLString.substring(1); // Try a bunch of really ugly stuff to find the fragment. // What's the right way to do this? // Create a XPath parser. XPathParser parser = new XPathParser((ErrorListener)m_problemListener.getErrorHandler(), null); XPathContext xpathContext = new XPathContext(); PrefixResolverDefault nsNode = new PrefixResolverDefault(fragBase); //xpathContext.getNamespaceContext(); NodeIterator nl = null; // Create the XPath object. try{ XPath xpath = new XPath(fragID, null, nsNode, XPath.MATCH); Compiler compiler = new Compiler(); // Parse the xpath parser.initXPath(compiler, "id("+fragID+")", nsNode); org.apache.xpath.objects.XObject xobj = xpath.execute(xpathContext, fragBase, nsNode); nl = new org.apache.xml.dtm.ref.DTMNodeIterator(xobj.iter()); if(nl.nextNode() == null) { // xobj = Stylesheet.evalXPathStr(getExecContext(), "//*[@id='"+fragID+"']", fragBase, nsNode); // Create the XPath object. xpath = new XPath(fragID, null, nsNode, XPath.MATCH); // Parse the xpath parser.initXPath(compiler, "//*[@id='"+fragID+"']", nsNode); xobj = xpath.execute(xpathContext, fragBase, nsNode); nl = new org.apache.xml.dtm.ref.DTMNodeIterator(xobj.iter()); if(nl.nextNode() == null) { // xobj = Stylesheet.evalXPathStr(getExecContext(), "//*[@name='"+fragID+"']", fragBase, nsNode); // Create the XPath object. xpath = new XPath(fragID, null, nsNode, XPath.MATCH); // Parse the xpath parser.initXPath(compiler, "//*[@name='"+fragID+"']", nsNode); xobj = xpath.execute(xpathContext, fragBase, nsNode); nl = new org.apache.xml.dtm.ref.DTMNodeIterator(xobj.iter()); if(nl.nextNode() == null) { // Well, hell, maybe it's an XPath... // xobj = Stylesheet.evalXPathStr(getExecContext(), fragID, fragBase, nsNode); // Create the XPath object. //((StylesheetHandler)( m_processor.getTemplatesBuilder())).getLocator() xpath = new XPath(fragID, null, nsNode, XPath.MATCH); // Parse the xpath parser.initXPath(compiler, fragID, nsNode); xobj = xpath.execute(xpathContext, fragBase, nsNode); nl = new org.apache.xml.dtm.ref.DTMNodeIterator(xobj.iter()); } } } } catch (TransformerException te) { throw new SAXException(te); } if(nl.nextNode() == null) { error(XSLTErrorResources.ER_COULDNT_FIND_FRAGMENT, new Object[] {fragID}); //"Could not find fragment: "+fragID); } // Use previous because the previous call moved the pointer. // or should we use getRoot?? Node frag = nl.previousNode(); //.item(0); if(Node.ELEMENT_NODE == frag.getNodeType()) { pushTime(frag); if(isRoot) { m_stylesheetRoot = createStylesheetRoot(stringHolder[0]); stylesheet = m_stylesheetRoot.getObject(); } else { //stylesheet = new Stylesheet(m_stylesheetRoot); // stylesheet = ((StylesheetHandler)(m_processor.getTemplatesBuilder())).getStylesheetRoot(); try{ Source source = new XSLTInputSource(fragID).getSourceObject(); Templates templates = m_tfactory.newTemplates(source); stylesheet = (org.apache.xalan.templates.StylesheetRoot)templates; } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } addTraceListenersToStylesheet(); try{ StylesheetHandler stylesheetProcessor = new StylesheetHandler((TransformerFactoryImpl)m_tfactory); stylesheetProcessor.pushStylesheet(stylesheet); TreeWalker tw = new TreeWalker(stylesheetProcessor, new org.apache.xpath.DOM2Helper()); tw.traverse(frag); displayDuration("Setup of "+xslURLString, frag); } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } else { stylesheetDoc = null; error(XSLTErrorResources.ER_NODE_NOT_ELEMENT, new Object[] {fragID}); //"Node pointed to by fragment identifier was not an element: "+fragID); } } else { // TODO: Use Reader here?? // hmmm.. for now I'll rely on the XML parser to handle // fragment URLs. diag(XSLMessages.createMessage(XSLTErrorResources.WG_PARSING_AND_PREPARING, new Object[] {xslURLString})); //"========= Parsing and preparing "+xslURLString+" =========="); pushTime(xslURLString); URL xslURL = getURLFromString(xslURLString, xmlBaseIdent); XSLTInputSource inputSource = new XSLTInputSource(xslURL.toString()); if(isRoot) { if(null != m_liaison) { try{ m_liaison.parse(SAXSource.sourceToInputSource(inputSource.getSourceObject())); DOMSource source = new DOMSource(m_liaison.getDocument()); Templates templates = m_tfactory.newTemplates(source); m_stylesheetRoot = new StylesheetRoot( (org.apache.xalan.templates.StylesheetRoot)templates); } catch (TransformerException tce) { throw new SAXException(tce); } } else { m_stylesheetRoot = createStylesheetRoot(xslURLString); } stylesheet = m_stylesheetRoot.getObject(); } else { stylesheet = new Stylesheet(m_stylesheetRoot.getObject()); } addTraceListenersToStylesheet(); try{ org.apache.xalan.processor.StylesheetHandler stylesheetProcessor = new StylesheetHandler((TransformerFactoryImpl)m_tfactory); //this, stylesheet); stylesheetProcessor.pushStylesheet(stylesheet); // new StylesheetHandler(this, stylesheet); } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } //m_parserLiaison.setDocumentHandler(stylesheetProcessor); //m_parserLiaison.parse(inputSource); displayDuration("Parsing and init of "+xslURLString, xslURLString); } return stylesheet; } /** * Take a user string and try and parse XML, and also return * the url. * @exception XSLProcessorException thrown if the active ProblemListener and XPathContext decide * the error condition is severe enough to halt processing. */ public static URL getURLFromString(String urlString, String base) throws SAXException { String origURLString = urlString; String origBase = base; // System.out.println("getURLFromString - urlString: "+urlString+", base: "+base); Object doc; URL url = null; int fileStartType = 0; try { if(null != base) { if(base.toLowerCase().startsWith("file:/")) { fileStartType = 1; } else if(base.toLowerCase().startsWith("file:")) { fileStartType = 2; } } boolean isAbsoluteURL; // From http://www.ics.uci.edu/pub/ietf/uri/rfc1630.txt // A partial form can be distinguished from an absolute form in that the // latter must have a colon and that colon must occur before any slash // characters. Systems not requiring partial forms should not use any // unencoded slashes in their naming schemes. If they do, absolute URIs // will still work, but confusion may result. int indexOfColon = urlString.indexOf(':'); int indexOfSlash = urlString.indexOf('/'); if((indexOfColon != -1) && (indexOfSlash != -1) && (indexOfColon < indexOfSlash)) { // The url (or filename, for that matter) is absolute. isAbsoluteURL = true; } else { isAbsoluteURL = false; } if(isAbsoluteURL || (null == base) || (base.length() == 0)) { try { url = new URL(urlString); } catch (MalformedURLException e) {} } // The Java URL handling doesn't seem to handle relative file names. else if(!((urlString.charAt(0) == '.') || (fileStartType > 0))) { try { URL baseUrl = new URL(base); url = new URL(baseUrl, urlString); } catch (MalformedURLException e) { } } if(null == url) { // Then we're going to try and make a file URL below, so strip // off the protocol header. if(urlString.toLowerCase().startsWith("file:/")) { urlString = urlString.substring(6); } else if(urlString.toLowerCase().startsWith("file:")) { urlString = urlString.substring(5); } } if((null == url) && ((null == base) || (fileStartType > 0))) { if(1 == fileStartType) { if(null != base) base = base.substring(6); fileStartType = 1; } else if(2 == fileStartType) { if(null != base) base = base.substring(5); fileStartType = 2; } File f = new File(urlString); if(!f.isAbsolute() && (null != base)) { // String dir = f.isDirectory() ? f.getAbsolutePath() : f.getParent(); // System.out.println("prebuiltUrlString (1): "+base); StringTokenizer tokenizer = new StringTokenizer(base, "\\/"); String fixedBase = null; while(tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (null == fixedBase) { // Thanks to Rick Maddy for the bug fix for UNIX here. if (base.charAt(0) == '\\' || base.charAt(0) == '/') { fixedBase = File.separator + token; } else { fixedBase = token; } } else { fixedBase+= File.separator + token; } } // System.out.println("rebuiltUrlString (1): "+fixedBase); f = new File(fixedBase); String dir = f.isDirectory() ? f.getAbsolutePath() : f.getParent(); // System.out.println("dir: "+dir); // System.out.println("urlString: "+urlString); // f = new File(dir, urlString); // System.out.println("f (1): "+f.toString()); // urlString = f.getAbsolutePath(); f = new File(urlString); boolean isAbsolute = f.isAbsolute() || (urlString.charAt( 0 ) == '\\') || (urlString.charAt( 0 ) == '/'); if(!isAbsolute) { // Getting more and more ugly... if(dir.charAt( dir.length()-1 ) != File.separator.charAt(0) && urlString.charAt( 0 ) != File.separator.charAt(0)) { urlString = dir + File.separator + urlString; } else { urlString = dir + urlString; } // System.out.println("prebuiltUrlString (2): "+urlString); tokenizer = new StringTokenizer(urlString, "\\/"); String rebuiltUrlString = null; while(tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (null == rebuiltUrlString) { // Thanks to Rick Maddy for the bug fix for UNIX here. if (urlString.charAt(0) == '\\' || urlString.charAt(0) == '/') { rebuiltUrlString = File.separator + token; } else { rebuiltUrlString = token; } } else { rebuiltUrlString+= File.separator + token; } } // System.out.println("rebuiltUrlString (2): "+rebuiltUrlString); if(null != rebuiltUrlString) urlString = rebuiltUrlString; } // System.out.println("fileStartType: "+fileStartType); if(1 == fileStartType) { if (urlString.charAt(0) == '/') { urlString = "file://"+urlString; } else { urlString = "file:/"+urlString; } } else if(2 == fileStartType) { urlString = "file:"+urlString; } try { // System.out.println("Final before try: "+urlString); url = new URL(urlString); } catch (MalformedURLException e) { // System.out.println("Error trying to make URL from "+urlString); } } } if(null == url) { // The sun java VM doesn't do this correctly, but I'll // try it here as a second-to-last resort. if((null != origBase) && (origBase.length() > 0)) { try { URL baseURL = new URL(origBase); // System.out.println("Trying to make URL from "+origBase+" and "+origURLString); url = new URL(baseURL, origURLString); // System.out.println("Success! New URL is: "+url.toString()); } catch (MalformedURLException e) { // System.out.println("Error trying to make URL from "+origBase+" and "+origURLString); } } if(null == url) { try { String lastPart; if(null != origBase) { File baseFile = new File(origBase); if(baseFile.isDirectory()) { lastPart = new File(baseFile, urlString).getAbsolutePath (); } else { String parentDir = baseFile.getParent(); lastPart = new File(parentDir, urlString).getAbsolutePath (); } } else { lastPart = new File (urlString).getAbsolutePath (); } // Hack // if((lastPart.charAt(0) == '/') && (lastPart.charAt(2) == ':')) // lastPart = lastPart.substring(1, lastPart.length() - 1); String fullpath; if (lastPart.charAt(0) == '\\' || lastPart.charAt(0) == '/') { fullpath = "file://" + lastPart; } else { fullpath = "file:" + lastPart; } url = new URL(fullpath); } catch (MalformedURLException e2) { throw new SAXException("Cannot create url for: " + urlString, e2 ); //XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CREATE_URL, new Object[]{urlString}),e2); //"Cannot create url for: " + urlString, e2 ); } } } } catch(SecurityException se) { try { url = new URL("http://xml.apache.org/xslt/"+java.lang.Math.random()); // dummy } catch (MalformedURLException e2) { // I give up } } // System.out.println("url: "+url.toString()); return url; } /** * Add a trace listener for the purposes of debugging and diagnosis. * @param tl Trace listener to be added. */ void addTraceListenersToStylesheet() throws SAXException { /*try { TraceManager tm = m_transformerImpl.getTraceManager(); if(tm.hasTraceListeners) { int nListeners = tm.size(); for(int i = 0; i < nListeners; i++) { TraceListener tl = (TraceListener)m_traceListeners.elementAt(i); if(null != m_stylesheetRoot) m_stylesheetRoot.addTraceListener(tl); } } } catch(TooManyListenersException tmle) { throw new SAXException(XSLMessages.createMessage(XSLTErrorResources.ER_TOO_MANY_LISTENERS, null),tmle ); //"addTraceListenersToStylesheet - TooManyListenersException", tmle); }*/ } /** * Warn the user of an problem. * This is public for access by extensions. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void message(String msg) throws SAXException { message(null, null, msg); } /** * Warn the user of an problem. * This is public for access by extensions. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void message(Node styleNode, Node sourceNode, String msg) throws SAXException { m_problemListener.message(msg); } /** * <meta name="usage" content="internal"/> * Warn the user of an problem. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void warn(int msg) throws SAXException { warn(null, null, msg, null); } /** * <meta name="usage" content="internal"/> * Warn the user of an problem. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void warn(int msg, Object[] args) throws SAXException { warn(null, null, msg, args); } /** * <meta name="usage" content="internal"/> * Warn the user of an problem. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void warn(Node styleNode, Node sourceNode, int msg) throws SAXException { warn(styleNode, sourceNode, msg, null); } /** * <meta name="usage" content="internal"/> * Warn the user of an problem. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void warn(Node styleNode, Node sourceNode, int msg, Object args[]) throws SAXException { Exception e = null; String fmsg = m_XSLMessages.createWarning(msg, args); SourceLocator locator = null; try{ locator = ((StylesheetHandler)(((TransformerFactoryImpl)this.m_tfactory).newTemplatesHandler())).getLocator(); } catch (TransformerConfigurationException tce) { } DefaultErrorHandler handler; if (m_problemListener == null) handler = (DefaultErrorHandler)m_tfactory.getErrorListener(); else handler = (DefaultErrorHandler)m_problemListener.getErrorHandler(); TransformerException te = (null == e) ? new TransformerException(fmsg, locator) : new TransformerException(fmsg, locator, e); if(null != handler) { try{ handler.warning(te); } catch (TransformerException te2) { throw new SAXException(te2); } } else throw new SAXException(te); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(String msg) throws SAXException { Exception e = null; SourceLocator locator = null; try{ locator = ((StylesheetHandler)(((TransformerFactoryImpl)this.m_tfactory).newTemplatesHandler())).getLocator(); } catch (TransformerConfigurationException tce) { } DefaultErrorHandler handler; if (m_problemListener == null) handler = (DefaultErrorHandler)m_tfactory.getErrorListener(); else handler = (DefaultErrorHandler)m_problemListener.getErrorHandler(); TransformerException te = (null == e) ? new TransformerException(msg, locator) : new TransformerException(msg, locator, e); if(null != handler) { try{ handler.fatalError(te); } catch (TransformerException te2) { throw new SAXException(te2); } } else throw new SAXException(te); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(int msg) throws SAXException { error(null, null, msg, null); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(int msg, Object[] args) throws SAXException { error(null, null, msg, args); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(int msg, Exception e) throws SAXException { error(msg, null, e); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(int msg, Object args[], Exception e) throws SAXException { String fmsg = m_XSLMessages.createMessage(msg, args); SourceLocator locator = null; try{ locator = ((StylesheetHandler)(((TransformerFactoryImpl)this.m_tfactory).newTemplatesHandler())).getLocator(); } catch (TransformerConfigurationException tce) { } DefaultErrorHandler handler; if (m_problemListener == null) handler = (DefaultErrorHandler)m_tfactory.getErrorListener(); else handler = (DefaultErrorHandler)m_problemListener.getErrorHandler(); TransformerException te = (null == e) ? new TransformerException(fmsg, locator) : new TransformerException(fmsg, locator, e); if(null != handler) { try{ handler.fatalError(te); } catch (TransformerException te2) { throw new SAXException(te2); } } else throw new SAXException(te); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(Node styleNode, Node sourceNode, int msg) throws SAXException { error(styleNode, sourceNode, msg, null); } /** * <meta name="usage" content="internal"/> * Tell the user of an error, and probably throw an * exception. * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide * the error condition is severe enough to halt processing. */ public void error(Node styleNode, Node sourceNode, int msg, Object args[]) throws SAXException { Exception e = null; String fmsg = m_XSLMessages.createMessage(msg, args); SourceLocator locator = null; try{ locator = ((StylesheetHandler)(((TransformerFactoryImpl)this.m_tfactory).newTemplatesHandler())).getLocator(); } catch (TransformerConfigurationException tce) { } DefaultErrorHandler handler; if (m_problemListener == null) handler = (DefaultErrorHandler)m_tfactory.getErrorListener(); else handler = (DefaultErrorHandler)m_problemListener.getErrorHandler(); TransformerException te = (null == e) ? new TransformerException(fmsg, locator) : new TransformerException(fmsg, locator, e); if(null != handler) { try{ handler.fatalError(te); } catch (TransformerException te2) { throw new SAXException(te2); } } else throw new SAXException(te); } /** * Mark the time, so that displayDuration can later * display the elapse. */ void pushTime(Object key) { if(null != key) { m_durationsTable.put(key, new Long(System.currentTimeMillis())); } } /** * Returns the duration since pushTime was called, * in milliseconds. */ long popDuration(Object key) { long millisecondsDuration = 0; if(null != key) { long start = ((Long)m_durationsTable.get(key)).longValue(); long stop = System.currentTimeMillis(); millisecondsDuration = stop - start; m_durationsTable.remove(key); } return millisecondsDuration; } /** * Display the duration since pushTime was called. */ protected void displayDuration(String info, Object key) { long millisecondsDuration = 0; if(null != key) { long start = ((Long)m_durationsTable.get(key)).longValue(); long stop = System.currentTimeMillis(); millisecondsDuration = stop - start; if(null != m_diagnosticsPrintWriter) { m_diagnosticsPrintWriter.println(info + " took " + millisecondsDuration + " milliseconds"); } m_durationsTable.remove(key); } } /** * If this is set, diagnostics will be * written to the m_diagnosticsPrintWriter stream. If * the value is null, then diagnostics will be turned * off. */ public void setDiagnosticsOutput(java.io.OutputStream out) { setDiagnosticsOutput(new PrintWriter(out)); } /** * If this is set, diagnostics will be * written to the m_diagnosticsPrintWriter stream. If * the value is null, then diagnostics will be turned * off. */ public void setDiagnosticsOutput(java.io.PrintWriter pw) { m_diagnosticsPrintWriter = pw; /* if(getProblemListener() instanceof ProblemListenerDefault) { ((ProblemListenerDefault)getProblemListener()).setDiagnosticsOutput(pw); }*/ } /** * Bottleneck output of diagnostics. */ protected void diag(String s) { if(null != m_diagnosticsPrintWriter) { m_diagnosticsPrintWriter.println(s); } } /** * If this is set to true, simple traces of * template calls are made. */ public void setTraceTemplates(boolean b) { m_traceTemplates = b; } /** * If this is set to true, simple traces of * template calls are made. */ public void setTraceSelect(boolean b) { m_traceSelects = b; } /** * If this is set to true, debug diagnostics about * template children as they are being constructed * will be written to the m_diagnosticsPrintWriter * stream. diagnoseTemplateChildren is false by * default. */ public void setTraceTemplateChildren(boolean b) { m_traceTemplateChildren = b; } // // Lexical handler interface // /** * Report the start of DTD declarations, if any. * * <p>Any declarations are assumed to be in the internal subset * unless otherwise indicated by a {@link #startEntity startEntity} * event.</p> * * <p>Note that the start/endDTD events will appear within * the start/endDocument events from ContentHandler and * before the first startElement event.</p> * * @param name The document type name. * @param publicId The declared public identifier for the * external DTD subset, or null if none was declared. * @param systemId The declared system identifier for the * external DTD subset, or null if none was declared. * @exception SAXException The application may raise an * exception. * @see #endDTD * @see #startEntity */ public void startDTD (String name, String publicId, String systemId) throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().startDTD(name, publicId, systemId); } /** * Report the end of DTD declarations. * * @exception SAXException The application may raise an exception. * @see #startDTD */ public void endDTD () throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().endDTD(); } public void startEntity (String name) throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().startEntity (name); } /** * Report the end of an entity. * * @param name The name of the entity that is ending. * @exception SAXException The application may raise an exception. * @see #startEntity */ public void endEntity (String name) throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().endEntity (name); } /** * Report the start of a CDATA section. * * <p>The contents of the CDATA section will be reported through * the regular {@link org.xml.sax.ContentHandler#characters * characters} event.</p> * * @exception SAXException The application may raise an exception. * @see #endCDATA */ public void startCDATA () throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().startCDATA(); } /** * Report the end of a CDATA section. * * @exception SAXException The application may raise an exception. * @see #startCDATA */ public void endCDATA () throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().endCDATA(); } /** * Report an XML comment anywhere in the document. * * <p>This callback will be used for comments inside or outside the * document element, including comments in the external DTD * subset (if read).</p> * * @param ch An array holding the characters in the comment. * @param start The starting position in the array. * @param length The number of characters to use from the array. * @exception SAXException The application may raise an exception. */ public void comment (char ch[], int start, int length) throws SAXException { if (m_transformerImpl != null && m_transformerImpl.getInputLexicalHandler() != null) m_transformerImpl.getInputLexicalHandler().comment (ch, start, length); } // DocumentHandler interface /** * Receive an object for locating the origin of SAX document events. * * <p>SAX parsers are strongly encouraged (though not absolutely * required) to supply a locator: if it does so, it must supply * the locator to the application by invoking this method before * invoking any of the other methods in the DocumentHandler * interface.</p> * * <p>The locator allows the application to determine the end * position of any document-related event, even if the parser is * not reporting an error. Typically, the application will * use this information for reporting its own errors (such as * character content that does not match an application's * business rules). The information returned by the locator * is probably not sufficient for use with a search engine.</p> * * <p>Note that the locator will return correct information only * during the invocation of the events in this interface. The * application should not attempt to use it at any other time.</p> * * @param locator An object that can return the location of * any SAX document event. * @see org.xml.sax.Locator */ public void setDocumentLocator (Locator locator) {} /** * Receive notification of the beginning of a document. * * <p>The SAX parser will invoke this method only once, before any * other methods in this interface or in DTDHandler (except for * setDocumentLocator).</p> * * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void startDocument () throws SAXException { if (m_documentHandler != null) m_documentHandler.startDocument(); else if (m_transformerImpl != null) m_transformerImpl.getInputContentHandler().startDocument(); //m_transformerImpl.getResultTreeHandler().startDocument(); } /** * Receive notification of the end of a document. * * <p>The SAX parser will invoke this method only once, and it will * be the last method invoked during the parse. The parser shall * not invoke this method until it has either abandoned parsing * (because of an unrecoverable error) or reached the end of * input.</p> * * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void endDocument () throws SAXException { if (m_documentHandler != null) m_documentHandler.endDocument(); else if (m_transformerImpl != null) m_transformerImpl.getInputContentHandler().endDocument(); //m_transformerImpl.getResultTreeHandler().endDocument(); } /** * Receive notification of the beginning of an element. * * <p>The Parser will invoke this method at the beginning of every * element in the XML document; there will be a corresponding * endElement() event for every startElement() event (even when the * element is empty). All of the element's content will be * reported, in order, before the corresponding endElement() * event.</p> * * <p>If the element name has a namespace prefix, the prefix will * still be attached. Note that the attribute list provided will * contain only attributes with explicit values (specified or * defaulted): #IMPLIED attributes will be omitted.</p> * * @param name The element type name. * @param atts The attributes attached to the element, if any. * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see #endElement * @see org.xml.sax.AttributeList */ public void startElement (String name, AttributeList atts) throws SAXException { if (m_documentHandler == null) { m_documentHandler = new ParserAdapter(new org.apache.xerces.parsers.SAXParser()); if (m_transformerImpl != null) { ((ParserAdapter)m_documentHandler).setContentHandler(m_transformerImpl.getInputContentHandler()); } /* else if (m_transformerImpl != null) { int index = name.indexOf(":"); if (index < 0 ) m_transformerImpl.getInputContentHandler().startElement(null, name, name, (Attributes)atts); //getResultTreeHandler().startElement(null, name, name, (Attributes)atts); else m_transformerImpl.getInputContentHandler().startElement(name.substring(0,index), name.substring(index+1), name, (Attributes)atts); //m_transformerImpl.getResultTreeHandler().startElement(name.substring(0,index), name.substring(index+1), name, (Attributes)atts); */ } m_documentHandler.startElement(name, atts); } /** * Receive notification of the end of an element. * * <p>The SAX parser will invoke this method at the end of every * element in the XML document; there will be a corresponding * startElement() event for every endElement() event (even when the * element is empty).</p> * * <p>If the element name has a namespace prefix, the prefix will * still be attached to the name.</p> * * @param name The element type name * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void endElement (String name) throws SAXException { if (m_documentHandler == null) { m_documentHandler = new ParserAdapter(new org.apache.xerces.parsers.SAXParser()); if (m_transformerImpl != null) { ((ParserAdapter)m_documentHandler).setContentHandler(m_transformerImpl.getInputContentHandler()); } /* else if (m_transformerImpl != null) { int index = name.indexOf(":"); if (index < 0 ) m_transformerImpl.getInputContentHandler().endElement(null, name, name); //m_transformerImpl.getResultTreeHandler().endElement(null, name, name); else m_transformerImpl.getInputContentHandler().endElement(name.substring(0,index), name.substring(index+1), name); //m_transformerImpl.getResultTreeHandler().endElement(name.substring(0,index), name.substring(index+1), name); */ } m_documentHandler.endElement(name); } /** * Receive notification of character data. * * <p>The Parser will call this method to report each chunk of * character data. SAX parsers may return all contiguous character * data in a single chunk, or they may split it into several * chunks; however, all of the characters in any single event * must come from the same external entity, so that the Locator * provides useful information.</p> * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Note that some parsers will report whitespace using the * ignorableWhitespace() method rather than this one (validating * parsers must do so).</p> * * @param ch The characters from the XML document. * @param start The start position in the array. * @param length The number of characters to read from the array. * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see #ignorableWhitespace * @see org.xml.sax.Locator */ public void characters (char ch[], int start, int length) throws SAXException { if (m_documentHandler != null) m_documentHandler.characters(ch, start, length); else if (m_transformerImpl != null) m_transformerImpl.getInputContentHandler().characters(ch, start, length); //m_transformerImpl.getResultTreeHandler().characters(ch, start, length); } /** * Receive notification of ignorable whitespace in element content. * * <p>Validating Parsers must use this method to report each chunk * of ignorable whitespace (see the W3C XML 1.0 recommendation, * section 2.10): non-validating parsers may also use this method * if they are capable of parsing and using content models.</p> * * <p>SAX parsers may return all contiguous whitespace in a single * chunk, or they may split it into several chunks; however, all of * the characters in any single event must come from the same * external entity, so that the Locator provides useful * information.</p> * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * @param ch The characters from the XML document. * @param start The start position in the array. * @param length The number of characters to read from the array. * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see #characters */ public void ignorableWhitespace (char ch[], int start, int length) throws SAXException { if (m_documentHandler != null) m_documentHandler.ignorableWhitespace(ch, start, length); else if (m_transformerImpl != null) m_transformerImpl.getInputContentHandler().ignorableWhitespace(ch, start, length); //m_transformerImpl.getResultTreeHandler().ignorableWhitespace(ch, start, length); } /** * Receive notification of a processing instruction. * * <p>The Parser will invoke this method once for each processing * instruction found: note that processing instructions may occur * before or after the main document element.</p> * * <p>A SAX parser should never report an XML declaration (XML 1.0, * section 2.8) or a text declaration (XML 1.0, section 4.3.1) * using this method.</p> * * @param target The processing instruction target. * @param data The processing instruction data, or null if * none was supplied. * @exception org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. */ public void processingInstruction (String target, String data) throws SAXException { if (m_documentHandler != null) m_documentHandler.processingInstruction(target, data); else if (m_transformerImpl != null) m_transformerImpl.getInputContentHandler().processingInstruction(target, data); //m_transformerImpl.getResultTreeHandler().processingInstruction(target, data); } // Implement XSLTProcessor /** * Set the output stream. Required when the XSLTProcessor is being used * as a SAX DocumentHandler. */ public void setOutputStream(java.io.OutputStream os) { TransformerHandlerImpl handler = new TransformerHandlerImpl(m_transformerImpl, false, null); handler.setResult(new StreamResult(os)); } /** * Convenience function to create an XString. * @param s A valid string. * @return An XString object. */ public XString createXString(String s) { return new XString(s); } /** * Convenience function to create an XObject. * @param o Any java object. * @return An XObject object. */ public XObject createXObject(Object o) { return new XObject(o); } /** * Convenience function to create an XNumber. * @param d Any double number. * @return An XNumber object. */ public XNumber createXNumber(double d) { return new XNumber(d); } /** * Convenience function to create an XBoolean. * @param b boolean value. * @return An XBoolean object. */ public XBoolean createXBoolean(boolean b) { return new XBoolean(b); } /** * Convenience function to create an XNodeSet. * @param nl A NodeList object. * @return An XNodeSet object. */ public XNodeSet createXNodeSet(NodeList nl) { return new XNodeSet(nl); } /** * Convenience function to create an XNodeSet from a node. * @param n A DOM node. * @return An XNodeSet object. */ public XNodeSet createXNodeSet(Node n) { return new XNodeSet(n); } /** * Convenience function to create an XNull. * @return An XNull object. */ public XNull createXNull() { return new XNull(); } /** * Get the XMLParserLiaison that this processor uses. */ public XMLParserLiaison getXMLProcessorLiaison() { return (XMLParserLiaison)m_liaison; } /** * Get the preferred stylesheet for the XSLTInputSource XML document, * as identified by the xml-stylesheet PI, and matching the media and * charset criteria. See <a href="http://www.w3.org/TR/xml-stylesheet/"> * Associating Style Sheets with XML documents</a>. * Does not yet handle the LINK REL="stylesheet" syntax. * * @param media The media attribute to be matched. May be null, in which * case the prefered stylesheet will be used (i.e., alternate = no). * @param title The value of the title attribute to match. May be null. * @param charset The value of the charset attribute to match. May be null. * @returns StylesheetSpec extends XSLTInputSource extedns SAX InputSource; the return value * can be passed to the processStylesheet method. */ public StylesheetSpec getAssociatedStylesheet(XSLTInputSource source, String media, String charset) throws SAXException { /*InputSource[]in = m_processor.getAssociatedStylesheets(source, media, null, charset); if (in.length >0) return (StylesheetSpec)in[0]; else return null; */ try{ Source s = m_tfactory.getAssociatedStylesheet(source.getSourceObject(), media, null, charset); return (StylesheetSpec)s; } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } /** * Get a list of stylesheet specifications for the XSLTInputSource XML document, * as identified by the xml-stylesheet PI, and matching the media and * charset criteria. See <a href="http://www.w3.org/TR/xml-stylesheet/"> * Associating Style Sheets with XML documents</a>. * Does not yet handle the LINK REL="stylesheet" syntax. * * @param media The media attribute to be matched. May be null, in which * case the prefered stylesheet will be used (i.e., alternate = no). * @param title The value of the title attribute to match. May be null. * @param charset The value of the charset attribute to match. May be null. * @returns list of StylesheetSpecs (extend XSLTInputSources extend SAX InputSources; a * list member may be passsed to the processStylesheet method. */ public Vector getAssociatedStylesheets(XSLTInputSource source, String media, String charset) throws SAXException { try{ Source s = m_tfactory.getAssociatedStylesheet(source.getSourceObject(), media, null, charset); Vector v = new Vector(); //for (int i = 0; i< in.length; i++) v.addElement((StylesheetSpec)s); return v; } catch (TransformerConfigurationException tce) { throw new SAXException(tce); } } /** * Submit a top-level stylesheet parameter. This value can * be evaluated in the stylesheet via xsl:param-variable. * @param key The name of the param. * @param value An XObject that will be used. */ public void setStylesheetParam(String key, XObject value) { setParameter(key, value.object()); } /** * Set a top-level stylesheet parameter. This value can * be evaluated via xsl:param-variable. Note that the value * passed is an expression, and not a string. This means that * setStylesheetParam("foo", "hello"); will look for the * element "hello". If you want to pass a string, you'll * need to put quotes around it: * setStylesheetParam("foo", "'hello'"); will look for the * @param key The name of the param. * @param expression An expression that will be evaluated. */ public void setStylesheetParam(String key, String expression) { if (m_evalList == null) m_evalList = new Vector(); if (!m_evalList.contains(key)) m_evalList.addElement(key); setParameter(key, expression); m_needToEval = true; } /** * Set a top-level stylesheet parameter. This value can * be evaluated via xsl:param-variable. Note that the value * passed is an expression, and not a string. This means that * setStylesheetParam("foo", "hello"); will look for the * element "hello". If you want to pass a string, you'll * need to put quotes around it: * setStylesheetParam("foo", "'hello'"); will look for the * @param key The name of the param. * @param expression An expression that will be evaluated. */ public void setParameter(String key, Object value) { if (m_stylesheetParams == null) m_stylesheetParams = new Hashtable(); m_stylesheetParams.put(key, value); } /** * Get the current FormatterListener (SAX DocumentHandler), or null if none has been set. */ public DocumentHandler getFormatterListener() { return m_documentHandler; } /** * Set the FormatterListener (the SAX DocumentHandler). */ public void setFormatterListener(DocumentHandler flistener) { m_documentHandler = flistener; } /** * Get the current SAX DocumentHandler (the same object as the FormatterListener), or null if none has been set. */ public DocumentHandler getDocumentHandler() { return m_documentHandler; } /** * Set the current SAX DocumentHandler (the same * object as the FormatterListener). */ public void setDocumentHandler(DocumentHandler listener) { if (listener instanceof ParserAdapter) m_transformerImpl.setContentHandler(((ParserAdapter)listener).getContentHandler()); else if (listener instanceof XSLTEngineImpl) m_transformerImpl.setContentHandler(((XSLTEngineImpl)listener).getTransformer().getContentHandler()); else if (listener instanceof XMLSerializer) { try{ m_transformerImpl.setContentHandler(((XMLSerializer)listener).asContentHandler()); } catch (IOException ioe) {} } m_documentHandler = listener; } /** * Add a trace listener for the purposes of debugging and diagnosis. * @param tl Trace listener to be added. */ public void addTraceListener(TraceListener tl) throws TooManyListenersException { if (m_transformerImpl != null) m_transformerImpl.getTraceManager().addTraceListener(tl); } /** * If set to true (the default), pattern conflict warnings are not printed to the diagnostics stream. * @param b true if conflict warnings should be suppressed. */ public void setQuietConflictWarnings(boolean b) { if (m_transformerImpl != null) m_transformerImpl.setQuietConflictWarnings(b); } /** * Remove a trace listener. * @param tl Trace listener to be removed. */ public void removeTraceListener(TraceListener tl) { if (m_transformerImpl != null) m_transformerImpl.getTraceManager().removeTraceListener(tl); } /** * Set the problem listener property. * The XSL class can have a single listener to be informed * of errors and warnings. The problem listener normally controls whether an exception * is thrown or not (or the problem listeners can throw its own RuntimeException). * @param l A ProblemListener interface. */ public void setProblemListener(ProblemListener l) { if (l instanceof ProblemListenerDefault) m_problemListener = (ProblemListenerDefault)l; else m_problemListener = new ProblemListenerDefault(l); if (m_transformerImpl != null) m_transformerImpl.setErrorListener(m_problemListener); m_tfactory.setErrorListener(m_problemListener); } /** * Get the problem listener property. * The XSL class can have a single listener to be informed * of errors and warnings. The problem listener normally controls whether an exception * is thrown or not (or the problem listener can throw its own RuntimeException). * @return A ProblemListener interface. */ public ProblemListener getProblemListener() { if (m_problemListener.getProblemListener() != null) return m_problemListener.getProblemListener(); else return m_problemListener; } public TransformerImpl getTransformer() { return m_transformerImpl; //return (StylesheetProcessor)m_processor; } public TransformerFactoryImpl getTransformerFactory() { return (TransformerFactoryImpl)m_tfactory; } } // end XSLTEngineImpl class