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 */
21package org.apache.xpath.objects;
22
23import java.io.Serializable;
24
25import org.apache.xalan.res.XSLMessages;
26import org.apache.xml.dtm.DTM;
27import org.apache.xml.dtm.DTMIterator;
28import org.apache.xml.utils.XMLString;
29import org.apache.xpath.Expression;
30import org.apache.xpath.ExpressionOwner;
31import org.apache.xpath.NodeSetDTM;
32import org.apache.xpath.XPathContext;
33import org.apache.xpath.XPathException;
34import org.apache.xpath.XPathVisitor;
35import org.apache.xpath.res.XPATHErrorResources;
36
37import org.w3c.dom.DocumentFragment;
38import org.w3c.dom.NodeList;
39import 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 */
48public 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   */
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   */
69  public XObject(Object obj)
70  {
71    setObject(obj);
72  }
73
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   */
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   */
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   */
112  public void detach(){}
113
114  /**
115   * Forces the object to release it's resources.  This is more harsh than
116   * detach().
117   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
376  public DocumentFragment rtree()
377  {
378    return null;
379  }
380
381  /**
382   * For functions to override.
383   *
384   * @return null
385   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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