KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > 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 com.sun.org.apache.xerces.internal.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 com.sun.org.apache.xerces.internal.impl.Constants;
32 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
33 import com.sun.org.apache.xerces.internal.impl.validation.EntityState;
34 import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
35 import com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator;
36 import com.sun.org.apache.xerces.internal.impl.xs.util.SimpleLocator;
37 import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
38 import com.sun.org.apache.xerces.internal.util.SymbolTable;
39 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
40 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
41 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
42 import com.sun.org.apache.xerces.internal.xni.QName;
43 import com.sun.org.apache.xerces.internal.xni.XMLString;
44 import com.sun.org.apache.xerces.internal.xni.XNIException;
45 import com.sun.org.apache.xerces.internal.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.2.2.1 2007/03/15 16:01:20 spericas 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         
401         toFill.uri = (namespace != null && namespace.length() > 0) ? fSymbolTable.addSymbol(namespace) : null;
402         toFill.rawname = (rawName != null) ? fSymbolTable.addSymbol(rawName) : XMLSymbols.EMPTY_STRING;
403         
404         // Is this a DOM level1 document?
405
if (localName == null) {
406             int k = rawName.indexOf(':');
407             if (k > 0) {
408                 toFill.prefix = fSymbolTable.addSymbol(rawName.substring(0, k));
409                 toFill.localpart = fSymbolTable.addSymbol(rawName.substring(k + 1));
410             }
411             else {
412                 toFill.prefix = XMLSymbols.EMPTY_STRING;
413                 toFill.localpart = toFill.rawname;
414             }
415         }
416         else {
417             toFill.prefix = (prefix != null) ? fSymbolTable.addSymbol(prefix) : XMLSymbols.EMPTY_STRING;
418             toFill.localpart = (localName != null) ? fSymbolTable.addSymbol(localName) : XMLSymbols.EMPTY_STRING;
419         }
420     }
421     
422     private void processAttributes(NamedNodeMap JavaDoc attrMap) {
423         final int attrCount = attrMap.getLength();
424         fAttributes.removeAllAttributes();
425         for (int i = 0; i < attrCount; ++i) {
426             Attr JavaDoc attr = (Attr JavaDoc) attrMap.item(i);
427             String JavaDoc value = attr.getValue();
428             if (value == null) {
429                 value = XMLSymbols.EMPTY_STRING;
430             }
431             fillQName(fAttributeQName, attr);
432             // REVISIT: Assuming all attributes are of type CDATA. The actual type may not matter. -- mrglavas
433
fAttributes.addAttributeNS(fAttributeQName, XMLSymbols.fCDATASymbol, value);
434             fAttributes.setSpecified(i, attr.getSpecified());
435             // REVISIT: Should we be looking at non-namespace attributes
436
// for additional mappings? Should we detect illegal namespace
437
// declarations and exclude them from the context? -- mrglavas
438
if (fAttributeQName.uri == NamespaceContext.XMLNS_URI) {
439                 // process namespace attribute
440
if (fAttributeQName.prefix == XMLSymbols.PREFIX_XMLNS) {
441                     fNamespaceContext.declarePrefix(fAttributeQName.localpart, value.length() != 0 ? fSymbolTable.addSymbol(value) : null);
442                 }
443                 else {
444                     fNamespaceContext.declarePrefix(XMLSymbols.EMPTY_STRING, value.length() != 0 ? fSymbolTable.addSymbol(value) : null);
445                 }
446             }
447         }
448     }
449     
450     private void sendCharactersToValidator(String JavaDoc str) {
451         if (str != null) {
452             final int length = str.length();
453             final int remainder = length & CHUNK_MASK;
454             if (remainder > 0) {
455                 str.getChars(0, remainder, fCharBuffer, 0);
456                 fTempString.setValues(fCharBuffer, 0, remainder);
457                 fSchemaValidator.characters(fTempString, null);
458             }
459             int i = remainder;
460             while (i < length) {
461                 str.getChars(i, i += CHUNK_SIZE, fCharBuffer, 0);
462                 fTempString.setValues(fCharBuffer, 0, CHUNK_SIZE);
463                 fSchemaValidator.characters(fTempString, null);
464             }
465         }
466     }
467     
468     Node JavaDoc getCurrentElement() {
469         return fCurrentElement;
470     }
471     
472     /**
473      * NamespaceContext for the DOMSource, includes context for ancestor nodes.
474      */

475     final class DOMNamespaceContext implements NamespaceContext {
476         
477         //
478
// Data
479
//
480

481         /**
482          * Namespace binding information. This array is composed of a
483          * series of tuples containing the namespace binding information:
484          * &lt;prefix, uri&gt;.
485          */

486         protected String JavaDoc[] fNamespace = new String JavaDoc[16 * 2];
487
488         /** The size of the namespace information array. */
489         protected int fNamespaceSize = 0;
490         
491         /**
492          * Flag indicating whether the namespace context
493          * has been from the root node's ancestors.
494          */

495         protected boolean fDOMContextBuilt = false;
496         
497         //
498
// Methods
499
//
500

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