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