/* * 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.xml.dtm.ref; import org.apache.xml.dtm.*; import java.util.Vector; // JAXP 1.1 import javax.xml.parsers.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.Source; import javax.xml.transform.SourceLocator; // Apache XML Utilities import org.apache.xml.utils.PrefixResolver; import org.apache.xml.utils.SystemIDResolver; import org.apache.xml.dtm.ref.dom2dtm.DOM2DTM; import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM; // W3C DOM import org.w3c.dom.Document; import org.w3c.dom.Node; // SAX2 import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.ContentHandler; import org.xml.sax.EntityResolver; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.helpers.XMLReaderFactory; import org.xml.sax.ext.DeclHandler; import org.xml.sax.ext.LexicalHandler; import org.apache.xml.utils.XMLString; import org.apache.xml.utils.XMLStringFactory; /** * The default implementation for the DTMManager. */ public class DTMManagerDefault extends DTMManager { /** * Vector of DTMs that this manager manages. */ protected DTM m_dtms[] = new DTM[4095]; /** * Add a DTM to the DTM table. * * @param dtm Should be a valid reference to a DTM. */ public void addDTM(DTM dtm, int id) { m_dtms[id] = dtm; } /** * Get the first free DTM ID available. */ public int getFirstFreeDTMID() { int n = m_dtms.length; for (int i = 1; i < n; i++) { if(null == m_dtms[i]) { return i; } } throw new DTMException("No more DTM IDs are available!"); } /** * The default table for exandedNameID lookups. */ private ExpandedNameTable m_expandedNameTable = new ExpandedNameTable(); /** * Constructor DTMManagerDefault * */ public DTMManagerDefault(){} /** Set this to true if you want a dump of the DTM after creation. */ private static final boolean DUMPTREE = false; /** Set this to true if you want a basic diagnostics. */ private static final boolean DEBUG = false; /** * Get an instance of a DTM, loaded with the content from the * specified source. If the unique flag is true, a new instance will * always be returned. Otherwise it is up to the DTMManager to return a * new instance or an instance that it already created and may be being used * by someone else. * (I think more parameters will need to be added for error handling, and entity * resolution). * * @param source the specification of the source object. * @param unique true if the returned DTM must be unique, probably because it * is going to be mutated. * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may * be null. * @param incremental true if the DTM should be built incrementally, if * possible. * @param doIndexing true if the caller considers it worth it to use * indexing schemes. * * @return a non-null DTM reference. */ public DTM getDTM(Source source, boolean unique, DTMWSFilter whiteSpaceFilter, boolean incremental, boolean doIndexing) { if(DEBUG && null != source) System.out.println("Starting source: "+source.getSystemId()); XMLStringFactory xstringFactory = m_xsf; int dtmPos = getFirstFreeDTMID(); int documentID = dtmPos << 20; if ((null != source) && source instanceof DOMSource) { DOM2DTM dtm = new DOM2DTM(this, (DOMSource) source, documentID, whiteSpaceFilter, xstringFactory, doIndexing); addDTM(dtm, dtmPos); // if (DUMPTREE) // { // dtm.dumpDTM(); // } return dtm; } else { boolean isSAXSource = (null != source) ? (source instanceof SAXSource) : true; boolean isStreamSource = (null != source) ? (source instanceof StreamSource) : false; if (isSAXSource || isStreamSource) { XMLReader reader; InputSource xmlSource; if (null == source) { xmlSource = null; reader = null; } else { reader = getXMLReader(source); xmlSource = SAXSource.sourceToInputSource(source); String urlOfSource = xmlSource.getSystemId(); if (null != urlOfSource) { try { urlOfSource = SystemIDResolver.getAbsoluteURI(urlOfSource); } catch (Exception e) { // %REVIEW% Is there a better way to send a warning? System.err.println("Can not absolutize URL: " + urlOfSource); } xmlSource.setSystemId(urlOfSource); } } // Create the basic SAX2DTM. SAX2DTM dtm = new SAX2DTM(this, source, documentID, whiteSpaceFilter, xstringFactory, doIndexing); // Go ahead and add the DTM to the lookup table. This needs to be // done before any parsing occurs. addDTM(dtm, dtmPos); boolean haveXercesParser = (null != reader) && (reader instanceof org.apache.xerces.parsers.SAXParser); if (haveXercesParser) incremental = true; // No matter what. %REVIEW% if (this.m_incremental && incremental) { // Create a CoroutineManager to manage the coordination between the // parser and the transformation. This will "throttle" between // the parser and the calling application. CoroutineManager coroutineManager = new CoroutineManager(); // Create an CoRoutine ID for the transformation. int appCoroutine = coroutineManager.co_joinCoroutineSet(-1); CoroutineParser coParser; if (haveXercesParser) { // CoroutineSAXParser_Xerces to avoid threading. // System.out.println("Using CoroutineSAXParser_Xerces to avoid threading"); coParser = new CoroutineSAXParser_Xerces( (org.apache.xerces.parsers.SAXParser) reader, coroutineManager, appCoroutine); } else { // Create a CoroutineSAXParser that will run on the secondary thread. if (null == reader) coParser = new CoroutineSAXParser(coroutineManager, appCoroutine); else coParser = new CoroutineSAXParser(coroutineManager, appCoroutine, reader); } // Have the DTM set itself up as the CoroutineSAXParser's listener. dtm.setCoroutineParser(coParser, appCoroutine); // Get the parser's CoRoutine ID. int parserCoroutine = coParser.getParserCoroutineID(); if (null == xmlSource) { // Then the user will construct it themselves. return dtm; } // System.out.println("parserCoroutine (mgr): "+parserCoroutine); // %TBD% It's probably OK to have these bypass the CoRoutine stuff?? // Or maybe not? // ... Depends on how broken will things get if they occur at the same // time that someone is trying to read the DTM model. I'd suggest that // we instead extend CoroutineParser to handle these, and let it // pass the registration through to the reader if that's the Right Thng reader.setDTDHandler(dtm); reader.setErrorHandler(dtm); try { // %REVIEW% Consider making coParser just be a throttling filter Object gotMore = coParser.doParse(xmlSource, appCoroutine); if (gotMore instanceof Exception) { dtm.clearCoRoutine(); throw ((Exception) gotMore); } else if (gotMore != Boolean.TRUE) { // %REVIEW% Consider having coParser self-terminate at end of file. dtm.clearCoRoutine(); } } catch (RuntimeException re) { // coroutineManager.co_exit(appCoroutine); dtm.clearCoRoutine(); throw re; } catch (Exception e) { // coroutineManager.co_exit(appCoroutine); dtm.clearCoRoutine(); throw new org.apache.xml.utils.WrappedRuntimeException(e); } } else { if (null == reader) { // Then the user will construct it themselves. return dtm; } // not incremental reader.setContentHandler(dtm); reader.setDTDHandler(dtm); reader.setErrorHandler(dtm); try { reader.setProperty( "http://xml.org/sax/properties/lexical-handler", dtm); } catch (SAXNotRecognizedException e){} catch (SAXNotSupportedException e){} try { reader.parse(xmlSource); } catch (RuntimeException re) { // coroutineManager.co_exit(appCoroutine); dtm.clearCoRoutine(); throw re; } catch (Exception e) { // coroutineManager.co_exit(appCoroutine); dtm.clearCoRoutine(); throw new org.apache.xml.utils.WrappedRuntimeException(e); } } if (DUMPTREE) { System.out.println("Dumping SAX2DOM"); dtm.dumpDTM(); } return dtm; } else { // It should have been handled by a derived class or the caller // made a mistake. throw new DTMException("Not supported: " + source); } } } /** * Given a W3C DOM node, try and return a DTM handle. * Note: calling this may be non-optimal, and there is no guarantee that * the node will be found in any particular DTM. * * @param node Non-null reference to a DOM node. * * @return a valid DTM handle. */ public int getDTMHandleFromNode(org.w3c.dom.Node node) { if (node instanceof org.apache.xml.dtm.ref.DTMNodeProxy) return ((org.apache.xml.dtm.ref.DTMNodeProxy) node).getDTMNodeNumber(); else { // Find the DOM2DTMs wrapped around this Document (if any) // and check whether they contain the Node in question. // // NOTE that since a DOM2DTM may represent a subtree rather // than a full document, we have to be prepared to check more // than one -- and there is no guarantee that we will find // one that contains ancestors or siblings of the node we're // seeking. // // %REVIEW% We could search for the one which contains this // node at the deepest level, and thus covers the widest // subtree, but that's going to entail additional work // checking more DTMs... and getHandleFromNode is not a // cheap operation in most implementations. // [I had to change this to look forward... sorry. -sb] int max = m_dtms.length; for(int i = 0; i < max; i++) { DTM thisDTM=m_dtms[i]; if((null != thisDTM) && thisDTM instanceof DOM2DTM) { int handle=((DOM2DTM)thisDTM).getHandleOfNode(node); if(handle!=DTM.NULL) return handle; } } // Fallback: Not found in one we know how to search. // Current solution: Generate a new DOM2DTM. // %REVIEW% Maybe the best I can do?? Node root = node.getOwnerDocument(); if(null == root) root = node; DTM dtm = getDTM(new javax.xml.transform.dom.DOMSource(root), false, null, true, true); return ((DOM2DTM)dtm).getHandleOfNode(node); } } /** * This method returns the SAX2 parser to use with the InputSource * obtained from this URI. * It may return null if any SAX2-conformant XML parser can be used, * or if getInputSource() will also return null. The parser must * be free for use (i.e. * not currently in use for another parse(). * * @param inputSource The value returned from the URIResolver. * @returns a SAX2 XMLReader to use to resolve the inputSource argument. * * @return non-null XMLReader reference ready to parse. */ public XMLReader getXMLReader(Source inputSource) { try { XMLReader reader = (inputSource instanceof SAXSource) ? ((SAXSource) inputSource).getXMLReader() : null; boolean isUserReader = (reader != null); if (null == reader) { try { javax.xml.parsers.SAXParserFactory factory = javax.xml.parsers.SAXParserFactory.newInstance(); factory.setNamespaceAware(true); javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser(); reader = jaxpParser.getXMLReader(); } catch (javax.xml.parsers.ParserConfigurationException ex) { throw new org.xml.sax.SAXException(ex); } catch (javax.xml.parsers.FactoryConfigurationError ex1) { throw new org.xml.sax.SAXException(ex1.toString()); } catch (NoSuchMethodError ex2){} catch (AbstractMethodError ame){} if (null == reader) reader = XMLReaderFactory.createXMLReader(); } try { reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); } catch (org.xml.sax.SAXException se) { // What can we do? // TODO: User diagnostics. } // Commented out as per discussion with Thomas2.Maesing@bgs-ag.de // about bug 2124. // if(!isUserReader) // { // try // { // reader.setFeature("http://apache.org/xml/features/validation/dynamic", // true); // } // catch (org.xml.sax.SAXException se) // { // // // What can we do? // // TODO: User diagnostics. // } // } return reader; } catch (org.xml.sax.SAXException se) { throw new DTMException(se.getMessage(), se); } } /** * NEEDSDOC Method getDTM * * * NEEDSDOC @param nodeHandle * * NEEDSDOC (getDTM) @return */ public DTM getDTM(int nodeHandle) { // Performance critical function. return m_dtms[nodeHandle >> 20]; } /** * Given a DTM, find it's ID number in the DTM list. * * * @param dtm The DTM reference in question. * * @return The ID, or -1 if not found in the list. */ public int getDTMIdentity(DTM dtm) { // A backwards search should normally be the fastest. // [But we can't do it... sorry. -sb] int n = m_dtms.length; for (int i = 0; i < n; i++) { DTM tdtm = m_dtms[i]; if (tdtm == dtm) return i; } return -1; } /** * NEEDSDOC Method release * * * NEEDSDOC @param dtm * NEEDSDOC @param shouldHardDelete * * NEEDSDOC (release) @return */ public boolean release(DTM dtm, boolean shouldHardDelete) { if (dtm instanceof SAX2DTM) { ((SAX2DTM) dtm).clearCoRoutine(); } int i = getDTMIdentity(dtm); if (i >= 0) { m_dtms[i] = null; } return true; } /** * Method createDocumentFragment * * * NEEDSDOC (createDocumentFragment) @return */ public DTM createDocumentFragment() { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.newDocument(); Node df = doc.createDocumentFragment(); return getDTM(new DOMSource(df), true, null, false, false); } catch (Exception e) { throw new DTMException(e); } } /** * NEEDSDOC Method createDTMIterator * * * NEEDSDOC @param whatToShow * NEEDSDOC @param filter * NEEDSDOC @param entityReferenceExpansion * * NEEDSDOC (createDTMIterator) @return */ public DTMIterator createDTMIterator(int whatToShow, DTMFilter filter, boolean entityReferenceExpansion) { /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */ return null; } /** * NEEDSDOC Method createDTMIterator * * * NEEDSDOC @param xpathString * NEEDSDOC @param presolver * * NEEDSDOC (createDTMIterator) @return */ public DTMIterator createDTMIterator(String xpathString, PrefixResolver presolver) { /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */ return null; } /** * NEEDSDOC Method createDTMIterator * * * NEEDSDOC @param node * * NEEDSDOC (createDTMIterator) @return */ public DTMIterator createDTMIterator(int node) { /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */ return null; } /** * NEEDSDOC Method createDTMIterator * * * NEEDSDOC @param xpathCompiler * NEEDSDOC @param pos * * NEEDSDOC (createDTMIterator) @return */ public DTMIterator createDTMIterator(Object xpathCompiler, int pos) { /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */ return null; } /** * return the expanded name table. * * NEEDSDOC @param dtm * * NEEDSDOC ($objectName$) @return */ public ExpandedNameTable getExpandedNameTable(DTM dtm) { return m_expandedNameTable; } }