1 /*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * @LastModified: Oct 2017
4 */
5 /*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements. See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 package com.sun.org.apache.xpath.internal.objects;
23
24 import com.sun.org.apache.xalan.internal.res.XSLMessages;
25 import com.sun.org.apache.xml.internal.dtm.DTM;
26 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
27 import com.sun.org.apache.xml.internal.utils.QName;
28 import com.sun.org.apache.xml.internal.utils.XMLString;
29 import com.sun.org.apache.xpath.internal.Expression;
30 import com.sun.org.apache.xpath.internal.ExpressionOwner;
31 import com.sun.org.apache.xpath.internal.NodeSetDTM;
32 import com.sun.org.apache.xpath.internal.XPathContext;
33 import com.sun.org.apache.xpath.internal.XPathException;
34 import com.sun.org.apache.xpath.internal.XPathVisitor;
35 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
36 import java.io.Serializable;
37 import java.util.List;
38 import org.w3c.dom.DocumentFragment;
39 import org.w3c.dom.NodeList;
40 import org.w3c.dom.traversal.NodeIterator;
41
42 /**
43 * This class represents an XPath object, and is capable of
44 * converting the object to various types, such as a string.
45 * This class acts as the base class to other XPath type objects,
46 * such as XString, and provides polymorphic casting capabilities.
47 * @xsl.usage general
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 }
--- EOF ---