0001: /*
0002: * Copyright 1999-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: /*
0017: * $Id: NodeSetDTM.java,v 1.17 2005/01/23 01:02:10 mcnamara Exp $
0018: */
0019: package org.apache.xpath;
0020:
0021: import org.apache.xalan.res.XSLMessages;
0022: import org.apache.xml.dtm.DTM;
0023: import org.apache.xml.dtm.DTMFilter;
0024: import org.apache.xml.dtm.DTMIterator;
0025: import org.apache.xml.dtm.DTMManager;
0026: import org.apache.xml.utils.NodeVector;
0027: import org.apache.xpath.res.XPATHErrorResources;
0028:
0029: import org.w3c.dom.Node;
0030: import org.w3c.dom.NodeList;
0031: import org.w3c.dom.traversal.NodeIterator;
0032:
0033: /**
0034: * <p>The NodeSetDTM class can act as either a NodeVector,
0035: * NodeList, or NodeIterator. However, in order for it to
0036: * act as a NodeVector or NodeList, it's required that
0037: * setShouldCacheNodes(true) be called before the first
0038: * nextNode() is called, in order that nodes can be added
0039: * as they are fetched. Derived classes that implement iterators
0040: * must override runTo(int index), in order that they may
0041: * run the iteration to the given index. </p>
0042: *
0043: * <p>Note that we directly implement the DOM's NodeIterator
0044: * interface. We do not emulate all the behavior of the
0045: * standard NodeIterator. In particular, we do not guarantee
0046: * to present a "live view" of the document ... but in XSLT,
0047: * the source document should never be mutated, so this should
0048: * never be an issue.</p>
0049: *
0050: * <p>Thought: Should NodeSetDTM really implement NodeList and NodeIterator,
0051: * or should there be specific subclasses of it which do so? The
0052: * advantage of doing it all here is that all NodeSetDTMs will respond
0053: * to the same calls; the disadvantage is that some of them may return
0054: * less-than-enlightening results when you do so.</p>
0055: * @xsl.usage advanced
0056: */
0057: public class NodeSetDTM extends NodeVector implements
0058: /* NodeList, NodeIterator, */DTMIterator, Cloneable {
0059: static final long serialVersionUID = 7686480133331317070L;
0060:
0061: /**
0062: * Create an empty nodelist.
0063: */
0064: public NodeSetDTM(DTMManager dtmManager) {
0065: super ();
0066: m_manager = dtmManager;
0067: }
0068:
0069: /**
0070: * Create an empty, using the given block size.
0071: *
0072: * @param blocksize Size of blocks to allocate
0073: * @param dummy pass zero for right now...
0074: */
0075: public NodeSetDTM(int blocksize, int dummy, DTMManager dtmManager) {
0076: super (blocksize);
0077: m_manager = dtmManager;
0078: }
0079:
0080: // %TBD%
0081: // /**
0082: // * Create a NodeSetDTM, and copy the members of the
0083: // * given nodelist into it.
0084: // *
0085: // * @param nodelist List of Nodes to be made members of the new set.
0086: // */
0087: // public NodeSetDTM(NodeList nodelist)
0088: // {
0089: //
0090: // super();
0091: //
0092: // addNodes(nodelist);
0093: // }
0094:
0095: /**
0096: * Create a NodeSetDTM, and copy the members of the
0097: * given NodeSetDTM into it.
0098: *
0099: * @param nodelist Set of Nodes to be made members of the new set.
0100: */
0101: public NodeSetDTM(NodeSetDTM nodelist) {
0102:
0103: super ();
0104: m_manager = nodelist.getDTMManager();
0105: m_root = nodelist.getRoot();
0106:
0107: addNodes((DTMIterator) nodelist);
0108: }
0109:
0110: /**
0111: * Create a NodeSetDTM, and copy the members of the
0112: * given DTMIterator into it.
0113: *
0114: * @param ni Iterator which yields Nodes to be made members of the new set.
0115: */
0116: public NodeSetDTM(DTMIterator ni) {
0117:
0118: super ();
0119:
0120: m_manager = ni.getDTMManager();
0121: m_root = ni.getRoot();
0122: addNodes(ni);
0123: }
0124:
0125: /**
0126: * Create a NodeSetDTM, and copy the members of the
0127: * given DTMIterator into it.
0128: *
0129: * @param iterator Iterator which yields Nodes to be made members of the new set.
0130: */
0131: public NodeSetDTM(NodeIterator iterator, XPathContext xctxt) {
0132:
0133: super ();
0134:
0135: Node node;
0136: m_manager = xctxt.getDTMManager();
0137:
0138: while (null != (node = iterator.nextNode())) {
0139: int handle = xctxt.getDTMHandleFromNode(node);
0140: addNodeInDocOrder(handle, xctxt);
0141: }
0142: }
0143:
0144: /**
0145: * Create a NodeSetDTM, and copy the members of the
0146: * given DTMIterator into it.
0147: *
0148: */
0149: public NodeSetDTM(NodeList nodeList, XPathContext xctxt) {
0150:
0151: super ();
0152:
0153: m_manager = xctxt.getDTMManager();
0154:
0155: int n = nodeList.getLength();
0156: for (int i = 0; i < n; i++) {
0157: Node node = nodeList.item(i);
0158: int handle = xctxt.getDTMHandleFromNode(node);
0159: // Do not reorder or strip duplicate nodes from the given DOM nodelist
0160: addNode(handle); // addNodeInDocOrder(handle, xctxt);
0161: }
0162: }
0163:
0164: /**
0165: * Create a NodeSetDTM which contains the given Node.
0166: *
0167: * @param node Single node to be added to the new set.
0168: */
0169: public NodeSetDTM(int node, DTMManager dtmManager) {
0170:
0171: super ();
0172: m_manager = dtmManager;
0173:
0174: addNode(node);
0175: }
0176:
0177: /**
0178: * Set the environment in which this iterator operates, which should provide:
0179: * a node (the context node... same value as "root" defined below)
0180: * a pair of non-zero positive integers (the context position and the context size)
0181: * a set of variable bindings
0182: * a function library
0183: * the set of namespace declarations in scope for the expression.
0184: *
0185: * <p>At this time the exact implementation of this environment is application
0186: * dependent. Probably a proper interface will be created fairly soon.</p>
0187: *
0188: * @param environment The environment object.
0189: */
0190: public void setEnvironment(Object environment) {
0191: // no-op
0192: }
0193:
0194: /**
0195: * @return The root node of the Iterator, as specified when it was created.
0196: * For non-Iterator NodeSetDTMs, this will be null.
0197: */
0198: public int getRoot() {
0199: if (DTM.NULL == m_root) {
0200: if (size() > 0)
0201: return item(0);
0202: else
0203: return DTM.NULL;
0204: } else
0205: return m_root;
0206: }
0207:
0208: /**
0209: * Initialize the context values for this expression
0210: * after it is cloned.
0211: *
0212: * @param context The XPath runtime context for this
0213: * transformation.
0214: */
0215: public void setRoot(int context, Object environment) {
0216: // no-op, I guess... (-sb)
0217: }
0218:
0219: /**
0220: * Clone this NodeSetDTM.
0221: * At this time, we only expect this to be used with LocPathIterators;
0222: * it may not work with other kinds of NodeSetDTMs.
0223: *
0224: * @return a new NodeSetDTM of the same type, having the same state...
0225: * though unless overridden in the subclasses, it may not copy all
0226: * the state information.
0227: *
0228: * @throws CloneNotSupportedException if this subclass of NodeSetDTM
0229: * does not support the clone() operation.
0230: */
0231: public Object clone() throws CloneNotSupportedException {
0232:
0233: NodeSetDTM clone = (NodeSetDTM) super .clone();
0234:
0235: return clone;
0236: }
0237:
0238: /**
0239: * Get a cloned Iterator, and reset its state to the beginning of the
0240: * iteration.
0241: *
0242: * @return a new NodeSetDTM of the same type, having the same state...
0243: * except that the reset() operation has been called.
0244: *
0245: * @throws CloneNotSupportedException if this subclass of NodeSetDTM
0246: * does not support the clone() operation.
0247: */
0248: public DTMIterator cloneWithReset()
0249: throws CloneNotSupportedException {
0250:
0251: NodeSetDTM clone = (NodeSetDTM) clone();
0252:
0253: clone.reset();
0254:
0255: return clone;
0256: }
0257:
0258: /**
0259: * Reset the iterator. May have no effect on non-iterator Nodesets.
0260: */
0261: public void reset() {
0262: m_next = 0;
0263: }
0264:
0265: /**
0266: * This attribute determines which node types are presented via the
0267: * iterator. The available set of constants is defined in the
0268: * <code>DTMFilter</code> interface. For NodeSetDTMs, the mask has been
0269: * hardcoded to show all nodes except EntityReference nodes, which have
0270: * no equivalent in the XPath data model.
0271: *
0272: * @return integer used as a bit-array, containing flags defined in
0273: * the DOM's DTMFilter class. The value will be
0274: * <code>SHOW_ALL & ~SHOW_ENTITY_REFERENCE</code>, meaning that
0275: * only entity references are suppressed.
0276: */
0277: public int getWhatToShow() {
0278: return DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE;
0279: }
0280:
0281: /**
0282: * The filter object used to screen nodes. Filters are applied to
0283: * further reduce (and restructure) the DTMIterator's view of the
0284: * document. In our case, we will be using hardcoded filters built
0285: * into our iterators... but getFilter() is part of the DOM's
0286: * DTMIterator interface, so we have to support it.
0287: *
0288: * @return null, which is slightly misleading. True, there is no
0289: * user-written filter object, but in fact we are doing some very
0290: * sophisticated custom filtering. A DOM purist might suggest
0291: * returning a placeholder object just to indicate that this is
0292: * not going to return all nodes selected by whatToShow.
0293: */
0294: public DTMFilter getFilter() {
0295: return null;
0296: }
0297:
0298: /**
0299: * The value of this flag determines whether the children of entity
0300: * reference nodes are visible to the iterator. If false, they will be
0301: * skipped over.
0302: * <br> To produce a view of the document that has entity references
0303: * expanded and does not expose the entity reference node itself, use the
0304: * whatToShow flags to hide the entity reference node and set
0305: * expandEntityReferences to true when creating the iterator. To produce
0306: * a view of the document that has entity reference nodes but no entity
0307: * expansion, use the whatToShow flags to show the entity reference node
0308: * and set expandEntityReferences to false.
0309: *
0310: * @return true for all iterators based on NodeSetDTM, meaning that the
0311: * contents of EntityRefrence nodes may be returned (though whatToShow
0312: * says that the EntityReferences themselves are not shown.)
0313: */
0314: public boolean getExpandEntityReferences() {
0315: return true;
0316: }
0317:
0318: /**
0319: * Get an instance of a DTM that "owns" a node handle. Since a node
0320: * iterator may be passed without a DTMManager, this allows the
0321: * caller to easily get the DTM using just the iterator.
0322: *
0323: * @param nodeHandle the nodeHandle.
0324: *
0325: * @return a non-null DTM reference.
0326: */
0327: public DTM getDTM(int nodeHandle) {
0328:
0329: return m_manager.getDTM(nodeHandle);
0330: }
0331:
0332: /* An instance of the DTMManager. */
0333: DTMManager m_manager;
0334:
0335: /**
0336: * Get an instance of the DTMManager. Since a node
0337: * iterator may be passed without a DTMManager, this allows the
0338: * caller to easily get the DTMManager using just the iterator.
0339: *
0340: * @return a non-null DTMManager reference.
0341: */
0342: public DTMManager getDTMManager() {
0343:
0344: return m_manager;
0345: }
0346:
0347: /**
0348: * Returns the next node in the set and advances the position of the
0349: * iterator in the set. After a DTMIterator is created, the first call
0350: * to nextNode() returns the first node in the set.
0351: * @return The next <code>Node</code> in the set being iterated over, or
0352: * <code>DTM.NULL</code> if there are no more members in that set.
0353: * @throws DOMException
0354: * INVALID_STATE_ERR: Raised if this method is called after the
0355: * <code>detach</code> method was invoked.
0356: */
0357: public int nextNode() {
0358:
0359: if ((m_next) < this .size()) {
0360: int next = this .elementAt(m_next);
0361:
0362: m_next++;
0363:
0364: return next;
0365: } else
0366: return DTM.NULL;
0367: }
0368:
0369: /**
0370: * Returns the previous node in the set and moves the position of the
0371: * iterator backwards in the set.
0372: * @return The previous <code>Node</code> in the set being iterated over,
0373: * or<code>DTM.NULL</code> if there are no more members in that set.
0374: * @throws DOMException
0375: * INVALID_STATE_ERR: Raised if this method is called after the
0376: * <code>detach</code> method was invoked.
0377: * @throws RuntimeException thrown if this NodeSetDTM is not of
0378: * a cached type, and hence doesn't know what the previous node was.
0379: */
0380: public int previousNode() {
0381:
0382: if (!m_cacheNodes)
0383: throw new RuntimeException(XSLMessages.createXPATHMessage(
0384: XPATHErrorResources.ER_NODESETDTM_CANNOT_ITERATE,
0385: null)); //"This NodeSetDTM can not iterate to a previous node!");
0386:
0387: if ((m_next - 1) > 0) {
0388: m_next--;
0389:
0390: return this .elementAt(m_next);
0391: } else
0392: return DTM.NULL;
0393: }
0394:
0395: /**
0396: * Detaches the iterator from the set which it iterated over, releasing
0397: * any computational resources and placing the iterator in the INVALID
0398: * state. After<code>detach</code> has been invoked, calls to
0399: * <code>nextNode</code> or<code>previousNode</code> will raise the
0400: * exception INVALID_STATE_ERR.
0401: * <p>
0402: * This operation is a no-op in NodeSetDTM, and will not cause
0403: * INVALID_STATE_ERR to be raised by later operations.
0404: * </p>
0405: */
0406: public void detach() {
0407: }
0408:
0409: /**
0410: * Specify if it's OK for detach to release the iterator for reuse.
0411: *
0412: * @param allowRelease true if it is OK for detach to release this iterator
0413: * for pooling.
0414: */
0415: public void allowDetachToRelease(boolean allowRelease) {
0416: // no action for right now.
0417: }
0418:
0419: /**
0420: * Tells if this NodeSetDTM is "fresh", in other words, if
0421: * the first nextNode() that is called will return the
0422: * first node in the set.
0423: *
0424: * @return true if nextNode() would return the first node in the set,
0425: * false if it would return a later one.
0426: */
0427: public boolean isFresh() {
0428: return (m_next == 0);
0429: }
0430:
0431: /**
0432: * If an index is requested, NodeSetDTM will call this method
0433: * to run the iterator to the index. By default this sets
0434: * m_next to the index. If the index argument is -1, this
0435: * signals that the iterator should be run to the end.
0436: *
0437: * @param index Position to advance (or retreat) to, with
0438: * 0 requesting the reset ("fresh") position and -1 (or indeed
0439: * any out-of-bounds value) requesting the final position.
0440: * @throws RuntimeException thrown if this NodeSetDTM is not
0441: * one of the types which supports indexing/counting.
0442: */
0443: public void runTo(int index) {
0444:
0445: if (!m_cacheNodes)
0446: throw new RuntimeException(XSLMessages.createXPATHMessage(
0447: XPATHErrorResources.ER_NODESETDTM_CANNOT_INDEX,
0448: null)); //"This NodeSetDTM can not do indexing or counting functions!");
0449:
0450: if ((index >= 0) && (m_next < m_firstFree))
0451: m_next = index;
0452: else
0453: m_next = m_firstFree - 1;
0454: }
0455:
0456: /**
0457: * Returns the <code>index</code>th item in the collection. If
0458: * <code>index</code> is greater than or equal to the number of nodes in
0459: * the list, this returns <code>null</code>.
0460: *
0461: * TODO: What happens if index is out of range?
0462: *
0463: * @param index Index into the collection.
0464: * @return The node at the <code>index</code>th position in the
0465: * <code>NodeList</code>, or <code>null</code> if that is not a valid
0466: * index.
0467: */
0468: public int item(int index) {
0469:
0470: runTo(index);
0471:
0472: return this .elementAt(index);
0473: }
0474:
0475: /**
0476: * The number of nodes in the list. The range of valid child node indices is
0477: * 0 to <code>length-1</code> inclusive. Note that this operation requires
0478: * finding all the matching nodes, which may defeat attempts to defer
0479: * that work.
0480: *
0481: * @return integer indicating how many nodes are represented by this list.
0482: */
0483: public int getLength() {
0484:
0485: runTo(-1);
0486:
0487: return this .size();
0488: }
0489:
0490: /**
0491: * Add a node to the NodeSetDTM. Not all types of NodeSetDTMs support this
0492: * operation
0493: *
0494: * @param n Node to be added
0495: * @throws RuntimeException thrown if this NodeSetDTM is not of
0496: * a mutable type.
0497: */
0498: public void addNode(int n) {
0499:
0500: if (!m_mutable)
0501: throw new RuntimeException(
0502: XSLMessages
0503: .createXPATHMessage(
0504: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0505: null)); //"This NodeSetDTM is not mutable!");
0506:
0507: this .addElement(n);
0508: }
0509:
0510: /**
0511: * Insert a node at a given position.
0512: *
0513: * @param n Node to be added
0514: * @param pos Offset at which the node is to be inserted,
0515: * with 0 being the first position.
0516: * @throws RuntimeException thrown if this NodeSetDTM is not of
0517: * a mutable type.
0518: */
0519: public void insertNode(int n, int pos) {
0520:
0521: if (!m_mutable)
0522: throw new RuntimeException(
0523: XSLMessages
0524: .createXPATHMessage(
0525: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0526: null)); //"This NodeSetDTM is not mutable!");
0527:
0528: insertElementAt(n, pos);
0529: }
0530:
0531: /**
0532: * Remove a node.
0533: *
0534: * @param n Node to be added
0535: * @throws RuntimeException thrown if this NodeSetDTM is not of
0536: * a mutable type.
0537: */
0538: public void removeNode(int n) {
0539:
0540: if (!m_mutable)
0541: throw new RuntimeException(
0542: XSLMessages
0543: .createXPATHMessage(
0544: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0545: null)); //"This NodeSetDTM is not mutable!");
0546:
0547: this .removeElement(n);
0548: }
0549:
0550: // %TBD%
0551: // /**
0552: // * Copy NodeList members into this nodelist, adding in
0553: // * document order. If a node is null, don't add it.
0554: // *
0555: // * @param nodelist List of nodes which should now be referenced by
0556: // * this NodeSetDTM.
0557: // * @throws RuntimeException thrown if this NodeSetDTM is not of
0558: // * a mutable type.
0559: // */
0560: // public void addNodes(NodeList nodelist)
0561: // {
0562: //
0563: // if (!m_mutable)
0564: // throw new RuntimeException("This NodeSetDTM is not mutable!");
0565: //
0566: // if (null != nodelist) // defensive to fix a bug that Sanjiva reported.
0567: // {
0568: // int nChildren = nodelist.getLength();
0569: //
0570: // for (int i = 0; i < nChildren; i++)
0571: // {
0572: // int obj = nodelist.item(i);
0573: //
0574: // if (null != obj)
0575: // {
0576: // addElement(obj);
0577: // }
0578: // }
0579: // }
0580: //
0581: // // checkDups();
0582: // }
0583:
0584: // %TBD%
0585: // /**
0586: // * <p>Copy NodeList members into this nodelist, adding in
0587: // * document order. Only genuine node references will be copied;
0588: // * nulls appearing in the source NodeSetDTM will
0589: // * not be added to this one. </p>
0590: // *
0591: // * <p> In case you're wondering why this function is needed: NodeSetDTM
0592: // * implements both DTMIterator and NodeList. If this method isn't
0593: // * provided, Java can't decide which of those to use when addNodes()
0594: // * is invoked. Providing the more-explicit match avoids that
0595: // * ambiguity.)</p>
0596: // *
0597: // * @param ns NodeSetDTM whose members should be merged into this NodeSetDTM.
0598: // * @throws RuntimeException thrown if this NodeSetDTM is not of
0599: // * a mutable type.
0600: // */
0601: // public void addNodes(NodeSetDTM ns)
0602: // {
0603: //
0604: // if (!m_mutable)
0605: // throw new RuntimeException("This NodeSetDTM is not mutable!");
0606: //
0607: // addNodes((DTMIterator) ns);
0608: // }
0609:
0610: /**
0611: * Copy NodeList members into this nodelist, adding in
0612: * document order. Null references are not added.
0613: *
0614: * @param iterator DTMIterator which yields the nodes to be added.
0615: * @throws RuntimeException thrown if this NodeSetDTM is not of
0616: * a mutable type.
0617: */
0618: public void addNodes(DTMIterator iterator) {
0619:
0620: if (!m_mutable)
0621: throw new RuntimeException(
0622: XSLMessages
0623: .createXPATHMessage(
0624: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0625: null)); //"This NodeSetDTM is not mutable!");
0626:
0627: if (null != iterator) // defensive to fix a bug that Sanjiva reported.
0628: {
0629: int obj;
0630:
0631: while (DTM.NULL != (obj = iterator.nextNode())) {
0632: addElement(obj);
0633: }
0634: }
0635:
0636: // checkDups();
0637: }
0638:
0639: // %TBD%
0640: // /**
0641: // * Copy NodeList members into this nodelist, adding in
0642: // * document order. If a node is null, don't add it.
0643: // *
0644: // * @param nodelist List of nodes to be added
0645: // * @param support The XPath runtime context.
0646: // * @throws RuntimeException thrown if this NodeSetDTM is not of
0647: // * a mutable type.
0648: // */
0649: // public void addNodesInDocOrder(NodeList nodelist, XPathContext support)
0650: // {
0651: //
0652: // if (!m_mutable)
0653: // throw new RuntimeException("This NodeSetDTM is not mutable!");
0654: //
0655: // int nChildren = nodelist.getLength();
0656: //
0657: // for (int i = 0; i < nChildren; i++)
0658: // {
0659: // int node = nodelist.item(i);
0660: //
0661: // if (null != node)
0662: // {
0663: // addNodeInDocOrder(node, support);
0664: // }
0665: // }
0666: // }
0667:
0668: /**
0669: * Copy NodeList members into this nodelist, adding in
0670: * document order. If a node is null, don't add it.
0671: *
0672: * @param iterator DTMIterator which yields the nodes to be added.
0673: * @param support The XPath runtime context.
0674: * @throws RuntimeException thrown if this NodeSetDTM is not of
0675: * a mutable type.
0676: */
0677: public void addNodesInDocOrder(DTMIterator iterator,
0678: XPathContext support) {
0679:
0680: if (!m_mutable)
0681: throw new RuntimeException(
0682: XSLMessages
0683: .createXPATHMessage(
0684: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0685: null)); //"This NodeSetDTM is not mutable!");
0686:
0687: int node;
0688:
0689: while (DTM.NULL != (node = iterator.nextNode())) {
0690: addNodeInDocOrder(node, support);
0691: }
0692: }
0693:
0694: // %TBD%
0695: // /**
0696: // * Add the node list to this node set in document order.
0697: // *
0698: // * @param start index.
0699: // * @param end index.
0700: // * @param testIndex index.
0701: // * @param nodelist The nodelist to add.
0702: // * @param support The XPath runtime context.
0703: // *
0704: // * @return false always.
0705: // * @throws RuntimeException thrown if this NodeSetDTM is not of
0706: // * a mutable type.
0707: // */
0708: // private boolean addNodesInDocOrder(int start, int end, int testIndex,
0709: // NodeList nodelist, XPathContext support)
0710: // {
0711: //
0712: // if (!m_mutable)
0713: // throw new RuntimeException("This NodeSetDTM is not mutable!");
0714: //
0715: // boolean foundit = false;
0716: // int i;
0717: // int node = nodelist.item(testIndex);
0718: //
0719: // for (i = end; i >= start; i--)
0720: // {
0721: // int child = elementAt(i);
0722: //
0723: // if (child == node)
0724: // {
0725: // i = -2; // Duplicate, suppress insert
0726: //
0727: // break;
0728: // }
0729: //
0730: // if (!support.getDOMHelper().isNodeAfter(node, child))
0731: // {
0732: // insertElementAt(node, i + 1);
0733: //
0734: // testIndex--;
0735: //
0736: // if (testIndex > 0)
0737: // {
0738: // boolean foundPrev = addNodesInDocOrder(0, i, testIndex, nodelist,
0739: // support);
0740: //
0741: // if (!foundPrev)
0742: // {
0743: // addNodesInDocOrder(i, size() - 1, testIndex, nodelist, support);
0744: // }
0745: // }
0746: //
0747: // break;
0748: // }
0749: // }
0750: //
0751: // if (i == -1)
0752: // {
0753: // insertElementAt(node, 0);
0754: // }
0755: //
0756: // return foundit;
0757: // }
0758:
0759: /**
0760: * Add the node into a vector of nodes where it should occur in
0761: * document order.
0762: * @param node The node to be added.
0763: * @param test true if we should test for doc order
0764: * @param support The XPath runtime context.
0765: * @return insertIndex.
0766: * @throws RuntimeException thrown if this NodeSetDTM is not of
0767: * a mutable type.
0768: */
0769: public int addNodeInDocOrder(int node, boolean test,
0770: XPathContext support) {
0771:
0772: if (!m_mutable)
0773: throw new RuntimeException(
0774: XSLMessages
0775: .createXPATHMessage(
0776: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0777: null)); //"This NodeSetDTM is not mutable!");
0778:
0779: int insertIndex = -1;
0780:
0781: if (test) {
0782:
0783: // This needs to do a binary search, but a binary search
0784: // is somewhat tough because the sequence test involves
0785: // two nodes.
0786: int size = size(), i;
0787:
0788: for (i = size - 1; i >= 0; i--) {
0789: int child = elementAt(i);
0790:
0791: if (child == node) {
0792: i = -2; // Duplicate, suppress insert
0793:
0794: break;
0795: }
0796:
0797: DTM dtm = support.getDTM(node);
0798: if (!dtm.isNodeAfter(node, child)) {
0799: break;
0800: }
0801: }
0802:
0803: if (i != -2) {
0804: insertIndex = i + 1;
0805:
0806: insertElementAt(node, insertIndex);
0807: }
0808: } else {
0809: insertIndex = this .size();
0810:
0811: boolean foundit = false;
0812:
0813: for (int i = 0; i < insertIndex; i++) {
0814: if (i == node) {
0815: foundit = true;
0816:
0817: break;
0818: }
0819: }
0820:
0821: if (!foundit)
0822: addElement(node);
0823: }
0824:
0825: // checkDups();
0826: return insertIndex;
0827: } // end addNodeInDocOrder(Vector v, Object obj)
0828:
0829: /**
0830: * Add the node into a vector of nodes where it should occur in
0831: * document order.
0832: * @param node The node to be added.
0833: * @param support The XPath runtime context.
0834: *
0835: * @return The index where it was inserted.
0836: * @throws RuntimeException thrown if this NodeSetDTM is not of
0837: * a mutable type.
0838: */
0839: public int addNodeInDocOrder(int node, XPathContext support) {
0840:
0841: if (!m_mutable)
0842: throw new RuntimeException(
0843: XSLMessages
0844: .createXPATHMessage(
0845: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0846: null)); //"This NodeSetDTM is not mutable!");
0847:
0848: return addNodeInDocOrder(node, true, support);
0849: } // end addNodeInDocOrder(Vector v, Object obj)
0850:
0851: /**
0852: * Get the length of the list.
0853: *
0854: * @return The size of this node set.
0855: */
0856: public int size() {
0857: return super .size();
0858: }
0859:
0860: /**
0861: * Append a Node onto the vector.
0862: *
0863: * @param value The node to be added.
0864: * @throws RuntimeException thrown if this NodeSetDTM is not of
0865: * a mutable type.
0866: */
0867: public void addElement(int value) {
0868:
0869: if (!m_mutable)
0870: throw new RuntimeException(
0871: XSLMessages
0872: .createXPATHMessage(
0873: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0874: null)); //"This NodeSetDTM is not mutable!");
0875:
0876: super .addElement(value);
0877: }
0878:
0879: /**
0880: * Inserts the specified node in this vector at the specified index.
0881: * Each component in this vector with an index greater or equal to
0882: * the specified index is shifted upward to have an index one greater
0883: * than the value it had previously.
0884: *
0885: * @param value The node to be inserted.
0886: * @param at The index where the insert should occur.
0887: * @throws RuntimeException thrown if this NodeSetDTM is not of
0888: * a mutable type.
0889: */
0890: public void insertElementAt(int value, int at) {
0891:
0892: if (!m_mutable)
0893: throw new RuntimeException(
0894: XSLMessages
0895: .createXPATHMessage(
0896: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0897: null)); //"This NodeSetDTM is not mutable!");
0898:
0899: super .insertElementAt(value, at);
0900: }
0901:
0902: /**
0903: * Append the nodes to the list.
0904: *
0905: * @param nodes The nodes to be appended to this node set.
0906: * @throws RuntimeException thrown if this NodeSetDTM is not of
0907: * a mutable type.
0908: */
0909: public void appendNodes(NodeVector nodes) {
0910:
0911: if (!m_mutable)
0912: throw new RuntimeException(
0913: XSLMessages
0914: .createXPATHMessage(
0915: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0916: null)); //"This NodeSetDTM is not mutable!");
0917:
0918: super .appendNodes(nodes);
0919: }
0920:
0921: /**
0922: * Inserts the specified node in this vector at the specified index.
0923: * Each component in this vector with an index greater or equal to
0924: * the specified index is shifted upward to have an index one greater
0925: * than the value it had previously.
0926: * @throws RuntimeException thrown if this NodeSetDTM is not of
0927: * a mutable type.
0928: */
0929: public void removeAllElements() {
0930:
0931: if (!m_mutable)
0932: throw new RuntimeException(
0933: XSLMessages
0934: .createXPATHMessage(
0935: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0936: null)); //"This NodeSetDTM is not mutable!");
0937:
0938: super .removeAllElements();
0939: }
0940:
0941: /**
0942: * Removes the first occurrence of the argument from this vector.
0943: * If the object is found in this vector, each component in the vector
0944: * with an index greater or equal to the object's index is shifted
0945: * downward to have an index one smaller than the value it had
0946: * previously.
0947: *
0948: * @param s The node to be removed.
0949: *
0950: * @return True if the node was successfully removed
0951: * @throws RuntimeException thrown if this NodeSetDTM is not of
0952: * a mutable type.
0953: */
0954: public boolean removeElement(int s) {
0955:
0956: if (!m_mutable)
0957: throw new RuntimeException(
0958: XSLMessages
0959: .createXPATHMessage(
0960: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0961: null)); //"This NodeSetDTM is not mutable!");
0962:
0963: return super .removeElement(s);
0964: }
0965:
0966: /**
0967: * Deletes the component at the specified index. Each component in
0968: * this vector with an index greater or equal to the specified
0969: * index is shifted downward to have an index one smaller than
0970: * the value it had previously.
0971: *
0972: * @param i The index of the node to be removed.
0973: * @throws RuntimeException thrown if this NodeSetDTM is not of
0974: * a mutable type.
0975: */
0976: public void removeElementAt(int i) {
0977:
0978: if (!m_mutable)
0979: throw new RuntimeException(
0980: XSLMessages
0981: .createXPATHMessage(
0982: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
0983: null)); //"This NodeSetDTM is not mutable!");
0984:
0985: super .removeElementAt(i);
0986: }
0987:
0988: /**
0989: * Sets the component at the specified index of this vector to be the
0990: * specified object. The previous component at that position is discarded.
0991: *
0992: * The index must be a value greater than or equal to 0 and less
0993: * than the current size of the vector.
0994: *
0995: * @param node The node to be set.
0996: * @param index The index of the node to be replaced.
0997: * @throws RuntimeException thrown if this NodeSetDTM is not of
0998: * a mutable type.
0999: */
1000: public void setElementAt(int node, int index) {
1001:
1002: if (!m_mutable)
1003: throw new RuntimeException(
1004: XSLMessages
1005: .createXPATHMessage(
1006: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
1007: null)); //"This NodeSetDTM is not mutable!");
1008:
1009: super .setElementAt(node, index);
1010: }
1011:
1012: /**
1013: * Same as setElementAt.
1014: *
1015: * @param node The node to be set.
1016: * @param index The index of the node to be replaced.
1017: * @throws RuntimeException thrown if this NodeSetDTM is not of
1018: * a mutable type.
1019: */
1020: public void setItem(int node, int index) {
1021:
1022: if (!m_mutable)
1023: throw new RuntimeException(
1024: XSLMessages
1025: .createXPATHMessage(
1026: XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE,
1027: null)); //"This NodeSetDTM is not mutable!");
1028:
1029: super .setElementAt(node, index);
1030: }
1031:
1032: /**
1033: * Get the nth element.
1034: *
1035: * @param i The index of the requested node.
1036: *
1037: * @return Node at specified index.
1038: */
1039: public int elementAt(int i) {
1040:
1041: runTo(i);
1042:
1043: return super .elementAt(i);
1044: }
1045:
1046: /**
1047: * Tell if the table contains the given node.
1048: *
1049: * @param s Node to look for
1050: *
1051: * @return True if the given node was found.
1052: */
1053: public boolean contains(int s) {
1054:
1055: runTo(-1);
1056:
1057: return super .contains(s);
1058: }
1059:
1060: /**
1061: * Searches for the first occurence of the given argument,
1062: * beginning the search at index, and testing for equality
1063: * using the equals method.
1064: *
1065: * @param elem Node to look for
1066: * @param index Index of where to start the search
1067: * @return the index of the first occurrence of the object
1068: * argument in this vector at position index or later in the
1069: * vector; returns -1 if the object is not found.
1070: */
1071: public int indexOf(int elem, int index) {
1072:
1073: runTo(-1);
1074:
1075: return super .indexOf(elem, index);
1076: }
1077:
1078: /**
1079: * Searches for the first occurence of the given argument,
1080: * beginning the search at index, and testing for equality
1081: * using the equals method.
1082: *
1083: * @param elem Node to look for
1084: * @return the index of the first occurrence of the object
1085: * argument in this vector at position index or later in the
1086: * vector; returns -1 if the object is not found.
1087: */
1088: public int indexOf(int elem) {
1089:
1090: runTo(-1);
1091:
1092: return super .indexOf(elem);
1093: }
1094:
1095: /** If this node is being used as an iterator, the next index that nextNode()
1096: * will return. */
1097: transient protected int m_next = 0;
1098:
1099: /**
1100: * Get the current position, which is one less than
1101: * the next nextNode() call will retrieve. i.e. if
1102: * you call getCurrentPos() and the return is 0, the next
1103: * fetch will take place at index 1.
1104: *
1105: * @return The the current position index.
1106: */
1107: public int getCurrentPos() {
1108: return m_next;
1109: }
1110:
1111: /**
1112: * Set the current position in the node set.
1113: * @param i Must be a valid index.
1114: * @throws RuntimeException thrown if this NodeSetDTM is not of
1115: * a cached type, and thus doesn't permit indexed access.
1116: */
1117: public void setCurrentPos(int i) {
1118:
1119: if (!m_cacheNodes)
1120: throw new RuntimeException(XSLMessages.createXPATHMessage(
1121: XPATHErrorResources.ER_NODESETDTM_CANNOT_INDEX,
1122: null)); //"This NodeSetDTM can not do indexing or counting functions!");
1123:
1124: m_next = i;
1125: }
1126:
1127: /**
1128: * Return the last fetched node. Needed to support the UnionPathIterator.
1129: *
1130: * @return the last fetched node.
1131: * @throws RuntimeException thrown if this NodeSetDTM is not of
1132: * a cached type, and thus doesn't permit indexed access.
1133: */
1134: public int getCurrentNode() {
1135:
1136: if (!m_cacheNodes)
1137: throw new RuntimeException(
1138: "This NodeSetDTM can not do indexing or counting functions!");
1139:
1140: int saved = m_next;
1141: // because nextNode always increments
1142: // But watch out for copy29, where the root iterator didn't
1143: // have nextNode called on it.
1144: int current = (m_next > 0) ? m_next - 1 : m_next;
1145: int n = (current < m_firstFree) ? elementAt(current) : DTM.NULL;
1146: m_next = saved; // HACK: I think this is a bit of a hack. -sb
1147: return n;
1148: }
1149:
1150: /** True if this list can be mutated. */
1151: transient protected boolean m_mutable = true;
1152:
1153: /** True if this list is cached.
1154: * @serial */
1155: transient protected boolean m_cacheNodes = true;
1156:
1157: /** The root of the iteration, if available. */
1158: protected int m_root = DTM.NULL;
1159:
1160: /**
1161: * Get whether or not this is a cached node set.
1162: *
1163: *
1164: * @return True if this list is cached.
1165: */
1166: public boolean getShouldCacheNodes() {
1167: return m_cacheNodes;
1168: }
1169:
1170: /**
1171: * If setShouldCacheNodes(true) is called, then nodes will
1172: * be cached. They are not cached by default. This switch must
1173: * be set before the first call to nextNode is made, to ensure
1174: * that all nodes are cached.
1175: *
1176: * @param b true if this node set should be cached.
1177: * @throws RuntimeException thrown if an attempt is made to
1178: * request caching after we've already begun stepping through the
1179: * nodes in this set.
1180: */
1181: public void setShouldCacheNodes(boolean b) {
1182:
1183: if (!isFresh())
1184: throw new RuntimeException(
1185: XSLMessages
1186: .createXPATHMessage(
1187: XPATHErrorResources.ER_CANNOT_CALL_SETSHOULDCACHENODE,
1188: null)); //"Can not call setShouldCacheNodes after nextNode has been called!");
1189:
1190: m_cacheNodes = b;
1191: m_mutable = true;
1192: }
1193:
1194: /**
1195: * Tells if this iterator can have nodes added to it or set via
1196: * the <code>setItem(int node, int index)</code> method.
1197: *
1198: * @return True if the nodelist can be mutated.
1199: */
1200: public boolean isMutable() {
1201: return m_mutable;
1202: }
1203:
1204: transient private int m_last = 0;
1205:
1206: public int getLast() {
1207: return m_last;
1208: }
1209:
1210: public void setLast(int last) {
1211: m_last = last;
1212: }
1213:
1214: /**
1215: * Returns true if all the nodes in the iteration well be returned in document
1216: * order.
1217: *
1218: * @return true as a default.
1219: */
1220: public boolean isDocOrdered() {
1221: return true;
1222: }
1223:
1224: /**
1225: * Returns the axis being iterated, if it is known.
1226: *
1227: * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
1228: * types.
1229: */
1230: public int getAxis() {
1231: return -1;
1232: }
1233:
1234: }
|