KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > value > Value


1 package net.sf.saxon.value;
2 import net.sf.saxon.Configuration;
3 import net.sf.saxon.Controller;
4 import net.sf.saxon.event.Builder;
5 import net.sf.saxon.event.PipelineConfiguration;
6 import net.sf.saxon.event.Sender;
7 import net.sf.saxon.event.SequenceReceiver;
8 import net.sf.saxon.expr.*;
9 import net.sf.saxon.functions.Aggregate;
10 import net.sf.saxon.om.*;
11 import net.sf.saxon.style.StandardNames;
12 import net.sf.saxon.tinytree.TinyBuilder;
13 import net.sf.saxon.trans.DynamicError;
14 import net.sf.saxon.trans.XPathException;
15 import net.sf.saxon.type.*;
16
17 import javax.xml.transform.Source JavaDoc;
18 import javax.xml.transform.dom.DOMSource JavaDoc;
19 import java.io.PrintStream JavaDoc;
20 import java.io.Serializable JavaDoc;
21 import java.lang.reflect.Array JavaDoc;
22 import java.lang.reflect.InvocationTargetException JavaDoc;
23 import java.lang.reflect.Method JavaDoc;
24 import java.math.BigDecimal JavaDoc;
25 import java.math.BigInteger JavaDoc;
26 import java.net.URI JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.util.*;
29
30 /**
31 * A value is the result of an expression but it is also an expression in its own right.
32 * Note that every value can be regarded as a sequence - in many cases, a sequence of
33 * length one.
34 */

35
36 public abstract class Value implements Expression, Serializable JavaDoc, ValueRepresentation {
37
38     /**
39      * Static method to make a Value from a given Item (which may be either an AtomicValue
40      * or a NodeInfo
41      * @param val The supplied value, or null, indicating the empty sequence.
42      * @return The supplied value, if it is a value, or a SingletonNode that
43      * wraps the item, if it is a node. If the supplied value was null,
44      * return an EmptySequence
45      */

46
47     public static Value asValue(ValueRepresentation val) {
48         if (val instanceof Value) {
49             return (Value)val;
50         } else if (val == null) {
51             return EmptySequence.getInstance();
52         } else {
53             return new SingletonNode((NodeInfo)val);
54         }
55     }
56
57     /**
58      * Static method to make an Item from a Value
59      * @param value the value to be converted
60      * @param context the context. It is probably safe to set this to null.
61      * @return null if the value is an empty sequence; or the only item in the value
62      * if it is a singleton sequence
63      * @throws XPathException if the Value contains multiple items
64      */

65
66     public static Item asItem(ValueRepresentation value, XPathContext context) throws XPathException {
67         if (value instanceof Item) {
68             return (Item)value;
69         } else if (value instanceof EmptySequence) {
70             return null;
71         } else if (value instanceof SingletonNode) {
72             return ((SingletonNode)value).getNode();
73         } else if (value instanceof AtomicValue) {
74             return (AtomicValue)value;
75         } else if (value instanceof Closure) {
76             return ((Closure)value).evaluateItem(context);
77         } else {
78             SequenceIterator iter = Value.getIterator(value);
79             Item item = iter.next();
80             if (item == null) {
81                 return null;
82             } else if (iter.next() != null) {
83                 throw new AssertionError JavaDoc("Attempting to access a sequence as an item");
84             } else {
85                 return item;
86             }
87         }
88     }
89
90     /**
91      * Static method to get an Iterator over any ValueRepresentation (which may be either a Value
92      * or a NodeInfo
93      * @param val The supplied value, or null, indicating the empty sequence.
94      * @param context The evaluation context. This may be null. It should always be possible to
95      * iterate over a value without supplying a context, but sometimes the context
96      * can provide access to better error information
97      * @return The supplied value, if it is a value, or a SingletonNode that
98      * wraps the item, if it is a node. If the supplied value was null,
99      * return an EmptySequence
100      */

101
102     public static SequenceIterator asIterator(ValueRepresentation val, XPathContext context) throws XPathException {
103         if (val instanceof Value) {
104             return ((Value)val).iterate(context);
105         } else if (val == null) {
106             return EmptyIterator.getInstance();
107         } else {
108             return SingletonIterator.makeIterator((NodeInfo)val);
109         }
110     }
111
112
113     /**
114     * Static method to convert strings to numbers. Might as well go here as anywhere else.
115     * @param s the String to be converted
116     * @return a double representing the value of the String
117      * @throws NumberFormatException if the value cannot be converted
118     */

119
120     public static double stringToNumber(CharSequence JavaDoc s) throws NumberFormatException JavaDoc {
121         String JavaDoc n = trimWhitespace(s).toString();
122         if ("INF".equals(n)) {
123             return Double.POSITIVE_INFINITY;
124         } else if ("-INF".equals(n)) {
125             return Double.NEGATIVE_INFINITY;
126         } else if ("NaN".equals(n)) {
127             return Double.NaN;
128         } else {
129             return Double.parseDouble(n);
130         }
131     }
132
133
134     /**
135     * Normalize whitespace as defined in XML Schema
136     */

137
138     public static CharSequence JavaDoc normalizeWhitespace(CharSequence JavaDoc in) {
139         FastStringBuffer sb = new FastStringBuffer(in.length());
140         for (int i=0; i<in.length(); i++) {
141             char c = in.charAt(i);
142             switch (c) {
143                 case '\n':
144                 case '\r':
145                 case '\t':
146                     sb.append(' ');
147                     break;
148                 default:
149                     sb.append(c);
150                     break;
151             }
152         }
153         return sb;
154     }
155
156     /**
157     * Collapse whitespace as defined in XML Schema
158     */

159
160     public static CharSequence JavaDoc collapseWhitespace(CharSequence JavaDoc in) {
161         if (in.length()==0) {
162             return in;
163         }
164
165         FastStringBuffer sb = new FastStringBuffer(in.length());
166         boolean inWhitespace = true;
167         int i = 0;
168         for (; i<in.length(); i++) {
169             char c = in.charAt(i);
170             switch (c) {
171                 case '\n':
172                 case '\r':
173                 case '\t':
174                 case ' ':
175                     if (inWhitespace) {
176                         // remove the whitespace
177
} else {
178                         sb.append(' ');
179                         inWhitespace = true;
180                     }
181                     break;
182                 default:
183                     sb.append(c);
184                     inWhitespace = false;
185                     break;
186             }
187         }
188         if (sb.charAt(sb.length()-1)==' ') {
189             sb.setLength(sb.length()-1);
190         }
191         return sb;
192     }
193
194     /**
195      * Remove leading and trailing whitespace. This has the same effect as collapseWhitespace,
196      * but is cheaper, for use by data types that do not allow internal whitespace.
197      * @param in the input string whose whitespace is to be removed
198      * @return the result of removing excess whitespace
199      */

200     public static CharSequence JavaDoc trimWhitespace(CharSequence JavaDoc in) {
201         if (in.length()==0) {
202             return in;
203         }
204         int first = 0;
205         int last = in.length()-1;
206         while (in.charAt(first) <= 0x20) {
207             if (first++ >= last) {
208                 return "";
209             }
210         }
211         while (in.charAt(last) <= 0x20) {
212             last--;
213         }
214         return in.subSequence(first, last+1);
215     }
216
217     /**
218      * Get a SequenceIterator over a ValueRepresentation
219      */

220
221     public static SequenceIterator getIterator(ValueRepresentation val) throws XPathException {
222         if (val instanceof Value) {
223             return ((Value)val).iterate(null);
224         } else if (val instanceof NodeInfo) {
225             return SingletonIterator.makeIterator((NodeInfo)val);
226         } else if (val == null) {
227             throw new AssertionError JavaDoc("Value of variable is undefined (null)");
228         } else {
229             throw new AssertionError JavaDoc("Unknown value representation " + val.getClass());
230         }
231     }
232
233     /**
234     * Simplify an expression
235     * @return for a Value, this always returns the value unchanged
236     */

237
238     public final Expression simplify(StaticContext env) {
239         return this;
240     }
241
242     /**
243     * TypeCheck an expression
244     * @return for a Value, this always returns the value unchanged
245     */

246
247     public final Expression typeCheck(StaticContext env, ItemType contextItemType) {
248         return this;
249     }
250
251     /**
252     * Optimize an expression
253     * @return for a Value, this always returns the value unchanged
254     */

255
256     public final Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) {
257         return this;
258     }
259
260
261     /**
262      * Determine the data type of the items in the expression, if possible
263      * @return AnyItemType (not known)
264      */

265
266     public ItemType getItemType() {
267         return AnyItemType.getInstance();
268     }
269
270     /**
271      * Determine the cardinality
272      */

273
274     public int getCardinality() {
275         try {
276             SequenceIterator iter = iterate(null);
277             Item next = iter.next();
278             if (next == null) {
279                 return StaticProperty.EMPTY;
280             } else {
281                 if (iter.next() != null) {
282                     return StaticProperty.ALLOWS_ONE_OR_MORE;
283                 } else {
284                     return StaticProperty.EXACTLY_ONE;
285                 }
286             }
287         } catch (XPathException err) {
288             // can't actually happen
289
return StaticProperty.ALLOWS_ZERO_OR_MORE;
290         }
291     }
292
293     /**
294      * Get the sub-expressions of this expression.
295      * @return for a Value, this always returns an empty array
296      */

297
298     public final Iterator iterateSubExpressions() {
299         return Collections.EMPTY_LIST.iterator();
300     }
301
302     /**
303      * Get the expression that immediately contains this expression. This method
304      * returns null for an outermost expression; it also return null in the case
305      * of literal values. For an XPath expression occurring within an XSLT stylesheet,
306      * this method returns the XSLT instruction containing the XPath expression.
307      * @return the expression that contains this expression, if known; return null
308      * if there is no containing expression or if the containing expression is unknown.
309      */

310
311     public final Container getParentExpression() {
312         return null;
313     }
314
315     /**
316      * Get the static properties of this expression (other than its type). For a
317      * Value, the only special property is {@link StaticProperty#NON_CREATIVE}.
318      * @return {@link StaticProperty#NON_CREATIVE}
319      */

320
321
322     public int getSpecialProperties() {
323         return StaticProperty.NON_CREATIVE;
324     }
325
326     /**
327      * Offer promotion for this subexpression. Values (constant expressions)
328      * are never promoted
329      * @param offer details of the offer, for example the offer to move
330      * expressions that don't depend on the context to an outer level in
331      * the containing expression
332      * @return For a Value, this always returns the value unchanged
333      */

334
335      public final Expression promote(PromotionOffer offer) {
336         return this;
337     }
338
339     /**
340     * Determine which aspects of the context the expression depends on. The result is
341     * a bitwise-or'ed value composed from constants such as StaticProperty.VARIABLES and
342     * StaticProperty.CURRENT_NODE
343      * @return for a Value, this always returns zero.
344     */

345
346     public final int getDependencies() {
347         return 0;
348     }
349
350     /**
351      * Get the n'th item in the sequence (starting from 0). This is defined for all
352      * Values, but its real benefits come for a sequence Value stored extensionally
353      * (or for a MemoClosure, once all the values have been read)
354      */

355
356     public Item itemAt(int n) throws XPathException {
357         if ((getImplementationMethod() & EVALUATE_METHOD) != 0) {
358             if (n==0) {
359                 Item item = evaluateItem(null);
360                 return (item == null ? null : item);
361             } else {
362                 return null;
363             }
364         }
365         if (n < 0) {
366             return null;
367         }
368         int i = 0; // indexing is zero-based
369
SequenceIterator iter = iterate(null);
370         while (true) {
371             Item item = iter.next();
372             if (item == null) {
373                 return null;
374             }
375             if (i++ == n) {
376                 return item;
377             }
378         }
379     }
380
381     /**
382      * Get the length of the sequence
383      */

384
385     public int getLength() throws XPathException {
386         return Aggregate.count(iterate(null));
387     }
388
389     /**
390      * Evaluate as a singleton item (or empty sequence)
391      */

392
393     public Item evaluateItem(XPathContext context) throws XPathException {
394         return iterate(context).next();
395     }
396
397
398     /**
399       * Process the value as an instruction, without returning any tail calls
400       * @param context The dynamic context, giving access to the current node,
401       * the current variables, etc.
402       */

403
404     public void process(XPathContext context) throws XPathException {
405         SequenceIterator iter = iterate(context);
406         SequenceReceiver out = context.getReceiver();
407         while (true) {
408             Item it = iter.next();
409             if (it==null) break;
410             out.append(it, 0, NodeInfo.ALL_NAMESPACES);
411         }
412     }
413
414
415     /**
416      * Convert the value to a string, using the serialization rules.
417      * For atomic values this is the same as a cast; for sequence values
418      * it gives a space-separated list.
419      * @throws XPathException The method can fail if evaluation of the value
420      * has been deferred, and if a failure occurs during the deferred evaluation.
421      * No failure is possible in the case of an AtomicValue.
422      */

423
424     public String JavaDoc getStringValue() throws XPathException {
425         FastStringBuffer sb = new FastStringBuffer(1024);
426         SequenceIterator iter = iterate(null);
427         Item item = iter.next();
428         if (item != null) {
429             while (true) {
430                 sb.append(item.getStringValueCS());
431                 item = iter.next();
432                 if (item == null) {
433                     break;
434                 }
435                 sb.append(' ');
436             }
437         }
438         return sb.toString();
439     }
440
441     /**
442      * Evaluate an expression as a String. This function must only be called in contexts
443      * where it is known that the expression will return a single string (or where an empty sequence
444      * is to be treated as a zero-length string). Implementations should not attempt to convert
445      * the result to a string, other than converting () to "". This method is used mainly to
446      * evaluate expressions produced by compiling an attribute value template.
447      *
448      * @exception XPathException if any dynamic error occurs evaluating the
449      * expression
450      * @exception ClassCastException if the result type of the
451      * expression is not xs:string?
452      * @param context The context in which the expression is to be evaluated
453      * @return the value of the expression, evaluated in the current context.
454      * The expression must return a string or (); if the value of the
455      * expression is (), this method returns "".
456      */

457
458     public String JavaDoc evaluateAsString(XPathContext context) throws XPathException {
459         AtomicValue value = (AtomicValue) evaluateItem(context);
460         if (value == null) return "";
461         return value.getStringValue();
462     }
463
464
465     /**
466      * Get the effective boolean value of the expression. This returns false if the value
467      * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
468      * false. Otherwise it returns true.
469      *
470      * @param context The context in which the expression is to be evaluated
471      * @exception XPathException if any dynamic error occurs evaluating the
472      * expression
473      * @return the effective boolean value
474      */

475
476     public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
477         return ExpressionTool.effectiveBooleanValue(iterate(context));
478     }
479
480     /**
481      * Compare two (sequence) values for equality. This supports identity constraints in XML Schema,
482      * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
483      * This method returns false if any error occurs during the comparison, or if any of the items
484      * in either sequence is a node rather than an atomic value.
485      */

486
487     public boolean equals(Object JavaDoc obj) {
488         try {
489             if (obj instanceof Value) {
490                 SequenceIterator iter1 = iterate(null);
491                 SequenceIterator iter2 = ((Value)obj).iterate(null);
492                 while (true) {
493                     Item item1 = iter1.next();
494                     Item item2 = iter2.next();
495                     if (item1 == null && item2 == null) {
496                         return true;
497                     }
498                     if (item1 == null || item2 == null) {
499                         return false;
500                     }
501                     if (item1 instanceof NodeInfo || item2 instanceof NodeInfo) {
502                         return false;
503                     }
504                     if (!item1.equals(item2)) {
505                         return false;
506                     }
507                 }
508             } else {
509                 return false;
510             }
511         } catch (XPathException e) {
512             return false;
513         }
514     }
515
516     /**
517      * Compare two (sequence) values for equality. This supports identity constraints in XML Schema,
518      * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
519      * This method returns false if any error occurs during the comparison, or if any of the items
520      * in either sequence is a node rather than an atomic value.
521      */

522
523     public boolean schemaEquals(Value obj) {
524         try {
525             SequenceIterator iter1 = iterate(null);
526             SequenceIterator iter2 = obj.iterate(null);
527             while (true) {
528                 Item item1 = iter1.next();
529                 Item item2 = iter2.next();
530                 if (item1 == null && item2 == null) {
531                     return true;
532                 }
533                 if (item1 == null || item2 == null) {
534                     return false;
535                 }
536                 if (item1 instanceof NodeInfo || item2 instanceof NodeInfo) {
537                     return false;
538                 }
539                 if (!((AtomicValue)item1).schemaEquals((AtomicValue)item2)) {
540                     return false;
541                 }
542             }
543         } catch (XPathException e) {
544             return false;
545         }
546     }
547
548     /**
549      * Return a hash code to support the equals() function
550      */

551
552     public int hashCode() {
553         try {
554             int hash = 0x06639662; // arbitrary seed
555
SequenceIterator iter = iterate(null);
556             while (true) {
557                 Item item = iter.next();
558                 if (item == null) {
559                     return hash;
560                 }
561                 hash ^= item.hashCode();
562             }
563         } catch (XPathException e) {
564             return 0;
565         }
566     }
567
568
569     /**
570      * Check statically that the results of the expression are capable of constructing the content
571      * of a given schema type.
572      * @param parentType The schema type
573      * @param env the static context
574      * @param whole
575      * @throws XPathException if the expression doesn't match the required content type
576      */

577
578     public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
579         return;
580     }
581
582     /**
583      * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value
584      * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing
585      * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression
586      * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue
587      */

588
589     public Value reduce() throws XPathException {
590         return this;
591     }
592
593     /**
594      * Convert to Java object (for passing to external functions)
595      */

596
597     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
598
599         if (target == Object JavaDoc.class) {
600             List list = new ArrayList(20);
601             return convertToJavaList(list, context);
602         }
603
604         // See if the extension function is written to accept native Saxon objects
605

606         if (target.isAssignableFrom(this.getClass())) {
607             return this;
608         } else if (target.isAssignableFrom(SequenceIterator.class)) {
609             return iterate(context);
610         }
611
612         // Offer the object to registered external object models
613

614         if ((this instanceof ObjectValue || !(this instanceof AtomicValue)) && !(this instanceof EmptySequence)) {
615             List externalObjectModels = context.getController().getConfiguration().getExternalObjectModels();
616             for (int m=0; m<externalObjectModels.size(); m++) {
617                 ExternalObjectModel model = (ExternalObjectModel)externalObjectModels.get(m);
618                 Object JavaDoc object = model.convertXPathValueToObject(this, target, context);
619                 if (object != null) {
620                     return object;
621                 }
622             }
623         }
624
625         if (Collection.class.isAssignableFrom(target)) {
626             Collection list;
627             if (target.isAssignableFrom(ArrayList.class)) {
628                 list = new ArrayList(100);
629             } else {
630                 try {
631                     list = (Collection)target.newInstance();
632                 } catch (InstantiationException JavaDoc e) {
633                     DynamicError de = new DynamicError("Cannot instantiate collection class " + target);
634                     de.setXPathContext(context);
635                     throw de;
636                 } catch (IllegalAccessException JavaDoc e) {
637                     DynamicError de = new DynamicError("Cannot access collection class " + target);
638                     de.setXPathContext(context);
639                     throw de;
640                 }
641             }
642             return convertToJavaList(list, context);
643         } else if (target.isArray()) {
644             Class JavaDoc component = target.getComponentType();
645             if (component.isAssignableFrom(Item.class) ||
646                     component.isAssignableFrom(NodeInfo.class) ||
647                     component.isAssignableFrom(DocumentInfo.class)) {
648                 Value extent = this;
649                 if (extent instanceof Closure) {
650                     extent = SequenceExtent.makeSequenceExtent(extent.iterate(null));
651                 }
652                 int length = extent.getLength();
653                 Object JavaDoc array = Array.newInstance(component, length);
654                 SequenceIterator iter = extent.iterate(null);
655                 for (int i=0; i<length; i++) {
656                     Item item = iter.next();
657                     try {
658                         Array.set(array, i, item);
659                     } catch (IllegalArgumentException JavaDoc err) {
660                         DynamicError d = new DynamicError(
661                                 "Item " + i + " in supplied sequence cannot be converted " +
662                                 "to the component type of the Java array (" + component + ')', err);
663                         d.setXPathContext(context);
664                         throw d;
665                     }
666                 }
667                 return array;
668             } else /* if (!(this instanceof AtomicValue)) */ {
669                 // try atomizing the sequence, unless this is a single atomic value, in which case we've already
670
// tried that.
671
SequenceIterator it = Atomizer.AtomizingFunction.getAtomizingIterator(iterate(context));
672                 int length;
673                 if ((it.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
674                     length = ((LastPositionFinder)it).getLastPosition();
675                 } else {
676                     SequenceExtent extent = new SequenceExtent(it);
677                     length = extent.getLength();
678                     it = extent.iterate(context);
679                 }
680                 Object JavaDoc array = Array.newInstance(component, length);
681                 for (int i=0; i<length; i++) {
682                     try {
683                         AtomicValue val = (AtomicValue)it.next();
684                         Object JavaDoc jval = val.convertToJava(component, context);
685                         Array.set(array, i, jval);
686                     } catch (XPathException err) {
687                         DynamicError d = new DynamicError(
688                                 "Cannot convert item in atomized sequence to the component type of the Java array", err);
689                         d.setXPathContext(context);
690                         throw d;
691                     }
692                 }
693                 return array;
694 // } else {
695
// DynamicError d = new DynamicError(
696
// "Cannot convert supplied argument value to the required type");
697
// d.setXPathContext(context);
698
// throw d;
699
}
700
701         } else if (target.isAssignableFrom(Item.class) ||
702                 target.isAssignableFrom(NodeInfo.class) ||
703                 target.isAssignableFrom(DocumentInfo.class)) {
704
705             // try passing the first item in the sequence provided it is the only one
706
SequenceIterator iter = iterate(null);
707             Item first = null;
708             while (true) {
709                 Item next = iter.next();
710                 if (next == null) {
711                     break;
712                 }
713                 if (first != null) {
714                     DynamicError err = new DynamicError("Sequence contains more than one value; Java method expects only one");
715                     err.setXPathContext(context);
716                     throw err;
717                 }
718                 first = next;
719             }
720             if (first == null) {
721                 // sequence is empty; pass a Java null
722
return null;
723             }
724             if (target.isAssignableFrom(first.getClass())) {
725                 // covers Item and NodeInfo
726
return first;
727             }
728
729             Object JavaDoc n = first;
730             while (n instanceof VirtualNode) {
731                 // If we've got a wrapper around a DOM or JDOM node, and the user wants a DOM
732
// or JDOM node, we unwrap it
733
Object JavaDoc vn = ((VirtualNode) n).getUnderlyingNode();
734                 if (target.isAssignableFrom(vn.getClass())) {
735                     return vn;
736                 } else {
737                     n = vn;
738                 }
739             }
740
741             throw new DynamicError("Cannot convert supplied XPath value to the required type for the extension function");
742         } else if (!(this instanceof AtomicValue)) {
743             // try atomizing the value, unless this is an atomic value, in which case we've already tried that
744
SequenceIterator it = Atomizer.AtomizingFunction.getAtomizingIterator(iterate(context));
745             Item first = null;
746             while (true) {
747                 Item next = it.next();
748                 if (next == null) {
749                     break;
750                 }
751                 if (first != null) {
752                     DynamicError err = new DynamicError("Sequence contains more than one value; Java method expects only one");
753                     err.setXPathContext(context);
754                     throw err;
755                 }
756                 first = next;
757             }
758             if (first == null) {
759                 // sequence is empty; pass a Java null
760
return null;
761             }
762             if (target.isAssignableFrom(first.getClass())) {
763                 return first;
764             } else {
765                 return ((AtomicValue)first).convertToJava(target, context);
766             }
767         } else {
768             throw new DynamicError("Cannot convert supplied XPath value to the required type for the extension function");
769         }
770     }
771
772     private Collection convertToJavaList(Collection list, XPathContext context) throws XPathException {
773         // TODO: with JDK 1.5, check to see if the item type of the list is constrained
774
SequenceIterator iter = iterate(null);
775         while (true) {
776             Item it = iter.next();
777             if (it == null) {
778 // if (list.size() == 0) {
779
// // map empty sequence to null
780
// return null;
781
// } else {
782
return list;
783 // }
784
}
785             if (it instanceof AtomicValue) {
786                 list.add(((AtomicValue)it).convertToJava(Object JavaDoc.class, context));
787             } else if (it instanceof VirtualNode) {
788                 list.add(((VirtualNode)it).getUnderlyingNode());
789             } else {
790                 list.add(it);
791             }
792         }
793     }
794
795     /**
796      * Diagnostic display of the expression
797      */

798
799     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
800         try {
801             out.println(ExpressionTool.indent(level) + "sequence of " +
802                     getItemType().toString() + " (");
803             SequenceIterator iter = iterate(null);
804             while (true) {
805                 Item it = iter.next();
806                 if (it == null) {
807                     break;
808                 }
809                 if (it instanceof NodeInfo) {
810                     out.println(ExpressionTool.indent(level + 1) + "node " + Navigator.getPath(((NodeInfo)it)));
811                 } else {
812                     out.println(ExpressionTool.indent(level + 1) + it.toString());
813                 }
814             }
815             out.println(ExpressionTool.indent(level) + ')');
816         } catch (XPathException err) {
817             out.println(ExpressionTool.indent(level) + "(*error*)");
818         }
819     }
820
821     /**
822     * Convert a Java object to an XPath value. This method is called to handle the result
823     * of an external function call (but only if the required type is not known),
824     * and also to process global parameters passed to the stylesheet or query.
825     * @param object The Java object to be converted
826     * @param requiredType The required type of the result (if known)
827     * @param config The Configuration: may be null, in which case certain kinds of object
828      * (eg. DOM nodes) cannot be handled
829     * @return the result of converting the value. If the value is null, returns null.
830     */

831
832     public static Value convertJavaObjectToXPath(
833             Object JavaDoc object, SequenceType requiredType, Configuration config)
834                                           throws XPathException {
835
836         ItemType requiredItemType = requiredType.getPrimaryType();
837
838         if (object==null) {
839             return EmptySequence.getInstance();
840         }
841
842         // Offer the object to all the registered external object models
843

844         List externalObjectModels = config.getExternalObjectModels();
845         for (int m=0; m<externalObjectModels.size(); m++) {
846             ExternalObjectModel model = (ExternalObjectModel)externalObjectModels.get(m);
847             Value val = model.convertObjectToXPathValue(object, config);
848             if (val != null && TypeChecker.testConformance(val, requiredType) == null) {
849                 return val;
850             }
851         }
852
853         if (requiredItemType instanceof ExternalObjectType) {
854             Class JavaDoc theClass = ((ExternalObjectType)requiredItemType).getJavaClass();
855             if (theClass.isAssignableFrom(object.getClass())) {
856                 return new ObjectValue(object);
857             } else {
858                 throw new DynamicError("Supplied parameter value is not of class " + theClass.getName());
859             }
860         }
861
862         Value value = convertToBestFit(object, config);
863         return value;
864
865     }
866
867     private static Value convertToBestFit(Object JavaDoc object, Configuration config) throws XPathException {
868         if (object instanceof String JavaDoc) {
869             return StringValue.makeStringValue((String JavaDoc)object);
870
871         } else if (object instanceof Character JavaDoc) {
872             return new StringValue(object.toString());
873
874         } else if (object instanceof Boolean JavaDoc) {
875             return BooleanValue.get(((Boolean JavaDoc)object).booleanValue());
876
877         } else if (object instanceof Double JavaDoc) {
878             return new DoubleValue(((Double JavaDoc)object).doubleValue());
879
880         } else if (object instanceof Float JavaDoc) {
881             return new FloatValue(((Float JavaDoc)object).floatValue());
882
883         } else if (object instanceof Short JavaDoc) {
884             return new IntegerValue(((Short JavaDoc)object).shortValue(),
885                                     (AtomicType)BuiltInSchemaFactory.getSchemaType(StandardNames.XS_SHORT));
886         } else if (object instanceof Integer JavaDoc) {
887             return new IntegerValue(((Integer JavaDoc)object).intValue(),
888                                     (AtomicType)BuiltInSchemaFactory.getSchemaType(StandardNames.XS_INT));
889         } else if (object instanceof Long JavaDoc) {
890             return new IntegerValue(((Long JavaDoc)object).longValue(),
891                                     (AtomicType)BuiltInSchemaFactory.getSchemaType(StandardNames.XS_LONG));
892         } else if (object instanceof Byte JavaDoc) {
893             return new IntegerValue(((Byte JavaDoc)object).byteValue(),
894                                     (AtomicType)BuiltInSchemaFactory.getSchemaType(StandardNames.XS_BYTE));
895
896         } else if (object instanceof BigInteger JavaDoc) {
897             return BigIntegerValue.makeValue(((BigInteger JavaDoc)object));
898
899         } else if (object instanceof BigDecimal JavaDoc) {
900             return new DecimalValue(((BigDecimal JavaDoc)object));
901
902 // } else if (object instanceof QName) {
903
// return new QNameValue((QName)object);
904
// TODO: reinstate above lines in JDK 1.5
905
} else if (object.getClass().getName().equals("javax.xml.namespace.QName")) {
906             return makeQNameValue(object, config);
907
908         } else if (object instanceof URI JavaDoc) {
909             return new AnyURIValue(object.toString());
910
911         } else if (object instanceof URL JavaDoc) {
912             return new AnyURIValue(object.toString());
913
914         } else if (object instanceof Closure) {
915             // Force eager evaluation, because of problems with side-effects.
916
// (The value might depend on data that is mutable.)
917
return ExpressionTool.eagerEvaluate((Closure)object, null);
918
919         } else if (object instanceof Value) {
920             return (Value)object;
921
922         } else if (object instanceof NodeInfo) {
923             if (((NodeInfo)object).getNamePool() != config.getNamePool()) {
924                 throw new DynamicError("Externally-supplied node belongs to wrong NamePool");
925             }
926             return new SingletonNode((NodeInfo)object);
927
928         } else if (object instanceof SequenceIterator) {
929             //return new SequenceIntent((SequenceIterator)object);
930
return Closure.makeIteratorClosure((SequenceIterator)object);
931
932         } else if (object instanceof List) {
933             Item[] array = new Item[((List)object).size()];
934             int a = 0;
935             for (Iterator i=((List)object).iterator(); i.hasNext(); ) {
936                 Object JavaDoc obj = i.next();
937                 if (obj instanceof NodeInfo) {
938                     array[a++] = (NodeInfo)obj;
939                 } else {
940                     Value v = convertToBestFit(obj, config);
941                     if (v!=null) {
942                         if (v instanceof Item) {
943                             array[a++] = (Item)v;
944                         } else if (v instanceof EmptySequence) {
945                             // no action
946
} else if (v instanceof SingletonNode) {
947                             NodeInfo node = ((SingletonNode)v).getNode();
948                             if (node != null) {
949                                 array[a++] = node;
950                             }
951                         } else {
952                             throw new DynamicError(
953                                     "Returned List contains an object that cannot be converted to an Item (" + obj.getClass() + ')');
954                         }
955                     }
956                 }
957             }
958
959             return new SequenceExtent(array);
960
961         } else if (object instanceof Object JavaDoc[]) {
962              Item[] array = new Item[((Object JavaDoc[])object).length];
963              int a = 0;
964              for (int i = 0; i < ((Object JavaDoc[])object).length; i++){
965                  Object JavaDoc obj = ((Object JavaDoc[])object)[i];
966                  if (obj instanceof NodeInfo) {
967                      array[a++] = (NodeInfo)obj;
968                  } else if (obj != null) {
969                      Value v = convertToBestFit(obj, config);
970                      if (v!=null) {
971                          if (v instanceof Item) {
972                              array[a++] = (Item)v;
973                          } else {
974                              throw new DynamicError(
975                                      "Returned array contains an object that cannot be converted to an Item (" + obj.getClass() + ')');
976                          }
977                      }
978                  }
979              }
980              return new SequenceExtent(array, 0, a);
981
982         } else if (object instanceof long[]) {
983              Item[] array = new Item[((long[])object).length];
984              for (int i = 0; i < ((long[])object).length; i++){
985                  array[i] = new IntegerValue(((long[])object)[i]);
986              }
987              return new SequenceExtent(array);
988
989         } else if (object instanceof int[]) {
990              Item[] array = new Item[((int[])object).length];
991              for (int i = 0; i < ((int[])object).length; i++){
992                  array[i] = new IntegerValue(((int[])object)[i]);
993              }
994              return new SequenceExtent(array);
995
996         } else if (object instanceof short[]) {
997              Item[] array = new Item[((short[])object).length];
998              for (int i = 0; i < ((short[])object).length; i++){
999                  array[i] = new IntegerValue(((short[])object)[i]);
1000             }
1001             return new SequenceExtent(array);
1002
1003        } else if (object instanceof byte[]) { // interpret this as unsigned bytes
1004
Item[] array = new Item[((byte[])object).length];
1005             for (int i = 0; i < ((byte[])object).length; i++){
1006                 array[i] = new IntegerValue(255 & (int)((byte[])object)[i]);
1007             }
1008             return new SequenceExtent(array);
1009
1010        } else if (object instanceof char[]) {
1011             return StringValue.makeStringValue(new String JavaDoc((char[])object));
1012
1013       } else if (object instanceof boolean[]) {
1014             Item[] array = new Item[((boolean[])object).length];
1015             for (int i = 0; i < ((boolean[])object).length; i++){
1016                 array[i] = BooleanValue.get(((boolean[])object)[i]);
1017             }
1018             return new SequenceExtent(array);
1019
1020        } else if (object instanceof Source JavaDoc && config != null) {
1021            if (object instanceof DOMSource JavaDoc) {
1022                return new SingletonNode(Controller.unravel((Source JavaDoc)object, config));
1023            }
1024            try {
1025                Builder b = new TinyBuilder();
1026                PipelineConfiguration pipe = config.makePipelineConfiguration();
1027                b.setPipelineConfiguration(pipe);
1028                new Sender(pipe).send((Source JavaDoc) object, b);
1029                return new SingletonNode(b.getCurrentRoot());
1030            } catch (XPathException err) {
1031                throw new DynamicError(err);
1032            }
1033        } else {
1034            // See whether this is an object representing a Node in some recognized object model
1035
ExternalObjectModel model = config.findExternalObjectModel(object);
1036            if (model != null) {
1037                DocumentInfo doc = model.wrapDocument(object, "", config);
1038                NodeInfo node = model.wrapNode(doc, object);
1039                return Value.asValue(node);
1040            }
1041        }
1042        return new ObjectValue(object);
1043    }
1044
1045    /**
1046     * Temporary method to make a QNameValue from a JAXP 1.3 QName, without creating a compile-time link
1047     * to the JDK 1.5 QName class
1048     * @param object an instance of javax.xml.namespace.QName
1049     * @return a corresponding Saxon QNameValue, or null if any error occurs performing the conversion
1050     */

1051
1052    public static QNameValue makeQNameValue(Object JavaDoc object, Configuration config) {
1053        try {
1054            Class JavaDoc qnameClass = config.getClass("javax.xml.namespace.QName", false, null);
1055            Class JavaDoc[] args = EMPTY_CLASS_ARRAY;
1056            Method JavaDoc getPrefix = qnameClass.getMethod("getPrefix", args);
1057            Method JavaDoc getLocalPart = qnameClass.getMethod("getLocalPart", args);
1058            Method JavaDoc getNamespaceURI = qnameClass.getMethod("getNamespaceURI", args);
1059            String JavaDoc prefix = (String JavaDoc)getPrefix.invoke(object, args);
1060            String JavaDoc localPart = (String JavaDoc)getLocalPart.invoke(object, args);
1061            String JavaDoc uri = (String JavaDoc)getNamespaceURI.invoke(object, args);
1062            return new QNameValue(prefix, uri, localPart);
1063        } catch (XPathException e) {
1064            return null;
1065        } catch (NoSuchMethodException JavaDoc e) {
1066            return null;
1067        } catch (IllegalAccessException JavaDoc e) {
1068            return null;
1069        } catch (InvocationTargetException JavaDoc e) {
1070            return null;
1071        }
1072    }
1073
1074    public static final Class JavaDoc[] EMPTY_CLASS_ARRAY = new Class JavaDoc[0];
1075
1076    /**
1077     * Convert to a string for diagnostic output
1078     */

1079
1080    public String JavaDoc toString() {
1081        try {
1082            return getStringValue();
1083        } catch (XPathException err) {
1084            return super.toString();
1085        }
1086    }
1087
1088    /**
1089     * Internal method to convert an XPath value to a Java object.
1090     * An atomic value is returned as an instance
1091     * of the best available Java class. If the item is a node, the node is "unwrapped",
1092     * to return the underlying node in the original model (which might be, for example,
1093     * a DOM or JDOM node).
1094    */

1095
1096    public static Object JavaDoc convert(Item item) throws XPathException {
1097        if (item instanceof NodeInfo) {
1098            Object JavaDoc node = item;
1099            while (node instanceof VirtualNode) {
1100                // strip off any layers of wrapping
1101
node = ((VirtualNode)node).getUnderlyingNode();
1102            }
1103            return node;
1104        } else {
1105            switch (((AtomicValue)item).getItemType().getPrimitiveType()) {
1106                case Type.STRING:
1107                case Type.UNTYPED_ATOMIC:
1108                case Type.ANY_URI:
1109                case Type.DURATION:
1110                    return item.getStringValue();
1111                case Type.BOOLEAN:
1112                    return (((BooleanValue)item).getBooleanValue() ? Boolean.TRUE : Boolean.FALSE );
1113                case Type.DECIMAL:
1114                    return ((DecimalValue)item).getValue();
1115                case Type.INTEGER:
1116                    return new Long JavaDoc(((NumericValue)item).longValue());
1117                case Type.DOUBLE:
1118                    return new Double JavaDoc(((DoubleValue)item).getDoubleValue());
1119                case Type.FLOAT:
1120                    return new Float JavaDoc(((FloatValue)item).getValue());
1121                case Type.DATE_TIME:
1122                    return ((DateTimeValue)item).getUTCDate();
1123                case Type.DATE:
1124                    return ((DateValue)item).getUTCDate();
1125                case Type.TIME:
1126                    return item.getStringValue();
1127                case Type.BASE64_BINARY:
1128                    return ((Base64BinaryValue)item).getBinaryValue();
1129                case Type.HEX_BINARY:
1130                    return ((HexBinaryValue)item).getBinaryValue();
1131                default:
1132                    return item;
1133            }
1134        }
1135    }
1136}
1137
1138//
1139
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1140
// you may not use this file except in compliance with the License. You may obtain a copy of the
1141
// License at http://www.mozilla.org/MPL/
1142
//
1143
// Software distributed under the License is distributed on an "AS IS" basis,
1144
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1145
// See the License for the specific language governing rights and limitations under the License.
1146
//
1147
// The Original Code is: all this file.
1148
//
1149
// The Initial Developer of the Original Code is Michael H. Kay.
1150
//
1151
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1152
//
1153
// Contributor(s): none.
1154
//
1155
Popular Tags