/*
 * 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: XNodeSet.java 469368 2006-10-31 04:41:36Z minchau $
 */
package org.apache.xpath.objects;

import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.dtm.DTMManager;
import org.apache.xml.utils.XMLString;
import org.apache.xpath.NodeSetDTM;
import org.apache.xpath.axes.NodeSequence;

import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.NodeIterator;

This class represents an XPath nodeset object, and is capable of converting the nodeset to other types, such as a string.
@xsl.usagegeneral
/** * This class represents an XPath nodeset object, and is capable of * converting the nodeset to other types, such as a string. * @xsl.usage general */
public class XNodeSet extends NodeSequence { static final long serialVersionUID = 1916026368035639667L;
Default constructor for derived objects.
/** * Default constructor for derived objects. */
protected XNodeSet() { }
Construct a XNodeSet object.
Params:
  • val – Value of the XNodeSet object
/** * Construct a XNodeSet object. * * @param val Value of the XNodeSet object */
public XNodeSet(DTMIterator val) { super(); if(val instanceof XNodeSet) { final XNodeSet nodeSet = (XNodeSet) val; setIter(nodeSet.m_iter); m_dtmMgr = nodeSet.m_dtmMgr; m_last = nodeSet.m_last; // First make sure the DTMIterator val has a cache, // so if it doesn't have one, make one. if(!nodeSet.hasCache()) nodeSet.setShouldCacheNodes(true); // Get the cache from val and use it ourselves (we share it). setObject(nodeSet.getIteratorCache()); } else setIter(val); }
Construct a XNodeSet object.
Params:
  • val – Value of the XNodeSet object
/** * Construct a XNodeSet object. * * @param val Value of the XNodeSet object */
public XNodeSet(XNodeSet val) { super(); setIter(val.m_iter); m_dtmMgr = val.m_dtmMgr; m_last = val.m_last; if(!val.hasCache()) val.setShouldCacheNodes(true); setObject(val.m_obj); }
Construct an empty XNodeSet object. This is used to create a mutable nodeset to which random nodes may be added.
/** * Construct an empty XNodeSet object. This is used to create a mutable * nodeset to which random nodes may be added. */
public XNodeSet(DTMManager dtmMgr) { this(DTM.NULL,dtmMgr); }
Construct a XNodeSet object for one node.
Params:
  • n – Node to add to the new XNodeSet object
/** * Construct a XNodeSet object for one node. * * @param n Node to add to the new XNodeSet object */
public XNodeSet(int n, DTMManager dtmMgr) { super(new NodeSetDTM(dtmMgr)); m_dtmMgr = dtmMgr; if (DTM.NULL != n) { ((NodeSetDTM) m_obj).addNode(n); m_last = 1; } else m_last = 0; }
Tell that this is a CLASS_NODESET.
Returns:type CLASS_NODESET
/** * Tell that this is a CLASS_NODESET. * * @return type CLASS_NODESET */
public int getType() { return CLASS_NODESET; }
Given a request type, return the equivalent string. For diagnostic purposes.
Returns:type string "#NODESET"
/** * Given a request type, return the equivalent string. * For diagnostic purposes. * * @return type string "#NODESET" */
public String getTypeString() { return "#NODESET"; }
Get numeric value of the string conversion from a single node.
Params:
  • n – Node to convert
Returns:numeric value of the string conversion from a single node.
/** * Get numeric value of the string conversion from a single node. * * @param n Node to convert * * @return numeric value of the string conversion from a single node. */
public double getNumberFromNode(int n) { XMLString xstr = m_dtmMgr.getDTM(n).getStringValue(n); return xstr.toDouble(); }
Cast result object to a number.
Returns:numeric value of the string conversion from the next node in the NodeSetDTM, or NAN if no node was found
/** * Cast result object to a number. * * @return numeric value of the string conversion from the * next node in the NodeSetDTM, or NAN if no node was found */
public double num() { int node = item(0); return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN; }
Cast result object to a number, but allow side effects, such as the incrementing of an iterator.
Returns:numeric value of the string conversion from the next node in the NodeSetDTM, or NAN if no node was found
/** * Cast result object to a number, but allow side effects, such as the * incrementing of an iterator. * * @return numeric value of the string conversion from the * next node in the NodeSetDTM, or NAN if no node was found */
public double numWithSideEffects() { int node = nextNode(); return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN; }
Cast result object to a boolean.
Returns:True if there is a next node in the nodeset
/** * Cast result object to a boolean. * * @return True if there is a next node in the nodeset */
public boolean bool() { return (item(0) != DTM.NULL); }
Cast result object to a boolean, but allow side effects, such as the incrementing of an iterator.
Returns:True if there is a next node in the nodeset
/** * Cast result object to a boolean, but allow side effects, such as the * incrementing of an iterator. * * @return True if there is a next node in the nodeset */
public boolean boolWithSideEffects() { return (nextNode() != DTM.NULL); }
Get the string conversion from a single node.
Params:
  • n – Node to convert
Returns:the string conversion from a single node.
/** * Get the string conversion from a single node. * * @param n Node to convert * * @return the string conversion from a single node. */
public XMLString getStringFromNode(int n) { // %OPT% // I guess we'll have to get a static instance of the DTM manager... if(DTM.NULL != n) { return m_dtmMgr.getDTM(n).getStringValue(n); } else { return org.apache.xpath.objects.XString.EMPTYSTRING; } }
Directly call the characters method on the passed ContentHandler for the string-value. Multiple calls to the ContentHandler's characters methods may well occur for a single call to this method.
Params:
  • ch – A non-null reference to a ContentHandler.
Throws:
/** * Directly call the * characters method on the passed ContentHandler for the * string-value. Multiple calls to the * ContentHandler's characters methods may well occur for a single call to * this method. * * @param ch A non-null reference to a ContentHandler. * * @throws org.xml.sax.SAXException */
public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch) throws org.xml.sax.SAXException { int node = item(0); if(node != DTM.NULL) { m_dtmMgr.getDTM(node).dispatchCharactersEvents(node, ch, false); } }
Cast result object to an XMLString.
Returns:The document fragment node data or the empty string.
/** * Cast result object to an XMLString. * * @return The document fragment node data or the empty string. */
public XMLString xstr() { int node = item(0); return (node != DTM.NULL) ? getStringFromNode(node) : XString.EMPTYSTRING; }
Cast result object to a string.
Returns:The string this wraps or the empty string if null
/** * Cast result object to a string. * * @return The string this wraps or the empty string if null */
public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb) { XString xstring = (XString)xstr(); xstring.appendToFsb(fsb); }
Cast result object to a string.
Returns:the string conversion from the next node in the nodeset or "" if there is no next node
/** * Cast result object to a string. * * @return the string conversion from the next node in the nodeset * or "" if there is no next node */
public String str() { int node = item(0); return (node != DTM.NULL) ? getStringFromNode(node).toString() : ""; }
Return a java object that's closest to the representation that should be handed to an extension.
Returns:The object that this class wraps
/** * Return a java object that's closest to the representation * that should be handed to an extension. * * @return The object that this class wraps */
public Object object() { if(null == m_obj) return this; else return m_obj; } // %REVIEW% // hmmm... // /** // * Cast result object to a result tree fragment. // * // * @param support The XPath context to use for the conversion // * // * @return the nodeset as a result tree fragment. // */ // public DocumentFragment rtree(XPathContext support) // { // DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // DocumentBuilder db = dbf.newDocumentBuilder(); // Document myDoc = db.newDocument(); // // DocumentFragment docFrag = myDoc.createDocumentFragment(); // // DTMIterator nl = iter(); // int node; // // while (DTM.NULL != (node = nl.nextNode())) // { // frag.appendChild(node, true, true); // } // // return frag.getDocument(); // }
Cast result object to a nodelist.
Throws:
Returns:a NodeIterator.
/** * Cast result object to a nodelist. * * @return a NodeIterator. * * @throws javax.xml.transform.TransformerException */
public NodeIterator nodeset() throws javax.xml.transform.TransformerException { return new org.apache.xml.dtm.ref.DTMNodeIterator(iter()); }
Cast result object to a nodelist.
Throws:
Returns:a NodeList.
/** * Cast result object to a nodelist. * * @return a NodeList. * * @throws javax.xml.transform.TransformerException */
public NodeList nodelist() throws javax.xml.transform.TransformerException { org.apache.xml.dtm.ref.DTMNodeList nodelist = new org.apache.xml.dtm.ref.DTMNodeList(this); // Creating a DTMNodeList has the side-effect that it will create a clone // XNodeSet with cache and run m_iter to the end. You cannot get any node // from m_iter after this call. As a fix, we call SetVector() on the clone's // cache. See Bugzilla 14406. XNodeSet clone = (XNodeSet)nodelist.getDTMIterator(); SetVector(clone.getVector()); return nodelist; } // /** // * Return a java object that's closest to the representation // * that should be handed to an extension. // * // * @return The object that this class wraps // */ // public Object object() // { // return new org.apache.xml.dtm.ref.DTMNodeList(iter()); // }
Return the iterator without cloning, etc.
/** * Return the iterator without cloning, etc. */
public DTMIterator iterRaw() { return this; } public void release(DTMIterator iter) { }
Cast result object to a nodelist.
Returns:The nodeset as a nodelist
/** * Cast result object to a nodelist. * * @return The nodeset as a nodelist */
public DTMIterator iter() { try { if(hasCache()) return cloneWithReset(); else return this; // don't bother to clone... won't do any good! } catch (CloneNotSupportedException cnse) { throw new RuntimeException(cnse.getMessage()); } }
Get a fresh copy of the object. For use with variables.
Returns:A fresh nodelist.
/** * Get a fresh copy of the object. For use with variables. * * @return A fresh nodelist. */
public XObject getFresh() { try { if(hasCache()) return (XObject)cloneWithReset(); else return this; // don't bother to clone... won't do any good! } catch (CloneNotSupportedException cnse) { throw new RuntimeException(cnse.getMessage()); } }
Cast result object to a mutableNodeset.
Returns:The nodeset as a mutableNodeset
/** * Cast result object to a mutableNodeset. * * @return The nodeset as a mutableNodeset */
public NodeSetDTM mutableNodeset() { NodeSetDTM mnl; if(m_obj instanceof NodeSetDTM) { mnl = (NodeSetDTM) m_obj; } else { mnl = new NodeSetDTM(iter()); setObject(mnl); setCurrentPos(0); } return mnl; }
Less than comparator
/** Less than comparator */
static final LessThanComparator S_LT = new LessThanComparator();
Less than or equal comparator
/** Less than or equal comparator */
static final LessThanOrEqualComparator S_LTE = new LessThanOrEqualComparator();
Greater than comparator
/** Greater than comparator */
static final GreaterThanComparator S_GT = new GreaterThanComparator();
Greater than or equal comparator
/** Greater than or equal comparator */
static final GreaterThanOrEqualComparator S_GTE = new GreaterThanOrEqualComparator();
Equal comparator
/** Equal comparator */
static final EqualComparator S_EQ = new EqualComparator();
Not equal comparator
/** Not equal comparator */
static final NotEqualComparator S_NEQ = new NotEqualComparator();
Tell if one object is less than the other.
Params:
  • obj2 – Object to compare this nodeset to
  • comparator – Comparator to use
Throws:
Returns:See the comments below for each object type comparison
/** * Tell if one object is less than the other. * * @param obj2 Object to compare this nodeset to * @param comparator Comparator to use * * @return See the comments below for each object type comparison * * @throws javax.xml.transform.TransformerException */
public boolean compare(XObject obj2, Comparator comparator) throws javax.xml.transform.TransformerException { boolean result = false; int type = obj2.getType(); if (XObject.CLASS_NODESET == type) { // %OPT% This should be XMLString based instead of string based... // From http://www.w3.org/TR/xpath: // If both objects to be compared are node-sets, then the comparison // will be true if and only if there is a node in the first node-set // and a node in the second node-set such that the result of performing // the comparison on the string-values of the two nodes is true. // Note this little gem from the draft: // NOTE: If $x is bound to a node-set, then $x="foo" // does not mean the same as not($x!="foo"): the former // is true if and only if some node in $x has the string-value // foo; the latter is true if and only if all nodes in $x have // the string-value foo. DTMIterator list1 = iterRaw(); DTMIterator list2 = ((XNodeSet) obj2).iterRaw(); int node1; java.util.Vector node2Strings = null; while (DTM.NULL != (node1 = list1.nextNode())) { XMLString s1 = getStringFromNode(node1); if (null == node2Strings) { int node2; while (DTM.NULL != (node2 = list2.nextNode())) { XMLString s2 = getStringFromNode(node2); if (comparator.compareStrings(s1, s2)) { result = true; break; } if (null == node2Strings) node2Strings = new java.util.Vector(); node2Strings.addElement(s2); } } else { int n = node2Strings.size(); for (int i = 0; i < n; i++) { if (comparator.compareStrings(s1, (XMLString)node2Strings.elementAt(i))) { result = true; break; } } } } list1.reset(); list2.reset(); } else if (XObject.CLASS_BOOLEAN == type) { // From http://www.w3.org/TR/xpath: // If one object to be compared is a node-set and the other is a boolean, // then the comparison will be true if and only if the result of // performing the comparison on the boolean and on the result of // converting the node-set to a boolean using the boolean function // is true. double num1 = bool() ? 1.0 : 0.0; double num2 = obj2.num(); result = comparator.compareNumbers(num1, num2); } else if (XObject.CLASS_NUMBER == type) { // From http://www.w3.org/TR/xpath: // If one object to be compared is a node-set and the other is a number, // then the comparison will be true if and only if there is a // node in the node-set such that the result of performing the // comparison on the number to be compared and on the result of // converting the string-value of that node to a number using // the number function is true. DTMIterator list1 = iterRaw(); double num2 = obj2.num(); int node; while (DTM.NULL != (node = list1.nextNode())) { double num1 = getNumberFromNode(node); if (comparator.compareNumbers(num1, num2)) { result = true; break; } } list1.reset(); } else if (XObject.CLASS_RTREEFRAG == type) { XMLString s2 = obj2.xstr(); DTMIterator list1 = iterRaw(); int node; while (DTM.NULL != (node = list1.nextNode())) { XMLString s1 = getStringFromNode(node); if (comparator.compareStrings(s1, s2)) { result = true; break; } } list1.reset(); } else if (XObject.CLASS_STRING == type) { // From http://www.w3.org/TR/xpath: // If one object to be compared is a node-set and the other is a // string, then the comparison will be true if and only if there // is a node in the node-set such that the result of performing // the comparison on the string-value of the node and the other // string is true. XMLString s2 = obj2.xstr(); DTMIterator list1 = iterRaw(); int node; while (DTM.NULL != (node = list1.nextNode())) { XMLString s1 = getStringFromNode(node); if (comparator.compareStrings(s1, s2)) { result = true; break; } } list1.reset(); } else { result = comparator.compareNumbers(this.num(), obj2.num()); } return result; }
Tell if one object is less than the other.
Params:
  • obj2 – object to compare this nodeset to
Throws:
Returns:see this.compare(...)
/** * Tell if one object is less than the other. * * @param obj2 object to compare this nodeset to * * @return see this.compare(...) * * @throws javax.xml.transform.TransformerException */
public boolean lessThan(XObject obj2) throws javax.xml.transform.TransformerException { return compare(obj2, S_LT); }
Tell if one object is less than or equal to the other.
Params:
  • obj2 – object to compare this nodeset to
Throws:
Returns:see this.compare(...)
/** * Tell if one object is less than or equal to the other. * * @param obj2 object to compare this nodeset to * * @return see this.compare(...) * * @throws javax.xml.transform.TransformerException */
public boolean lessThanOrEqual(XObject obj2) throws javax.xml.transform.TransformerException { return compare(obj2, S_LTE); }
Tell if one object is less than the other.
Params:
  • obj2 – object to compare this nodeset to
Throws:
Returns:see this.compare(...)
/** * Tell if one object is less than the other. * * @param obj2 object to compare this nodeset to * * @return see this.compare(...) * * @throws javax.xml.transform.TransformerException */
public boolean greaterThan(XObject obj2) throws javax.xml.transform.TransformerException { return compare(obj2, S_GT); }
Tell if one object is less than the other.
Params:
  • obj2 – object to compare this nodeset to
Throws:
Returns:see this.compare(...)
/** * Tell if one object is less than the other. * * @param obj2 object to compare this nodeset to * * @return see this.compare(...) * * @throws javax.xml.transform.TransformerException */
public boolean greaterThanOrEqual(XObject obj2) throws javax.xml.transform.TransformerException { return compare(obj2, S_GTE); }
Tell if two objects are functionally equal.
Params:
  • obj2 – object to compare this nodeset to
Throws:
Returns:see this.compare(...)
/** * Tell if two objects are functionally equal. * * @param obj2 object to compare this nodeset to * * @return see this.compare(...) * * @throws javax.xml.transform.TransformerException */
public boolean equals(XObject obj2) { try { return compare(obj2, S_EQ); } catch(javax.xml.transform.TransformerException te) { throw new org.apache.xml.utils.WrappedRuntimeException(te); } }
Tell if two objects are functionally not equal.
Params:
  • obj2 – object to compare this nodeset to
Throws:
Returns:see this.compare(...)
/** * Tell if two objects are functionally not equal. * * @param obj2 object to compare this nodeset to * * @return see this.compare(...) * * @throws javax.xml.transform.TransformerException */
public boolean notEquals(XObject obj2) throws javax.xml.transform.TransformerException { return compare(obj2, S_NEQ); } }
compares nodes for various boolean operations.
/** * compares nodes for various boolean operations. */
abstract class Comparator {
Compare two strings
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:Whether the strings are equal or not
/** * Compare two strings * * * @param s1 First string to compare * @param s2 Second String to compare * * @return Whether the strings are equal or not */
abstract boolean compareStrings(XMLString s1, XMLString s2);
Compare two numbers
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:Whether the numbers are equal or not
/** * Compare two numbers * * * @param n1 First number to compare * @param n2 Second number to compare * * @return Whether the numbers are equal or not */
abstract boolean compareNumbers(double n1, double n2); }
Compare strings or numbers for less than.
/** * Compare strings or numbers for less than. */
class LessThanComparator extends Comparator {
Compare two strings for less than.
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:True if s1 is less than s2
/** * Compare two strings for less than. * * * @param s1 First string to compare * @param s2 Second String to compare * * @return True if s1 is less than s2 */
boolean compareStrings(XMLString s1, XMLString s2) { return (s1.toDouble() < s2.toDouble()); // return s1.compareTo(s2) < 0; }
Compare two numbers for less than.
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:true if n1 is less than n2
/** * Compare two numbers for less than. * * * @param n1 First number to compare * @param n2 Second number to compare * * @return true if n1 is less than n2 */
boolean compareNumbers(double n1, double n2) { return n1 < n2; } }
Compare strings or numbers for less than or equal.
/** * Compare strings or numbers for less than or equal. */
class LessThanOrEqualComparator extends Comparator {
Compare two strings for less than or equal.
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:true if s1 is less than or equal to s2
/** * Compare two strings for less than or equal. * * * @param s1 First string to compare * @param s2 Second String to compare * * @return true if s1 is less than or equal to s2 */
boolean compareStrings(XMLString s1, XMLString s2) { return (s1.toDouble() <= s2.toDouble()); // return s1.compareTo(s2) <= 0; }
Compare two numbers for less than or equal.
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:true if n1 is less than or equal to n2
/** * Compare two numbers for less than or equal. * * * @param n1 First number to compare * @param n2 Second number to compare * * @return true if n1 is less than or equal to n2 */
boolean compareNumbers(double n1, double n2) { return n1 <= n2; } }
Compare strings or numbers for greater than.
/** * Compare strings or numbers for greater than. */
class GreaterThanComparator extends Comparator {
Compare two strings for greater than.
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:true if s1 is greater than s2
/** * Compare two strings for greater than. * * * @param s1 First string to compare * @param s2 Second String to compare * * @return true if s1 is greater than s2 */
boolean compareStrings(XMLString s1, XMLString s2) { return (s1.toDouble() > s2.toDouble()); // return s1.compareTo(s2) > 0; }
Compare two numbers for greater than.
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:true if n1 is greater than n2
/** * Compare two numbers for greater than. * * * @param n1 First number to compare * @param n2 Second number to compare * * @return true if n1 is greater than n2 */
boolean compareNumbers(double n1, double n2) { return n1 > n2; } }
Compare strings or numbers for greater than or equal.
/** * Compare strings or numbers for greater than or equal. */
class GreaterThanOrEqualComparator extends Comparator {
Compare two strings for greater than or equal.
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:true if s1 is greater than or equal to s2
/** * Compare two strings for greater than or equal. * * * @param s1 First string to compare * @param s2 Second String to compare * * @return true if s1 is greater than or equal to s2 */
boolean compareStrings(XMLString s1, XMLString s2) { return (s1.toDouble() >= s2.toDouble()); // return s1.compareTo(s2) >= 0; }
Compare two numbers for greater than or equal.
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:true if n1 is greater than or equal to n2
/** * Compare two numbers for greater than or equal. * * * @param n1 First number to compare * @param n2 Second number to compare * * @return true if n1 is greater than or equal to n2 */
boolean compareNumbers(double n1, double n2) { return n1 >= n2; } }
Compare strings or numbers for equality.
/** * Compare strings or numbers for equality. */
class EqualComparator extends Comparator {
Compare two strings for equality.
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:true if s1 is equal to s2
/** * Compare two strings for equality. * * * @param s1 First string to compare * @param s2 Second String to compare * * @return true if s1 is equal to s2 */
boolean compareStrings(XMLString s1, XMLString s2) { return s1.equals(s2); }
Compare two numbers for equality.
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:true if n1 is equal to n2
/** * Compare two numbers for equality. * * * @param n1 First number to compare * @param n2 Second number to compare * * @return true if n1 is equal to n2 */
boolean compareNumbers(double n1, double n2) { return n1 == n2; } }
Compare strings or numbers for non-equality.
/** * Compare strings or numbers for non-equality. */
class NotEqualComparator extends Comparator {
Compare two strings for non-equality.
Params:
  • s1 – First string to compare
  • s2 – Second String to compare
Returns:true if s1 is not equal to s2
/** * Compare two strings for non-equality. * * * @param s1 First string to compare * @param s2 Second String to compare * * @return true if s1 is not equal to s2 */
boolean compareStrings(XMLString s1, XMLString s2) { return !s1.equals(s2); }
Compare two numbers for non-equality.
Params:
  • n1 – First number to compare
  • n2 – Second number to compare
Returns:true if n1 is not equal to n2
/** * Compare two numbers for non-equality. * * * @param n1 First number to compare * @param n2 Second number to compare * * @return true if n1 is not equal to n2 */
boolean compareNumbers(double n1, double n2) { return n1 != n2; } }