KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > impl > XMLDocumentFragmentScannerImpl


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999-2004 The Apache Software Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package com.sun.org.apache.xerces.internal.impl;
59
60 import java.io.CharConversionException JavaDoc;
61 import java.io.EOFException JavaDoc;
62 import java.io.IOException JavaDoc;
63
64 import com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException;
65 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
66 import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
67 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
68 import com.sun.org.apache.xerces.internal.util.XMLChar;
69 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
70 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
71 import com.sun.org.apache.xerces.internal.xni.Augmentations;
72 import com.sun.org.apache.xerces.internal.xni.QName;
73 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
74 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
75 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
76 import com.sun.org.apache.xerces.internal.xni.XMLString;
77 import com.sun.org.apache.xerces.internal.xni.XNIException;
78 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
79 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
80 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
81 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner;
82 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
83 import com.sun.org.apache.xerces.internal.util.SecurityManager;
84
85 /**
86  * This class is responsible for scanning the structure and content
87  * of document fragments. The scanner acts as the source for the
88  * document information which is communicated to the document handler.
89  * <p>
90  * This component requires the following features and properties from the
91  * component manager that uses it:
92  * <ul>
93  * <li>http://xml.org/sax/features/validation</li>
94  * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
95  * <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
96  * <li>http://apache.org/xml/properties/internal/symbol-table</li>
97  * <li>http://apache.org/xml/properties/internal/error-reporter</li>
98  * <li>http://apache.org/xml/properties/internal/entity-manager</li>
99  * </ul>
100  *
101  * @author Glenn Marcy, IBM
102  * @author Andy Clark, IBM
103  * @author Arnaud Le Hors, IBM
104  * @author Eric Ye, IBM
105  *
106  * @version $Id: XMLDocumentFragmentScannerImpl.java,v 1.52 2004/04/08 22:24:52 mrglavas Exp $
107  */

108 public class XMLDocumentFragmentScannerImpl
109     extends XMLScanner
110     implements XMLDocumentScanner, XMLComponent, XMLEntityHandler {
111
112     //
113
// Constants
114
//
115

116     protected SecurityManager JavaDoc fSecurityManager = null;
117     
118     // scanner states
119

120     /** Scanner state: start of markup. */
121     protected static final int SCANNER_STATE_START_OF_MARKUP = 1;
122
123     /** Scanner state: comment. */
124     protected static final int SCANNER_STATE_COMMENT = 2;
125
126     /** Scanner state: processing instruction. */
127     protected static final int SCANNER_STATE_PI = 3;
128
129     /** Scanner state: DOCTYPE. */
130     protected static final int SCANNER_STATE_DOCTYPE = 4;
131
132     /** Scanner state: root element. */
133     protected static final int SCANNER_STATE_ROOT_ELEMENT = 6;
134
135     /** Scanner state: content. */
136     protected static final int SCANNER_STATE_CONTENT = 7;
137
138     /** Scanner state: reference. */
139     protected static final int SCANNER_STATE_REFERENCE = 8;
140
141     /** Scanner state: end of input. */
142     protected static final int SCANNER_STATE_END_OF_INPUT = 13;
143
144     /** Scanner state: terminated. */
145     protected static final int SCANNER_STATE_TERMINATED = 14;
146
147     /** Scanner state: CDATA section. */
148     protected static final int SCANNER_STATE_CDATA = 15;
149
150     /** Scanner state: Text declaration. */
151     protected static final int SCANNER_STATE_TEXT_DECL = 16;
152
153     // feature identifiers
154

155     /** Feature identifier: namespaces. */
156     protected static final String JavaDoc NAMESPACES =
157         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
158
159     /** Feature identifier: notify built-in refereces. */
160     protected static final String JavaDoc NOTIFY_BUILTIN_REFS =
161         Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_BUILTIN_REFS_FEATURE;
162         
163     // property identifiers
164

165     /** Property identifier: entity resolver. */
166     protected static final String JavaDoc ENTITY_RESOLVER =
167         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
168     
169     // recognized features and properties
170

171     /** Recognized features. */
172     private static final String JavaDoc[] RECOGNIZED_FEATURES = {
173         NAMESPACES,
174         VALIDATION,
175         NOTIFY_BUILTIN_REFS,
176         NOTIFY_CHAR_REFS,
177     };
178
179     /** Feature defaults. */
180     private static final Boolean JavaDoc[] FEATURE_DEFAULTS = {
181         null,
182         null,
183         Boolean.FALSE,
184         Boolean.FALSE,
185     };
186
187     /** Recognized properties. */
188     private static final String JavaDoc[] RECOGNIZED_PROPERTIES = {
189         SYMBOL_TABLE,
190         ERROR_REPORTER,
191         ENTITY_MANAGER,
192         ENTITY_RESOLVER,
193     };
194
195     /** Property defaults. */
196     private static final Object JavaDoc[] PROPERTY_DEFAULTS = {
197         null,
198         null,
199         null,
200         null,
201     };
202
203     // debugging
204

205     /** Debug scanner state. */
206     private static final boolean DEBUG_SCANNER_STATE = false;
207
208     /** Debug dispatcher. */
209     private static final boolean DEBUG_DISPATCHER = false;
210
211     /** Debug content dispatcher scanning. */
212     protected static final boolean DEBUG_CONTENT_SCANNING = false;
213
214     //
215
// Data
216
//
217

218     // protected data
219

220     /** Document handler. */
221     protected XMLDocumentHandler fDocumentHandler;
222
223     /** Entity stack. */
224     protected int[] fEntityStack = new int[4];
225
226     /** Markup depth. */
227     protected int fMarkupDepth;
228
229     /** Scanner state. */
230     protected int fScannerState;
231
232     /** SubScanner state: inside scanContent method. */
233     protected boolean fInScanContent = false;
234
235     /** has external dtd */
236     protected boolean fHasExternalDTD;
237     
238     /** Standalone. */
239     protected boolean fStandalone;
240
241     //variable to restrict attribute limit
242
protected int fElementAttributeLimit;
243     
244     
245     /** External subset resolver. **/
246     protected ExternalSubsetResolver fExternalSubsetResolver;
247
248     // element information
249

250     /** Current element. */
251     protected QName fCurrentElement;
252
253     /** Element stack. */
254     protected ElementStack fElementStack = new ElementStack();
255
256     // other info
257

258     /** Document system identifier.
259      * REVISIT: So what's this used for? - NG
260     * protected String fDocumentSystemId;
261      ******/

262
263     // features
264

265     /** Notify built-in references. */
266     protected boolean fNotifyBuiltInRefs = false;
267
268     // dispatchers
269

270     /** Active dispatcher. */
271     protected Dispatcher fDispatcher;
272
273     /** Content dispatcher. */
274     protected Dispatcher fContentDispatcher = createContentDispatcher();
275
276     // temporary variables
277

278     /** Element QName. */
279     protected QName fElementQName = new QName();
280
281     /** Attribute QName. */
282     protected QName fAttributeQName = new QName();
283
284     /** Element attributes. */
285     protected XMLAttributesImpl fAttributes = new XMLAttributesImpl();
286
287     /** String. */
288     protected XMLString fTempString = new XMLString();
289
290     /** String. */
291     protected XMLString fTempString2 = new XMLString();
292
293     /** Array of 3 strings. */
294     private String JavaDoc[] fStrings = new String JavaDoc[3];
295
296     /** String buffer. */
297     private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
298
299     /** String buffer. */
300     private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
301
302
303     /** Another QName. */
304     private QName fQName = new QName();
305
306
307     /** Single character array. */
308     private final char[] fSingleChar = new char[1];
309
310     /** External entity. */
311     private XMLEntityManager.ExternalEntity fExternalEntity = new XMLEntityManager.ExternalEntity();
312     
313     /**
314      * Saw spaces after element name or between attributes.
315      *
316      * This is reserved for the case where scanning of a start element spans
317      * several methods, as is the case when scanning the start of a root element
318      * where a DTD external subset may be read after scanning the element name.
319      */

320     private boolean fSawSpace;
321
322     //
323
// Constructors
324
//
325

326     /** Default constructor. */
327     public XMLDocumentFragmentScannerImpl() {} // <init>()
328

329     //
330
// XMLDocumentScanner methods
331
//
332

333     /**
334      * Sets the input source.
335      *
336      * @param inputSource The input source.
337      *
338      * @throws IOException Thrown on i/o error.
339      */

340     public void setInputSource(XMLInputSource inputSource) throws IOException JavaDoc {
341         fEntityManager.setEntityHandler(this);
342         fEntityManager.startEntity("$fragment$", inputSource, false, true);
343         //fDocumentSystemId = fEntityManager.expandSystemId(inputSource.getSystemId());
344
} // setInputSource(XMLInputSource)
345

346     /**
347      * Scans a document.
348      *
349      * @param complete True if the scanner should scan the document
350      * completely, pushing all events to the registered
351      * document handler. A value of false indicates that
352      * that the scanner should only scan the next portion
353      * of the document and return. A scanner instance is
354      * permitted to completely scan a document if it does
355      * not support this "pull" scanning model.
356      *
357      * @return True if there is more to scan, false otherwise.
358      */

359     public boolean scanDocument(boolean complete)
360         throws IOException JavaDoc, XNIException {
361         
362         // reset entity scanner
363
fEntityScanner = fEntityManager.getEntityScanner();
364         
365         // keep dispatching "events"
366
fEntityManager.setEntityHandler(this);
367         do {
368             if (!fDispatcher.dispatch(complete)) {
369                 return false;
370             }
371         } while (complete);
372
373         // return success
374
return true;
375
376     } // scanDocument(boolean):boolean
377

378     //
379
// XMLComponent methods
380
//
381

382     /**
383      * Resets the component. The component can query the component manager
384      * about any features and properties that affect the operation of the
385      * component.
386      *
387      * @param componentManager The component manager.
388      *
389      * @throws SAXException Thrown by component on initialization error.
390      * For example, if a feature or property is
391      * required for the operation of the component, the
392      * component manager may throw a
393      * SAXNotRecognizedException or a
394      * SAXNotSupportedException.
395      */

396     public void reset(XMLComponentManager componentManager)
397         throws XMLConfigurationException {
398
399         super.reset(componentManager);
400
401         // other settings
402
//fDocumentSystemId = null;
403

404         // sax features
405
fAttributes.setNamespaces(fNamespaces);
406
407         // initialize vars
408
fMarkupDepth = 0;
409         fCurrentElement = null;
410         fElementStack.clear();
411         fHasExternalDTD = false;
412         fStandalone = false;
413         fInScanContent = false;
414
415         // setup dispatcher
416
try {
417             fSecurityManager = (SecurityManager JavaDoc)componentManager.getProperty(Constants.SECURITY_MANAGER);
418         } catch (XMLConfigurationException e) {
419             fSecurityManager = null;
420         }
421         
422         fElementAttributeLimit = (fSecurityManager != null)?fSecurityManager.getElementAttrLimit():0;
423         setScannerState(SCANNER_STATE_CONTENT);
424         setDispatcher(fContentDispatcher);
425
426         if (fParserSettings) {
427             // parser settings have changed. reset them.
428

429             // xerces features
430
try {
431                 fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS);
432             } catch (XMLConfigurationException e) {
433                 fNotifyBuiltInRefs = false;
434             }
435             
436             // xerces properties
437
try {
438                 Object JavaDoc resolver = componentManager.getProperty(ENTITY_RESOLVER);
439                 fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ?
440                     (ExternalSubsetResolver) resolver : null;
441             }
442             catch (XMLConfigurationException e) {
443                 fExternalSubsetResolver = null;
444             }
445         }
446
447     } // reset(XMLComponentManager)
448

449     /**
450      * Returns a list of feature identifiers that are recognized by
451      * this component. This method may return null if no features
452      * are recognized by this component.
453      */

454     public String JavaDoc[] getRecognizedFeatures() {
455         return (String JavaDoc[])(RECOGNIZED_FEATURES.clone());
456     } // getRecognizedFeatures():String[]
457

458     /**
459      * Sets the state of a feature. This method is called by the component
460      * manager any time after reset when a feature changes state.
461      * <p>
462      * <strong>Note:</strong> Components should silently ignore features
463      * that do not affect the operation of the component.
464      *
465      * @param featureId The feature identifier.
466      * @param state The state of the feature.
467      *
468      * @throws SAXNotRecognizedException The component should not throw
469      * this exception.
470      * @throws SAXNotSupportedException The component should not throw
471      * this exception.
472      */

473     public void setFeature(String JavaDoc featureId, boolean state)
474         throws XMLConfigurationException {
475
476         super.setFeature(featureId, state);
477             
478         // Xerces properties
479
if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
480             final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
481             if (suffixLength == Constants.NOTIFY_BUILTIN_REFS_FEATURE.length() &&
482                 featureId.endsWith(Constants.NOTIFY_BUILTIN_REFS_FEATURE)) {
483                 fNotifyBuiltInRefs = state;
484             }
485         }
486
487     } // setFeature(String,boolean)
488

489     /**
490      * Returns a list of property identifiers that are recognized by
491      * this component. This method may return null if no properties
492      * are recognized by this component.
493      */

494     public String JavaDoc[] getRecognizedProperties() {
495         return (String JavaDoc[])(RECOGNIZED_PROPERTIES.clone());
496     } // getRecognizedProperties():String[]
497

498     /**
499      * Sets the value of a property. This method is called by the component
500      * manager any time after reset when a property changes value.
501      * <p>
502      * <strong>Note:</strong> Components should silently ignore properties
503      * that do not affect the operation of the component.
504      *
505      * @param propertyId The property identifier.
506      * @param value The value of the property.
507      *
508      * @throws SAXNotRecognizedException The component should not throw
509      * this exception.
510      * @throws SAXNotSupportedException The component should not throw
511      * this exception.
512      */

513     public void setProperty(String JavaDoc propertyId, Object JavaDoc value)
514         throws XMLConfigurationException {
515         
516         super.setProperty(propertyId, value);
517
518         // Xerces properties
519
if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
520             final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
521             if (suffixLength == Constants.ENTITY_MANAGER_PROPERTY.length() &&
522                 propertyId.endsWith(Constants.ENTITY_MANAGER_PROPERTY)) {
523                 fEntityManager = (XMLEntityManager)value;
524                 return;
525             }
526             if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() &&
527                 propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
528                 fExternalSubsetResolver = (value instanceof ExternalSubsetResolver) ?
529                     (ExternalSubsetResolver) value : null;
530                 return;
531             }
532         }
533         
534     } // setProperty(String,Object)
535

536     /**
537      * Returns the default state for a feature, or null if this
538      * component does not want to report a default value for this
539      * feature.
540      *
541      * @param featureId The feature identifier.
542      *
543      * @since Xerces 2.2.0
544      */

545     public Boolean JavaDoc getFeatureDefault(String JavaDoc featureId) {
546         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
547             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
548                 return FEATURE_DEFAULTS[i];
549             }
550         }
551         return null;
552     } // getFeatureDefault(String):Boolean
553

554     /**
555      * Returns the default state for a property, or null if this
556      * component does not want to report a default value for this
557      * property.
558      *
559      * @param propertyId The property identifier.
560      *
561      * @since Xerces 2.2.0
562      */

563     public Object JavaDoc getPropertyDefault(String JavaDoc propertyId) {
564         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
565             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
566                 return PROPERTY_DEFAULTS[i];
567             }
568         }
569         return null;
570     } // getPropertyDefault(String):Object
571

572     //
573
// XMLDocumentSource methods
574
//
575

576     /**
577      * setDocumentHandler
578      *
579      * @param documentHandler
580      */

581     public void setDocumentHandler(XMLDocumentHandler documentHandler) {
582         fDocumentHandler = documentHandler;
583     } // setDocumentHandler(XMLDocumentHandler)
584

585
586     /** Returns the document handler */
587     public XMLDocumentHandler getDocumentHandler(){
588         return fDocumentHandler;
589     }
590
591     //
592
// XMLEntityHandler methods
593
//
594

595     /**
596      * This method notifies of the start of an entity. The DTD has the
597      * pseudo-name of "[dtd]" parameter entity names start with '%'; and
598      * general entities are just specified by their name.
599      *
600      * @param name The name of the entity.
601      * @param identifier The resource identifier.
602      * @param encoding The auto-detected IANA encoding name of the entity
603      * stream. This value will be null in those situations
604      * where the entity encoding is not auto-detected (e.g.
605      * internal entities or a document entity that is
606      * parsed from a java.io.Reader).
607      * @param augs Additional information that may include infoset augmentations
608      *
609      * @throws XNIException Thrown by handler to signal an error.
610      */

611     public void startEntity(String JavaDoc name,
612                             XMLResourceIdentifier identifier,
613                             String JavaDoc encoding, Augmentations augs) throws XNIException {
614
615         // keep track of this entity before fEntityDepth is increased
616
if (fEntityDepth == fEntityStack.length) {
617             int[] entityarray = new int[fEntityStack.length * 2];
618             System.arraycopy(fEntityStack, 0, entityarray, 0, fEntityStack.length);
619             fEntityStack = entityarray;
620         }
621         fEntityStack[fEntityDepth] = fMarkupDepth;
622
623         super.startEntity(name, identifier, encoding, augs);
624
625         // WFC: entity declared in external subset in standalone doc
626
if(fStandalone && fEntityManager.isEntityDeclInExternalSubset(name)) {
627             reportFatalError("MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
628                 new Object JavaDoc[]{name});
629         }
630
631         // call handler
632
if (fDocumentHandler != null && !fScanningAttribute) {
633             if (!name.equals("[xml]")) {
634                 fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
635             }
636         }
637
638     } // startEntity(String,XMLResourceIdentifier,String)
639

640     /**
641      * This method notifies the end of an entity. The DTD has the pseudo-name
642      * of "[dtd]" parameter entity names start with '%'; and general entities
643      * are just specified by their name.
644      *
645      * @param name The name of the entity.
646      * @param augs Additional information that may include infoset augmentations
647      *
648      * @throws XNIException Thrown by handler to signal an error.
649      */

650     public void endEntity(String JavaDoc name, Augmentations augs) throws XNIException {
651
652         // flush possible pending output buffer - see scanContent
653
if (fInScanContent && fStringBuffer.length != 0
654             && fDocumentHandler != null) {
655             fDocumentHandler.characters(fStringBuffer, null);
656             fStringBuffer.length = 0; // make sure we know it's been flushed
657
}
658
659         super.endEntity(name, augs);
660
661         // make sure markup is properly balanced
662
if (fMarkupDepth != fEntityStack[fEntityDepth]) {
663             reportFatalError("MarkupEntityMismatch", null);
664         }
665
666         // call handler
667
if (fDocumentHandler != null && !fScanningAttribute) {
668             if (!name.equals("[xml]")) {
669                 fDocumentHandler.endGeneralEntity(name, augs);
670             }
671         }
672         
673     } // endEntity(String)
674

675     //
676
// Protected methods
677
//
678

679     // dispatcher factory methods
680

681     /** Creates a content dispatcher. */
682     protected Dispatcher createContentDispatcher() {
683         return new FragmentContentDispatcher();
684     } // createContentDispatcher():Dispatcher
685

686     // scanning methods
687

688     /**
689      * Scans an XML or text declaration.
690      * <p>
691      * <pre>
692      * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
693      * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
694      * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
695      * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
696      * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
697      * | ('"' ('yes' | 'no') '"'))
698      *
699      * [77] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?>'
700      * </pre>
701      *
702      * @param scanningTextDecl True if a text declaration is to
703      * be scanned instead of an XML
704      * declaration.
705      */

706     protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl)
707         throws IOException JavaDoc, XNIException {
708
709         // scan decl
710
super.scanXMLDeclOrTextDecl(scanningTextDecl, fStrings);
711         fMarkupDepth--;
712
713         // pseudo-attribute values
714
String JavaDoc version = fStrings[0];
715         String JavaDoc encoding = fStrings[1];
716         String JavaDoc standalone = fStrings[2];
717
718         // set standalone
719
fStandalone = standalone != null && standalone.equals("yes");
720         fEntityManager.setStandalone(fStandalone);
721
722         // call handler
723
if (fDocumentHandler != null) {
724             if (scanningTextDecl) {
725                 fDocumentHandler.textDecl(version, encoding, null);
726             }
727             else {
728                 fDocumentHandler.xmlDecl(version, encoding, standalone, null);
729             }
730         }
731
732         // set encoding on reader
733
if (encoding != null && !fEntityScanner.fCurrentEntity.isDeclaredEncoding()) {
734             fEntityScanner.setEncoding(encoding);
735         }
736
737     } // scanXMLDeclOrTextDecl(boolean)
738

739     /**
740      * Scans a processing data. This is needed to handle the situation
741      * where a document starts with a processing instruction whose
742      * target name <em>starts with</em> "xml". (e.g. xmlfoo)
743      *
744      * @param target The PI target
745      * @param data The string to fill in with the data
746      */

747     protected void scanPIData(String JavaDoc target, XMLString data)
748         throws IOException JavaDoc, XNIException {
749
750         super.scanPIData(target, data);
751         fMarkupDepth--;
752
753         // call handler
754
if (fDocumentHandler != null) {
755             fDocumentHandler.processingInstruction(target, data, null);
756         }
757
758     } // scanPIData(String)
759

760     /**
761      * Scans a comment.
762      * <p>
763      * <pre>
764      * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
765      * </pre>
766      * <p>
767      * <strong>Note:</strong> Called after scanning past '&lt;!--'
768      */

769     protected void scanComment() throws IOException JavaDoc, XNIException {
770
771         scanComment(fStringBuffer);
772         fMarkupDepth--;
773
774         // call handler
775
if (fDocumentHandler != null) {
776             fDocumentHandler.comment(fStringBuffer, null);
777         }
778
779     } // scanComment()
780

781     /**
782      * Scans a start element. This method will handle the binding of
783      * namespace information and notifying the handler of the start
784      * of the element.
785      * <p>
786      * <pre>
787      * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
788      * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
789      * </pre>
790      * <p>
791      * <strong>Note:</strong> This method assumes that the leading
792      * '&lt;' character has been consumed.
793      * <p>
794      * <strong>Note:</strong> This method uses the fElementQName and
795      * fAttributes variables. The contents of these variables will be
796      * destroyed. The caller should copy important information out of
797      * these variables before calling this method.
798      *
799      * @return True if element is empty. (i.e. It matches
800      * production [44].
801      */

802     protected boolean scanStartElement()
803         throws IOException JavaDoc, XNIException {
804         if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanStartElement()");
805
806         // name
807
if (fNamespaces) {
808             fEntityScanner.scanQName(fElementQName);
809         }
810         else {
811             String JavaDoc name = fEntityScanner.scanName();
812             fElementQName.setValues(null, name, name, null);
813         }
814         String JavaDoc rawname = fElementQName.rawname;
815
816         // push element stack
817
fCurrentElement = fElementStack.pushElement(fElementQName);
818
819         // attributes
820
boolean empty = false;
821         fAttributes.removeAllAttributes();
822         do {
823             // spaces
824
boolean sawSpace = fEntityScanner.skipSpaces();
825
826             // end tag?
827
int c = fEntityScanner.peekChar();
828             if (c == '>') {
829                 fEntityScanner.scanChar();
830                 break;
831             }
832             else if (c == '/') {
833                 fEntityScanner.scanChar();
834                 if (!fEntityScanner.skipChar('>')) {
835                     reportFatalError("ElementUnterminated",
836                                      new Object JavaDoc[]{rawname});
837                 }
838                 empty = true;
839                 break;
840             }
841             else if (!isValidNameStartChar(c) || !sawSpace) {
842                 // Second chance. Check if this character is a high
843
// surrogate of a valid name start character.
844
if (!isValidNameStartHighSurrogate(c) || !sawSpace) {
845                     reportFatalError("ElementUnterminated",
846                                      new Object JavaDoc[] { rawname });
847                 }
848             }
849
850             // attributes
851
scanAttribute(fAttributes);
852             if (fSecurityManager != null && fAttributes.getLength() > fElementAttributeLimit){
853                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
854                                              "ElementAttributeLimit",
855                                              new Object JavaDoc[]{rawname, new Integer JavaDoc(fAttributes.getLength()) },
856                                              XMLErrorReporter.SEVERITY_FATAL_ERROR );
857             }
858         } while (true);
859
860         // call handler
861
if (fDocumentHandler != null) {
862             if (empty) {
863
864                 //decrease the markup depth..
865
fMarkupDepth--;
866                 // check that this element was opened in the same entity
867
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
868                     reportFatalError("ElementEntityMismatch",
869                                      new Object JavaDoc[]{fCurrentElement.rawname});
870                 }
871
872                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
873
874                 //pop the element off the stack..
875
fElementStack.popElement(fElementQName);
876             }
877             else {
878                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
879             }
880         }
881
882         if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanStartElement(): "+empty);
883         return empty;
884
885     } // scanStartElement():boolean
886

887     /**
888      * Scans the name of an element in a start or empty tag.
889      *
890      * @see #scanStartElement()
891      */

892     protected void scanStartElementName ()
893         throws IOException JavaDoc, XNIException {
894         // name
895
if (fNamespaces) {
896             fEntityScanner.scanQName(fElementQName);
897         }
898         else {
899             String JavaDoc name = fEntityScanner.scanName();
900             fElementQName.setValues(null, name, name, null);
901         }
902         // Must skip spaces here because the DTD scanner
903
// would consume them at the end of the external subset.
904
fSawSpace = fEntityScanner.skipSpaces();
905     } // scanStartElementName()
906

907     /**
908      * Scans the remainder of a start or empty tag after the element name.
909      *
910      * @see #scanStartElement
911      * @return True if element is empty.
912      */

913     protected boolean scanStartElementAfterName()
914         throws IOException JavaDoc, XNIException {
915         String JavaDoc rawname = fElementQName.rawname;
916
917         // push element stack
918
fCurrentElement = fElementStack.pushElement(fElementQName);
919
920         // attributes
921
boolean empty = false;
922         fAttributes.removeAllAttributes();
923         do {
924             
925             // end tag?
926
int c = fEntityScanner.peekChar();
927             if (c == '>') {
928                 fEntityScanner.scanChar();
929                 break;
930             }
931             else if (c == '/') {
932                 fEntityScanner.scanChar();
933                 if (!fEntityScanner.skipChar('>')) {
934                     reportFatalError("ElementUnterminated",
935                                      new Object JavaDoc[]{rawname});
936                 }
937                 empty = true;
938                 break;
939             }
940             else if (!isValidNameStartChar(c) || !fSawSpace) {
941                 // Second chance. Check if this character is a high
942
// surrogate of a valid name start character.
943
if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
944                     reportFatalError("ElementUnterminated",
945                                      new Object JavaDoc[] { rawname });
946                 }
947             }
948
949             // attributes
950
scanAttribute(fAttributes);
951             
952             // spaces
953
fSawSpace = fEntityScanner.skipSpaces();
954
955         } while (true);
956
957         // call handler
958
if (fDocumentHandler != null) {
959             if (empty) {
960
961                 //decrease the markup depth..
962
fMarkupDepth--;
963                 // check that this element was opened in the same entity
964
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
965                     reportFatalError("ElementEntityMismatch",
966                                      new Object JavaDoc[]{fCurrentElement.rawname});
967                 }
968
969                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
970
971                 //pop the element off the stack..
972
fElementStack.popElement(fElementQName);
973             }
974             else {
975                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
976             }
977         }
978
979         if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanStartElementAfterName(): "+empty);
980         return empty;
981     } // scanStartElementAfterName()
982

983     /**
984      * Scans an attribute.
985      * <p>
986      * <pre>
987      * [41] Attribute ::= Name Eq AttValue
988      * </pre>
989      * <p>
990      * <strong>Note:</strong> This method assumes that the next
991      * character on the stream is the first character of the attribute
992      * name.
993      * <p>
994      * <strong>Note:</strong> This method uses the fAttributeQName and
995      * fQName variables. The contents of these variables will be
996      * destroyed.
997      *
998      * @param attributes The attributes list for the scanned attribute.
999      */

1000    protected void scanAttribute(XMLAttributes attributes)
1001        throws IOException JavaDoc, XNIException {
1002        if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanAttribute()");
1003
1004        // name
1005
if (fNamespaces) {
1006            fEntityScanner.scanQName(fAttributeQName);
1007        }
1008        else {
1009            String JavaDoc name = fEntityScanner.scanName();
1010            fAttributeQName.setValues(null, name, name, null);
1011        }
1012
1013        // equals
1014
fEntityScanner.skipSpaces();
1015        if (!fEntityScanner.skipChar('=')) {
1016            reportFatalError("EqRequiredInAttribute",
1017                             new Object JavaDoc[]{fCurrentElement.rawname,fAttributeQName.rawname});
1018        }
1019        fEntityScanner.skipSpaces();
1020
1021        // content
1022
int oldLen = attributes.getLength();
1023        int attrIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null);
1024
1025        // WFC: Unique Att Spec
1026
if (oldLen == attributes.getLength()) {
1027            reportFatalError("AttributeNotUnique",
1028                             new Object JavaDoc[]{fCurrentElement.rawname,
1029                                          fAttributeQName.rawname});
1030        }
1031        //REVISIT: one more case needs to be included: external PE and standalone is no
1032
boolean isVC = fHasExternalDTD && !fStandalone;
1033        scanAttributeValue(fTempString, fTempString2,
1034                           fAttributeQName.rawname, isVC,
1035                           fCurrentElement.rawname);
1036        attributes.setValue(attrIndex, fTempString.toString());
1037        attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
1038        attributes.setSpecified(attrIndex, true);
1039
1040        if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanAttribute()");
1041    } // scanAttribute(XMLAttributes)
1042

1043    /**
1044     * Scans element content.
1045     *
1046     * @return Returns the next character on the stream.
1047     */

1048    protected int scanContent() throws IOException JavaDoc, XNIException {
1049
1050        XMLString content = fTempString;
1051        int c = fEntityScanner.scanContent(content);
1052        if (c == '\r') {
1053            // happens when there is the character reference &#13;
1054
fEntityScanner.scanChar();
1055            fStringBuffer.clear();
1056            fStringBuffer.append(fTempString);
1057            fStringBuffer.append((char)c);
1058            content = fStringBuffer;
1059            c = -1;
1060        }
1061        if (fDocumentHandler != null && content.length > 0) {
1062            fDocumentHandler.characters(content, null);
1063        }
1064
1065        if (c == ']' && fTempString.length == 0) {
1066            fStringBuffer.clear();
1067            fStringBuffer.append((char)fEntityScanner.scanChar());
1068            // remember where we are in case we get an endEntity before we
1069
// could flush the buffer out - this happens when we're parsing an
1070
// entity which ends with a ]
1071
fInScanContent = true;
1072            //
1073
// We work on a single character basis to handle cases such as:
1074
// ']]]>' which we might otherwise miss.
1075
//
1076
if (fEntityScanner.skipChar(']')) {
1077                fStringBuffer.append(']');
1078                while (fEntityScanner.skipChar(']')) {
1079                    fStringBuffer.append(']');
1080                }
1081                if (fEntityScanner.skipChar('>')) {
1082                    reportFatalError("CDEndInContent", null);
1083                }
1084            }
1085            if (fDocumentHandler != null && fStringBuffer.length != 0) {
1086                fDocumentHandler.characters(fStringBuffer, null);
1087            }
1088            fInScanContent = false;
1089            c = -1;
1090        }
1091        return c;
1092
1093    } // scanContent():int
1094

1095
1096    /**
1097     * Scans a CDATA section.
1098     * <p>
1099     * <strong>Note:</strong> This method uses the fTempString and
1100     * fStringBuffer variables.
1101     *
1102     * @param complete True if the CDATA section is to be scanned
1103     * completely.
1104     *
1105     * @return True if CDATA is completely scanned.
1106     */

1107    protected boolean scanCDATASection(boolean complete)
1108        throws IOException JavaDoc, XNIException {
1109        
1110        // call handler
1111
if (fDocumentHandler != null) {
1112            fDocumentHandler.startCDATA(null);
1113        }
1114
1115        while (true) {
1116            fStringBuffer.clear();
1117            if (!fEntityScanner.scanData("]]", fStringBuffer)) {
1118                if (fDocumentHandler != null && fStringBuffer.length > 0) {
1119                    fDocumentHandler.characters(fStringBuffer, null);
1120                }
1121                int brackets = 0;
1122                while (fEntityScanner.skipChar(']')) {
1123                    brackets++;
1124                }
1125                if (fDocumentHandler != null && brackets > 0) {
1126                    fStringBuffer.clear();
1127                    if (brackets > XMLEntityManager.DEFAULT_BUFFER_SIZE) {
1128                        // Handle large sequences of ']'
1129
int chunks = brackets / XMLEntityManager.DEFAULT_BUFFER_SIZE;
1130                        int remainder = brackets % XMLEntityManager.DEFAULT_BUFFER_SIZE;
1131                        for (int i = 0; i < XMLEntityManager.DEFAULT_BUFFER_SIZE; i++) {
1132                            fStringBuffer.append(']');
1133                        }
1134                        for (int i = 0; i < chunks; i++) {
1135                            fDocumentHandler.characters(fStringBuffer, null);
1136                        }
1137                        if (remainder != 0) {
1138                            fStringBuffer.length = remainder;
1139                            fDocumentHandler.characters(fStringBuffer, null);
1140                        }
1141                    }
1142                    else {
1143                        for (int i = 0; i < brackets; i++) {
1144                            fStringBuffer.append(']');
1145                        }
1146                       fDocumentHandler.characters(fStringBuffer, null);
1147                    }
1148                }
1149                if (fEntityScanner.skipChar('>')) {
1150                    break;
1151                }
1152                if (fDocumentHandler != null) {
1153                    fStringBuffer.clear();
1154                    fStringBuffer.append("]]");
1155                    fDocumentHandler.characters(fStringBuffer, null);
1156                }
1157            }
1158            else {
1159                if (fDocumentHandler != null) {
1160                    fDocumentHandler.characters(fStringBuffer, null);
1161                }
1162                int c = fEntityScanner.peekChar();
1163                if (c != -1 && isInvalidLiteral(c)) {
1164                    if (XMLChar.isHighSurrogate(c)) {
1165                        fStringBuffer.clear();
1166                        scanSurrogates(fStringBuffer);
1167                        if (fDocumentHandler != null) {
1168                            fDocumentHandler.characters(fStringBuffer, null);
1169                        }
1170                    }
1171                    else {
1172                        reportFatalError("InvalidCharInCDSect",
1173                                        new Object JavaDoc[]{Integer.toString(c,16)});
1174                        fEntityScanner.scanChar();
1175                    }
1176                }
1177            }
1178        }
1179        fMarkupDepth--;
1180
1181        // call handler
1182
if (fDocumentHandler != null) {
1183            fDocumentHandler.endCDATA(null);
1184        }
1185
1186        return true;
1187
1188    } // scanCDATASection(boolean):boolean
1189

1190    /**
1191     * Scans an end element.
1192     * <p>
1193     * <pre>
1194     * [42] ETag ::= '&lt;/' Name S? '>'
1195     * </pre>
1196     * <p>
1197     * <strong>Note:</strong> This method uses the fElementQName variable.
1198     * The contents of this variable will be destroyed. The caller should
1199     * copy the needed information out of this variable before calling
1200     * this method.
1201     *
1202     * @return The element depth.
1203     */

1204    protected int scanEndElement() throws IOException JavaDoc, XNIException {
1205        if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanEndElement()");
1206
1207        fElementStack.popElement(fElementQName) ;
1208
1209        // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
1210
//In scanners most of the time is consumed on checks done for XML characters, we can
1211
// optimize on it and avoid the checks done for endElement,
1212
//we will also avoid symbol table lookup - neeraj.bajaj@sun.com
1213

1214        // this should work both for namespace processing true or false...
1215

1216        //REVISIT: if the string is not the same as expected.. we need to do better error handling..
1217
//We can skip this for now... In any case if the string doesn't match -- document is not well formed.
1218
if (!fEntityScanner.skipString(fElementQName.rawname)) {
1219            reportFatalError("ETagRequired", new Object JavaDoc[]{fElementQName.rawname});
1220        }
1221
1222        // end
1223
fEntityScanner.skipSpaces();
1224        if (!fEntityScanner.skipChar('>')) {
1225            reportFatalError("ETagUnterminated",
1226                             new Object JavaDoc[]{fElementQName.rawname});
1227        }
1228        fMarkupDepth--;
1229
1230        //we have increased the depth for two markup "<" characters
1231
fMarkupDepth--;
1232      
1233        // check that this element was opened in the same entity
1234
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
1235            reportFatalError("ElementEntityMismatch",
1236                             new Object JavaDoc[]{fCurrentElement.rawname});
1237        }
1238
1239        // call handler
1240
if (fDocumentHandler != null ) {
1241            fDocumentHandler.endElement(fElementQName, null);
1242        }
1243
1244        return fMarkupDepth;
1245 
1246    } // scanEndElement():int
1247

1248    /**
1249     * Scans a character reference.
1250     * <p>
1251     * <pre>
1252     * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
1253     * </pre>
1254     */

1255    protected void scanCharReference()
1256        throws IOException JavaDoc, XNIException {
1257
1258        fStringBuffer2.clear();
1259        int ch = scanCharReferenceValue(fStringBuffer2, null);
1260        fMarkupDepth--;
1261        if (ch != -1) {
1262            // call handler
1263
if (fDocumentHandler != null) {
1264                if (fNotifyCharRefs) {
1265                    fDocumentHandler.startGeneralEntity(fCharRefLiteral, null, null, null);
1266                }
1267                Augmentations augs = null;
1268                if (fValidation && ch <= 0x20) {
1269                    augs = new AugmentationsImpl();
1270                    augs.putItem(Constants.CHAR_REF_PROBABLE_WS, Boolean.TRUE);
1271                }
1272                fDocumentHandler.characters(fStringBuffer2, augs);
1273                if (fNotifyCharRefs) {
1274                    fDocumentHandler.endGeneralEntity(fCharRefLiteral, null);
1275                }
1276            }
1277        }
1278
1279    } // scanCharReference()
1280

1281    /**
1282     * Scans an entity reference.
1283     *
1284     * @throws IOException Thrown if i/o error occurs.
1285     * @throws XNIException Thrown if handler throws exception upon
1286     * notification.
1287     */

1288    protected void scanEntityReference() throws IOException JavaDoc, XNIException {
1289
1290        // name
1291
String JavaDoc name = fEntityScanner.scanName();
1292        if (name == null) {
1293            reportFatalError("NameRequiredInReference", null);
1294            return;
1295        }
1296
1297        // end
1298
if (!fEntityScanner.skipChar(';')) {
1299            reportFatalError("SemicolonRequiredInReference", new Object JavaDoc []{name});
1300        }
1301        fMarkupDepth--;
1302
1303        // handle built-in entities
1304
if (name == fAmpSymbol) {
1305            handleCharacter('&', fAmpSymbol);
1306        }
1307        else if (name == fLtSymbol) {
1308            handleCharacter('<', fLtSymbol);
1309        }
1310        else if (name == fGtSymbol) {
1311            handleCharacter('>', fGtSymbol);
1312        }
1313        else if (name == fQuotSymbol) {
1314            handleCharacter('"', fQuotSymbol);
1315        }
1316        else if (name == fAposSymbol) {
1317            handleCharacter('\'', fAposSymbol);
1318        }
1319        // start general entity
1320
else if (fEntityManager.isUnparsedEntity(name)) {
1321            reportFatalError("ReferenceToUnparsedEntity", new Object JavaDoc[]{name});
1322        }
1323        else {
1324            if (!fEntityManager.isDeclaredEntity(name)) {
1325                //REVISIT: one more case needs to be included: external PE and standalone is no
1326
if ( fHasExternalDTD && !fStandalone) {
1327                    if (fValidation)
1328                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared",
1329                                                    new Object JavaDoc[]{name}, XMLErrorReporter.SEVERITY_ERROR);
1330                }
1331                else
1332                    reportFatalError("EntityNotDeclared", new Object JavaDoc[]{name});
1333            }
1334            fEntityManager.startEntity(name, false);
1335        }
1336
1337    } // scanEntityReference()
1338

1339    // utility methods
1340

1341    /**
1342     * Calls document handler with a single character resulting from
1343     * built-in entity resolution.
1344     *
1345     * @param c
1346     * @param entity built-in name
1347     */

1348    private void handleCharacter(char c, String JavaDoc entity) throws XNIException {
1349        if (fDocumentHandler != null) {
1350            if (fNotifyBuiltInRefs) {
1351                fDocumentHandler.startGeneralEntity(entity, null, null, null);
1352            }
1353            
1354            fSingleChar[0] = c;
1355            fTempString.setValues(fSingleChar, 0, 1);
1356            fDocumentHandler.characters(fTempString, null);
1357            
1358            if (fNotifyBuiltInRefs) {
1359                fDocumentHandler.endGeneralEntity(entity, null);
1360            }
1361        }
1362    } // handleCharacter(char)
1363

1364    /**
1365     * Handles the end element. This method will make sure that
1366     * the end element name matches the current element and notify
1367     * the handler about the end of the element and the end of any
1368     * relevent prefix mappings.
1369     * <p>
1370     * <strong>Note:</strong> This method uses the fQName variable.
1371     * The contents of this variable will be destroyed.
1372     *
1373     * @param element The element.
1374     *
1375     * @return The element depth.
1376     *
1377     * @throws XNIException Thrown if the handler throws a SAX exception
1378     * upon notification.
1379     *
1380     */

1381    // REVISIT: need to remove this method. It's not called anymore, because
1382
// the handling is done when the end tag is scanned. - SG
1383
protected int handleEndElement(QName element, boolean isEmpty)
1384        throws XNIException {
1385
1386        fMarkupDepth--;
1387        // check that this element was opened in the same entity
1388
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
1389            reportFatalError("ElementEntityMismatch",
1390                             new Object JavaDoc[]{fCurrentElement.rawname});
1391        }
1392        // make sure the elements match
1393
QName startElement = fQName;
1394        fElementStack.popElement(startElement);
1395        if (element.rawname != startElement.rawname) {
1396            reportFatalError("ETagRequired",
1397                             new Object JavaDoc[]{startElement.rawname});
1398        }
1399
1400        // bind namespaces
1401
if (fNamespaces) {
1402            element.uri = startElement.uri;
1403        }
1404        
1405        // call handler
1406
if (fDocumentHandler != null && !isEmpty) {
1407            fDocumentHandler.endElement(element, null);
1408        }
1409
1410        return fMarkupDepth;
1411
1412    } // callEndElement(QName,boolean):int
1413

1414    // helper methods
1415

1416    /**
1417     * Sets the scanner state.
1418     *
1419     * @param state The new scanner state.
1420     */

1421    protected final void setScannerState(int state) {
1422
1423        fScannerState = state;
1424        if (DEBUG_SCANNER_STATE) {
1425            System.out.print("### setScannerState: ");
1426            System.out.print(getScannerStateName(state));
1427            System.out.println();
1428        }
1429
1430    } // setScannerState(int)
1431

1432    /**
1433     * Sets the dispatcher.
1434     *
1435     * @param dispatcher The new dispatcher.
1436     */

1437    protected final void setDispatcher(Dispatcher dispatcher) {
1438        fDispatcher = dispatcher;
1439        if (DEBUG_DISPATCHER) {
1440            System.out.print("%%% setDispatcher: ");
1441            System.out.print(getDispatcherName(dispatcher));
1442            System.out.println();
1443        }
1444    }
1445
1446    //
1447
// Private methods
1448
//
1449

1450    /** Returns the scanner state name. */
1451    protected String JavaDoc getScannerStateName(int state) {
1452
1453        switch (state) {
1454            case SCANNER_STATE_DOCTYPE: return "SCANNER_STATE_DOCTYPE";
1455            case SCANNER_STATE_ROOT_ELEMENT: return "SCANNER_STATE_ROOT_ELEMENT";
1456            case SCANNER_STATE_START_OF_MARKUP: return "SCANNER_STATE_START_OF_MARKUP";
1457            case SCANNER_STATE_COMMENT: return "SCANNER_STATE_COMMENT";
1458            case SCANNER_STATE_PI: return "SCANNER_STATE_PI";
1459            case SCANNER_STATE_CONTENT: return "SCANNER_STATE_CONTENT";
1460            case SCANNER_STATE_REFERENCE: return "SCANNER_STATE_REFERENCE";
1461            case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT";
1462            case SCANNER_STATE_TERMINATED: return "SCANNER_STATE_TERMINATED";
1463            case SCANNER_STATE_CDATA: return "SCANNER_STATE_CDATA";
1464            case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL";
1465        }
1466
1467        return "??? ("+state+')';
1468
1469    } // getScannerStateName(int):String
1470

1471    /** Returns the dispatcher name. */
1472    public String JavaDoc getDispatcherName(Dispatcher dispatcher) {
1473
1474        if (DEBUG_DISPATCHER) {
1475            if (dispatcher != null) {
1476                String JavaDoc name = dispatcher.getClass().getName();
1477                int index = name.lastIndexOf('.');
1478                if (index != -1) {
1479                    name = name.substring(index + 1);
1480                    index = name.lastIndexOf('$');
1481                    if (index != -1) {
1482                        name = name.substring(index + 1);
1483                    }
1484                }
1485                return name;
1486            }
1487        }
1488        return "null";
1489
1490    } // getDispatcherName():String
1491

1492    //
1493
// Classes
1494
//
1495

1496    /**
1497     * Element stack. This stack operates without synchronization, error
1498     * checking, and it re-uses objects instead of throwing popped items
1499     * away.
1500     *
1501     * @author Andy Clark, IBM
1502     */

1503    protected static class ElementStack {
1504
1505        //
1506
// Data
1507
//
1508

1509        /** The stack data. */
1510        protected QName[] fElements;
1511
1512        /** The size of the stack. */
1513        protected int fSize;
1514
1515        //
1516
// Constructors
1517
//
1518

1519        /** Default constructor. */
1520        public ElementStack() {
1521            fElements = new QName[10];
1522            for (int i = 0; i < fElements.length; i++) {
1523                fElements[i] = new QName();
1524            }
1525        } // <init>()
1526

1527        //
1528
// Public methods
1529
//
1530

1531        /**
1532         * Pushes an element on the stack.
1533         * <p>
1534         * <strong>Note:</strong> The QName values are copied into the
1535         * stack. In other words, the caller does <em>not</em> orphan
1536         * the element to the stack. Also, the QName object returned
1537         * is <em>not</em> orphaned to the caller. It should be
1538         * considered read-only.
1539         *
1540         * @param element The element to push onto the stack.
1541         *
1542         * @return Returns the actual QName object that stores the
1543         */

1544        public QName pushElement(QName element) {
1545            if (fSize == fElements.length) {
1546                QName[] array = new QName[fElements.length * 2];
1547                System.arraycopy(fElements, 0, array, 0, fSize);
1548                fElements = array;
1549                for (int i = fSize; i < fElements.length; i++) {
1550                    fElements[i] = new QName();
1551                }
1552            }
1553            fElements[fSize].setValues(element);
1554            return fElements[fSize++];
1555        } // pushElement(QName):QName
1556

1557        /**
1558         * Pops an element off of the stack by setting the values of
1559         * the specified QName.
1560         * <p>
1561         * <strong>Note:</strong> The object returned is <em>not</em>
1562         * orphaned to the caller. Therefore, the caller should consider
1563         * the object to be read-only.
1564         */

1565        public void popElement(QName element) {
1566            element.setValues(fElements[--fSize]);
1567        } // popElement(QName)
1568

1569        /** Clears the stack without throwing away existing QName objects. */
1570        public void clear() {
1571            fSize = 0;
1572        } // clear()
1573

1574    } // class ElementStack
1575

1576    /**
1577     * This interface defines an XML "event" dispatching model. Classes
1578     * that implement this interface are responsible for scanning parts
1579     * of the XML document and dispatching callbacks.
1580     *
1581     * @author Glenn Marcy, IBM
1582     */

1583    protected interface Dispatcher {
1584
1585        //
1586
// Dispatcher methods
1587
//
1588

1589        /**
1590         * Dispatch an XML "event".
1591         *
1592         * @param complete True if this dispatcher is intended to scan
1593         * and dispatch as much as possible.
1594         *
1595         * @return True if there is more to dispatch either from this
1596         * or a another dispatcher.
1597         *
1598         * @throws IOException Thrown on i/o error.
1599         * @throws XNIException Thrown on parse error.
1600         */

1601        public boolean dispatch(boolean complete)
1602            throws IOException JavaDoc, XNIException;
1603
1604    } // interface Dispatcher
1605

1606    /**
1607     * Dispatcher to handle content scanning.
1608     *
1609     * @author Andy Clark, IBM
1610     * @author Eric Ye, IBM
1611     */

1612    protected class FragmentContentDispatcher
1613        implements Dispatcher {
1614
1615        //
1616
// Dispatcher methods
1617
//
1618

1619        /**
1620         * Dispatch an XML "event".
1621         *
1622         * @param complete True if this dispatcher is intended to scan
1623         * and dispatch as much as possible.
1624         *
1625         * @return True if there is more to dispatch either from this
1626         * or a another dispatcher.
1627         *
1628         * @throws IOException Thrown on i/o error.
1629         * @throws XNIException Thrown on parse error.
1630         */

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

1828        //
1829
// Protected methods
1830
//
1831

1832        // hooks
1833

1834        // NOTE: These hook methods are added so that the full document
1835
// scanner can share the majority of code with this class.
1836

1837        /**
1838         * Scan for DOCTYPE hook. This method is a hook for subclasses
1839         * to add code to handle scanning for a the "DOCTYPE" string
1840         * after the string "<!" has been scanned.
1841         *
1842         * @return True if the "DOCTYPE" was scanned; false if "DOCTYPE"
1843         * was not scanned.
1844         */

1845        protected boolean scanForDoctypeHook()
1846            throws IOException JavaDoc, XNIException {
1847            return false;
1848        } // scanForDoctypeHook():boolean
1849

1850        /**
1851         * Element depth iz zero. This methos is a hook for subclasses
1852         * to add code to handle when the element depth hits zero. When
1853         * scanning a document fragment, an element depth of zero is
1854         * normal. However, when scanning a full XML document, the
1855         * scanner must handle the trailing miscellanous section of
1856         * the document after the end of the document's root element.
1857         *
1858         * @return True if the caller should stop and return true which
1859         * allows the scanner to switch to a new scanning
1860         * dispatcher. A return value of false indicates that
1861         * the content dispatcher should continue as normal.
1862         */

1863        protected boolean elementDepthIsZeroHook()
1864            throws IOException JavaDoc, XNIException {
1865            return false;
1866        } // elementDepthIsZeroHook():boolean
1867

1868        /**
1869         * Scan for root element hook. This method is a hook for
1870         * subclasses to add code that handles scanning for the root
1871         * element. When scanning a document fragment, there is no
1872         * "root" element. However, when scanning a full XML document,
1873         * the scanner must handle the root element specially.
1874         *
1875         * @return True if the caller should stop and return true which
1876         * allows the scanner to switch to a new scanning
1877         * dispatcher. A return value of false indicates that
1878         * the content dispatcher should continue as normal.
1879         */

1880        protected boolean scanRootElementHook()
1881            throws IOException JavaDoc, XNIException {
1882            return false;
1883        } // scanRootElementHook():boolean
1884

1885        /**
1886         * End of file hook. This method is a hook for subclasses to
1887         * add code that handles the end of file. The end of file in
1888         * a document fragment is OK if the markup depth is zero.
1889         * However, when scanning a full XML document, an end of file
1890         * is always premature.
1891         */

1892        protected void endOfFileHook(EOFException JavaDoc e)
1893            throws IOException JavaDoc, XNIException {
1894
1895            // NOTE: An end of file is only only an error if we were
1896
// in the middle of scanning some markup. -Ac
1897
if (fMarkupDepth != 0) {
1898                reportFatalError("PrematureEOF", null);
1899            }
1900
1901        } // endOfFileHook()
1902

1903    } // class FragmentContentDispatcher
1904

1905} // class XMLDocumentFragmentScannerImpl
1906
Popular Tags