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 }