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