KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > expr > NodeSetValue


1 package com.icl.saxon.expr;
2 import com.icl.saxon.*;
3 import com.icl.saxon.om.*;
4 import com.icl.saxon.sort.LocalOrderComparer;
5
6 import java.util.*;
7 import org.w3c.dom.*;
8
9 /**
10 * A node-set value. We use this both for node-sets and node-lists. The node set will only
11 * be sorted into document order when requested (using sort() or evaluate()). This is an abstract
12 * class with a number of concrete implementations including NodeSetExtent (for extensional node-sets)
13 * and NodeSetIntent (for intensional node-sets).
14 */

15
16 public abstract class NodeSetValue extends Value {
17
18     private Hashtable stringValues = null; // used for testing equality
19

20     /**
21     * Determine the data type of the expression
22     * @return Value.NODESET
23     */

24
25     public int getDataType() {
26         return Value.NODESET;
27     }
28
29     /**
30     * Evaluate the Node Set. This guarantees to return the result in sorted order.
31     * @param context The context for evaluation (not used)
32     */

33
34     public Value evaluate(Context context) throws XPathException {
35         sort();
36         return this;
37     }
38     
39     /**
40     * Evaluate an expression as a NodeSet.
41     * @param context The context in which the expression is to be evaluated
42     * @return the value of the expression, evaluated in the current context
43     */

44
45     public NodeSetValue evaluateAsNodeSet(Context context) throws XPathException {
46         sort();
47         return this;
48     }
49
50     /**
51     * Return an enumeration of this nodeset value. Unless sort() has been called
52     * the nodes can be in any order.
53     */

54
55     public abstract NodeEnumeration enumerate() throws XPathException;
56
57     /**
58     * Return an enumeration of this nodeset value. This is to satisfy the interface for
59     * Expression.
60     * @param context The context is ignored.
61     * @param sorted Indicates that the result must be in document order
62     */

63
64     public NodeEnumeration enumerate(Context c, boolean sorted) throws XPathException {
65         if (sorted) sort();
66         return enumerate();
67     }
68
69     /**
70     * Set a flag to indicate whether the nodes are sorted. Used when the creator of the
71     * node-set knows that they are already in document order.
72     * @param isSorted true if the caller wishes to assert that the nodes are in document order
73     * and do not need to be further sorted
74     */

75
76     public abstract void setSorted(boolean isSorted);
77     
78     /**
79     * Test whether the value is known to be sorted
80     * @return true if the value is known to be sorted in document order, false if it is not
81     * known whether it is sorted.
82     */

83
84     public abstract boolean isSorted() throws XPathException;
85     
86     /**
87     * Convert to string value
88     * @return the value of the first node in the node-set if there
89     * is one, otherwise an empty string
90     */

91
92     public abstract String JavaDoc asString() throws XPathException;
93
94     /**
95     * Evaluate as a number.
96     * @return the number obtained by evaluating as a String and converting the string to a number
97     */

98
99     public double asNumber() throws XPathException {
100         return (new StringValue(asString())).asNumber();
101     }
102
103     /**
104     * Evaluate as a boolean.
105     * @return true if the node set is not empty
106     */

107
108     public abstract boolean asBoolean() throws XPathException;
109
110     /**
111     * Count the nodes in the node-set. Note this will sort the node set if necessary, to
112     * make sure there are no duplicates.
113     */

114
115     public abstract int getCount() throws XPathException;
116   
117     /**
118     * Sort the nodes into document order.
119     * This does nothing if the nodes are already known to be sorted; to force a sort,
120     * call setSorted(false)
121     * @param controller The controller used to sort nodes into document order
122     * @return the same NodeSetValue, after sorting. (The reason for returning this is that
123     * it makes life easier for the XSL compiler).
124     */

125
126     public abstract NodeSetValue sort() throws XPathException;
127     
128     /**
129     * Get the first node in the nodeset (in document order)
130     * @return the first node
131     */

132
133     public abstract NodeInfo getFirst() throws XPathException;
134
135     /**
136     * Get a hashtable containing all the string values of nodes in this node-set
137     */

138     
139     private Hashtable getStringValues() throws XPathException {
140         if (stringValues==null) {
141             stringValues = new Hashtable();
142             NodeEnumeration e1 = this.enumerate();
143             while (e1.hasMoreElements()) {
144                 stringValues.put(e1.nextElement().getStringValue(), "x");
145             }
146         }
147         return stringValues;
148     }
149
150     /**
151     * Test whether a nodeset "equals" another Value
152     */

153
154     public boolean equals(Value other) throws XPathException {
155                 
156         if (other instanceof ObjectValue) {
157             return false;
158
159         } else if (other instanceof SingletonNodeSet) {
160             if (other.asBoolean()) {
161                 return equals(new StringValue(other.asString()));
162             } else {
163                 return false;
164             }
165             
166         } else if (other instanceof NodeSetValue) {
167
168             // see if there is a node in A with the same string value as a node in B
169

170             Hashtable table = getStringValues();
171
172             NodeEnumeration e2 = ((NodeSetValue)other).enumerate();
173             while (e2.hasMoreElements()) {
174                 if (table.get(e2.nextElement().getStringValue())!=null) return true;
175             }
176             return false;
177
178         } else if (other instanceof NumericValue) {
179             NodeEnumeration e1 = this.enumerate();
180             while (e1.hasMoreElements()) {
181                 NodeInfo node = e1.nextElement();
182                 if (Value.stringToNumber(node.getStringValue())==other.asNumber()) return true;
183             }
184             return false;
185             
186         } else if (other instanceof StringValue) {
187             if (stringValues==null) {
188                 NodeEnumeration e1 = this.enumerate();
189                 while (e1.hasMoreElements()) {
190                     NodeInfo node = e1.nextElement();
191                     if (node.getStringValue().equals(other.asString())) return true;
192                 }
193                 return false;
194             } else {
195                 return stringValues.get(other.asString()) != null;
196             }
197                 
198         } else if (other instanceof BooleanValue) {
199                             // fix bug 4.5/010
200
return (asBoolean()==other.asBoolean());
201                 
202         } else {
203             throw new InternalSaxonError("Unknown data type in a relational expression");
204             // TODO: handle External Object data type
205
}
206
207     }
208
209     /**
210     * Test whether a nodeset "not-equals" another Value
211     */

212
213     public boolean notEquals(Value other) throws XPathException {
214         
215         if (other instanceof ObjectValue) {
216             return false;
217
218         } else if (other instanceof SingletonNodeSet) {
219             if (other.asBoolean()) {
220                 return notEquals(new StringValue(other.asString()));
221             } else {
222                 return false;
223             }
224             
225         } else if (other instanceof NodeSetValue) {
226
227             // see if there is a node in A with a different string value as a node in B
228
// use a nested loop: it will usually finish very quickly!
229

230             NodeEnumeration e1 = this.enumerate();
231             while (e1.hasMoreElements()) {
232                 String JavaDoc s1 = e1.nextElement().getStringValue();
233                 NodeEnumeration e2 = ((NodeSetValue)other).enumerate();
234                 while (e2.hasMoreElements()) {
235                     String JavaDoc s2 = e2.nextElement().getStringValue();
236                     if (!s1.equals(s2)) return true;
237                 }
238             }
239             return false;
240
241         } else if (other instanceof NumericValue) {
242             NodeEnumeration e1 = this.enumerate();
243             while (e1.hasMoreElements()) {
244                 NodeInfo node = e1.nextElement();
245                 if (Value.stringToNumber(node.getStringValue())!=other.asNumber()) return true;
246             }
247             return false;
248             
249         } else if (other instanceof StringValue) {
250             NodeEnumeration e1 = this.enumerate();
251             while (e1.hasMoreElements()) {
252                 NodeInfo node = e1.nextElement();
253                 if (!(node.getStringValue().equals(other.asString()))) return true;
254             }
255             return false;
256             
257         } else if (other instanceof BooleanValue) {
258             // bug 4.5/010
259
return (asBoolean()!=other.asBoolean());
260
261         } else {
262             throw new InternalSaxonError("Unknown data type in a relational expression");
263         }
264       
265     }
266
267     /**
268     * Test how a nodeset compares to another Value under a relational comparison
269     * @param operator The comparison operator, one of Tokenizer.LE, Tokenizer.LT,
270     * Tokenizer.GE, Tokenizer.GT,
271     */

272
273     public boolean compare(int operator, Value other) throws XPathException {
274         if (other instanceof ObjectValue) {
275             return false;
276
277         }
278         if (other instanceof SingletonNodeSet) {
279             if (other.asBoolean()) {
280                 other = new StringValue(other.asString());
281             } else {
282                 return false;
283             }
284         }
285
286         if (operator==Tokenizer.EQUALS) return equals(other);
287         if (operator==Tokenizer.NE) return notEquals(other);
288         
289         if (other instanceof NodeSetValue) {
290
291             // find the min and max values in this nodeset
292

293             double thismax = Double.NEGATIVE_INFINITY;
294             double thismin = Double.POSITIVE_INFINITY;
295             boolean thisIsEmpty = true;
296
297             NodeEnumeration e1 = enumerate();
298             while (e1.hasMoreElements()) {
299                 double val = Value.stringToNumber(e1.nextElement().getStringValue());
300                 if (val<thismin) thismin = val;
301                 if (val>thismax) thismax = val;
302                 thisIsEmpty = false;
303             }
304         
305             if (thisIsEmpty) return false;
306                         
307             // find the minimum and maximum values in the other nodeset
308

309             double othermax = Double.NEGATIVE_INFINITY;
310             double othermin = Double.POSITIVE_INFINITY;
311             boolean otherIsEmpty = true;
312             
313             NodeEnumeration e2 = ((NodeSetValue)other).enumerate();
314             while (e2.hasMoreElements()) {
315                 double val = Value.stringToNumber(e2.nextElement().getStringValue());
316                 if (val<othermin) othermin = val;
317                 if (val>othermax) othermax = val;
318                 otherIsEmpty = false;
319             }
320
321             if (otherIsEmpty) return false;
322
323             switch(operator) {
324                 case Tokenizer.LT:
325                     return thismin < othermax;
326                 case Tokenizer.LE:
327                     return thismin <= othermax;
328                 case Tokenizer.GT:
329                     return thismax > othermin;
330                 case Tokenizer.GE:
331                     return thismax >= othermin;
332                 default:
333                     return false;
334             }
335
336         } else {
337             if (other instanceof NumericValue || other instanceof StringValue) {
338                 NodeEnumeration e1 = enumerate();
339                 while (e1.hasMoreElements()) {
340                     NodeInfo node = e1.nextElement();
341                     if (numericCompare(operator,
342                                  Value.stringToNumber(node.getStringValue()),
343                                  other.asNumber()))
344                         return true;
345                 }
346                 return false;
347             } else if (other instanceof BooleanValue) {
348                 return numericCompare(operator,
349                                     new BooleanValue(this.asBoolean()).asNumber(),
350                                     new BooleanValue(other.asBoolean()).asNumber());
351             } else {
352                 throw new InternalSaxonError("Unknown data type in a relational expression");
353             }
354         }
355      
356     }
357
358     /**
359     * Diagnostic print of expression structure
360     */

361     
362     public void display(int level) {
363         System.err.println(indent(level) + "** node set value (class " + getClass() + ") **");
364     }
365
366     /**
367     * Get conversion preference for this value to a Java class. A low result
368     * indicates higher preference.
369     */

370        
371     public int conversionPreference(Class JavaDoc required) {
372
373         if (required.isAssignableFrom(NodeSetValue.class)) return 0;
374
375         if (required==NodeList.class) return 0;
376         if (required==boolean.class) return 8;
377         if (required==Boolean JavaDoc.class) return 9;
378         if (required==byte.class) return 6;
379         if (required==Byte JavaDoc.class) return 7;
380         if (required==char.class) return 4;
381         if (required==Character JavaDoc.class) return 5;
382         if (required==double.class) return 6;
383         if (required==Double JavaDoc.class) return 7;
384         if (required==float.class) return 6;
385         if (required==Float JavaDoc.class) return 7;
386         if (required==int.class) return 6;
387         if (required==Integer JavaDoc.class) return 7;
388         if (required==long.class) return 6;
389         if (required==Long JavaDoc.class) return 7;
390         if (required==short.class) return 6;
391         if (required==Short JavaDoc.class) return 7;
392         if (required==String JavaDoc.class) return 2;
393         if (required==Object JavaDoc.class) return 3;
394         if (required==Node.class) return 1;
395         if (required==Element.class) return 1;
396         if (required==Document.class) return 1;
397         if (required==DocumentFragment.class) return 1;
398         if (required==Attr.class) return 1;
399         if (required==Comment.class) return 1;
400         if (required==Text.class) return 1;
401         if (required==CharacterData.class) return 1;
402         if (required==ProcessingInstruction.class) return 1;
403         return Integer.MAX_VALUE;
404     }
405
406     /**
407     * Convert to Java object (for passing to external functions)
408     */

409     
410     public Object JavaDoc convertToJava(Class JavaDoc target) throws XPathException {
411
412         if (target.isAssignableFrom(getClass())) {
413             return this;
414         } else if (target==NodeEnumeration.class) {
415             return this.enumerate();
416         
417         } else if (target==boolean.class) {
418             return new Boolean JavaDoc(asBoolean());
419         } else if (target==Boolean JavaDoc.class) {
420             return new Boolean JavaDoc(asBoolean());
421             
422         } else if (target==Object JavaDoc.class || target==NodeList.class) {
423             if (this instanceof NodeList) {
424                 return this;
425             } else {
426                 // TODO: really need a full DocumentOrderComparer here...
427
return new NodeSetExtent(this.enumerate(), new LocalOrderComparer());
428             }
429             
430         } else if (target==Node.class) {
431             NodeInfo node = getFirst();
432             return node;
433             
434         } else if (target==Attr.class) {
435             NodeInfo node = getFirst();
436             if (node==null) return null;
437             if (node.getNodeType() == NodeInfo.ATTRIBUTE) return node;
438             throw new XPathException("Node is of wrong type");
439             
440         } else if (target==CharacterData.class || target==Text.class) {
441             NodeInfo node = getFirst();
442             if (node==null) return null;
443             if (node.getNodeType() == NodeInfo.TEXT) return node;
444             throw new XPathException("Node is of wrong type");
445                                     
446         } else if (target==Comment.class) {
447             NodeInfo node = getFirst();
448             if (node==null) return null;
449             if (node.getNodeType() == NodeInfo.COMMENT) return node;
450             throw new XPathException("Node is of wrong type");
451                         
452         } else if (target==Document.class) {
453             NodeInfo node = getFirst();
454             if (node==null) return null;
455             if (node.getNodeType() == NodeInfo.ROOT) return node;
456             throw new XPathException("Node is of wrong type");
457                         
458         } else if (target==Element.class) {
459             NodeInfo node = getFirst();
460             if (node==null) return null;
461             if (node.getNodeType() == NodeInfo.ELEMENT) return node;
462             throw new XPathException("Node is of wrong type");
463                         
464         } else if (target==ProcessingInstruction.class) {
465             NodeInfo node = getFirst();
466             if (node==null) return null;
467             if (node.getNodeType() == NodeInfo.PI) return node;
468             throw new XPathException("Node is of wrong type");
469             
470         } else if (target==String JavaDoc.class) {
471             return asString();
472         } else if (target==double.class) {
473             return new Double JavaDoc(asNumber());
474         } else if (target==Double JavaDoc.class) {
475             return new Double JavaDoc(asNumber());
476         } else if (target==float.class) {
477             return new Float JavaDoc(asNumber());
478         } else if (target==Float JavaDoc.class) {
479             return new Float JavaDoc(asNumber());
480         } else if (target==long.class) {
481             return new Long JavaDoc((long)asNumber());
482         } else if (target==Long JavaDoc.class) {
483             return new Long JavaDoc((long)asNumber());
484         } else if (target==int.class) {
485             return new Integer JavaDoc((int)asNumber());
486         } else if (target==Integer JavaDoc.class) {
487             return new Integer JavaDoc((int)asNumber());
488         } else if (target==short.class) {
489             return new Short JavaDoc((short)asNumber());
490         } else if (target==Short JavaDoc.class) {
491             return new Short JavaDoc((short)asNumber());
492         } else if (target==byte.class) {
493             return new Byte JavaDoc((byte)asNumber());
494         } else if (target==Byte JavaDoc.class) {
495             return new Byte JavaDoc((byte)asNumber());
496         } else if (target==char.class || target==Character JavaDoc.class) {
497             String JavaDoc s = asString();
498             if (s.length()==1) {
499                 return new Character JavaDoc(s.charAt(0));
500             } else {
501                 throw new XPathException("Cannot convert string to Java char unless length is 1");
502             }
503         } else {
504             throw new XPathException("Conversion of node-set to " + target.getName() +
505                         " is not supported");
506         }
507     }
508                     
509
510
511 }
512
513 //
514
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
515
// you may not use this file except in compliance with the License. You may obtain a copy of the
516
// License at http://www.mozilla.org/MPL/
517
//
518
// Software distributed under the License is distributed on an "AS IS" basis,
519
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
520
// See the License for the specific language governing rights and limitations under the License.
521
//
522
// The Original Code is: all this file.
523
//
524
// The Initial Developer of the Original Code is
525
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
526
//
527
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
528
//
529
// Contributor(s): none.
530
//
531

532
Popular Tags