1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: XObject.java 469368 2006-10-31 04:41:36Z minchau $
20  */
21 package org.apache.xpath.objects;
22 
23 import java.io.Serializable;
24 
25 import org.apache.xalan.res.XSLMessages;
26 import org.apache.xml.dtm.DTM;
27 import org.apache.xml.dtm.DTMIterator;
28 import org.apache.xml.utils.XMLString;
29 import org.apache.xpath.Expression;
30 import org.apache.xpath.ExpressionOwner;
31 import org.apache.xpath.NodeSetDTM;
32 import org.apache.xpath.XPathContext;
33 import org.apache.xpath.XPathException;
34 import org.apache.xpath.XPathVisitor;
35 import org.apache.xpath.res.XPATHErrorResources;
36 
37 import org.w3c.dom.DocumentFragment;
38 import org.w3c.dom.NodeList;
39 import org.w3c.dom.traversal.NodeIterator;
40 
41 /**
42  * This class represents an XPath object, and is capable of
43  * converting the object to various types, such as a string.
44  * This class acts as the base class to other XPath type objects,
45  * such as XString, and provides polymorphic casting capabilities.
46  * @xsl.usage general
47  */
48 public class XObject extends Expression implements Serializable, Cloneable
49 {
50     static final long serialVersionUID = -821887098985662951L;
51 
52   /**
53    * The java object which this object wraps.
54    *  @serial
55    */
56   protected Object m_obj;  // This may be NULL!!!
57 
58   /**
59    * Create an XObject.
60    */
XObject()61   public XObject(){}
62 
63   /**
64    * Create an XObject.
65    *
66    * @param obj Can be any object, should be a specific type
67    * for derived classes, or null.
68    */
XObject(Object obj)69   public XObject(Object obj)
70   {
71     setObject(obj);
72   }
73 
setObject(Object obj)74   protected void setObject(Object obj) {
75       m_obj = obj;
76   }
77 
78   /**
79    * For support of literal objects in xpaths.
80    *
81    * @param xctxt The XPath execution context.
82    *
83    * @return This object.
84    *
85    * @throws javax.xml.transform.TransformerException
86    */
execute(XPathContext xctxt)87   public XObject execute(XPathContext xctxt)
88           throws javax.xml.transform.TransformerException
89   {
90     return this;
91   }
92 
93   /**
94    * Specify if it's OK for detach to release the iterator for reuse.
95    * This function should be called with a value of false for objects that are
96    * stored in variables.
97    * Calling this with a value of false on a XNodeSet will cause the nodeset
98    * to be cached.
99    *
100    * @param allowRelease true if it is OK for detach to release this iterator
101    * for pooling.
102    */
allowDetachToRelease(boolean allowRelease)103   public void allowDetachToRelease(boolean allowRelease){}
104 
105   /**
106    * Detaches the <code>DTMIterator</code> from the set which it iterated
107    * over, releasing any computational resources and placing the iterator
108    * in the INVALID state. After <code>detach</code> has been invoked,
109    * calls to <code>nextNode</code> or <code>previousNode</code> will
110    * raise a runtime exception.
111    */
detach()112   public void detach(){}
113 
114   /**
115    * Forces the object to release it's resources.  This is more harsh than
116    * detach().
117    */
destruct()118   public void destruct()
119   {
120 
121     if (null != m_obj)
122     {
123       allowDetachToRelease(true);
124       detach();
125 
126       setObject(null);
127     }
128   }
129 
130   /**
131    * Reset for fresh reuse.
132    */
reset()133   public void reset()
134   {
135   }
136 
137   /**
138    * Directly call the
139    * characters method on the passed ContentHandler for the
140    * string-value. Multiple calls to the
141    * ContentHandler's characters methods may well occur for a single call to
142    * this method.
143    *
144    * @param ch A non-null reference to a ContentHandler.
145    *
146    * @throws org.xml.sax.SAXException
147    */
dispatchCharactersEvents(org.xml.sax.ContentHandler ch)148   public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
149           throws org.xml.sax.SAXException
150   {
151     xstr().dispatchCharactersEvents(ch);
152   }
153 
154   /**
155    * Create the right XObject based on the type of the object passed.  This
156    * function can not make an XObject that exposes DOM Nodes, NodeLists, and
157    * NodeIterators to the XSLT stylesheet as node-sets.
158    *
159    * @param val The java object which this object will wrap.
160    *
161    * @return the right XObject based on the type of the object passed.
162    */
create(Object val)163   static public XObject create(Object val)
164   {
165     return XObjectFactory.create(val);
166   }
167 
168   /**
169    * Create the right XObject based on the type of the object passed.
170    * This function <emph>can</emph> make an XObject that exposes DOM Nodes, NodeLists, and
171    * NodeIterators to the XSLT stylesheet as node-sets.
172    *
173    * @param val The java object which this object will wrap.
174    * @param xctxt The XPath context.
175    *
176    * @return the right XObject based on the type of the object passed.
177    */
create(Object val, XPathContext xctxt)178   static public XObject create(Object val, XPathContext xctxt)
179   {
180     return XObjectFactory.create(val, xctxt);
181   }
182 
183   /** Constant for NULL object type */
184   public static final int CLASS_NULL = -1;
185 
186   /** Constant for UNKNOWN object type */
187   public static final int CLASS_UNKNOWN = 0;
188 
189   /** Constant for BOOLEAN  object type */
190   public static final int CLASS_BOOLEAN = 1;
191 
192   /** Constant for NUMBER object type */
193   public static final int CLASS_NUMBER = 2;
194 
195   /** Constant for STRING object type */
196   public static final int CLASS_STRING = 3;
197 
198   /** Constant for NODESET object type */
199   public static final int CLASS_NODESET = 4;
200 
201   /** Constant for RESULT TREE FRAGMENT object type */
202   public static final int CLASS_RTREEFRAG = 5;
203 
204   /** Represents an unresolved variable type as an integer. */
205   public static final int CLASS_UNRESOLVEDVARIABLE = 600;
206 
207   /**
208    * Tell what kind of class this is.
209    *
210    * @return CLASS_UNKNOWN
211    */
getType()212   public int getType()
213   {
214     return CLASS_UNKNOWN;
215   }
216 
217   /**
218    * Given a request type, return the equivalent string.
219    * For diagnostic purposes.
220    *
221    * @return type string "#UNKNOWN" + object class name
222    */
getTypeString()223   public String getTypeString()
224   {
225     return "#UNKNOWN (" + object().getClass().getName() + ")";
226   }
227 
228   /**
229    * Cast result object to a number. Always issues an error.
230    *
231    * @return 0.0
232    *
233    * @throws javax.xml.transform.TransformerException
234    */
num()235   public double num() throws javax.xml.transform.TransformerException
236   {
237 
238     error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
239           new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a number");
240 
241     return 0.0;
242   }
243 
244   /**
245    * Cast result object to a number, but allow side effects, such as the
246    * incrementing of an iterator.
247    *
248    * @return numeric value of the string conversion from the
249    * next node in the NodeSetDTM, or NAN if no node was found
250    */
numWithSideEffects()251   public double numWithSideEffects()  throws javax.xml.transform.TransformerException
252   {
253     return num();
254   }
255 
256   /**
257    * Cast result object to a boolean. Always issues an error.
258    *
259    * @return false
260    *
261    * @throws javax.xml.transform.TransformerException
262    */
bool()263   public boolean bool() throws javax.xml.transform.TransformerException
264   {
265 
266     error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
267           new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a number");
268 
269     return false;
270   }
271 
272   /**
273    * Cast result object to a boolean, but allow side effects, such as the
274    * incrementing of an iterator.
275    *
276    * @return True if there is a next node in the nodeset
277    */
boolWithSideEffects()278   public boolean boolWithSideEffects() throws javax.xml.transform.TransformerException
279   {
280     return bool();
281   }
282 
283 
284   /**
285    * Cast result object to a string.
286    *
287    * @return The string this wraps or the empty string if null
288    */
xstr()289   public XMLString xstr()
290   {
291     return XMLStringFactoryImpl.getFactory().newstr(str());
292   }
293 
294   /**
295    * Cast result object to a string.
296    *
297    * @return The object as a string
298    */
str()299   public String str()
300   {
301     return (m_obj != null) ? m_obj.toString() : "";
302   }
303 
304   /**
305    * Return the string representation of the object
306    *
307    *
308    * @return the string representation of the object
309    */
toString()310   public String toString()
311   {
312     return str();
313   }
314 
315   /**
316    * Cast result object to a result tree fragment.
317    *
318    * @param support XPath context to use for the conversion
319    *
320    * @return the objec as a result tree fragment.
321    */
rtf(XPathContext support)322   public int rtf(XPathContext support)
323   {
324 
325     int result = rtf();
326 
327     if (DTM.NULL == result)
328     {
329       DTM frag = support.createDocumentFragment();
330 
331       // %OPT%
332       frag.appendTextChild(str());
333 
334       result = frag.getDocument();
335     }
336 
337     return result;
338   }
339 
340   /**
341    * Cast result object to a result tree fragment.
342    *
343    * @param support XPath context to use for the conversion
344    *
345    * @return the objec as a result tree fragment.
346    */
rtree(XPathContext support)347   public DocumentFragment rtree(XPathContext support)
348   {
349     DocumentFragment docFrag = null;
350     int result = rtf();
351 
352     if (DTM.NULL == result)
353     {
354       DTM frag = support.createDocumentFragment();
355 
356       // %OPT%
357       frag.appendTextChild(str());
358 
359       docFrag = (DocumentFragment)frag.getNode(frag.getDocument());
360     }
361     else
362     {
363       DTM frag = support.getDTM(result);
364       docFrag = (DocumentFragment)frag.getNode(frag.getDocument());
365     }
366 
367     return docFrag;
368   }
369 
370 
371   /**
372    * For functions to override.
373    *
374    * @return null
375    */
rtree()376   public DocumentFragment rtree()
377   {
378     return null;
379   }
380 
381   /**
382    * For functions to override.
383    *
384    * @return null
385    */
rtf()386   public int rtf()
387   {
388     return DTM.NULL;
389   }
390 
391   /**
392    * Return a java object that's closest to the representation
393    * that should be handed to an extension.
394    *
395    * @return The object that this class wraps
396    */
object()397   public Object object()
398   {
399     return m_obj;
400   }
401 
402   /**
403    * Cast result object to a nodelist. Always issues an error.
404    *
405    * @return null
406    *
407    * @throws javax.xml.transform.TransformerException
408    */
iter()409   public DTMIterator iter() throws javax.xml.transform.TransformerException
410   {
411 
412     error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
413           new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeList!");
414 
415     return null;
416   }
417 
418   /**
419    * Get a fresh copy of the object.  For use with variables.
420    *
421    * @return This object, unless overridden by subclass.
422    */
getFresh()423   public XObject getFresh()
424   {
425     return this;
426   }
427 
428 
429   /**
430    * Cast result object to a nodelist. Always issues an error.
431    *
432    * @return null
433    *
434    * @throws javax.xml.transform.TransformerException
435    */
nodeset()436   public NodeIterator nodeset() throws javax.xml.transform.TransformerException
437   {
438 
439     error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
440           new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeList!");
441 
442     return null;
443   }
444 
445   /**
446    * Cast result object to a nodelist. Always issues an error.
447    *
448    * @return null
449    *
450    * @throws javax.xml.transform.TransformerException
451    */
nodelist()452   public NodeList nodelist() throws javax.xml.transform.TransformerException
453   {
454 
455     error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
456           new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeList!");
457 
458     return null;
459   }
460 
461 
462   /**
463    * Cast result object to a nodelist. Always issues an error.
464    *
465    * @return The object as a NodeSetDTM.
466    *
467    * @throws javax.xml.transform.TransformerException
468    */
mutableNodeset()469   public NodeSetDTM mutableNodeset()
470           throws javax.xml.transform.TransformerException
471   {
472 
473     error(XPATHErrorResources.ER_CANT_CONVERT_TO_MUTABLENODELIST,
474           new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeSetDTM!");
475 
476     return (NodeSetDTM) m_obj;
477   }
478 
479   /**
480    * Cast object to type t.
481    *
482    * @param t Type of object to cast this to
483    * @param support XPath context to use for the conversion
484    *
485    * @return This object as the given type t
486    *
487    * @throws javax.xml.transform.TransformerException
488    */
castToType(int t, XPathContext support)489   public Object castToType(int t, XPathContext support)
490           throws javax.xml.transform.TransformerException
491   {
492 
493     Object result;
494 
495     switch (t)
496     {
497     case CLASS_STRING :
498       result = str();
499       break;
500     case CLASS_NUMBER :
501       result = new Double(num());
502       break;
503     case CLASS_NODESET :
504       result = iter();
505       break;
506     case CLASS_BOOLEAN :
507       result = new Boolean(bool());
508       break;
509     case CLASS_UNKNOWN :
510       result = m_obj;
511       break;
512 
513     // %TBD%  What to do here?
514     //    case CLASS_RTREEFRAG :
515     //      result = rtree(support);
516     //      break;
517     default :
518       error(XPATHErrorResources.ER_CANT_CONVERT_TO_TYPE,
519             new Object[]{ getTypeString(),
520                           Integer.toString(t) });  //"Can not convert "+getTypeString()+" to a type#"+t);
521 
522       result = null;
523     }
524 
525     return result;
526   }
527 
528   /**
529    * Tell if one object is less than the other.
530    *
531    * @param obj2 Object to compare this to
532    *
533    * @return True if this object is less than the given object
534    *
535    * @throws javax.xml.transform.TransformerException
536    */
lessThan(XObject obj2)537   public boolean lessThan(XObject obj2)
538           throws javax.xml.transform.TransformerException
539   {
540 
541     // In order to handle the 'all' semantics of
542     // nodeset comparisons, we always call the
543     // nodeset function.  Because the arguments
544     // are backwards, we call the opposite comparison
545     // function.
546     if (obj2.getType() == XObject.CLASS_NODESET)
547       return obj2.greaterThan(this);
548 
549     return this.num() < obj2.num();
550   }
551 
552   /**
553    * Tell if one object is less than or equal to the other.
554    *
555    * @param obj2 Object to compare this to
556    *
557    * @return True if this object is less than or equal to the given object
558    *
559    * @throws javax.xml.transform.TransformerException
560    */
lessThanOrEqual(XObject obj2)561   public boolean lessThanOrEqual(XObject obj2)
562           throws javax.xml.transform.TransformerException
563   {
564 
565     // In order to handle the 'all' semantics of
566     // nodeset comparisons, we always call the
567     // nodeset function.  Because the arguments
568     // are backwards, we call the opposite comparison
569     // function.
570     if (obj2.getType() == XObject.CLASS_NODESET)
571       return obj2.greaterThanOrEqual(this);
572 
573     return this.num() <= obj2.num();
574   }
575 
576   /**
577    * Tell if one object is greater than the other.
578    *
579    * @param obj2 Object to compare this to
580    *
581    * @return True if this object is greater than the given object
582    *
583    * @throws javax.xml.transform.TransformerException
584    */
greaterThan(XObject obj2)585   public boolean greaterThan(XObject obj2)
586           throws javax.xml.transform.TransformerException
587   {
588 
589     // In order to handle the 'all' semantics of
590     // nodeset comparisons, we always call the
591     // nodeset function.  Because the arguments
592     // are backwards, we call the opposite comparison
593     // function.
594     if (obj2.getType() == XObject.CLASS_NODESET)
595       return obj2.lessThan(this);
596 
597     return this.num() > obj2.num();
598   }
599 
600   /**
601    * Tell if one object is greater than or equal to the other.
602    *
603    * @param obj2 Object to compare this to
604    *
605    * @return True if this object is greater than or equal to the given object
606    *
607    * @throws javax.xml.transform.TransformerException
608    */
greaterThanOrEqual(XObject obj2)609   public boolean greaterThanOrEqual(XObject obj2)
610           throws javax.xml.transform.TransformerException
611   {
612 
613     // In order to handle the 'all' semantics of
614     // nodeset comparisons, we always call the
615     // nodeset function.  Because the arguments
616     // are backwards, we call the opposite comparison
617     // function.
618     if (obj2.getType() == XObject.CLASS_NODESET)
619       return obj2.lessThanOrEqual(this);
620 
621     return this.num() >= obj2.num();
622   }
623 
624   /**
625    * Tell if two objects are functionally equal.
626    *
627    * @param obj2 Object to compare this to
628    *
629    * @return True if this object is equal to the given object
630    *
631    * @throws javax.xml.transform.TransformerException
632    */
equals(XObject obj2)633   public boolean equals(XObject obj2)
634   {
635 
636     // In order to handle the 'all' semantics of
637     // nodeset comparisons, we always call the
638     // nodeset function.
639     if (obj2.getType() == XObject.CLASS_NODESET)
640       return obj2.equals(this);
641 
642     if (null != m_obj)
643     {
644       return m_obj.equals(obj2.m_obj);
645     }
646     else
647     {
648       return obj2.m_obj == null;
649     }
650   }
651 
652   /**
653    * Tell if two objects are functionally not equal.
654    *
655    * @param obj2 Object to compare this to
656    *
657    * @return True if this object is not equal to the given object
658    *
659    * @throws javax.xml.transform.TransformerException
660    */
notEquals(XObject obj2)661   public boolean notEquals(XObject obj2)
662           throws javax.xml.transform.TransformerException
663   {
664 
665     // In order to handle the 'all' semantics of
666     // nodeset comparisons, we always call the
667     // nodeset function.
668     if (obj2.getType() == XObject.CLASS_NODESET)
669       return obj2.notEquals(this);
670 
671     return !equals(obj2);
672   }
673 
674   /**
675    * Tell the user of an error, and probably throw an
676    * exception.
677    *
678    * @param msg Error message to issue
679    *
680    * @throws javax.xml.transform.TransformerException
681    */
error(String msg)682   protected void error(String msg)
683           throws javax.xml.transform.TransformerException
684   {
685     error(msg, null);
686   }
687 
688   /**
689    * Tell the user of an error, and probably throw an
690    * exception.
691    *
692    * @param msg Error message to issue
693    * @param args Arguments to use in the message
694    *
695    * @throws javax.xml.transform.TransformerException
696    */
error(String msg, Object[] args)697   protected void error(String msg, Object[] args)
698           throws javax.xml.transform.TransformerException
699   {
700 
701     String fmsg = XSLMessages.createXPATHMessage(msg, args);
702 
703     // boolean shouldThrow = support.problem(m_support.XPATHPROCESSOR,
704     //                                      m_support.ERROR,
705     //                                      null,
706     //                                      null, fmsg, 0, 0);
707     // if(shouldThrow)
708     {
709       throw new XPathException(fmsg, this);
710     }
711   }
712 
713 
714   /**
715    * XObjects should not normally need to fix up variables.
716    */
fixupVariables(java.util.Vector vars, int globalsSize)717   public void fixupVariables(java.util.Vector vars, int globalsSize)
718   {
719     // no-op
720   }
721 
722 
723   /**
724    * Cast result object to a string.
725    *
726    *
727    * NEEDSDOC @param fsb
728    * @return The string this wraps or the empty string if null
729    */
appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)730   public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
731   {
732     fsb.append(str());
733   }
734 
735   /**
736    * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
737    */
callVisitors(ExpressionOwner owner, XPathVisitor visitor)738   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
739   {
740   	assertion(false, "callVisitors should not be called for this object!!!");
741   }
742   /**
743    * @see Expression#deepEquals(Expression)
744    */
deepEquals(Expression expr)745   public boolean deepEquals(Expression expr)
746   {
747   	if(!isSameClass(expr))
748   		return false;
749 
750   	// If equals at the expression level calls deepEquals, I think we're
751   	// still safe from infinite recursion since this object overrides
752   	// equals.  I hope.
753   	if(!this.equals((XObject)expr))
754   		return false;
755 
756   	return true;
757   }
758 
759 }
760