001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: XObject.java,v 1.27 2005/01/23 01:08:21 mcnamara Exp $
018: */
019: package org.apache.xpath.objects;
020:
021: import java.io.Serializable;
022:
023: import org.apache.xalan.res.XSLMessages;
024: import org.apache.xml.dtm.DTM;
025: import org.apache.xml.dtm.DTMIterator;
026: import org.apache.xml.utils.XMLString;
027: import org.apache.xpath.Expression;
028: import org.apache.xpath.ExpressionOwner;
029: import org.apache.xpath.NodeSetDTM;
030: import org.apache.xpath.XPathContext;
031: import org.apache.xpath.XPathException;
032: import org.apache.xpath.XPathVisitor;
033: import org.apache.xpath.res.XPATHErrorResources;
034:
035: import org.w3c.dom.DocumentFragment;
036: import org.w3c.dom.NodeList;
037: import org.w3c.dom.traversal.NodeIterator;
038:
039: /**
040: * This class represents an XPath object, and is capable of
041: * converting the object to various types, such as a string.
042: * This class acts as the base class to other XPath type objects,
043: * such as XString, and provides polymorphic casting capabilities.
044: * @xsl.usage general
045: */
046: public class XObject extends Expression implements Serializable,
047: Cloneable {
048: static final long serialVersionUID = -821887098985662951L;
049:
050: /**
051: * The java object which this object wraps.
052: * @serial
053: */
054: protected Object m_obj; // This may be NULL!!!
055:
056: /**
057: * Create an XObject.
058: */
059: public XObject() {
060: }
061:
062: /**
063: * Create an XObject.
064: *
065: * @param obj Can be any object, should be a specific type
066: * for derived classes, or null.
067: */
068: public XObject(Object obj) {
069: m_obj = obj;
070: }
071:
072: /**
073: * For support of literal objects in xpaths.
074: *
075: * @param xctxt The XPath execution context.
076: *
077: * @return This object.
078: *
079: * @throws javax.xml.transform.TransformerException
080: */
081: public XObject execute(XPathContext xctxt)
082: throws javax.xml.transform.TransformerException {
083: return this ;
084: }
085:
086: /**
087: * Specify if it's OK for detach to release the iterator for reuse.
088: * This function should be called with a value of false for objects that are
089: * stored in variables.
090: * Calling this with a value of false on a XNodeSet will cause the nodeset
091: * to be cached.
092: *
093: * @param allowRelease true if it is OK for detach to release this iterator
094: * for pooling.
095: */
096: public void allowDetachToRelease(boolean allowRelease) {
097: }
098:
099: /**
100: * Detaches the <code>DTMIterator</code> from the set which it iterated
101: * over, releasing any computational resources and placing the iterator
102: * in the INVALID state. After <code>detach</code> has been invoked,
103: * calls to <code>nextNode</code> or <code>previousNode</code> will
104: * raise a runtime exception.
105: */
106: public void detach() {
107: }
108:
109: /**
110: * Forces the object to release it's resources. This is more harsh than
111: * detach().
112: */
113: public void destruct() {
114:
115: if (null != m_obj) {
116: allowDetachToRelease(true);
117: detach();
118:
119: m_obj = null;
120: }
121: }
122:
123: /**
124: * Reset for fresh reuse.
125: */
126: public void reset() {
127: }
128:
129: /**
130: * Directly call the
131: * characters method on the passed ContentHandler for the
132: * string-value. Multiple calls to the
133: * ContentHandler's characters methods may well occur for a single call to
134: * this method.
135: *
136: * @param ch A non-null reference to a ContentHandler.
137: *
138: * @throws org.xml.sax.SAXException
139: */
140: public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
141: throws org.xml.sax.SAXException {
142: xstr().dispatchCharactersEvents(ch);
143: }
144:
145: /**
146: * Create the right XObject based on the type of the object passed. This
147: * function can not make an XObject that exposes DOM Nodes, NodeLists, and
148: * NodeIterators to the XSLT stylesheet as node-sets.
149: *
150: * @param val The java object which this object will wrap.
151: *
152: * @return the right XObject based on the type of the object passed.
153: */
154: static public XObject create(Object val) {
155: return XObjectFactory.create(val);
156: }
157:
158: /**
159: * Create the right XObject based on the type of the object passed.
160: * This function <emph>can</emph> make an XObject that exposes DOM Nodes, NodeLists, and
161: * NodeIterators to the XSLT stylesheet as node-sets.
162: *
163: * @param val The java object which this object will wrap.
164: * @param xctxt The XPath context.
165: *
166: * @return the right XObject based on the type of the object passed.
167: */
168: static public XObject create(Object val, XPathContext xctxt) {
169: return XObjectFactory.create(val, xctxt);
170: }
171:
172: /** Constant for NULL object type */
173: public static final int CLASS_NULL = -1;
174:
175: /** Constant for UNKNOWN object type */
176: public static final int CLASS_UNKNOWN = 0;
177:
178: /** Constant for BOOLEAN object type */
179: public static final int CLASS_BOOLEAN = 1;
180:
181: /** Constant for NUMBER object type */
182: public static final int CLASS_NUMBER = 2;
183:
184: /** Constant for STRING object type */
185: public static final int CLASS_STRING = 3;
186:
187: /** Constant for NODESET object type */
188: public static final int CLASS_NODESET = 4;
189:
190: /** Constant for RESULT TREE FRAGMENT object type */
191: public static final int CLASS_RTREEFRAG = 5;
192:
193: /** Represents an unresolved variable type as an integer. */
194: public static final int CLASS_UNRESOLVEDVARIABLE = 600;
195:
196: /**
197: * Tell what kind of class this is.
198: *
199: * @return CLASS_UNKNOWN
200: */
201: public int getType() {
202: return CLASS_UNKNOWN;
203: }
204:
205: /**
206: * Given a request type, return the equivalent string.
207: * For diagnostic purposes.
208: *
209: * @return type string "#UNKNOWN" + object class name
210: */
211: public String getTypeString() {
212: return "#UNKNOWN (" + object().getClass().getName() + ")";
213: }
214:
215: /**
216: * Cast result object to a number. Always issues an error.
217: *
218: * @return 0.0
219: *
220: * @throws javax.xml.transform.TransformerException
221: */
222: public double num() throws javax.xml.transform.TransformerException {
223:
224: error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
225: new Object[] { getTypeString() }); //"Can not convert "+getTypeString()+" to a number");
226:
227: return 0.0;
228: }
229:
230: /**
231: * Cast result object to a number, but allow side effects, such as the
232: * incrementing of an iterator.
233: *
234: * @return numeric value of the string conversion from the
235: * next node in the NodeSetDTM, or NAN if no node was found
236: */
237: public double numWithSideEffects()
238: throws javax.xml.transform.TransformerException {
239: return num();
240: }
241:
242: /**
243: * Cast result object to a boolean. Always issues an error.
244: *
245: * @return false
246: *
247: * @throws javax.xml.transform.TransformerException
248: */
249: public boolean bool()
250: throws javax.xml.transform.TransformerException {
251:
252: error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
253: new Object[] { getTypeString() }); //"Can not convert "+getTypeString()+" to a number");
254:
255: return false;
256: }
257:
258: /**
259: * Cast result object to a boolean, but allow side effects, such as the
260: * incrementing of an iterator.
261: *
262: * @return True if there is a next node in the nodeset
263: */
264: public boolean boolWithSideEffects()
265: throws javax.xml.transform.TransformerException {
266: return bool();
267: }
268:
269: /**
270: * Cast result object to a string.
271: *
272: * @return The string this wraps or the empty string if null
273: */
274: public XMLString xstr() {
275: return XMLStringFactoryImpl.getFactory().newstr(str());
276: }
277:
278: /**
279: * Cast result object to a string.
280: *
281: * @return The object as a string
282: */
283: public String str() {
284: return (m_obj != null) ? m_obj.toString() : "";
285: }
286:
287: /**
288: * Return the string representation of the object
289: *
290: *
291: * @return the string representation of the object
292: */
293: public String toString() {
294: return str();
295: }
296:
297: /**
298: * Cast result object to a result tree fragment.
299: *
300: * @param support XPath context to use for the conversion
301: *
302: * @return the objec as a result tree fragment.
303: */
304: public int rtf(XPathContext support) {
305:
306: int result = rtf();
307:
308: if (DTM.NULL == result) {
309: DTM frag = support.createDocumentFragment();
310:
311: // %OPT%
312: frag.appendTextChild(str());
313:
314: result = frag.getDocument();
315: }
316:
317: return result;
318: }
319:
320: /**
321: * Cast result object to a result tree fragment.
322: *
323: * @param support XPath context to use for the conversion
324: *
325: * @return the objec as a result tree fragment.
326: */
327: public DocumentFragment rtree(XPathContext support) {
328: DocumentFragment docFrag = null;
329: int result = rtf();
330:
331: if (DTM.NULL == result) {
332: DTM frag = support.createDocumentFragment();
333:
334: // %OPT%
335: frag.appendTextChild(str());
336:
337: docFrag = (DocumentFragment) frag.getNode(frag
338: .getDocument());
339: } else {
340: DTM frag = support.getDTM(result);
341: docFrag = (DocumentFragment) frag.getNode(frag
342: .getDocument());
343: }
344:
345: return docFrag;
346: }
347:
348: /**
349: * For functions to override.
350: *
351: * @return null
352: */
353: public DocumentFragment rtree() {
354: return null;
355: }
356:
357: /**
358: * For functions to override.
359: *
360: * @return null
361: */
362: public int rtf() {
363: return DTM.NULL;
364: }
365:
366: /**
367: * Return a java object that's closest to the representation
368: * that should be handed to an extension.
369: *
370: * @return The object that this class wraps
371: */
372: public Object object() {
373: return m_obj;
374: }
375:
376: /**
377: * Cast result object to a nodelist. Always issues an error.
378: *
379: * @return null
380: *
381: * @throws javax.xml.transform.TransformerException
382: */
383: public DTMIterator iter()
384: throws javax.xml.transform.TransformerException {
385:
386: error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
387: new Object[] { getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!");
388:
389: return null;
390: }
391:
392: /**
393: * Get a fresh copy of the object. For use with variables.
394: *
395: * @return This object, unless overridden by subclass.
396: */
397: public XObject getFresh() {
398: return this ;
399: }
400:
401: /**
402: * Cast result object to a nodelist. Always issues an error.
403: *
404: * @return null
405: *
406: * @throws javax.xml.transform.TransformerException
407: */
408: public NodeIterator nodeset()
409: throws javax.xml.transform.TransformerException {
410:
411: error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
412: new Object[] { getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!");
413:
414: return null;
415: }
416:
417: /**
418: * Cast result object to a nodelist. Always issues an error.
419: *
420: * @return null
421: *
422: * @throws javax.xml.transform.TransformerException
423: */
424: public NodeList nodelist()
425: throws javax.xml.transform.TransformerException {
426:
427: error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
428: new Object[] { getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!");
429:
430: return null;
431: }
432:
433: /**
434: * Cast result object to a nodelist. Always issues an error.
435: *
436: * @return The object as a NodeSetDTM.
437: *
438: * @throws javax.xml.transform.TransformerException
439: */
440: public NodeSetDTM mutableNodeset()
441: throws javax.xml.transform.TransformerException {
442:
443: error(XPATHErrorResources.ER_CANT_CONVERT_TO_MUTABLENODELIST,
444: new Object[] { getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeSetDTM!");
445:
446: return (NodeSetDTM) m_obj;
447: }
448:
449: /**
450: * Cast object to type t.
451: *
452: * @param t Type of object to cast this to
453: * @param support XPath context to use for the conversion
454: *
455: * @return This object as the given type t
456: *
457: * @throws javax.xml.transform.TransformerException
458: */
459: public Object castToType(int t, XPathContext support)
460: throws javax.xml.transform.TransformerException {
461:
462: Object result;
463:
464: switch (t) {
465: case CLASS_STRING:
466: result = str();
467: break;
468: case CLASS_NUMBER:
469: result = new Double(num());
470: break;
471: case CLASS_NODESET:
472: result = iter();
473: break;
474: case CLASS_BOOLEAN:
475: result = new Boolean(bool());
476: break;
477: case CLASS_UNKNOWN:
478: result = m_obj;
479: break;
480:
481: // %TBD% What to do here?
482: // case CLASS_RTREEFRAG :
483: // result = rtree(support);
484: // break;
485: default:
486: error(
487: XPATHErrorResources.ER_CANT_CONVERT_TO_TYPE,
488: new Object[] { getTypeString(), Integer.toString(t) }); //"Can not convert "+getTypeString()+" to a type#"+t);
489:
490: result = null;
491: }
492:
493: return result;
494: }
495:
496: /**
497: * Tell if one object is less than the other.
498: *
499: * @param obj2 Object to compare this to
500: *
501: * @return True if this object is less than the given object
502: *
503: * @throws javax.xml.transform.TransformerException
504: */
505: public boolean lessThan(XObject obj2)
506: throws javax.xml.transform.TransformerException {
507:
508: // In order to handle the 'all' semantics of
509: // nodeset comparisons, we always call the
510: // nodeset function. Because the arguments
511: // are backwards, we call the opposite comparison
512: // function.
513: if (obj2.getType() == XObject.CLASS_NODESET)
514: return obj2.greaterThan(this );
515:
516: return this .num() < obj2.num();
517: }
518:
519: /**
520: * Tell if one object is less than or equal to the other.
521: *
522: * @param obj2 Object to compare this to
523: *
524: * @return True if this object is less than or equal to the given object
525: *
526: * @throws javax.xml.transform.TransformerException
527: */
528: public boolean lessThanOrEqual(XObject obj2)
529: throws javax.xml.transform.TransformerException {
530:
531: // In order to handle the 'all' semantics of
532: // nodeset comparisons, we always call the
533: // nodeset function. Because the arguments
534: // are backwards, we call the opposite comparison
535: // function.
536: if (obj2.getType() == XObject.CLASS_NODESET)
537: return obj2.greaterThanOrEqual(this );
538:
539: return this .num() <= obj2.num();
540: }
541:
542: /**
543: * Tell if one object is greater than the other.
544: *
545: * @param obj2 Object to compare this to
546: *
547: * @return True if this object is greater than the given object
548: *
549: * @throws javax.xml.transform.TransformerException
550: */
551: public boolean greaterThan(XObject obj2)
552: throws javax.xml.transform.TransformerException {
553:
554: // In order to handle the 'all' semantics of
555: // nodeset comparisons, we always call the
556: // nodeset function. Because the arguments
557: // are backwards, we call the opposite comparison
558: // function.
559: if (obj2.getType() == XObject.CLASS_NODESET)
560: return obj2.lessThan(this );
561:
562: return this .num() > obj2.num();
563: }
564:
565: /**
566: * Tell if one object is greater than or equal to the other.
567: *
568: * @param obj2 Object to compare this to
569: *
570: * @return True if this object is greater than or equal to the given object
571: *
572: * @throws javax.xml.transform.TransformerException
573: */
574: public boolean greaterThanOrEqual(XObject obj2)
575: throws javax.xml.transform.TransformerException {
576:
577: // In order to handle the 'all' semantics of
578: // nodeset comparisons, we always call the
579: // nodeset function. Because the arguments
580: // are backwards, we call the opposite comparison
581: // function.
582: if (obj2.getType() == XObject.CLASS_NODESET)
583: return obj2.lessThanOrEqual(this );
584:
585: return this .num() >= obj2.num();
586: }
587:
588: /**
589: * Tell if two objects are functionally equal.
590: *
591: * @param obj2 Object to compare this to
592: *
593: * @return True if this object is equal to the given object
594: *
595: * @throws javax.xml.transform.TransformerException
596: */
597: public boolean equals(XObject obj2) {
598:
599: // In order to handle the 'all' semantics of
600: // nodeset comparisons, we always call the
601: // nodeset function.
602: if (obj2.getType() == XObject.CLASS_NODESET)
603: return obj2.equals(this );
604:
605: if (null != m_obj) {
606: return m_obj.equals(obj2.m_obj);
607: } else {
608: return obj2.m_obj == null;
609: }
610: }
611:
612: /**
613: * Tell if two objects are functionally not equal.
614: *
615: * @param obj2 Object to compare this to
616: *
617: * @return True if this object is not equal to the given object
618: *
619: * @throws javax.xml.transform.TransformerException
620: */
621: public boolean notEquals(XObject obj2)
622: throws javax.xml.transform.TransformerException {
623:
624: // In order to handle the 'all' semantics of
625: // nodeset comparisons, we always call the
626: // nodeset function.
627: if (obj2.getType() == XObject.CLASS_NODESET)
628: return obj2.notEquals(this );
629:
630: return !equals(obj2);
631: }
632:
633: /**
634: * Tell the user of an error, and probably throw an
635: * exception.
636: *
637: * @param msg Error message to issue
638: *
639: * @throws javax.xml.transform.TransformerException
640: */
641: protected void error(String msg)
642: throws javax.xml.transform.TransformerException {
643: error(msg, null);
644: }
645:
646: /**
647: * Tell the user of an error, and probably throw an
648: * exception.
649: *
650: * @param msg Error message to issue
651: * @param args Arguments to use in the message
652: *
653: * @throws javax.xml.transform.TransformerException
654: */
655: protected void error(String msg, Object[] args)
656: throws javax.xml.transform.TransformerException {
657:
658: String fmsg = XSLMessages.createXPATHMessage(msg, args);
659:
660: // boolean shouldThrow = support.problem(m_support.XPATHPROCESSOR,
661: // m_support.ERROR,
662: // null,
663: // null, fmsg, 0, 0);
664: // if(shouldThrow)
665: {
666: throw new XPathException(fmsg, this );
667: }
668: }
669:
670: /**
671: * XObjects should not normally need to fix up variables.
672: */
673: public void fixupVariables(java.util.Vector vars, int globalsSize) {
674: // no-op
675: }
676:
677: /**
678: * Cast result object to a string.
679: *
680: *
681: * NEEDSDOC @param fsb
682: * @return The string this wraps or the empty string if null
683: */
684: public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb) {
685: fsb.append(str());
686: }
687:
688: /**
689: * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
690: */
691: public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) {
692: assertion(false,
693: "callVisitors should not be called for this object!!!");
694: }
695:
696: /**
697: * @see Expression#deepEquals(Expression)
698: */
699: public boolean deepEquals(Expression expr) {
700: if (!isSameClass(expr))
701: return false;
702:
703: // If equals at the expression level calls deepEquals, I think we're
704: // still safe from infinite recursion since this object overrides
705: // equals. I hope.
706: if (!this .equals((XObject) expr))
707: return false;
708:
709: return true;
710: }
711:
712: }
|