001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: XObject.java 1225284 2011-12-28 18:56:49Z mrglavas $
020     */
021    package org.apache.xpath.objects;
022    
023    import java.io.Serializable;
024    
025    import org.apache.xalan.res.XSLMessages;
026    import org.apache.xml.dtm.DTM;
027    import org.apache.xml.dtm.DTMIterator;
028    import org.apache.xml.utils.XMLString;
029    import org.apache.xpath.Expression;
030    import org.apache.xpath.ExpressionOwner;
031    import org.apache.xpath.NodeSetDTM;
032    import org.apache.xpath.XPathContext;
033    import org.apache.xpath.XPathException;
034    import org.apache.xpath.XPathVisitor;
035    import org.apache.xpath.res.XPATHErrorResources;
036    
037    import org.w3c.dom.DocumentFragment;
038    import org.w3c.dom.NodeList;
039    import org.w3c.dom.traversal.NodeIterator;
040    
041    /**
042     * This class represents an XPath object, and is capable of
043     * converting the object to various types, such as a string.
044     * This class acts as the base class to other XPath type objects,
045     * such as XString, and provides polymorphic casting capabilities.
046     * @xsl.usage general
047     */
048    public class XObject extends Expression implements Serializable, Cloneable
049    {
050        static final long serialVersionUID = -821887098985662951L;
051    
052      /**
053       * The java object which this object wraps.
054       *  @serial  
055       */
056      protected Object m_obj;  // This may be NULL!!!
057    
058      /**
059       * Create an XObject.
060       */
061      public XObject(){}
062    
063      /**
064       * Create an XObject.
065       *
066       * @param obj Can be any object, should be a specific type
067       * for derived classes, or null.
068       */
069      public XObject(Object obj)
070      {
071        setObject(obj);
072      }
073      
074      protected void setObject(Object obj) {
075          m_obj = obj;
076      }
077    
078      /**
079       * For support of literal objects in xpaths.
080       *
081       * @param xctxt The XPath execution context.
082       *
083       * @return This object.
084       *
085       * @throws javax.xml.transform.TransformerException
086       */
087      public XObject execute(XPathContext xctxt)
088              throws javax.xml.transform.TransformerException
089      {
090        return this;
091      }
092    
093      /**
094       * Specify if it's OK for detach to release the iterator for reuse.
095       * This function should be called with a value of false for objects that are 
096       * stored in variables.
097       * Calling this with a value of false on a XNodeSet will cause the nodeset 
098       * to be cached.
099       *
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 = bool() ? Boolean.TRUE : Boolean.FALSE;
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    }