/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ /* * $Id: DTMDocumentImpl.java 468653 2006-10-28 07:07:05Z minchau $ */ package org.apache.xml.dtm.ref; import javax.xml.transform.SourceLocator; import org.apache.xml.dtm.DTM; import org.apache.xml.dtm.DTMAxisIterator; import org.apache.xml.dtm.DTMAxisTraverser; import org.apache.xml.dtm.DTMManager; import org.apache.xml.dtm.DTMWSFilter; import org.apache.xml.utils.FastStringBuffer; import org.apache.xml.utils.XMLString; import org.apache.xml.utils.XMLStringFactory; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.ext.LexicalHandler; /** * This is the implementation of the DTM document interface. It receives * requests from an XML content handler similar to that of an XML DOM or SAX parser * to store information from the xml document in an array based * dtm table structure. This informtion is used later for document navigation, * query, and SAX event dispatch functions. The DTM can also be used directly as a * document composition model for an application. The requests received are: * *

State: In progress!!

* * %REVIEW% I _think_ the SAX convention is that "no namespace" is expressed * as "" rather than as null (which is the DOM's convention). What should * DTM expect? What should it do with the other? * *

Origin: the implemention is a composite logic based on the DTM of XalanJ1 and * DocImpl, DocumentImpl, ElementImpl, TextImpl, etc. of XalanJ2

*/ public class DTMDocumentImpl implements DTM, org.xml.sax.ContentHandler, org.xml.sax.ext.LexicalHandler { // Number of lower bits used to represent node index. protected static final byte DOCHANDLE_SHIFT = 22; // Masks the lower order of node handle. // Same as {@link DTMConstructor.IDENT_NODE_DEFAULT} protected static final int NODEHANDLE_MASK = (1 << (DOCHANDLE_SHIFT + 1)) - 1; // Masks the higher order Document handle // Same as {@link DTMConstructor.IDENT_DOC_DEFAULT} protected static final int DOCHANDLE_MASK = -1 - NODEHANDLE_MASK; int m_docHandle = NULL; // masked document handle for this dtm document int m_docElement = NULL; // nodeHandle to the root of the actual dtm doc content // Context for parse-and-append operations int currentParent = 0; // current parent - default is document root int previousSibling = 0; // previous sibling - no previous sibling protected int m_currentNode = -1; // current node // The tree under construction can itself be used as // the element stack, so m_elemStack isn't needed. //protected Stack m_elemStack = new Stack(); // element stack private boolean previousSiblingWasParent = false; // Local cache for record-at-a-time fetch int gotslot[] = new int[4]; // endDocument recieved? private boolean done = false; boolean m_isError = false; private final boolean DEBUG = false; /** The document base URI. */ protected String m_documentBaseURI; /** If we're building the model incrementally on demand, we need to * be able to tell the source when to send us more data. * * Note that if this has not been set, and you attempt to read ahead * of the current build point, we'll probably throw a null-pointer * exception. We could try to wait-and-retry instead, as a very poor * fallback, but that has all the known problems with multithreading * on multiprocessors and we Don't Want to Go There. * * @see setIncrementalSAXSource */ private IncrementalSAXSource m_incrSAXSource=null; // ========= DTM data structure declarations. ============== // nodes array: integer array blocks to hold the first level reference of the nodes, // each reference slot is addressed by a nodeHandle index value. // Assumes indices are not larger than {@link NODEHANDLE_MASK} // ({@link DOCHANDLE_SHIFT} bits). ChunkedIntArray nodes = new ChunkedIntArray(4); // text/comment table: string buffer to hold the text string values of the document, // each of which is addressed by the absolute offset and length in the buffer private FastStringBuffer m_char = new FastStringBuffer(); // Start of string currently being accumulated into m_char; // needed because the string may be appended in several chunks. private int m_char_current_start=0; // %TBD% INITIALIZATION/STARTUP ISSUES // -- Should we really be creating these, or should they be // passed in from outside? Scott want to be able to share // pools across multiple documents, so setting them here is // probably not the right default. private DTMStringPool m_localNames = new DTMStringPool(); private DTMStringPool m_nsNames = new DTMStringPool(); private DTMStringPool m_prefixNames = new DTMStringPool(); // %TBD% If we use the current ExpandedNameTable mapper, it // needs to be bound to the NS and local name pools. Which // means it needs to attach to them AFTER we've resolved their // startup. Or it needs to attach to this document and // retrieve them each time. Or this needs to be // an interface _implemented_ by this class... which might be simplest! private ExpandedNameTable m_expandedNames= new ExpandedNameTable(); private XMLStringFactory m_xsf; /** * Construct a DTM. * * @param documentNumber the ID number assigned to this document. * It will be shifted up into the high bits and returned as part of * all node ID numbers, so those IDs indicate which document they * came from as well as a location within the document. It is the * DTMManager's responsibility to assign a unique number to each * document. */ public DTMDocumentImpl(DTMManager mgr, int documentNumber, DTMWSFilter whiteSpaceFilter, XMLStringFactory xstringfactory){ initDocument(documentNumber); // clear nodes and document handle m_xsf = xstringfactory; } /** Bind a IncrementalSAXSource to this DTM. If we discover we need nodes * that have not yet been built, we will ask this object to send us more * events, and it will manage interactions with its data sources. * * Note that we do not actually build the IncrementalSAXSource, since we don't * know what source it's reading from, what thread that source will run in, * or when it will run. * * @param source The IncrementalSAXSource that we want to recieve events from * on demand. */ public void setIncrementalSAXSource(IncrementalSAXSource source) { m_incrSAXSource=source; // Establish SAX-stream link so we can receive the requested data source.setContentHandler(this); source.setLexicalHandler(this); // Are the following really needed? IncrementalSAXSource doesn't yet // support them, and they're mostly no-ops here... //source.setErrorHandler(this); //source.setDTDHandler(this); //source.setDeclHandler(this); } /** * Wrapper for ChunkedIntArray.append, to automatically update the * previous sibling's "next" reference (if necessary) and periodically * wake a reader who may have encountered incomplete data and entered * a wait state. * @param w0 int As in ChunkedIntArray.append * @param w1 int As in ChunkedIntArray.append * @param w2 int As in ChunkedIntArray.append * @param w3 int As in ChunkedIntArray.append * @return int As in ChunkedIntArray.append * @see ChunkedIntArray.append */ private final int appendNode(int w0, int w1, int w2, int w3) { // A decent compiler may inline this. int slotnumber = nodes.appendSlot(w0, w1, w2, w3); if (DEBUG) System.out.println(slotnumber+": "+w0+" "+w1+" "+w2+" "+w3); if (previousSiblingWasParent) nodes.writeEntry(previousSibling,2,slotnumber); previousSiblingWasParent = false; // Set the default; endElement overrides return slotnumber; } // ========= DTM Implementation Control Functions. ============== /** * Set an implementation dependent feature. *

* %REVIEW% Do we really expect to set features on DTMs? * * @param featureId A feature URL. * @param state true if this feature should be on, false otherwise. */ public void setFeature(String featureId, boolean state) {}; /** * Set a reference pointer to the element name symbol table. * %REVIEW% Should this really be Public? Changing it while * DTM is in use would be a disaster. * * @param poolRef DTMStringPool reference to an instance of table. */ public void setLocalNameTable(DTMStringPool poolRef) { m_localNames = poolRef; } /** * Get a reference pointer to the element name symbol table. * * @return DTMStringPool reference to an instance of table. */ public DTMStringPool getLocalNameTable() { return m_localNames; } /** * Set a reference pointer to the namespace URI symbol table. * %REVIEW% Should this really be Public? Changing it while * DTM is in use would be a disaster. * * @param poolRef DTMStringPool reference to an instance of table. */ public void setNsNameTable(DTMStringPool poolRef) { m_nsNames = poolRef; } /** * Get a reference pointer to the namespace URI symbol table. * * @return DTMStringPool reference to an instance of table. */ public DTMStringPool getNsNameTable() { return m_nsNames; } /** * Set a reference pointer to the prefix name symbol table. * %REVIEW% Should this really be Public? Changing it while * DTM is in use would be a disaster. * * @param poolRef DTMStringPool reference to an instance of table. */ public void setPrefixNameTable(DTMStringPool poolRef) { m_prefixNames = poolRef; } /** * Get a reference pointer to the prefix name symbol table. * * @return DTMStringPool reference to an instance of table. */ public DTMStringPool getPrefixNameTable() { return m_prefixNames; } /** * Set a reference pointer to the content-text repository * * @param buffer FastStringBuffer reference to an instance of * buffer */ void setContentBuffer(FastStringBuffer buffer) { m_char = buffer; } /** * Get a reference pointer to the content-text repository * * @return FastStringBuffer reference to an instance of buffer */ FastStringBuffer getContentBuffer() { return m_char; } /** getContentHandler returns "our SAX builder" -- the thing that * someone else should send SAX events to in order to extend this * DTM model. * * @return null if this model doesn't respond to SAX events, * "this" if the DTM object has a built-in SAX ContentHandler, * the IncrementalSAXSource if we're bound to one and should receive * the SAX stream via it for incremental build purposes... * */ public org.xml.sax.ContentHandler getContentHandler() { if (m_incrSAXSource instanceof IncrementalSAXSource_Filter) return (ContentHandler) m_incrSAXSource; else return this; } /** * Return this DTM's lexical handler. * * %REVIEW% Should this return null if constrution already done/begun? * * @return null if this model doesn't respond to lexical SAX events, * "this" if the DTM object has a built-in SAX ContentHandler, * the IncrementalSAXSource if we're bound to one and should receive * the SAX stream via it for incremental build purposes... */ public LexicalHandler getLexicalHandler() { if (m_incrSAXSource instanceof IncrementalSAXSource_Filter) return (LexicalHandler) m_incrSAXSource; else return this; } /** * Return this DTM's EntityResolver. * * @return null if this model doesn't respond to SAX entity ref events. */ public org.xml.sax.EntityResolver getEntityResolver() { return null; } /** * Return this DTM's DTDHandler. * * @return null if this model doesn't respond to SAX dtd events. */ public org.xml.sax.DTDHandler getDTDHandler() { return null; } /** * Return this DTM's ErrorHandler. * * @return null if this model doesn't respond to SAX error events. */ public org.xml.sax.ErrorHandler getErrorHandler() { return null; } /** * Return this DTM's DeclHandler. * * @return null if this model doesn't respond to SAX Decl events. */ public org.xml.sax.ext.DeclHandler getDeclHandler() { return null; } /** @return true iff we're building this model incrementally (eg * we're partnered with a IncrementalSAXSource) and thus require that the * transformation and the parse run simultaneously. Guidance to the * DTMManager. * */ public boolean needsTwoThreads() { return null!=m_incrSAXSource; } //================================================================ // ========= SAX2 ContentHandler methods ========= // Accept SAX events, use them to build/extend the DTM tree. // Replaces the deprecated DocumentHandler interface. public void characters(char[] ch, int start, int length) throws org.xml.sax.SAXException { // Actually creating the text node is handled by // processAccumulatedText(); here we just accumulate the // characters into the buffer. m_char.append(ch,start,length); } // Flush string accumulation into a text node private void processAccumulatedText() { int len=m_char.length(); if(len!=m_char_current_start) { // The FastStringBuffer has been previously agreed upon appendTextChild(m_char_current_start,len-m_char_current_start); m_char_current_start=len; } } public void endDocument() throws org.xml.sax.SAXException { // May need to tell the low-level builder code to pop up a level. // There _should't_ be any significant pending text at this point. appendEndDocument(); } public void endElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName) throws org.xml.sax.SAXException { processAccumulatedText(); // No args but we do need to tell the low-level builder code to // pop up a level. appendEndElement(); } public void endPrefixMapping(java.lang.String prefix) throws org.xml.sax.SAXException { // No-op } public void ignorableWhitespace(char[] ch, int start, int length) throws org.xml.sax.SAXException { // %TBD% I believe ignorable text isn't part of the DTM model...? } public void processingInstruction(java.lang.String target, java.lang.String data) throws org.xml.sax.SAXException { processAccumulatedText(); // %TBD% Which pools do target and data go into? } public void setDocumentLocator(Locator locator) { // No-op for DTM } public void skippedEntity(java.lang.String name) throws org.xml.sax.SAXException { processAccumulatedText(); //%TBD% } public void startDocument() throws org.xml.sax.SAXException { appendStartDocument(); } public void startElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName, Attributes atts) throws org.xml.sax.SAXException { processAccumulatedText(); // %TBD% Split prefix off qname String prefix=null; int colon=qName.indexOf(':'); if(colon>0) prefix=qName.substring(0,colon); // %TBD% Where do we pool expandedName, or is it just the union, or... /**/System.out.println("Prefix="+prefix+" index="+m_prefixNames.stringToIndex(prefix)); appendStartElement(m_nsNames.stringToIndex(namespaceURI), m_localNames.stringToIndex(localName), m_prefixNames.stringToIndex(prefix)); /////// %TBD% // %TBD% I'm assuming that DTM will require resequencing of // NS decls before other attrs, hence two passes are taken. // %TBD% Is there an easier way to test for NSDecl? int nAtts=(atts==null) ? 0 : atts.getLength(); // %TBD% Countdown is more efficient if nobody cares about sequence. for(int i=nAtts-1;i>=0;--i) { qName=atts.getQName(i); if(qName.startsWith("xmlns:") || "xmlns".equals(qName)) { prefix=null; colon=qName.indexOf(':'); if(colon>0) { prefix=qName.substring(0,colon); } else { // %REVEIW% Null or ""? prefix=null; // Default prefix } appendNSDeclaration( m_prefixNames.stringToIndex(prefix), m_nsNames.stringToIndex(atts.getValue(i)), atts.getType(i).equalsIgnoreCase("ID")); } } for(int i=nAtts-1;i>=0;--i) { qName=atts.getQName(i); if(!(qName.startsWith("xmlns:") || "xmlns".equals(qName))) { // %TBD% I hate having to extract the prefix into a new // string when we may never use it. Consider pooling whole // qNames, which are already strings? prefix=null; colon=qName.indexOf(':'); if(colon>0) { prefix=qName.substring(0,colon); localName=qName.substring(colon+1); } else { prefix=""; // Default prefix localName=qName; } m_char.append(atts.getValue(i)); // Single-string value int contentEnd=m_char.length(); if(!("xmlns".equals(prefix) || "xmlns".equals(qName))) appendAttribute(m_nsNames.stringToIndex(atts.getURI(i)), m_localNames.stringToIndex(localName), m_prefixNames.stringToIndex(prefix), atts.getType(i).equalsIgnoreCase("ID"), m_char_current_start, contentEnd-m_char_current_start); m_char_current_start=contentEnd; } } } public void startPrefixMapping(java.lang.String prefix, java.lang.String uri) throws org.xml.sax.SAXException { // No-op in DTM, handled during element/attr processing? } // // LexicalHandler support. Not all SAX2 parsers support these events // but we may want to pass them through when they exist... // public void comment(char[] ch, int start, int length) throws org.xml.sax.SAXException { processAccumulatedText(); m_char.append(ch,start,length); // Single-string value appendComment(m_char_current_start,length); m_char_current_start+=length; } public void endCDATA() throws org.xml.sax.SAXException { // No-op in DTM } public void endDTD() throws org.xml.sax.SAXException { // No-op in DTM } public void endEntity(java.lang.String name) throws org.xml.sax.SAXException { // No-op in DTM } public void startCDATA() throws org.xml.sax.SAXException { // No-op in DTM } public void startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) throws org.xml.sax.SAXException { // No-op in DTM } public void startEntity(java.lang.String name) throws org.xml.sax.SAXException { // No-op in DTM } //================================================================ // ========= Document Handler Functions ========= // %REVIEW% jjk -- DocumentHandler is SAX Level 1, and deprecated.... // and this wasn't a fully compliant or declared implementation of that API // in any case. Phase out in favor of SAX2 ContentHandler/LexicalHandler /** * Reset a dtm document to its initial (empty) state. * * The DTMManager will invoke this method when the dtm is created. * * @param documentNumber the handle for the DTM document. */ final void initDocument(int documentNumber) { // save masked DTM document handle m_docHandle = documentNumber<The content handler will invoke this method only once, and it will // * be the last method invoked during the parse. The handler shall not // * not invoke this method until it has either abandoned parsing // * (because of an unrecoverable error) or reached the end of // * input.

// */ // public void documentEnd() // { // done = true; // // %TBD% may need to notice the last slot number and slot count to avoid // // residual data from provious use of this DTM // } // /** // * Receive notification of the beginning of a document. // * // *

The SAX parser will invoke this method only once, before any // * other methods in this interface.

// */ // public void reset() // { // // %TBD% reset slot 0 to indicate ChunkedIntArray reuse or wait for // // the next initDocument(). // m_docElement = NULL; // reset nodeHandle to the root of the actual dtm doc content // initDocument(0); // } // /** // * Factory method; creates an Element node in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // *

The XML content handler will invoke endElement() method after all // * of the element's content are processed in order to give DTM the indication // * to prepare and patch up parent and sibling node pointers.

// * // *

The following interface for createElement will use an index value corresponds // * to the symbol entry in the DTMDStringPool based symbol tables.

// * // * @param nsIndex The namespace of the node // * @param nameIndex The element name. // * @see #endElement // * @see org.xml.sax.Attributes // * @return nodeHandle int of the element created // */ // public int createElement(int nsIndex, int nameIndex, Attributes atts) // { // // do document root node creation here on the first element, create nodes for // // this element and its attributes, store the element, namespace, and attritute // // name indexes to the nodes array, keep track of the current node and parent // // element used // // W0 High: Namespace Low: Node Type // int w0 = (nsIndex << 16) | ELEMENT_NODE; // // W1: Parent // int w1 = currentParent; // // W2: Next (initialized as 0) // int w2 = 0; // // W3: Tagname // int w3 = nameIndex; // //int ourslot = nodes.appendSlot(w0, w1, w2, w3); // int ourslot = appendNode(w0, w1, w2, w3); // currentParent = ourslot; // previousSibling = 0; // setAttributes(atts); // // set the root element pointer when creating the first element node // if (m_docElement == NULL) // m_docElement = ourslot; // return (m_docHandle | ourslot); // } // // Factory method to create an Element node not associated with a given name space // // using String value parameters passed in from a content handler or application // /** // * Factory method; creates an Element node not associated with a given name space in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // *

The XML content handler or application will invoke endElement() method after all // * of the element's content are processed in order to give DTM the indication // * to prepare and patch up parent and sibling node pointers.

// * // *

The following parameters for createElement contains raw string values for name // * symbols used in an Element node.

// * // * @param name String the element name, including the prefix if any. // * @param atts The attributes attached to the element, if any. // * @see #endElement // * @see org.xml.sax.Attributes // */ // public int createElement(String name, Attributes atts) // { // // This method wraps around the index valued interface of the createElement interface. // // The raw string values are stored into the current DTM name symbol tables. The method // // method will then use the index values returned to invoke the other createElement() // // onverted to index values modified to match a // // method. // int nsIndex = NULL; // int nameIndex = m_localNames.stringToIndex(name); // // note - there should be no prefix separator in the name because it is not associated // // with a name space // return createElement(nsIndex, nameIndex, atts); // } // // Factory method to create an Element node associated with a given name space // // using String value parameters passed in from a content handler or application // /** // * Factory method; creates an Element node associated with a given name space in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // *

The XML content handler or application will invoke endElement() method after all // * of the element's content are processed in order to give DTM the indication // * to prepare and patch up parent and sibling node pointers.

// * // *

The following parameters for createElementNS contains raw string values for name // * symbols used in an Element node.

// * // * @param ns String the namespace of the node // * @param name String the element name, including the prefix if any. // * @param atts The attributes attached to the element, if any. // * @see #endElement // * @see org.xml.sax.Attributes // */ // public int createElementNS(String ns, String name, Attributes atts) // { // // This method wraps around the index valued interface of the createElement interface. // // The raw string values are stored into the current DTM name symbol tables. The method // // method will then use the index values returned to invoke the other createElement() // // onverted to index values modified to match a // // method. // int nsIndex = m_nsNames.stringToIndex(ns); // int nameIndex = m_localNames.stringToIndex(name); // // The prefixIndex is not needed by the indexed interface of the createElement method // int prefixSep = name.indexOf(":"); // int prefixIndex = m_prefixNames.stringToIndex(name.substring(0, prefixSep)); // return createElement(nsIndex, nameIndex, atts); // } // /** // * Receive an indication for the end of an element. // * // *

The XML content handler will invoke this method at the end of every // * element in the XML document to give hint its time to pop up the current // * element and parent and patch up parent and sibling pointers if necessary // * // *

%tbd% The following interface may need to be modified to match a // * coordinated access to the DTMDStringPool based symbol tables.

// * // * @param ns the namespace of the element // * @param name The element name // */ // public void endElement(String ns, String name) // { // // pop up the stacks // // // if (previousSiblingWasParent) // nodes.writeEntry(previousSibling, 2, NULL); // // Pop parentage // previousSibling = currentParent; // nodes.readSlot(currentParent, gotslot); // currentParent = gotslot[1] & 0xFFFF; // // The element just being finished will be // // the previous sibling for the next operation // previousSiblingWasParent = true; // // Pop a level of namespace table // // namespaceTable.removeLastElem(); // } // /** // * Creates attributes for the current node. // * // * @param atts Attributes to be created. // */ // void setAttributes(Attributes atts) { // int atLength = (null == atts) ? 0 : atts.getLength(); // for (int i=0; i < atLength; i++) { // String qname = atts.getQName(i); // createAttribute(atts.getQName(i), atts.getValue(i)); // } // } // /** // * Appends an attribute to the document. // * @param qname Qualified Name of the attribute // * @param value Value of the attribute // * @return Handle of node // */ // public int createAttribute(String qname, String value) { // int colonpos = qname.indexOf(":"); // String attName = qname.substring(colonpos+1); // int w0 = 0; // if (colonpos > 0) { // String prefix = qname.substring(0, colonpos); // if (prefix.equals("xml")) { // //w0 = ATTRIBUTE_NODE | // // (org.apache.xalan.templates.Constants.S_XMLNAMESPACEURI << 16); // } else { // //w0 = ATTRIBUTE_NODE | // } // } else { // w0 = ATTRIBUTE_NODE; // } // // W1: Parent // int w1 = currentParent; // // W2: Next (not yet resolved) // int w2 = 0; // // W3: Tag name // int w3 = m_localNames.stringToIndex(attName); // // Add node // int ourslot = appendNode(w0, w1, w2, w3); // previousSibling = ourslot; // Should attributes be previous siblings // // W0: Node Type // w0 = TEXT_NODE; // // W1: Parent // w1 = ourslot; // // W2: Start Position within buffer // w2 = m_char.length(); // m_char.append(value); // // W3: Length // w3 = m_char.length() - w2; // appendNode(w0, w1, w2, w3); // charStringStart=m_char.length(); // charStringLength = 0; // //previousSibling = ourslot; // // Attrs are Parents // previousSiblingWasParent = true; // return (m_docHandle | ourslot); // } // /** // * Factory method; creates a Text node in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // * @param text String The characters text string from the XML document. // * @return int DTM node-number of the text node created // */ // public int createTextNode(String text) // throws DTMException // { // // wraps around the index value based createTextNode method // return createTextNode(text.toCharArray(), 0, text.length()); // } // /** // * Factory method; creates a Text node in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // * %REVIEW% for text normalization issues, unless we are willing to // * insist that all adjacent text must be merged before this method // * is called. // * // * @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. // */ // public int createTextNode(char ch[], int start, int length) // throws DTMException // { // m_char.append(ch, start, length); // store the chunk to the text/comment string table // // create a Text Node // // %TBD% may be possible to combine with appendNode()to replace the next chunk of code // int w0 = TEXT_NODE; // // W1: Parent // int w1 = currentParent; // // W2: Start position within m_char // int w2 = charStringStart; // // W3: Length of the full string // int w3 = length; // int ourslot = appendNode(w0, w1, w2, w3); // previousSibling = ourslot; // charStringStart=m_char.length(); // charStringLength = 0; // return (m_docHandle | ourslot); // } // /** // * Factory method; creates a Comment node in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // * @param text String The characters text string from the XML document. // * @return int DTM node-number of the text node created // */ // public int createComment(String text) // throws DTMException // { // // wraps around the index value based createTextNode method // return createComment(text.toCharArray(), 0, text.length()); // } // /** // * Factory method; creates a Comment node in this document. // * // * The node created will be chained according to its natural order of request // * received. %TBD% It can be rechained later via the optional DTM writable interface. // * // * @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. // * @see DTMException // */ // public int createComment(char ch[], int start, int length) // throws DTMException // { // m_char.append(ch, start, length); // store the comment string to the text/comment string table // // create a Comment Node // // %TBD% may be possible to combine with appendNode()to replace the next chunk of code // int w0 = COMMENT_NODE; // // W1: Parent // int w1 = currentParent; // // W2: Start position within m_char // int w2 = charStringStart; // // W3: Length of the full string // int w3 = length; // int ourslot = appendNode(w0, w1, w2, w3); // previousSibling = ourslot; // charStringStart=m_char.length(); // charStringLength = 0; // return (m_docHandle | ourslot); // } // // Counters to keep track of the current text string being accumulated with respect // // to the text/comment string table: charStringStart should point to the starting // // offset of the string in the table and charStringLength the acccumulated length when // // appendAccumulatedText starts, and reset to the end of the table and 0 at the end // // of appendAccumulatedText for the next set of characters receives // int charStringStart=0,charStringLength=0; // ========= Document Navigation Functions ========= /** Given a node handle, test if it has child nodes. *

%REVIEW% This is obviously useful at the DOM layer, where it * would permit testing this without having to create a proxy * node. It's less useful in the DTM API, where * (dtm.getFirstChild(nodeHandle)!=DTM.NULL) is just as fast and * almost as self-evident. But it's a convenience, and eases porting * of DOM code to DTM.

* * @param nodeHandle int Handle of the node. * @return int true if the given node has child nodes. */ public boolean hasChildNodes(int nodeHandle) { return(getFirstChild(nodeHandle) != NULL); } /** * Given a node handle, get the handle of the node's first child. * If not yet resolved, waits for more nodes to be added to the document and * tries again. * * @param nodeHandle int Handle of the node. * @return int DTM node-number of first child, or DTM.NULL to indicate none exists. */ public int getFirstChild(int nodeHandle) { // ###shs worry about tracing/debug later nodeHandle &= NODEHANDLE_MASK; // Read node into variable nodes.readSlot(nodeHandle, gotslot); // type is the last half of first slot short type = (short) (gotslot[0] & 0xFFFF); // Check to see if Element or Document node if ((type == ELEMENT_NODE) || (type == DOCUMENT_NODE) || (type == ENTITY_REFERENCE_NODE)) { // In case when Document root is given // if (nodeHandle == 0) nodeHandle = 1; // %TBD% Probably was a mistake. // If someone explicitly asks for first child // of Document, I would expect them to want // that and only that. int kid = nodeHandle + 1; nodes.readSlot(kid, gotslot); while (ATTRIBUTE_NODE == (gotslot[0] & 0xFFFF)) { // points to next sibling kid = gotslot[2]; // Return NULL if node has only attributes if (kid == NULL) return NULL; nodes.readSlot(kid, gotslot); } // If parent slot matches given parent, return kid if (gotslot[1] == nodeHandle) { int firstChild = kid | m_docHandle; return firstChild; } } // No child found return NULL; } /** * Given a node handle, advance to its last child. * If not yet resolved, waits for more nodes to be added to the document and * tries again. * * @param nodeHandle int Handle of the node. * @return int Node-number of last child, * or DTM.NULL to indicate none exists. */ public int getLastChild(int nodeHandle) { // ###shs put trace/debug later nodeHandle &= NODEHANDLE_MASK; // do not need to test node type since getFirstChild does that int lastChild = NULL; for (int nextkid = getFirstChild(nodeHandle); nextkid != NULL; nextkid = getNextSibling(nextkid)) { lastChild = nextkid; } return lastChild | m_docHandle; } /** * Retrieves an attribute node by by qualified name and namespace URI. * * @param nodeHandle int Handle of the node upon which to look up this attribute. * @param namespaceURI The namespace URI of the attribute to * retrieve, or null. * @param name The local name of the attribute to * retrieve. * @return The attribute node handle with the specified name ( * nodeName) or DTM.NULL if there is no such * attribute. */ public int getAttributeNode(int nodeHandle, String namespaceURI, String name) { int nsIndex = m_nsNames.stringToIndex(namespaceURI), nameIndex = m_localNames.stringToIndex(name); nodeHandle &= NODEHANDLE_MASK; nodes.readSlot(nodeHandle, gotslot); short type = (short) (gotslot[0] & 0xFFFF); // If nodeHandle points to element next slot would be first attribute if (type == ELEMENT_NODE) nodeHandle++; // Iterate through Attribute Nodes while (type == ATTRIBUTE_NODE) { if ((nsIndex == (gotslot[0] << 16)) && (gotslot[3] == nameIndex)) return nodeHandle | m_docHandle; // Goto next sibling nodeHandle = gotslot[2]; nodes.readSlot(nodeHandle, gotslot); } return NULL; } /** * Given a node handle, get the index of the node's first attribute. * * @param nodeHandle int Handle of the Element node. * @return Handle of first attribute, or DTM.NULL to indicate none exists. */ public int getFirstAttribute(int nodeHandle) { nodeHandle &= NODEHANDLE_MASK; // %REVIEW% jjk: Just a quick observation: If you're going to // call readEntry repeatedly on the same node, it may be // more efficiently to do a readSlot to get the data locally, // reducing the addressing and call-and-return overhead. // Should we check if handle is element (do we want sanity checks?) if (ELEMENT_NODE != (nodes.readEntry(nodeHandle, 0) & 0xFFFF)) return NULL; // First Attribute (if any) should be at next position in table nodeHandle++; return(ATTRIBUTE_NODE == (nodes.readEntry(nodeHandle, 0) & 0xFFFF)) ? nodeHandle | m_docHandle : NULL; } /** * Given a node handle, get the index of the node's first child. * If not yet resolved, waits for more nodes to be added to the document and * tries again * * @param nodeHandle handle to node, which should probably be an element * node, but need not be. * * @param inScope true if all namespaces in scope should be returned, * false if only the namespace declarations should be * returned. * @return handle of first namespace, or DTM.NULL to indicate none exists. */ public int getFirstNamespaceNode(int nodeHandle, boolean inScope) { return NULL; } /** * Given a node handle, advance to its next sibling. * * %TBD% This currently uses the DTM-internal definition of * sibling; eg, the last attr's next sib is the first * child. In the old DTM, the DOM proxy layer provided the * additional logic for the public view. If we're rewriting * for XPath emulation, that test must be done here. * * %TBD% CODE INTERACTION WITH INCREMENTAL PARSE - If not yet * resolved, should wait for more nodes to be added to the document * and tries again. * * @param nodeHandle int Handle of the node. * @return int Node-number of next sibling, * or DTM.NULL to indicate none exists. * */ public int getNextSibling(int nodeHandle) { nodeHandle &= NODEHANDLE_MASK; // Document root has no next sibling if (nodeHandle == 0) return NULL; short type = (short) (nodes.readEntry(nodeHandle, 0) & 0xFFFF); if ((type == ELEMENT_NODE) || (type == ATTRIBUTE_NODE) || (type == ENTITY_REFERENCE_NODE)) { int nextSib = nodes.readEntry(nodeHandle, 2); if (nextSib == NULL) return NULL; if (nextSib != 0) return (m_docHandle | nextSib); // ###shs should cycle/wait if nextSib is 0? Working on threading next } // Next Sibling is in the next position if it shares the same parent int thisParent = nodes.readEntry(nodeHandle, 1); if (nodes.readEntry(++nodeHandle, 1) == thisParent) return (m_docHandle | nodeHandle); return NULL; } /** * Given a node handle, find its preceeding sibling. * WARNING: DTM is asymmetric; this operation is resolved by search, and is * relatively expensive. * * @param nodeHandle the id of the node. * @return int Node-number of the previous sib, * or DTM.NULL to indicate none exists. */ public int getPreviousSibling(int nodeHandle) { nodeHandle &= NODEHANDLE_MASK; // Document root has no previous sibling if (nodeHandle == 0) return NULL; int parent = nodes.readEntry(nodeHandle, 1); int kid = NULL; for (int nextkid = getFirstChild(parent); nextkid != nodeHandle; nextkid = getNextSibling(nextkid)) { kid = nextkid; } return kid | m_docHandle; } /** * Given a node handle, advance to the next attribute. If an * element, we advance to its first attribute; if an attr, we advance to * the next attr on the same node. * * @param nodeHandle int Handle of the node. * @return int DTM node-number of the resolved attr, * or DTM.NULL to indicate none exists. */ public int getNextAttribute(int nodeHandle) { nodeHandle &= NODEHANDLE_MASK; nodes.readSlot(nodeHandle, gotslot); //%REVIEW% Why are we using short here? There's no storage //reduction for an automatic variable, especially one used //so briefly, and it typically costs more cycles to process //than an int would. short type = (short) (gotslot[0] & 0xFFFF); if (type == ELEMENT_NODE) { return getFirstAttribute(nodeHandle); } else if (type == ATTRIBUTE_NODE) { if (gotslot[2] != NULL) return (m_docHandle | gotslot[2]); } return NULL; } /** * Given a namespace handle, advance to the next namespace. * * %TBD% THIS METHOD DOES NOT MATCH THE CURRENT SIGNATURE IN * THE DTM INTERFACE. FIX IT, OR JUSTIFY CHANGING THE DTM * API. * * @param namespaceHandle handle to node which must be of type NAMESPACE_NODE. * @return handle of next namespace, or DTM.NULL to indicate none exists. */ public int getNextNamespaceNode(int baseHandle,int namespaceHandle, boolean inScope) { // ###shs need to work on namespace return NULL; } /** * Given a node handle, advance to its next descendant. * If not yet resolved, waits for more nodes to be added to the document and * tries again. * * @param subtreeRootHandle * @param nodeHandle int Handle of the node. * @return handle of next descendant, * or DTM.NULL to indicate none exists. */ public int getNextDescendant(int subtreeRootHandle, int nodeHandle) { subtreeRootHandle &= NODEHANDLE_MASK; nodeHandle &= NODEHANDLE_MASK; // Document root [Document Node? -- jjk] - no next-sib if (nodeHandle == 0) return NULL; while (!m_isError) { // Document done and node out of bounds if (done && (nodeHandle > nodes.slotsUsed())) break; if (nodeHandle > subtreeRootHandle) { nodes.readSlot(nodeHandle+1, gotslot); if (gotslot[2] != 0) { short type = (short) (gotslot[0] & 0xFFFF); if (type == ATTRIBUTE_NODE) { nodeHandle +=2; } else { int nextParentPos = gotslot[1]; if (nextParentPos >= subtreeRootHandle) return (m_docHandle | (nodeHandle+1)); else break; } } else if (!done) { // Add wait logic here } else break; } else { nodeHandle++; } } // Probably should throw error here like original instead of returning return NULL; } /** * Given a node handle, advance to the next node on the following axis. * * @param axisContextHandle the start of the axis that is being traversed. * @param nodeHandle * @return handle of next sibling, * or DTM.NULL to indicate none exists. */ public int getNextFollowing(int axisContextHandle, int nodeHandle) { //###shs still working on return NULL; } /** * Given a node handle, advance to the next node on the preceding axis. * * @param axisContextHandle the start of the axis that is being traversed. * @param nodeHandle the id of the node. * @return int Node-number of preceding sibling, * or DTM.NULL to indicate none exists. */ public int getNextPreceding(int axisContextHandle, int nodeHandle) { // ###shs copied from Xalan 1, what is this suppose to do? nodeHandle &= NODEHANDLE_MASK; while (nodeHandle > 1) { nodeHandle--; if (ATTRIBUTE_NODE == (nodes.readEntry(nodeHandle, 0) & 0xFFFF)) continue; // if nodeHandle is _not_ an ancestor of // axisContextHandle, specialFind will return it. // If it _is_ an ancestor, specialFind will return -1 // %REVIEW% unconditional return defeats the // purpose of the while loop -- does this // logic make any sense? return (m_docHandle | nodes.specialFind(axisContextHandle, nodeHandle)); } return NULL; } /** * Given a node handle, find its parent node. * * @param nodeHandle the id of the node. * @return int Node-number of parent, * or DTM.NULL to indicate none exists. */ public int getParent(int nodeHandle) { // Should check to see within range? // Document Root should not have to be handled differently return (m_docHandle | nodes.readEntry(nodeHandle, 1)); } /** * Returns the root element of the document. * @return nodeHandle to the Document Root. */ public int getDocumentRoot() { return (m_docHandle | m_docElement); } /** * Given a node handle, find the owning document node. * * @return int Node handle of document, which should always be valid. */ public int getDocument() { return m_docHandle; } /** * Given a node handle, find the owning document node. This has the exact * same semantics as the DOM Document method of the same name, in that if * the nodeHandle is a document node, it will return NULL. * *

%REVIEW% Since this is DOM-specific, it may belong at the DOM * binding layer. Included here as a convenience function and to * aid porting of DOM code to DTM.

* * @param nodeHandle the id of the node. * @return int Node handle of owning document, or NULL if the nodeHandle is * a document. */ public int getOwnerDocument(int nodeHandle) { // Assumption that Document Node is always in 0 slot if ((nodeHandle & NODEHANDLE_MASK) == 0) return NULL; return (nodeHandle & DOCHANDLE_MASK); } /** * Given a node handle, find the owning document node. This has the DTM * semantics; a Document node is its own owner. * *

%REVIEW% Since this is DOM-specific, it may belong at the DOM * binding layer. Included here as a convenience function and to * aid porting of DOM code to DTM.

* * @param nodeHandle the id of the node. * @return int Node handle of owning document, or NULL if the nodeHandle is * a document. */ public int getDocumentRoot(int nodeHandle) { // Assumption that Document Node is always in 0 slot if ((nodeHandle & NODEHANDLE_MASK) == 0) return NULL; return (nodeHandle & DOCHANDLE_MASK); } /** * Get the string-value of a node as a String object * (see http://www.w3.org/TR/xpath#data-model * for the definition of a node's string-value). * * @param nodeHandle The node ID. * * @return A string object that represents the string-value of the given node. */ public XMLString getStringValue(int nodeHandle) { // ###zaj - researching nodes.readSlot(nodeHandle, gotslot); int nodetype=gotslot[0] & 0xFF; String value=null; switch (nodetype) { case TEXT_NODE: case COMMENT_NODE: case CDATA_SECTION_NODE: value= m_char.getString(gotslot[2], gotslot[3]); break; case PROCESSING_INSTRUCTION_NODE: case ATTRIBUTE_NODE: case ELEMENT_NODE: case ENTITY_REFERENCE_NODE: default: break; } return m_xsf.newstr( value ); } /** * Get number of character array chunks in * the string-value of a node. * (see http://www.w3.org/TR/xpath#data-model * for the definition of a node's string-value). * Note that a single text node may have multiple text chunks. * * EXPLANATION: This method is an artifact of the fact that the * underlying m_chars object may not store characters in a * single contiguous array -- for example,the current * FastStringBuffer may split a single node's text across * multiple allocation units. This call tells us how many * separate accesses will be required to retrieve the entire * content. PLEASE NOTE that this may not be the same as the * number of SAX characters() events that caused the text node * to be built in the first place, since m_chars buffering may * be on different boundaries than the parser's buffers. * * @param nodeHandle The node ID. * * @return number of character array chunks in * the string-value of a node. * */ //###zaj - tbd public int getStringValueChunkCount(int nodeHandle) { //###zaj return value return 0; } /** * Get a character array chunk in the string-value of a node. * (see http://www.w3.org/TR/xpath#data-model * for the definition of a node's string-value). * Note that a single text node may have multiple text chunks. * * EXPLANATION: This method is an artifact of the fact that * the underlying m_chars object may not store characters in a * single contiguous array -- for example,the current * FastStringBuffer may split a single node's text across * multiple allocation units. This call retrieves a single * contiguous portion of the text -- as much as m-chars was * able to store in a single allocation unit. PLEASE NOTE * that this may not be the same granularityas the SAX * characters() events that caused the text node to be built * in the first place, since m_chars buffering may be on * different boundaries than the parser's buffers. * * @param nodeHandle The node ID. * @param chunkIndex Which chunk to get. * @param startAndLen An array of 2 where the start position and length of * the chunk will be returned. * * @return The character array reference where the chunk occurs. */ //###zaj - tbd public char[] getStringValueChunk(int nodeHandle, int chunkIndex, int[] startAndLen) {return new char[0];} /** * Given a node handle, return an ID that represents the node's expanded name. * * @param nodeHandle The handle to the node in question. * * @return the expanded-name id of the node. */ public int getExpandedTypeID(int nodeHandle) { nodes.readSlot(nodeHandle, gotslot); String qName = m_localNames.indexToString(gotslot[3]); // Remove prefix from qName // %TBD% jjk This is assuming the elementName is the qName. int colonpos = qName.indexOf(":"); String localName = qName.substring(colonpos+1); // Get NS String namespace = m_nsNames.indexToString(gotslot[0] << 16); // Create expanded name String expandedName = namespace + ":" + localName; int expandedNameID = m_nsNames.stringToIndex(expandedName); return expandedNameID; } /** * Given an expanded name, return an ID. If the expanded-name does not * exist in the internal tables, the entry will be created, and the ID will * be returned. Any additional nodes that are created that have this * expanded name will use this ID. * * @return the expanded-name id of the node. */ public int getExpandedTypeID(String namespace, String localName, int type) { // Create expanded name // %TBD% jjk Expanded name is bitfield-encoded as // typeID[6]nsuriID[10]localID[16]. Switch to that form, and to // accessing the ns/local via their tables rather than confusing // nsnames and expandednames. String expandedName = namespace + ":" + localName; int expandedNameID = m_nsNames.stringToIndex(expandedName); return expandedNameID; } /** * Given an expanded-name ID, return the local name part. * * @param ExpandedNameID an ID that represents an expanded-name. * @return String Local name of this node. */ public String getLocalNameFromExpandedNameID(int ExpandedNameID) { // Get expanded name String expandedName = m_localNames.indexToString(ExpandedNameID); // Remove prefix from expanded name int colonpos = expandedName.indexOf(":"); String localName = expandedName.substring(colonpos+1); return localName; } /** * Given an expanded-name ID, return the namespace URI part. * * @param ExpandedNameID an ID that represents an expanded-name. * @return String URI value of this node's namespace, or null if no * namespace was resolved. */ public String getNamespaceFromExpandedNameID(int ExpandedNameID) { String expandedName = m_localNames.indexToString(ExpandedNameID); // Remove local name from expanded name int colonpos = expandedName.indexOf(":"); String nsName = expandedName.substring(0, colonpos); return nsName; } /** * fixednames */ private static final String[] fixednames= { null,null, // nothing, Element null,"#text", // Attr, Text "#cdata_section",null, // CDATA, EntityReference null,null, // Entity, PI "#comment","#document", // Comment, Document null,"#document-fragment", // Doctype, DocumentFragment null}; // Notation /** * Given a node handle, return its DOM-style node name. This will * include names such as #text or #document. * * @param nodeHandle the id of the node. * @return String Name of this node, which may be an empty string. * %REVIEW% Document when empty string is possible... */ public String getNodeName(int nodeHandle) { nodes.readSlot(nodeHandle, gotslot); short type = (short) (gotslot[0] & 0xFFFF); String name = fixednames[type]; if (null == name) { int i=gotslot[3]; /**/System.out.println("got i="+i+" "+(i>>16)+"/"+(i&0xffff)); name=m_localNames.indexToString(i & 0xFFFF); String prefix=m_prefixNames.indexToString(i >>16); if(prefix!=null && prefix.length()>0) name=prefix+":"+name; } return name; } /** * Given a node handle, return the XPath node name. This should be * the name as described by the XPath data model, NOT the DOM-style * name. * * @param nodeHandle the id of the node. * @return String Name of this node. */ public String getNodeNameX(int nodeHandle) {return null;} /** * Given a node handle, return its DOM-style localname. * (As defined in Namespaces, this is the portion of the name after any * colon character) * * %REVIEW% What's the local name of something other than Element/Attr? * Should this be DOM-style (undefined unless namespaced), or other? * * @param nodeHandle the id of the node. * @return String Local name of this node. */ public String getLocalName(int nodeHandle) { nodes.readSlot(nodeHandle, gotslot); short type = (short) (gotslot[0] & 0xFFFF); String name = ""; if ((type==ELEMENT_NODE) || (type==ATTRIBUTE_NODE)) { int i=gotslot[3]; name=m_localNames.indexToString(i & 0xFFFF); if(name==null) name=""; } return name; } /** * Given a namespace handle, return the prefix that the namespace decl is * mapping. * Given a node handle, return the prefix used to map to the namespace. * *

%REVIEW% Are you sure you want "" for no prefix?

* * %REVIEW% Should this be DOM-style (undefined unless namespaced), * or other? * * @param nodeHandle the id of the node. * @return String prefix of this node's name, or "" if no explicit * namespace prefix was given. */ public String getPrefix(int nodeHandle) { nodes.readSlot(nodeHandle, gotslot); short type = (short) (gotslot[0] & 0xFFFF); String name = ""; if((type==ELEMENT_NODE) || (type==ATTRIBUTE_NODE)) { int i=gotslot[3]; name=m_prefixNames.indexToString(i >>16); if(name==null) name=""; } return name; } /** * Given a node handle, return its DOM-style namespace URI * (As defined in Namespaces, this is the declared URI which this node's * prefix -- or default in lieu thereof -- was mapped to.) * * @param nodeHandle the id of the node. * @return String URI value of this node's namespace, or null if no * namespace was resolved. */ public String getNamespaceURI(int nodeHandle) {return null;} /** * Given a node handle, return its node value. This is mostly * as defined by the DOM, but may ignore some conveniences. *

* * @param nodeHandle The node id. * @return String Value of this node, or null if not * meaningful for this node type. */ public String getNodeValue(int nodeHandle) { nodes.readSlot(nodeHandle, gotslot); int nodetype=gotslot[0] & 0xFF; // ###zaj use mask to get node type String value=null; switch (nodetype) { // ###zaj todo - document nodetypes case ATTRIBUTE_NODE: nodes.readSlot(nodeHandle+1, gotslot); case TEXT_NODE: case COMMENT_NODE: case CDATA_SECTION_NODE: value=m_char.getString(gotslot[2], gotslot[3]); //###zaj break; case PROCESSING_INSTRUCTION_NODE: case ELEMENT_NODE: case ENTITY_REFERENCE_NODE: default: break; } return value; } /** * Given a node handle, return its DOM-style node type. *

* %REVIEW% Generally, returning short is false economy. Return int? * * @param nodeHandle The node id. * @return int Node type, as per the DOM's Node._NODE constants. */ public short getNodeType(int nodeHandle) { return(short) (nodes.readEntry(nodeHandle, 0) & 0xFFFF); } /** * Get the depth level of this node in the tree (equals 1 for * a parentless node). * * @param nodeHandle The node id. * @return the number of ancestors, plus one * @xsl.usage internal */ public short getLevel(int nodeHandle) { short count = 0; while (nodeHandle != 0) { count++; nodeHandle = nodes.readEntry(nodeHandle, 1); } return count; } // ============== Document query functions ============== /** * Tests whether DTM DOM implementation implements a specific feature and * that feature is supported by this node. * * @param feature The name of the feature to test. * @param version This is the version number of the feature to test. * If the version is not * specified, supporting any version of the feature will cause the * method to return true. * @return Returns true if the specified feature is * supported on this node, false otherwise. */ public boolean isSupported(String feature, String version) {return false;} /** * Return the base URI of the document entity. If it is not known * (because the document was parsed from a socket connection or from * standard input, for example), the value of this property is unknown. * * @return the document base URI String object or null if unknown. */ public String getDocumentBaseURI() { return m_documentBaseURI; } /** * Set the base URI of the document entity. * * @param baseURI the document base URI String object or null if unknown. */ public void setDocumentBaseURI(String baseURI) { m_documentBaseURI = baseURI; } /** * Return the system identifier of the document entity. If * it is not known, the value of this property is unknown. * * @param nodeHandle The node id, which can be any valid node handle. * @return the system identifier String object or null if unknown. */ public String getDocumentSystemIdentifier(int nodeHandle) {return null;} /** * Return the name of the character encoding scheme * in which the document entity is expressed. * * @param nodeHandle The node id, which can be any valid node handle. * @return the document encoding String object. */ public String getDocumentEncoding(int nodeHandle) {return null;} /** * Return an indication of the standalone status of the document, * either "yes" or "no". This property is derived from the optional * standalone document declaration in the XML declaration at the * beginning of the document entity, and has no value if there is no * standalone document declaration. * * @param nodeHandle The node id, which can be any valid node handle. * @return the document standalone String object, either "yes", "no", or null. */ public String getDocumentStandalone(int nodeHandle) {return null;} /** * Return a string representing the XML version of the document. This * property is derived from the XML declaration optionally present at the * beginning of the document entity, and has no value if there is no XML * declaration. * * @param documentHandle the document handle * * @return the document version String object */ public String getDocumentVersion(int documentHandle) {return null;} /** * Return an indication of * whether the processor has read the complete DTD. Its value is a * boolean. If it is false, then certain properties (indicated in their * descriptions below) may be unknown. If it is true, those properties * are never unknown. * * @return true if all declarations were processed {}; * false otherwise. */ public boolean getDocumentAllDeclarationsProcessed() {return false;} /** * A document type declaration information item has the following properties: * * 1. [system identifier] The system identifier of the external subset, if * it exists. Otherwise this property has no value. * * @return the system identifier String object, or null if there is none. */ public String getDocumentTypeDeclarationSystemIdentifier() {return null;} /** * Return the public identifier of the external subset, * normalized as described in 4.2.2 External Entities [XML]. If there is * no external subset or if it has no public identifier, this property * has no value. * * @return the public identifier String object, or null if there is none. */ public String getDocumentTypeDeclarationPublicIdentifier() {return null;} /** * Returns the Element whose ID is given by * elementId. If no such element exists, returns * DTM.NULL. Behavior is not defined if more than one element * has this ID. Attributes (including those * with the name "ID") are not of type ID unless so defined by DTD/Schema * information available to the DTM implementation. * Implementations that do not know whether attributes are of type ID or * not are expected to return DTM.NULL. * *

%REVIEW% Presumably IDs are still scoped to a single document, * and this operation searches only within a single document, right? * Wouldn't want collisions between DTMs in the same process.

* * @param elementId The unique id value for an element. * @return The handle of the matching element. */ public int getElementById(String elementId) {return 0;} /** * The getUnparsedEntityURI function returns the URI of the unparsed * entity with the specified name in the same document as the context * node (see [3.3 Unparsed Entities]). It returns the empty string if * there is no such entity. *

* XML processors may choose to use the System Identifier (if one * is provided) to resolve the entity, rather than the URI in the * Public Identifier. The details are dependent on the processor, and * we would have to support some form of plug-in resolver to handle * this properly. Currently, we simply return the System Identifier if * present, and hope that it a usable URI or that our caller can * map it to one. * TODO: Resolve Public Identifiers... or consider changing function name. *

* If we find a relative URI * reference, XML expects it to be resolved in terms of the base URI * of the document. The DOM doesn't do that for us, and it isn't * entirely clear whether that should be done here; currently that's * pushed up to a higher level of our application. (Note that DOM Level * 1 didn't store the document's base URI.) * TODO: Consider resolving Relative URIs. *

* (The DOM's statement that "An XML processor may choose to * completely expand entities before the structure model is passed * to the DOM" refers only to parsed entities, not unparsed, and hence * doesn't affect this function.) * * @param name A string containing the Entity Name of the unparsed * entity. * * @return String containing the URI of the Unparsed Entity, or an * empty string if no such entity exists. */ public String getUnparsedEntityURI(String name) {return null;} // ============== Boolean methods ================ /** * Return true if the xsl:strip-space or xsl:preserve-space was processed * during construction of the DTM document. * *

%REVEIW% Presumes a 1:1 mapping from DTM to Document, since * we aren't saying which Document to query...?

*/ public boolean supportsPreStripping() {return false;} /** * Figure out whether nodeHandle2 should be considered as being later * in the document than nodeHandle1, in Document Order as defined * by the XPath model. This may not agree with the ordering defined * by other XML applications. *

* There are some cases where ordering isn't defined, and neither are * the results of this function -- though we'll generally return true. * * TODO: Make sure this does the right thing with attribute nodes!!! * * @param nodeHandle1 DOM Node to perform position comparison on. * @param nodeHandle2 DOM Node to perform position comparison on . * * @return false if node2 comes before node1, otherwise return true. * You can think of this as * (node1.documentOrderPosition <= node2.documentOrderPosition). */ public boolean isNodeAfter(int nodeHandle1, int nodeHandle2) {return false;} /** * 2. [element content whitespace] A boolean indicating whether the * character is white space appearing within element content (see [XML], * 2.10 "White Space Handling"). Note that validating XML processors are * required by XML 1.0 to provide this information. If there is no * declaration for the containing element, this property has no value for * white space characters. If no declaration has been read, but the [all * declarations processed] property of the document information item is * false (so there may be an unread declaration), then the value of this * property is unknown for white space characters. It is always false for * characters that are not white space. * * @param nodeHandle the node ID. * @return true if the character data is whitespace; * false otherwise. */ public boolean isCharacterElementContentWhitespace(int nodeHandle) {return false;} /** * 10. [all declarations processed] This property is not strictly speaking * part of the infoset of the document. Rather it is an indication of * whether the processor has read the complete DTD. Its value is a * boolean. If it is false, then certain properties (indicated in their * descriptions below) may be unknown. If it is true, those properties * are never unknown. * * @param documentHandle A node handle that must identify a document. * @return true if all declarations were processed; * false otherwise. */ public boolean isDocumentAllDeclarationsProcessed(int documentHandle) {return false;} /** * 5. [specified] A flag indicating whether this attribute was actually * specified in the start-tag of its element, or was defaulted from the * DTD. * * @param attributeHandle the attribute handle * @return true if the attribute was specified; * false if it was defaulted. */ public boolean isAttributeSpecified(int attributeHandle) {return false;} // ========== Direct SAX Dispatch, for optimization purposes ======== /** * Directly call the * characters method on the passed ContentHandler for the * string-value of the given node (see http://www.w3.org/TR/xpath#data-model * for the definition of a node's string-value). Multiple calls to the * ContentHandler's characters methods may well occur for a single call to * this method. * * @param nodeHandle The node ID. * @param ch A non-null reference to a ContentHandler. * * @throws org.xml.sax.SAXException */ public void dispatchCharactersEvents( int nodeHandle, org.xml.sax.ContentHandler ch, boolean normalize) throws org.xml.sax.SAXException {} /** * Directly create SAX parser events from a subtree. * * @param nodeHandle The node ID. * @param ch A non-null reference to a ContentHandler. * * @throws org.xml.sax.SAXException */ public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch) throws org.xml.sax.SAXException {} /** * Return an DOM node for the given node. * * @param nodeHandle The node ID. * * @return A node representation of the DTM node. */ public org.w3c.dom.Node getNode(int nodeHandle) { return null; } // ==== Construction methods (may not be supported by some implementations!) ===== // %REVIEW% jjk: These probably aren't the right API. At the very least // they need to deal with current-insertion-location and end-element // issues. /** * Append a child to the end of the child list of the current node. Please note that the node * is always cloned if it is owned by another document. * *

%REVIEW% "End of the document" needs to be defined more clearly. * Does it become the last child of the Document? Of the root element?

* * @param newChild Must be a valid new node handle. * @param clone true if the child should be cloned into the document. * @param cloneDepth if the clone argument is true, specifies that the * clone should include all it's children. */ public void appendChild(int newChild, boolean clone, boolean cloneDepth) { boolean sameDoc = ((newChild & DOCHANDLE_MASK) == m_docHandle); if (clone || !sameDoc) { } else { } } /** * Append a text node child that will be constructed from a string, * to the end of the document. * *

%REVIEW% "End of the document" needs to be defined more clearly. * Does it become the last child of the Document? Of the root element?

* * @param str Non-null reference to a string. */ public void appendTextChild(String str) { // ###shs Think more about how this differs from createTextNode //%TBD% } //================================================================ // ==== BUILDER methods ==== // %TBD% jjk: SHOULD PROBABLY BE INLINED, unless we want to support // both SAX1 and SAX2 and share this logic between them. /** Append a text child at the current insertion point. Assumes that the * actual content of the text has previously been appended to the m_char * buffer (shared with the builder). * * @param m_char_current_start int Starting offset of node's content in m_char. * @param contentLength int Length of node's content in m_char. * */ void appendTextChild(int m_char_current_start,int contentLength) { // create a Text Node // %TBD% may be possible to combine with appendNode()to replace the next chunk of code int w0 = TEXT_NODE; // W1: Parent int w1 = currentParent; // W2: Start position within m_char int w2 = m_char_current_start; // W3: Length of the full string int w3 = contentLength; int ourslot = appendNode(w0, w1, w2, w3); previousSibling = ourslot; } /** Append a comment child at the current insertion point. Assumes that the * actual content of the comment has previously been appended to the m_char * buffer (shared with the builder). * * @param m_char_current_start int Starting offset of node's content in m_char. * @param contentLength int Length of node's content in m_char. * */ void appendComment(int m_char_current_start,int contentLength) { // create a Comment Node // %TBD% may be possible to combine with appendNode()to replace the next chunk of code int w0 = COMMENT_NODE; // W1: Parent int w1 = currentParent; // W2: Start position within m_char int w2 = m_char_current_start; // W3: Length of the full string int w3 = contentLength; int ourslot = appendNode(w0, w1, w2, w3); previousSibling = ourslot; } /** Append an Element child at the current insertion point. This * Element then _becomes_ the insertion point; subsequent appends * become its lastChild until an appendEndElement() call is made. * * Assumes that the symbols (local name, namespace URI and prefix) * have already been added to the pools * * Note that this _only_ handles the Element node itself. Attrs and * namespace nodes are unbundled in the ContentHandler layer * and appended separately. * * @param namespaceIndex: Index within the namespaceURI string pool * @param localNameIndex Index within the local name string pool * @param prefixIndex: Index within the prefix string pool * */ void appendStartElement(int namespaceIndex,int localNameIndex, int prefixIndex) { // do document root node creation here on the first element, create nodes for // this element and its attributes, store the element, namespace, and attritute // name indexes to the nodes array, keep track of the current node and parent // element used // W0 High: Namespace Low: Node Type int w0 = (namespaceIndex << 16) | ELEMENT_NODE; // W1: Parent int w1 = currentParent; // W2: Next (initialized as 0) int w2 = 0; // W3: Tagname high: prefix Low: local name int w3 = localNameIndex | prefixIndex<<16; /**/System.out.println("set w3="+w3+" "+(w3>>16)+"/"+(w3&0xffff)); //int ourslot = nodes.appendSlot(w0, w1, w2, w3); int ourslot = appendNode(w0, w1, w2, w3); currentParent = ourslot; previousSibling = 0; // set the root element pointer when creating the first element node if (m_docElement == NULL) m_docElement = ourslot; } /** Append a Namespace Declaration child at the current insertion point. * Assumes that the symbols (namespace URI and prefix) have already been * added to the pools * * @param prefixIndex: Index within the prefix string pool * @param namespaceIndex: Index within the namespaceURI string pool * @param isID: If someone really insists on writing a bad DTD, it is * theoretically possible for a namespace declaration to also be declared * as being a node ID. I don't really want to support that stupidity, * but I'm not sure we can refuse to accept it. * */ void appendNSDeclaration(int prefixIndex, int namespaceIndex, boolean isID) { // %REVIEW% I'm assigning this node the "namespace for namespaces" // which the DOM defined. It is expected that the Namespace spec will // adopt this as official. It isn't strictly needed since it's implied // by the nodetype, but for now... // %REVIEW% Prefix need not be recorded; it's implied too. But // recording it might simplify the design. // %TBD% isID is not currently honored. final int namespaceForNamespaces=m_nsNames.stringToIndex("http://www.w3.org/2000/xmlns/"); // W0 High: Namespace Low: Node Type int w0 = NAMESPACE_NODE | (m_nsNames.stringToIndex("http://www.w3.org/2000/xmlns/")<<16); // W1: Parent int w1 = currentParent; // W2: CURRENTLY UNUSED -- It's next-sib in attrs, but we have no kids. int w2 = 0; // W3: namespace name int w3 = namespaceIndex; // Add node int ourslot = appendNode(w0, w1, w2, w3); previousSibling = ourslot; // Should attributes be previous siblings previousSiblingWasParent = false; return ;//(m_docHandle | ourslot); } /** Append an Attribute child at the current insertion * point. Assumes that the symbols (namespace URI, local name, and * prefix) have already been added to the pools, and that the content has * already been appended to m_char. Note that the attribute's content has * been flattened into a single string; DTM does _NOT_ attempt to model * the details of entity references within attribute values. * * @param namespaceIndex int Index within the namespaceURI string pool * @param localNameIndex int Index within the local name string pool * @param prefixIndex int Index within the prefix string pool * @param isID boolean True if this attribute was declared as an ID * (for use in supporting getElementByID). * @param m_char_current_start int Starting offset of node's content in m_char. * @param contentLength int Length of node's content in m_char. * */ void appendAttribute(int namespaceIndex, int localNameIndex, int prefixIndex, boolean isID, int m_char_current_start, int contentLength) { // %TBD% isID is not currently honored. // W0 High: Namespace Low: Node Type int w0 = ATTRIBUTE_NODE | namespaceIndex<<16; // W1: Parent int w1 = currentParent; // W2: Next (not yet resolved) int w2 = 0; // W3: Tagname high: prefix Low: local name int w3 = localNameIndex | prefixIndex<<16; /**/System.out.println("set w3="+w3+" "+(w3>>16)+"/"+(w3&0xffff)); // Add node int ourslot = appendNode(w0, w1, w2, w3); previousSibling = ourslot; // Should attributes be previous siblings // Attribute's content is currently appended as a Text Node // W0: Node Type w0 = TEXT_NODE; // W1: Parent w1 = ourslot; // W2: Start Position within buffer w2 = m_char_current_start; // W3: Length w3 = contentLength; appendNode(w0, w1, w2, w3); // Attrs are Parents previousSiblingWasParent = true; return ;//(m_docHandle | ourslot); } /** * This returns a stateless "traverser", that can navigate over an * XPath axis, though not in document order. * * @param axis One of Axes.ANCESTORORSELF, etc. * * @return A DTMAxisIterator, or null if the given axis isn't supported. */ public DTMAxisTraverser getAxisTraverser(final int axis) { return null; } /** * This is a shortcut to the iterators that implement the * supported XPath axes (only namespace::) is not supported. * Returns a bare-bones iterator that must be initialized * with a start node (using iterator.setStartNode()). * * @param axis One of Axes.ANCESTORORSELF, etc. * * @return A DTMAxisIterator, or null if the given axis isn't supported. */ public DTMAxisIterator getAxisIterator(final int axis) { // %TBD% return null; } /** * Get an iterator that can navigate over an XPath Axis, predicated by * the extended type ID. * * * @param axis * @param type An extended type ID. * * @return A DTMAxisIterator, or null if the given axis isn't supported. */ public DTMAxisIterator getTypedAxisIterator(final int axis, final int type) { // %TBD% return null; } /** Terminate the element currently acting as an insertion point. Subsequent * insertions will occur as the last child of this element's parent. * */ void appendEndElement() { // pop up the stacks if (previousSiblingWasParent) nodes.writeEntry(previousSibling, 2, NULL); // Pop parentage previousSibling = currentParent; nodes.readSlot(currentParent, gotslot); currentParent = gotslot[1] & 0xFFFF; // The element just being finished will be // the previous sibling for the next operation previousSiblingWasParent = true; // Pop a level of namespace table // namespaceTable.removeLastElem(); } /** Starting a new document. Perform any resets/initialization * not already handled. * */ void appendStartDocument() { // %TBD% reset slot 0 to indicate ChunkedIntArray reuse or wait for // the next initDocument(). m_docElement = NULL; // reset nodeHandle to the root of the actual dtm doc content initDocument(0); } /** All appends to this document have finished; do whatever final * cleanup is needed. * */ void appendEndDocument() { done = true; // %TBD% may need to notice the last slot number and slot count to avoid // residual data from provious use of this DTM } /** * For the moment all the run time properties are ignored by this * class. * * @param property a String value * @param value an Object value */ public void setProperty(String property, Object value) { } /** * Source information is not handled yet, so return * null here. * * @param node an int value * @return null */ public SourceLocator getSourceLocatorFor(int node) { return null; } /** * A dummy routine to satisify the abstract interface. If the DTM * implememtation that extends the default base requires notification * of registration, they can override this method. */ public void documentRegistration() { } /** * A dummy routine to satisify the abstract interface. If the DTM * implememtation that extends the default base requires notification * when the document is being released, they can override this method */ public void documentRelease() { } /** * Migrate a DTM built with an old DTMManager to a new DTMManager. * After the migration, the new DTMManager will treat the DTM as * one that is built by itself. * This is used to support DTM sharing between multiple transformations. * @param manager the DTMManager */ public void migrateTo(DTMManager manager) { } }