| /* |
| * 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: SAX2DTM2.java 468653 2006-10-28 07:07:05Z minchau $ |
| */ |
| package org.apache.xml.dtm.ref.sax2dtm; |
| |
| import org.apache.xml.dtm.*; |
| import org.apache.xml.dtm.ref.*; |
| import org.apache.xml.utils.FastStringBuffer; |
| import org.apache.xml.utils.XMLString; |
| import org.apache.xml.utils.XMLStringDefault; |
| import org.apache.xml.utils.XMLStringFactory; |
| import org.apache.xml.res.XMLMessages; |
| import org.apache.xml.res.XMLErrorResources; |
| import org.apache.xml.serializer.SerializationHandler; |
| |
| import javax.xml.transform.Source; |
| import java.util.Vector; |
| import org.apache.xml.utils.SuballocatedIntVector; |
| import org.xml.sax.*; |
| |
| /** |
| * SAX2DTM2 is an optimized version of SAX2DTM which is used in non-incremental situation. |
| * It is used as the super class of the XSLTC SAXImpl. Many of the interfaces in SAX2DTM |
| * and DTMDefaultBase are overridden in SAX2DTM2 in order to allow fast, efficient |
| * access to the DTM model. Some nested iterators in DTMDefaultBaseIterators |
| * are also overridden in SAX2DTM2 for performance reasons. |
| * <p> |
| * Performance is the biggest consideration in the design of SAX2DTM2. To make the code most |
| * efficient, the incremental support is dropped in SAX2DTM2, which means that you should not |
| * use it in incremental situation. To reduce the overhead of pulling data from the DTM model, |
| * a few core interfaces in SAX2DTM2 have direct access to the internal arrays of the |
| * SuballocatedIntVectors. |
| * <p> |
| * The design of SAX2DTM2 may limit its extensibilty. If you have a reason to extend the |
| * SAX2DTM model, please extend from SAX2DTM instead of this class. |
| * <p> |
| * TODO: This class is currently only used by XSLTC. We need to investigate the possibility |
| * of also using it in Xalan-J Interpretive. Xalan's performance is likely to get an instant |
| * boost if we use SAX2DTM2 instead of SAX2DTM in non-incremental case. |
| * <p> |
| * %MK% The code in this class is critical to the XSLTC_DTM performance. Be very careful |
| * when making changes here! |
| */ |
| public class SAX2DTM2 extends SAX2DTM |
| { |
| |
| /**************************************************************** |
| * Optimized version of the nested iterators |
| ****************************************************************/ |
| |
| /** |
| * Iterator that returns all immediate children of a given node |
| */ |
| public final class ChildrenIterator extends InternalAxisIteratorBase |
| { |
| |
| /** |
| * Setting start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * <p> |
| * If the iterator is not restartable, this has no effect. |
| * %REVIEW% Should it return/throw something in that case, |
| * or set current node to END, to indicate request-not-honored? |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| _currentNode = (node == DTM.NULL) ? DTM.NULL |
| : _firstch2(makeNodeIdentity(node)); |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END if no more |
| * are available. |
| */ |
| public int next() |
| { |
| if (_currentNode != NULL) { |
| int node = _currentNode; |
| _currentNode = _nextsib2(node); |
| return returnNode(makeNodeHandle(node)); |
| } |
| |
| return END; |
| } |
| } // end of ChildrenIterator |
| |
| /** |
| * Iterator that returns the parent of a given node. Note that |
| * this delivers only a single node; if you want all the ancestors, |
| * see AncestorIterator. |
| */ |
| public final class ParentIterator extends InternalAxisIteratorBase |
| { |
| |
| /** The extended type ID that was requested. */ |
| private int _nodeType = DTM.NULL; |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| |
| if (node != DTM.NULL) |
| _currentNode = _parent2(makeNodeIdentity(node)); |
| else |
| _currentNode = DTM.NULL; |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Set the node type of the parent that we're looking for. |
| * Note that this does _not_ mean "find the nearest ancestor of |
| * this type", but "yield the parent if it is of this type". |
| * |
| * |
| * @param type extended type ID. |
| * |
| * @return ParentIterator configured with the type filter set. |
| */ |
| public DTMAxisIterator setNodeType(final int type) |
| { |
| |
| _nodeType = type; |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. In this case, we return |
| * only the immediate parent, _if_ it matches the requested nodeType. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| int result = _currentNode; |
| if (result == END) |
| return DTM.NULL; |
| |
| // %OPT% The most common case is handled first. |
| if (_nodeType == NULL) { |
| _currentNode = END; |
| return returnNode(makeNodeHandle(result)); |
| } |
| else if (_nodeType >= DTM.NTYPES) { |
| if (_nodeType == _exptype2(result)) { |
| _currentNode = END; |
| return returnNode(makeNodeHandle(result)); |
| } |
| } |
| else { |
| if (_nodeType == _type2(result)) { |
| _currentNode = END; |
| return returnNode(makeNodeHandle(result)); |
| } |
| } |
| |
| return DTM.NULL; |
| } |
| } // end of ParentIterator |
| |
| /** |
| * Iterator that returns children of a given type for a given node. |
| * The functionality chould be achieved by putting a filter on top |
| * of a basic child iterator, but a specialised iterator is used |
| * for efficiency (both speed and size of translet). |
| */ |
| public final class TypedChildrenIterator extends InternalAxisIteratorBase |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedChildrenIterator |
| * |
| * |
| * @param nodeType The extended type ID being requested. |
| */ |
| public TypedChildrenIterator(int nodeType) |
| { |
| _nodeType = nodeType; |
| } |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| _currentNode = (node == DTM.NULL) |
| ? DTM.NULL |
| : _firstch2(makeNodeIdentity(_startNode)); |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| int node = _currentNode; |
| if (node == DTM.NULL) |
| return DTM.NULL; |
| |
| final int nodeType = _nodeType; |
| |
| if (nodeType != DTM.ELEMENT_NODE) { |
| while (node != DTM.NULL && _exptype2(node) != nodeType) { |
| node = _nextsib2(node); |
| } |
| } |
| // %OPT% If the nodeType is element (matching child::*), we only |
| // need to compare the expType with DTM.NTYPES. A child node of |
| // an element can be either an element, text, comment or |
| // processing instruction node. Only element node has an extended |
| // type greater than or equal to DTM.NTYPES. |
| else { |
| int eType; |
| while (node != DTM.NULL) { |
| eType = _exptype2(node); |
| if (eType >= DTM.NTYPES) |
| break; |
| else |
| node = _nextsib2(node); |
| } |
| } |
| |
| if (node == DTM.NULL) { |
| _currentNode = DTM.NULL; |
| return DTM.NULL; |
| } else { |
| _currentNode = _nextsib2(node); |
| return returnNode(makeNodeHandle(node)); |
| } |
| |
| } |
| |
| /** |
| * Return the node at the given position. |
| */ |
| public int getNodeByPosition(int position) |
| { |
| if (position <= 0) |
| return DTM.NULL; |
| |
| int node = _currentNode; |
| int pos = 0; |
| |
| final int nodeType = _nodeType; |
| if (nodeType != DTM.ELEMENT_NODE) { |
| while (node != DTM.NULL) { |
| if (_exptype2(node) == nodeType) { |
| pos++; |
| if (pos == position) |
| return makeNodeHandle(node); |
| } |
| |
| node = _nextsib2(node); |
| } |
| return NULL; |
| } |
| else { |
| while (node != DTM.NULL) { |
| if (_exptype2(node) >= DTM.NTYPES) { |
| pos++; |
| if (pos == position) |
| return makeNodeHandle(node); |
| } |
| node = _nextsib2(node); |
| } |
| return NULL; |
| } |
| } |
| |
| } // end of TypedChildrenIterator |
| |
| /** |
| * Iterator that returns the namespace nodes as defined by the XPath data model |
| * for a given node, filtered by extended type ID. |
| */ |
| public class TypedRootIterator extends RootIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedRootIterator |
| * |
| * @param nodeType The extended type ID being requested. |
| */ |
| public TypedRootIterator(int nodeType) |
| { |
| super(); |
| _nodeType = nodeType; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| if(_startNode == _currentNode) |
| return NULL; |
| |
| final int node = _startNode; |
| int expType = _exptype2(makeNodeIdentity(node)); |
| |
| _currentNode = node; |
| |
| if (_nodeType >= DTM.NTYPES) { |
| if (_nodeType == expType) { |
| return returnNode(node); |
| } |
| } |
| else { |
| if (expType < DTM.NTYPES) { |
| if (expType == _nodeType) { |
| return returnNode(node); |
| } |
| } |
| else { |
| if (m_extendedTypes[expType].getNodeType() == _nodeType) { |
| return returnNode(node); |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| } // end of TypedRootIterator |
| |
| /** |
| * Iterator that returns all siblings of a given node. |
| */ |
| public class FollowingSiblingIterator extends InternalAxisIteratorBase |
| { |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| _currentNode = makeNodeIdentity(node); |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| _currentNode = (_currentNode == DTM.NULL) ? DTM.NULL |
| : _nextsib2(_currentNode); |
| return returnNode(makeNodeHandle(_currentNode)); |
| } |
| } // end of FollowingSiblingIterator |
| |
| /** |
| * Iterator that returns all following siblings of a given node. |
| */ |
| public final class TypedFollowingSiblingIterator |
| extends FollowingSiblingIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedFollowingSiblingIterator |
| * |
| * |
| * @param type The extended type ID being requested. |
| */ |
| public TypedFollowingSiblingIterator(int type) |
| { |
| _nodeType = type; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| if (_currentNode == DTM.NULL) { |
| return DTM.NULL; |
| } |
| |
| int node = _currentNode; |
| final int nodeType = _nodeType; |
| |
| if (nodeType != DTM.ELEMENT_NODE) { |
| while ((node = _nextsib2(node)) != DTM.NULL && _exptype2(node) != nodeType) {} |
| } |
| else { |
| while ((node = _nextsib2(node)) != DTM.NULL && _exptype2(node) < DTM.NTYPES) {} |
| } |
| |
| _currentNode = node; |
| |
| return (node == DTM.NULL) |
| ? DTM.NULL |
| : returnNode(makeNodeHandle(node)); |
| } |
| |
| } // end of TypedFollowingSiblingIterator |
| |
| /** |
| * Iterator that returns attribute nodes (of what nodes?) |
| */ |
| public final class AttributeIterator extends InternalAxisIteratorBase |
| { |
| |
| // assumes caller will pass element nodes |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| _currentNode = getFirstAttributeIdentity(makeNodeIdentity(node)); |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| |
| final int node = _currentNode; |
| |
| if (node != NULL) { |
| _currentNode = getNextAttributeIdentity(node); |
| return returnNode(makeNodeHandle(node)); |
| } |
| |
| return NULL; |
| } |
| } // end of AttributeIterator |
| |
| /** |
| * Iterator that returns attribute nodes of a given type |
| */ |
| public final class TypedAttributeIterator extends InternalAxisIteratorBase |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedAttributeIterator |
| * |
| * |
| * @param nodeType The extended type ID that is requested. |
| */ |
| public TypedAttributeIterator(int nodeType) |
| { |
| _nodeType = nodeType; |
| } |
| |
| // assumes caller will pass element nodes |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| if (_isRestartable) |
| { |
| _startNode = node; |
| |
| _currentNode = getTypedAttribute(node, _nodeType); |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| |
| final int node = _currentNode; |
| |
| // singleton iterator, since there can only be one attribute of |
| // a given type. |
| _currentNode = NULL; |
| |
| return returnNode(node); |
| } |
| } // end of TypedAttributeIterator |
| |
| /** |
| * Iterator that returns preceding siblings of a given node |
| */ |
| public class PrecedingSiblingIterator extends InternalAxisIteratorBase |
| { |
| |
| /** |
| * The node identity of _startNode for this iterator |
| */ |
| protected int _startNodeID; |
| |
| /** |
| * True if this iterator has a reversed axis. |
| * |
| * @return true. |
| */ |
| public boolean isReverse() |
| { |
| return true; |
| } |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| node = _startNodeID = makeNodeIdentity(node); |
| |
| if(node == NULL) |
| { |
| _currentNode = node; |
| return resetPosition(); |
| } |
| |
| int type = _type2(node); |
| if(ExpandedNameTable.ATTRIBUTE == type |
| || ExpandedNameTable.NAMESPACE == type ) |
| { |
| _currentNode = node; |
| } |
| else |
| { |
| // Be careful to handle the Document node properly |
| _currentNode = _parent2(node); |
| if(NULL!=_currentNode) |
| _currentNode = _firstch2(_currentNode); |
| else |
| _currentNode = node; |
| } |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| |
| if (_currentNode == _startNodeID || _currentNode == DTM.NULL) |
| { |
| return NULL; |
| } |
| else |
| { |
| final int node = _currentNode; |
| _currentNode = _nextsib2(node); |
| |
| return returnNode(makeNodeHandle(node)); |
| } |
| } |
| } // end of PrecedingSiblingIterator |
| |
| /** |
| * Iterator that returns preceding siblings of a given type for |
| * a given node |
| */ |
| public final class TypedPrecedingSiblingIterator |
| extends PrecedingSiblingIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedPrecedingSiblingIterator |
| * |
| * |
| * @param type The extended type ID being requested. |
| */ |
| public TypedPrecedingSiblingIterator(int type) |
| { |
| _nodeType = type; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| int node = _currentNode; |
| |
| final int nodeType = _nodeType; |
| final int startNodeID = _startNodeID; |
| |
| if (nodeType != DTM.ELEMENT_NODE) { |
| while (node != NULL && node != startNodeID && _exptype2(node) != nodeType) { |
| node = _nextsib2(node); |
| } |
| } |
| else { |
| while (node != NULL && node != startNodeID && _exptype2(node) < DTM.NTYPES) { |
| node = _nextsib2(node); |
| } |
| } |
| |
| if (node == DTM.NULL || node == startNodeID) { |
| _currentNode = NULL; |
| return NULL; |
| } |
| else { |
| _currentNode = _nextsib2(node); |
| return returnNode(makeNodeHandle(node)); |
| } |
| } |
| |
| /** |
| * Return the index of the last node in this iterator. |
| */ |
| public int getLast() |
| { |
| if (_last != -1) |
| return _last; |
| |
| setMark(); |
| |
| int node = _currentNode; |
| final int nodeType = _nodeType; |
| final int startNodeID = _startNodeID; |
| |
| int last = 0; |
| if (nodeType != DTM.ELEMENT_NODE) { |
| while (node != NULL && node != startNodeID) { |
| if (_exptype2(node) == nodeType) { |
| last++; |
| } |
| node = _nextsib2(node); |
| } |
| } |
| else { |
| while (node != NULL && node != startNodeID) { |
| if (_exptype2(node) >= DTM.NTYPES) { |
| last++; |
| } |
| node = _nextsib2(node); |
| } |
| } |
| |
| gotoMark(); |
| |
| return (_last = last); |
| } |
| } // end of TypedPrecedingSiblingIterator |
| |
| /** |
| * Iterator that returns preceding nodes of a given node. |
| * This includes the node set {root+1, start-1}, but excludes |
| * all ancestors, attributes, and namespace nodes. |
| */ |
| public class PrecedingIterator extends InternalAxisIteratorBase |
| { |
| |
| /** The max ancestors, but it can grow... */ |
| private final int _maxAncestors = 8; |
| |
| /** |
| * The stack of start node + ancestors up to the root of the tree, |
| * which we must avoid. |
| */ |
| protected int[] _stack = new int[_maxAncestors]; |
| |
| /** (not sure yet... -sb) */ |
| protected int _sp, _oldsp; |
| |
| protected int _markedsp, _markedNode, _markedDescendant; |
| |
| /* _currentNode precedes candidates. This is the identity, not the handle! */ |
| |
| /** |
| * True if this iterator has a reversed axis. |
| * |
| * @return true since this iterator is a reversed axis. |
| */ |
| public boolean isReverse() |
| { |
| return true; |
| } |
| |
| /** |
| * Returns a deep copy of this iterator. The cloned iterator is not reset. |
| * |
| * @return a deep copy of this iterator. |
| */ |
| public DTMAxisIterator cloneIterator() |
| { |
| _isRestartable = false; |
| |
| try |
| { |
| final PrecedingIterator clone = (PrecedingIterator) super.clone(); |
| final int[] stackCopy = new int[_stack.length]; |
| System.arraycopy(_stack, 0, stackCopy, 0, _stack.length); |
| |
| clone._stack = stackCopy; |
| |
| // return clone.reset(); |
| return clone; |
| } |
| catch (CloneNotSupportedException e) |
| { |
| throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ITERATOR_CLONE_NOT_SUPPORTED, null)); //"Iterator clone not supported."); |
| } |
| } |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| node = makeNodeIdentity(node); |
| |
| // iterator is not a clone |
| int parent, index; |
| |
| if (_type2(node) == DTM.ATTRIBUTE_NODE) |
| node = _parent2(node); |
| |
| _startNode = node; |
| _stack[index = 0] = node; |
| |
| parent=node; |
| while ((parent = _parent2(parent)) != NULL) |
| { |
| if (++index == _stack.length) |
| { |
| final int[] stack = new int[index*2]; |
| System.arraycopy(_stack, 0, stack, 0, index); |
| _stack = stack; |
| } |
| _stack[index] = parent; |
| } |
| |
| if(index>0) |
| --index; // Pop actual root node (if not start) back off the stack |
| |
| _currentNode=_stack[index]; // Last parent before root node |
| |
| _oldsp = _sp = index; |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| // Bugzilla 8324: We were forgetting to skip Attrs and NS nodes. |
| // Also recoded the loop controls for clarity and to flatten out |
| // the tail-recursion. |
| for(++_currentNode; _sp>=0; ++_currentNode) |
| { |
| if(_currentNode < _stack[_sp]) |
| { |
| int type = _type2(_currentNode); |
| if(type != ATTRIBUTE_NODE && type != NAMESPACE_NODE) |
| return returnNode(makeNodeHandle(_currentNode)); |
| } |
| else |
| --_sp; |
| } |
| return NULL; |
| } |
| |
| // redefine DTMAxisIteratorBase's reset |
| |
| /** |
| * Resets the iterator to the last start node. |
| * |
| * @return A DTMAxisIterator, which may or may not be the same as this |
| * iterator. |
| */ |
| public DTMAxisIterator reset() |
| { |
| |
| _sp = _oldsp; |
| |
| return resetPosition(); |
| } |
| |
| public void setMark() { |
| _markedsp = _sp; |
| _markedNode = _currentNode; |
| _markedDescendant = _stack[0]; |
| } |
| |
| public void gotoMark() { |
| _sp = _markedsp; |
| _currentNode = _markedNode; |
| } |
| } // end of PrecedingIterator |
| |
| /** |
| * Iterator that returns preceding nodes of agiven type for a |
| * given node. This includes the node set {root+1, start-1}, but |
| * excludes all ancestors. |
| */ |
| public final class TypedPrecedingIterator extends PrecedingIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedPrecedingIterator |
| * |
| * |
| * @param type The extended type ID being requested. |
| */ |
| public TypedPrecedingIterator(int type) |
| { |
| _nodeType = type; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| int node = _currentNode; |
| final int nodeType = _nodeType; |
| |
| if (nodeType >= DTM.NTYPES) { |
| while (true) { |
| node++; |
| |
| if (_sp < 0) { |
| node = NULL; |
| break; |
| } |
| else if (node >= _stack[_sp]) { |
| if (--_sp < 0) { |
| node = NULL; |
| break; |
| } |
| } |
| else if (_exptype2(node) == nodeType) { |
| break; |
| } |
| } |
| } |
| else { |
| int expType; |
| |
| while (true) { |
| node++; |
| |
| if (_sp < 0) { |
| node = NULL; |
| break; |
| } |
| else if (node >= _stack[_sp]) { |
| if (--_sp < 0) { |
| node = NULL; |
| break; |
| } |
| } |
| else { |
| expType = _exptype2(node); |
| if (expType < DTM.NTYPES) { |
| if (expType == nodeType) { |
| break; |
| } |
| } |
| else { |
| if (m_extendedTypes[expType].getNodeType() == nodeType) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| _currentNode = node; |
| |
| return (node == NULL) ? NULL : returnNode(makeNodeHandle(node)); |
| } |
| } // end of TypedPrecedingIterator |
| |
| /** |
| * Iterator that returns following nodes of for a given node. |
| */ |
| public class FollowingIterator extends InternalAxisIteratorBase |
| { |
| //DTMAxisTraverser m_traverser; // easier for now |
| |
| public FollowingIterator() |
| { |
| //m_traverser = getAxisTraverser(Axis.FOLLOWING); |
| } |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| _startNode = node; |
| |
| //_currentNode = m_traverser.first(node); |
| |
| node = makeNodeIdentity(node); |
| |
| int first; |
| int type = _type2(node); |
| |
| if ((DTM.ATTRIBUTE_NODE == type) || (DTM.NAMESPACE_NODE == type)) |
| { |
| node = _parent2(node); |
| first = _firstch2(node); |
| |
| if (NULL != first) { |
| _currentNode = makeNodeHandle(first); |
| return resetPosition(); |
| } |
| } |
| |
| do |
| { |
| first = _nextsib2(node); |
| |
| if (NULL == first) |
| node = _parent2(node); |
| } |
| while (NULL == first && NULL != node); |
| |
| _currentNode = makeNodeHandle(first); |
| |
| // _currentNode precedes possible following(node) nodes |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| |
| int node = _currentNode; |
| |
| //_currentNode = m_traverser.next(_startNode, _currentNode); |
| int current = makeNodeIdentity(node); |
| |
| while (true) |
| { |
| current++; |
| |
| int type = _type2(current); |
| if (NULL == type) { |
| _currentNode = NULL; |
| return returnNode(node); |
| } |
| |
| if (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type) |
| continue; |
| |
| _currentNode = makeNodeHandle(current); |
| return returnNode(node); |
| } |
| } |
| |
| } // end of FollowingIterator |
| |
| /** |
| * Iterator that returns following nodes of a given type for a given node. |
| */ |
| public final class TypedFollowingIterator extends FollowingIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedFollowingIterator |
| * |
| * |
| * @param type The extended type ID being requested. |
| */ |
| public TypedFollowingIterator(int type) |
| { |
| _nodeType = type; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| int current; |
| int node; |
| int type; |
| |
| final int nodeType = _nodeType; |
| int currentNodeID = makeNodeIdentity(_currentNode); |
| |
| if (nodeType >= DTM.NTYPES) { |
| do { |
| node = currentNodeID; |
| current = node; |
| |
| do { |
| current++; |
| type = _type2(current); |
| } |
| while (type != NULL && (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type)); |
| |
| currentNodeID = (type != NULL) ? current : NULL; |
| } |
| while (node != DTM.NULL && _exptype2(node) != nodeType); |
| } |
| else { |
| do { |
| node = currentNodeID; |
| current = node; |
| |
| do { |
| current++; |
| type = _type2(current); |
| } |
| while (type != NULL && (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type)); |
| |
| currentNodeID = (type != NULL) ? current : NULL; |
| } |
| while (node != DTM.NULL |
| && (_exptype2(node) != nodeType && _type2(node) != nodeType)); |
| } |
| |
| _currentNode = makeNodeHandle(currentNodeID); |
| return (node == DTM.NULL ? DTM.NULL :returnNode(makeNodeHandle(node))); |
| } |
| } // end of TypedFollowingIterator |
| |
| /** |
| * Iterator that returns the ancestors of a given node in document |
| * order. (NOTE! This was changed from the XSLTC code!) |
| */ |
| public class AncestorIterator extends InternalAxisIteratorBase |
| { |
| // The initial size of the ancestor array |
| private static final int m_blocksize = 32; |
| |
| // The array for ancestor nodes. This array will grow dynamically. |
| int[] m_ancestors = new int[m_blocksize]; |
| |
| // Number of ancestor nodes in the array |
| int m_size = 0; |
| |
| int m_ancestorsPos; |
| |
| int m_markedPos; |
| |
| /** The real start node for this axes, since _startNode will be adjusted. */ |
| int m_realStartNode; |
| |
| /** |
| * Get start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @return The root node of the iteration. |
| */ |
| public int getStartNode() |
| { |
| return m_realStartNode; |
| } |
| |
| /** |
| * True if this iterator has a reversed axis. |
| * |
| * @return true since this iterator is a reversed axis. |
| */ |
| public final boolean isReverse() |
| { |
| return true; |
| } |
| |
| /** |
| * Returns a deep copy of this iterator. The cloned iterator is not reset. |
| * |
| * @return a deep copy of this iterator. |
| */ |
| public DTMAxisIterator cloneIterator() |
| { |
| _isRestartable = false; // must set to false for any clone |
| |
| try |
| { |
| final AncestorIterator clone = (AncestorIterator) super.clone(); |
| |
| clone._startNode = _startNode; |
| |
| // return clone.reset(); |
| return clone; |
| } |
| catch (CloneNotSupportedException e) |
| { |
| throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ITERATOR_CLONE_NOT_SUPPORTED, null)); //"Iterator clone not supported."); |
| } |
| } |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| m_realStartNode = node; |
| |
| if (_isRestartable) |
| { |
| int nodeID = makeNodeIdentity(node); |
| m_size = 0; |
| |
| if (nodeID == DTM.NULL) { |
| _currentNode = DTM.NULL; |
| m_ancestorsPos = 0; |
| return this; |
| } |
| |
| // Start from the current node's parent if |
| // _includeSelf is false. |
| if (!_includeSelf) { |
| nodeID = _parent2(nodeID); |
| node = makeNodeHandle(nodeID); |
| } |
| |
| _startNode = node; |
| |
| while (nodeID != END) { |
| //m_ancestors.addElement(node); |
| if (m_size >= m_ancestors.length) |
| { |
| int[] newAncestors = new int[m_size * 2]; |
| System.arraycopy(m_ancestors, 0, newAncestors, 0, m_ancestors.length); |
| m_ancestors = newAncestors; |
| } |
| |
| m_ancestors[m_size++] = node; |
| nodeID = _parent2(nodeID); |
| node = makeNodeHandle(nodeID); |
| } |
| |
| m_ancestorsPos = m_size - 1; |
| |
| _currentNode = (m_ancestorsPos>=0) |
| ? m_ancestors[m_ancestorsPos] |
| : DTM.NULL; |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Resets the iterator to the last start node. |
| * |
| * @return A DTMAxisIterator, which may or may not be the same as this |
| * iterator. |
| */ |
| public DTMAxisIterator reset() |
| { |
| |
| m_ancestorsPos = m_size - 1; |
| |
| _currentNode = (m_ancestorsPos >= 0) ? m_ancestors[m_ancestorsPos] |
| : DTM.NULL; |
| |
| return resetPosition(); |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| |
| int next = _currentNode; |
| |
| int pos = --m_ancestorsPos; |
| |
| _currentNode = (pos >= 0) ? m_ancestors[m_ancestorsPos] |
| : DTM.NULL; |
| |
| return returnNode(next); |
| } |
| |
| public void setMark() { |
| m_markedPos = m_ancestorsPos; |
| } |
| |
| public void gotoMark() { |
| m_ancestorsPos = m_markedPos; |
| _currentNode = m_ancestorsPos>=0 ? m_ancestors[m_ancestorsPos] |
| : DTM.NULL; |
| } |
| } // end of AncestorIterator |
| |
| /** |
| * Typed iterator that returns the ancestors of a given node. |
| */ |
| public final class TypedAncestorIterator extends AncestorIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedAncestorIterator |
| * |
| * |
| * @param type The extended type ID being requested. |
| */ |
| public TypedAncestorIterator(int type) |
| { |
| _nodeType = type; |
| } |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| m_realStartNode = node; |
| |
| if (_isRestartable) |
| { |
| int nodeID = makeNodeIdentity(node); |
| m_size = 0; |
| |
| if (nodeID == DTM.NULL) { |
| _currentNode = DTM.NULL; |
| m_ancestorsPos = 0; |
| return this; |
| } |
| |
| final int nodeType = _nodeType; |
| |
| if (!_includeSelf) { |
| nodeID = _parent2(nodeID); |
| node = makeNodeHandle(nodeID); |
| } |
| |
| _startNode = node; |
| |
| if (nodeType >= DTM.NTYPES) { |
| while (nodeID != END) { |
| int eType = _exptype2(nodeID); |
| |
| if (eType == nodeType) { |
| if (m_size >= m_ancestors.length) |
| { |
| int[] newAncestors = new int[m_size * 2]; |
| System.arraycopy(m_ancestors, 0, newAncestors, 0, m_ancestors.length); |
| m_ancestors = newAncestors; |
| } |
| m_ancestors[m_size++] = makeNodeHandle(nodeID); |
| } |
| nodeID = _parent2(nodeID); |
| } |
| } |
| else { |
| while (nodeID != END) { |
| int eType = _exptype2(nodeID); |
| |
| if ((eType < DTM.NTYPES && eType == nodeType) |
| || (eType >= DTM.NTYPES |
| && m_extendedTypes[eType].getNodeType() == nodeType)) { |
| if (m_size >= m_ancestors.length) |
| { |
| int[] newAncestors = new int[m_size * 2]; |
| System.arraycopy(m_ancestors, 0, newAncestors, 0, m_ancestors.length); |
| m_ancestors = newAncestors; |
| } |
| m_ancestors[m_size++] = makeNodeHandle(nodeID); |
| } |
| nodeID = _parent2(nodeID); |
| } |
| } |
| m_ancestorsPos = m_size - 1; |
| |
| _currentNode = (m_ancestorsPos>=0) |
| ? m_ancestors[m_ancestorsPos] |
| : DTM.NULL; |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Return the node at the given position. |
| */ |
| public int getNodeByPosition(int position) |
| { |
| if (position > 0 && position <= m_size) { |
| return m_ancestors[position-1]; |
| } |
| else |
| return DTM.NULL; |
| } |
| |
| /** |
| * Returns the position of the last node within the iteration, as |
| * defined by XPath. |
| */ |
| public int getLast() { |
| return m_size; |
| } |
| } // end of TypedAncestorIterator |
| |
| /** |
| * Iterator that returns the descendants of a given node. |
| */ |
| public class DescendantIterator extends InternalAxisIteratorBase |
| { |
| |
| /** |
| * Set start to END should 'close' the iterator, |
| * i.e. subsequent call to next() should return END. |
| * |
| * @param node Sets the root of the iteration. |
| * |
| * @return A DTMAxisIterator set to the start of the iteration. |
| */ |
| public DTMAxisIterator setStartNode(int node) |
| { |
| //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily |
| if (node == DTMDefaultBase.ROOTNODE) |
| node = getDocument(); |
| if (_isRestartable) |
| { |
| node = makeNodeIdentity(node); |
| _startNode = node; |
| |
| if (_includeSelf) |
| node--; |
| |
| _currentNode = node; |
| |
| return resetPosition(); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * Tell if this node identity is a descendant. Assumes that |
| * the node info for the element has already been obtained. |
| * |
| * This one-sided test works only if the parent has been |
| * previously tested and is known to be a descendent. It fails if |
| * the parent is the _startNode's next sibling, or indeed any node |
| * that follows _startNode in document order. That may suffice |
| * for this iterator, but it's not really an isDescendent() test. |
| * %REVIEW% rename? |
| * |
| * @param identity The index number of the node in question. |
| * @return true if the index is a descendant of _startNode. |
| */ |
| protected final boolean isDescendant(int identity) |
| { |
| return (_parent2(identity) >= _startNode) || (_startNode == identity); |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| final int startNode = _startNode; |
| if (startNode == NULL) { |
| return NULL; |
| } |
| |
| if (_includeSelf && (_currentNode + 1) == startNode) |
| return returnNode(makeNodeHandle(++_currentNode)); // | m_dtmIdent); |
| |
| int node = _currentNode; |
| int type; |
| |
| // %OPT% If the startNode is the root node, do not need |
| // to do the isDescendant() check. |
| if (startNode == ROOTNODE) { |
| int eType; |
| do { |
| node++; |
| eType = _exptype2(node); |
| |
| if (NULL == eType) { |
| _currentNode = NULL; |
| return END; |
| } |
| } while (eType == TEXT_NODE |
| || (type = m_extendedTypes[eType].getNodeType()) == ATTRIBUTE_NODE |
| || type == NAMESPACE_NODE); |
| } |
| else { |
| do { |
| node++; |
| type = _type2(node); |
| |
| if (NULL == type ||!isDescendant(node)) { |
| _currentNode = NULL; |
| return END; |
| } |
| } while(ATTRIBUTE_NODE == type || TEXT_NODE == type |
| || NAMESPACE_NODE == type); |
| } |
| |
| _currentNode = node; |
| return returnNode(makeNodeHandle(node)); // make handle. |
| } |
| |
| /** |
| * Reset. |
| * |
| */ |
| public DTMAxisIterator reset() |
| { |
| |
| final boolean temp = _isRestartable; |
| |
| _isRestartable = true; |
| |
| setStartNode(makeNodeHandle(_startNode)); |
| |
| _isRestartable = temp; |
| |
| return this; |
| } |
| |
| } // end of DescendantIterator |
| |
| /** |
| * Typed iterator that returns the descendants of a given node. |
| */ |
| public final class TypedDescendantIterator extends DescendantIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedDescendantIterator |
| * |
| * |
| * @param nodeType Extended type ID being requested. |
| */ |
| public TypedDescendantIterator(int nodeType) |
| { |
| _nodeType = nodeType; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| final int startNode = _startNode; |
| if (_startNode == NULL) { |
| return NULL; |
| } |
| |
| int node = _currentNode; |
| |
| int expType; |
| final int nodeType = _nodeType; |
| |
| if (nodeType != DTM.ELEMENT_NODE) |
| { |
| do |
| { |
| node++; |
| expType = _exptype2(node); |
| |
| if (NULL == expType || _parent2(node) < startNode && startNode != node) { |
| _currentNode = NULL; |
| return END; |
| } |
| } |
| while (expType != nodeType); |
| } |
| // %OPT% If the start node is root (e.g. in the case of //node), |
| // we can save the isDescendant() check, because all nodes are |
| // descendants of root. |
| else if (startNode == DTMDefaultBase.ROOTNODE) |
| { |
| do |
| { |
| node++; |
| expType = _exptype2(node); |
| |
| if (NULL == expType) { |
| _currentNode = NULL; |
| return END; |
| } |
| } while (expType < DTM.NTYPES |
| || m_extendedTypes[expType].getNodeType() != DTM.ELEMENT_NODE); |
| } |
| else |
| { |
| do |
| { |
| node++; |
| expType = _exptype2(node); |
| |
| if (NULL == expType || _parent2(node) < startNode && startNode != node) { |
| _currentNode = NULL; |
| return END; |
| } |
| } |
| while (expType < DTM.NTYPES |
| || m_extendedTypes[expType].getNodeType() != DTM.ELEMENT_NODE); |
| } |
| |
| _currentNode = node; |
| return returnNode(makeNodeHandle(node)); |
| } |
| } // end of TypedDescendantIterator |
| |
| /** |
| * Iterator that returns a given node only if it is of a given type. |
| */ |
| public final class TypedSingletonIterator extends SingletonIterator |
| { |
| |
| /** The extended type ID that was requested. */ |
| private final int _nodeType; |
| |
| /** |
| * Constructor TypedSingletonIterator |
| * |
| * |
| * @param nodeType The extended type ID being requested. |
| */ |
| public TypedSingletonIterator(int nodeType) |
| { |
| _nodeType = nodeType; |
| } |
| |
| /** |
| * Get the next node in the iteration. |
| * |
| * @return The next node handle in the iteration, or END. |
| */ |
| public int next() |
| { |
| |
| final int result = _currentNode; |
| if (result == END) |
| return DTM.NULL; |
| |
| _currentNode = END; |
| |
| if (_nodeType >= DTM.NTYPES) { |
| if (_exptype2(makeNodeIdentity(result)) == _nodeType) { |
| return returnNode(result); |
| } |
| } |
| else { |
| if (_type2(makeNodeIdentity(result)) == _nodeType) { |
| return returnNode(result); |
| } |
| } |
| |
| return NULL; |
| } |
| } // end of TypedSingletonIterator |
| |
| /******************************************************************* |
| * End of nested iterators |
| *******************************************************************/ |
| |
| |
| // %OPT% Array references which are used to cache the map0 arrays in |
| // SuballocatedIntVectors. Using the cached arrays reduces the level |
| // of indirection and results in better performance than just calling |
| // SuballocatedIntVector.elementAt(). |
| private int[] m_exptype_map0; |
| private int[] m_nextsib_map0; |
| private int[] m_firstch_map0; |
| private int[] m_parent_map0; |
| |
| // Double array references to the map arrays in SuballocatedIntVectors. |
| private int[][] m_exptype_map; |
| private int[][] m_nextsib_map; |
| private int[][] m_firstch_map; |
| private int[][] m_parent_map; |
| |
| // %OPT% Cache the array of extended types in this class |
| protected ExtendedType[] m_extendedTypes; |
| |
| // A Vector which is used to store the values of attribute, namespace, |
| // comment and PI nodes. |
| // |
| // %OPT% These values are unlikely to be equal. Storing |
| // them in a plain Vector is more efficient than storing in the |
| // DTMStringPool because we can save the cost for hash calculation. |
| // |
| // %REVISIT% Do we need a custom class (e.g. StringVector) here? |
| protected Vector m_values; |
| |
| // The current index into the m_values Vector. |
| private int m_valueIndex = 0; |
| |
| // The maximum value of the current node index. |
| private int m_maxNodeIndex; |
| |
| // Cache the shift and mask values for the SuballocatedIntVectors. |
| protected int m_SHIFT; |
| protected int m_MASK; |
| protected int m_blocksize; |
| |
| /** %OPT% If the offset and length of a Text node are within certain limits, |
| * we store a bitwise encoded value into an int, using 10 bits (max. 1024) |
| * for length and 21 bits for offset. We can save two SuballocatedIntVector |
| * calls for each getStringValueX() and dispatchCharacterEvents() call by |
| * doing this. |
| */ |
| // The number of bits for the length of a Text node. |
| protected final static int TEXT_LENGTH_BITS = 10; |
| |
| // The number of bits for the offset of a Text node. |
| protected final static int TEXT_OFFSET_BITS = 21; |
| |
| // The maximum length value |
| protected final static int TEXT_LENGTH_MAX = (1<<TEXT_LENGTH_BITS) - 1; |
| |
| // The maximum offset value |
| protected final static int TEXT_OFFSET_MAX = (1<<TEXT_OFFSET_BITS) - 1; |
| |
| // True if we want to build the ID index table. |
| protected boolean m_buildIdIndex = true; |
| |
| // Constant for empty String |
| private static final String EMPTY_STR = ""; |
| |
| // Constant for empty XMLString |
| private static final XMLString EMPTY_XML_STR = new XMLStringDefault(""); |
| |
| /** |
| * Construct a SAX2DTM2 object using the default block size. |
| */ |
| public SAX2DTM2(DTMManager mgr, Source source, int dtmIdentity, |
| DTMWSFilter whiteSpaceFilter, |
| XMLStringFactory xstringfactory, |
| boolean doIndexing) |
| { |
| |
| this(mgr, source, dtmIdentity, whiteSpaceFilter, |
| xstringfactory, doIndexing, DEFAULT_BLOCKSIZE, true, true, false); |
| } |
| |
| /** |
| * Construct a SAX2DTM2 object using the given block size. |
| */ |
| public SAX2DTM2(DTMManager mgr, Source source, int dtmIdentity, |
| DTMWSFilter whiteSpaceFilter, |
| XMLStringFactory xstringfactory, |
| boolean doIndexing, |
| int blocksize, |
| boolean usePrevsib, |
| boolean buildIdIndex, |
| boolean newNameTable) |
| { |
| |
| super(mgr, source, dtmIdentity, whiteSpaceFilter, |
| xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable); |
| |
| // Initialize the values of m_SHIFT and m_MASK. |
| int shift; |
| for(shift=0; (blocksize>>>=1) != 0; ++shift); |
| |
| m_blocksize = 1<<shift; |
| m_SHIFT = shift; |
| m_MASK = m_blocksize - 1; |
| |
| m_buildIdIndex = buildIdIndex; |
| |
| // Some documents do not have attribute nodes. That is why |
| // we set the initial size of this Vector to be small and set |
| // the increment to a bigger number. |
| m_values = new Vector(32, 512); |
| |
| m_maxNodeIndex = 1 << DTMManager.IDENT_DTM_NODE_BITS; |
| |
| // Set the map0 values in the constructor. |
| m_exptype_map0 = m_exptype.getMap0(); |
| m_nextsib_map0 = m_nextsib.getMap0(); |
| m_firstch_map0 = m_firstch.getMap0(); |
| m_parent_map0 = m_parent.getMap0(); |
| } |
| |
| /** |
| * Override DTMDefaultBase._exptype() by dropping the incremental code. |
| * |
| * <p>This one is less efficient than _exptype2. It is only used during |
| * DTM building. _exptype2 is used after the document is fully built. |
| */ |
| public final int _exptype(int identity) |
| { |
| return m_exptype.elementAt(identity); |
| } |
| |
| /************************************************************************ |
| * DTM base accessor interfaces |
| * |
| * %OPT% The code in the following interfaces (e.g. _exptype2, etc.) are |
| * very important to the DTM performance. To have the best performace, |
| * these several interfaces have direct access to the internal arrays of |
| * the SuballocatedIntVectors. The final modifier also has a noticeable |
| * impact on performance. |
| ***********************************************************************/ |
| |
| /** |
| * The optimized version of DTMDefaultBase._exptype(). |
| * |
| * @param identity A node identity, which <em>must not</em> be equal to |
| * <code>DTM.NULL</code> |
| */ |
| public final int _exptype2(int identity) |
| { |
| //return m_exptype.elementAt(identity); |
| |
| if (identity < m_blocksize) |
| return m_exptype_map0[identity]; |
| else |
| return m_exptype_map[identity>>>m_SHIFT][identity&m_MASK]; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase._nextsib(). |
| * |
| * @param identity A node identity, which <em>must not</em> be equal to |
| * <code>DTM.NULL</code> |
| */ |
| public final int _nextsib2(int identity) |
| { |
| //return m_nextsib.elementAt(identity); |
| |
| if (identity < m_blocksize) |
| return m_nextsib_map0[identity]; |
| else |
| return m_nextsib_map[identity>>>m_SHIFT][identity&m_MASK]; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase._firstch(). |
| * |
| * @param identity A node identity, which <em>must not</em> be equal to |
| * <code>DTM.NULL</code> |
| */ |
| public final int _firstch2(int identity) |
| { |
| //return m_firstch.elementAt(identity); |
| |
| if (identity < m_blocksize) |
| return m_firstch_map0[identity]; |
| else |
| return m_firstch_map[identity>>>m_SHIFT][identity&m_MASK]; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase._parent(). |
| * |
| * @param identity A node identity, which <em>must not</em> be equal to |
| * <code>DTM.NULL</code> |
| */ |
| public final int _parent2(int identity) |
| { |
| //return m_parent.elementAt(identity); |
| |
| if (identity < m_blocksize) |
| return m_parent_map0[identity]; |
| else |
| return m_parent_map[identity>>>m_SHIFT][identity&m_MASK]; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase._type(). |
| * |
| * @param identity A node identity, which <em>must not</em> be equal to |
| * <code>DTM.NULL</code> |
| */ |
| public final int _type2(int identity) |
| { |
| //int eType = _exptype2(identity); |
| int eType; |
| if (identity < m_blocksize) |
| eType = m_exptype_map0[identity]; |
| else |
| eType = m_exptype_map[identity>>>m_SHIFT][identity&m_MASK]; |
| |
| if (NULL != eType) |
| return m_extendedTypes[eType].getNodeType(); |
| else |
| return NULL; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase.getExpandedTypeID(int). |
| * |
| * <p>This one is only used by DOMAdapter.getExpandedTypeID(int), which |
| * is mostly called from the compiled translets. |
| */ |
| public final int getExpandedTypeID2(int nodeHandle) |
| { |
| int nodeID = makeNodeIdentity(nodeHandle); |
| |
| //return (nodeID != NULL) ? _exptype2(nodeID) : NULL; |
| |
| if (nodeID != NULL) { |
| if (nodeID < m_blocksize) |
| return m_exptype_map0[nodeID]; |
| else |
| return m_exptype_map[nodeID>>>m_SHIFT][nodeID&m_MASK]; |
| } |
| else |
| return NULL; |
| } |
| |
| /************************************************************************* |
| * END of DTM base accessor interfaces |
| *************************************************************************/ |
| |
| |
| /** |
| * Return the node type from the expanded type |
| */ |
| public final int _exptype2Type(int exptype) |
| { |
| if (NULL != exptype) |
| return m_extendedTypes[exptype].getNodeType(); |
| else |
| return NULL; |
| } |
| |
| /** |
| * Get a prefix either from the uri mapping, or just make |
| * one up! |
| * |
| * @param uri The namespace URI, which may be null. |
| * |
| * @return The prefix if there is one, or null. |
| */ |
| public int getIdForNamespace(String uri) |
| { |
| int index = m_values.indexOf(uri); |
| if (index < 0) |
| { |
| m_values.addElement(uri); |
| return m_valueIndex++; |
| } |
| else |
| return index; |
| } |
| |
| /** |
| * Override SAX2DTM.startElement() |
| * |
| * <p>Receive notification of the start of an element. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions at the start of |
| * each element (such as allocating a new tree node or writing |
| * output to a file).</p> |
| * |
| * @param uri The Namespace URI, or the empty string if the |
| * element has no Namespace URI or if Namespace |
| * processing is not being performed. |
| * @param localName The local name (without prefix), or the |
| * empty string if Namespace processing is not being |
| * performed. |
| * @param qName The qualified name (with prefix), or the |
| * empty string if qualified names are not available. |
| * @param attributes The specified or defaulted attributes. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#startElement |
| */ |
| public void startElement(String uri, String localName, String qName, Attributes attributes) |
| throws SAXException |
| { |
| |
| charactersFlush(); |
| |
| int exName = m_expandedNameTable.getExpandedTypeID(uri, localName, DTM.ELEMENT_NODE); |
| |
| int prefixIndex = (qName.length() != localName.length()) |
| ? m_valuesOrPrefixes.stringToIndex(qName) : 0; |
| |
| int elemNode = addNode(DTM.ELEMENT_NODE, exName, |
| m_parents.peek(), m_previous, prefixIndex, true); |
| |
| if(m_indexing) |
| indexNode(exName, elemNode); |
| |
| m_parents.push(elemNode); |
| |
| int startDecls = m_contextIndexes.peek(); |
| int nDecls = m_prefixMappings.size(); |
| String prefix; |
| |
| if(!m_pastFirstElement) |
| { |
| // SPECIAL CASE: Implied declaration at root element |
| prefix="xml"; |
| String declURL = "http://www.w3.org/XML/1998/namespace"; |
| exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE); |
| m_values.addElement(declURL); |
| int val = m_valueIndex++; |
| addNode(DTM.NAMESPACE_NODE, exName, elemNode, |
| DTM.NULL, val, false); |
| m_pastFirstElement=true; |
| } |
| |
| for (int i = startDecls; i < nDecls; i += 2) |
| { |
| prefix = (String) m_prefixMappings.elementAt(i); |
| |
| if (prefix == null) |
| continue; |
| |
| String declURL = (String) m_prefixMappings.elementAt(i + 1); |
| |
| exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE); |
| |
| m_values.addElement(declURL); |
| int val = m_valueIndex++; |
| |
| addNode(DTM.NAMESPACE_NODE, exName, elemNode, DTM.NULL, val, false); |
| } |
| |
| int n = attributes.getLength(); |
| |
| for (int i = 0; i < n; i++) |
| { |
| String attrUri = attributes.getURI(i); |
| String attrQName = attributes.getQName(i); |
| String valString = attributes.getValue(i); |
| |
| int nodeType; |
| |
| String attrLocalName = attributes.getLocalName(i); |
| |
| if ((null != attrQName) |
| && (attrQName.equals("xmlns") |
| || attrQName.startsWith("xmlns:"))) |
| { |
| prefix = getPrefix(attrQName, attrUri); |
| if (declAlreadyDeclared(prefix)) |
| continue; // go to the next attribute. |
| |
| nodeType = DTM.NAMESPACE_NODE; |
| } |
| else |
| { |
| nodeType = DTM.ATTRIBUTE_NODE; |
| |
| if (m_buildIdIndex && attributes.getType(i).equalsIgnoreCase("ID")) |
| setIDAttribute(valString, elemNode); |
| } |
| |
| // Bit of a hack... if somehow valString is null, stringToIndex will |
| // return -1, which will make things very unhappy. |
| if(null == valString) |
| valString = ""; |
| |
| m_values.addElement(valString); |
| int val = m_valueIndex++; |
| |
| if (attrLocalName.length() != attrQName.length()) |
| { |
| |
| prefixIndex = m_valuesOrPrefixes.stringToIndex(attrQName); |
| |
| int dataIndex = m_data.size(); |
| |
| m_data.addElement(prefixIndex); |
| m_data.addElement(val); |
| |
| val = -dataIndex; |
| } |
| |
| exName = m_expandedNameTable.getExpandedTypeID(attrUri, attrLocalName, nodeType); |
| addNode(nodeType, exName, elemNode, DTM.NULL, val, |
| false); |
| } |
| |
| if (null != m_wsfilter) |
| { |
| short wsv = m_wsfilter.getShouldStripSpace(makeNodeHandle(elemNode), this); |
| boolean shouldStrip = (DTMWSFilter.INHERIT == wsv) |
| ? getShouldStripWhitespace() |
| : (DTMWSFilter.STRIP == wsv); |
| |
| pushShouldStripWhitespace(shouldStrip); |
| } |
| |
| m_previous = DTM.NULL; |
| |
| m_contextIndexes.push(m_prefixMappings.size()); // for the children. |
| } |
| |
| /** |
| * Receive notification of the end of an element. |
| * |
| * <p>By default, do nothing. Application writers may override this |
| * method in a subclass to take specific actions at the end of |
| * each element (such as finalising a tree node or writing |
| * output to a file).</p> |
| * |
| * @param uri The Namespace URI, or the empty string if the |
| * element has no Namespace URI or if Namespace |
| * processing is not being performed. |
| * @param localName The local name (without prefix), or the |
| * empty string if Namespace processing is not being |
| * performed. |
| * @param qName The qualified XML 1.0 name (with prefix), or the |
| * empty string if qualified names are not available. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#endElement |
| */ |
| public void endElement(String uri, String localName, String qName) |
| throws SAXException |
| { |
| charactersFlush(); |
| |
| // If no one noticed, startPrefixMapping is a drag. |
| // Pop the context for the last child (the one pushed by startElement) |
| m_contextIndexes.quickPop(1); |
| |
| // Do it again for this one (the one pushed by the last endElement). |
| int topContextIndex = m_contextIndexes.peek(); |
| if (topContextIndex != m_prefixMappings.size()) { |
| m_prefixMappings.setSize(topContextIndex); |
| } |
| |
| m_previous = m_parents.pop(); |
| |
| popShouldStripWhitespace(); |
| } |
| |
| /** |
| * Report an XML comment anywhere in the document. |
| * |
| * <p>This callback will be used for comments inside or outside the |
| * document element, including comments in the external DTD |
| * subset (if read).</p> |
| * |
| * @param ch An array holding the characters in the comment. |
| * @param start The starting position in the array. |
| * @param length The number of characters to use from the array. |
| * @throws SAXException The application may raise an exception. |
| */ |
| public void comment(char ch[], int start, int length) throws SAXException |
| { |
| |
| if (m_insideDTD) // ignore comments if we're inside the DTD |
| return; |
| |
| charactersFlush(); |
| |
| // %OPT% Saving the comment string in a Vector has a lower cost than |
| // saving it in DTMStringPool. |
| m_values.addElement(new String(ch, start, length)); |
| int dataIndex = m_valueIndex++; |
| |
| m_previous = addNode(DTM.COMMENT_NODE, DTM.COMMENT_NODE, |
| m_parents.peek(), m_previous, dataIndex, false); |
| } |
| |
| /** |
| * Receive notification of the beginning of the document. |
| * |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#startDocument |
| */ |
| public void startDocument() throws SAXException |
| { |
| |
| int doc = addNode(DTM.DOCUMENT_NODE, |
| DTM.DOCUMENT_NODE, |
| DTM.NULL, DTM.NULL, 0, true); |
| |
| m_parents.push(doc); |
| m_previous = DTM.NULL; |
| |
| m_contextIndexes.push(m_prefixMappings.size()); // for the next element. |
| } |
| |
| /** |
| * Receive notification of the end of the document. |
| * |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#endDocument |
| */ |
| public void endDocument() throws SAXException |
| { |
| super.endDocument(); |
| |
| // Add a NULL entry to the end of the node arrays as |
| // the end indication. |
| m_exptype.addElement(NULL); |
| m_parent.addElement(NULL); |
| m_nextsib.addElement(NULL); |
| m_firstch.addElement(NULL); |
| |
| // Set the cached references after the document is built. |
| m_extendedTypes = m_expandedNameTable.getExtendedTypes(); |
| m_exptype_map = m_exptype.getMap(); |
| m_nextsib_map = m_nextsib.getMap(); |
| m_firstch_map = m_firstch.getMap(); |
| m_parent_map = m_parent.getMap(); |
| } |
| |
| /** |
| * Construct the node map from the node. |
| * |
| * @param type raw type ID, one of DTM.XXX_NODE. |
| * @param expandedTypeID The expended type ID. |
| * @param parentIndex The current parent index. |
| * @param previousSibling The previous sibling index. |
| * @param dataOrPrefix index into m_data table, or string handle. |
| * @param canHaveFirstChild true if the node can have a first child, false |
| * if it is atomic. |
| * |
| * @return The index identity of the node that was added. |
| */ |
| protected final int addNode(int type, int expandedTypeID, |
| int parentIndex, int previousSibling, |
| int dataOrPrefix, boolean canHaveFirstChild) |
| { |
| // Common to all nodes: |
| int nodeIndex = m_size++; |
| |
| // Have we overflowed a DTM Identity's addressing range? |
| //if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS)) |
| if (nodeIndex == m_maxNodeIndex) |
| { |
| addNewDTMID(nodeIndex); |
| m_maxNodeIndex += (1 << DTMManager.IDENT_DTM_NODE_BITS); |
| } |
| |
| m_firstch.addElement(DTM.NULL); |
| m_nextsib.addElement(DTM.NULL); |
| m_parent.addElement(parentIndex); |
| m_exptype.addElement(expandedTypeID); |
| m_dataOrQName.addElement(dataOrPrefix); |
| |
| if (m_prevsib != null) { |
| m_prevsib.addElement(previousSibling); |
| } |
| |
| if (m_locator != null && m_useSourceLocationProperty) { |
| setSourceLocation(); |
| } |
| |
| // Note that nextSibling is not processed until charactersFlush() |
| // is called, to handle successive characters() events. |
| |
| // Special handling by type: Declare namespaces, attach first child |
| switch(type) |
| { |
| case DTM.NAMESPACE_NODE: |
| declareNamespaceInContext(parentIndex,nodeIndex); |
| break; |
| case DTM.ATTRIBUTE_NODE: |
| break; |
| default: |
| if (DTM.NULL != previousSibling) { |
| m_nextsib.setElementAt(nodeIndex,previousSibling); |
| } |
| else if (DTM.NULL != parentIndex) { |
| m_firstch.setElementAt(nodeIndex,parentIndex); |
| } |
| break; |
| } |
| |
| return nodeIndex; |
| } |
| |
| /** |
| * Check whether accumulated text should be stripped; if not, |
| * append the appropriate flavor of text/cdata node. |
| */ |
| protected final void charactersFlush() |
| { |
| |
| if (m_textPendingStart >= 0) // -1 indicates no-text-in-progress |
| { |
| int length = m_chars.size() - m_textPendingStart; |
| boolean doStrip = false; |
| |
| if (getShouldStripWhitespace()) |
| { |
| doStrip = m_chars.isWhitespace(m_textPendingStart, length); |
| } |
| |
| if (doStrip) { |
| m_chars.setLength(m_textPendingStart); // Discard accumulated text |
| } else { |
| // Guard against characters/ignorableWhitespace events that |
| // contained no characters. They should not result in a node. |
| if (length > 0) { |
| // If the offset and length do not exceed the given limits |
| // (offset < 2^21 and length < 2^10), then save both the offset |
| // and length in a bitwise encoded value. |
| if (length <= TEXT_LENGTH_MAX |
| && m_textPendingStart <= TEXT_OFFSET_MAX) { |
| m_previous = addNode(m_coalescedTextType, DTM.TEXT_NODE, |
| m_parents.peek(), m_previous, |
| length + (m_textPendingStart << TEXT_LENGTH_BITS), |
| false); |
| |
| } else { |
| // Store offset and length in the m_data array if one exceeds |
| // the given limits. Use a negative dataIndex as an indication. |
| int dataIndex = m_data.size(); |
| m_previous = addNode(m_coalescedTextType, DTM.TEXT_NODE, |
| m_parents.peek(), m_previous, -dataIndex, false); |
| |
| m_data.addElement(m_textPendingStart); |
| m_data.addElement(length); |
| } |
| } |
| } |
| |
| // Reset for next text block |
| m_textPendingStart = -1; |
| m_textType = m_coalescedTextType = DTM.TEXT_NODE; |
| } |
| } |
| |
| /** |
| * Override the processingInstruction() interface in SAX2DTM2. |
| * <p> |
| * %OPT% This one is different from SAX2DTM.processingInstruction() |
| * in that we do not use extended types for PI nodes. The name of |
| * the PI is saved in the DTMStringPool. |
| * |
| * Receive notification of a processing instruction. |
| * |
| * @param target The processing instruction target. |
| * @param data The processing instruction data, or null if |
| * none is supplied. |
| * @throws SAXException Any SAX exception, possibly |
| * wrapping another exception. |
| * @see org.xml.sax.ContentHandler#processingInstruction |
| */ |
| public void processingInstruction(String target, String data) |
| throws SAXException |
| { |
| |
| charactersFlush(); |
| |
| int dataIndex = m_data.size(); |
| m_previous = addNode(DTM.PROCESSING_INSTRUCTION_NODE, |
| DTM.PROCESSING_INSTRUCTION_NODE, |
| m_parents.peek(), m_previous, |
| -dataIndex, false); |
| |
| m_data.addElement(m_valuesOrPrefixes.stringToIndex(target)); |
| m_values.addElement(data); |
| m_data.addElement(m_valueIndex++); |
| |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase.getFirstAttribute(). |
| * <p> |
| * Given a node handle, get the index of the node's first attribute. |
| * |
| * @param nodeHandle int Handle of the node. |
| * @return Handle of first attribute, or DTM.NULL to indicate none exists. |
| */ |
| public final int getFirstAttribute(int nodeHandle) |
| { |
| int nodeID = makeNodeIdentity(nodeHandle); |
| |
| if (nodeID == DTM.NULL) |
| return DTM.NULL; |
| |
| int type = _type2(nodeID); |
| |
| if (DTM.ELEMENT_NODE == type) |
| { |
| // Assume that attributes and namespaces immediately follow the element. |
| while (true) |
| { |
| nodeID++; |
| // Assume this can not be null. |
| type = _type2(nodeID); |
| |
| if (type == DTM.ATTRIBUTE_NODE) |
| { |
| return makeNodeHandle(nodeID); |
| } |
| else if (DTM.NAMESPACE_NODE != type) |
| { |
| break; |
| } |
| } |
| } |
| |
| return DTM.NULL; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase.getFirstAttributeIdentity(int). |
| * <p> |
| * Given a node identity, get the index of the node's first attribute. |
| * |
| * @param identity int identity of the node. |
| * @return Identity of first attribute, or DTM.NULL to indicate none exists. |
| */ |
| protected int getFirstAttributeIdentity(int identity) { |
| if (identity == NULL) { |
| return NULL; |
| } |
| int type = _type2(identity); |
| |
| if (DTM.ELEMENT_NODE == type) |
| { |
| // Assume that attributes and namespaces immediately follow the element. |
| while (true) |
| { |
| identity++; |
| |
| // Assume this can not be null. |
| type = _type2(identity); |
| |
| if (type == DTM.ATTRIBUTE_NODE) |
| { |
| return identity; |
| } |
| else if (DTM.NAMESPACE_NODE != type) |
| { |
| break; |
| } |
| } |
| } |
| |
| return DTM.NULL; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase.getNextAttributeIdentity(int). |
| * <p> |
| * Given a node identity for an attribute, advance to the next attribute. |
| * |
| * @param identity int identity of the attribute node. This |
| * <strong>must</strong> be an attribute node. |
| * |
| * @return int DTM node-identity of the resolved attr, |
| * or DTM.NULL to indicate none exists. |
| * |
| */ |
| protected int getNextAttributeIdentity(int identity) { |
| // Assume that attributes and namespace nodes immediately follow the element |
| while (true) { |
| identity++; |
| int type = _type2(identity); |
| |
| if (type == DTM.ATTRIBUTE_NODE) { |
| return identity; |
| } else if (type != DTM.NAMESPACE_NODE) { |
| break; |
| } |
| } |
| |
| return DTM.NULL; |
| } |
| |
| /** |
| * The optimized version of DTMDefaultBase.getTypedAttribute(int, int). |
| * <p> |
| * Given a node handle and an expanded type ID, get the index of the node's |
| * attribute of that type, if any. |
| * |
| * @param nodeHandle int Handle of the node. |
| * @param attType int expanded type ID of the required attribute. |
| * @return Handle of attribute of the required type, or DTM.NULL to indicate |
| * none exists. |
| */ |
| protected final int getTypedAttribute(int nodeHandle, int attType) |
| { |
| |
| int nodeID = makeNodeIdentity(nodeHandle); |
| |
| if (nodeID == DTM.NULL) |
| return DTM.NULL; |
| |
| int type = _type2(nodeID); |
| |
| if (DTM.ELEMENT_NODE == type) |
| { |
| int expType; |
| while (true) |
| { |
| nodeID++; |
| expType = _exptype2(nodeID); |
| |
| if (expType != DTM.NULL) |
| type = m_extendedTypes[expType].getNodeType(); |
| else |
| return DTM.NULL; |
| |
| if (type == DTM.ATTRIBUTE_NODE) |
| { |
| if (expType == attType) return makeNodeHandle(nodeID); |
| } |
| else if (DTM.NAMESPACE_NODE != type) |
| { |
| break; |
| } |
| } |
| } |
| |
| return DTM.NULL; |
| } |
| |
| /** |
| * Override SAX2DTM.getLocalName() in SAX2DTM2. |
| * <p>Processing for PIs is different. |
| * |
| * Given a node handle, return its XPath- style localname. (As defined in |
| * Namespaces, this is the portion of the name after any colon character). |
| * |
| * @param nodeHandle the id of the node. |
| * @return String Local name of this node. |
| */ |
| public String getLocalName(int nodeHandle) |
| { |
| int expType = _exptype(makeNodeIdentity(nodeHandle)); |
| |
| if (expType == DTM.PROCESSING_INSTRUCTION_NODE) |
| { |
| int dataIndex = _dataOrQName(makeNodeIdentity(nodeHandle)); |
| dataIndex = m_data.elementAt(-dataIndex); |
| return m_valuesOrPrefixes.indexToString(dataIndex); |
| } |
| else |
| return m_expandedNameTable.getLocalName(expType); |
| } |
| |
| /** |
| * The optimized version of SAX2DTM.getNodeNameX(). |
| * <p> |
| * 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, which may be an empty string. |
| */ |
| public final String getNodeNameX(int nodeHandle) |
| { |
| |
| int nodeID = makeNodeIdentity(nodeHandle); |
| int eType = _exptype2(nodeID); |
| |
| if (eType == DTM.PROCESSING_INSTRUCTION_NODE) |
| { |
| int dataIndex = _dataOrQName(nodeID); |
| dataIndex = m_data.elementAt(-dataIndex); |
| return m_valuesOrPrefixes.indexToString(dataIndex); |
| } |
| |
| final ExtendedType extType = m_extendedTypes[eType]; |
| |
| if (extType.getNamespace().length() == 0) |
| { |
| return extType.getLocalName(); |
| } |
| else |
| { |
| int qnameIndex = m_dataOrQName.elementAt(nodeID); |
| |
| if (qnameIndex == 0) |
| return extType.getLocalName(); |
| |
| if (qnameIndex < 0) |
| { |
| qnameIndex = -qnameIndex; |
| qnameIndex = m_data.elementAt(qnameIndex); |
| } |
| |
| return m_valuesOrPrefixes.indexToString(qnameIndex); |
| } |
| } |
| |
| /** |
| * The optimized version of SAX2DTM.getNodeName(). |
| * <p> |
| * 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... |
| * %REVIEW-COMMENT% It should never be empty, should it? |
| */ |
| public String getNodeName(int nodeHandle) |
| { |
| |
| int nodeID = makeNodeIdentity(nodeHandle); |
| int eType = _exptype2(nodeID); |
| |
| final ExtendedType extType = m_extendedTypes[eType]; |
| if (extType.getNamespace().length() == 0) |
| { |
| int type = extType.getNodeType(); |
| |
| String localName = extType.getLocalName(); |
| if (type == DTM.NAMESPACE_NODE) |
| { |
| if (localName.length() == 0) |
| return "xmlns"; |
| else |
| return "xmlns:" + localName; |
| } |
| else if (type == DTM.PROCESSING_INSTRUCTION_NODE) |
| { |
| int dataIndex = _dataOrQName(nodeID); |
| dataIndex = m_data.elementAt(-dataIndex); |
| return m_valuesOrPrefixes.indexToString(dataIndex); |
| } |
| else if (localName.length() == 0) |
| { |
| return getFixedNames(type); |
| } |
| else |
| return localName; |
| } |
| else |
| { |
| int qnameIndex = m_dataOrQName.elementAt(nodeID); |
| |
| if (qnameIndex == 0) |
| return extType.getLocalName(); |
| |
| if (qnameIndex < 0) |
| { |
| qnameIndex = -qnameIndex; |
| qnameIndex = m_data.elementAt(qnameIndex); |
| } |
| |
| return m_valuesOrPrefixes.indexToString(qnameIndex); |
| } |
| } |
| |
| /** |
| * Override SAX2DTM.getStringValue(int) |
| * <p> |
| * This method is only used by Xalan-J Interpretive. It is not used by XSLTC. |
| * <p> |
| * If the caller supplies an XMLStringFactory, the getStringValue() interface |
| * in SAX2DTM will be called. Otherwise just calls getStringValueX() and |
| * wraps the returned String in an XMLString. |
| * |
| * 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) |
| { |
| int identity = makeNodeIdentity(nodeHandle); |
| if (identity == DTM.NULL) |
| return EMPTY_XML_STR; |
| |
| int type= _type2(identity); |
| |
| if (type == DTM.ELEMENT_NODE || type == DTM.DOCUMENT_NODE) |
| { |
| int startNode = identity; |
| identity = _firstch2(identity); |
| if (DTM.NULL != identity) |
| { |
| int offset = -1; |
| int length = 0; |
| |
| do |
| { |
| type = _exptype2(identity); |
| |
| if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| if (dataIndex >= 0) |
| { |
| if (-1 == offset) |
| { |
| offset = dataIndex >>> TEXT_LENGTH_BITS; |
| } |
| |
| length += dataIndex & TEXT_LENGTH_MAX; |
| } |
| else |
| { |
| if (-1 == offset) |
| { |
| offset = m_data.elementAt(-dataIndex); |
| } |
| |
| length += m_data.elementAt(-dataIndex + 1); |
| } |
| } |
| |
| identity++; |
| } while (_parent2(identity) >= startNode); |
| |
| if (length > 0) |
| { |
| if (m_xstrf != null) |
| return m_xstrf.newstr(m_chars, offset, length); |
| else |
| return new XMLStringDefault(m_chars.getString(offset, length)); |
| } |
| else |
| return EMPTY_XML_STR; |
| } |
| else |
| return EMPTY_XML_STR; |
| } |
| else if (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| if (dataIndex >= 0) |
| { |
| if (m_xstrf != null) |
| return m_xstrf.newstr(m_chars, dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX); |
| else |
| return new XMLStringDefault(m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX)); |
| } |
| else |
| { |
| if (m_xstrf != null) |
| return m_xstrf.newstr(m_chars, m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1)); |
| else |
| return new XMLStringDefault(m_chars.getString(m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1))); |
| } |
| } |
| else |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| if (m_xstrf != null) |
| return m_xstrf.newstr((String)m_values.elementAt(dataIndex)); |
| else |
| return new XMLStringDefault((String)m_values.elementAt(dataIndex)); |
| } |
| } |
| |
| /** |
| * The optimized version of SAX2DTM.getStringValue(int). |
| * <p> |
| * %OPT% This is one of the most often used interfaces. Performance is |
| * critical here. This one is different from SAX2DTM.getStringValue(int) in |
| * that it returns a String instead of a XMLString. |
| * |
| * 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 final String getStringValueX(final int nodeHandle) |
| { |
| int identity = makeNodeIdentity(nodeHandle); |
| if (identity == DTM.NULL) |
| return EMPTY_STR; |
| |
| int type= _type2(identity); |
| |
| if (type == DTM.ELEMENT_NODE || type == DTM.DOCUMENT_NODE) |
| { |
| int startNode = identity; |
| identity = _firstch2(identity); |
| if (DTM.NULL != identity) |
| { |
| int offset = -1; |
| int length = 0; |
| |
| do |
| { |
| type = _exptype2(identity); |
| |
| if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| if (dataIndex >= 0) |
| { |
| if (-1 == offset) |
| { |
| offset = dataIndex >>> TEXT_LENGTH_BITS; |
| } |
| |
| length += dataIndex & TEXT_LENGTH_MAX; |
| } |
| else |
| { |
| if (-1 == offset) |
| { |
| offset = m_data.elementAt(-dataIndex); |
| } |
| |
| length += m_data.elementAt(-dataIndex + 1); |
| } |
| } |
| |
| identity++; |
| } while (_parent2(identity) >= startNode); |
| |
| if (length > 0) |
| { |
| return m_chars.getString(offset, length); |
| } |
| else |
| return EMPTY_STR; |
| } |
| else |
| return EMPTY_STR; |
| } |
| else if (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| if (dataIndex >= 0) |
| { |
| return m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX); |
| } |
| else |
| { |
| return m_chars.getString(m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1)); |
| } |
| } |
| else |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| return (String)m_values.elementAt(dataIndex); |
| } |
| } |
| |
| /** |
| * Returns the string value of the entire tree |
| */ |
| public String getStringValue() |
| { |
| int child = _firstch2(ROOTNODE); |
| if (child == DTM.NULL) return EMPTY_STR; |
| |
| // optimization: only create StringBuffer if > 1 child |
| if ((_exptype2(child) == DTM.TEXT_NODE) && (_nextsib2(child) == DTM.NULL)) |
| { |
| int dataIndex = m_dataOrQName.elementAt(child); |
| if (dataIndex >= 0) |
| return m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS, dataIndex & TEXT_LENGTH_MAX); |
| else |
| return m_chars.getString(m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex + 1)); |
| } |
| else |
| return getStringValueX(getDocument()); |
| |
| } |
| |
| /** |
| * The optimized version of SAX2DTM.dispatchCharactersEvents(int, ContentHandler, boolean). |
| * <p> |
| * 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. |
| * @param normalize true if the content should be normalized according to |
| * the rules for the XPath |
| * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a> |
| * function. |
| * |
| * @throws SAXException |
| */ |
| public final void dispatchCharactersEvents(int nodeHandle, ContentHandler ch, |
| boolean normalize) |
| throws SAXException |
| { |
| |
| int identity = makeNodeIdentity(nodeHandle); |
| |
| if (identity == DTM.NULL) |
| return; |
| |
| int type = _type2(identity); |
| |
| if (type == DTM.ELEMENT_NODE || type == DTM.DOCUMENT_NODE) |
| { |
| int startNode = identity; |
| identity = _firstch2(identity); |
| if (DTM.NULL != identity) |
| { |
| int offset = -1; |
| int length = 0; |
| |
| do |
| { |
| type = _exptype2(identity); |
| |
| if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| |
| if (dataIndex >= 0) |
| { |
| if (-1 == offset) |
| { |
| offset = dataIndex >>> TEXT_LENGTH_BITS; |
| } |
| |
| length += dataIndex & TEXT_LENGTH_MAX; |
| } |
| else |
| { |
| if (-1 == offset) |
| { |
| offset = m_data.elementAt(-dataIndex); |
| } |
| |
| length += m_data.elementAt(-dataIndex + 1); |
| } |
| } |
| |
| identity++; |
| } while (_parent2(identity) >= startNode); |
| |
| if (length > 0) |
| { |
| if(normalize) |
| m_chars.sendNormalizedSAXcharacters(ch, offset, length); |
| else |
| m_chars.sendSAXcharacters(ch, offset, length); |
| } |
| } |
| } |
| else if (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type) |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| |
| if (dataIndex >= 0) |
| { |
| if (normalize) |
| m_chars.sendNormalizedSAXcharacters(ch, dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX); |
| else |
| m_chars.sendSAXcharacters(ch, dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX); |
| } |
| else |
| { |
| if (normalize) |
| m_chars.sendNormalizedSAXcharacters(ch, m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1)); |
| else |
| m_chars.sendSAXcharacters(ch, m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1)); |
| } |
| } |
| else |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| String str = (String)m_values.elementAt(dataIndex); |
| |
| if(normalize) |
| FastStringBuffer.sendNormalizedSAXcharacters(str.toCharArray(), |
| 0, str.length(), ch); |
| else |
| ch.characters(str.toCharArray(), 0, str.length()); |
| } |
| } |
| |
| /** |
| * Given a node handle, return its node value. This is mostly |
| * as defined by the DOM, but may ignore some conveniences. |
| * <p> |
| * |
| * @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) |
| { |
| |
| int identity = makeNodeIdentity(nodeHandle); |
| int type = _type2(identity); |
| |
| if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE) |
| { |
| int dataIndex = _dataOrQName(identity); |
| if (dataIndex > 0) |
| { |
| return m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX); |
| } |
| else |
| { |
| return m_chars.getString(m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1)); |
| } |
| } |
| else if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_FRAGMENT_NODE == type |
| || DTM.DOCUMENT_NODE == type) |
| { |
| return null; |
| } |
| else |
| { |
| int dataIndex = m_dataOrQName.elementAt(identity); |
| |
| if (dataIndex < 0) |
| { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| return (String)m_values.elementAt(dataIndex); |
| } |
| } |
| |
| /** |
| * Copy the String value of a Text node to a SerializationHandler |
| */ |
| protected final void copyTextNode(final int nodeID, SerializationHandler handler) |
| throws SAXException |
| { |
| if (nodeID != DTM.NULL) { |
| int dataIndex = m_dataOrQName.elementAt(nodeID); |
| if (dataIndex >= 0) { |
| m_chars.sendSAXcharacters(handler, |
| dataIndex >>> TEXT_LENGTH_BITS, |
| dataIndex & TEXT_LENGTH_MAX); |
| } else { |
| m_chars.sendSAXcharacters(handler, m_data.elementAt(-dataIndex), |
| m_data.elementAt(-dataIndex+1)); |
| } |
| } |
| } |
| |
| /** |
| * Copy an Element node to a SerializationHandler. |
| * |
| * @param nodeID The node identity |
| * @param exptype The expanded type of the Element node |
| * @param handler The SerializationHandler |
| * @return The qualified name of the Element node. |
| */ |
| protected final String copyElement(int nodeID, int exptype, |
| SerializationHandler handler) |
| throws SAXException |
| { |
| final ExtendedType extType = m_extendedTypes[exptype]; |
| String uri = extType.getNamespace(); |
| String name = extType.getLocalName(); |
| |
| if (uri.length() == 0) { |
| handler.startElement(name); |
| return name; |
| } |
| else { |
| int qnameIndex = m_dataOrQName.elementAt(nodeID); |
| |
| if (qnameIndex == 0) { |
| handler.startElement(name); |
| handler.namespaceAfterStartElement(EMPTY_STR, uri); |
| return name; |
| } |
| |
| if (qnameIndex < 0) { |
| qnameIndex = -qnameIndex; |
| qnameIndex = m_data.elementAt(qnameIndex); |
| } |
| |
| String qName = m_valuesOrPrefixes.indexToString(qnameIndex); |
| handler.startElement(qName); |
| int prefixIndex = qName.indexOf(':'); |
| String prefix; |
| if (prefixIndex > 0) { |
| prefix = qName.substring(0, prefixIndex); |
| } |
| else { |
| prefix = null; |
| } |
| handler.namespaceAfterStartElement(prefix, uri); |
| return qName; |
| } |
| |
| } |
| |
| /** |
| * Copy namespace nodes. |
| * |
| * @param nodeID The Element node identity |
| * @param handler The SerializationHandler |
| * @param inScope true if all namespaces in scope should be copied, |
| * false if only the namespace declarations should be copied. |
| */ |
| protected final void copyNS(final int nodeID, SerializationHandler handler, boolean inScope) |
| throws SAXException |
| { |
| // %OPT% Optimization for documents which does not have any explicit |
| // namespace nodes. For these documents, there is an implicit |
| // namespace node (xmlns:xml="http://www.w3.org/XML/1998/namespace") |
| // declared on the root element node. In this case, there is no |
| // need to do namespace copying. We can safely return without |
| // doing anything. |
| if (m_namespaceDeclSetElements != null && |
| m_namespaceDeclSetElements.size() == 1 && |
| m_namespaceDeclSets != null && |
| ((SuballocatedIntVector)m_namespaceDeclSets.elementAt(0)) |
| .size() == 1) |
| return; |
| |
| SuballocatedIntVector nsContext = null; |
| int nextNSNode; |
| |
| // Find the first namespace node |
| if (inScope) { |
| nsContext = findNamespaceContext(nodeID); |
| if (nsContext == null || nsContext.size() < 1) |
| return; |
| else |
| nextNSNode = makeNodeIdentity(nsContext.elementAt(0)); |
| } |
| else |
| nextNSNode = getNextNamespaceNode2(nodeID); |
| |
| int nsIndex = 1; |
| while (nextNSNode != DTM.NULL) { |
| // Retrieve the name of the namespace node |
| int eType = _exptype2(nextNSNode); |
| String nodeName = m_extendedTypes[eType].getLocalName(); |
| |
| // Retrieve the node value of the namespace node |
| int dataIndex = m_dataOrQName.elementAt(nextNSNode); |
| |
| if (dataIndex < 0) { |
| dataIndex = -dataIndex; |
| dataIndex = m_data.elementAt(dataIndex + 1); |
| } |
| |
| String nodeValue = (String)m_values.elementAt(dataIndex); |
| |
| handler.namespaceAfterStartElement(nodeName, nodeValue); |
| |
| if (inScope) { |
| if (nsIndex < nsContext.size()) { |
| nextNSNode = makeNodeIdentity(nsContext.elementAt(nsIndex)); |
| nsIndex++; |
| } |
| else |
| return; |
| } |
| else |
| nextNSNode = getNextNamespaceNode2(nextNSNode); |
| } |
| } |
| |
| /** |
| * Return the next namespace node following the given base node. |
| * |
| * @baseID The node identity of the base node, which can be an |
| * element, attribute or namespace node. |
| * @return The namespace node immediately following the base node. |
| */ |
| protected final int getNextNamespaceNode2(int baseID) { |
| int type; |
| while ((type = _type2(++baseID)) == DTM.ATTRIBUTE_NODE); |
| |
| if (type == DTM.NAMESPACE_NODE) |
| return baseID; |
| else |
| return NULL; |
| } |
| |
| /** |
| * Copy attribute nodes from an element . |
| * |
| * @param nodeID The Element node identity |
| * @param handler The SerializationHandler |
| */ |
| protected final void copyAttributes(final int nodeID, SerializationHandler handler) |
| throws SAXException{ |
| |
| for(int current = getFirstAttributeIdentity(nodeID); current != DTM.NULL; current = getNextAttributeIdentity(current)){ |
| int eType = _exptype2(current); |
| copyAttribute(current, eType, handler); |
| } |
| } |
| |
| |
| |
| /** |
| * Copy an Attribute node to a SerializationHandler |
| * |
| * @param nodeID The node identity |
| * @param exptype The expanded type of the Element node |
| * @param handler The SerializationHandler |
| */ |
| protected final void copyAttribute(int nodeID, int exptype, |
| SerializationHandler handler) |
| throws SAXException |
| { |
| /* |
| final String uri = getNamespaceName(node); |
| if (uri.length() != 0) { |
| final String prefix = getPrefix(node); |
| handler.namespaceAfterStartElement(prefix, uri); |
| } |
| handler.addAttribute(getNodeName(node), getNodeValue(node)); |
| */ |
| final ExtendedType extType = m_extendedTypes[exptype]; |
| final String uri = extType.getNamespace(); |
| final String localName = extType.getLocalName(); |
| |
| String prefix = null; |
| String qname = null; |
| int dataIndex = _dataOrQName(nodeID); |
| int valueIndex = dataIndex; |
| if (dataIndex <= 0) { |
| int prefixIndex = m_data.elementAt(-dataIndex); |
| valueIndex = m_data.elementAt(-dataIndex+1); |
| qname = m_valuesOrPrefixes.indexToString(prefixIndex); |
| int colonIndex = qname.indexOf(':'); |
| if (colonIndex > 0) { |
| prefix = qname.substring(0, colonIndex); |
| } |
| } |
| if (uri.length() != 0) { |
| handler.namespaceAfterStartElement(prefix, uri); |
| } |
| |
| String nodeName = (prefix != null) ? qname : localName; |
| String nodeValue = (String)m_values.elementAt(valueIndex); |
| |
| handler.addAttribute(nodeName, nodeValue); |
| } |
| |
| } |