KickJava   Java API By Example, From Geeks To Geeks.

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


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.CharConversionException JavaDoc;
20 import java.io.EOFException JavaDoc;
21 import java.io.IOException JavaDoc;
22
23 import org.apache.xerces.impl.io.MalformedByteSequenceException;
24 import org.apache.xerces.impl.msg.XMLMessageFormatter;
25 import org.apache.xerces.util.AugmentationsImpl;
26 import org.apache.xerces.util.XMLAttributesImpl;
27 import org.apache.xerces.util.XMLChar;
28 import org.apache.xerces.util.XMLStringBuffer;
29 import org.apache.xerces.util.XMLSymbols;
30 import org.apache.xerces.xni.Augmentations;
31 import org.apache.xerces.xni.QName;
32 import org.apache.xerces.xni.XMLAttributes;
33 import org.apache.xerces.xni.XMLDocumentHandler;
34 import org.apache.xerces.xni.XMLResourceIdentifier;
35 import org.apache.xerces.xni.XMLString;
36 import org.apache.xerces.xni.XNIException;
37 import org.apache.xerces.xni.parser.XMLComponent;
38 import org.apache.xerces.xni.parser.XMLComponentManager;
39 import org.apache.xerces.xni.parser.XMLConfigurationException;
40 import org.apache.xerces.xni.parser.XMLDocumentScanner;
41 import org.apache.xerces.xni.parser.XMLInputSource;
42
43 /**
44  * This class is responsible for scanning the structure and content
45  * of document fragments. The scanner acts as the source for the
46  * document information which is communicated to the document handler.
47  * <p>
48  * This component requires the following features and properties from the
49  * component manager that uses it:
50  * <ul>
51  * <li>http://xml.org/sax/features/validation</li>
52  * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
53  * <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
54  * <li>http://apache.org/xml/properties/internal/symbol-table</li>
55  * <li>http://apache.org/xml/properties/internal/error-reporter</li>
56  * <li>http://apache.org/xml/properties/internal/entity-manager</li>
57  * </ul>
58  *
59  * @xerces.internal
60  *
61  * @author Glenn Marcy, IBM
62  * @author Andy Clark, IBM
63  * @author Arnaud Le Hors, IBM
64  * @author Eric Ye, IBM
65  *
66  * @version $Id: XMLDocumentFragmentScannerImpl.java,v 1.58 2004/10/04 21:45:49 mrglavas Exp $
67  */

68 public class XMLDocumentFragmentScannerImpl
69     extends XMLScanner
70     implements XMLDocumentScanner, XMLComponent, XMLEntityHandler {
71
72     //
73
// Constants
74
//
75

76     // scanner states
77

78     /** Scanner state: start of markup. */
79     protected static final int SCANNER_STATE_START_OF_MARKUP = 1;
80
81     /** Scanner state: comment. */
82     protected static final int SCANNER_STATE_COMMENT = 2;
83
84     /** Scanner state: processing instruction. */
85     protected static final int SCANNER_STATE_PI = 3;
86
87     /** Scanner state: DOCTYPE. */
88     protected static final int SCANNER_STATE_DOCTYPE = 4;
89
90     /** Scanner state: root element. */
91     protected static final int SCANNER_STATE_ROOT_ELEMENT = 6;
92
93     /** Scanner state: content. */
94     protected static final int SCANNER_STATE_CONTENT = 7;
95
96     /** Scanner state: reference. */
97     protected static final int SCANNER_STATE_REFERENCE = 8;
98
99     /** Scanner state: end of input. */
100     protected static final int SCANNER_STATE_END_OF_INPUT = 13;
101
102     /** Scanner state: terminated. */
103     protected static final int SCANNER_STATE_TERMINATED = 14;
104
105     /** Scanner state: CDATA section. */
106     protected static final int SCANNER_STATE_CDATA = 15;
107
108     /** Scanner state: Text declaration. */
109     protected static final int SCANNER_STATE_TEXT_DECL = 16;
110
111     // feature identifiers
112

113     /** Feature identifier: namespaces. */
114     protected static final String JavaDoc NAMESPACES =
115         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
116
117     /** Feature identifier: notify built-in refereces. */
118     protected static final String JavaDoc NOTIFY_BUILTIN_REFS =
119         Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_BUILTIN_REFS_FEATURE;
120         
121     // property identifiers
122

123     /** Property identifier: entity resolver. */
124     protected static final String JavaDoc ENTITY_RESOLVER =
125         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
126     
127     // recognized features and properties
128

129     /** Recognized features. */
130     private static final String JavaDoc[] RECOGNIZED_FEATURES = {
131         NAMESPACES,
132         VALIDATION,
133         NOTIFY_BUILTIN_REFS,
134         NOTIFY_CHAR_REFS,
135     };
136
137     /** Feature defaults. */
138     private static final Boolean JavaDoc[] FEATURE_DEFAULTS = {
139         null,
140         null,
141         Boolean.FALSE,
142         Boolean.FALSE,
143     };
144
145     /** Recognized properties. */
146     private static final String JavaDoc[] RECOGNIZED_PROPERTIES = {
147         SYMBOL_TABLE,
148         ERROR_REPORTER,
149         ENTITY_MANAGER,
150         ENTITY_RESOLVER,
151     };
152
153     /** Property defaults. */
154     private static final Object JavaDoc[] PROPERTY_DEFAULTS = {
155         null,
156         null,
157         null,
158         null,
159     };
160
161     // debugging
162

163     /** Debug scanner state. */
164     private static final boolean DEBUG_SCANNER_STATE = false;
165
166     /** Debug dispatcher. */
167     private static final boolean DEBUG_DISPATCHER = false;
168
169     /** Debug content dispatcher scanning. */
170     protected static final boolean DEBUG_CONTENT_SCANNING = false;
171
172     //
173
// Data
174
//
175

176     // protected data
177

178     /** Document handler. */
179     protected XMLDocumentHandler fDocumentHandler;
180
181     /** Entity stack. */
182     protected int[] fEntityStack = new int[4];
183
184     /** Markup depth. */
185     protected int fMarkupDepth;
186
187     /** Scanner state. */
188     protected int fScannerState;
189
190     /** SubScanner state: inside scanContent method. */
191     protected boolean fInScanContent = false;
192
193     /** has external dtd */
194     protected boolean fHasExternalDTD;
195     
196     /** Standalone. */
197     protected boolean fStandalone;
198     
199     /** External subset resolver. **/
200     protected ExternalSubsetResolver fExternalSubsetResolver;
201
202     // element information
203

204     /** Current element. */
205     protected QName fCurrentElement;
206
207     /** Element stack. */
208     protected ElementStack fElementStack = new ElementStack();
209
210     // other info
211

212     /** Document system identifier.
213      * REVISIT: So what's this used for? - NG
214     * protected String fDocumentSystemId;
215      ******/

216
217     // features
218

219     /** Notify built-in references. */
220     protected boolean fNotifyBuiltInRefs = false;
221
222     // dispatchers
223

224     /** Active dispatcher. */
225     protected Dispatcher fDispatcher;
226
227     /** Content dispatcher. */
228     protected Dispatcher fContentDispatcher = createContentDispatcher();
229
230     // temporary variables
231

232     /** Element QName. */
233     protected final QName fElementQName = new QName();
234
235     /** Attribute QName. */
236     protected final QName fAttributeQName = new QName();
237
238     /** Element attributes. */
239     protected final XMLAttributesImpl fAttributes = new XMLAttributesImpl();
240
241     /** String. */
242     protected final XMLString fTempString = new XMLString();
243
244     /** String. */
245     protected final XMLString fTempString2 = new XMLString();
246
247     /** Array of 3 strings. */
248     private final String JavaDoc[] fStrings = new String JavaDoc[3];
249
250     /** String buffer. */
251     private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
252
253     /** String buffer. */
254     private final XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
255
256     /** Another QName. */
257     private final QName fQName = new QName();
258
259     /** Single character array. */
260     private final char[] fSingleChar = new char[1];
261
262     /** External entity. */
263     private final XMLEntityManager.ExternalEntity fExternalEntity = new XMLEntityManager.ExternalEntity();
264     
265     /**
266      * Saw spaces after element name or between attributes.
267      *
268      * This is reserved for the case where scanning of a start element spans
269      * several methods, as is the case when scanning the start of a root element
270      * where a DTD external subset may be read after scanning the element name.
271      */

272     private boolean fSawSpace;
273     
274     /** Reusable Augmentations. */
275     private Augmentations fTempAugmentations = null;
276
277     //
278
// Constructors
279
//
280

281     /** Default constructor. */
282     public XMLDocumentFragmentScannerImpl() {} // <init>()
283

284     //
285
// XMLDocumentScanner methods
286
//
287

288     /**
289      * Sets the input source.
290      *
291      * @param inputSource The input source.
292      *
293      * @throws IOException Thrown on i/o error.
294      */

295     public void setInputSource(XMLInputSource inputSource) throws IOException JavaDoc {
296         fEntityManager.setEntityHandler(this);
297         fEntityManager.startEntity("$fragment$", inputSource, false, true);
298         //fDocumentSystemId = fEntityManager.expandSystemId(inputSource.getSystemId());
299
} // setInputSource(XMLInputSource)
300

301     /**
302      * Scans a document.
303      *
304      * @param complete True if the scanner should scan the document
305      * completely, pushing all events to the registered
306      * document handler. A value of false indicates that
307      * that the scanner should only scan the next portion
308      * of the document and return. A scanner instance is
309      * permitted to completely scan a document if it does
310      * not support this "pull" scanning model.
311      *
312      * @return True if there is more to scan, false otherwise.
313      */

314     public boolean scanDocument(boolean complete)
315         throws IOException JavaDoc, XNIException {
316         
317         // reset entity scanner
318
fEntityScanner = fEntityManager.getEntityScanner();
319         
320         // keep dispatching "events"
321
fEntityManager.setEntityHandler(this);
322         do {
323             if (!fDispatcher.dispatch(complete)) {
324                 return false;
325             }
326         } while (complete);
327
328         // return success
329
return true;
330
331     } // scanDocument(boolean):boolean
332

333     //
334
// XMLComponent methods
335
//
336

337     /**
338      * Resets the component. The component can query the component manager
339      * about any features and properties that affect the operation of the
340      * component.
341      *
342      * @param componentManager The component manager.
343      *
344      * @throws SAXException Thrown by component on initialization error.
345      * For example, if a feature or property is
346      * required for the operation of the component, the
347      * component manager may throw a
348      * SAXNotRecognizedException or a
349      * SAXNotSupportedException.
350      */

351     public void reset(XMLComponentManager componentManager)
352         throws XMLConfigurationException {
353
354         super.reset(componentManager);
355
356         // other settings
357
//fDocumentSystemId = null;
358

359         // sax features
360
fAttributes.setNamespaces(fNamespaces);
361
362         // initialize vars
363
fMarkupDepth = 0;
364         fCurrentElement = null;
365         fElementStack.clear();
366         fHasExternalDTD = false;
367         fStandalone = false;
368         fInScanContent = false;
369
370         // setup dispatcher
371
setScannerState(SCANNER_STATE_CONTENT);
372         setDispatcher(fContentDispatcher);
373         
374
375         if (fParserSettings) {
376             // parser settings have changed. reset them.
377

378             // xerces features
379
try {
380                 fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS);
381             } catch (XMLConfigurationException e) {
382                 fNotifyBuiltInRefs = false;
383             }
384             
385             // xerces properties
386
try {
387                 Object JavaDoc resolver = componentManager.getProperty(ENTITY_RESOLVER);
388                 fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ?
389                     (ExternalSubsetResolver) resolver : null;
390             }
391             catch (XMLConfigurationException e) {
392                 fExternalSubsetResolver = null;
393             }
394         }
395
396     } // reset(XMLComponentManager)
397

398     /**
399      * Returns a list of feature identifiers that are recognized by
400      * this component. This method may return null if no features
401      * are recognized by this component.
402      */

403     public String JavaDoc[] getRecognizedFeatures() {
404         return (String JavaDoc[])(RECOGNIZED_FEATURES.clone());
405     } // getRecognizedFeatures():String[]
406

407     /**
408      * Sets the state of a feature. This method is called by the component
409      * manager any time after reset when a feature changes state.
410      * <p>
411      * <strong>Note:</strong> Components should silently ignore features
412      * that do not affect the operation of the component.
413      *
414      * @param featureId The feature identifier.
415      * @param state The state of the feature.
416      *
417      * @throws SAXNotRecognizedException The component should not throw
418      * this exception.
419      * @throws SAXNotSupportedException The component should not throw
420      * this exception.
421      */

422     public void setFeature(String JavaDoc featureId, boolean state)
423         throws XMLConfigurationException {
424
425         super.setFeature(featureId, state);
426             
427         // Xerces properties
428
if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
429             final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
430             if (suffixLength == Constants.NOTIFY_BUILTIN_REFS_FEATURE.length() &&
431                 featureId.endsWith(Constants.NOTIFY_BUILTIN_REFS_FEATURE)) {
432                 fNotifyBuiltInRefs = state;
433             }
434         }
435
436     } // setFeature(String,boolean)
437

438     /**
439      * Returns a list of property identifiers that are recognized by
440      * this component. This method may return null if no properties
441      * are recognized by this component.
442      */

443     public String JavaDoc[] getRecognizedProperties() {
444         return (String JavaDoc[])(RECOGNIZED_PROPERTIES.clone());
445     } // getRecognizedProperties():String[]
446

447     /**
448      * Sets the value of a property. This method is called by the component
449      * manager any time after reset when a property changes value.
450      * <p>
451      * <strong>Note:</strong> Components should silently ignore properties
452      * that do not affect the operation of the component.
453      *
454      * @param propertyId The property identifier.
455      * @param value The value of the property.
456      *
457      * @throws SAXNotRecognizedException The component should not throw
458      * this exception.
459      * @throws SAXNotSupportedException The component should not throw
460      * this exception.
461      */

462     public void setProperty(String JavaDoc propertyId, Object JavaDoc value)
463         throws XMLConfigurationException {
464         
465         super.setProperty(propertyId, value);
466
467         // Xerces properties
468
if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
469             final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
470             if (suffixLength == Constants.ENTITY_MANAGER_PROPERTY.length() &&
471                 propertyId.endsWith(Constants.ENTITY_MANAGER_PROPERTY)) {
472                 fEntityManager = (XMLEntityManager)value;
473                 return;
474             }
475             if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() &&
476                 propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
477                 fExternalSubsetResolver = (value instanceof ExternalSubsetResolver) ?
478                     (ExternalSubsetResolver) value : null;
479                 return;
480             }
481         }
482         
483     } // setProperty(String,Object)
484

485     /**
486      * Returns the default state for a feature, or null if this
487      * component does not want to report a default value for this
488      * feature.
489      *
490      * @param featureId The feature identifier.
491      *
492      * @since Xerces 2.2.0
493      */

494     public Boolean JavaDoc getFeatureDefault(String JavaDoc featureId) {
495         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
496             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
497                 return FEATURE_DEFAULTS[i];
498             }
499         }
500         return null;
501     } // getFeatureDefault(String):Boolean
502

503     /**
504      * Returns the default state for a property, or null if this
505      * component does not want to report a default value for this
506      * property.
507      *
508      * @param propertyId The property identifier.
509      *
510      * @since Xerces 2.2.0
511      */

512     public Object JavaDoc getPropertyDefault(String JavaDoc propertyId) {
513         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
514             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
515                 return PROPERTY_DEFAULTS[i];
516             }
517         }
518         return null;
519     } // getPropertyDefault(String):Object
520

521     //
522
// XMLDocumentSource methods
523
//
524

525     /**
526      * setDocumentHandler
527      *
528      * @param documentHandler
529      */

530     public void setDocumentHandler(XMLDocumentHandler documentHandler) {
531         fDocumentHandler = documentHandler;
532     } // setDocumentHandler(XMLDocumentHandler)
533

534
535     /** Returns the document handler */
536     public XMLDocumentHandler getDocumentHandler(){
537         return fDocumentHandler;
538     }
539
540     //
541
// XMLEntityHandler methods
542
//
543

544     /**
545      * This method notifies of the start of an entity. The DTD has the
546      * pseudo-name of "[dtd]" parameter entity names start with '%'; and
547      * general entities are just specified by their name.
548      *
549      * @param name The name of the entity.
550      * @param identifier The resource identifier.
551      * @param encoding The auto-detected IANA encoding name of the entity
552      * stream. This value will be null in those situations
553      * where the entity encoding is not auto-detected (e.g.
554      * internal entities or a document entity that is
555      * parsed from a java.io.Reader).
556      * @param augs Additional information that may include infoset augmentations
557      *
558      * @throws XNIException Thrown by handler to signal an error.
559      */

560     public void startEntity(String JavaDoc name,
561                             XMLResourceIdentifier identifier,
562                             String JavaDoc encoding, Augmentations augs) throws XNIException {
563
564         // keep track of this entity before fEntityDepth is increased
565
if (fEntityDepth == fEntityStack.length) {
566             int[] entityarray = new int[fEntityStack.length * 2];
567             System.arraycopy(fEntityStack, 0, entityarray, 0, fEntityStack.length);
568             fEntityStack = entityarray;
569         }
570         fEntityStack[fEntityDepth] = fMarkupDepth;
571
572         super.startEntity(name, identifier, encoding, augs);
573
574         // WFC: entity declared in external subset in standalone doc
575
if(fStandalone && fEntityManager.isEntityDeclInExternalSubset(name)) {
576             reportFatalError("MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
577                 new Object JavaDoc[]{name});
578         }
579
580         // call handler
581
if (fDocumentHandler != null && !fScanningAttribute) {
582             if (!name.equals("[xml]")) {
583                 fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
584             }
585         }
586
587     } // startEntity(String,XMLResourceIdentifier,String)
588

589     /**
590      * This method notifies the end of an entity. The DTD has the pseudo-name
591      * of "[dtd]" parameter entity names start with '%'; and general entities
592      * are just specified by their name.
593      *
594      * @param name The name of the entity.
595      * @param augs Additional information that may include infoset augmentations
596      *
597      * @throws XNIException Thrown by handler to signal an error.
598      */

599     public void endEntity(String JavaDoc name, Augmentations augs) throws XNIException {
600
601         // flush possible pending output buffer - see scanContent
602
if (fInScanContent && fStringBuffer.length != 0
603             && fDocumentHandler != null) {
604             fDocumentHandler.characters(fStringBuffer, null);
605             fStringBuffer.length = 0; // make sure we know it's been flushed
606
}
607
608         super.endEntity(name, augs);
609
610         // make sure markup is properly balanced
611
if (fMarkupDepth != fEntityStack[fEntityDepth]) {
612             reportFatalError("MarkupEntityMismatch", null);
613         }
614
615         // call handler
616
if (fDocumentHandler != null && !fScanningAttribute) {
617             if (!name.equals("[xml]")) {
618                 fDocumentHandler.endGeneralEntity(name, augs);
619             }
620         }
621         
622     } // endEntity(String)
623

624     //
625
// Protected methods
626
//
627

628     // dispatcher factory methods
629

630     /** Creates a content dispatcher. */
631     protected Dispatcher createContentDispatcher() {
632         return new FragmentContentDispatcher();
633     } // createContentDispatcher():Dispatcher
634

635     // scanning methods
636

637     /**
638      * Scans an XML or text declaration.
639      * <p>
640      * <pre>
641      * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
642      * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
643      * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
644      * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
645      * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
646      * | ('"' ('yes' | 'no') '"'))
647      *
648      * [77] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?>'
649      * </pre>
650      *
651      * @param scanningTextDecl True if a text declaration is to
652      * be scanned instead of an XML
653      * declaration.
654      */

655     protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl)
656         throws IOException JavaDoc, XNIException {
657
658         // scan decl
659
super.scanXMLDeclOrTextDecl(scanningTextDecl, fStrings);
660         fMarkupDepth--;
661
662         // pseudo-attribute values
663
String JavaDoc version = fStrings[0];
664         String JavaDoc encoding = fStrings[1];
665         String JavaDoc standalone = fStrings[2];
666
667         // set standalone
668
fStandalone = standalone != null && standalone.equals("yes");
669         fEntityManager.setStandalone(fStandalone);
670         
671         // set version on reader
672
fEntityScanner.setXMLVersion(version);
673
674         // call handler
675
if (fDocumentHandler != null) {
676             if (scanningTextDecl) {
677                 fDocumentHandler.textDecl(version, encoding, null);
678             }
679             else {
680                 fDocumentHandler.xmlDecl(version, encoding, standalone, null);
681             }
682         }
683
684         // set encoding on reader
685
if (encoding != null && !fEntityScanner.fCurrentEntity.isEncodingExternallySpecified()) {
686             fEntityScanner.setEncoding(encoding);
687         }
688
689     } // scanXMLDeclOrTextDecl(boolean)
690

691     /**
692      * Scans a processing data. This is needed to handle the situation
693      * where a document starts with a processing instruction whose
694      * target name <em>starts with</em> "xml". (e.g. xmlfoo)
695      *
696      * @param target The PI target
697      * @param data The string to fill in with the data
698      */

699     protected void scanPIData(String JavaDoc target, XMLString data)
700         throws IOException JavaDoc, XNIException {
701
702         super.scanPIData(target, data);
703         fMarkupDepth--;
704
705         // call handler
706
if (fDocumentHandler != null) {
707             fDocumentHandler.processingInstruction(target, data, null);
708         }
709
710     } // scanPIData(String)
711

712     /**
713      * Scans a comment.
714      * <p>
715      * <pre>
716      * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
717      * </pre>
718      * <p>
719      * <strong>Note:</strong> Called after scanning past '&lt;!--'
720      */

721     protected void scanComment() throws IOException JavaDoc, XNIException {
722
723         scanComment(fStringBuffer);
724         fMarkupDepth--;
725
726         // call handler
727
if (fDocumentHandler != null) {
728             fDocumentHandler.comment(fStringBuffer, null);
729         }
730
731     } // scanComment()
732

733     /**
734      * Scans a start element. This method will handle the binding of
735      * namespace information and notifying the handler of the start
736      * of the element.
737      * <p>
738      * <pre>
739      * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
740      * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
741      * </pre>
742      * <p>
743      * <strong>Note:</strong> This method assumes that the leading
744      * '&lt;' character has been consumed.
745      * <p>
746      * <strong>Note:</strong> This method uses the fElementQName and
747      * fAttributes variables. The contents of these variables will be
748      * destroyed. The caller should copy important information out of
749      * these variables before calling this method.
750      *
751      * @return True if element is empty. (i.e. It matches
752      * production [44].
753      */

754     protected boolean scanStartElement()
755         throws IOException JavaDoc, XNIException {
756         if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanStartElement()");
757
758         // name
759
if (fNamespaces) {
760             fEntityScanner.scanQName(fElementQName);
761         }
762         else {
763             String JavaDoc name = fEntityScanner.scanName();
764             fElementQName.setValues(null, name, name, null);
765         }
766         String JavaDoc rawname = fElementQName.rawname;
767
768         // push element stack
769
fCurrentElement = fElementStack.pushElement(fElementQName);
770
771         // attributes
772
boolean empty = false;
773         fAttributes.removeAllAttributes();
774         do {
775             // spaces
776
boolean sawSpace = fEntityScanner.skipSpaces();
777
778             // end tag?
779
int c = fEntityScanner.peekChar();
780             if (c == '>') {
781                 fEntityScanner.scanChar();
782                 break;
783             }
784             else if (c == '/') {
785                 fEntityScanner.scanChar();
786                 if (!fEntityScanner.skipChar('>')) {
787                     reportFatalError("ElementUnterminated",
788                                      new Object JavaDoc[]{rawname});
789                 }
790                 empty = true;
791                 break;
792             }
793             else if (!isValidNameStartChar(c) || !sawSpace) {
794                 // Second chance. Check if this character is a high
795
// surrogate of a valid name start character.
796
if (!isValidNameStartHighSurrogate(c) || !sawSpace) {
797                     reportFatalError("ElementUnterminated",
798                                      new Object JavaDoc[] { rawname });
799                 }
800             }
801
802             // attributes
803
scanAttribute(fAttributes);
804
805         } while (true);
806
807         // call handler
808
if (fDocumentHandler != null) {
809             if (empty) {
810
811                 //decrease the markup depth..
812
fMarkupDepth--;
813                 // check that this element was opened in the same entity
814
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
815                     reportFatalError("ElementEntityMismatch",
816                                      new Object JavaDoc[]{fCurrentElement.rawname});
817                 }
818
819                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
820
821                 //pop the element off the stack..
822
fElementStack.popElement(fElementQName);
823             }
824             else {
825                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
826             }
827         }
828
829         if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanStartElement(): "+empty);
830         return empty;
831
832     } // scanStartElement():boolean
833

834     /**
835      * Scans the name of an element in a start or empty tag.
836      *
837      * @see #scanStartElement()
838      */

839     protected void scanStartElementName ()
840         throws IOException JavaDoc, XNIException {
841         // name
842
if (fNamespaces) {
843             fEntityScanner.scanQName(fElementQName);
844         }
845         else {
846             String JavaDoc name = fEntityScanner.scanName();
847             fElementQName.setValues(null, name, name, null);
848         }
849         // Must skip spaces here because the DTD scanner
850
// would consume them at the end of the external subset.
851
fSawSpace = fEntityScanner.skipSpaces();
852     } // scanStartElementName()
853

854     /**
855      * Scans the remainder of a start or empty tag after the element name.
856      *
857      * @see #scanStartElement
858      * @return True if element is empty.
859      */

860     protected boolean scanStartElementAfterName()
861         throws IOException JavaDoc, XNIException {
862         String JavaDoc rawname = fElementQName.rawname;
863
864         // push element stack
865
fCurrentElement = fElementStack.pushElement(fElementQName);
866
867         // attributes
868
boolean empty = false;
869         fAttributes.removeAllAttributes();
870         do {
871             
872             // end tag?
873
int c = fEntityScanner.peekChar();
874             if (c == '>') {
875                 fEntityScanner.scanChar();
876                 break;
877             }
878             else if (c == '/') {
879                 fEntityScanner.scanChar();
880                 if (!fEntityScanner.skipChar('>')) {
881                     reportFatalError("ElementUnterminated",
882                                      new Object JavaDoc[]{rawname});
883                 }
884                 empty = true;
885                 break;
886             }
887             else if (!isValidNameStartChar(c) || !fSawSpace) {
888                 // Second chance. Check if this character is a high
889
// surrogate of a valid name start character.
890
if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
891                     reportFatalError("ElementUnterminated",
892                                      new Object JavaDoc[] { rawname });
893                 }
894             }
895
896             // attributes
897
scanAttribute(fAttributes);
898             
899             // spaces
900
fSawSpace = fEntityScanner.skipSpaces();
901
902         } while (true);
903
904         // call handler
905
if (fDocumentHandler != null) {
906             if (empty) {
907
908                 //decrease the markup depth..
909
fMarkupDepth--;
910                 // check that this element was opened in the same entity
911
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
912                     reportFatalError("ElementEntityMismatch",
913                                      new Object JavaDoc[]{fCurrentElement.rawname});
914                 }
915
916                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
917
918                 //pop the element off the stack..
919
fElementStack.popElement(fElementQName);
920             }
921             else {
922                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
923             }
924         }
925
926         if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanStartElementAfterName(): "+empty);
927         return empty;
928     } // scanStartElementAfterName()
929

930     /**
931      * Scans an attribute.
932      * <p>
933      * <pre>
934      * [41] Attribute ::= Name Eq AttValue
935      * </pre>
936      * <p>
937      * <strong>Note:</strong> This method assumes that the next
938      * character on the stream is the first character of the attribute
939      * name.
940      * <p>
941      * <strong>Note:</strong> This method uses the fAttributeQName and
942      * fQName variables. The contents of these variables will be
943      * destroyed.
944      *
945      * @param attributes The attributes list for the scanned attribute.
946      */

947     protected void scanAttribute(XMLAttributes attributes)
948         throws IOException JavaDoc, XNIException {
949         if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanAttribute()");
950
951         // name
952
if (fNamespaces) {
953             fEntityScanner.scanQName(fAttributeQName);
954         }
955         else {
956             String JavaDoc name = fEntityScanner.scanName();
957             fAttributeQName.setValues(null, name, name, null);
958         }
959
960         // equals
961
fEntityScanner.skipSpaces();
962         if (!fEntityScanner.skipChar('=')) {
963             reportFatalError("EqRequiredInAttribute",
964                              new Object JavaDoc[]{fCurrentElement.rawname,fAttributeQName.rawname});
965         }
966         fEntityScanner.skipSpaces();
967
968         // content
969
int oldLen = attributes.getLength();
970         int attrIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null);
971
972         // WFC: Unique Att Spec
973
if (oldLen == attributes.getLength()) {
974             reportFatalError("AttributeNotUnique",
975                              new Object JavaDoc[]{fCurrentElement.rawname,
976                                           fAttributeQName.rawname});
977         }
978         //REVISIT: one more case needs to be included: external PE and standalone is no
979
boolean isVC = fHasExternalDTD && !fStandalone;
980         
981         // Scan attribute value and return true if the un-normalized and normalized value are the same
982
boolean isSameNormalizedAttr = scanAttributeValue(fTempString, fTempString2,
983                 fAttributeQName.rawname, isVC, fCurrentElement.rawname);
984         
985         attributes.setValue(attrIndex, fTempString.toString());
986         // If the non-normalized and normalized value are the same, avoid creating a new string.
987
if (!isSameNormalizedAttr) {
988             attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
989         }
990         attributes.setSpecified(attrIndex, true);
991
992         if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanAttribute()");
993     } // scanAttribute(XMLAttributes)
994

995     /**
996      * Scans element content.
997      *
998      * @return Returns the next character on the stream.
999      */

1000    protected int scanContent() throws IOException JavaDoc, XNIException {
1001
1002        XMLString content = fTempString;
1003        int c = fEntityScanner.scanContent(content);
1004        if (c == '\r') {
1005            // happens when there is the character reference &#13;
1006
fEntityScanner.scanChar();
1007            fStringBuffer.clear();
1008            fStringBuffer.append(fTempString);
1009            fStringBuffer.append((char)c);
1010            content = fStringBuffer;
1011            c = -1;
1012        }
1013        if (fDocumentHandler != null && content.length > 0) {
1014            fDocumentHandler.characters(content, null);
1015        }
1016
1017        if (c == ']' && fTempString.length == 0) {
1018            fStringBuffer.clear();
1019            fStringBuffer.append((char)fEntityScanner.scanChar());
1020            // remember where we are in case we get an endEntity before we
1021
// could flush the buffer out - this happens when we're parsing an
1022
// entity which ends with a ]
1023
fInScanContent = true;
1024            //
1025
// We work on a single character basis to handle cases such as:
1026
// ']]]>' which we might otherwise miss.
1027
//
1028
if (fEntityScanner.skipChar(']')) {
1029                fStringBuffer.append(']');
1030                while (fEntityScanner.skipChar(']')) {
1031                    fStringBuffer.append(']');
1032                }
1033                if (fEntityScanner.skipChar('>')) {
1034                    reportFatalError("CDEndInContent", null);
1035                }
1036            }
1037            if (fDocumentHandler != null && fStringBuffer.length != 0) {
1038                fDocumentHandler.characters(fStringBuffer, null);
1039            }
1040            fInScanContent = false;
1041            c = -1;
1042        }
1043        return c;
1044
1045    } // scanContent():int
1046

1047
1048    /**
1049     * Scans a CDATA section.
1050     * <p>
1051     * <strong>Note:</strong> This method uses the fTempString and
1052     * fStringBuffer variables.
1053     *
1054     * @param complete True if the CDATA section is to be scanned
1055     * completely.
1056     *
1057     * @return True if CDATA is completely scanned.
1058     */

1059    protected boolean scanCDATASection(boolean complete)
1060        throws IOException JavaDoc, XNIException {
1061        
1062        // call handler
1063
if (fDocumentHandler != null) {
1064            fDocumentHandler.startCDATA(null);
1065        }
1066
1067        while (true) {
1068            fStringBuffer.clear();
1069            if (!fEntityScanner.scanData("]]", fStringBuffer)) {
1070                if (fDocumentHandler != null && fStringBuffer.length > 0) {
1071                    fDocumentHandler.characters(fStringBuffer, null);
1072                }
1073                int brackets = 0;
1074                while (fEntityScanner.skipChar(']')) {
1075                    brackets++;
1076                }
1077                if (fDocumentHandler != null && brackets > 0) {
1078                    fStringBuffer.clear();
1079                    if (brackets > XMLEntityManager.DEFAULT_BUFFER_SIZE) {
1080                        // Handle large sequences of ']'
1081
int chunks = brackets / XMLEntityManager.DEFAULT_BUFFER_SIZE;
1082                        int remainder = brackets % XMLEntityManager.DEFAULT_BUFFER_SIZE;
1083                        for (int i = 0; i < XMLEntityManager.DEFAULT_BUFFER_SIZE; i++) {
1084                            fStringBuffer.append(']');
1085                        }
1086                        for (int i = 0; i < chunks; i++) {
1087                            fDocumentHandler.characters(fStringBuffer, null);
1088                        }
1089                        if (remainder != 0) {
1090                            fStringBuffer.length = remainder;
1091                            fDocumentHandler.characters(fStringBuffer, null);
1092                        }
1093                    }
1094                    else {
1095                        for (int i = 0; i < brackets; i++) {
1096                            fStringBuffer.append(']');
1097                        }
1098                       fDocumentHandler.characters(fStringBuffer, null);
1099                    }
1100                }
1101                if (fEntityScanner.skipChar('>')) {
1102                    break;
1103                }
1104                if (fDocumentHandler != null) {
1105                    fStringBuffer.clear();
1106                    fStringBuffer.append("]]");
1107                    fDocumentHandler.characters(fStringBuffer, null);
1108                }
1109            }
1110            else {
1111                if (fDocumentHandler != null) {
1112                    fDocumentHandler.characters(fStringBuffer, null);
1113                }
1114                int c = fEntityScanner.peekChar();
1115                if (c != -1 && isInvalidLiteral(c)) {
1116                    if (XMLChar.isHighSurrogate(c)) {
1117                        fStringBuffer.clear();
1118                        scanSurrogates(fStringBuffer);
1119                        if (fDocumentHandler != null) {
1120                            fDocumentHandler.characters(fStringBuffer, null);
1121                        }
1122                    }
1123                    else {
1124                        reportFatalError("InvalidCharInCDSect",
1125                                        new Object JavaDoc[]{Integer.toString(c,16)});
1126                        fEntityScanner.scanChar();
1127                    }
1128                }
1129            }
1130        }
1131        fMarkupDepth--;
1132
1133        // call handler
1134
if (fDocumentHandler != null) {
1135            fDocumentHandler.endCDATA(null);
1136        }
1137
1138        return true;
1139
1140    } // scanCDATASection(boolean):boolean
1141

1142    /**
1143     * Scans an end element.
1144     * <p>
1145     * <pre>
1146     * [42] ETag ::= '&lt;/' Name S? '>'
1147     * </pre>
1148     * <p>
1149     * <strong>Note:</strong> This method uses the fElementQName variable.
1150     * The contents of this variable will be destroyed. The caller should
1151     * copy the needed information out of this variable before calling
1152     * this method.
1153     *
1154     * @return The element depth.
1155     */

1156    protected int scanEndElement() throws IOException JavaDoc, XNIException {
1157        if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanEndElement()");
1158
1159        fElementStack.popElement(fElementQName) ;
1160
1161        // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
1162
//In scanners most of the time is consumed on checks done for XML characters, we can
1163
// optimize on it and avoid the checks done for endElement,
1164
//we will also avoid symbol table lookup - neeraj.bajaj@sun.com
1165

1166        // this should work both for namespace processing true or false...
1167

1168        //REVISIT: if the string is not the same as expected.. we need to do better error handling..
1169
//We can skip this for now... In any case if the string doesn't match -- document is not well formed.
1170
if (!fEntityScanner.skipString(fElementQName.rawname)) {
1171            reportFatalError("ETagRequired", new Object JavaDoc[]{fElementQName.rawname});
1172        }
1173
1174        // end
1175
fEntityScanner.skipSpaces();
1176        if (!fEntityScanner.skipChar('>')) {
1177            reportFatalError("ETagUnterminated",
1178                             new Object JavaDoc[]{fElementQName.rawname});
1179        }
1180        fMarkupDepth--;
1181
1182        //we have increased the depth for two markup "<" characters
1183
fMarkupDepth--;
1184      
1185        // check that this element was opened in the same entity
1186
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
1187            reportFatalError("ElementEntityMismatch",
1188                             new Object JavaDoc[]{fCurrentElement.rawname});
1189        }
1190
1191        // call handler
1192
if (fDocumentHandler != null ) {
1193            fDocumentHandler.endElement(fElementQName, null);
1194        }
1195
1196        return fMarkupDepth;
1197 
1198    } // scanEndElement():int
1199

1200    /**
1201     * Scans a character reference.
1202     * <p>
1203     * <pre>
1204     * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
1205     * </pre>
1206     */

1207    protected void scanCharReference()
1208        throws IOException JavaDoc, XNIException {
1209
1210        fStringBuffer2.clear();
1211        int ch = scanCharReferenceValue(fStringBuffer2, null);
1212        fMarkupDepth--;
1213        if (ch != -1) {
1214            // call handler
1215
if (fDocumentHandler != null) {
1216                if (fNotifyCharRefs) {
1217                    fDocumentHandler.startGeneralEntity(fCharRefLiteral, null, null, null);
1218                }
1219                Augmentations augs = null;
1220                if (fValidation && ch <= 0x20) {
1221                    if (fTempAugmentations != null) {
1222                        fTempAugmentations.removeAllItems();
1223                    }
1224                    else {
1225                        fTempAugmentations = new AugmentationsImpl();
1226                    }
1227                    augs = fTempAugmentations;
1228                    augs.putItem(Constants.CHAR_REF_PROBABLE_WS, Boolean.TRUE);
1229                }
1230                fDocumentHandler.characters(fStringBuffer2, augs);
1231                if (fNotifyCharRefs) {
1232                    fDocumentHandler.endGeneralEntity(fCharRefLiteral, null);
1233                }
1234            }
1235        }
1236
1237    } // scanCharReference()
1238

1239    /**
1240     * Scans an entity reference.
1241     *
1242     * @throws IOException Thrown if i/o error occurs.
1243     * @throws XNIException Thrown if handler throws exception upon
1244     * notification.
1245     */

1246    protected void scanEntityReference() throws IOException JavaDoc, XNIException {
1247
1248        // name
1249
String JavaDoc name = fEntityScanner.scanName();
1250        if (name == null) {
1251            reportFatalError("NameRequiredInReference", null);
1252            return;
1253        }
1254
1255        // end
1256
if (!fEntityScanner.skipChar(';')) {
1257            reportFatalError("SemicolonRequiredInReference", new Object JavaDoc []{name});
1258        }
1259        fMarkupDepth--;
1260
1261        // handle built-in entities
1262
if (name == fAmpSymbol) {
1263            handleCharacter('&', fAmpSymbol);
1264        }
1265        else if (name == fLtSymbol) {
1266            handleCharacter('<', fLtSymbol);
1267        }
1268        else if (name == fGtSymbol) {
1269            handleCharacter('>', fGtSymbol);
1270        }
1271        else if (name == fQuotSymbol) {
1272            handleCharacter('"', fQuotSymbol);
1273        }
1274        else if (name == fAposSymbol) {
1275            handleCharacter('\'', fAposSymbol);
1276        }
1277        // start general entity
1278
else if (fEntityManager.isUnparsedEntity(name)) {
1279            reportFatalError("ReferenceToUnparsedEntity", new Object JavaDoc[]{name});
1280        }
1281        else {
1282            if (!fEntityManager.isDeclaredEntity(name)) {
1283                //REVISIT: one more case needs to be included: external PE and standalone is no
1284
if ( fHasExternalDTD && !fStandalone) {
1285                    if (fValidation)
1286                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared",
1287                                                    new Object JavaDoc[]{name}, XMLErrorReporter.SEVERITY_ERROR);
1288                }
1289                else
1290                    reportFatalError("EntityNotDeclared", new Object JavaDoc[]{name});
1291            }
1292            fEntityManager.startEntity(name, false);
1293        }
1294
1295    } // scanEntityReference()
1296

1297    // utility methods
1298

1299    /**
1300     * Calls document handler with a single character resulting from
1301     * built-in entity resolution.
1302     *
1303     * @param c
1304     * @param entity built-in name
1305     */

1306    private void handleCharacter(char c, String JavaDoc entity) throws XNIException {
1307        if (fDocumentHandler != null) {
1308            if (fNotifyBuiltInRefs) {
1309                fDocumentHandler.startGeneralEntity(entity, null, null, null);
1310            }
1311            
1312            fSingleChar[0] = c;
1313            fTempString.setValues(fSingleChar, 0, 1);
1314            fDocumentHandler.characters(fTempString, null);
1315            
1316            if (fNotifyBuiltInRefs) {
1317                fDocumentHandler.endGeneralEntity(entity, null);
1318            }
1319        }
1320    } // handleCharacter(char)
1321

1322    /**
1323     * Handles the end element. This method will make sure that
1324     * the end element name matches the current element and notify
1325     * the handler about the end of the element and the end of any
1326     * relevent prefix mappings.
1327     * <p>
1328     * <strong>Note:</strong> This method uses the fQName variable.
1329     * The contents of this variable will be destroyed.
1330     *
1331     * @param element The element.
1332     *
1333     * @return The element depth.
1334     *
1335     * @throws XNIException Thrown if the handler throws a SAX exception
1336     * upon notification.
1337     *
1338     */

1339    // REVISIT: need to remove this method. It's not called anymore, because
1340
// the handling is done when the end tag is scanned. - SG
1341
protected int handleEndElement(QName element, boolean isEmpty)
1342        throws XNIException {
1343
1344        fMarkupDepth--;
1345        // check that this element was opened in the same entity
1346
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
1347            reportFatalError("ElementEntityMismatch",
1348                             new Object JavaDoc[]{fCurrentElement.rawname});
1349        }
1350        // make sure the elements match
1351
QName startElement = fQName;
1352        fElementStack.popElement(startElement);
1353        if (element.rawname != startElement.rawname) {
1354            reportFatalError("ETagRequired",
1355                             new Object JavaDoc[]{startElement.rawname});
1356        }
1357
1358        // bind namespaces
1359
if (fNamespaces) {
1360            element.uri = startElement.uri;
1361        }
1362        
1363        // call handler
1364
if (fDocumentHandler != null && !isEmpty) {
1365            fDocumentHandler.endElement(element, null);
1366        }
1367
1368        return fMarkupDepth;
1369
1370    } // callEndElement(QName,boolean):int
1371

1372    // helper methods
1373

1374    /**
1375     * Sets the scanner state.
1376     *
1377     * @param state The new scanner state.
1378     */

1379    protected final void setScannerState(int state) {
1380
1381        fScannerState = state;
1382        if (DEBUG_SCANNER_STATE) {
1383            System.out.print("### setScannerState: ");
1384            System.out.print(getScannerStateName(state));
1385            System.out.println();
1386        }
1387
1388    } // setScannerState(int)
1389

1390    /**
1391     * Sets the dispatcher.
1392     *
1393     * @param dispatcher The new dispatcher.
1394     */

1395    protected final void setDispatcher(Dispatcher dispatcher) {
1396        fDispatcher = dispatcher;
1397        if (DEBUG_DISPATCHER) {
1398            System.out.print("%%% setDispatcher: ");
1399            System.out.print(getDispatcherName(dispatcher));
1400            System.out.println();
1401        }
1402    }
1403
1404    //
1405
// Private methods
1406
//
1407

1408    /** Returns the scanner state name. */
1409    protected String JavaDoc getScannerStateName(int state) {
1410
1411        switch (state) {
1412            case SCANNER_STATE_DOCTYPE: return "SCANNER_STATE_DOCTYPE";
1413            case SCANNER_STATE_ROOT_ELEMENT: return "SCANNER_STATE_ROOT_ELEMENT";
1414            case SCANNER_STATE_START_OF_MARKUP: return "SCANNER_STATE_START_OF_MARKUP";
1415            case SCANNER_STATE_COMMENT: return "SCANNER_STATE_COMMENT";
1416            case SCANNER_STATE_PI: return "SCANNER_STATE_PI";
1417            case SCANNER_STATE_CONTENT: return "SCANNER_STATE_CONTENT";
1418            case SCANNER_STATE_REFERENCE: return "SCANNER_STATE_REFERENCE";
1419            case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT";
1420            case SCANNER_STATE_TERMINATED: return "SCANNER_STATE_TERMINATED";
1421            case SCANNER_STATE_CDATA: return "SCANNER_STATE_CDATA";
1422            case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL";
1423        }
1424
1425        return "??? ("+state+')';
1426
1427    } // getScannerStateName(int):String
1428

1429    /** Returns the dispatcher name. */
1430    public String JavaDoc getDispatcherName(Dispatcher dispatcher) {
1431
1432        if (DEBUG_DISPATCHER) {
1433            if (dispatcher != null) {
1434                String JavaDoc name = dispatcher.getClass().getName();
1435                int index = name.lastIndexOf('.');
1436                if (index != -1) {
1437                    name = name.substring(index + 1);
1438                    index = name.lastIndexOf('$');
1439                    if (index != -1) {
1440                        name = name.substring(index + 1);
1441                    }
1442                }
1443                return name;
1444            }
1445        }
1446        return "null";
1447
1448    } // getDispatcherName():String
1449

1450    //
1451
// Classes
1452
//
1453

1454    /**
1455     * Element stack. This stack operates without synchronization, error
1456     * checking, and it re-uses objects instead of throwing popped items
1457     * away.
1458     *
1459     * @author Andy Clark, IBM
1460     */

1461    protected static class ElementStack {
1462
1463        //
1464
// Data
1465
//
1466

1467        /** The stack data. */
1468        protected QName[] fElements;
1469
1470        /** The size of the stack. */
1471        protected int fSize;
1472
1473        //
1474
// Constructors
1475
//
1476

1477        /** Default constructor. */
1478        public ElementStack() {
1479            fElements = new QName[10];
1480            for (int i = 0; i < fElements.length; i++) {
1481                fElements[i] = new QName();
1482            }
1483        } // <init>()
1484

1485        //
1486
// Public methods
1487
//
1488

1489        /**
1490         * Pushes an element on the stack.
1491         * <p>
1492         * <strong>Note:</strong> The QName values are copied into the
1493         * stack. In other words, the caller does <em>not</em> orphan
1494         * the element to the stack. Also, the QName object returned
1495         * is <em>not</em> orphaned to the caller. It should be
1496         * considered read-only.
1497         *
1498         * @param element The element to push onto the stack.
1499         *
1500         * @return Returns the actual QName object that stores the
1501         */

1502        public QName pushElement(QName element) {
1503            if (fSize == fElements.length) {
1504                QName[] array = new QName[fElements.length * 2];
1505                System.arraycopy(fElements, 0, array, 0, fSize);
1506                fElements = array;
1507                for (int i = fSize; i < fElements.length; i++) {
1508                    fElements[i] = new QName();
1509                }
1510            }
1511            fElements[fSize].setValues(element);
1512            return fElements[fSize++];
1513        } // pushElement(QName):QName
1514

1515        /**
1516         * Pops an element off of the stack by setting the values of
1517         * the specified QName.
1518         * <p>
1519         * <strong>Note:</strong> The object returned is <em>not</em>
1520         * orphaned to the caller. Therefore, the caller should consider
1521         * the object to be read-only.
1522         */

1523        public void popElement(QName element) {
1524            element.setValues(fElements[--fSize]);
1525        } // popElement(QName)
1526

1527        /** Clears the stack without throwing away existing QName objects. */
1528        public void clear() {
1529            fSize = 0;
1530        } // clear()
1531

1532    } // class ElementStack
1533

1534    /**
1535     * This interface defines an XML "event" dispatching model. Classes
1536     * that implement this interface are responsible for scanning parts
1537     * of the XML document and dispatching callbacks.
1538     *
1539     * @xerces.internal
1540     *
1541     * @author Glenn Marcy, IBM
1542     */

1543    protected interface Dispatcher {
1544
1545        //
1546
// Dispatcher methods
1547
//
1548

1549        /**
1550         * Dispatch an XML "event".
1551         *
1552         * @param complete True if this dispatcher is intended to scan
1553         * and dispatch as much as possible.
1554         *
1555         * @return True if there is more to dispatch either from this
1556         * or a another dispatcher.
1557         *
1558         * @throws IOException Thrown on i/o error.
1559         * @throws XNIException Thrown on parse error.
1560         */

1561        public boolean dispatch(boolean complete)
1562            throws IOException JavaDoc, XNIException;
1563
1564    } // interface Dispatcher
1565

1566    /**
1567     * Dispatcher to handle content scanning.
1568     *
1569     * @author Andy Clark, IBM
1570     * @author Eric Ye, IBM
1571     */

1572    protected class FragmentContentDispatcher
1573        implements Dispatcher {
1574
1575        //
1576
// Dispatcher methods
1577
//
1578

1579        /**
1580         * Dispatch an XML "event".
1581         *
1582         * @param complete True if this dispatcher is intended to scan
1583         * and dispatch as much as possible.
1584         *
1585         * @return True if there is more to dispatch either from this
1586         * or a another dispatcher.
1587         *
1588         * @throws IOException Thrown on i/o error.
1589         * @throws XNIException Thrown on parse error.
1590         */

1591        public boolean dispatch(boolean complete)
1592            throws IOException JavaDoc, XNIException {
1593            try {
1594                boolean again;
1595                do {
1596                    again = false;
1597                    switch (fScannerState) {
1598                        case SCANNER_STATE_CONTENT: {
1599                            if (fEntityScanner.skipChar('<')) {
1600                                setScannerState(SCANNER_STATE_START_OF_MARKUP);
1601                                again = true;
1602                            }
1603                            else if (fEntityScanner.skipChar('&')) {
1604                                setScannerState(SCANNER_STATE_REFERENCE);
1605                                again = true;
1606                            }
1607                            else {
1608                                do {
1609                                    int c = scanContent();
1610                                    if (c == '<') {
1611                                        fEntityScanner.scanChar();
1612                                        setScannerState(SCANNER_STATE_START_OF_MARKUP);
1613                                        break;
1614                                    }
1615                                    else if (c == '&') {
1616                                        fEntityScanner.scanChar();
1617                                        setScannerState(SCANNER_STATE_REFERENCE);
1618                                        break;
1619                                    }
1620                                    else if (c != -1 && isInvalidLiteral(c)) {
1621                                        if (XMLChar.isHighSurrogate(c)) {
1622                                            // special case: surrogates
1623
fStringBuffer.clear();
1624                                            if (scanSurrogates(fStringBuffer)) {
1625                                                // call handler
1626
if (fDocumentHandler != null) {
1627                                                    fDocumentHandler.characters(fStringBuffer, null);
1628                                                }
1629                                            }
1630                                        }
1631                                        else {
1632                                            reportFatalError("InvalidCharInContent",
1633                                                             new Object JavaDoc[] {
1634                                                Integer.toString(c, 16)});
1635                                            fEntityScanner.scanChar();
1636                                        }
1637                                    }
1638                                } while (complete);
1639                            }
1640                            break;
1641                        }
1642                        case SCANNER_STATE_START_OF_MARKUP: {
1643                            fMarkupDepth++;
1644                            if (fEntityScanner.skipChar('/')) {
1645                                if (scanEndElement() == 0) {
1646                                    if (elementDepthIsZeroHook()) {
1647                                        return true;
1648                                    }
1649                                }
1650                                setScannerState(SCANNER_STATE_CONTENT);
1651                            }
1652                            else if (isValidNameStartChar(fEntityScanner.peekChar())) {
1653                                scanStartElement();
1654                                setScannerState(SCANNER_STATE_CONTENT);
1655                            }
1656                            else if (fEntityScanner.skipChar('!')) {
1657                                if (fEntityScanner.skipChar('-')) {
1658                                    if (!fEntityScanner.skipChar('-')) {
1659                                        reportFatalError("InvalidCommentStart",
1660                                                         null);
1661                                    }
1662                                    setScannerState(SCANNER_STATE_COMMENT);
1663                                    again = true;
1664                                }
1665                                else if (fEntityScanner.skipString("[CDATA[")) {
1666                                    setScannerState(SCANNER_STATE_CDATA);
1667                                    again = true;
1668                                }
1669                                else if (!scanForDoctypeHook()) {
1670                                    reportFatalError("MarkupNotRecognizedInContent",
1671                                                     null);
1672                                }
1673                            }
1674                            else if (fEntityScanner.skipChar('?')) {
1675                                setScannerState(SCANNER_STATE_PI);
1676                                again = true;
1677                            }
1678                            else if (isValidNameStartHighSurrogate(fEntityScanner.peekChar())) {
1679                                scanStartElement();
1680                                setScannerState(SCANNER_STATE_CONTENT);
1681                            }
1682                            else {
1683                                reportFatalError("MarkupNotRecognizedInContent",
1684                                                 null);
1685                                setScannerState(SCANNER_STATE_CONTENT);
1686                            }
1687                            break;
1688                        }
1689                        case SCANNER_STATE_COMMENT: {
1690                            scanComment();
1691                            setScannerState(SCANNER_STATE_CONTENT);
1692                            break;
1693                        }
1694                        case SCANNER_STATE_PI: {
1695                            scanPI();
1696                            setScannerState(SCANNER_STATE_CONTENT);
1697                            break;
1698                        }
1699                        case SCANNER_STATE_CDATA: {
1700                            scanCDATASection(complete);
1701                            setScannerState(SCANNER_STATE_CONTENT);
1702                            break;
1703                        }
1704                        case SCANNER_STATE_REFERENCE: {
1705                            fMarkupDepth++;
1706                            // NOTE: We need to set the state beforehand
1707
// because the XMLEntityHandler#startEntity
1708
// callback could set the state to
1709
// SCANNER_STATE_TEXT_DECL and we don't want
1710
// to override that scanner state.
1711
setScannerState(SCANNER_STATE_CONTENT);
1712                            if (fEntityScanner.skipChar('#')) {
1713                                scanCharReference();
1714                            }
1715                            else {
1716                                scanEntityReference();
1717                            }
1718                            break;
1719                        }
1720                        case SCANNER_STATE_TEXT_DECL: {
1721                            // scan text decl
1722
if (fEntityScanner.skipString("<?xml")) {
1723                                fMarkupDepth++;
1724                                // NOTE: special case where entity starts with a PI
1725
// whose name starts with "xml" (e.g. "xmlfoo")
1726
if (isValidNameChar(fEntityScanner.peekChar())) {
1727                                    fStringBuffer.clear();
1728                                    fStringBuffer.append("xml");
1729                                    if (fNamespaces) {
1730                                        while (isValidNCName(fEntityScanner.peekChar())) {
1731                                            fStringBuffer.append((char)fEntityScanner.scanChar());
1732                                        }
1733                                    }
1734                                    else {
1735                                        while (isValidNameChar(fEntityScanner.peekChar())) {
1736                                            fStringBuffer.append((char)fEntityScanner.scanChar());
1737                                        }
1738                                    }
1739                                    String JavaDoc target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
1740                                    scanPIData(target, fTempString);
1741                                }
1742                
1743                                // standard text declaration
1744
else {
1745                                    scanXMLDeclOrTextDecl(true);
1746                                }
1747                            }
1748                            // now that we've straightened out the readers, we can read in chunks:
1749
fEntityManager.fCurrentEntity.mayReadChunks = true;
1750                            setScannerState(SCANNER_STATE_CONTENT);
1751                            break;
1752                        }
1753                        case SCANNER_STATE_ROOT_ELEMENT: {
1754                            if (scanRootElementHook()) {
1755                                return true;
1756                            }
1757                            setScannerState(SCANNER_STATE_CONTENT);
1758                            break;
1759                        }
1760                        case SCANNER_STATE_DOCTYPE: {
1761                            reportFatalError("DoctypeIllegalInContent",
1762                                             null);
1763                            setScannerState(SCANNER_STATE_CONTENT);
1764                        }
1765                    }
1766                } while (complete || again);
1767            }
1768            // encoding errors
1769
catch (MalformedByteSequenceException e) {
1770                fErrorReporter.reportError(e.getDomain(), e.getKey(),
1771                    e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR);
1772                return false;
1773            }
1774            catch (CharConversionException JavaDoc e) {
1775                reportFatalError("CharConversionFailure", null);
1776                return false;
1777            }
1778            // premature end of file
1779
catch (EOFException JavaDoc e) {
1780                endOfFileHook(e);
1781                return false;
1782            }
1783
1784            return true;
1785
1786        } // dispatch(boolean):boolean
1787

1788        //
1789
// Protected methods
1790
//
1791

1792        // hooks
1793

1794        // NOTE: These hook methods are added so that the full document
1795
// scanner can share the majority of code with this class.
1796

1797        /**
1798         * Scan for DOCTYPE hook. This method is a hook for subclasses
1799         * to add code to handle scanning for a the "DOCTYPE" string
1800         * after the string "<!" has been scanned.
1801         *
1802         * @return True if the "DOCTYPE" was scanned; false if "DOCTYPE"
1803         * was not scanned.
1804         */

1805        protected boolean scanForDoctypeHook()
1806            throws IOException JavaDoc, XNIException {
1807            return false;
1808        } // scanForDoctypeHook():boolean
1809

1810        /**
1811         * Element depth iz zero. This methos is a hook for subclasses
1812         * to add code to handle when the element depth hits zero. When
1813         * scanning a document fragment, an element depth of zero is
1814         * normal. However, when scanning a full XML document, the
1815         * scanner must handle the trailing miscellanous section of
1816         * the document after the end of the document's root element.
1817         *
1818         * @return True if the caller should stop and return true which
1819         * allows the scanner to switch to a new scanning
1820         * dispatcher. A return value of false indicates that
1821         * the content dispatcher should continue as normal.
1822         */

1823        protected boolean elementDepthIsZeroHook()
1824            throws IOException JavaDoc, XNIException {
1825            return false;
1826        } // elementDepthIsZeroHook():boolean
1827

1828        /**
1829         * Scan for root element hook. This method is a hook for
1830         * subclasses to add code that handles scanning for the root
1831         * element. When scanning a document fragment, there is no
1832         * "root" element. However, when scanning a full XML document,
1833         * the scanner must handle the root element specially.
1834         *
1835         * @return True if the caller should stop and return true which
1836         * allows the scanner to switch to a new scanning
1837         * dispatcher. A return value of false indicates that
1838         * the content dispatcher should continue as normal.
1839         */

1840        protected boolean scanRootElementHook()
1841            throws IOException JavaDoc, XNIException {
1842            return false;
1843        } // scanRootElementHook():boolean
1844

1845        /**
1846         * End of file hook. This method is a hook for subclasses to
1847         * add code that handles the end of file. The end of file in
1848         * a document fragment is OK if the markup depth is zero.
1849         * However, when scanning a full XML document, an end of file
1850         * is always premature.
1851         */

1852        protected void endOfFileHook(EOFException JavaDoc e)
1853            throws IOException JavaDoc, XNIException {
1854
1855            // NOTE: An end of file is only only an error if we were
1856
// in the middle of scanning some markup. -Ac
1857
if (fMarkupDepth != 0) {
1858                reportFatalError("PrematureEOF", null);
1859            }
1860
1861        } // endOfFileHook()
1862

1863    } // class FragmentContentDispatcher
1864

1865} // class XMLDocumentFragmentScannerImpl
1866
Popular Tags