Source Code Cross Referenced for SqlXmlUtil.java in  » Database-DBMS » db-derby-10.2 » org » apache » derby » iapi » types » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » db derby 10.2 » org.apache.derby.iapi.types 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:
003:           Derby - Class org.apache.derby.iapi.types.SqlXmlUtil
004:
005:           Licensed to the Apache Software Foundation (ASF) under one or more
006:           contributor license agreements.  See the NOTICE file distributed with
007:           this work for additional information regarding copyright ownership.
008:           The ASF licenses this file to you under the Apache License, Version 2.0
009:           (the "License"); you may not use this file except in compliance with
010:           the License.  You may obtain a copy of the License at
011:
012:              http://www.apache.org/licenses/LICENSE-2.0
013:
014:           Unless required by applicable law or agreed to in writing, software
015:           distributed under the License is distributed on an "AS IS" BASIS,
016:           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017:           See the License for the specific language governing permissions and
018:           limitations under the License.
019:
020:         */
021:
022:        package org.apache.derby.iapi.types;
023:
024:        import org.apache.derby.iapi.error.StandardException;
025:        import org.apache.derby.iapi.reference.SQLState;
026:        import org.apache.derby.iapi.services.io.Formatable;
027:        import org.apache.derby.iapi.services.io.StoredFormatIds;
028:        import org.apache.derby.iapi.services.sanity.SanityManager;
029:
030:        import java.util.Properties;
031:        import java.util.ArrayList;
032:
033:        import java.io.IOException;
034:        import java.io.ObjectOutput;
035:        import java.io.ObjectInput;
036:        import java.io.StringReader;
037:
038:        // -- JDBC 3.0 JAXP API classes.
039:
040:        import org.w3c.dom.Attr;
041:        import org.w3c.dom.Document;
042:        import org.w3c.dom.Element;
043:        import org.w3c.dom.Node;
044:        import org.w3c.dom.NodeList;
045:        import org.w3c.dom.Text;
046:
047:        import org.xml.sax.ErrorHandler;
048:        import org.xml.sax.InputSource;
049:        import org.xml.sax.SAXException;
050:        import org.xml.sax.SAXParseException;
051:
052:        import javax.xml.parsers.DocumentBuilder;
053:        import javax.xml.parsers.DocumentBuilderFactory;
054:
055:        import javax.xml.transform.OutputKeys;
056:        import javax.xml.transform.TransformerException;
057:
058:        // -- Xalan-specific classes.
059:
060:        import org.apache.xpath.XPath;
061:        import org.apache.xpath.XPathContext;
062:        import org.apache.xpath.objects.XObject;
063:        import org.apache.xpath.objects.XNodeSet;
064:
065:        import org.apache.xml.utils.PrefixResolverDefault;
066:
067:        import org.apache.xalan.serialize.DOMSerializer;
068:        import org.apache.xalan.serialize.Serializer;
069:        import org.apache.xalan.serialize.SerializerFactory;
070:        import org.apache.xalan.templates.OutputProperties;
071:
072:        /**
073:         * This class contains "utility" methods that work with XML-specific
074:         * objects that are only available if JAXP and/or Xalan are in
075:         * the classpath.
076:         *
077:         * NOTE: This class is only compiled with JDK 1.4 and higher since
078:         * the XML-related classes that it uses (JAXP and Xalan) are not
079:         * part of earlier JDKs.
080:         *
081:         * Having a separate class for this functionality is beneficial
082:         * for two reasons:
083:         *
084:         *    1. Allows us to allocate XML objects and compile an XML
085:         *       query expression a single time per statement, instead of
086:         *       having to do it for every row against which the query
087:         *       is evaluated.  An instance of this class is created at
088:         *       compile time and then passed (using "saved objects")
089:         *       to the appropriate operator implementation method in
090:         *       XML.java; see SqlXmlExecutor.java for more about the
091:         *       role this class plays in "saved object" processing.
092:         *
093:         *    2. By keeping all XML-specific references in this one class, 
094:         *       we have a single "point of entry" to the XML objects--namely,
095:         *       the constructor for this class.  Thus, if we always make
096:         *       sure to check for the required XML classes _before_ calling
097:         *       this class's constructor, we can detect early on whether
098:         *       some classes (ex. Xalan) are missing, and can throw a friendly
099:         *       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:            // Used to parse a string into an XML value (DOM); checks
119:            // the well-formedness of the string while parsing.
120:            private DocumentBuilder dBuilder;
121:
122:            // Used to serialize an XML value according the standard
123:            // XML serialization rules.
124:            private Serializer serializer;
125:
126:            // Classes used to compile and execute an XPath expression
127:            // against Xalan.
128:            private XPath query;
129:            private XPathContext xpContext;
130:
131:            // Used to recompile the XPath expression when this formatable
132:            // object is reconstructed.  e.g.:  SPS 
133:            private String queryExpr;
134:            private String opName;
135:            private boolean recompileQuery;
136:
137:            /**
138:             * Constructor: Initializes objects required for parsing
139:             * and serializing XML values.  Since most XML operations
140:             * that require XML-specific classes perform both parsing
141:             * and serialization at some point, we just initialize the
142:             * objects up front.
143:             */
144:            public SqlXmlUtil() throws StandardException {
145:                try {
146:
147:                    /* Note: Use of DocumentBuilderFactory means that we get
148:                     * whatever XML parser is the "default" for the JVM in
149:                     * use--and thus, we don't have to hard-code the parser
150:                     * name, nor do we have to require that the user have a
151:                     * specific parser in his/her classpath.
152:                     *
153:                     * This DocumentBuilder is currently used for parsing
154:                     * (esp. XMLPARSE), and the SQL/XML spec says that XMLPARSE
155:                     * should NOT perform validation (SQL/XML[2006], 6.15:
156:                     * "Perform a non-validating parse of a string to produce
157:                     * an XML value.").   So we disable validation here, and
158:                     * we also make the parser namespace aware.
159:                     *
160:                     * At some point in the future we will probably want to add
161:                     * support for the XMLVALIDATE function--but until then, user
162:                     * is unable to validate the XML values s/he inserts.
163:                     *
164:                     * Note that, even with validation turned off, XMLPARSE
165:                     * _will_ still check the well-formedness of the values,
166:                     * and it _will_ still process DTDs to get default values,
167:                     * etc--but that's it; no validation errors will be thrown.
168:                     */
169:
170:                    DocumentBuilderFactory dBF = null;
171:                    try {
172:
173:                        dBF = DocumentBuilderFactory.newInstance();
174:
175:                    } catch (Throwable e) {
176:
177:                        /* We assume that if we get an error creating the
178:                         * DocumentBuilderFactory, it's because there's no
179:                         * JAXP implementation.  This can happen in the
180:                         * (admittedly unlikely) case where the classpath
181:                         * contains the JAXP _interfaces_ (ex. via xml-apis.jar)
182:                         * and the Xalan classes but does not actually
183:                         * contain a JAXP _implementation_.  In that case the
184:                         * check in XML.checkXMLRequirements() will pass
185:                         * and this class (SqlXmlUtil) will be instantiated
186:                         * successfully--which is how we get to this constructor.
187:                         * But then attempts to create a DocumentBuilderFactory
188:                         * will fail, bringing us here.  Note that we can't
189:                         * check for a valid JAXP implementation in the
190:                         * XML.checkXMLRequirements() method because we
191:                         * always want to allow the XML.java class to be
192:                         * instantiated, even if the required XML classes
193:                         * are not present--and that means that it (the
194:                         * XML class) cannot reference DocumentBuilder nor
195:                         * any of the JAXP classes directly.
196:                         */
197:                        throw StandardException.newException(
198:                                SQLState.LANG_MISSING_XML_CLASSES, "JAXP");
199:
200:                    }
201:
202:                    dBF.setValidating(false);
203:                    dBF.setNamespaceAware(true);
204:
205:                    // Load document builder that can be used for parsing XML.
206:                    dBuilder = dBF.newDocumentBuilder();
207:                    dBuilder.setErrorHandler(new XMLErrorHandler());
208:
209:                    // Load serializer for serializing XML into string according
210:                    // XML serialization rules.
211:                    loadSerializer();
212:
213:                } catch (StandardException se) {
214:
215:                    // Just rethrow it.
216:                    throw se;
217:
218:                } catch (Throwable t) {
219:
220:                    /* Must be something caused by JAXP or Xalan; wrap it in a
221:                     * StandardException and rethrow it. Note: we catch "Throwable"
222:                     * here to catch as many external errors as possible in order
223:                     * to minimize the chance of an uncaught JAXP/Xalan error (such
224:                     * as a NullPointerException) causing Derby to fail in a more
225:                     * serious way.  In particular, an uncaught Java exception
226:                     * like NPE can result in Derby throwing "ERROR 40XT0: An
227:                     * internal error was identified by RawStore module" for all
228:                     * statements on the connection after the failure--which we
229:                     * clearly don't want.  If we catch the error and wrap it,
230:                     * though, the statement will fail but Derby will continue to
231:                     * run as normal.
232:                     */
233:                    throw StandardException.newException(
234:                            SQLState.LANG_UNEXPECTED_XML_EXCEPTION, t, t
235:                                    .getMessage());
236:
237:                }
238:
239:                // At construction time we don't have an XML query expression
240:                // to compile.  If one is required, we'll load/compile it later.
241:                query = null;
242:            }
243:
244:            /**
245:             * Take the received string, which is an XML query expression,
246:             * compile it, and store the compiled query locally.  Note
247:             * that for now, we only support XPath because that's what
248:             * Xalan supports.
249:             *
250:             * @param queryExpr The XPath expression to compile
251:             */
252:            public void compileXQExpr(String queryExpr, String opName)
253:                    throws StandardException {
254:                try {
255:
256:                    /* The following XPath constructor compiles the expression
257:                     * as part of the construction process.  We have to pass
258:                     * in a PrefixResolver object in order to avoid NPEs when
259:                     * invalid/unknown functions are used, so we just create
260:                     * a dummy one, which means prefixes will not be resolved
261:                     * in the query (Xalan will just throw an error if a prefix
262:                     * is used).  In the future we may want to revisit this
263:                     * to make it easier for users to query based on namespaces.
264:                     */
265:                    query = new XPath(queryExpr, null,
266:                            new PrefixResolverDefault(dBuilder.newDocument()),
267:                            XPath.SELECT);
268:
269:                    this .queryExpr = queryExpr;
270:                    this .opName = opName;
271:                    this .recompileQuery = false;
272:
273:                } catch (Throwable te) {
274:
275:                    /* Something went wrong during compilation of the
276:                     * expression; wrap the error and re-throw it.
277:                     * Note: we catch "Throwable" here to catch as many
278:                     * Xalan-produced errors as possible in order to
279:                     * minimize the chance of an uncaught Xalan error
280:                     * (such as a NullPointerException) causing Derby
281:                     * to fail in a more serious way.  In particular, an
282:                     * uncaught Java exception like NPE can result in
283:                     * Derby throwing "ERROR 40XT0: An internal error was
284:                     * identified by RawStore module" for all statements on
285:                     * the connection after the failure--which we clearly
286:                     * don't want.  If we catch the error and wrap it,
287:                     * though, the statement will fail but Derby will
288:                     * continue to run as normal. 
289:                     */
290:                    throw StandardException.newException(
291:                            SQLState.LANG_XML_QUERY_ERROR, te, opName, te
292:                                    .getMessage());
293:
294:                }
295:            }
296:
297:            /**
298:             * Take a string representing an XML value and serialize it
299:             * according SQL/XML serialization rules.  Right now, we perform
300:             * this serialization by first parsing the string into a JAXP
301:             * Document object, and then applying the serialization semantics
302:             * to that Document.  That seems a bit inefficient, but neither
303:             * Xalan nor JAXP provides a more direct way to do this.
304:             *
305:             * @param xmlAsText String version of XML on which to perform
306:             *   serialization.
307:             * @return A properly serialized version of xmlAsText.
308:             */
309:            protected String serializeToString(String xmlAsText)
310:                    throws Exception {
311:                ArrayList aList = new ArrayList();
312:
313:                /* The call to dBuilder.parse() is a call to an external
314:                 * (w.r.t. to Derby) JAXP parser.  If the received XML
315:                 * text references an external DTD, then the JAXP parser
316:                 * will try to read that external DTD.  Thus we wrap the
317:                 * call to parse inside a privileged action to make sure
318:                 * that the JAXP parser has the required permissions for
319:                 * reading the DTD file.
320:                 */
321:                try {
322:
323:                    final InputSource is = new InputSource(new StringReader(
324:                            xmlAsText));
325:                    aList
326:                            .add(java.security.AccessController
327:                                    .doPrivileged(new java.security.PrivilegedExceptionAction() {
328:                                        public Object run() throws IOException,
329:                                                SAXException {
330:                                            return dBuilder.parse(is);
331:                                        }
332:                                    }));
333:
334:                } catch (java.security.PrivilegedActionException pae) {
335:
336:                    /* Unwrap the privileged exception so that the user can
337:                     * see what the underlying error is. For example, it could
338:                     * be an i/o error from parsing the XML value, which can
339:                     * happen if the XML value references an external DTD file
340:                     * but the JAXP parser hits an i/o error when trying to read
341:                     * the DTD.  In that case we want to throw the i/o error
342:                     * itself so that it does not appear as a security exception
343:                     * to the user.
344:                     */
345:                    throw pae.getException();
346:
347:                }
348:
349:                /* The second argument in the following call is for
350:                 * catching cases where we have a top-level (parentless)
351:                 * attribute node--but since we just created the list
352:                 * with a single Document node, we already we know we
353:                 * don't have a top-level attribute node in the list,
354:                 * so we don't have to worry.  Hence the "null" here.
355:                 */
356:                return serializeToString(aList, null);
357:            }
358:
359:            /**
360:             * Take an array list (sequence) of XML nodes and/or string values
361:             * and serialize that entire list according to SQL/XML serialization
362:             * rules, which ultimately point to XML serialization rules as
363:             * defined by w3c.  As part of that serialization process we have
364:             * to first "normalize" the sequence.  We do that by iterating through
365:             * the list and performing the steps for "sequence normalization" as
366:             * defined here:
367:             *
368:             * http://www.w3.org/TR/xslt-xquery-serialization/#serdm
369:             *
370:             * This method primarily focuses on taking the steps for normalization;
371:             * for the rest of the serialization work, we just make calls on the
372:             * DOMSerializer class provided by Xalan.
373:             *
374:             * @param items List of items to serialize
375:             * @param xmlVal XMLDataValue into which the serialized string
376:             *  returned by this method is ultimately going to be stored.
377:             *  This is used for keeping track of XML values that represent
378:             *  sequences having top-level (parentless) attribute nodes.
379:             * @return Single string holding the serialized version of the
380:             *  normalized sequence created from the items in the received
381:             *  list.
382:             */
383:            protected String serializeToString(ArrayList items,
384:                    XMLDataValue xmlVal) throws java.io.IOException {
385:                if ((items == null) || (items.size() == 0))
386:                    // nothing to do; return empty sequence.
387:                    return "";
388:
389:                java.io.StringWriter sWriter = new java.io.StringWriter();
390:
391:                // Serializer should have been set by now.
392:                if (SanityManager.DEBUG) {
393:                    SanityManager
394:                            .ASSERT(serializer != null,
395:                                    "Tried to serialize with uninitialized XML serializer.");
396:                }
397:
398:                serializer.setWriter(sWriter);
399:                DOMSerializer dSer = serializer.asDOMSerializer();
400:
401:                int sz = items.size();
402:                Object obj = null;
403:
404:                /* Step 1: Empty sequence.  If we have an empty sequence then we
405:                 * won't ever enter the for loop and the call to sWriter.toString()
406:                 * at the end of this method will return an empty string, as
407:                 * required.  Otherwise, for a non-empty sequence our "items"
408:                 * list already corresponds to "S1".
409:                 */
410:
411:                // Iterate through the list and serialize each item.
412:                boolean lastItemWasString = false;
413:                for (int i = 0; i < sz; i++) {
414:                    obj = items.get(i);
415:                    // if it's a string, then this corresponds to some atomic
416:                    // value, so just echo the string as it is.
417:                    if (obj instanceof  String) {
418:                        /* Step 2: Atomic values.  If "obj" is a string then it
419:                         * corresponds to some atomic value whose "lexical
420:                         * representation" is obj.  So we just take that.
421:                         */
422:
423:                        if (lastItemWasString) {
424:                            /* Step 3: Adjacent strings.  If we have multiple adjacent
425:                             * strings then concatenate them with a single space
426:                             * between them.
427:                             */
428:                            sWriter.write(" ");
429:                        }
430:
431:                        /* Step 4: Create a Text node from the adjacent strings.
432:                         * Since we're just going to serialize the Text node back
433:                         * into a string, we short-cut this step by skipping the
434:                         * creation of the Text node and just writing the string
435:                         * out directly to our serialized stream.
436:                         */
437:                        sWriter.write((String) obj);
438:                        lastItemWasString = true;
439:                    } else if (obj instanceof  Attr) {
440:                        /* Step 7a: Attribute nodes.  If there is an Attribute node
441:                         * node in the sequence then we have to throw a serialization
442:                         * error.  NOTE: The rules say we also have to throw an error
443:                         * for Namespace nodes, but JAXP doesn't define a "Namespace"
444:                         * object per se; it just defines namespace prefixes and URIs
445:                         * on other Nodes.  So we just check for attributes.  If we
446:                         * find one then we take note of the fact that the result has
447:                         * a parentless attribute node and later, if the user calls
448:                         * XMLSERIALIZE on the received XMLDataValue we'll throw the
449:                         * error as required.  Note that we currently only get here
450:                         * for the XMLQUERY operator, which means we're serializing
451:                         * a result sequence returned from Xalan and we're going to
452:                         * store the serialized version into a Derby XML value.  In
453:                         * that case the serialization is an internal operation--and
454:                         * since the user didn't ask for it, we don't want to throw
455:                         * the serialization error here.  If we did, then whenever an
456:                         * XMLQUERY operation returned a result sequence with a top-
457:                         * level attribute in it, the user would see a serialization
458:                         * error. That's not correct since it is technically okay for
459:                         * the XMLQUERY operation to return a sequence with an attribute
460:                         * node; it's just not okay for a user to explicitly try to
461:                         * serialize that sequence. So instead of throwing the error
462:                         * here, we just take note of the fact that the sequence has
463:                         * a top-level attribute.  Then later, IF the user makes an
464:                         * explicit call to serialize the sequence, we'll throw the
465:                         * appropriate error (see XML.XMLSerialize()).
466:                         */
467:                        if (xmlVal != null)
468:                            xmlVal.markAsHavingTopLevelAttr();
469:                        dSer.serialize((Node) obj);
470:                        lastItemWasString = false;
471:                    } else { // We have a Node, so try to serialize it.
472:                        Node n = (Node) obj;
473:                        if (n instanceof  Text) {
474:                            /* Step 6: Combine adjacent text nodes into a single
475:                             * text node.  Since we're just going to serialize the
476:                             * Text node back into a string, we short-cut this step
477:                             * by skipping the creation of a new Text node and just
478:                             * writing the text value out directly to our serialized
479:                             * stream.  Step 6 also says that empty text nodes should
480:                             * be dropped--but if the text node is empty, the call
481:                             * to getNodeValue() will return an empty string and
482:                             * thus we've effectively "dropped" the text node from
483:                             * the serialized result.  Note: it'd be cleaner to just
484:                             * call "serialize()" on the Text node like we do for
485:                             * all other Nodes, but Xalan doesn't allow that.  So
486:                             * use the getNodeValue() method instead.
487:                             */
488:                            sWriter.write(n.getNodeValue());
489:                        } else {
490:                            /* Steps 5 and 7b: Copy all non-attribute, non-text
491:                             * nodes to the "normalized sequence" and then serialize
492:                             * that normalized sequence.  We short-cut this by
493:                             * just letting Xalan do the serialization for every
494:                             * Node in the current list of items that wasn't
495:                             * "serialized" as an atomic value, attribute, or
496:                             * text node.
497:                             */
498:                            dSer.serialize(n);
499:                        }
500:
501:                        lastItemWasString = false;
502:                    }
503:                }
504:
505:                /* At this point sWriter holds the serialized version of the
506:                 * normalized sequence that corresponds to the received list
507:                 * of items.  So that's what we return.
508:                 */
509:                sWriter.flush();
510:                return sWriter.toString();
511:            }
512:
513:            /**
514:             * Evaluate this object's compiled XML query expression against
515:             * the received xmlContext.  Then if returnResults is false,
516:             * return an empty sequence (ArrayList) if evaluation yields
517:             * at least one item and return null if evaluation yields zero
518:             * items (the caller can then just check for null to see if the
519:             * query returned any items).  If returnResults is true, then return
520:             * return a sequence (ArrayList) containing all items returned
521:             * from evaluation of the expression.  This array list can contain
522:             * any combination of atomic values and XML nodes; it may also
523:             * be empty.
524:             *
525:             * Assumption here is that the query expression has already been
526:             * compiled and is stored in this.query.
527:             *
528:             * @param xmlContext The XML value against which to evaluate
529:             *  the stored (compiled) query expression
530:             * @param returnResults Whether or not to return the actual
531:             *  results of the query
532:             * @param resultXType The qualified XML type of the result
533:             *  of evaluating the expression, if returnResults is true.
534:             *  If the result is a sequence of exactly one Document node
535:             *  then this will be XML(DOCUMENT(ANY)); else it will be
536:             *  XML(SEQUENCE).  If returnResults is false, this value
537:             *  is ignored.
538:             * @return If returnResults is false then return an empty
539:             *  ArrayList if evaluation returned at least one item and return
540:             *  null otherwise.  If returnResults is true then return an
541:             *  array list containing all of the result items and return
542:             *  the qualified XML type via the resultXType parameter.
543:             * @exception Exception thrown on error (and turned into a
544:             *  StandardException by the caller).
545:             */
546:            protected ArrayList evalXQExpression(XMLDataValue xmlContext,
547:                    boolean returnResults, int[] resultXType) throws Exception {
548:                // if this object is in an SPS, we need to recompile the query
549:                if (recompileQuery) {
550:                    compileXQExpr(queryExpr, opName);
551:                }
552:
553:                // Make sure we have a compiled query.
554:                if (SanityManager.DEBUG) {
555:                    SanityManager.ASSERT((query != null)
556:                            && (query.getExpression() != null),
557:                            "Failed to locate compiled XML query expression.");
558:                }
559:
560:                /* Create a DOM node from the xmlContext, since that's how
561:                 * we feed the context to Xalan.  We do this by creating
562:                 * a Document node using DocumentBuilder, which means that
563:                 * the serialized form of the context node must be a string
564:                 * value that is parse-able by DocumentBuilder--i.e. it must
565:                 * constitute a valid XML document.  If that's true then
566:                 * the context item's qualified type will be DOC_ANY.
567:                 */
568:                if (xmlContext.getXType() != XML.XML_DOC_ANY) {
569:                    throw StandardException.newException(
570:                            SQLState.LANG_INVALID_XML_CONTEXT_ITEM,
571:                            (returnResults ? "XMLQUERY" : "XMLEXISTS"));
572:                }
573:
574:                Document docNode = null;
575:                docNode = dBuilder.parse(new InputSource(new StringReader(
576:                        xmlContext.getString())));
577:
578:                // Evaluate the expresion using Xalan.
579:                getXPathContext();
580:                xpContext.reset();
581:                XObject xOb = query.execute(xpContext, docNode, null);
582:
583:                if (!returnResults) {
584:                    // We don't want to return the actual results, we just
585:                    // want to know if there was at least one item in the
586:                    // result sequence.
587:                    if ((xOb instanceof  XNodeSet)
588:                            && (((XNodeSet) xOb).nodelist().getLength() > 0)) { // If we have a sequence (XNodeSet) of length greater
589:                        // than zero, then we know that at least one item
590:                        // "exists" in the result so return a non-null list.
591:                        return new ArrayList(0);
592:                    } else if (!(xOb instanceof  XNodeSet))
593:                        // we have a single atomic value, which means the result is
594:                        // non-empty.  So return a non-null list.
595:                        return new ArrayList(0);
596:                    else {
597:                        // return null; caller will take this to mean we have an
598:                        // an empty sequence.
599:                        return null;
600:                    }
601:                }
602:
603:                // Else process the results.
604:                NodeList nodeList = null;
605:                int numItems = 0;
606:                if (!(xOb instanceof  XNodeSet))
607:                    // then we only have a single (probably atomic) item.
608:                    numItems = 1;
609:                else {
610:                    nodeList = xOb.nodelist();
611:                    numItems = nodeList.getLength();
612:                }
613:
614:                // Return a list of the items contained in the query results.
615:                ArrayList itemRefs = new ArrayList();
616:                if (nodeList == null)
617:                    // result is a single, non-node value (ex. it's an atomic number);
618:                    // in this case, just take the string value.
619:                    itemRefs.add(xOb.str());
620:                else {
621:                    for (int i = 0; i < numItems; i++)
622:                        itemRefs.add(nodeList.item(i));
623:                }
624:
625:                nodeList = null;
626:
627:                /* Indicate what kind of XML result value we have.  If
628:                 * we have a sequence of exactly one Document then it
629:                 * is XMLPARSE-able and so we consider it to be of type
630:                 * XML_DOC_ANY (which means we can store it in a Derby
631:                 * XML column).
632:                 */
633:                if ((numItems == 1) && (itemRefs.get(0) instanceof  Document))
634:                    resultXType[0] = XML.XML_DOC_ANY;
635:                else
636:                    resultXType[0] = XML.XML_SEQUENCE;
637:
638:                return itemRefs;
639:            }
640:
641:            /* ****
642:             * Helper classes and methods.
643:             * */
644:
645:            /**
646:             * Create and return an instance of Xalan's XPathContext
647:             * that can be used to compile an XPath expression.
648:             */
649:            private XPathContext getXPathContext() {
650:                if (xpContext == null)
651:                    xpContext = new XPathContext();
652:
653:                return xpContext;
654:            }
655:
656:            /**
657:             * Create an instance of Xalan serializer for the sake of
658:             * serializing an XML value according the SQL/XML specification
659:             * for serialization.
660:             */
661:            private void loadSerializer() throws java.io.IOException {
662:                java.io.StringWriter sWriter = new java.io.StringWriter();
663:
664:                // Set serialization properties.
665:                Properties props = OutputProperties
666:                        .getDefaultMethodProperties("xml");
667:
668:                // SQL/XML[2006] 10.15:General Rules:6 says method is "xml".
669:                props.setProperty(OutputKeys.METHOD, "xml");
670:
671:                /* Since the XMLSERIALIZE operator doesn't currently support
672:                 * the DOCUMENT nor CONTENT keywords, SQL/XML spec says that
673:                 * the default is CONTENT (6.7:Syntax Rules:2.a).  Further,
674:                 * since the XMLSERIALIZE operator doesn't currently support the
675:                 * <XML declaration option> syntax, the SQL/XML spec says
676:                 * that the default for that option is "Unknown" (6.7:General
677:                 * Rules:2.f).  Put those together and that in turn means that
678:                 * the value of "OMIT XML DECLARATION" must be "Yes", as
679:                 * stated in section 10.15:General Rules:8.c.  SO, that's what
680:                 * we set here.
681:                 *
682:                 * NOTE: currently the only way to view the contents of an
683:                 * XML column is by using an explicit XMLSERIALIZE operator.
684:                 * This means that if an XML document is stored and it
685:                 * begins with an XML declaration, the user will never be
686:                 * able to _see_ that declaration after inserting the doc
687:                 * because, as explained above, our current support for
688:                 * XMLSERIALIZE dictates that the declaration must be
689:                 * omitted.  Similarly, other transformations that may
690:                 * occur from serialization (ex. entity replacement,
691:                 * attribute order, single-to-double quotes, etc)) will
692:                 * always be in effect for the string returned to the user;
693:                 * the original form of the XML document, if different
694:                 * from the serialized version, is not currently retrievable.
695:                 */
696:                props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
697:
698:                // We serialize everything as UTF-8 to match what we
699:                // store on disk.
700:                props.setProperty(OutputKeys.ENCODING, "UTF-8");
701:
702:                // Load the serializer with the correct properties.
703:                serializer = SerializerFactory.getSerializer(props);
704:                return;
705:            }
706:
707:            /* ****
708:             * Formatable interface implementation
709:             * */
710:
711:            /** 
712:             * @see java.io.Externalizable#writeExternal 
713:             * 
714:             * @exception IOException on error
715:             */
716:            public void writeExternal(ObjectOutput out) throws IOException {
717:                // query may be null
718:                if (query == null) {
719:                    out.writeBoolean(false);
720:                } else {
721:                    out.writeBoolean(true);
722:                    out.writeObject(queryExpr);
723:                    out.writeObject(opName);
724:                }
725:            }
726:
727:            /** 
728:             * @see java.io.Externalizable#readExternal 
729:             *
730:             * @exception IOException on error
731:             * @exception ClassNotFoundException on error
732:             */
733:            public void readExternal(ObjectInput in) throws IOException,
734:                    ClassNotFoundException {
735:                if (in.readBoolean()) {
736:                    queryExpr = (String) in.readObject();
737:                    opName = (String) in.readObject();
738:                    recompileQuery = true;
739:                }
740:            }
741:
742:            /**
743:             * Get the formatID which corresponds to this class.
744:             *
745:             * @return	the formatID of this class
746:             */
747:            public int getTypeFormatId() {
748:                return StoredFormatIds.SQL_XML_UTIL_V01_ID;
749:            }
750:
751:            /*
752:             ** The XMLErrorHandler class is just a generic implementation
753:             ** of the ErrorHandler interface.  It allows us to catch
754:             ** and process XML parsing errors in a graceful manner.
755:             */
756:            private class XMLErrorHandler implements  ErrorHandler {
757:                public void error(SAXParseException exception)
758:                        throws SAXException {
759:                    throw new SAXException(exception);
760:                }
761:
762:                public void fatalError(SAXParseException exception)
763:                        throws SAXException {
764:                    throw new SAXException(exception);
765:                }
766:
767:                public void warning(SAXParseException exception)
768:                        throws SAXException {
769:                    throw new SAXException(exception);
770:                }
771:            }
772:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.