KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > impl > XML11NSDocumentScannerImpl


1 /*
2  * Copyright 1999-2004 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.impl;
18
19 import java.io.IOException JavaDoc;
20
21 import org.apache.xerces.impl.dtd.XMLDTDValidatorFilter;
22 import org.apache.xerces.impl.msg.XMLMessageFormatter;
23 import org.apache.xerces.util.XMLAttributesImpl;
24 import org.apache.xerces.util.XMLSymbols;
25 import org.apache.xerces.xni.NamespaceContext;
26 import org.apache.xerces.xni.QName;
27 import org.apache.xerces.xni.XMLDocumentHandler;
28 import org.apache.xerces.xni.XNIException;
29 import org.apache.xerces.xni.parser.XMLComponentManager;
30 import org.apache.xerces.xni.parser.XMLConfigurationException;
31 import org.apache.xerces.xni.parser.XMLDocumentSource;
32
33 /**
34  * The scanner acts as the source for the document
35  * information which is communicated to the document handler.
36  *
37  * This class scans an XML document, checks if document has a DTD, and if
38  * DTD is not found the scanner will remove the DTD Validator from the pipeline and perform
39  * namespace binding.
40  *
41  * Note: This scanner should only be used when the namespace processing is on!
42  *
43  * <p>
44  * This component requires the following features and properties from the
45  * component manager that uses it:
46  * <ul>
47  * <li>http://xml.org/sax/features/namespaces {true} -- if the value of this
48  * feature is set to false this scanner must not be used.</li>
49  * <li>http://xml.org/sax/features/validation</li>
50  * <li>http://apache.org/xml/features/nonvalidating/load-external-dtd</li>
51  * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
52  * <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
53  * <li>http://apache.org/xml/properties/internal/symbol-table</li>
54  * <li>http://apache.org/xml/properties/internal/error-reporter</li>
55  * <li>http://apache.org/xml/properties/internal/entity-manager</li>
56  * <li>http://apache.org/xml/properties/internal/dtd-scanner</li>
57  * </ul>
58  *
59  * @xerces.internal
60  *
61  * @author Elena Litani, IBM
62  * @author Michael Glavassevich, IBM
63  *
64  * @version $Id: XML11NSDocumentScannerImpl.java,v 1.13 2004/10/04 21:45:49 mrglavas Exp $
65  */

66 public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
67
68     /**
69      * If is true, the dtd validator is no longer in the pipeline
70      * and the scanner should bind namespaces
71      */

72     protected boolean fBindNamespaces;
73
74     /**
75      * If validating parser, make sure we report an error in the
76      * scanner if DTD grammar is missing.
77      */

78     protected boolean fPerformValidation;
79
80     // private data
81
//
82

83     /** DTD validator */
84     private XMLDTDValidatorFilter fDTDValidator;
85     
86     /**
87      * Saw spaces after element name or between attributes.
88      *
89      * This is reserved for the case where scanning of a start element spans
90      * several methods, as is the case when scanning the start of a root element
91      * where a DTD external subset may be read after scanning the element name.
92      */

93     private boolean fSawSpace;
94
95     /**
96      * The scanner is responsible for removing DTD validator
97      * from the pipeline if it is not needed.
98      *
99      * @param validator the DTD validator from the pipeline
100      */

101     public void setDTDValidator(XMLDTDValidatorFilter validator) {
102         fDTDValidator = validator;
103     }
104
105     /**
106      * Scans a start element. This method will handle the binding of
107      * namespace information and notifying the handler of the start
108      * of the element.
109      * <p>
110      * <pre>
111      * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
112      * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
113      * </pre>
114      * <p>
115      * <strong>Note:</strong> This method assumes that the leading
116      * '&lt;' character has been consumed.
117      * <p>
118      * <strong>Note:</strong> This method uses the fElementQName and
119      * fAttributes variables. The contents of these variables will be
120      * destroyed. The caller should copy important information out of
121      * these variables before calling this method.
122      *
123      * @return True if element is empty. (i.e. It matches
124      * production [44].
125      */

126     protected boolean scanStartElement() throws IOException JavaDoc, XNIException {
127         if (DEBUG_CONTENT_SCANNING)
128             System.out.println(">>> scanStartElementNS()");
129
130         // Note: namespace processing is on by default
131
fEntityScanner.scanQName(fElementQName);
132         // REVISIT - [Q] Why do we need this local variable? -- mrglavas
133
String JavaDoc rawname = fElementQName.rawname;
134         if (fBindNamespaces) {
135             fNamespaceContext.pushContext();
136             if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
137                 if (fPerformValidation) {
138                     fErrorReporter.reportError(
139                         XMLMessageFormatter.XML_DOMAIN,
140                         "MSG_GRAMMAR_NOT_FOUND",
141                         new Object JavaDoc[] { rawname },
142                         XMLErrorReporter.SEVERITY_ERROR);
143
144                     if (fDoctypeName == null
145                         || !fDoctypeName.equals(rawname)) {
146                         fErrorReporter.reportError(
147                             XMLMessageFormatter.XML_DOMAIN,
148                             "RootElementTypeMustMatchDoctypedecl",
149                             new Object JavaDoc[] { fDoctypeName, rawname },
150                             XMLErrorReporter.SEVERITY_ERROR);
151                     }
152                 }
153             }
154         }
155
156         // push element stack
157
fCurrentElement = fElementStack.pushElement(fElementQName);
158
159         // attributes
160
boolean empty = false;
161         fAttributes.removeAllAttributes();
162         do {
163             // spaces
164
boolean sawSpace = fEntityScanner.skipSpaces();
165
166             // end tag?
167
int c = fEntityScanner.peekChar();
168             if (c == '>') {
169                 fEntityScanner.scanChar();
170                 break;
171             } else if (c == '/') {
172                 fEntityScanner.scanChar();
173                 if (!fEntityScanner.skipChar('>')) {
174                     reportFatalError(
175                         "ElementUnterminated",
176                         new Object JavaDoc[] { rawname });
177                 }
178                 empty = true;
179                 break;
180             } else if (!isValidNameStartChar(c) || !sawSpace) {
181                 // Second chance. Check if this character is a high
182
// surrogate of a valid name start character.
183
if (!isValidNameStartHighSurrogate(c) || !sawSpace) {
184                     reportFatalError(
185                         "ElementUnterminated",
186                         new Object JavaDoc[] { rawname });
187                 }
188             }
189
190             // attributes
191
scanAttribute(fAttributes);
192
193         } while (true);
194
195         if (fBindNamespaces) {
196             // REVISIT: is it required? forbit xmlns prefix for element
197
if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
198                 fErrorReporter.reportError(
199                     XMLMessageFormatter.XMLNS_DOMAIN,
200                     "ElementXMLNSPrefix",
201                     new Object JavaDoc[] { fElementQName.rawname },
202                     XMLErrorReporter.SEVERITY_FATAL_ERROR);
203             }
204
205             // bind the element
206
String JavaDoc prefix =
207                 fElementQName.prefix != null
208                     ? fElementQName.prefix
209                     : XMLSymbols.EMPTY_STRING;
210             // assign uri to the element
211
fElementQName.uri = fNamespaceContext.getURI(prefix);
212             // make sure that object in the element stack is updated as well
213
fCurrentElement.uri = fElementQName.uri;
214
215             if (fElementQName.prefix == null && fElementQName.uri != null) {
216                 fElementQName.prefix = XMLSymbols.EMPTY_STRING;
217                 // making sure that the object in the element stack is updated too.
218
fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
219             }
220             if (fElementQName.prefix != null && fElementQName.uri == null) {
221                 fErrorReporter.reportError(
222                     XMLMessageFormatter.XMLNS_DOMAIN,
223                     "ElementPrefixUnbound",
224                     new Object JavaDoc[] {
225                         fElementQName.prefix,
226                         fElementQName.rawname },
227                     XMLErrorReporter.SEVERITY_FATAL_ERROR);
228             }
229
230             // bind attributes (xmlns are already bound bellow)
231
int length = fAttributes.getLength();
232             for (int i = 0; i < length; i++) {
233                 fAttributes.getName(i, fAttributeQName);
234
235                 String JavaDoc aprefix =
236                     fAttributeQName.prefix != null
237                         ? fAttributeQName.prefix
238                         : XMLSymbols.EMPTY_STRING;
239                 String JavaDoc uri = fNamespaceContext.getURI(aprefix);
240                 // REVISIT: try removing the first "if" and see if it is faster.
241
//
242
if (fAttributeQName.uri != null
243                     && fAttributeQName.uri == uri) {
244                     continue;
245                 }
246                 if (aprefix != XMLSymbols.EMPTY_STRING) {
247                     fAttributeQName.uri = uri;
248                     if (uri == null) {
249                         fErrorReporter.reportError(
250                             XMLMessageFormatter.XMLNS_DOMAIN,
251                             "AttributePrefixUnbound",
252                             new Object JavaDoc[] {
253                                 fElementQName.rawname,
254                                 fAttributeQName.rawname,
255                                 aprefix },
256                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
257                     }
258                     fAttributes.setURI(i, uri);
259                 }
260             }
261
262             if (length > 1) {
263                 QName name = fAttributes.checkDuplicatesNS();
264                 if (name != null) {
265                     if (name.uri != null) {
266                         fErrorReporter.reportError(
267                             XMLMessageFormatter.XMLNS_DOMAIN,
268                             "AttributeNSNotUnique",
269                             new Object JavaDoc[] {
270                                 fElementQName.rawname,
271                                 name.localpart,
272                                 name.uri },
273                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
274                     } else {
275                         fErrorReporter.reportError(
276                             XMLMessageFormatter.XMLNS_DOMAIN,
277                             "AttributeNotUnique",
278                             new Object JavaDoc[] {
279                                 fElementQName.rawname,
280                                 name.rawname },
281                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
282                     }
283                 }
284             }
285         }
286
287         // call handler
288
if (fDocumentHandler != null) {
289             if (empty) {
290
291                 //decrease the markup depth..
292
fMarkupDepth--;
293
294                 // check that this element was opened in the same entity
295
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
296                     reportFatalError(
297                         "ElementEntityMismatch",
298                         new Object JavaDoc[] { fCurrentElement.rawname });
299                 }
300
301                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
302
303                 if (fBindNamespaces) {
304                     fNamespaceContext.popContext();
305                 }
306                 //pop the element off the stack..
307
fElementStack.popElement(fElementQName);
308             } else {
309                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
310             }
311         }
312
313         if (DEBUG_CONTENT_SCANNING)
314             System.out.println("<<< scanStartElement(): " + empty);
315         return empty;
316
317     } // scanStartElement():boolean
318

319     /**
320      * Scans the name of an element in a start or empty tag.
321      *
322      * @see #scanStartElement()
323      */

324     protected void scanStartElementName ()
325         throws IOException JavaDoc, XNIException {
326         // Note: namespace processing is on by default
327
fEntityScanner.scanQName(fElementQName);
328         // Must skip spaces here because the DTD scanner
329
// would consume them at the end of the external subset.
330
fSawSpace = fEntityScanner.skipSpaces();
331     } // scanStartElementName()
332

333     /**
334      * Scans the remainder of a start or empty tag after the element name.
335      *
336      * @see #scanStartElement
337      * @return True if element is empty.
338      */

339     protected boolean scanStartElementAfterName()
340         throws IOException JavaDoc, XNIException {
341
342         // REVISIT - [Q] Why do we need this local variable? -- mrglavas
343
String JavaDoc rawname = fElementQName.rawname;
344         if (fBindNamespaces) {
345             fNamespaceContext.pushContext();
346             if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
347                 if (fPerformValidation) {
348                     fErrorReporter.reportError(
349                         XMLMessageFormatter.XML_DOMAIN,
350                         "MSG_GRAMMAR_NOT_FOUND",
351                         new Object JavaDoc[] { rawname },
352                         XMLErrorReporter.SEVERITY_ERROR);
353
354                     if (fDoctypeName == null
355                         || !fDoctypeName.equals(rawname)) {
356                         fErrorReporter.reportError(
357                             XMLMessageFormatter.XML_DOMAIN,
358                             "RootElementTypeMustMatchDoctypedecl",
359                             new Object JavaDoc[] { fDoctypeName, rawname },
360                             XMLErrorReporter.SEVERITY_ERROR);
361                     }
362                 }
363             }
364         }
365
366         // push element stack
367
fCurrentElement = fElementStack.pushElement(fElementQName);
368
369         // attributes
370
boolean empty = false;
371         fAttributes.removeAllAttributes();
372         do {
373             
374             // end tag?
375
int c = fEntityScanner.peekChar();
376             if (c == '>') {
377                 fEntityScanner.scanChar();
378                 break;
379             } else if (c == '/') {
380                 fEntityScanner.scanChar();
381                 if (!fEntityScanner.skipChar('>')) {
382                     reportFatalError(
383                         "ElementUnterminated",
384                         new Object JavaDoc[] { rawname });
385                 }
386                 empty = true;
387                 break;
388             } else if (!isValidNameStartChar(c) || !fSawSpace) {
389                 // Second chance. Check if this character is a high
390
// surrogate of a valid name start character.
391
if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
392                     reportFatalError(
393                         "ElementUnterminated",
394                         new Object JavaDoc[] { rawname });
395                 }
396             }
397
398             // attributes
399
scanAttribute(fAttributes);
400             
401             // spaces
402
fSawSpace = fEntityScanner.skipSpaces();
403
404         } while (true);
405
406         if (fBindNamespaces) {
407             // REVISIT: is it required? forbit xmlns prefix for element
408
if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
409                 fErrorReporter.reportError(
410                     XMLMessageFormatter.XMLNS_DOMAIN,
411                     "ElementXMLNSPrefix",
412                     new Object JavaDoc[] { fElementQName.rawname },
413                     XMLErrorReporter.SEVERITY_FATAL_ERROR);
414             }
415
416             // bind the element
417
String JavaDoc prefix =
418                 fElementQName.prefix != null
419                     ? fElementQName.prefix
420                     : XMLSymbols.EMPTY_STRING;
421             // assign uri to the element
422
fElementQName.uri = fNamespaceContext.getURI(prefix);
423             // make sure that object in the element stack is updated as well
424
fCurrentElement.uri = fElementQName.uri;
425
426             if (fElementQName.prefix == null && fElementQName.uri != null) {
427                 fElementQName.prefix = XMLSymbols.EMPTY_STRING;
428                 // making sure that the object in the element stack is updated too.
429
fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
430             }
431             if (fElementQName.prefix != null && fElementQName.uri == null) {
432                 fErrorReporter.reportError(
433                     XMLMessageFormatter.XMLNS_DOMAIN,
434                     "ElementPrefixUnbound",
435                     new Object JavaDoc[] {
436                         fElementQName.prefix,
437                         fElementQName.rawname },
438                     XMLErrorReporter.SEVERITY_FATAL_ERROR);
439             }
440
441             // bind attributes (xmlns are already bound bellow)
442
int length = fAttributes.getLength();
443             for (int i = 0; i < length; i++) {
444                 fAttributes.getName(i, fAttributeQName);
445
446                 String JavaDoc aprefix =
447                     fAttributeQName.prefix != null
448                         ? fAttributeQName.prefix
449                         : XMLSymbols.EMPTY_STRING;
450                 String JavaDoc uri = fNamespaceContext.getURI(aprefix);
451                 // REVISIT: try removing the first "if" and see if it is faster.
452
//
453
if (fAttributeQName.uri != null
454                     && fAttributeQName.uri == uri) {
455                     continue;
456                 }
457                 if (aprefix != XMLSymbols.EMPTY_STRING) {
458                     fAttributeQName.uri = uri;
459                     if (uri == null) {
460                         fErrorReporter.reportError(
461                             XMLMessageFormatter.XMLNS_DOMAIN,
462                             "AttributePrefixUnbound",
463                             new Object JavaDoc[] {
464                                 fElementQName.rawname,
465                                 fAttributeQName.rawname,
466                                 aprefix },
467                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
468                     }
469                     fAttributes.setURI(i, uri);
470                 }
471             }
472
473             if (length > 1) {
474                 QName name = fAttributes.checkDuplicatesNS();
475                 if (name != null) {
476                     if (name.uri != null) {
477                         fErrorReporter.reportError(
478                             XMLMessageFormatter.XMLNS_DOMAIN,
479                             "AttributeNSNotUnique",
480                             new Object JavaDoc[] {
481                                 fElementQName.rawname,
482                                 name.localpart,
483                                 name.uri },
484                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
485                     } else {
486                         fErrorReporter.reportError(
487                             XMLMessageFormatter.XMLNS_DOMAIN,
488                             "AttributeNotUnique",
489                             new Object JavaDoc[] {
490                                 fElementQName.rawname,
491                                 name.rawname },
492                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
493                     }
494                 }
495             }
496         }
497
498         // call handler
499
if (fDocumentHandler != null) {
500             if (empty) {
501
502                 //decrease the markup depth..
503
fMarkupDepth--;
504
505                 // check that this element was opened in the same entity
506
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
507                     reportFatalError(
508                         "ElementEntityMismatch",
509                         new Object JavaDoc[] { fCurrentElement.rawname });
510                 }
511
512                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
513
514                 if (fBindNamespaces) {
515                     fNamespaceContext.popContext();
516                 }
517                 //pop the element off the stack..
518
fElementStack.popElement(fElementQName);
519             } else {
520                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
521             }
522         }
523
524         if (DEBUG_CONTENT_SCANNING)
525             System.out.println("<<< scanStartElementAfterName(): " + empty);
526         return empty;
527         
528     } // scanStartElementAfterName()
529

530     /**
531      * Scans an attribute.
532      * <p>
533      * <pre>
534      * [41] Attribute ::= Name Eq AttValue
535      * </pre>
536      * <p>
537      * <strong>Note:</strong> This method assumes that the next
538      * character on the stream is the first character of the attribute
539      * name.
540      * <p>
541      * <strong>Note:</strong> This method uses the fAttributeQName and
542      * fQName variables. The contents of these variables will be
543      * destroyed.
544      *
545      * @param attributes The attributes list for the scanned attribute.
546      */

547     protected void scanAttribute(XMLAttributesImpl attributes)
548         throws IOException JavaDoc, XNIException {
549         if (DEBUG_CONTENT_SCANNING)
550             System.out.println(">>> scanAttribute()");
551
552         // name
553
fEntityScanner.scanQName(fAttributeQName);
554
555         // equals
556
fEntityScanner.skipSpaces();
557         if (!fEntityScanner.skipChar('=')) {
558             reportFatalError(
559                 "EqRequiredInAttribute",
560                 new Object JavaDoc[] {
561                     fCurrentElement.rawname,
562                     fAttributeQName.rawname });
563         }
564         fEntityScanner.skipSpaces();
565
566         // content
567
int attrIndex;
568
569         if (fBindNamespaces) {
570             attrIndex = attributes.getLength();
571             attributes.addAttributeNS(
572                 fAttributeQName,
573                 XMLSymbols.fCDATASymbol,
574                 null);
575         } else {
576             int oldLen = attributes.getLength();
577             attrIndex =
578                 attributes.addAttribute(
579                     fAttributeQName,
580                     XMLSymbols.fCDATASymbol,
581                     null);
582
583             // WFC: Unique Att Spec
584
if (oldLen == attributes.getLength()) {
585                 reportFatalError(
586                     "AttributeNotUnique",
587                     new Object JavaDoc[] {
588                         fCurrentElement.rawname,
589                         fAttributeQName.rawname });
590             }
591         }
592
593         //REVISIT: one more case needs to be included: external PE and standalone is no
594
boolean isVC = fHasExternalDTD && !fStandalone;
595
596         // Scan attribute value and return true if the non-normalized and normalized value are the same
597
boolean isSameNormalizedAttr = scanAttributeValue(this.fTempString, fTempString2,
598                 fAttributeQName.rawname,isVC,fCurrentElement.rawname);
599         
600         String JavaDoc value = fTempString.toString();
601         attributes.setValue(attrIndex, value);
602         // If the non-normalized and normalized value are the same, avoid creating a new string.
603
if (!isSameNormalizedAttr) {
604             attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
605         }
606         attributes.setSpecified(attrIndex, true);
607
608         // record namespace declarations if any.
609
if (fBindNamespaces) {
610
611             String JavaDoc localpart = fAttributeQName.localpart;
612             String JavaDoc prefix =
613                 fAttributeQName.prefix != null
614                     ? fAttributeQName.prefix
615                     : XMLSymbols.EMPTY_STRING;
616             // when it's of form xmlns="..." or xmlns:prefix="...",
617
// it's a namespace declaration. but prefix:xmlns="..." isn't.
618
if (prefix == XMLSymbols.PREFIX_XMLNS
619                 || prefix == XMLSymbols.EMPTY_STRING
620                 && localpart == XMLSymbols.PREFIX_XMLNS) {
621
622                 // get the internalized value of this attribute
623
String JavaDoc uri = fSymbolTable.addSymbol(value);
624
625                 // 1. "xmlns" can't be bound to any namespace
626
if (prefix == XMLSymbols.PREFIX_XMLNS
627                     && localpart == XMLSymbols.PREFIX_XMLNS) {
628                     fErrorReporter.reportError(
629                         XMLMessageFormatter.XMLNS_DOMAIN,
630                         "CantBindXMLNS",
631                         new Object JavaDoc[] { fAttributeQName },
632                         XMLErrorReporter.SEVERITY_FATAL_ERROR);
633                 }
634
635                 // 2. the namespace for "xmlns" can't be bound to any prefix
636
if (uri == NamespaceContext.XMLNS_URI) {
637                     fErrorReporter.reportError(
638                         XMLMessageFormatter.XMLNS_DOMAIN,
639                         "CantBindXMLNS",
640                         new Object JavaDoc[] { fAttributeQName },
641                         XMLErrorReporter.SEVERITY_FATAL_ERROR);
642                 }
643
644                 // 3. "xml" can't be bound to any other namespace than it's own
645
if (localpart == XMLSymbols.PREFIX_XML) {
646                     if (uri != NamespaceContext.XML_URI) {
647                         fErrorReporter.reportError(
648                             XMLMessageFormatter.XMLNS_DOMAIN,
649                             "CantBindXML",
650                             new Object JavaDoc[] { fAttributeQName },
651                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
652                     }
653                 }
654                 // 4. the namespace for "xml" can't be bound to any other prefix
655
else {
656                     if (uri == NamespaceContext.XML_URI) {
657                         fErrorReporter.reportError(
658                             XMLMessageFormatter.XMLNS_DOMAIN,
659                             "CantBindXML",
660                             new Object JavaDoc[] { fAttributeQName },
661                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
662                     }
663                 }
664
665                 prefix =
666                     localpart != XMLSymbols.PREFIX_XMLNS
667                         ? localpart
668                         : XMLSymbols.EMPTY_STRING;
669
670                 // Declare prefix in context. Removing the association between a prefix and a
671
// namespace name is permitted in XML 1.1, so if the uri value is the empty string,
672
// the prefix is being unbound. -- mrglavas
673
fNamespaceContext.declarePrefix(
674                     prefix,
675                     uri.length() != 0 ? uri : null);
676                 // bind namespace attribute to a namespace
677
attributes.setURI(
678                     attrIndex,
679                     fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS));
680
681             } else {
682                 // attempt to bind attribute
683
if (fAttributeQName.prefix != null) {
684                     attributes.setURI(
685                         attrIndex,
686                         fNamespaceContext.getURI(fAttributeQName.prefix));
687                 }
688             }
689         }
690
691         if (DEBUG_CONTENT_SCANNING)
692             System.out.println("<<< scanAttribute()");
693     } // scanAttribute(XMLAttributes)
694

695     /**
696      * Scans an end element.
697      * <p>
698      * <pre>
699      * [42] ETag ::= '&lt;/' Name S? '>'
700      * </pre>
701      * <p>
702      * <strong>Note:</strong> This method uses the fElementQName variable.
703      * The contents of this variable will be destroyed. The caller should
704      * copy the needed information out of this variable before calling
705      * this method.
706      *
707      * @return The element depth.
708      */

709     protected int scanEndElement() throws IOException JavaDoc, XNIException {
710         if (DEBUG_CONTENT_SCANNING)
711             System.out.println(">>> scanEndElement()");
712
713         // pop context
714
fElementStack.popElement(fElementQName);
715
716         // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
717
//In scanners most of the time is consumed on checks done for XML characters, we can
718
// optimize on it and avoid the checks done for endElement,
719
//we will also avoid symbol table lookup - neeraj.bajaj@sun.com
720

721         // this should work both for namespace processing true or false...
722

723         //REVISIT: if the string is not the same as expected.. we need to do better error handling..
724
//We can skip this for now... In any case if the string doesn't match -- document is not well formed.
725
if (!fEntityScanner.skipString(fElementQName.rawname)) {
726             reportFatalError(
727                 "ETagRequired",
728                 new Object JavaDoc[] { fElementQName.rawname });
729         }
730
731         // end
732
fEntityScanner.skipSpaces();
733         if (!fEntityScanner.skipChar('>')) {
734             reportFatalError(
735                 "ETagUnterminated",
736                 new Object JavaDoc[] { fElementQName.rawname });
737         }
738         fMarkupDepth--;
739
740         //we have increased the depth for two markup "<" characters
741
fMarkupDepth--;
742
743         // check that this element was opened in the same entity
744
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
745             reportFatalError(
746                 "ElementEntityMismatch",
747                 new Object JavaDoc[] { fCurrentElement.rawname });
748         }
749
750         // call handler
751
if (fDocumentHandler != null) {
752
753             fDocumentHandler.endElement(fElementQName, null);
754             if (fBindNamespaces) {
755                 fNamespaceContext.popContext();
756             }
757
758         }
759
760         return fMarkupDepth;
761
762     } // scanEndElement():int
763

764     public void reset(XMLComponentManager componentManager)
765         throws XMLConfigurationException {
766
767         super.reset(componentManager);
768         fPerformValidation = false;
769         fBindNamespaces = false;
770     }
771
772     /** Creates a content dispatcher. */
773     protected Dispatcher createContentDispatcher() {
774         return new NS11ContentDispatcher();
775     } // createContentDispatcher():Dispatcher
776

777     /**
778      * Dispatcher to handle content scanning.
779      */

780     protected final class NS11ContentDispatcher extends ContentDispatcher {
781         /**
782          * Scan for root element hook. This method is a hook for
783          * subclasses to add code that handles scanning for the root
784          * element. This method will also attempt to remove DTD validator
785          * from the pipeline, if there is no DTD grammar. If DTD validator
786          * is no longer in the pipeline bind namespaces in the scanner.
787          *
788          *
789          * @return True if the caller should stop and return true which
790          * allows the scanner to switch to a new scanning
791          * dispatcher. A return value of false indicates that
792          * the content dispatcher should continue as normal.
793          */

794         protected boolean scanRootElementHook()
795             throws IOException JavaDoc, XNIException {
796             
797             if (fExternalSubsetResolver != null && !fSeenDoctypeDecl
798                 && !fDisallowDoctype && (fValidation || fLoadExternalDTD)) {
799                 scanStartElementName();
800                 resolveExternalSubsetAndRead();
801                 reconfigurePipeline();
802                 if (scanStartElementAfterName()) {
803                     setScannerState(SCANNER_STATE_TRAILING_MISC);
804                     setDispatcher(fTrailingMiscDispatcher);
805                     return true;
806                 }
807             }
808             else {
809                 reconfigurePipeline();
810                 if (scanStartElement()) {
811                     setScannerState(SCANNER_STATE_TRAILING_MISC);
812                     setDispatcher(fTrailingMiscDispatcher);
813                     return true;
814                 }
815             }
816             return false;
817
818         } // scanRootElementHook():boolean
819

820         /**
821          * Re-configures pipeline by removing the DTD validator
822          * if no DTD grammar exists. If no validator exists in the
823          * pipeline or there is no DTD grammar, namespace binding
824          * is performed by the scanner in the enclosing class.
825          */

826         private void reconfigurePipeline() {
827             if (fDTDValidator == null) {
828                 fBindNamespaces = true;
829             }
830             else if (!fDTDValidator.hasGrammar()) {
831                 fBindNamespaces = true;
832                 fPerformValidation = fDTDValidator.validate();
833                 // re-configure pipeline
834
XMLDocumentSource source = fDTDValidator.getDocumentSource();
835                 XMLDocumentHandler handler = fDTDValidator.getDocumentHandler();
836                 source.setDocumentHandler(handler);
837                 if (handler != null)
838                     handler.setDocumentSource(source);
839                 fDTDValidator.setDocumentSource(null);
840                 fDTDValidator.setDocumentHandler(null);
841             }
842         } // reconfigurePipeline()
843
}
844 }
845
Popular Tags