KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xpath > domapi > XPathResultImpl


1 /*
2  * Copyright 2002-2004 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$
18  */

19
20
21 package org.apache.xpath.domapi;
22
23 import javax.xml.transform.TransformerException JavaDoc;
24
25 import org.apache.xalan.res.XSLMessages;
26 import org.apache.xpath.objects.XObject;
27 import org.apache.xpath.res.XPATHErrorResources;
28
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 Draft 28, March 2002.
43  *
44  * <p>See also the <a HREF='http://www.w3.org/TR/2002/WD-DOM-Level-3-XPath-20020328'>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 org.apache.xpath.objects.XObject
55  * @see org.w3c.dom.xpath.XPathResult
56  *
57  * @xsl.usage experimental
58  */

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

64     private XObject m_resultObj;
65     
66     /**
67      * This the type specified by the user during construction. Typically
68      * the constructor will be called by org.apache.xpath.XPath.evaluate().
69      */

70     private short m_resultType = ANY_TYPE;
71     
72     private boolean m_isInvalidIteratorState = false;
73     
74     /**
75      * Only used to attach a mutation event handler when specified
76      * type is an iterator type.
77      */

78     private Node JavaDoc m_contextNode;
79     
80     /**
81      * The iterator, if this is an iterator type.
82      */

83     private NodeIterator m_iterator = null;
84     
85     /**
86      * The list, if this is a snapshot type.
87      */

88     private NodeList JavaDoc m_list = null;
89     
90
91     /**
92      * Constructor for XPathResultImpl.
93      *
94      * For internal use only.
95      */

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

127         // If this is an iterator type get the iterator
128
if ((m_resultType == ORDERED_NODE_ITERATOR_TYPE) ||
129             (m_resultType == UNORDERED_NODE_ITERATOR_TYPE) ||
130             (m_resultType == ANY_UNORDERED_NODE_TYPE) ||
131             (m_resultType == FIRST_ORDERED_NODE_TYPE)) {
132     
133             try {
134                 m_iterator = m_resultObj.nodeset();
135             } catch (TransformerException JavaDoc te) {
136                 // probably not a node type
137
String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_INCOMPATIBLE_TYPES, new Object JavaDoc[] {getTypeString(getTypeFromXObject(m_resultObj)),getTypeString(m_resultType)});
138                 throw new XPathException(XPathException.TYPE_ERR, fmsg); // The returned type: {0} can not be coerced into the specified type: {1}
139
}
140     
141                 // If user requested ordered nodeset and result is unordered
142
// need to sort...TODO
143
// if ((m_resultType == ORDERED_NODE_ITERATOR_TYPE) &&
144
// (!(((DTMNodeIterator)m_iterator).getDTMIterator().isDocOrdered()))) {
145
//
146
// }
147

148         // If it's a snapshot type, get the nodelist
149
} else if ((m_resultType == UNORDERED_NODE_SNAPSHOT_TYPE) ||
150                    (m_resultType == ORDERED_NODE_SNAPSHOT_TYPE)) {
151             try {
152                m_list = m_resultObj.nodelist();
153             } catch (TransformerException JavaDoc te) {
154                 // probably not a node type
155
String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_INCOMPATIBLE_TYPES, new Object JavaDoc[] {getTypeString(getTypeFromXObject(m_resultObj)),getTypeString(m_resultType)});
156                 throw new XPathException(XPathException.TYPE_ERR, fmsg); // The returned type: {0} can not be coerced into the specified type: {1}
157
}
158         }
159     }
160
161     /**
162      * @see org.w3c.dom.xpath.XPathResult#getResultType()
163      */

164     public short getResultType() {
165         return m_resultType;
166     }
167
168     /**
169      * The value of this number result.
170      * @exception XPathException
171      * TYPE_ERR: raised if <code>resultType</code> is not
172      * <code>NUMBER_TYPE</code>.
173      * @see org.w3c.dom.xpath.XPathResult#getNumberValue()
174      */

175     public double getNumberValue() throws XPathException {
176         if (getResultType() != NUMBER_TYPE) {
177             String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER, new Object JavaDoc[] {getTypeString(m_resultType)});
178             throw new XPathException(XPathException.TYPE_ERR,fmsg); // Can not convert {0} to a number
179
} else {
180             try {
181                return m_resultObj.num();
182             } catch (Exception JavaDoc e) {
183                 // Type check above should prevent this exception from occurring.
184
throw new XPathException(XPathException.TYPE_ERR,e.getMessage());
185             }
186         }
187     }
188
189     /**
190      * The value of this string result.
191      * @exception XPathException
192      * TYPE_ERR: raised if <code>resultType</code> is not
193      * <code>STRING_TYPE</code>.
194      *
195      * @see org.w3c.dom.xpath.XPathResult#getStringValue()
196      */

197     public String JavaDoc getStringValue() throws XPathException {
198         if (getResultType() != STRING_TYPE) {
199             String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_STRING, new Object JavaDoc[] {m_resultObj.getTypeString()});
200             throw new XPathException(XPathException.TYPE_ERR,fmsg); // Can not convert {0} to a string.
201
} else {
202             try {
203                return m_resultObj.str();
204             } catch (Exception JavaDoc e) {
205                 // Type check above should prevent this exception from occurring.
206
throw new XPathException(XPathException.TYPE_ERR,e.getMessage());
207             }
208         }
209     }
210
211     /**
212      * @see org.w3c.dom.xpath.XPathResult#getBooleanValue()
213      */

214     public boolean getBooleanValue() throws XPathException {
215         if (getResultType() != BOOLEAN_TYPE) {
216             String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_BOOLEAN, new Object JavaDoc[] {getTypeString(m_resultType)});
217             throw new XPathException(XPathException.TYPE_ERR,fmsg); // Can not convert {0} to a boolean
218
} else {
219             try {
220                return m_resultObj.bool();
221             } catch (TransformerException JavaDoc e) {
222                 // Type check above should prevent this exception from occurring.
223
throw new XPathException(XPathException.TYPE_ERR,e.getMessage());
224             }
225         }
226     }
227
228     /**
229      * The value of this single node result, which may be <code>null</code>.
230      * @exception XPathException
231      * TYPE_ERR: raised if <code>resultType</code> is not
232      * <code>ANY_UNORDERED_NODE_TYPE</code> or
233      * <code>FIRST_ORDERED_NODE_TYPE</code>.
234      *
235      * @see org.w3c.dom.xpath.XPathResult#getSingleNodeValue()
236      */

237     public Node JavaDoc getSingleNodeValue() throws XPathException {
238         
239         if ((m_resultType != ANY_UNORDERED_NODE_TYPE) &&
240             (m_resultType != FIRST_ORDERED_NODE_TYPE)) {
241            String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_CONVERT_TO_SINGLENODE, new Object JavaDoc[] {getTypeString(m_resultType)});
242            throw new XPathException(XPathException.TYPE_ERR,fmsg); // Can not convert {0} to a single node. This getter applies to types
243
// ANY_UNORDERED_NODE_TYPE and FIRST_ORDERED_NODE_TYPE.
244
}
245     
246         NodeIterator result = null;
247         try {
248             result = m_resultObj.nodeset();
249         } catch (TransformerException JavaDoc te) {
250             throw new XPathException(XPathException.TYPE_ERR,te.getMessage());
251         }
252         
253         if (null == result) return null;
254         
255         Node JavaDoc node = result.nextNode();
256          
257         // Wrap "namespace node" in an XPathNamespace
258
if (isNamespaceNode(node)) {
259             return new XPathNamespaceImpl(node);
260         } else {
261             return node;
262         }
263     }
264
265     /**
266      * @see org.w3c.dom.xpath.XPathResult#getInvalidIteratorState()
267      */

268     public boolean getInvalidIteratorState() {
269         return m_isInvalidIteratorState;
270     }
271
272     /**
273      * The number of nodes in the result snapshot. Valid values for
274      * snapshotItem indices are <code>0</code> to
275      * <code>snapshotLength-1</code> inclusive.
276      * @exception XPathException
277      * TYPE_ERR: raised if <code>resultType</code> is not
278      * <code>UNORDERED_NODE_SNAPSHOT_TYPE</code> or
279      * <code>ORDERED_NODE_SNAPSHOT_TYPE</code>.
280      *
281      * @see org.w3c.dom.xpath.XPathResult#getSnapshotLength()
282      */

283     public int getSnapshotLength() throws XPathException {
284     
285         if ((m_resultType != UNORDERED_NODE_SNAPSHOT_TYPE) &&
286             (m_resultType != ORDERED_NODE_SNAPSHOT_TYPE)) {
287            String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANT_GET_SNAPSHOT_LENGTH, new Object JavaDoc[] {getTypeString(m_resultType)});
288            throw new XPathException(XPathException.TYPE_ERR,fmsg); // Can not get snapshot length on type: {0}. This getter applies to types
289
//UNORDERED_NODE_SNAPSHOT_TYPE and ORDERED_NODE_SNAPSHOT_TYPE.
290
}
291             
292         return m_list.getLength();
293     }
294
295     /**
296      * Iterates and returns the next node from the node set or
297      * <code>null</code>if there are no more nodes.
298      * @return Returns the next node.
299      * @exception XPathException
300      * TYPE_ERR: raised if <code>resultType</code> is not
301      * <code>UNORDERED_NODE_ITERATOR_TYPE</code> or
302      * <code>ORDERED_NODE_ITERATOR_TYPE</code>.
303      * @exception DOMException
304      * INVALID_STATE_ERR: The document has been mutated since the result was
305      * returned.
306      * @see org.w3c.dom.xpath.XPathResult#iterateNext()
307      */

308     public Node JavaDoc iterateNext() throws XPathException, DOMException JavaDoc {
309         if ((m_resultType != UNORDERED_NODE_ITERATOR_TYPE) &&
310             (m_resultType != ORDERED_NODE_ITERATOR_TYPE)) {
311           String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NON_ITERATOR_TYPE, new Object JavaDoc[] {getTypeString(m_resultType)});
312           throw new XPathException(XPathException.TYPE_ERR, fmsg); // Can not iterate over non iterator type: {0}
313
}
314
315         if (getInvalidIteratorState()) {
316           String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_DOC_MUTATED, null);
317           throw new DOMException JavaDoc(DOMException.INVALID_STATE_ERR,fmsg); // Document mutated since result was returned. Iterator is invalid.
318
}
319
320         Node JavaDoc node = m_iterator.nextNode();
321         
322         // Wrap "namespace node" in an XPathNamespace
323
if (isNamespaceNode(node)) {
324             return new XPathNamespaceImpl(node);
325         } else {
326             return node;
327         }
328     }
329
330     /**
331      * Returns the <code>index</code>th item in the snapshot collection. If
332      * <code>index</code> is greater than or equal to the number of nodes in
333      * the list, this method returns <code>null</code>. Unlike the iterator
334      * result, the snapshot does not become invalid, but may not correspond
335      * to the current document if it is mutated.
336      * @param index Index into the snapshot collection.
337      * @return The node at the <code>index</code>th position in the
338      * <code>NodeList</code>, or <code>null</code> if that is not a valid
339      * index.
340      * @exception XPathException
341      * TYPE_ERR: raised if <code>resultType</code> is not
342      * <code>UNORDERED_NODE_SNAPSHOT_TYPE</code> or
343      * <code>ORDERED_NODE_SNAPSHOT_TYPE</code>.
344      *
345      * @see org.w3c.dom.xpath.XPathResult#snapshotItem(int)
346      */

347     public Node JavaDoc snapshotItem(int index) throws XPathException {
348         
349         if ((m_resultType != UNORDERED_NODE_SNAPSHOT_TYPE) &&
350             (m_resultType != ORDERED_NODE_SNAPSHOT_TYPE)) {
351            String JavaDoc fmsg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NON_SNAPSHOT_TYPE, new Object JavaDoc[] {getTypeString(m_resultType)});
352            throw new XPathException(XPathException.TYPE_ERR, fmsg); // Can call snapshotItem on type: {0}. This method applies to types
353
// UNORDERED_NODE_SNAPSHOT_TYPE and ORDERED_NODE_SNAPSHOT_TYPE.
354
}
355         
356         Node JavaDoc node = m_list.item(index);
357         
358         // Wrap "namespace node" in an XPathNamespace
359
if (isNamespaceNode(node)) {
360             return new XPathNamespaceImpl(node);
361         } else {
362             return node;
363         }
364     }
365
366     
367     /**
368      * Check if the specified type is one of the supported types.
369      * @param type The specified type
370      *
371      * @return true If the specified type is supported; otherwise, returns false.
372      */

373     public static boolean isValidType( short type ) {
374         switch (type) {
375             case ANY_TYPE:
376             case NUMBER_TYPE:
377             case STRING_TYPE:
378             case BOOLEAN_TYPE:
379             case UNORDERED_NODE_ITERATOR_TYPE:
380             case ORDERED_NODE_ITERATOR_TYPE:
381             case UNORDERED_NODE_SNAPSHOT_TYPE:
382             case ORDERED_NODE_SNAPSHOT_TYPE:
383             case ANY_UNORDERED_NODE_TYPE:
384             case FIRST_ORDERED_NODE_TYPE: return true;
385             default: return false;
386         }
387     }
388
389     /**
390      * @see org.w3c.dom.events.EventListener#handleEvent(Event)
391      */

392     public void handleEvent(Event JavaDoc event) {
393         
394         if (event.getType().equals("MutationEvents")) {
395             // invalidate the iterator
396
m_isInvalidIteratorState = true;
397             
398             // deregister as a listener to reduce computational load
399
((EventTarget JavaDoc)m_contextNode).removeEventListener("MutationEvents",this,true);
400             
401         }
402     }
403     
404   /**
405    * Given a request type, return the equivalent string.
406    * For diagnostic purposes.
407    *
408    * @return type string
409    */

410   public String JavaDoc getTypeString(int type)
411   {
412      switch (type) {
413       case ANY_TYPE: return "ANY_TYPE";
414       case ANY_UNORDERED_NODE_TYPE: return "ANY_UNORDERED_NODE_TYPE";
415       case BOOLEAN_TYPE: return "BOOLEAN";
416       case FIRST_ORDERED_NODE_TYPE: return "FIRST_ORDERED_NODE_TYPE";
417       case NUMBER_TYPE: return "NUMBER_TYPE";
418       case ORDERED_NODE_ITERATOR_TYPE: return "ORDERED_NODE_ITERATOR_TYPE";
419       case ORDERED_NODE_SNAPSHOT_TYPE: return "ORDERED_NODE_SNAPSHOT_TYPE";
420       case STRING_TYPE: return "STRING_TYPE";
421       case UNORDERED_NODE_ITERATOR_TYPE: return "UNORDERED_NODE_ITERATOR_TYPE";
422       case UNORDERED_NODE_SNAPSHOT_TYPE: return "UNORDERED_NODE_SNAPSHOT_TYPE";
423       default: return "#UNKNOWN";
424     }
425   }
426   
427   /**
428    * Given an XObject, determine the corresponding DOM XPath type
429    *
430    * @return type string
431    */

432   private short getTypeFromXObject(XObject object) {
433       switch (object.getType()) {
434         case XObject.CLASS_BOOLEAN: return BOOLEAN_TYPE;
435         case XObject.CLASS_NODESET: return UNORDERED_NODE_ITERATOR_TYPE;
436         case XObject.CLASS_NUMBER: return NUMBER_TYPE;
437         case XObject.CLASS_STRING: return STRING_TYPE;
438         // XPath 2.0 types
439
// case XObject.CLASS_DATE:
440
// case XObject.CLASS_DATETIME:
441
// case XObject.CLASS_DTDURATION:
442
// case XObject.CLASS_GDAY:
443
// case XObject.CLASS_GMONTH:
444
// case XObject.CLASS_GMONTHDAY:
445
// case XObject.CLASS_GYEAR:
446
// case XObject.CLASS_GYEARMONTH:
447
// case XObject.CLASS_TIME:
448
// case XObject.CLASS_YMDURATION: return STRING_TYPE; // treat all date types as strings?
449

450         case XObject.CLASS_RTREEFRAG: return UNORDERED_NODE_ITERATOR_TYPE;
451         case XObject.CLASS_NULL: return ANY_TYPE; // throw exception ?
452
default: return ANY_TYPE; // throw exception ?
453
}
454     
455   }
456
457 /**
458  * Given a node, determine if it is a namespace node.
459  *
460  * @param node
461  *
462  * @return boolean Returns true if this is a namespace node; otherwise, returns false.
463  */

464   private boolean isNamespaceNode(Node JavaDoc node) {
465     
466      if ((null != node) &&
467          (node.getNodeType() == Node.ATTRIBUTE_NODE) &&
468          (node.getNodeName().startsWith("xmlns:") || node.getNodeName().equals("xmlns"))) {
469         return true;
470      } else {
471         return false;
472      }
473   }
474   
475 }
476
Popular Tags