| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, Lotus |
| * Development Corporation., http://www.lotus.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.xpath.objects; |
| |
| import javax.xml.transform.TransformerException; |
| |
| import org.apache.xml.dtm.DTM; |
| import org.apache.xml.dtm.DTMIterator; |
| import org.apache.xml.dtm.XType; |
| import org.apache.xml.dtm.ref.DTMNodeList; |
| import org.apache.xml.utils.DateTimeObj; |
| import org.apache.xml.utils.WrappedRuntimeException; |
| import org.apache.xml.utils.XMLString; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.traversal.NodeIterator; |
| |
| /** |
| * The responsibility of enclosing_type is to . |
| * |
| * Created Jul 20, 2002 |
| * @author sboag |
| */ |
| public class XNodeSequenceSingleton extends XObject |
| implements XSequence |
| { |
| DTM m_dtm; |
| int m_nodeHandle; |
| int m_pos = -1; |
| |
| /** |
| * Constructor for XNodeSequenceSingleton. |
| */ |
| public XNodeSequenceSingleton(int nodeHandle, XNodeSet owningNodeSet) |
| { |
| this(nodeHandle,owningNodeSet.getDTM(nodeHandle)); |
| } |
| |
| /** |
| * Constructor for XNodeSequenceSingleton. |
| */ |
| public XNodeSequenceSingleton(int nodeHandle, DTM dtm) |
| { |
| m_dtm=dtm; |
| m_nodeHandle = nodeHandle; |
| } |
| |
| |
| /** |
| * Tell what kind of class this is. |
| * |
| * @return CLASS_UNKNOWN |
| * @see org.apache.xpath.objects.XObject@getType() |
| */ |
| /* |
| public int getType() |
| { |
| // %REVIEW% May not be the right thing. Makes extensions work, |
| // but currently causes InstanceofExpr to cast it incorrectly |
| // (to XNodeSet, not compatable). |
| // Should we be subclassing XNodeSet rather than XObject? |
| // Should we change this type? |
| // Should we change InstanceOfExpr to cast to XSequence, |
| // which is a shared superclass? |
| return CLASS_NODESET; |
| } |
| */ |
| |
| /** |
| * Given a request type, return the equivalent string. |
| * For diagnostic purposes. |
| * |
| * @return type string "#UNKNOWN" + object class name |
| * @see org.apache.xpath.objects.XObject@getTypeString() |
| */ |
| /* |
| public String getTypeString() |
| { |
| return "#NODESET"; |
| } |
| */ |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getTypes() |
| */ |
| public int getTypes() |
| { |
| DTM dtm = getDTM(); |
| return XType.getTypeID(dtm, m_nodeHandle); |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#next() |
| */ |
| public XObject next() |
| { |
| if(m_pos == -1) |
| { |
| m_pos++; |
| return this; |
| } |
| else |
| return null; |
| /// try |
| // { |
| // if(m_pos == -1) |
| // { |
| // XObject next = (XObject)this.clone(); |
| // next.reset(); |
| // m_pos++; |
| // return next; |
| // } |
| // else |
| // return null; |
| // } |
| // catch (CloneNotSupportedException e) |
| // { |
| // throw new WrappedRuntimeException(e); |
| // } |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#previous() |
| */ |
| public XObject previous() |
| { |
| if(m_pos == 0) |
| { |
| m_pos--; |
| return this; |
| } |
| else |
| return null; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getCurrent() |
| */ |
| public XObject getCurrent() |
| { |
| if(m_pos == 0) |
| { |
| return this; |
| } |
| else |
| return null; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#isFresh() |
| */ |
| public boolean isFresh() |
| { |
| return m_pos == -1; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getTypeNS() |
| */ |
| public String getTypeNS() |
| { |
| DTM dtm = getDTM(); |
| return dtm.getSchemaTypeNamespace(m_nodeHandle); |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getTypeLocalName() |
| */ |
| public String getTypeLocalName() |
| { |
| DTM dtm = getDTM(); |
| return dtm.getSchemaTypeLocalName(m_nodeHandle); |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#isSchemaType(String, String) |
| */ |
| public boolean isSchemaType(String namespace, String localname) |
| { |
| DTM dtm = getDTM(); |
| return dtm.isNodeSchemaType(m_nodeHandle, namespace, localname); |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#isPureNodeSequence() |
| */ |
| public boolean isPureNodeSequence() |
| { |
| return true; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#setShouldCache(boolean) |
| */ |
| public void setShouldCache(boolean b) |
| { |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getIsRandomAccess() |
| */ |
| public boolean getIsRandomAccess() |
| { |
| return true; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#isMutable() |
| */ |
| public boolean isMutable() |
| { |
| return false; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getCurrentPos() |
| */ |
| public int getCurrentPos() |
| { |
| return m_pos; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#setCurrentPos(int) |
| */ |
| public void setCurrentPos(int i) |
| { |
| m_pos = i; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#getLength() |
| */ |
| public int getLength() |
| { |
| return 1; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XSequence#isSingletonOrEmpty() |
| */ |
| public boolean isSingletonOrEmpty() |
| { |
| return true; |
| } |
| |
| /** |
| * @see java.lang.Object#clone() |
| */ |
| public Object clone() throws CloneNotSupportedException |
| { |
| return super.clone(); |
| } |
| |
| /** |
| * 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 = getDTM().getStringValue(n); |
| // String str = xstr.toString(); |
| return xstr.toDouble(); |
| } |
| |
| /** |
| * 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 = m_nodeHandle; |
| return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#floatVal() |
| */ |
| public float floatVal() throws TransformerException |
| { |
| int node = m_nodeHandle; |
| return (node != DTM.NULL) ? (float)getNumberFromNode(node) : Float.NaN; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#integer() |
| */ |
| public int integer() throws TransformerException |
| { |
| int node = m_nodeHandle; |
| return (node != DTM.NULL) ? (int)getNumberFromNode(node) : 0; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#numWithSideEffects() |
| */ |
| public double numWithSideEffects() throws TransformerException |
| { |
| return super.numWithSideEffects(); |
| } |
| |
| |
| /** |
| * Cast result object to a boolean. |
| * |
| * @return True if there is a next node in the nodeset |
| */ |
| public boolean bool() |
| { |
| // Check the new rules about this. -sb |
| return (m_nodeHandle != DTM.NULL); |
| } |
| |
| /** |
| * 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 (m_nodeHandle != DTM.NULL); |
| } |
| |
| |
| /** |
| * 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 getDTM().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. |
| * |
| * @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 = m_nodeHandle; |
| |
| if(node != DTM.NULL) |
| { |
| getDTM().dispatchCharactersEvents(node, ch, false); |
| } |
| |
| } |
| |
| /** |
| * Cast result object to an XMLString. |
| * |
| * @return The document fragment node data or the empty string. |
| */ |
| public XMLString xstr() |
| { |
| int node = m_nodeHandle; |
| return (node != DTM.NULL) ? getStringFromNode(node) : XString.EMPTYSTRING; |
| } |
| |
| /** |
| * 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. |
| * |
| * @return the string conversion from the next node in the nodeset |
| * or "" if there is no next node |
| */ |
| public String str() |
| { |
| int node = m_nodeHandle; |
| return (node != DTM.NULL) ? getStringFromNode(node).toString() : ""; |
| } |
| |
| /** |
| * 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_dtm) // Should this case ever arise??? |
| return this; // And is this response useful? %REVIEW% |
| else |
| //return m_dtm; |
| return m_dtm.getNode(m_nodeHandle); |
| } |
| |
| /** |
| * Returns the dtm. |
| * @return DTM |
| */ |
| public DTM getDTM() |
| { |
| return m_dtm; |
| } |
| |
| /** |
| * Returns the nodeHandle. |
| * @return int |
| */ |
| public int getNodeHandle() |
| { |
| return m_nodeHandle; |
| } |
| |
| /** |
| * Sets the dtm. |
| * @param dtm The dtm to set |
| */ |
| public void setDTM(DTM dtm) |
| { |
| m_dtm = dtm; |
| } |
| |
| /** |
| * Sets the nodeHandle. |
| * @param nodeHandle The nodeHandle to set |
| */ |
| public void setNodeHandle(int nodeHandle) |
| { |
| m_nodeHandle = nodeHandle; |
| } |
| |
| /** |
| * 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, XNodeSet.S_LT); |
| } |
| |
| /** |
| * 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, XNodeSet.S_LTE); |
| } |
| |
| /** |
| * 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, XNodeSet.S_GT); |
| } |
| |
| /** |
| * 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, XNodeSet.S_GTE); |
| } |
| |
| /** |
| * 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 equalsExistential(XObject obj2) |
| { |
| try |
| { |
| return compare(obj2, XNodeSet.S_EQ); |
| } |
| catch(javax.xml.transform.TransformerException te) |
| { |
| throw new org.apache.xml.utils.WrappedRuntimeException(te); |
| } |
| } |
| |
| /** |
| * 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, XNodeSet.S_NEQ); |
| } |
| |
| /** |
| * @see java.lang.Object#equals(Object) |
| */ |
| public boolean equals(XObject arg0) |
| { |
| int nodeHandle = ((XObject) arg0).getNodeHandle(); |
| return nodeHandle == m_nodeHandle; |
| } |
| |
| |
| /** |
| * @see java.lang.Object#equals(Object) |
| */ |
| public boolean equals(Object arg0) |
| { |
| if(arg0 instanceof XObject) |
| { |
| equals((XObject)arg0); |
| } |
| return super.equals(arg0); |
| } |
| |
| /** |
| * 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 (obj2.isNodesetExpr()) |
| { |
| // %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. |
| XSequence list2 = obj2.xseq(); |
| int node1 = m_nodeHandle; |
| java.util.Vector node2Strings = null; |
| |
| XMLString s1 = getStringFromNode(node1); |
| |
| if (null == node2Strings) |
| { |
| |
| XObject next; |
| while (null != (next = list2.next())) |
| { |
| XMLString s2 = next.xstr(); |
| |
| 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; |
| } |
| } |
| } |
| 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. |
| double num2 = obj2.num(); |
| int node = m_nodeHandle; |
| |
| double num1 = getNumberFromNode(node); |
| |
| if (comparator.compareNumbers(num1, num2)) |
| { |
| result = true; |
| } |
| } |
| else |
| if (XObject.CLASS_RTREEFRAG == type) |
| { |
| XMLString s2 = obj2.xstr(); |
| int node = m_nodeHandle; |
| |
| XMLString s1 = getStringFromNode(node); |
| |
| if (comparator.compareStrings(s1, s2)) |
| { |
| result = true; |
| } |
| } |
| 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(); |
| int node = m_nodeHandle; |
| |
| XMLString s1 = getStringFromNode(node); |
| if (comparator.compareStrings(s1, s2)) |
| { |
| result = true; |
| } |
| } |
| else |
| { |
| result = comparator.compareNumbers(this.num(), obj2.num()); |
| } |
| |
| return result; |
| } |
| |
| |
| /** |
| * @see org.apache.xpath.Expression#isNodesetExpr() |
| */ |
| public boolean isNodesetExpr() |
| { |
| return true; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#xseq() |
| */ |
| public XSequence xseq() |
| { |
| try |
| { |
| XSequence newXSeq = (XSequence)clone(); |
| // newXSeq.reset(); |
| return newXSeq; |
| } |
| catch (CloneNotSupportedException e) |
| { |
| throw new WrappedRuntimeException(e); |
| } |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#reset() |
| */ |
| public void reset() |
| { |
| super.reset(); |
| m_pos = -1; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#nodelist() |
| */ |
| public NodeList nodelist() throws TransformerException |
| { |
| return new DTMNodeList(getDTM(),getNodeHandle(), false); |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#nodeset() |
| */ |
| public NodeIterator nodeset() throws TransformerException |
| { |
| DTM dtm = getDTM(); |
| DTMIterator iter = new XNodeSet(getNodeHandle(), dtm.getManager()); |
| return new org.apache.xml.dtm.ref.DTMNodeIterator(iter); |
| } |
| |
| /** |
| * Cast result object to a nodelist. Always issues an error. |
| * |
| * @return null |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| public DTMIterator iter() throws javax.xml.transform.TransformerException |
| { |
| |
| DTM dtm = getDTM(); |
| DTMIterator iter = new XNodeSet(getNodeHandle(), dtm.getManager()); |
| |
| return iter; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#getType() |
| */ |
| public int getType() |
| { |
| return XType.NODE; |
| } |
| |
| /** |
| * @see org.apache.xpath.objects.XObject#getValueType() |
| */ |
| public int getValueType() |
| { |
| int nodeHandle = getNodeHandle(); |
| if (DTM.NULL != nodeHandle) |
| { |
| DTM dtm = getDTM(); |
| |
| return XType.getTypeID(dtm, nodeHandle); |
| } |
| else |
| return XType.ANYTYPE; |
| } |
| |
| // /** |
| // * @see org.apache.xpath.objects.XObject#date() |
| // */ |
| // public DateTimeObj date() throws TransformerException |
| // { |
| // DTM dtm = getDTM(); |
| // dtm.getN |
| // return super.date(); |
| // } |
| |
| } |