KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > jaxp > validation > DOMValidatorHelper


1 /*
2  * Copyright 2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.jaxp.validation;
18
19 import java.io.IOException JavaDoc;
20 import java.util.Enumeration JavaDoc;
21 import java.util.Locale JavaDoc;
22
23 import javax.xml.parsers.DocumentBuilder JavaDoc;
24 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
25 import javax.xml.parsers.ParserConfigurationException JavaDoc;
26 import javax.xml.transform.Result JavaDoc;
27 import javax.xml.transform.Source JavaDoc;
28 import javax.xml.transform.dom.DOMResult JavaDoc;
29 import javax.xml.transform.dom.DOMSource JavaDoc;
30
31 import org.apache.xerces.impl.Constants;
32 import org.apache.xerces.impl.XMLErrorReporter;
33 import org.apache.xerces.impl.validation.EntityState;
34 import org.apache.xerces.impl.validation.ValidationManager;
35 import org.apache.xerces.impl.xs.XMLSchemaValidator;
36 import org.apache.xerces.impl.xs.util.SimpleLocator;
37 import org.apache.xerces.util.NamespaceSupport;
38 import org.apache.xerces.util.SymbolTable;
39 import org.apache.xerces.util.XMLAttributesImpl;
40 import org.apache.xerces.util.XMLSymbols;
41 import org.apache.xerces.xni.NamespaceContext;
42 import org.apache.xerces.xni.QName;
43 import org.apache.xerces.xni.XMLString;
44 import org.apache.xerces.xni.XNIException;
45 import org.apache.xerces.xni.parser.XMLParseException;
46 import org.w3c.dom.Attr JavaDoc;
47 import org.w3c.dom.CDATASection JavaDoc;
48 import org.w3c.dom.Comment JavaDoc;
49 import org.w3c.dom.Document JavaDoc;
50 import org.w3c.dom.DocumentType JavaDoc;
51 import org.w3c.dom.Entity JavaDoc;
52 import org.w3c.dom.NamedNodeMap JavaDoc;
53 import org.w3c.dom.Node JavaDoc;
54 import org.w3c.dom.ProcessingInstruction JavaDoc;
55 import org.w3c.dom.Text JavaDoc;
56 import org.xml.sax.SAXException JavaDoc;
57
58 /**
59  * <p>A validator helper for <code>DOMSource</code>s.</p>
60  *
61  * @author Michael Glavassevich, IBM
62  * @version $Id: DOMValidatorHelper.java,v 1.5 2005/06/23 05:38:35 mrglavas Exp $
63  */

64 final class DOMValidatorHelper implements ValidatorHelper, EntityState {
65     
66     //
67
// Constants
68
//
69

70     /** Chunk size (1024). */
71     private static final int CHUNK_SIZE = (1 << 10);
72     
73     /** Chunk mask (CHUNK_SIZE - 1). */
74     private static final int CHUNK_MASK = CHUNK_SIZE - 1;
75     
76     // property identifiers
77

78     /** Property identifier: error reporter. */
79     private static final String JavaDoc ERROR_REPORTER =
80         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
81     
82     /** Property identifier: namespace context. */
83     private static final String JavaDoc NAMESPACE_CONTEXT =
84         Constants.XERCES_PROPERTY_PREFIX + Constants.NAMESPACE_CONTEXT_PROPERTY;
85     
86     /** Property identifier: XML Schema validator. */
87     private static final String JavaDoc SCHEMA_VALIDATOR =
88         Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_VALIDATOR_PROPERTY;
89     
90     /** Property identifier: symbol table. */
91     private static final String JavaDoc SYMBOL_TABLE =
92         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
93     
94     /** Property identifier: validation manager. */
95     private static final String JavaDoc VALIDATION_MANAGER =
96         Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
97     
98     //
99
// Data
100
//
101

102     /** Error reporter. */
103     private XMLErrorReporter fErrorReporter;
104     
105     /** The namespace context of this document: stores namespaces in scope. **/
106     private NamespaceSupport fNamespaceContext;
107     
108     /** The namespace context of the DOMSource, includes context from ancestor nodes. **/
109     private DOMNamespaceContext fDOMNamespaceContext = new DOMNamespaceContext();
110     
111     /** Schema validator. **/
112     private XMLSchemaValidator fSchemaValidator;
113     
114     /** Symbol table **/
115     private SymbolTable fSymbolTable;
116     
117     /** Validation manager. **/
118     private ValidationManager fValidationManager;
119     
120     /** Component manager. **/
121     private XMLSchemaValidatorComponentManager fComponentManager;
122     
123     /** Simple Locator. **/
124     private final SimpleLocator fXMLLocator = new SimpleLocator(null, null, -1, -1, -1);
125     
126     /** DOM document handler. **/
127     private DOMDocumentHandler fDOMValidatorHandler;
128     
129     /** DOM result augmentor. **/
130     private final DOMResultAugmentor fDOMResultAugmentor = new DOMResultAugmentor(this);
131     
132     /** DOM result builder. **/
133     private final DOMResultBuilder fDOMResultBuilder = new DOMResultBuilder();
134     
135     /** Map for tracking unparsed entities. **/
136     private NamedNodeMap JavaDoc fEntities = null;
137     
138     /** Array for holding character data. **/
139     private char [] fCharBuffer = new char[CHUNK_SIZE];
140     
141     /** Root node. **/
142     private Node JavaDoc fRoot;
143     
144     /** Current element. **/
145     private Node JavaDoc fCurrentElement;
146     
147     /** Fields for start element, end element and characters. **/
148     final QName fElementQName = new QName();
149     final QName fAttributeQName = new QName();
150     final XMLAttributesImpl fAttributes = new XMLAttributesImpl();
151     final XMLString fTempString = new XMLString();
152     
153     public DOMValidatorHelper(XMLSchemaValidatorComponentManager componentManager) {
154         fComponentManager = componentManager;
155         fErrorReporter = (XMLErrorReporter) fComponentManager.getProperty(ERROR_REPORTER);
156         fNamespaceContext = (NamespaceSupport) fComponentManager.getProperty(NAMESPACE_CONTEXT);
157         fSchemaValidator = (XMLSchemaValidator) fComponentManager.getProperty(SCHEMA_VALIDATOR);
158         fSymbolTable = (SymbolTable) fComponentManager.getProperty(SYMBOL_TABLE);
159         fValidationManager = (ValidationManager) fComponentManager.getProperty(VALIDATION_MANAGER);
160     }
161     
162     /*
163      * ValidatorHelper methods
164      */

165     
166     public void validate(Source JavaDoc source, Result JavaDoc result)
167         throws SAXException JavaDoc, IOException JavaDoc {
168         if (result instanceof DOMResult JavaDoc || result == null) {
169             final DOMSource JavaDoc domSource = (DOMSource JavaDoc) source;
170             final DOMResult JavaDoc domResult = (DOMResult JavaDoc) result;
171             Node JavaDoc node = domSource.getNode();
172             fRoot = node;
173             if (node != null) {
174                 fComponentManager.reset();
175                 fValidationManager.setEntityState(this);
176                 fDOMNamespaceContext.reset();
177                 String JavaDoc systemId = domSource.getSystemId();
178                 fXMLLocator.setLiteralSystemId(systemId);
179                 fXMLLocator.setExpandedSystemId(systemId);
180                 fErrorReporter.setDocumentLocator(fXMLLocator);
181                 try {
182                     // regardless of what type of node this is, fire start and end document events
183
setupEntityMap((node.getNodeType() == Node.DOCUMENT_NODE) ? (Document JavaDoc) node : node.getOwnerDocument());
184                     setupDOMResultHandler(domSource, domResult);
185                     fSchemaValidator.startDocument(fXMLLocator, null, fDOMNamespaceContext, null);
186                     validate(node);
187                     fSchemaValidator.endDocument(null);
188                 }
189                 catch (XMLParseException e) {
190                     throw Util.toSAXParseException(e);
191                 }
192                 catch (XNIException e) {
193                     throw Util.toSAXException(e);
194                 }
195                 finally {
196                     // Release references to application objects
197
fRoot = null;
198                     fCurrentElement = null;
199                     fEntities = null;
200                     if (fDOMValidatorHandler != null) {
201                         fDOMValidatorHandler.setDOMResult(null);
202                     }
203                 }
204             }
205             return;
206         }
207         throw new IllegalArgumentException JavaDoc(JAXPValidationMessageFormatter.formatMessage(Locale.getDefault(),
208                 "SourceResultMismatch",
209                 new Object JavaDoc [] {source.getClass().getName(), result.getClass().getName()}));
210     }
211     
212     /*
213      * EntityState methods
214      */

215     
216     public boolean isEntityDeclared(String JavaDoc name) {
217         return false;
218     }
219     
220     public boolean isEntityUnparsed(String JavaDoc name) {
221         if (fEntities != null) {
222             Entity JavaDoc entity = (Entity JavaDoc) fEntities.getNamedItem(name);
223             if (entity != null) {
224                 return (entity.getNotationName() != null);
225             }
226         }
227         return false;
228     }
229     
230     /*
231      * Other methods
232      */

233     
234     /** Traverse the DOM and fire events to the schema validator. */
235     private void validate(Node JavaDoc node) {
236         final Node JavaDoc top = node;
237         // Performs a non-recursive traversal of the DOM. This
238
// will avoid a stack overflow for DOMs with high depth.
239
while (node != null) {
240             beginNode(node);
241             Node JavaDoc next = node.getFirstChild();
242             while (next == null) {
243                 finishNode(node);
244                 if (top == node) {
245                     break;
246                 }
247                 next = node.getNextSibling();
248                 if (next == null) {
249                     node = node.getParentNode();
250                     if (node == null || top == node) {
251                         if (node != null) {
252                             finishNode(node);
253                         }
254                         next = null;
255                         break;
256                     }
257                 }
258             }
259             node = next;
260         }
261     }
262     
263     /** Do processing for the start of a node. */
264     private void beginNode(Node JavaDoc node) {
265         switch (node.getNodeType()) {
266             case Node.ELEMENT_NODE:
267                 fCurrentElement = node;
268                 // push namespace context
269
fNamespaceContext.pushContext();
270                 // start element
271
fillQName(fElementQName, node);
272                 processAttributes(node.getAttributes());
273                 fSchemaValidator.startElement(fElementQName, fAttributes, null);
274                 break;
275             case Node.TEXT_NODE:
276                 if (fDOMValidatorHandler != null) {
277                     fDOMValidatorHandler.setIgnoringCharacters(true);
278                     sendCharactersToValidator(node.getNodeValue());
279                     fDOMValidatorHandler.setIgnoringCharacters(false);
280                     fDOMValidatorHandler.characters((Text JavaDoc) node);
281                 }
282                 else {
283                     sendCharactersToValidator(node.getNodeValue());
284                 }
285                 break;
286             case Node.CDATA_SECTION_NODE:
287                 if (fDOMValidatorHandler != null) {
288                     fDOMValidatorHandler.setIgnoringCharacters(true);
289                     fSchemaValidator.startCDATA(null);
290                     sendCharactersToValidator(node.getNodeValue());
291                     fSchemaValidator.endCDATA(null);
292                     fDOMValidatorHandler.setIgnoringCharacters(false);
293                     fDOMValidatorHandler.cdata((CDATASection JavaDoc) node);
294                 }
295                 else {
296                     fSchemaValidator.startCDATA(null);
297                     sendCharactersToValidator(node.getNodeValue());
298                     fSchemaValidator.endCDATA(null);
299                 }
300                 break;
301             case Node.PROCESSING_INSTRUCTION_NODE:
302                 /**
303                  * The validator does nothing with processing instructions so bypass it.
304                  * Send the ProcessingInstruction node directly to the result builder.
305                  */

306                 if (fDOMValidatorHandler != null) {
307                     fDOMValidatorHandler.processingInstruction((ProcessingInstruction JavaDoc) node);
308                 }
309                 break;
310             case Node.COMMENT_NODE:
311                 /**
312                  * The validator does nothing with comments so bypass it.
313                  * Send the Comment node directly to the result builder.
314                  */

315                 if (fDOMValidatorHandler != null) {
316                     fDOMValidatorHandler.comment((Comment JavaDoc) node);
317                 }
318                 break;
319             case Node.DOCUMENT_TYPE_NODE:
320                 /**
321                  * Send the DocumentType node directly to the result builder.
322                  */

323                 if (fDOMValidatorHandler != null) {
324                     fDOMValidatorHandler.doctypeDecl((DocumentType JavaDoc) node);
325                 }
326                 break;
327             default: // Ignore other node types.
328
break;
329         }
330     }
331     
332     /** Do processing for the end of a node. */
333     private void finishNode(Node JavaDoc node) {
334         if (node.getNodeType() == Node.ELEMENT_NODE) {
335             fCurrentElement = node;
336             // end element
337
fillQName(fElementQName, node);
338             fSchemaValidator.endElement(fElementQName, null);
339             // pop namespace context
340
fNamespaceContext.popContext();
341         }
342     }
343     
344     /**
345      * Extracts NamedNodeMap of entities. We need this to validate
346      * elements and attributes of type xs:ENTITY, xs:ENTITIES or
347      * types dervied from them.
348      */

349     private void setupEntityMap(Document JavaDoc doc) {
350         if (doc != null) {
351             DocumentType JavaDoc docType = doc.getDoctype();
352             if (docType != null) {
353                 fEntities = docType.getEntities();
354                 return;
355             }
356         }
357         fEntities = null;
358     }
359     
360     /**
361      * Sets up handler for <code>DOMResult</code>.
362      */

363     private void setupDOMResultHandler(DOMSource JavaDoc source, DOMResult JavaDoc result) throws SAXException JavaDoc {
364         // If there's no DOMResult, unset the validator handler
365
if (result == null) {
366             fDOMValidatorHandler = null;
367             fSchemaValidator.setDocumentHandler(null);
368             return;
369         }
370         final Node JavaDoc nodeResult = result.getNode();
371         // If the source node and result node are the same use the DOMResultAugmentor.
372
// Otherwise use the DOMResultBuilder.
373
if (source.getNode() == nodeResult) {
374             fDOMValidatorHandler = fDOMResultAugmentor;
375             fDOMResultAugmentor.setDOMResult(result);
376             fSchemaValidator.setDocumentHandler(fDOMResultAugmentor);
377             return;
378         }
379         if (result.getNode() == null) {
380             try {
381                 DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
382                 factory.setNamespaceAware(true);
383                 DocumentBuilder JavaDoc builder = factory.newDocumentBuilder();
384                 result.setNode(builder.newDocument());
385             }
386             catch (ParserConfigurationException JavaDoc e) {
387                 throw new SAXException JavaDoc(e);
388             }
389         }
390         fDOMValidatorHandler = fDOMResultBuilder;
391         fDOMResultBuilder.setDOMResult(result);
392         fSchemaValidator.setDocumentHandler(fDOMResultBuilder);
393     }
394     
395     private void fillQName(QName toFill, Node JavaDoc node) {
396         final String JavaDoc prefix = node.getPrefix();
397         final String JavaDoc localName = node.getLocalName();
398         final String JavaDoc rawName = node.getNodeName();
399         final String JavaDoc namespace = node.getNamespaceURI();
400         toFill.prefix = (prefix != null) ? fSymbolTable.addSymbol(prefix) : XMLSymbols.EMPTY_STRING;
401         toFill.localpart = (localName != null) ? fSymbolTable.addSymbol(localName) : XMLSymbols.EMPTY_STRING;
402         toFill.rawname = (rawName != null) ? fSymbolTable.addSymbol(rawName) : XMLSymbols.EMPTY_STRING;
403         toFill.uri = (namespace != null && namespace.length() > 0) ? fSymbolTable.addSymbol(namespace) : null;
404     }
405     
406     private void processAttributes(NamedNodeMap JavaDoc attrMap) {
407         final int attrCount = attrMap.getLength();
408         fAttributes.removeAllAttributes();
409         for (int i = 0; i < attrCount; ++i) {
410             Attr JavaDoc attr = (Attr JavaDoc) attrMap.item(i);
411             String JavaDoc value = attr.getValue();
412             if (value == null) {
413                 value = XMLSymbols.EMPTY_STRING;
414             }
415             fillQName(fAttributeQName, attr);
416             // REVISIT: Assuming all attributes are of type CDATA. The actual type may not matter. -- mrglavas
417
fAttributes.addAttributeNS(fAttributeQName, XMLSymbols.fCDATASymbol, value);
418             fAttributes.setSpecified(i, attr.getSpecified());
419             // REVISIT: Should we be looking at non-namespace attributes
420
// for additional mappings? Should we detect illegal namespace
421
// declarations and exclude them from the context? -- mrglavas
422
if (fAttributeQName.uri == NamespaceContext.XMLNS_URI) {
423                 // process namespace attribute
424
if (fAttributeQName.prefix == XMLSymbols.PREFIX_XMLNS) {
425                     fNamespaceContext.declarePrefix(fAttributeQName.localpart, value.length() != 0 ? fSymbolTable.addSymbol(value) : null);
426                 }
427                 else {
428                     fNamespaceContext.declarePrefix(XMLSymbols.EMPTY_STRING, value.length() != 0 ? fSymbolTable.addSymbol(value) : null);
429                 }
430             }
431         }
432     }
433     
434     private void sendCharactersToValidator(String JavaDoc str) {
435         if (str != null) {
436             final int length = str.length();
437             final int remainder = length & CHUNK_MASK;
438             if (remainder > 0) {
439                 str.getChars(0, remainder, fCharBuffer, 0);
440                 fTempString.setValues(fCharBuffer, 0, remainder);
441                 fSchemaValidator.characters(fTempString, null);
442             }
443             int i = remainder;
444             while (i < length) {
445                 str.getChars(i, i += CHUNK_SIZE, fCharBuffer, 0);
446                 fTempString.setValues(fCharBuffer, 0, CHUNK_SIZE);
447                 fSchemaValidator.characters(fTempString, null);
448             }
449         }
450     }
451     
452     Node JavaDoc getCurrentElement() {
453         return fCurrentElement;
454     }
455     
456     /**
457      * NamespaceContext for the DOMSource, includes context for ancestor nodes.
458      */

459     final class DOMNamespaceContext implements NamespaceContext {
460         
461         //
462
// Data
463
//
464

465         /**
466          * Namespace binding information. This array is composed of a
467          * series of tuples containing the namespace binding information:
468          * &lt;prefix, uri&gt;.
469          */

470         protected String JavaDoc[] fNamespace = new String JavaDoc[16 * 2];
471
472         /** The size of the namespace information array. */
473         protected int fNamespaceSize = 0;
474         
475         /**
476          * Flag indicating whether the namespace context
477          * has been from the root node's ancestors.
478          */

479         protected boolean fDOMContextBuilt = false;
480         
481         //
482
// Methods
483
//
484

485         public void pushContext() {
486             fNamespaceContext.pushContext();
487         }
488
489         public void popContext() {
490             fNamespaceContext.popContext();
491         }
492
493         public boolean declarePrefix(String JavaDoc prefix, String JavaDoc uri) {
494             return fNamespaceContext.declarePrefix(prefix, uri);
495         }
496
497         public String JavaDoc getURI(String JavaDoc prefix) {
498             String JavaDoc uri = fNamespaceContext.getURI(prefix);
499             if (uri == null) {
500                 if (!fDOMContextBuilt) {
501                     fillNamespaceContext();
502                     fDOMContextBuilt = true;
503                 }
504                 if (fNamespaceSize > 0 &&
505                     !fNamespaceContext.containsPrefix(prefix)) {
506                     uri = getURI0(prefix);
507                 }
508             }
509             return uri;
510         }
511
512         public String JavaDoc getPrefix(String JavaDoc uri) {
513             return fNamespaceContext.getPrefix(uri);
514         }
515
516         public int getDeclaredPrefixCount() {
517             return fNamespaceContext.getDeclaredPrefixCount();
518         }
519
520         public String JavaDoc getDeclaredPrefixAt(int index) {
521             return fNamespaceContext.getDeclaredPrefixAt(index);
522         }
523
524         public Enumeration JavaDoc getAllPrefixes() {
525             return fNamespaceContext.getAllPrefixes();
526         }
527
528         public void reset() {
529             fDOMContextBuilt = false;
530             fNamespaceSize = 0;
531         }
532         
533         private void fillNamespaceContext() {
534             if (fRoot != null) {
535                 Node JavaDoc currentNode = fRoot.getParentNode();
536                 while (currentNode != null) {
537                     if (Node.ELEMENT_NODE == currentNode.getNodeType()) {
538                         NamedNodeMap JavaDoc attributes = currentNode.getAttributes();
539                         final int attrCount = attributes.getLength();
540                         for (int i = 0; i < attrCount; ++i) {
541                             Attr JavaDoc attr = (Attr JavaDoc) attributes.item(i);
542                             String JavaDoc value = attr.getValue();
543                             if (value == null) {
544                                 value = XMLSymbols.EMPTY_STRING;
545                             }
546                             fillQName(fAttributeQName, attr);
547                             // REVISIT: Should we be looking at non-namespace attributes
548
// for additional mappings? Should we detect illegal namespace
549
// declarations and exclude them from the context? -- mrglavas
550
if (fAttributeQName.uri == NamespaceContext.XMLNS_URI) {
551                                 // process namespace attribute
552
if (fAttributeQName.prefix == XMLSymbols.PREFIX_XMLNS) {
553                                     declarePrefix0(fAttributeQName.localpart, value.length() != 0 ? fSymbolTable.addSymbol(value) : null);
554                                 }
555                                 else {
556                                     declarePrefix0(XMLSymbols.EMPTY_STRING, value.length() != 0 ? fSymbolTable.addSymbol(value) : null);
557                                 }
558                             }
559                         }
560                         
561                     }
562                     currentNode = currentNode.getParentNode();
563                 }
564             }
565         }
566         
567         private void declarePrefix0(String JavaDoc prefix, String JavaDoc uri) {
568             // resize array, if needed
569
if (fNamespaceSize == fNamespace.length) {
570                 String JavaDoc[] namespacearray = new String JavaDoc[fNamespaceSize * 2];
571                 System.arraycopy(fNamespace, 0, namespacearray, 0, fNamespaceSize);
572                 fNamespace = namespacearray;
573             }
574
575             // bind prefix to uri in current context
576
fNamespace[fNamespaceSize++] = prefix;
577             fNamespace[fNamespaceSize++] = uri;
578         }
579         
580         private String JavaDoc getURI0(String JavaDoc prefix) {
581             // find prefix in the DOM context
582
for (int i = 0; i < fNamespaceSize; i += 2) {
583                 if (fNamespace[i] == prefix) {
584                     return fNamespace[i + 1];
585                 }
586             }
587             // prefix not found
588
return null;
589         }
590     }
591     
592 } // DOMValidatorHelper
593
Popular Tags