KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.SqlXmlUtil
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 import org.apache.derby.iapi.reference.SQLState;
26 import org.apache.derby.iapi.services.io.Formatable;
27 import org.apache.derby.iapi.services.io.StoredFormatIds;
28 import org.apache.derby.iapi.services.sanity.SanityManager;
29
30 import java.util.Properties JavaDoc;
31 import java.util.ArrayList JavaDoc;
32
33 import java.io.IOException JavaDoc;
34 import java.io.ObjectOutput JavaDoc;
35 import java.io.ObjectInput JavaDoc;
36 import java.io.StringReader JavaDoc;
37
38 // -- JDBC 3.0 JAXP API classes.
39

40 import org.w3c.dom.Attr JavaDoc;
41 import org.w3c.dom.Document JavaDoc;
42 import org.w3c.dom.Element JavaDoc;
43 import org.w3c.dom.Node JavaDoc;
44 import org.w3c.dom.NodeList JavaDoc;
45 import org.w3c.dom.Text JavaDoc;
46
47 import org.xml.sax.ErrorHandler JavaDoc;
48 import org.xml.sax.InputSource JavaDoc;
49 import org.xml.sax.SAXException JavaDoc;
50 import org.xml.sax.SAXParseException JavaDoc;
51
52 import javax.xml.parsers.DocumentBuilder JavaDoc;
53 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
54
55 import javax.xml.transform.OutputKeys JavaDoc;
56 import javax.xml.transform.TransformerException JavaDoc;
57
58 // -- Xalan-specific classes.
59

60 import org.apache.xpath.XPath;
61 import org.apache.xpath.XPathContext;
62 import org.apache.xpath.objects.XObject;
63 import org.apache.xpath.objects.XNodeSet;
64
65 import org.apache.xml.utils.PrefixResolverDefault;
66
67 import org.apache.xalan.serialize.DOMSerializer;
68 import org.apache.xalan.serialize.Serializer;
69 import org.apache.xalan.serialize.SerializerFactory;
70 import org.apache.xalan.templates.OutputProperties;
71
72 /**
73  * This class contains "utility" methods that work with XML-specific
74  * objects that are only available if JAXP and/or Xalan are in
75  * the classpath.
76  *
77  * NOTE: This class is only compiled with JDK 1.4 and higher since
78  * the XML-related classes that it uses (JAXP and Xalan) are not
79  * part of earlier JDKs.
80  *
81  * Having a separate class for this functionality is beneficial
82  * for two reasons:
83  *
84  * 1. Allows us to allocate XML objects and compile an XML
85  * query expression a single time per statement, instead of
86  * having to do it for every row against which the query
87  * is evaluated. An instance of this class is created at
88  * compile time and then passed (using "saved objects")
89  * to the appropriate operator implementation method in
90  * XML.java; see SqlXmlExecutor.java for more about the
91  * role this class plays in "saved object" processing.
92  *
93  * 2. By keeping all XML-specific references in this one class,
94  * we have a single "point of entry" to the XML objects--namely,
95  * the constructor for this class. Thus, if we always make
96  * sure to check for the required XML classes _before_ calling
97  * this class's constructor, we can detect early on whether
98  * some classes (ex. Xalan) are missing, and can throw a friendly
99  * error up front, instead of a ClassNotFoundException somewhere
100  * deeper in the execution codepath. The initial check for the
101  * required XML classes can be found in XML.checkXMLRequirements().
102  *
103  * Note that we don't want to put references to XML-specific
104  * objects directly into XML.java because that class (XML.java) is
105  * instantiated anytime a table with an XML column is referenced.
106  * That would mean that if a user tried to select a non-XML column
107  * (ex. integer) from a table that had at least one XML column in
108  * it, the user would have to have JAXP and Xalan classes in
109  * his/her classpath--which we don't want. Instead, by keeping
110  * all XML-specific objects in this one class, and then only
111  * instantiating this class when an XML operator is used (either
112  * implicitly or explicitly), we make it so that the user is only
113  * required to have XML-specific classes in his/her classpath
114  * _if_ s/he is trying to access or operate on XML values.
115  */

116
117 public class SqlXmlUtil implements Formatable
118 {
119     // Used to parse a string into an XML value (DOM); checks
120
// the well-formedness of the string while parsing.
121
private DocumentBuilder JavaDoc dBuilder;
122
123     // Used to serialize an XML value according the standard
124
// XML serialization rules.
125
private Serializer serializer;
126
127     // Classes used to compile and execute an XPath expression
128
// against Xalan.
129
private XPath query;
130     private XPathContext xpContext;
131
132     // Used to recompile the XPath expression when this formatable
133
// object is reconstructed. e.g.: SPS
134
private String JavaDoc queryExpr;
135     private String JavaDoc opName;
136     private boolean recompileQuery;
137     
138     /**
139      * Constructor: Initializes objects required for parsing
140      * and serializing XML values. Since most XML operations
141      * that require XML-specific classes perform both parsing
142      * and serialization at some point, we just initialize the
143      * objects up front.
144      */

145     public SqlXmlUtil() throws StandardException
146     {
147         try {
148
149             /* Note: Use of DocumentBuilderFactory means that we get
150              * whatever XML parser is the "default" for the JVM in
151              * use--and thus, we don't have to hard-code the parser
152              * name, nor do we have to require that the user have a
153              * specific parser in his/her classpath.
154              *
155              * This DocumentBuilder is currently used for parsing
156              * (esp. XMLPARSE), and the SQL/XML spec says that XMLPARSE
157              * should NOT perform validation (SQL/XML[2006], 6.15:
158              * "Perform a non-validating parse of a string to produce
159              * an XML value."). So we disable validation here, and
160              * we also make the parser namespace aware.
161              *
162              * At some point in the future we will probably want to add
163              * support for the XMLVALIDATE function--but until then, user
164              * is unable to validate the XML values s/he inserts.
165              *
166              * Note that, even with validation turned off, XMLPARSE
167              * _will_ still check the well-formedness of the values,
168              * and it _will_ still process DTDs to get default values,
169              * etc--but that's it; no validation errors will be thrown.
170              */

171
172             DocumentBuilderFactory JavaDoc dBF = null;
173             try {
174
175                 dBF = DocumentBuilderFactory.newInstance();
176
177             } catch (Throwable JavaDoc e) {
178
179                 /* We assume that if we get an error creating the
180                  * DocumentBuilderFactory, it's because there's no
181                  * JAXP implementation. This can happen in the
182                  * (admittedly unlikely) case where the classpath
183                  * contains the JAXP _interfaces_ (ex. via xml-apis.jar)
184                  * and the Xalan classes but does not actually
185                  * contain a JAXP _implementation_. In that case the
186                  * check in XML.checkXMLRequirements() will pass
187                  * and this class (SqlXmlUtil) will be instantiated
188                  * successfully--which is how we get to this constructor.
189                  * But then attempts to create a DocumentBuilderFactory
190                  * will fail, bringing us here. Note that we can't
191                  * check for a valid JAXP implementation in the
192                  * XML.checkXMLRequirements() method because we
193                  * always want to allow the XML.java class to be
194                  * instantiated, even if the required XML classes
195                  * are not present--and that means that it (the
196                  * XML class) cannot reference DocumentBuilder nor
197                  * any of the JAXP classes directly.
198                  */

199                  throw StandardException.newException(
200                      SQLState.LANG_MISSING_XML_CLASSES, "JAXP");
201
202             }
203
204             dBF.setValidating(false);
205             dBF.setNamespaceAware(true);
206
207             // Load document builder that can be used for parsing XML.
208
dBuilder = dBF.newDocumentBuilder();
209             dBuilder.setErrorHandler(new XMLErrorHandler());
210
211             // Load serializer for serializing XML into string according
212
// XML serialization rules.
213
loadSerializer();
214
215         } catch (StandardException se) {
216
217             // Just rethrow it.
218
throw se;
219
220         } catch (Throwable JavaDoc t) {
221
222             /* Must be something caused by JAXP or Xalan; wrap it in a
223              * StandardException and rethrow it. Note: we catch "Throwable"
224              * here to catch as many external errors as possible in order
225              * to minimize the chance of an uncaught JAXP/Xalan error (such
226              * as a NullPointerException) causing Derby to fail in a more
227              * serious way. In particular, an uncaught Java exception
228              * like NPE can result in Derby throwing "ERROR 40XT0: An
229              * internal error was identified by RawStore module" for all
230              * statements on the connection after the failure--which we
231              * clearly don't want. If we catch the error and wrap it,
232              * though, the statement will fail but Derby will continue to
233              * run as normal.
234              */

235             throw StandardException.newException(
236                 SQLState.LANG_UNEXPECTED_XML_EXCEPTION, t, t.getMessage());
237
238         }
239
240         // At construction time we don't have an XML query expression
241
// to compile. If one is required, we'll load/compile it later.
242
query = null;
243     }
244
245     /**
246      * Take the received string, which is an XML query expression,
247      * compile it, and store the compiled query locally. Note
248      * that for now, we only support XPath because that's what
249      * Xalan supports.
250      *
251      * @param queryExpr The XPath expression to compile
252      */

253     public void compileXQExpr(String JavaDoc queryExpr, String JavaDoc opName)
254         throws StandardException
255     {
256         try {
257
258             /* The following XPath constructor compiles the expression
259              * as part of the construction process. We have to pass
260              * in a PrefixResolver object in order to avoid NPEs when
261              * invalid/unknown functions are used, so we just create
262              * a dummy one, which means prefixes will not be resolved
263              * in the query (Xalan will just throw an error if a prefix
264              * is used). In the future we may want to revisit this
265              * to make it easier for users to query based on namespaces.
266              */

267             query = new XPath(queryExpr, null,
268                 new PrefixResolverDefault(dBuilder.newDocument()),
269                 XPath.SELECT);
270             
271             this.queryExpr = queryExpr;
272             this.opName = opName;
273             this.recompileQuery = false;
274
275         } catch (Throwable JavaDoc te) {
276
277             /* Something went wrong during compilation of the
278              * expression; wrap the error and re-throw it.
279              * Note: we catch "Throwable" here to catch as many
280              * Xalan-produced errors as possible in order to
281              * minimize the chance of an uncaught Xalan error
282              * (such as a NullPointerException) causing Derby
283              * to fail in a more serious way. In particular, an
284              * uncaught Java exception like NPE can result in
285              * Derby throwing "ERROR 40XT0: An internal error was
286              * identified by RawStore module" for all statements on
287              * the connection after the failure--which we clearly
288              * don't want. If we catch the error and wrap it,
289              * though, the statement will fail but Derby will
290              * continue to run as normal.
291              */

292             throw StandardException.newException(
293                 SQLState.LANG_XML_QUERY_ERROR, te, opName, te.getMessage());
294
295         }
296     }
297
298     /**
299      * Take a string representing an XML value and serialize it
300      * according SQL/XML serialization rules. Right now, we perform
301      * this serialization by first parsing the string into a JAXP
302      * Document object, and then applying the serialization semantics
303      * to that Document. That seems a bit inefficient, but neither
304      * Xalan nor JAXP provides a more direct way to do this.
305      *
306      * @param xmlAsText String version of XML on which to perform
307      * serialization.
308      * @return A properly serialized version of xmlAsText.
309      */

310     protected String JavaDoc serializeToString(String JavaDoc xmlAsText)
311         throws Exception JavaDoc
312     {
313         ArrayList JavaDoc aList = new ArrayList JavaDoc();
314
315         /* The call to dBuilder.parse() is a call to an external
316          * (w.r.t. to Derby) JAXP parser. If the received XML
317          * text references an external DTD, then the JAXP parser
318          * will try to read that external DTD. Thus we wrap the
319          * call to parse inside a privileged action to make sure
320          * that the JAXP parser has the required permissions for
321          * reading the DTD file.
322          */

323         try {
324
325             final InputSource JavaDoc is = new InputSource JavaDoc(new StringReader JavaDoc(xmlAsText));
326             aList.add(java.security.AccessController.doPrivileged(
327                 new java.security.PrivilegedExceptionAction JavaDoc()
328                 {
329                     public Object JavaDoc run() throws IOException JavaDoc, SAXException JavaDoc
330                     {
331                         return dBuilder.parse(is);
332                     }
333                 }));
334
335         } catch (java.security.PrivilegedActionException JavaDoc pae) {
336
337             /* Unwrap the privileged exception so that the user can
338              * see what the underlying error is. For example, it could
339              * be an i/o error from parsing the XML value, which can
340              * happen if the XML value references an external DTD file
341              * but the JAXP parser hits an i/o error when trying to read
342              * the DTD. In that case we want to throw the i/o error
343              * itself so that it does not appear as a security exception
344              * to the user.
345              */

346             throw pae.getException();
347
348         }
349
350         /* The second argument in the following call is for
351          * catching cases where we have a top-level (parentless)
352          * attribute node--but since we just created the list
353          * with a single Document node, we already we know we
354          * don't have a top-level attribute node in the list,
355          * so we don't have to worry. Hence the "null" here.
356          */

357         return serializeToString(aList, null);
358     }
359
360     /**
361      * Take an array list (sequence) of XML nodes and/or string values
362      * and serialize that entire list according to SQL/XML serialization
363      * rules, which ultimately point to XML serialization rules as
364      * defined by w3c. As part of that serialization process we have
365      * to first "normalize" the sequence. We do that by iterating through
366      * the list and performing the steps for "sequence normalization" as
367      * defined here:
368      *
369      * http://www.w3.org/TR/xslt-xquery-serialization/#serdm
370      *
371      * This method primarily focuses on taking the steps for normalization;
372      * for the rest of the serialization work, we just make calls on the
373      * DOMSerializer class provided by Xalan.
374      *
375      * @param items List of items to serialize
376      * @param xmlVal XMLDataValue into which the serialized string
377      * returned by this method is ultimately going to be stored.
378      * This is used for keeping track of XML values that represent
379      * sequences having top-level (parentless) attribute nodes.
380      * @return Single string holding the serialized version of the
381      * normalized sequence created from the items in the received
382      * list.
383      */

384     protected String JavaDoc serializeToString(ArrayList JavaDoc items,
385         XMLDataValue xmlVal) throws java.io.IOException JavaDoc
386     {
387         if ((items == null) || (items.size() == 0))
388         // nothing to do; return empty sequence.
389
return "";
390
391         java.io.StringWriter JavaDoc sWriter = new java.io.StringWriter JavaDoc();
392
393         // Serializer should have been set by now.
394
if (SanityManager.DEBUG)
395         {
396             SanityManager.ASSERT(serializer != null,
397                 "Tried to serialize with uninitialized XML serializer.");
398         }
399
400         serializer.setWriter(sWriter);
401         DOMSerializer dSer = serializer.asDOMSerializer();
402
403         int sz = items.size();
404         Object JavaDoc obj = null;
405
406         /* Step 1: Empty sequence. If we have an empty sequence then we
407          * won't ever enter the for loop and the call to sWriter.toString()
408          * at the end of this method will return an empty string, as
409          * required. Otherwise, for a non-empty sequence our "items"
410          * list already corresponds to "S1".
411          */

412
413         // Iterate through the list and serialize each item.
414
boolean lastItemWasString = false;
415         for (int i = 0; i < sz; i++)
416         {
417             obj = items.get(i);
418             // if it's a string, then this corresponds to some atomic
419
// value, so just echo the string as it is.
420
if (obj instanceof String JavaDoc)
421             {
422                 /* Step 2: Atomic values. If "obj" is a string then it
423                  * corresponds to some atomic value whose "lexical
424                  * representation" is obj. So we just take that.
425                  */

426
427                 if (lastItemWasString)
428                 {
429                     /* Step 3: Adjacent strings. If we have multiple adjacent
430                      * strings then concatenate them with a single space
431                      * between them.
432                      */

433                     sWriter.write(" ");
434                 }
435
436                 /* Step 4: Create a Text node from the adjacent strings.
437                  * Since we're just going to serialize the Text node back
438                  * into a string, we short-cut this step by skipping the
439                  * creation of the Text node and just writing the string
440                  * out directly to our serialized stream.
441                  */

442                 sWriter.write((String JavaDoc)obj);
443                 lastItemWasString = true;
444             }
445             else if (obj instanceof Attr JavaDoc)
446             {
447                 /* Step 7a: Attribute nodes. If there is an Attribute node
448                  * node in the sequence then we have to throw a serialization
449                  * error. NOTE: The rules say we also have to throw an error
450                  * for Namespace nodes, but JAXP doesn't define a "Namespace"
451                  * object per se; it just defines namespace prefixes and URIs
452                  * on other Nodes. So we just check for attributes. If we
453                  * find one then we take note of the fact that the result has
454                  * a parentless attribute node and later, if the user calls
455                  * XMLSERIALIZE on the received XMLDataValue we'll throw the
456                  * error as required. Note that we currently only get here
457                  * for the XMLQUERY operator, which means we're serializing
458                  * a result sequence returned from Xalan and we're going to
459                  * store the serialized version into a Derby XML value. In
460                  * that case the serialization is an internal operation--and
461                  * since the user didn't ask for it, we don't want to throw
462                  * the serialization error here. If we did, then whenever an
463                  * XMLQUERY operation returned a result sequence with a top-
464                  * level attribute in it, the user would see a serialization
465                  * error. That's not correct since it is technically okay for
466                  * the XMLQUERY operation to return a sequence with an attribute
467                  * node; it's just not okay for a user to explicitly try to
468                  * serialize that sequence. So instead of throwing the error
469                  * here, we just take note of the fact that the sequence has
470                  * a top-level attribute. Then later, IF the user makes an
471                  * explicit call to serialize the sequence, we'll throw the
472                  * appropriate error (see XML.XMLSerialize()).
473                  */

474                 if (xmlVal != null)
475                     xmlVal.markAsHavingTopLevelAttr();
476                 dSer.serialize((Node JavaDoc)obj);
477                 lastItemWasString = false;
478             }
479             else
480             { // We have a Node, so try to serialize it.
481
Node JavaDoc n = (Node JavaDoc)obj;
482                 if (n instanceof Text JavaDoc)
483                 {
484                     /* Step 6: Combine adjacent text nodes into a single
485                      * text node. Since we're just going to serialize the
486                      * Text node back into a string, we short-cut this step
487                      * by skipping the creation of a new Text node and just
488                      * writing the text value out directly to our serialized
489                      * stream. Step 6 also says that empty text nodes should
490                      * be dropped--but if the text node is empty, the call
491                      * to getNodeValue() will return an empty string and
492                      * thus we've effectively "dropped" the text node from
493                      * the serialized result. Note: it'd be cleaner to just
494                      * call "serialize()" on the Text node like we do for
495                      * all other Nodes, but Xalan doesn't allow that. So
496                      * use the getNodeValue() method instead.
497                      */

498                     sWriter.write(n.getNodeValue());
499                 }
500                 else
501                 {
502                     /* Steps 5 and 7b: Copy all non-attribute, non-text
503                      * nodes to the "normalized sequence" and then serialize
504                      * that normalized sequence. We short-cut this by
505                      * just letting Xalan do the serialization for every
506                      * Node in the current list of items that wasn't
507                      * "serialized" as an atomic value, attribute, or
508                      * text node.
509                      */

510                     dSer.serialize(n);
511                 }
512
513                 lastItemWasString = false;
514             }
515         }
516
517         /* At this point sWriter holds the serialized version of the
518          * normalized sequence that corresponds to the received list
519          * of items. So that's what we return.
520          */

521         sWriter.flush();
522         return sWriter.toString();
523     }
524
525     /**
526      * Evaluate this object's compiled XML query expression against
527      * the received xmlContext. Then if returnResults is false,
528      * return an empty sequence (ArrayList) if evaluation yields
529      * at least one item and return null if evaluation yields zero
530      * items (the caller can then just check for null to see if the
531      * query returned any items). If returnResults is true, then return
532      * return a sequence (ArrayList) containing all items returned
533      * from evaluation of the expression. This array list can contain
534      * any combination of atomic values and XML nodes; it may also
535      * be empty.
536      *
537      * Assumption here is that the query expression has already been
538      * compiled and is stored in this.query.
539      *
540      * @param xmlContext The XML value against which to evaluate
541      * the stored (compiled) query expression
542      * @param returnResults Whether or not to return the actual
543      * results of the query
544      * @param resultXType The qualified XML type of the result
545      * of evaluating the expression, if returnResults is true.
546      * If the result is a sequence of exactly one Document node
547      * then this will be XML(DOCUMENT(ANY)); else it will be
548      * XML(SEQUENCE). If returnResults is false, this value
549      * is ignored.
550      * @return If returnResults is false then return an empty
551      * ArrayList if evaluation returned at least one item and return
552      * null otherwise. If returnResults is true then return an
553      * array list containing all of the result items and return
554      * the qualified XML type via the resultXType parameter.
555      * @exception Exception thrown on error (and turned into a
556      * StandardException by the caller).
557      */

558     protected ArrayList JavaDoc evalXQExpression(XMLDataValue xmlContext,
559         boolean returnResults, int [] resultXType) throws Exception JavaDoc
560     {
561         // if this object is in an SPS, we need to recompile the query
562
if (recompileQuery)
563         {
564             compileXQExpr(queryExpr, opName);
565         }
566
567         // Make sure we have a compiled query.
568
if (SanityManager.DEBUG) {
569             SanityManager.ASSERT(
570                 (query != null) && (query.getExpression() != null),
571                 "Failed to locate compiled XML query expression.");
572         }
573
574         /* Create a DOM node from the xmlContext, since that's how
575          * we feed the context to Xalan. We do this by creating
576          * a Document node using DocumentBuilder, which means that
577          * the serialized form of the context node must be a string
578          * value that is parse-able by DocumentBuilder--i.e. it must
579          * constitute a valid XML document. If that's true then
580          * the context item's qualified type will be DOC_ANY.
581          */

582         if (xmlContext.getXType() != XML.XML_DOC_ANY)
583         {
584             throw StandardException.newException(
585                 SQLState.LANG_INVALID_XML_CONTEXT_ITEM,
586                 (returnResults ? "XMLQUERY" : "XMLEXISTS"));
587         }
588
589         Document JavaDoc docNode = null;
590         docNode = dBuilder.parse(
591             new InputSource JavaDoc(
592                 new StringReader JavaDoc(xmlContext.getString())));
593
594         // Evaluate the expresion using Xalan.
595
getXPathContext();
596         xpContext.reset();
597         XObject xOb = query.execute(xpContext, docNode, null);
598
599         if (!returnResults)
600         {
601             // We don't want to return the actual results, we just
602
// want to know if there was at least one item in the
603
// result sequence.
604
if ((xOb instanceof XNodeSet) &&
605                 (((XNodeSet)xOb).nodelist().getLength() > 0))
606             { // If we have a sequence (XNodeSet) of length greater
607
// than zero, then we know that at least one item
608
// "exists" in the result so return a non-null list.
609
return new ArrayList JavaDoc(0);
610             }
611             else if (!(xOb instanceof XNodeSet))
612             // we have a single atomic value, which means the result is
613
// non-empty. So return a non-null list.
614
return new ArrayList JavaDoc(0);
615             else {
616             // return null; caller will take this to mean we have an
617
// an empty sequence.
618
return null;
619             }
620         }
621
622         // Else process the results.
623
NodeList JavaDoc nodeList = null;
624         int numItems = 0;
625         if (!(xOb instanceof XNodeSet))
626         // then we only have a single (probably atomic) item.
627
numItems = 1;
628         else {
629             nodeList = xOb.nodelist();
630             numItems = nodeList.getLength();
631         }
632
633         // Return a list of the items contained in the query results.
634
ArrayList JavaDoc itemRefs = new ArrayList JavaDoc();
635         if (nodeList == null)
636         // result is a single, non-node value (ex. it's an atomic number);
637
// in this case, just take the string value.
638
itemRefs.add(xOb.str());
639         else {
640             for (int i = 0; i < numItems; i++)
641                 itemRefs.add(nodeList.item(i));
642         }
643
644         nodeList = null;
645
646         /* Indicate what kind of XML result value we have. If
647          * we have a sequence of exactly one Document then it
648          * is XMLPARSE-able and so we consider it to be of type
649          * XML_DOC_ANY (which means we can store it in a Derby
650          * XML column).
651          */

652         if ((numItems == 1) && (itemRefs.get(0) instanceof Document JavaDoc))
653             resultXType[0] = XML.XML_DOC_ANY;
654         else
655             resultXType[0] = XML.XML_SEQUENCE;
656
657         return itemRefs;
658     }
659
660     /* ****
661      * Helper classes and methods.
662      * */

663
664     /**
665      * Create and return an instance of Xalan's XPathContext
666      * that can be used to compile an XPath expression.
667      */

668     private XPathContext getXPathContext()
669     {
670         if (xpContext == null)
671             xpContext = new XPathContext();
672
673         return xpContext;
674     }
675
676     /**
677      * Create an instance of Xalan serializer for the sake of
678      * serializing an XML value according the SQL/XML specification
679      * for serialization.
680      */

681     private void loadSerializer() throws java.io.IOException JavaDoc
682     {
683         java.io.StringWriter JavaDoc sWriter = new java.io.StringWriter JavaDoc();
684
685         // Set serialization properties.
686
Properties JavaDoc props = OutputProperties.getDefaultMethodProperties("xml");
687
688         // SQL/XML[2006] 10.15:General Rules:6 says method is "xml".
689
props.setProperty(OutputKeys.METHOD, "xml");
690
691         /* Since the XMLSERIALIZE operator doesn't currently support
692          * the DOCUMENT nor CONTENT keywords, SQL/XML spec says that
693          * the default is CONTENT (6.7:Syntax Rules:2.a). Further,
694          * since the XMLSERIALIZE operator doesn't currently support the
695          * <XML declaration option> syntax, the SQL/XML spec says
696          * that the default for that option is "Unknown" (6.7:General
697          * Rules:2.f). Put those together and that in turn means that
698          * the value of "OMIT XML DECLARATION" must be "Yes", as
699          * stated in section 10.15:General Rules:8.c. SO, that's what
700          * we set here.
701          *
702          * NOTE: currently the only way to view the contents of an
703          * XML column is by using an explicit XMLSERIALIZE operator.
704          * This means that if an XML document is stored and it
705          * begins with an XML declaration, the user will never be
706          * able to _see_ that declaration after inserting the doc
707          * because, as explained above, our current support for
708          * XMLSERIALIZE dictates that the declaration must be
709          * omitted. Similarly, other transformations that may
710          * occur from serialization (ex. entity replacement,
711          * attribute order, single-to-double quotes, etc)) will
712          * always be in effect for the string returned to the user;
713          * the original form of the XML document, if different
714          * from the serialized version, is not currently retrievable.
715          */

716         props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
717
718         // We serialize everything as UTF-8 to match what we
719
// store on disk.
720
props.setProperty(OutputKeys.ENCODING, "UTF-8");
721
722         // Load the serializer with the correct properties.
723
serializer = SerializerFactory.getSerializer(props);
724         return;
725     }
726
727     /* ****
728      * Formatable interface implementation
729      * */

730
731     /**
732      * @see java.io.Externalizable#writeExternal
733      *
734      * @exception IOException on error
735      */

736     public void writeExternal(ObjectOutput JavaDoc out)
737         throws IOException JavaDoc
738     {
739         // query may be null
740
if (query == null)
741         {
742             out.writeBoolean(false);
743         }
744         else
745         {
746             out.writeBoolean(true);
747             out.writeObject(queryExpr);
748             out.writeObject(opName);
749         }
750     }
751
752     /**
753      * @see java.io.Externalizable#readExternal
754      *
755      * @exception IOException on error
756      * @exception ClassNotFoundException on error
757      */

758     public void readExternal(ObjectInput JavaDoc in)
759         throws IOException JavaDoc, ClassNotFoundException JavaDoc
760     {
761         if (in.readBoolean())
762         {
763             queryExpr = (String JavaDoc)in.readObject();
764             opName = (String JavaDoc)in.readObject();
765             recompileQuery = true;
766         }
767     }
768
769     /**
770      * Get the formatID which corresponds to this class.
771      *
772      * @return the formatID of this class
773      */

774     public int getTypeFormatId()
775     {
776         return StoredFormatIds.SQL_XML_UTIL_V01_ID;
777     }
778
779     /*
780      ** The XMLErrorHandler class is just a generic implementation
781      ** of the ErrorHandler interface. It allows us to catch
782      ** and process XML parsing errors in a graceful manner.
783      */

784     private class XMLErrorHandler implements ErrorHandler JavaDoc
785     {
786         public void error (SAXParseException JavaDoc exception)
787             throws SAXException JavaDoc
788         {
789             throw new SAXException JavaDoc (exception);
790         }
791
792         public void fatalError (SAXParseException JavaDoc exception)
793             throws SAXException JavaDoc
794         {
795             throw new SAXException JavaDoc (exception);
796         }
797
798         public void warning (SAXParseException JavaDoc exception)
799             throws SAXException JavaDoc
800         {
801             throw new SAXException JavaDoc (exception);
802         }
803     }
804 }
805
Popular Tags