KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xpath > internal > domapi > XPathResultImpl


1 /*
2  * Copyright 2002-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: XPathResultImpl.java,v 1.2.4.1 2005/09/10 04:18:54 jeffsuttor Exp $
18  */

19
20
21 package com.sun.org.apache.xpath.internal.domapi;
22
23 import javax.xml.transform.TransformerException JavaDoc;
24
25 import com.sun.org.apache.xpath.internal.XPath;
26 import com.sun.org.apache.xpath.internal.objects.XObject;
27 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
28 import com.sun.org.apache.xpath.internal.res.XPATHMessages;
29 import org.w3c.dom.DOMException JavaDoc;
30 import org.w3c.dom.Node JavaDoc;
31 import org.w3c.dom.NodeList JavaDoc;
32 import org.w3c.dom.events.Event JavaDoc;
33 import org.w3c.dom.events.EventListener JavaDoc;
34 import org.w3c.dom.events.EventTarget JavaDoc;
35 import org.w3c.dom.traversal.NodeIterator;
36 import org.w3c.dom.xpath.XPathException;
37 import org.w3c.dom.xpath.XPathResult;
38
39 /**
40  *
41  * The class provides an implementation XPathResult according
42  * to the DOM L3 XPath Specification, Working Group Note 26 February 2004.
43  *
44  * <p>See also the <a HREF='http://www.w3.org/TR/2004/NOTE-DOM-Level-3-XPath-20040226'>Document Object Model (DOM) Level 3 XPath Specification</a>.</p>
45  *
46  * <p>The <code>XPathResult</code> interface represents the result of the
47  * evaluation of an XPath expression within the context of a particular
48  * node. Since evaluation of an XPath expression can result in various
49  * result types, this object makes it possible to discover and manipulate
50  * the type and value of the result.</p>
51  *
52  * <p>This implementation wraps an <code>XObject</code>.
53  *
54  * @see com.sun.org.apache.xpath.internal.objects.XObject
55  * @see org.w3c.dom.xpath.XPathResult
56  *
57  * @xsl.usage internal
58  */

59 class XPathResultImpl implements XPathResult, EventListener JavaDoc {
60
61     /**
62      * The wrapped XObject
63      */

64     final private XObject m_resultObj;
65     
66     /**
67      * The xpath object that wraps the expression used for this result.
68      */

69     final private XPath m_xpath;
70         
71     /**
72      * This the type specified by the user during construction. Typically
73      * the constructor will be called by com.sun.org.apache.xpath.internal.XPath.evaluate().
74      */

75     final private short m_resultType;
76     
77     private boolean m_isInvalidIteratorState = false;
78     
79     /**
80      * Only used to attach a mutation event handler when specified
81      * type is an iterator type.
82      */

83     final private Node JavaDoc m_contextNode;
84     
85     /**
86      * The iterator, if this is an iterator type.
87      */

88     private NodeIterator m_iterator = null;;
89     
90     /**
91      * The list, if this is a snapshot type.
92      */

93     private NodeList JavaDoc m_list = null;
94     
95
96     /**
97      * Constructor for XPathResultImpl.
98      *
99      * For internal use only.
100      */

101      XPathResultImpl(short type, XObject result, Node JavaDoc contextNode, XPath xpath) {
102         // Check that the type is valid
103
if (!isValidType(type)) {
104             String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_INVALID_XPATH_TYPE, new Object JavaDoc[] {new Integer JavaDoc(type)});
105             throw new XPathException(XPathException.TYPE_ERR,fmsg); // Invalid XPath type argument: {0}
106
}
107
108         // Result object should never be null!
109
if (null == result) {
110             String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_EMPTY_XPATH_RESULT, null);
111             throw new XPathException(XPathException.INVALID_EXPRESSION_ERR,fmsg); // Empty XPath result object
112
}
113         
114         this.m_resultObj = result;
115         this.m_contextNode = contextNode;
116         this.m_xpath = xpath;
117
118         // If specified result was ANY_TYPE, determine XObject type
119
if (type == ANY_TYPE) {
120             this.m_resultType = getTypeFromXObject(result);
121         } else {
122             this.m_resultType = type;
123         }
124         
125         // If the context node supports DOM Events and the type is one of the iterator
126
// types register this result as an event listener
127
if (((m_resultType == XPathResult.ORDERED_NODE_ITERATOR_TYPE) ||
128             (m_resultType == XPathResult.UNORDERED_NODE_ITERATOR_TYPE))) {
129                 addEventListener();
130                 
131         }// else can we handle iterator types if contextNode doesn't support EventTarget??
132

133         // If this is an iterator type get the iterator
134
if ((m_resultType == ORDERED_NODE_ITERATOR_TYPE) ||
135             (m_resultType == UNORDERED_NODE_ITERATOR_TYPE) ||
136             (m_resultType == ANY_UNORDERED_NODE_TYPE) ||
137             (m_resultType == FIRST_ORDERED_NODE_TYPE)) {
138     
139             try {
140                 m_iterator = m_resultObj.nodeset();
141             } catch (TransformerException JavaDoc te) {
142                 // probably not a node type
143
String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_INCOMPATIBLE_TYPES, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(getTypeFromXObject(m_resultObj)),getTypeString(m_resultType)});
144                 throw new XPathException(XPathException.TYPE_ERR, fmsg); // "The XPathResult of XPath expression {0} has an XPathResultType of {1} which cannot be coerced into the specified XPathResultType of {2}."},
145
}
146     
147                 // If user requested ordered nodeset and result is unordered
148
// need to sort...TODO
149
// if ((m_resultType == ORDERED_NODE_ITERATOR_TYPE) &&
150
// (!(((DTMNodeIterator)m_iterator).getDTMIterator().isDocOrdered()))) {
151
//
152
// }
153

154         // If it's a snapshot type, get the nodelist
155
} else if ((m_resultType == UNORDERED_NODE_SNAPSHOT_TYPE) ||
156                    (m_resultType == ORDERED_NODE_SNAPSHOT_TYPE)) {
157             try {
158                m_list = m_resultObj.nodelist();
159             } catch (TransformerException JavaDoc te) {
160                 // probably not a node type
161
String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_INCOMPATIBLE_TYPES, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(getTypeFromXObject(m_resultObj)),getTypeString(m_resultType)});
162                 throw new XPathException(XPathException.TYPE_ERR, fmsg); // "The XPathResult of XPath expression {0} has an XPathResultType of {1} which cannot be coerced into the specified XPathResultType of {2}."},
163
}
164         }
165     }
166
167     /**
168      * @see org.w3c.dom.xpath.XPathResult#getResultType()
169      */

170     public short getResultType() {
171         return m_resultType;
172     }
173
174     /**
175      * The value of this number result.
176      * @exception XPathException
177      * TYPE_ERR: raised if <code>resultType</code> is not
178      * <code>NUMBER_TYPE</code>.
179      * @see org.w3c.dom.xpath.XPathResult#getNumberValue()
180      */

181     public double getNumberValue() throws XPathException {
182         if (getResultType() != NUMBER_TYPE) {
183             String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_XPATHRESULTTYPE_TO_NUMBER, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(m_resultType)});
184             throw new XPathException(XPathException.TYPE_ERR,fmsg);
185 // "The XPathResult of XPath expression {0} has an XPathResultType of {1} which cannot be converted to a number"
186
} else {
187             try {
188                return m_resultObj.num();
189             } catch (Exception JavaDoc e) {
190                 // Type check above should prevent this exception from occurring.
191
throw new XPathException(XPathException.TYPE_ERR,e.getMessage());
192             }
193         }
194     }
195
196     /**
197      * The value of this string result.
198      * @exception XPathException
199      * TYPE_ERR: raised if <code>resultType</code> is not
200      * <code>STRING_TYPE</code>.
201      *
202      * @see org.w3c.dom.xpath.XPathResult#getStringValue()
203      */

204     public String JavaDoc getStringValue() throws XPathException {
205         if (getResultType() != STRING_TYPE) {
206             String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_STRING, new Object JavaDoc[] {m_xpath.getPatternString(), m_resultObj.getTypeString()});
207             throw new XPathException(XPathException.TYPE_ERR,fmsg);
208 // "The XPathResult of XPath expression {0} has an XPathResultType of {1} which cannot be converted to a string."
209
} else {
210             try {
211                return m_resultObj.str();
212             } catch (Exception JavaDoc e) {
213                 // Type check above should prevent this exception from occurring.
214
throw new XPathException(XPathException.TYPE_ERR,e.getMessage());
215             }
216         }
217     }
218
219     /**
220      * @see org.w3c.dom.xpath.XPathResult#getBooleanValue()
221      */

222     public boolean getBooleanValue() throws XPathException {
223         if (getResultType() != BOOLEAN_TYPE) {
224             String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_BOOLEAN, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(m_resultType)});
225             throw new XPathException(XPathException.TYPE_ERR,fmsg);
226 // "The XPathResult of XPath expression {0} has an XPathResultType of {1} which cannot be converted to a boolean."
227
} else {
228             try {
229                return m_resultObj.bool();
230             } catch (TransformerException JavaDoc e) {
231                 // Type check above should prevent this exception from occurring.
232
throw new XPathException(XPathException.TYPE_ERR,e.getMessage());
233             }
234         }
235     }
236
237     /**
238      * The value of this single node result, which may be <code>null</code>.
239      * @exception XPathException
240      * TYPE_ERR: raised if <code>resultType</code> is not
241      * <code>ANY_UNORDERED_NODE_TYPE</code> or
242      * <code>FIRST_ORDERED_NODE_TYPE</code>.
243      *
244      * @see org.w3c.dom.xpath.XPathResult#getSingleNodeValue()
245      */

246     public Node JavaDoc getSingleNodeValue() throws XPathException {
247         
248         if ((m_resultType != ANY_UNORDERED_NODE_TYPE) &&
249             (m_resultType != FIRST_ORDERED_NODE_TYPE)) {
250                 String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_SINGLENODE, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(m_resultType)});
251                 throw new XPathException(XPathException.TYPE_ERR,fmsg);
252 // "The XPathResult of XPath expression {0} has an XPathResultType of {1} which cannot be converted to a single node.
253
// This method applies only to types ANY_UNORDERED_NODE_TYPE and FIRST_ORDERED_NODE_TYPE."
254
}
255     
256         NodeIterator result = null;
257         try {
258             result = m_resultObj.nodeset();
259         } catch (TransformerException JavaDoc te) {
260             throw new XPathException(XPathException.TYPE_ERR,te.getMessage());
261         }
262         
263         if (null == result) return null;
264         
265         Node JavaDoc node = result.nextNode();
266          
267         // Wrap "namespace node" in an XPathNamespace
268
if (isNamespaceNode(node)) {
269             return new XPathNamespaceImpl(node);
270         } else {
271             return node;
272         }
273     }
274
275     /**
276      * @see org.w3c.dom.xpath.XPathResult#getInvalidIteratorState()
277      */

278     public boolean getInvalidIteratorState() {
279         return m_isInvalidIteratorState;
280     }
281
282     /**
283      * The number of nodes in the result snapshot. Valid values for
284      * snapshotItem indices are <code>0</code> to
285      * <code>snapshotLength-1</code> inclusive.
286      * @exception XPathException
287      * TYPE_ERR: raised if <code>resultType</code> is not
288      * <code>UNORDERED_NODE_SNAPSHOT_TYPE</code> or
289      * <code>ORDERED_NODE_SNAPSHOT_TYPE</code>.
290      *
291      * @see org.w3c.dom.xpath.XPathResult#getSnapshotLength()
292      */

293     public int getSnapshotLength() throws XPathException {
294     
295         if ((m_resultType != UNORDERED_NODE_SNAPSHOT_TYPE) &&
296             (m_resultType != ORDERED_NODE_SNAPSHOT_TYPE)) {
297                 String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_GET_SNAPSHOT_LENGTH, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(m_resultType)});
298                 throw new XPathException(XPathException.TYPE_ERR,fmsg);
299 // "The method getSnapshotLength cannot be called on the XPathResult of XPath expression {0} because its XPathResultType is {1}.
300
}
301             
302         return m_list.getLength();
303     }
304
305     /**
306      * Iterates and returns the next node from the node set or
307      * <code>null</code>if there are no more nodes.
308      * @return Returns the next node.
309      * @exception XPathException
310      * TYPE_ERR: raised if <code>resultType</code> is not
311      * <code>UNORDERED_NODE_ITERATOR_TYPE</code> or
312      * <code>ORDERED_NODE_ITERATOR_TYPE</code>.
313      * @exception DOMException
314      * INVALID_STATE_ERR: The document has been mutated since the result was
315      * returned.
316      * @see org.w3c.dom.xpath.XPathResult#iterateNext()
317      */

318     public Node JavaDoc iterateNext() throws XPathException, DOMException JavaDoc {
319         if ((m_resultType != UNORDERED_NODE_ITERATOR_TYPE) &&
320             (m_resultType != ORDERED_NODE_ITERATOR_TYPE)) {
321           String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_NON_ITERATOR_TYPE, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(m_resultType)});
322           throw new XPathException(XPathException.TYPE_ERR, fmsg);
323 // "The method iterateNext cannot be called on the XPathResult of XPath expression {0} because its XPathResultType is {1}.
324
// This method applies only to types UNORDERED_NODE_ITERATOR_TYPE and ORDERED_NODE_ITERATOR_TYPE."},
325
}
326
327         if (getInvalidIteratorState()) {
328           String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_DOC_MUTATED, null);
329           throw new DOMException JavaDoc(DOMException.INVALID_STATE_ERR,fmsg); // Document mutated since result was returned. Iterator is invalid.
330
}
331
332         Node JavaDoc node = m_iterator.nextNode();
333         if(null == node)
334             removeEventListener(); // JIRA 1673
335
// Wrap "namespace node" in an XPathNamespace
336
if (isNamespaceNode(node)) {
337             return new XPathNamespaceImpl(node);
338         } else {
339             return node;
340         }
341     }
342
343     /**
344      * Returns the <code>index</code>th item in the snapshot collection. If
345      * <code>index</code> is greater than or equal to the number of nodes in
346      * the list, this method returns <code>null</code>. Unlike the iterator
347      * result, the snapshot does not become invalid, but may not correspond
348      * to the current document if it is mutated.
349      * @param index Index into the snapshot collection.
350      * @return The node at the <code>index</code>th position in the
351      * <code>NodeList</code>, or <code>null</code> if that is not a valid
352      * index.
353      * @exception XPathException
354      * TYPE_ERR: raised if <code>resultType</code> is not
355      * <code>UNORDERED_NODE_SNAPSHOT_TYPE</code> or
356      * <code>ORDERED_NODE_SNAPSHOT_TYPE</code>.
357      *
358      * @see org.w3c.dom.xpath.XPathResult#snapshotItem(int)
359      */

360     public Node JavaDoc snapshotItem(int index) throws XPathException {
361         
362         if ((m_resultType != UNORDERED_NODE_SNAPSHOT_TYPE) &&
363             (m_resultType != ORDERED_NODE_SNAPSHOT_TYPE)) {
364            String JavaDoc fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_NON_SNAPSHOT_TYPE, new Object JavaDoc[] {m_xpath.getPatternString(), getTypeString(m_resultType)});
365            throw new XPathException(XPathException.TYPE_ERR, fmsg);
366 // "The method snapshotItem cannot be called on the XPathResult of XPath expression {0} because its XPathResultType is {1}.
367
// This method applies only to types UNORDERED_NODE_SNAPSHOT_TYPE and ORDERED_NODE_SNAPSHOT_TYPE."},
368
}
369         
370         Node JavaDoc node = m_list.item(index);
371         
372         // Wrap "namespace node" in an XPathNamespace
373
if (isNamespaceNode(node)) {
374             return new XPathNamespaceImpl(node);
375         } else {
376             return node;
377         }
378     }
379
380     
381     /**
382      * Check if the specified type is one of the supported types.
383      * @param type The specified type
384      *
385      * @return true If the specified type is supported; otherwise, returns false.
386      */

387     static boolean isValidType( short type ) {
388         switch (type) {
389             case ANY_TYPE:
390             case NUMBER_TYPE:
391             case STRING_TYPE:
392             case BOOLEAN_TYPE:
393             case UNORDERED_NODE_ITERATOR_TYPE:
394             case ORDERED_NODE_ITERATOR_TYPE:
395             case UNORDERED_NODE_SNAPSHOT_TYPE:
396             case ORDERED_NODE_SNAPSHOT_TYPE:
397             case ANY_UNORDERED_NODE_TYPE:
398             case FIRST_ORDERED_NODE_TYPE: return true;
399             default: return false;
400         }
401     }
402
403     /**
404      * @see org.w3c.dom.events.EventListener#handleEvent(Event)
405      */

406     public void handleEvent(Event JavaDoc event) {
407         
408         if (event.getType().equals("DOMSubtreeModified")) {
409             // invalidate the iterator
410
m_isInvalidIteratorState = true;
411             
412             // deregister as a listener to reduce computational load
413
removeEventListener();
414         }
415     }
416     
417   /**
418    * Given a request type, return the equivalent string.
419    * For diagnostic purposes.
420    *
421    * @return type string
422    */

423   private String JavaDoc getTypeString(int type)
424   {
425      switch (type) {
426       case ANY_TYPE: return "ANY_TYPE";
427       case ANY_UNORDERED_NODE_TYPE: return "ANY_UNORDERED_NODE_TYPE";
428       case BOOLEAN_TYPE: return "BOOLEAN";
429       case FIRST_ORDERED_NODE_TYPE: return "FIRST_ORDERED_NODE_TYPE";
430       case NUMBER_TYPE: return "NUMBER_TYPE";
431       case ORDERED_NODE_ITERATOR_TYPE: return "ORDERED_NODE_ITERATOR_TYPE";
432       case ORDERED_NODE_SNAPSHOT_TYPE: return "ORDERED_NODE_SNAPSHOT_TYPE";
433       case STRING_TYPE: return "STRING_TYPE";
434       case UNORDERED_NODE_ITERATOR_TYPE: return "UNORDERED_NODE_ITERATOR_TYPE";
435       case UNORDERED_NODE_SNAPSHOT_TYPE: return "UNORDERED_NODE_SNAPSHOT_TYPE";
436       default: return "#UNKNOWN";
437     }
438   }
439   
440   /**
441    * Given an XObject, determine the corresponding DOM XPath type
442    *
443    * @return type string
444    */

445   private short getTypeFromXObject(XObject object) {
446       switch (object.getType()) {
447         case XObject.CLASS_BOOLEAN: return BOOLEAN_TYPE;
448         case XObject.CLASS_NODESET: return UNORDERED_NODE_ITERATOR_TYPE;
449         case XObject.CLASS_NUMBER: return NUMBER_TYPE;
450         case XObject.CLASS_STRING: return STRING_TYPE;
451         // XPath 2.0 types
452
// case XObject.CLASS_DATE:
453
// case XObject.CLASS_DATETIME:
454
// case XObject.CLASS_DTDURATION:
455
// case XObject.CLASS_GDAY:
456
// case XObject.CLASS_GMONTH:
457
// case XObject.CLASS_GMONTHDAY:
458
// case XObject.CLASS_GYEAR:
459
// case XObject.CLASS_GYEARMONTH:
460
// case XObject.CLASS_TIME:
461
// case XObject.CLASS_YMDURATION: return STRING_TYPE; // treat all date types as strings?
462

463         case XObject.CLASS_RTREEFRAG: return UNORDERED_NODE_ITERATOR_TYPE;
464         case XObject.CLASS_NULL: return ANY_TYPE; // throw exception ?
465
default: return ANY_TYPE; // throw exception ?
466
}
467     
468   }
469
470 /**
471  * Given a node, determine if it is a namespace node.
472  *
473  * @param node
474  *
475  * @return boolean Returns true if this is a namespace node; otherwise, returns false.
476  */

477   private boolean isNamespaceNode(Node JavaDoc node) {
478     
479      if ((null != node) &&
480          (node.getNodeType() == Node.ATTRIBUTE_NODE) &&
481          (node.getNodeName().startsWith("xmlns:") || node.getNodeName().equals("xmlns"))) {
482         return true;
483      } else {
484         return false;
485      }
486   }
487   
488 /**
489  * Add m_contextNode to Event Listner to listen for Mutations Events
490  *
491  */

492   private void addEventListener(){
493     if(m_contextNode instanceof EventTarget JavaDoc)
494         ((EventTarget JavaDoc)m_contextNode).addEventListener("DOMSubtreeModified",this,true);
495     
496   }
497   
498
499 /**
500  * Remove m_contextNode to Event Listner to listen for Mutations Events
501  *
502  */

503 private void removeEventListener(){
504     if(m_contextNode instanceof EventTarget JavaDoc)
505         ((EventTarget JavaDoc)m_contextNode).removeEventListener("DOMSubtreeModified",this,true);
506 }
507   
508 }
509
Popular Tags