KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > iapi > types > XML


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.XML
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.iapi.types;
23
24 import org.apache.derby.iapi.error.StandardException;
25
26 import org.apache.derby.iapi.services.cache.ClassSize;
27 import org.apache.derby.iapi.services.io.ArrayInputStream;
28 import org.apache.derby.iapi.services.io.StoredFormatIds;
29 import org.apache.derby.iapi.services.io.StreamStorable;
30 import org.apache.derby.iapi.services.io.Storable;
31 import org.apache.derby.iapi.services.io.TypedFormat;
32 import org.apache.derby.iapi.services.loader.ClassInspector;
33 import org.apache.derby.iapi.services.sanity.SanityManager;
34
35 import org.apache.derby.iapi.types.DataValueDescriptor;
36 import org.apache.derby.iapi.types.StringDataValue;
37 import org.apache.derby.iapi.types.BooleanDataValue;
38
39 import org.apache.derby.iapi.reference.SQLState;
40
41 import java.sql.ResultSet JavaDoc;
42 import java.sql.SQLException JavaDoc;
43 import java.sql.Types JavaDoc;
44
45 import java.io.InputStream JavaDoc;
46 import java.io.IOException JavaDoc;
47 import java.io.ObjectOutput JavaDoc;
48 import java.io.ObjectInput JavaDoc;
49 import java.io.StringReader JavaDoc;
50
51 import java.util.ArrayList JavaDoc;
52
53 /**
54  * This type implements the XMLDataValue interface and thus is
55  * the type on which all XML related operations are executed.
56  *
57  * The first and simplest XML store implementation is a UTF-8
58  * based one--all XML data is stored on disk as a UTF-8 string,
59  * just like the other Derby string types. In order to make
60  * it possible for smarter XML implementations to exist in
61  * the future, this class always writes an "XML implementation
62  * id" to disk before writing the rest of its data. When
63  * reading the data, the impl id is read first and serves
64  * as an indicator of how the rest of the data should be
65  * read.
66  *
67  * So long as there's only one implementation (UTF-8)
68  * the impl id can be ignored; but when smarter implementations
69  * are written, the impl id will be the key to figuring out
70  * how an XML value should be read, written, and processed.
71  */

72 public class XML
73     extends DataType implements XMLDataValue, StreamStorable
74 {
75     // Id for this implementation. Should be unique
76
// across all XML type implementations.
77
protected static final short UTF8_IMPL_ID = 0;
78
79     // Guess at how much memory this type will take.
80
private static final int BASE_MEMORY_USAGE =
81         ClassSize.estimateBaseFromCatalog(XML.class);
82
83     // Some syntax-related constants used to determine
84
// operator behavior.
85
public static final short XQ_PASS_BY_REF = 1;
86     public static final short XQ_PASS_BY_VALUE = 2;
87     public static final short XQ_RETURN_SEQUENCE = 3;
88     public static final short XQ_RETURN_CONTENT = 4;
89     public static final short XQ_EMPTY_ON_EMPTY = 5;
90     public static final short XQ_NULL_ON_EMPTY = 6;
91
92     /* Per SQL/XML[2006] 4.2.2, there are several different
93      * XML "types" defined through use of primary and secondary
94      * "type modifiers". For Derby we only support two kinds:
95      *
96      * XML(DOCUMENT(ANY)) : A valid and well-formed XML
97      * document as defined by W3C, meaning that there is
98      * exactly one root element node. This is the only
99      * type of XML that can be stored into a Derby XML
100      * column. This is also the type returned by a call
101      * to XMLPARSE since we require the DOCUMENT keyword.
102      *
103      * XML(SEQUENCE): A sequence of items (could be nodes or
104      * atomic values). This is the type returned from an
105      * XMLQUERY operation. Any node that is XML(DOCUMENT(ANY))
106      * is also XML(SEQUENCE). Note that an XML(SEQUENCE)
107      * value is *only* storable into a Derby XML column
108      * if it is also an XML(DOCUMENT(ANY)). See the
109      * normalize method below for the code that enforces
110      * this.
111      */

112     public static final int XML_DOC_ANY = 0;
113     public static final int XML_SEQUENCE = 1;
114
115     // The fully-qualified type for this XML value.
116
private int xType;
117
118     // The actual XML data in this implementation is just a simple
119
// string, so this class really just wraps a SQLChar and
120
// defers most calls to the corresponding calls on that
121
// SQLChar. Note that, even though a SQLChar is the
122
// underlying implementation, an XML value is nonetheless
123
// NOT considered comparable nor compatible with any of
124
// Derby string types.
125
private SQLChar xmlStringValue;
126
127     /*
128      * Status variable used to verify that user's classpath contains
129      * required classes for accessing/operating on XML data values.
130      */

131     private static String JavaDoc xmlReqCheck = null;
132
133     /*
134      * Whether or not this XML value corresponds to a sequence
135      * that has one or more top-level ("parentless") attribute
136      * nodes. If so then we have to throw an error if the user
137      * attempts to serialize this value, per XML serialization
138      * rules.
139      */

140     private boolean containsTopLevelAttr;
141
142     /**
143      * Default constructor.
144      */

145     public XML()
146     {
147         xmlStringValue = null;
148         xType = -1;
149         containsTopLevelAttr = false;
150     }
151
152     /**
153      * Private constructor used for the getClone() method.
154      * Returns a new instance of XML whose fields are clones
155      * of the values received.
156      *
157      * @param val A SQLChar instance to clone and use for
158      * this XML value.
159      * @param xmlType Qualified XML type for "val"
160      * @param seqWithAttr Whether or not "val" corresponds to
161      * sequence with one or more top-level attribute nodes.
162      */

163     private XML(SQLChar val, int xmlType, boolean seqWithAttr)
164     {
165         xmlStringValue = (val == null ? null : (SQLChar)val.getClone());
166         setXType(xmlType);
167         if (seqWithAttr)
168             markAsHavingTopLevelAttr();
169     }
170
171     /* ****
172      * DataValueDescriptor interface.
173      * */

174
175     /**
176      * @see DataValueDescriptor#getClone
177      */

178     public DataValueDescriptor getClone()
179     {
180         return new XML(xmlStringValue, getXType(), hasTopLevelAttr());
181     }
182
183     /**
184      * @see DataValueDescriptor#getNewNull
185      */

186     public DataValueDescriptor getNewNull()
187     {
188         return new XML();
189     }
190
191     /**
192      * @see DataValueDescriptor#getTypeName
193      */

194     public String JavaDoc getTypeName()
195     {
196         return TypeId.XML_NAME;
197     }
198
199     /**
200      * @see DataValueDescriptor#typePrecedence
201      */

202     public int typePrecedence()
203     {
204         return TypeId.XML_PRECEDENCE;
205     }
206
207     /**
208      * @see DataValueDescriptor#getString
209      */

210     public String JavaDoc getString() throws StandardException
211     {
212         return (xmlStringValue == null) ? null : xmlStringValue.getString();
213     }
214
215     /**
216      * @see DataValueDescriptor#getLength
217      */

218     public int getLength() throws StandardException
219     {
220         return ((xmlStringValue == null) ? 0 : xmlStringValue.getLength());
221     }
222
223     /**
224      * @see DataValueDescriptor#estimateMemoryUsage
225      */

226     public int estimateMemoryUsage()
227     {
228         int sz = BASE_MEMORY_USAGE;
229         if (xmlStringValue != null)
230             sz += xmlStringValue.estimateMemoryUsage();
231         return sz;
232     }
233
234     /**
235      * @see DataValueDescriptor#readExternalFromArray
236      */

237     public void readExternalFromArray(ArrayInputStream in)
238         throws IOException JavaDoc
239     {
240         if (xmlStringValue == null)
241             xmlStringValue = new SQLChar();
242
243         // Read the XML implementation id. Right now there's
244
// only one implementation (UTF-8 based), so we don't
245
// use this value. But if better implementations come
246
// up in the future, we'll have to use this impl id to
247
// figure out how to read the data.
248
in.readShort();
249
250         // Now just read the XML data as UTF-8.
251
xmlStringValue.readExternalFromArray(in);
252
253         // If we read it from disk then it must have type
254
// XML_DOC_ANY because that's all we allow to be
255
// written into an XML column.
256
setXType(XML_DOC_ANY);
257     }
258
259     /**
260      * @see DataType#setFrom
261      */

262     protected void setFrom(DataValueDescriptor theValue)
263         throws StandardException
264     {
265         String JavaDoc strVal = theValue.getString();
266         if (strVal == null)
267         {
268             xmlStringValue = null;
269
270             // Null is a valid value for DOCUMENT(ANY)
271
setXType(XML_DOC_ANY);
272             return;
273         }
274
275         // Here we just store the received value locally.
276
if (xmlStringValue == null)
277             xmlStringValue = new SQLChar();
278         xmlStringValue.setValue(strVal);
279
280         /*
281          * Assumption is that if theValue is not an XML
282          * value then the caller is aware of whether or
283          * not theValue constitutes a valid XML(DOCUMENT(ANY))
284          * and will behave accordingly (see in particular the
285          * XMLQuery method of this class, which calls the
286          * setValue() method of XMLDataValue which in turn
287          * brings us to this method).
288          */

289         if (theValue instanceof XMLDataValue)
290         {
291             setXType(((XMLDataValue)theValue).getXType());
292             if (((XMLDataValue)theValue).hasTopLevelAttr())
293                 markAsHavingTopLevelAttr();
294         }
295     }
296
297     /**
298      * @see DataValueDescriptor#setValueFromResultSet
299      */

300     public final void setValueFromResultSet(
301         ResultSet JavaDoc resultSet, int colNumber, boolean isNullable)
302         throws SQLException JavaDoc
303     {
304         if (xmlStringValue == null)
305             xmlStringValue = new SQLChar();
306         xmlStringValue.setValue(resultSet.getString(colNumber));
307     }
308
309     /**
310      * Compare two XML DataValueDescriptors. NOTE: This method
311      * should only be used by the database store for the purpose of
312      * index positioning--comparisons of XML type are not allowed
313      * from the language side of things. That said, all store
314      * wants to do is order the NULLs, so we don't actually
315      * have to do a full comparison. Just return an order
316      * value based on whether or not this XML value and the
317      * other XML value are null. As mentioned in the "compare"
318      * method of DataValueDescriptor, nulls are considered
319      * equal to other nulls and less than all other values.
320      *
321      * An example of when this method might be used is if the
322      * user executed a query like:
323      *
324      * select i from x_table where x_col is not null
325      *
326      * @see DataValueDescriptor#compare
327      */

328     public int compare(DataValueDescriptor other)
329         throws StandardException
330     {
331         if (SanityManager.DEBUG) {
332             SanityManager.ASSERT(other instanceof XMLDataValue,
333                 "Store should NOT have tried to compare an XML value " +
334                 "with a non-XML value.");
335         }
336
337         if (isNull()) {
338             if (other.isNull())
339             // both null, so call them 'equal'.
340
return 0;
341             // This XML is 'less than' the other.
342
return -1;
343         }
344
345         if (other.isNull())
346         // This XML is 'greater than' the other.
347
return 1;
348
349         // Two non-null values: we shouldn't ever get here,
350
// since that would necessitate a comparsion of XML
351
// values, which isn't allowed.
352
if (SanityManager.DEBUG) {
353             SanityManager.THROWASSERT(
354                 "Store tried to compare two non-null XML values, " +
355                 "which isn't allowed.");
356         }
357         return 0;
358     }
359
360     /**
361      * Normalization method - this method will always be called when
362      * storing an XML value into an XML column, for example, when
363      * inserting/updating. We always force normalization in this
364      * case because we need to make sure the qualified type of the
365      * value we're trying to store is XML_DOC_ANY--we don't allow
366      * anything else.
367      *
368      * @param desiredType The type to normalize the source column to
369      * @param source The value to normalize
370      *
371      * @exception StandardException Thrown if source is not
372      * XML_DOC_ANY.
373      */

374     public void normalize(
375                 DataTypeDescriptor desiredType,
376                 DataValueDescriptor source)
377                     throws StandardException
378     {
379         if (SanityManager.DEBUG) {
380             SanityManager.ASSERT(source instanceof XMLDataValue,
381                 "Tried to store non-XML value into XML column; " +
382                 "should have thrown error at compile time.");
383         }
384
385         if (((XMLDataValue)source).getXType() != XML_DOC_ANY) {
386             throw StandardException.newException(
387                 SQLState.LANG_NOT_AN_XML_DOCUMENT);
388         }
389
390         ((DataValueDescriptor) this).setValue(source);
391         return;
392
393     }
394
395     /* ****
396      * Storable interface, implies Externalizable, TypedFormat
397      */

398
399     /**
400      * @see TypedFormat#getTypeFormatId
401      *
402      * From the engine's perspective, all XML implementations share
403      * the same format id.
404      */

405     public int getTypeFormatId() {
406         return StoredFormatIds.XML_ID;
407     }
408
409     /**
410      * @see Storable#isNull
411      */

412     public boolean isNull()
413     {
414         return ((xmlStringValue == null) || xmlStringValue.isNull());
415     }
416
417     /**
418      * @see Storable#restoreToNull
419      */

420     public void restoreToNull()
421     {
422         if (xmlStringValue != null)
423             xmlStringValue.restoreToNull();
424     }
425
426     /**
427      * Read an XML value from an input stream.
428      * @param in The stream from which we're reading.
429      */

430     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc
431     {
432         if (xmlStringValue == null)
433             xmlStringValue = new SQLChar();
434
435         // Read the XML implementation id. Right now there's
436
// only one implementation (UTF-8 based), so we don't
437
// use this value. But if better implementations come
438
// up in the future, we'll have to use this impl id to
439
// figure out how to read the data.
440
in.readShort();
441
442         // Now just read the XML data as UTF-8.
443
xmlStringValue.readExternal(in);
444
445         // If we read it from disk then it must have type
446
// XML_DOC_ANY because that's all we allow to be
447
// written into an XML column.
448
setXType(XML_DOC_ANY);
449     }
450
451     /**
452      * Write an XML value.
453      * @param out The stream to which we're writing.
454      */

455     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc
456     {
457         // never called when value is null
458
if (SanityManager.DEBUG)
459             SanityManager.ASSERT(!isNull());
460
461         // Write out the XML store impl id.
462
out.writeShort(UTF8_IMPL_ID);
463
464         // Now write out the data.
465
xmlStringValue.writeExternal(out);
466     }
467
468     /* ****
469      * StreamStorable interface
470      * */

471
472     /**
473      * @see StreamStorable#returnStream
474      */

475     public InputStream returnStream()
476     {
477         return
478             (xmlStringValue == null) ? null : xmlStringValue.returnStream();
479     }
480
481     /**
482      * @see StreamStorable#setStream
483      */

484     public void setStream(InputStream newStream)
485     {
486         if (xmlStringValue == null)
487             xmlStringValue = new SQLChar();
488
489         // The stream that we receive is for an XML data value,
490
// which means it has an XML implementation id stored
491
// at the front (we put it there when we wrote it out).
492
// If we leave that there we'll get a failure when
493
// our underlying SQLChar tries to read from the
494
// stream, because the extra impl id will throw
495
// off the UTF format. So we need to read in (and
496
// ignore) the impl id before using the stream.
497
try {
498             // 2 bytes equal a short, which is what an impl id is.
499
newStream.read();
500             newStream.read();
501         } catch (Exception JavaDoc e) {
502             if (SanityManager.DEBUG)
503                 SanityManager.THROWASSERT("Failed to read impl id" +
504                     "bytes in setStream.");
505         }
506
507         // Now go ahead and use the stream.
508
xmlStringValue.setStream(newStream);
509
510         // If we read it from disk then it must have type
511
// XML_DOC_ANY because that's all we allow to be
512
// written into an XML column.
513
setXType(XML_DOC_ANY);
514     }
515
516     /**
517      * @see StreamStorable#loadStream
518      */

519     public void loadStream() throws StandardException
520     {
521         getString();
522     }
523
524     /* ****
525      * XMLDataValue interface.
526      * */

527
528     /**
529      * Method to parse an XML string and, if it's valid,
530      * store the _serialized_ version locally and then return
531      * this XMLDataValue.
532      *
533      * @param text The string value to check.
534      * @param preserveWS Whether or not to preserve
535      * ignorable whitespace.
536      * @param sqlxUtil Contains SQL/XML objects and util
537      * methods that facilitate execution of XML-related
538      * operations
539      * @return If 'text' constitutes a valid XML document,
540      * it has been stored in this XML value and this XML
541      * value is returned; otherwise, an exception is thrown.
542      * @exception StandardException Thrown on error.
543      */

544     public XMLDataValue XMLParse(String JavaDoc text, boolean preserveWS,
545         SqlXmlUtil sqlxUtil) throws StandardException
546     {
547         try {
548
549             if (preserveWS) {
550             // Currently the only way a user can view the contents of
551
// an XML value is by explicitly calling XMLSERIALIZE.
552
// So do a serialization now and just store the result,
553
// so that we don't have to re-serialize every time a
554
// call is made to XMLSERIALIZE.
555
text = sqlxUtil.serializeToString(text);
556             }
557             else {
558             // We don't support this yet, so we shouldn't
559
// get here.
560
if (SanityManager.DEBUG)
561                     SanityManager.THROWASSERT("Tried to STRIP whitespace " +
562                         "but we shouldn't have made it this far");
563             }
564
565         } catch (Throwable JavaDoc t) {
566         /* Couldn't parse the XML document. Throw a StandardException
567          * with the parse exception (or other error) nested in it.
568          * Note: we catch "Throwable" here to catch as many external
569          * errors as possible in order to minimize the chance of an
570          * uncaught JAXP/Xalan error (such as a NullPointerException)
571          * causing Derby to fail in a more serious way. In particular,
572          * an uncaught Java exception like NPE can result in Derby
573          * throwing "ERROR 40XT0: An internal error was identified by
574          * RawStore module" for all statements on the connection after
575          * the failure--which we clearly don't want. If we catch the
576          * error and wrap it, though, the statement will fail but Derby
577          * will continue to run as normal.
578          */

579             throw StandardException.newException(
580                 SQLState.LANG_INVALID_XML_DOCUMENT, t, t.getMessage());
581
582         }
583
584         // If we get here, the text is valid XML so go ahead
585
// and load/store it.
586
setXType(XML_DOC_ANY);
587         if (xmlStringValue == null)
588             xmlStringValue = new SQLChar();
589         xmlStringValue.setValue(text);
590         return this;
591     }
592
593     /**
594      * The SQL/XML XMLSerialize operator.
595      * Serializes this XML value into a string with a user-specified
596      * character type, and returns that string via the received
597      * StringDataValue (if the received StringDataValue is non-null
598      * and of the correct type; else, a new StringDataValue is
599      * returned).
600      *
601      * @param result The result of a previous call to this method,
602      * null if not called yet.
603      * @param targetType The string type to which we want to serialize.
604      * @param targetWidth The width of the target type.
605      * @return A serialized (to string) version of this XML object,
606      * in the form of a StringDataValue object.
607      * @exception StandardException Thrown on error
608      */

609     public StringDataValue XMLSerialize(StringDataValue result,
610         int targetType, int targetWidth) throws StandardException
611     {
612         if (result == null) {
613             switch (targetType)
614             {
615                 case Types.CHAR: result = new SQLChar(); break;
616                 case Types.VARCHAR: result = new SQLVarchar(); break;
617                 case Types.LONGVARCHAR: result = new SQLLongvarchar(); break;
618                 case Types.CLOB: result = new SQLClob(); break;
619                 default:
620                 // Shouldn't ever get here, as this check was performed
621
// at bind time.
622

623                     if (SanityManager.DEBUG) {
624                         SanityManager.THROWASSERT(
625                             "Should NOT have made it to XMLSerialize " +
626                             "with a non-string target type: " + targetType);
627                     }
628                     return null;
629             }
630         }
631
632         // Else we're reusing a StringDataValue. We only reuse
633
// the result if we're executing the _same_ XMLSERIALIZE
634
// call on multiple rows. That means that all rows
635
// must have the same result type (targetType) and thus
636
// we know that the StringDataValue already has the
637
// correct type. So we're set.
638

639         if (this.isNull()) {
640         // Attempts to serialize a null XML value lead to a null
641
// result (SQL/XML[2003] section 10.13).
642
result.setToNull();
643             return result;
644         }
645
646         /* XML serialization rules say that sequence "normalization"
647          * must occur before serialization, and normalization dictates
648          * that a serialization error must be thrown if the XML value
649          * is a sequence with a top-level attribute. We normalized
650          * (and serialized) this XML value when it was first created,
651          * and at that time we took note of whether or not there is
652          * a top-level attribute. So throw the error here if needed.
653          * See SqlXmlUtil.serializeToString() for more on sequence
654          * normalization.
655          */

656         if (this.hasTopLevelAttr())
657         {
658             throw StandardException.newException(
659                 SQLState.LANG_XQUERY_SERIALIZATION_ERROR);
660         }
661
662         // Get the XML value as a string. For this UTF-8 impl,
663
// we already have it as a UTF-8 string, so just use
664
// that.
665
result.setValue(getString());
666
667         // Seems wrong to trunc an XML document, as it then becomes non-
668
// well-formed and thus useless. So we throw an error (that's
669
// what the "true" in the next line says).
670
result.setWidth(targetWidth, 0, true);
671         return result;
672     }
673
674     /**
675      * The SQL/XML XMLExists operator.
676      * Checks to see if evaluation of the query expression contained
677      * within the received util object against this XML value returns
678      * at least one item. NOTE: For now, the query expression must be
679      * XPath only (XQuery not supported) because that's what Xalan
680      * supports.
681      *
682      * @param sqlxUtil Contains SQL/XML objects and util
683      * methods that facilitate execution of XML-related
684      * operations
685      * @return True if evaluation of the query expression stored
686      * in sqlxUtil returns at least one node for this XML value;
687      * unknown if the xml value is NULL; false otherwise.
688      * @exception StandardException Thrown on error
689      */

690     public BooleanDataValue XMLExists(SqlXmlUtil sqlxUtil)
691         throws StandardException
692     {
693         if (this.isNull()) {
694         // if the user specified a context node and that context
695
// is null, result of evaluating the query is null
696
// (per SQL/XML 6.17:General Rules:1.a), which means that we
697
// return "unknown" here (per SQL/XML 8.4:General Rules:2.a).
698
return SQLBoolean.unknownTruthValue();
699         }
700
701         // Make sure we have a compiled query (and associated XML
702
// objects) to evaluate.
703
if (SanityManager.DEBUG) {
704             SanityManager.ASSERT(
705                 sqlxUtil != null,
706                 "Tried to evaluate XML xquery, but no XML objects were loaded.");
707         }
708
709         try {
710
711             return new SQLBoolean(null !=
712                 sqlxUtil.evalXQExpression(this, false, new int[1]));
713
714         } catch (StandardException se) {
715
716             // Just re-throw it.
717
throw se;
718
719         } catch (Throwable JavaDoc xe) {
720         /* Failed somewhere during evaluation of the XML query expression;
721          * turn error into a StandardException and throw it. Note: we
722          * catch "Throwable" here to catch as many Xalan-produced errors
723          * as possible in order to minimize the chance of an uncaught Xalan
724          * error (such as a NullPointerException) causing Derby to fail in
725          * a more serious way. In particular, an uncaught Java exception
726          * like NPE can result in Derby throwing "ERROR 40XT0: An internal
727          * error was identified by RawStore module" for all statements on
728          * the connection after the failure--which we clearly don't want.
729          * If we catch the error and wrap it, though, the statement will
730          * fail but Derby will continue to run as normal.
731          */

732             throw StandardException.newException(
733                 SQLState.LANG_XML_QUERY_ERROR, xe,
734                 "XMLEXISTS", xe.getMessage());
735         }
736     }
737
738     /**
739      * Evaluate the XML query expression contained within the received
740      * util object against this XML value and store the results into
741      * the received XMLDataValue "result" param (assuming "result" is
742      * non-null; else create a new XMLDataValue).
743      *
744      * @param result The result of a previous call to this method; null
745      * if not called yet.
746      * @param sqlxUtil Contains SQL/XML objects and util methods that
747      * facilitate execution of XML-related operations
748      * @return An XMLDataValue whose content corresponds to the serialized
749      * version of the results from evaluation of the query expression.
750      * Note: this XMLDataValue may not be storable into Derby XML
751      * columns.
752      * @exception Exception thrown on error (and turned into a
753      * StandardException by the caller).
754      */

755     public XMLDataValue XMLQuery(XMLDataValue result,
756         SqlXmlUtil sqlxUtil) throws StandardException
757     {
758         if (this.isNull()) {
759         // if the context is null, we return null,
760
// per SQL/XML[2006] 6.17:GR.1.a.ii.1.
761
if (result == null)
762                 result = (XMLDataValue)getNewNull();
763             else
764                 result.setToNull();
765             return result;
766         }
767
768         try {
769  
770             // Return an XML data value whose contents are the
771
// serialized version of the query results.
772
int [] xType = new int[1];
773             ArrayList JavaDoc itemRefs = sqlxUtil.evalXQExpression(
774                 this, true, xType);
775
776             if (result == null)
777                 result = new XML();
778             String JavaDoc strResult = sqlxUtil.serializeToString(itemRefs, result);
779             result.setValue(new SQLChar(strResult));
780
781             // Now that we've set the result value, make sure
782
// to indicate what kind of XML value we have.
783
result.setXType(xType[0]);
784
785             // And finally we return the query result as an XML value.
786
return result;
787
788         } catch (StandardException se) {
789
790             // Just re-throw it.
791
throw se;
792
793         } catch (Throwable JavaDoc xe) {
794         /* Failed somewhere during evaluation of the XML query expression;
795          * turn error into a StandardException and throw it. Note: we
796          * catch "Throwable" here to catch as many Xalan-produced errors
797          * as possible in order to minimize the chance of an uncaught Xalan
798          * error (such as a NullPointerException) causing Derby to fail in
799          * a more serious way. In particular, an uncaught Java exception
800          * like NPE can result in Derby throwing "ERROR 40XT0: An internal
801          * error was identified by RawStore module" for all statements on
802          * the connection after the failure--which we clearly don't want.
803          * If we catch the error and wrap it, though, the statement will
804          * fail but Derby will continue to run as normal.
805          */

806             throw StandardException.newException(
807                 SQLState.LANG_XML_QUERY_ERROR, xe,
808                 "XMLQUERY", xe.getMessage());
809         }
810     }
811
812     /* ****
813      * Helper classes and methods.
814      * */

815
816     /**
817      * Set this XML value's qualified type.
818      */

819     public void setXType(int xtype)
820     {
821         this.xType = xtype;
822
823         /* If the target type is XML_DOC_ANY then this XML value
824          * holds a single well-formed Document. So we know that
825          * we do NOT have any top-level attribute nodes. Note: if
826          * xtype is SEQUENCE we don't set "containsTopLevelAttr"
827          * here; assumption is that the caller of this method will
828          * then set the field as appropriate. Ex. see "setFrom()"
829          * in this class.
830          */

831         if (xtype == XML_DOC_ANY)
832             containsTopLevelAttr = false;
833     }
834
835     /**
836      * Retrieve this XML value's qualified type.
837      */

838     public int getXType()
839     {
840         return xType;
841     }
842
843     /**
844      * Take note of the fact this XML value represents an XML
845      * sequence that has one or more top-level attribute nodes.
846      */

847     public void markAsHavingTopLevelAttr()
848     {
849         this.containsTopLevelAttr = true;
850     }
851
852     /**
853      * Return whether or not this XML value represents a sequence
854      * that has one or more top-level attribute nodes.
855      */

856     public boolean hasTopLevelAttr()
857     {
858         return containsTopLevelAttr;
859     }
860
861     /**
862      * See if the required JAXP and Xalan classes are in the
863      * user's classpath. Assumption is that we will always
864      * call this method before instantiating an instance of
865      * SqlXmlUtil, and thus we will never get a ClassNotFound
866      * exception caused by missing JAXP/Xalan classes. Instead,
867      * if either is missing we should throw an informative
868      * error indicating what the problem is.
869      *
870      * NOTE: This method only does the checks necessary to
871      * allow successful instantiation of the SqlXmlUtil
872      * class. Further checks (esp. the presence of a JAXP
873      * _implementation_ in addition to the JAXP _interfaces_)
874      * are performed in the SqlXmlUtil constructor.
875      *
876      * @exception StandardException thrown if the required
877      * classes cannot be located in the classpath.
878      */

879     public static void checkXMLRequirements()
880         throws StandardException
881     {
882         // Only check once; after that, just re-use the result.
883
if (xmlReqCheck == null)
884         {
885             xmlReqCheck = "";
886
887             /* If the w3c Document class exists, then we
888              * assume a JAXP implementation is present in
889              * the classpath. If this assumption is incorrect
890              * then we at least know that the JAXP *interface*
891              * exists and thus we'll be able to instantiate
892              * the SqlXmlUtil class. We can then do a check
893              * for an actual JAXP *implementation* from within
894              * the SqlXmlUtil class (see the constructor of
895              * that class).
896              *
897              * Note: The JAXP API and implementation are
898              * provided as part the JVM if it is jdk 1.4 or
899              * greater.
900              */

901             if (!ClassInspector.classIsLoadable("org.w3c.dom.Document"))
902                 xmlReqCheck = "JAXP";
903
904             /* If the XPath class exists, then we assume that our XML
905              * query processor (in this case, Xalan), is present in the
906              * classpath. Note: if JAXP API classes aren't present
907              * then the following check will return false even if the
908              * Xalan classes *are* present; this is because the Xalan
909              * XPath class relies on JAXP, as well. Thus there's no
910              * point in checking for Xalan unless we've already confirmed
911              * that we have the JAXP interfaces.
912              */

913             else if (!ClassInspector.classIsLoadable("org.apache.xpath.XPath"))
914                 xmlReqCheck = "Xalan";
915         }
916
917         if (xmlReqCheck.length() != 0)
918         {
919             throw StandardException.newException(
920                 SQLState.LANG_MISSING_XML_CLASSES, xmlReqCheck);
921         }
922
923         return;
924     }
925
926 }
927
Popular Tags