Home | History | Annotate | Download | only in objects
      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    */
     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