1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xerces.internal.impl.dtd;
23
24import com.sun.org.apache.xerces.internal.impl.Constants;
25import com.sun.org.apache.xerces.internal.impl.RevalidationHandler;
26import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
27import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
28import com.sun.org.apache.xerces.internal.impl.dtd.models.ContentModelValidator;
29import com.sun.org.apache.xerces.internal.impl.dv.DTDDVFactory;
30import com.sun.org.apache.xerces.internal.impl.dv.DatatypeValidator;
31import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
32import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
33import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
34import com.sun.org.apache.xerces.internal.impl.validation.ValidationState;
35import com.sun.org.apache.xerces.internal.util.SymbolTable;
36import com.sun.org.apache.xerces.internal.util.XMLChar;
37import com.sun.org.apache.xerces.internal.util.XMLSymbols;
38import com.sun.org.apache.xerces.internal.xni.Augmentations;
39import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
40import com.sun.org.apache.xerces.internal.xni.QName;
41import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
42import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
43import com.sun.org.apache.xerces.internal.xni.XMLLocator;
44import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
45import com.sun.org.apache.xerces.internal.xni.XMLString;
46import com.sun.org.apache.xerces.internal.xni.XNIException;
47import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
48import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
49import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
50import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
51import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
52import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
53import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
54import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
55import java.util.Iterator;
56
57/**
58 * The DTD validator. The validator implements a document
59 * filter: receiving document events from the scanner; validating
60 * the content and structure; augmenting the InfoSet, if applicable;
61 * and notifying the parser of the information resulting from the
62 * validation process.
63 * <p> Formerly, this component also handled DTD events and grammar construction.
64 * To facilitate the development of a meaningful DTD grammar caching/preparsing
65 * framework, this functionality has been moved into the XMLDTDLoader
66 * class.  Therefore, this class no longer implements the DTDFilter
67 * or DTDContentModelFilter interfaces.
68 * <p>
69 * This component requires the following features and properties from the
70 * component manager that uses it:
71 * <ul>
72 *  <li>http://xml.org/sax/features/namespaces</li>
73 *  <li>http://xml.org/sax/features/validation</li>
74 *  <li>http://apache.org/xml/features/validation/dynamic</li>
75 *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
76 *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
77 *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
78 *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
79 * </ul>
80 *
81 * @xerces.internal
82 *
83 * @author Eric Ye, IBM
84 * @author Andy Clark, IBM
85 * @author Jeffrey Rodriguez IBM
86 * @author Neil Graham, IBM
87 *
88 */
89public class XMLDTDValidator
90        implements XMLComponent, XMLDocumentFilter, XMLDTDValidatorFilter, RevalidationHandler {
91
92    //
93    // Constants
94    //
95
96    /** Symbol: "&lt;&lt;datatypes>>". */
97
98    /** Top level scope (-1). */
99    private static final int TOP_LEVEL_SCOPE = -1;
100
101    // feature identifiers
102
103    /** Feature identifier: namespaces. */
104    protected static final String NAMESPACES =
105        Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
106
107    /** Feature identifier: validation. */
108    protected static final String VALIDATION =
109        Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
110
111    /** Feature identifier: dynamic validation. */
112    protected static final String DYNAMIC_VALIDATION =
113        Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;
114
115    /** Feature identifier: balance syntax trees. */
116    protected static final String BALANCE_SYNTAX_TREES =
117        Constants.XERCES_FEATURE_PREFIX + Constants.BALANCE_SYNTAX_TREES;
118
119    /** Feature identifier: warn on duplicate attdef */
120    protected static final String WARN_ON_DUPLICATE_ATTDEF =
121        Constants.XERCES_FEATURE_PREFIX + Constants.WARN_ON_DUPLICATE_ATTDEF_FEATURE;
122
123        protected static final String PARSER_SETTINGS =
124                Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
125
126
127
128    // property identifiers
129
130    /** Property identifier: symbol table. */
131    protected static final String SYMBOL_TABLE =
132        Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
133
134    /** Property identifier: error reporter. */
135    protected static final String ERROR_REPORTER =
136        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
137
138    /** Property identifier: grammar pool. */
139    protected static final String GRAMMAR_POOL =
140        Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
141
142    /** Property identifier: datatype validator factory. */
143    protected static final String DATATYPE_VALIDATOR_FACTORY =
144        Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY;
145
146    // property identifier:  ValidationManager
147    protected static final String VALIDATION_MANAGER =
148        Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
149
150    // recognized features and properties
151
152    /** Recognized features. */
153    private static final String[] RECOGNIZED_FEATURES = {
154        NAMESPACES,
155        VALIDATION,
156        DYNAMIC_VALIDATION,
157        BALANCE_SYNTAX_TREES
158    };
159
160    /** Feature defaults. */
161    private static final Boolean[] FEATURE_DEFAULTS = {
162        null,
163        null,
164        Boolean.FALSE,
165        Boolean.FALSE,
166    };
167
168    /** Recognized properties. */
169    private static final String[] RECOGNIZED_PROPERTIES = {
170        SYMBOL_TABLE,
171        ERROR_REPORTER,
172        GRAMMAR_POOL,
173        DATATYPE_VALIDATOR_FACTORY,
174        VALIDATION_MANAGER
175    };
176
177    /** Property defaults. */
178    private static final Object[] PROPERTY_DEFAULTS = {
179        null,
180        null,
181        null,
182        null,
183        null,
184    };
185
186    // debugging
187
188    /** Compile to true to debug attributes. */
189    private static final boolean DEBUG_ATTRIBUTES = false;
190
191    /** Compile to true to debug element children. */
192    private static final boolean DEBUG_ELEMENT_CHILDREN = false;
193
194    //
195    // Data
196    //
197
198    // updated during reset
199    protected ValidationManager fValidationManager = null;
200
201    // validation state
202    protected final ValidationState fValidationState = new ValidationState();
203
204    // features
205
206    /** Namespaces. */
207    protected boolean fNamespaces;
208
209    /** Validation. */
210    protected boolean fValidation;
211
212    /** Validation against only DTD */
213    protected boolean fDTDValidation;
214
215    /**
216     * Dynamic validation. This state of this feature is only useful when
217     * the validation feature is set to <code>true</code>.
218     */
219    protected boolean fDynamicValidation;
220
221    /** Controls whether the DTD grammar produces balanced syntax trees. */
222    protected boolean fBalanceSyntaxTrees;
223
224    /** warn on duplicate attribute definition, this feature works only when validation is true */
225    protected boolean fWarnDuplicateAttdef;
226
227    // properties
228
229    /** Symbol table. */
230    protected SymbolTable fSymbolTable;
231
232    /** Error reporter. */
233    protected XMLErrorReporter fErrorReporter;
234
235    // the grammar pool
236    protected XMLGrammarPool fGrammarPool;
237
238    /** Grammar bucket. */
239    protected DTDGrammarBucket fGrammarBucket;
240
241    /* location of the document as passed in from startDocument call */
242    protected XMLLocator fDocLocation;
243
244    /** Namespace support. */
245    protected NamespaceContext fNamespaceContext = null;
246
247    /** Datatype validator factory. */
248    protected DTDDVFactory fDatatypeValidatorFactory;
249
250    // handlers
251
252    /** Document handler. */
253    protected XMLDocumentHandler fDocumentHandler;
254
255    protected XMLDocumentSource fDocumentSource;
256    // grammars
257
258    /** DTD Grammar. */
259    protected DTDGrammar fDTDGrammar;
260
261    // state
262
263    /** True if seen DOCTYPE declaration. */
264    protected boolean fSeenDoctypeDecl = false;
265
266    /** Perform validation. */
267    private boolean fPerformValidation;
268
269    /** Schema type: None, DTD, Schema */
270    private String fSchemaType;
271
272    // information regarding the current element
273
274    /** Current element name. */
275    private final QName fCurrentElement = new QName();
276
277    /** Current element index. */
278    private int fCurrentElementIndex = -1;
279
280    /** Current content spec type. */
281    private int fCurrentContentSpecType = -1;
282
283    /** The root element name. */
284    private final QName fRootElement = new QName();
285
286    private boolean fInCDATASection = false;
287    // element stack
288
289    /** Element index stack. */
290    private int[] fElementIndexStack = new int[8];
291
292    /** Content spec type stack. */
293    private int[] fContentSpecTypeStack = new int[8];
294
295    /** Element name stack. */
296    private QName[] fElementQNamePartsStack = new QName[8];
297
298    // children list and offset stack
299
300    /**
301     * Element children. This data structure is a growing stack that
302     * holds the children of elements from the root to the current
303     * element depth. This structure never gets "deeper" than the
304     * deepest element. Space is re-used once each element is closed.
305     * <p>
306     * <strong>Note:</strong> This is much more efficient use of memory
307     * than creating new arrays for each element depth.
308     * <p>
309     * <strong>Note:</strong> The use of this data structure is for
310     * validation "on the way out". If the validation model changes to
311     * "on the way in", then this data structure is not needed.
312     */
313    private QName[] fElementChildren = new QName[32];
314
315    /** Element children count. */
316    private int fElementChildrenLength = 0;
317
318    /**
319     * Element children offset stack. This stack refers to offsets
320     * into the <code>fElementChildren</code> array.
321     * @see #fElementChildren
322     */
323    private int[] fElementChildrenOffsetStack = new int[32];
324
325    /** Element depth. */
326    private int fElementDepth = -1;
327
328    // validation states
329
330    /** True if seen the root element. */
331    private boolean fSeenRootElement = false;
332
333    /** True if inside of element content. */
334    private boolean fInElementContent = false;
335
336    // temporary variables
337
338    /** Temporary element declaration. */
339    private final XMLElementDecl fTempElementDecl = new XMLElementDecl();
340
341    /** Temporary atribute declaration. */
342    private final XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl();
343
344    /** Temporary entity declaration. */
345    private final XMLEntityDecl fEntityDecl = new XMLEntityDecl();
346
347    /** Temporary qualified name. */
348    private final QName fTempQName = new QName();
349
350    /** Temporary string buffers. */
351    private final StringBuffer fBuffer = new StringBuffer();
352
353    // symbols: general
354
355    // attribute validators
356
357    /** Datatype validator: ID. */
358    protected DatatypeValidator fValID;
359
360    /** Datatype validator: IDREF. */
361    protected DatatypeValidator fValIDRef;
362
363    /** Datatype validator: IDREFS. */
364    protected DatatypeValidator fValIDRefs;
365
366    /** Datatype validator: ENTITY. */
367    protected DatatypeValidator fValENTITY;
368
369    /** Datatype validator: ENTITIES. */
370    protected DatatypeValidator fValENTITIES;
371
372    /** Datatype validator: NMTOKEN. */
373    protected DatatypeValidator fValNMTOKEN;
374
375    /** Datatype validator: NMTOKENS. */
376    protected DatatypeValidator fValNMTOKENS;
377
378    /** Datatype validator: NOTATION. */
379    protected DatatypeValidator fValNOTATION;
380
381    // to check for duplicate ID or ANNOTATION attribute declare in
382    // ATTLIST, and misc VCs
383
384    //
385    // Constructors
386    //
387
388    /** Default constructor. */
389    public XMLDTDValidator() {
390
391        // initialize data
392        for (int i = 0; i < fElementQNamePartsStack.length; i++) {
393            fElementQNamePartsStack[i] = new QName();
394        }
395        fGrammarBucket = new DTDGrammarBucket();
396
397    } // <init>()
398
399    DTDGrammarBucket getGrammarBucket() {
400        return fGrammarBucket;
401    } // getGrammarBucket():  DTDGrammarBucket
402
403    //
404    // XMLComponent methods
405    //
406
407    /*
408     * Resets the component. The component can query the component manager
409     * about any features and properties that affect the operation of the
410     * component.
411     *
412     * @param componentManager The component manager.
413     *
414     * @throws SAXException Thrown by component on finitialization error.
415     *                      For example, if a feature or property is
416     *                      required for the operation of the component, the
417     *                      component manager may throw a
418     *                      SAXNotRecognizedException or a
419     *                      SAXNotSupportedException.
420     */
421    public void reset(XMLComponentManager componentManager)
422    throws XMLConfigurationException {
423
424        // clear grammars
425        fDTDGrammar = null;
426        fSeenDoctypeDecl = false;
427        fInCDATASection = false;
428        // initialize state
429        fSeenRootElement = false;
430        fInElementContent = false;
431        fCurrentElementIndex = -1;
432        fCurrentContentSpecType = -1;
433
434        fRootElement.clear();
435
436                fValidationState.resetIDTables();
437
438                fGrammarBucket.clear();
439                fElementDepth = -1;
440                fElementChildrenLength = 0;
441
442        boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
443
444        if (!parser_settings){
445                // parser settings have not been changed
446                        fValidationManager.addValidationState(fValidationState);
447                return;
448        }
449
450        // sax features
451        fNamespaces = componentManager.getFeature(NAMESPACES, true);
452        fValidation = componentManager.getFeature(VALIDATION, false);
453        fDTDValidation = !(componentManager.getFeature(Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE, false));
454
455        // Xerces features
456        fDynamicValidation = componentManager.getFeature(DYNAMIC_VALIDATION, false);
457        fBalanceSyntaxTrees = componentManager.getFeature(BALANCE_SYNTAX_TREES, false);
458        fWarnDuplicateAttdef = componentManager.getFeature(WARN_ON_DUPLICATE_ATTDEF, false);
459
460        fSchemaType = (String)componentManager.getProperty (Constants.JAXP_PROPERTY_PREFIX
461            + Constants.SCHEMA_LANGUAGE, null);
462
463        fValidationManager= (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER);
464        fValidationManager.addValidationState(fValidationState);
465        fValidationState.setUsingNamespaces(fNamespaces);
466
467        // get needed components
468        fErrorReporter = (XMLErrorReporter)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.ERROR_REPORTER_PROPERTY);
469        fSymbolTable = (SymbolTable)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.SYMBOL_TABLE_PROPERTY);
470        fGrammarPool= (XMLGrammarPool)componentManager.getProperty(GRAMMAR_POOL, null);
471
472        fDatatypeValidatorFactory = (DTDDVFactory)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY);
473                init();
474
475    } // reset(XMLComponentManager)
476
477    /**
478     * Returns a list of feature identifiers that are recognized by
479     * this component. This method may return null if no features
480     * are recognized by this component.
481     */
482    public String[] getRecognizedFeatures() {
483        return (String[])(RECOGNIZED_FEATURES.clone());
484    } // getRecognizedFeatures():String[]
485
486    /**
487     * Sets the state of a feature. This method is called by the component
488     * manager any time after reset when a feature changes state.
489     * <p>
490     * <strong>Note:</strong> Components should silently ignore features
491     * that do not affect the operation of the component.
492     *
493     * @param featureId The feature identifier.
494     * @param state     The state of the feature.
495     *
496     * @throws SAXNotRecognizedException The component should not throw
497     *                                   this exception.
498     * @throws SAXNotSupportedException The component should not throw
499     *                                  this exception.
500     */
501    public void setFeature(String featureId, boolean state)
502    throws XMLConfigurationException {
503    } // setFeature(String,boolean)
504
505    /**
506     * Returns a list of property identifiers that are recognized by
507     * this component. This method may return null if no properties
508     * are recognized by this component.
509     */
510    public String[] getRecognizedProperties() {
511        return (String[])(RECOGNIZED_PROPERTIES.clone());
512    } // getRecognizedProperties():String[]
513
514    /**
515     * Sets the value of a property. This method is called by the component
516     * manager any time after reset when a property changes value.
517     * <p>
518     * <strong>Note:</strong> Components should silently ignore properties
519     * that do not affect the operation of the component.
520     *
521     * @param propertyId The property identifier.
522     * @param value      The value of the property.
523     *
524     * @throws SAXNotRecognizedException The component should not throw
525     *                                   this exception.
526     * @throws SAXNotSupportedException The component should not throw
527     *                                  this exception.
528     */
529    public void setProperty(String propertyId, Object value)
530    throws XMLConfigurationException {
531    } // setProperty(String,Object)
532
533    /**
534     * Returns the default state for a feature, or null if this
535     * component does not want to report a default value for this
536     * feature.
537     *
538     * @param featureId The feature identifier.
539     *
540     * @since Xerces 2.2.0
541     */
542    public Boolean getFeatureDefault(String featureId) {
543        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
544            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
545                return FEATURE_DEFAULTS[i];
546            }
547        }
548        return null;
549    } // getFeatureDefault(String):Boolean
550
551    /**
552     * Returns the default state for a property, or null if this
553     * component does not want to report a default value for this
554     * property.
555     *
556     * @param propertyId The property identifier.
557     *
558     * @since Xerces 2.2.0
559     */
560    public Object getPropertyDefault(String propertyId) {
561        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
562            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
563                return PROPERTY_DEFAULTS[i];
564            }
565        }
566        return null;
567    } // getPropertyDefault(String):Object
568
569    //
570    // XMLDocumentSource methods
571    //
572
573    /** Sets the document handler to receive information about the document. */
574    public void setDocumentHandler(XMLDocumentHandler documentHandler) {
575        fDocumentHandler = documentHandler;
576    } // setDocumentHandler(XMLDocumentHandler)
577
578    /** Returns the document handler */
579    public XMLDocumentHandler getDocumentHandler() {
580        return fDocumentHandler;
581    } // getDocumentHandler():  XMLDocumentHandler
582
583
584    //
585    // XMLDocumentHandler methods
586    //
587
588    /** Sets the document source */
589    public void setDocumentSource(XMLDocumentSource source){
590        fDocumentSource = source;
591    } // setDocumentSource
592
593    /** Returns the document source */
594    public XMLDocumentSource getDocumentSource (){
595        return fDocumentSource;
596    } // getDocumentSource
597
598    /**
599     * The start of the document.
600     *
601     * @param locator The system identifier of the entity if the entity
602     *                 is external, null otherwise.
603     * @param encoding The auto-detected IANA encoding name of the entity
604     *                 stream. This value will be null in those situations
605     *                 where the entity encoding is not auto-detected (e.g.
606     *                 internal entities or a document entity that is
607     *                 parsed from a java.io.Reader).
608     * @param namespaceContext
609     *                 The namespace context in effect at the
610     *                 start of this document.
611     *                 This object represents the current context.
612     *                 Implementors of this class are responsible
613     *                 for copying the namespace bindings from the
614     *                 the current context (and its parent contexts)
615     *                 if that information is important.
616     * @param augs   Additional information that may include infoset augmentations
617     *
618     * @throws XNIException Thrown by handler to signal an error.
619     */
620    public void startDocument(XMLLocator locator, String encoding,
621                              NamespaceContext namespaceContext, Augmentations augs)
622    throws XNIException {
623
624        // call handlers
625        // get initial grammars
626        if (fGrammarPool != null) {
627            Grammar [] grammars = fGrammarPool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_DTD);
628            final int length = (grammars != null) ? grammars.length : 0;
629            for (int i = 0; i < length; ++i) {
630                fGrammarBucket.putGrammar((DTDGrammar)grammars[i]);
631            }
632        }
633        fDocLocation = locator;
634        fNamespaceContext = namespaceContext;
635
636        if (fDocumentHandler != null) {
637            fDocumentHandler.startDocument(locator, encoding, namespaceContext, augs);
638        }
639
640    } // startDocument(XMLLocator,String)
641
642    /**
643     * Notifies of the presence of an XMLDecl line in the document. If
644     * present, this method will be called immediately following the
645     * startDocument call.
646     *
647     * @param version    The XML version.
648     * @param encoding   The IANA encoding name of the document, or null if
649     *                   not specified.
650     * @param standalone The standalone value, or null if not specified.
651     * @param augs   Additional information that may include infoset augmentations
652     *
653     * @throws XNIException Thrown by handler to signal an error.
654     */
655    public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
656    throws XNIException {
657
658        // save standalone state
659        fGrammarBucket.setStandalone(standalone != null && standalone.equals("yes"));
660
661        // call handlers
662        if (fDocumentHandler != null) {
663            fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
664        }
665
666    } // xmlDecl(String,String,String)
667
668    /**
669     * Notifies of the presence of the DOCTYPE line in the document.
670     *
671     * @param rootElement The name of the root element.
672     * @param publicId    The public identifier if an external DTD or null
673     *                    if the external DTD is specified using SYSTEM.
674     * @param systemId    The system identifier if an external DTD, null
675     *                    otherwise.
676     * @param augs   Additional information that may include infoset augmentations
677     *
678     * @throws XNIException Thrown by handler to signal an error.
679     */
680    public void doctypeDecl(String rootElement, String publicId, String systemId,
681                            Augmentations augs)
682    throws XNIException {
683
684        // save root element state
685        fSeenDoctypeDecl = true;
686        fRootElement.setValues(null, rootElement, rootElement, null);
687        // find or create grammar:
688        String eid = null;
689        try {
690            eid = XMLEntityManager.expandSystemId(systemId, fDocLocation.getExpandedSystemId(), false);
691        } catch (java.io.IOException e) {
692        }
693        XMLDTDDescription grammarDesc = new XMLDTDDescription(publicId, systemId, fDocLocation.getExpandedSystemId(), eid, rootElement);
694        fDTDGrammar = fGrammarBucket.getGrammar(grammarDesc);
695        if(fDTDGrammar == null) {
696            // give grammar pool a chance...
697            //
698            // Do not bother checking the pool if no public or system identifier was provided.
699            // Since so many different DTDs have roots in common, using only a root name as the
700            // key may cause an unexpected grammar to be retrieved from the grammar pool. This scenario
701            // would occur when an ExternalSubsetResolver has been queried and the
702            // XMLInputSource returned contains an input stream but no external identifier.
703            // This can never happen when the instance document specified a DOCTYPE. -- mrglavas
704            if (fGrammarPool != null && (systemId != null || publicId != null)) {
705                fDTDGrammar = (DTDGrammar)fGrammarPool.retrieveGrammar(grammarDesc);
706            }
707        }
708        if(fDTDGrammar == null) {
709            // we'll have to create it...
710            if (!fBalanceSyntaxTrees) {
711                fDTDGrammar = new DTDGrammar(fSymbolTable, grammarDesc);
712            }
713            else {
714                fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, grammarDesc);
715            }
716        } else {
717            // we've found a cached one;so let's make sure not to read
718            // any external subset!
719            fValidationManager.setCachedDTD(true);
720        }
721        fGrammarBucket.setActiveGrammar(fDTDGrammar);
722
723        // call handlers
724        if (fDocumentHandler != null) {
725            fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
726        }
727
728    } // doctypeDecl(String,String,String, Augmentations)
729
730
731    /**
732     * The start of an element.
733     *
734     * @param element    The name of the element.
735     * @param attributes The element attributes.
736     * @param augs   Additional information that may include infoset augmentations
737     *
738     * @throws XNIException Thrown by handler to signal an error.
739     */
740    public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
741    throws XNIException {
742
743        handleStartElement(element, attributes, augs);
744        // call handlers
745        if (fDocumentHandler != null) {
746            fDocumentHandler.startElement(element, attributes, augs);
747
748        }
749
750    } // startElement(QName,XMLAttributes)
751
752    /**
753     * An empty element.
754     *
755     * @param element    The name of the element.
756     * @param attributes The element attributes.
757     * @param augs   Additional information that may include infoset augmentations
758     *
759     * @throws XNIException Thrown by handler to signal an error.
760     */
761    public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
762    throws XNIException {
763
764        boolean removed = handleStartElement(element, attributes, augs);
765
766        if (fDocumentHandler !=null) {
767            fDocumentHandler.emptyElement(element, attributes, augs);
768        }
769        if (!removed) {
770            handleEndElement(element, augs, true);
771        }
772
773
774    } // emptyElement(QName,XMLAttributes)
775
776    /**
777     * Character content.
778     *
779     * @param text The content.
780     *
781     * @param augs   Additional information that may include infoset augmentations
782     *
783     * @throws XNIException Thrown by handler to signal an error.
784     */
785    public void characters(XMLString text, Augmentations augs) throws XNIException {
786
787        boolean callNextCharacters = true;
788
789        // REVISIT: [Q] Is there a more efficient way of doing this?
790        //          Perhaps if the scanner told us so we don't have to
791        //          look at the characters again. -Ac
792        boolean allWhiteSpace = true;
793        for (int i=text.offset; i< text.offset+text.length; i++) {
794            if (!isSpace(text.ch[i])) {
795                allWhiteSpace = false;
796                break;
797            }
798        }
799        // call the ignoreableWhiteSpace callback
800        // never call ignorableWhitespace if we are in cdata section
801        if (fInElementContent && allWhiteSpace && !fInCDATASection) {
802            if (fDocumentHandler != null) {
803                fDocumentHandler.ignorableWhitespace(text, augs);
804                callNextCharacters = false;
805            }
806        }
807
808        // validate
809        if (fPerformValidation) {
810            if (fInElementContent) {
811                if (fGrammarBucket.getStandalone() &&
812                    fDTDGrammar.getElementDeclIsExternal(fCurrentElementIndex)) {
813                    if (allWhiteSpace) {
814                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
815                                                    "MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE",
816                                                    null, XMLErrorReporter.SEVERITY_ERROR);
817                    }
818                }
819                if (!allWhiteSpace) {
820                    charDataInContent();
821                }
822
823                // For E15.2
824                if (augs != null && augs.getItem(Constants.CHAR_REF_PROBABLE_WS) == Boolean.TRUE) {
825                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
826                                               "MSG_CONTENT_INVALID_SPECIFIED",
827                                               new Object[]{ fCurrentElement.rawname,
828                                                   fDTDGrammar.getContentSpecAsString(fElementDepth),
829                                                   "character reference"},
830                                               XMLErrorReporter.SEVERITY_ERROR);
831                }
832            }
833
834            if (fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
835                charDataInContent();
836            }
837        }
838
839        // call handlers
840        if (callNextCharacters && fDocumentHandler != null) {
841            fDocumentHandler.characters(text, augs);
842        }
843
844    } // characters(XMLString)
845
846
847
848    /**
849     * Ignorable whitespace. For this method to be called, the document
850     * source must have some way of determining that the text containing
851     * only whitespace characters should be considered ignorable. For
852     * example, the validator can determine if a length of whitespace
853     * characters in the document are ignorable based on the element
854     * content model.
855     *
856     * @param text The ignorable whitespace.
857     * @param augs   Additional information that may include infoset augmentations
858     *
859     * @throws XNIException Thrown by handler to signal an error.
860     */
861    public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
862
863        // call handlers
864        if (fDocumentHandler != null) {
865            fDocumentHandler.ignorableWhitespace(text, augs);
866        }
867
868    } // ignorableWhitespace(XMLString)
869
870    /**
871     * The end of an element.
872     *
873     * @param element The name of the element.
874     * @param augs   Additional information that may include infoset augmentations
875     *
876     * @throws XNIException Thrown by handler to signal an error.
877     */
878    public void endElement(QName element, Augmentations augs) throws XNIException {
879
880        handleEndElement(element,  augs, false);
881
882    } // endElement(QName)
883
884    /**
885     * The start of a CDATA section.
886     * @param augs   Additional information that may include infoset augmentations
887     *
888     * @throws XNIException Thrown by handler to signal an error.
889     */
890    public void startCDATA(Augmentations augs) throws XNIException {
891
892        if (fPerformValidation && fInElementContent) {
893            charDataInContent();
894        }
895        fInCDATASection = true;
896        // call handlers
897        if (fDocumentHandler != null) {
898            fDocumentHandler.startCDATA(augs);
899        }
900
901    } // startCDATA()
902
903    /**
904     * The end of a CDATA section.
905     * @param augs   Additional information that may include infoset augmentations
906     *
907     * @throws XNIException Thrown by handler to signal an error.
908     */
909    public void endCDATA(Augmentations augs) throws XNIException {
910
911        fInCDATASection = false;
912        // call handlers
913        if (fDocumentHandler != null) {
914            fDocumentHandler.endCDATA(augs);
915        }
916
917    } // endCDATA()
918
919    /**
920     * The end of the document.
921     * @param augs   Additional information that may include infoset augmentations
922     *
923     * @throws XNIException Thrown by handler to signal an error.
924     */
925    public void endDocument(Augmentations augs) throws XNIException {
926
927        // call handlers
928        if (fDocumentHandler != null) {
929            fDocumentHandler.endDocument(augs);
930        }
931
932    } // endDocument()
933
934    /**
935     * A comment.
936     *
937     * @param text The text in the comment.
938     * @param augs   Additional information that may include infoset augmentations
939     *
940     * @throws XNIException Thrown by application to signal an error.
941     */
942    public void comment(XMLString text, Augmentations augs) throws XNIException {
943        // fixes E15.1
944        if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) {
945            fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
946            if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
947                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
948                                               "MSG_CONTENT_INVALID_SPECIFIED",
949                                               new Object[]{ fCurrentElement.rawname,
950                                                             "EMPTY",
951                                                             "comment"},
952                                               XMLErrorReporter.SEVERITY_ERROR);
953            }
954        }
955        // call handlers
956        if (fDocumentHandler != null) {
957            fDocumentHandler.comment(text, augs);
958        }
959
960    } // comment(XMLString)
961
962
963    /**
964     * A processing instruction. Processing instructions consist of a
965     * target name and, optionally, text data. The data is only meaningful
966     * to the application.
967     * <p>
968     * Typically, a processing instruction's data will contain a series
969     * of pseudo-attributes. These pseudo-attributes follow the form of
970     * element attributes but are <strong>not</strong> parsed or presented
971     * to the application as anything other than text. The application is
972     * responsible for parsing the data.
973     *
974     * @param target The target.
975     * @param data   The data or null if none specified.
976     * @param augs   Additional information that may include infoset augmentations
977     *
978     * @throws XNIException Thrown by handler to signal an error.
979     */
980    public void processingInstruction(String target, XMLString data, Augmentations augs)
981    throws XNIException {
982
983        // fixes E15.1
984        if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) {
985            fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
986            if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
987                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
988                                               "MSG_CONTENT_INVALID_SPECIFIED",
989                                               new Object[]{ fCurrentElement.rawname,
990                                                             "EMPTY",
991                                                             "processing instruction"},
992                                               XMLErrorReporter.SEVERITY_ERROR);
993            }
994        }
995        // call handlers
996        if (fDocumentHandler != null) {
997            fDocumentHandler.processingInstruction(target, data, augs);
998        }
999    } // processingInstruction(String,XMLString)
1000
1001    /**
1002     * This method notifies the start of a general entity.
1003     * <p>
1004     * <strong>Note:</strong> This method is not called for entity references
1005     * appearing as part of attribute values.
1006     *
1007     * @param name     The name of the general entity.
1008     * @param identifier The resource identifier.
1009     * @param encoding The auto-detected IANA encoding name of the entity
1010     *                 stream. This value will be null in those situations
1011     *                 where the entity encoding is not auto-detected (e.g.
1012     *                 internal entities or a document entity that is
1013     *                 parsed from a java.io.Reader).
1014     * @param augs     Additional information that may include infoset augmentations
1015     *
1016     * @exception XNIException Thrown by handler to signal an error.
1017     */
1018    public void startGeneralEntity(String name,
1019                                   XMLResourceIdentifier identifier,
1020                                   String encoding,
1021                                   Augmentations augs) throws XNIException {
1022        if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) {
1023            fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
1024            // fixes E15.1
1025            if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
1026                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1027                                           "MSG_CONTENT_INVALID_SPECIFIED",
1028                                           new Object[]{ fCurrentElement.rawname,
1029                                                         "EMPTY", "ENTITY"},
1030                                           XMLErrorReporter.SEVERITY_ERROR);
1031            }
1032            if (fGrammarBucket.getStandalone()) {
1033                XMLDTDLoader.checkStandaloneEntityRef(name, fDTDGrammar, fEntityDecl, fErrorReporter);
1034            }
1035        }
1036        if (fDocumentHandler != null) {
1037            fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
1038        }
1039    }
1040
1041    /**
1042     * This method notifies the end of a general entity.
1043     * <p>
1044     * <strong>Note:</strong> This method is not called for entity references
1045     * appearing as part of attribute values.
1046     *
1047     * @param name   The name of the entity.
1048     * @param augs   Additional information that may include infoset augmentations
1049     *
1050     * @exception XNIException
1051     *                   Thrown by handler to signal an error.
1052     */
1053    public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
1054        // call handlers
1055        if (fDocumentHandler != null) {
1056            fDocumentHandler.endGeneralEntity(name, augs);
1057        }
1058    } // endEntity(String)
1059
1060    /**
1061     * Notifies of the presence of a TextDecl line in an entity. If present,
1062     * this method will be called immediately following the startParameterEntity call.
1063     * <p>
1064     * <strong>Note:</strong> This method is only called for external
1065     * parameter entities referenced in the DTD.
1066     *
1067     * @param version  The XML version, or null if not specified.
1068     * @param encoding The IANA encoding name of the entity.
1069     * @param augs Additional information that may include infoset
1070     *                      augmentations.
1071     *
1072     * @throws XNIException Thrown by handler to signal an error.
1073     */
1074    public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
1075
1076        // call handlers
1077        if (fDocumentHandler != null) {
1078            fDocumentHandler.textDecl(version, encoding, augs);
1079        }
1080    }
1081
1082
1083    public final boolean hasGrammar(){
1084
1085        return (fDTDGrammar != null);
1086    }
1087
1088    public final boolean validate(){
1089        // Do validation if all of the following are true:
1090        // 1. The JAXP Schema Language property is not XML Schema
1091        //    REVISIT: since only DTD and Schema are supported at this time,
1092        //             such checking is sufficient. but if more schema types
1093        //             are introduced in the future, we'll need to change it
1094        //             to something like
1095        //             (fSchemaType == null || fSchemaType == NS_XML_DTD)
1096        // 2. One of the following is true (validation features)
1097        // 2.1 Dynamic validation is off, and validation is on
1098        // 2.2 Dynamic validation is on, and DOCTYPE was seen
1099        // 3 Xerces schema validation feature is off, or DOCTYPE was seen.
1100        return (fSchemaType != Constants.NS_XMLSCHEMA) &&
1101               (!fDynamicValidation && fValidation ||
1102                fDynamicValidation && fSeenDoctypeDecl) &&
1103               (fDTDValidation || fSeenDoctypeDecl);
1104    }
1105
1106            //REVISIT:we can convert into functions.. adding default attribute values.. and one validating.
1107
1108    /** Add default attributes and validate. */
1109    protected void addDTDDefaultAttrsAndValidate(QName elementName, int elementIndex,
1110                                               XMLAttributes attributes)
1111    throws XNIException {
1112
1113        // is there anything to do?
1114        if (elementIndex == -1 || fDTDGrammar == null) {
1115            return;
1116        }
1117
1118        //
1119        // Check after all specified attrs are scanned
1120        // (1) report error for REQUIRED attrs that are missing (V_TAGc)
1121        // (2) add default attrs (FIXED and NOT_FIXED)
1122        //
1123        int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
1124
1125        while (attlistIndex != -1) {
1126
1127            fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);
1128
1129            if (DEBUG_ATTRIBUTES) {
1130                if (fTempAttDecl != null) {
1131                    XMLElementDecl elementDecl = new XMLElementDecl();
1132                    fDTDGrammar.getElementDecl(elementIndex, elementDecl);
1133                    System.out.println("element: "+(elementDecl.name.localpart));
1134                    System.out.println("attlistIndex " + attlistIndex + "\n"+
1135                                       "attName : '"+(fTempAttDecl.name.localpart) + "'\n"
1136                                       + "attType : "+fTempAttDecl.simpleType.type + "\n"
1137                                       + "attDefaultType : "+fTempAttDecl.simpleType.defaultType + "\n"
1138                                       + "attDefaultValue : '"+fTempAttDecl.simpleType.defaultValue + "'\n"
1139                                       + attributes.getLength() +"\n"
1140                                      );
1141                }
1142            }
1143            String attPrefix = fTempAttDecl.name.prefix;
1144            String attLocalpart = fTempAttDecl.name.localpart;
1145            String attRawName = fTempAttDecl.name.rawname;
1146            String attType = getAttributeTypeName(fTempAttDecl);
1147            int attDefaultType =fTempAttDecl.simpleType.defaultType;
1148            String attValue = null;
1149
1150            if (fTempAttDecl.simpleType.defaultValue != null) {
1151                attValue = fTempAttDecl.simpleType.defaultValue;
1152            }
1153
1154            boolean specified = false;
1155            boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED;
1156            boolean cdata = attType == XMLSymbols.fCDATASymbol;
1157
1158            if (!cdata || required || attValue != null) {
1159                int attrCount = attributes.getLength();
1160                for (int i = 0; i < attrCount; i++) {
1161                    if (attributes.getQName(i) == attRawName) {
1162                        specified = true;
1163                        break;
1164                    }
1165                }
1166            }
1167
1168            if (!specified) {
1169                if (required) {
1170                    if (fPerformValidation) {
1171                        Object[] args = {elementName.localpart, attRawName};
1172                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1173                                                   "MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED", args,
1174                                                   XMLErrorReporter.SEVERITY_ERROR);
1175                    }
1176                }
1177                else if (attValue != null) {
1178                    if (fPerformValidation && fGrammarBucket.getStandalone()) {
1179                        if (fDTDGrammar.getAttributeDeclIsExternal(attlistIndex)) {
1180
1181                            Object[] args = { elementName.localpart, attRawName};
1182                            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1183                                                       "MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED", args,
1184                                                       XMLErrorReporter.SEVERITY_ERROR);
1185                        }
1186                    }
1187
1188                    // add namespace information
1189                    if (fNamespaces) {
1190                        int index = attRawName.indexOf(':');
1191                        if (index != -1) {
1192                            attPrefix = attRawName.substring(0, index);
1193                            attPrefix = fSymbolTable.addSymbol(attPrefix);
1194                            attLocalpart = attRawName.substring(index + 1);
1195                            attLocalpart = fSymbolTable.addSymbol(attLocalpart);
1196                        }
1197                    }
1198
1199                    // add attribute
1200                    fTempQName.setValues(attPrefix, attLocalpart, attRawName, fTempAttDecl.name.uri);
1201                    int newAttr = attributes.addAttribute(fTempQName, attType, attValue);
1202                }
1203            }
1204            // get next att decl in the Grammar for this element
1205            attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex);
1206        }
1207
1208        // now iterate through the expanded attributes for
1209        // 1. if every attribute seen is declared in the DTD
1210        // 2. check if the VC: default_fixed holds
1211        // 3. validate every attribute.
1212        int attrCount = attributes.getLength();
1213        for (int i = 0; i < attrCount; i++) {
1214            String attrRawName = attributes.getQName(i);
1215            boolean declared = false;
1216            if (fPerformValidation) {
1217                if (fGrammarBucket.getStandalone()) {
1218                    // check VC: Standalone Document Declaration, entities
1219                    // references appear in the document.
1220                    // REVISIT: this can be combined to a single check in
1221                    // startEntity if we add one more argument in
1222                    // startEntity, inAttrValue
1223                    String nonNormalizedValue = attributes.getNonNormalizedValue(i);
1224                    if (nonNormalizedValue != null) {
1225                        String entityName = getExternalEntityRefInAttrValue(nonNormalizedValue);
1226                        if (entityName != null) {
1227                            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1228                                                       "MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
1229                                                       new Object[]{entityName},
1230                                                       XMLErrorReporter.SEVERITY_ERROR);
1231                        }
1232                    }
1233                }
1234            }
1235            int attDefIndex = -1;
1236            int position =
1237            fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
1238            while (position != -1) {
1239                fDTDGrammar.getAttributeDecl(position, fTempAttDecl);
1240                if (fTempAttDecl.name.rawname == attrRawName) {
1241                    // found the match att decl,
1242                    attDefIndex = position;
1243                    declared = true;
1244                    break;
1245                }
1246                position = fDTDGrammar.getNextAttributeDeclIndex(position);
1247            }
1248            if (!declared) {
1249                if (fPerformValidation) {
1250                    // REVISIT - cache the elem/attr tuple so that we only
1251                    // give this error once for each unique occurrence
1252                    Object[] args = { elementName.rawname, attrRawName};
1253
1254                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1255                                               "MSG_ATTRIBUTE_NOT_DECLARED",
1256                                               args,XMLErrorReporter.SEVERITY_ERROR);
1257                }
1258                continue;
1259            }
1260            // attribute is declared
1261
1262            // fTempAttDecl should have the right value set now, so
1263            // the following is not needed
1264            // fGrammar.getAttributeDecl(attDefIndex,fTempAttDecl);
1265
1266            String type = getAttributeTypeName(fTempAttDecl);
1267            attributes.setType(i, type);
1268            attributes.getAugmentations(i).putItem(Constants.ATTRIBUTE_DECLARED, Boolean.TRUE);
1269
1270            boolean changedByNormalization = false;
1271            String oldValue = attributes.getValue(i);
1272            String attrValue = oldValue;
1273            if (attributes.isSpecified(i) && type != XMLSymbols.fCDATASymbol) {
1274                changedByNormalization = normalizeAttrValue(attributes, i);
1275                attrValue = attributes.getValue(i);
1276                if (fPerformValidation && fGrammarBucket.getStandalone()
1277                    && changedByNormalization
1278                    && fDTDGrammar.getAttributeDeclIsExternal(position)
1279                   ) {
1280                    // check VC: Standalone Document Declaration
1281                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1282                                               "MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE",
1283                                               new Object[]{attrRawName, oldValue, attrValue},
1284                                               XMLErrorReporter.SEVERITY_ERROR);
1285                }
1286            }
1287            if (!fPerformValidation) {
1288                continue;
1289            }
1290            if (fTempAttDecl.simpleType.defaultType ==
1291                XMLSimpleType.DEFAULT_TYPE_FIXED) {
1292                String defaultValue = fTempAttDecl.simpleType.defaultValue;
1293
1294                if (!attrValue.equals(defaultValue)) {
1295                    Object[] args = {elementName.localpart,
1296                        attrRawName,
1297                        attrValue,
1298                        defaultValue};
1299                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1300                                               "MSG_FIXED_ATTVALUE_INVALID",
1301                                               args, XMLErrorReporter.SEVERITY_ERROR);
1302                }
1303            }
1304
1305            if (fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENTITY ||
1306                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENUMERATION ||
1307                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ID ||
1308                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_IDREF ||
1309                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NMTOKEN ||
1310                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NOTATION
1311               ) {
1312                validateDTDattribute(elementName, attrValue, fTempAttDecl);
1313            }
1314        } // for all attributes
1315
1316    } // addDTDDefaultAttrsAndValidate(int,XMLAttrList)
1317
1318    /** Checks entities in attribute values for standalone VC. */
1319    protected String getExternalEntityRefInAttrValue(String nonNormalizedValue) {
1320        int valLength = nonNormalizedValue.length();
1321        int ampIndex = nonNormalizedValue.indexOf('&');
1322        while (ampIndex != -1) {
1323            if (ampIndex + 1 < valLength &&
1324                nonNormalizedValue.charAt(ampIndex+1) != '#') {
1325                int semicolonIndex = nonNormalizedValue.indexOf(';', ampIndex+1);
1326                String entityName = nonNormalizedValue.substring(ampIndex+1, semicolonIndex);
1327                entityName = fSymbolTable.addSymbol(entityName);
1328                int entIndex = fDTDGrammar.getEntityDeclIndex(entityName);
1329                if (entIndex > -1) {
1330                    fDTDGrammar.getEntityDecl(entIndex, fEntityDecl);
1331                    if (fEntityDecl.inExternal ||
1332                        (entityName = getExternalEntityRefInAttrValue(fEntityDecl.value)) != null) {
1333                        return entityName;
1334                    }
1335                }
1336            }
1337            ampIndex = nonNormalizedValue.indexOf('&', ampIndex+1);
1338        }
1339        return null;
1340    } // isExternalEntityRefInAttrValue(String):String
1341
1342    /**
1343     * Validate attributes in DTD fashion.
1344     */
1345    protected void validateDTDattribute(QName element, String attValue,
1346                                      XMLAttributeDecl attributeDecl)
1347    throws XNIException {
1348
1349        switch (attributeDecl.simpleType.type) {
1350        case XMLSimpleType.TYPE_ENTITY: {
1351                // NOTE: Save this information because invalidStandaloneAttDef
1352                boolean isAlistAttribute = attributeDecl.simpleType.list;
1353
1354                try {
1355                    if (isAlistAttribute) {
1356                        fValENTITIES.validate(attValue, fValidationState);
1357                    }
1358                    else {
1359                        fValENTITY.validate(attValue, fValidationState);
1360                    }
1361                }
1362                catch (InvalidDatatypeValueException ex) {
1363                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1364                                               ex.getKey(),
1365                                               ex.getArgs(),
1366                                               XMLErrorReporter.SEVERITY_ERROR );
1367
1368                }
1369                break;
1370            }
1371
1372        case XMLSimpleType.TYPE_NOTATION:
1373        case XMLSimpleType.TYPE_ENUMERATION: {
1374                boolean found = false;
1375                String [] enumVals = attributeDecl.simpleType.enumeration;
1376                if (enumVals == null) {
1377                    found = false;
1378                }
1379                else
1380                    for (int i = 0; i < enumVals.length; i++) {
1381                        if (attValue == enumVals[i] || attValue.equals(enumVals[i])) {
1382                            found = true;
1383                            break;
1384                        }
1385                    }
1386
1387                if (!found) {
1388                    StringBuffer enumValueString = new StringBuffer();
1389                    if (enumVals != null)
1390                        for (int i = 0; i < enumVals.length; i++) {
1391                            enumValueString.append(enumVals[i]+" ");
1392                        }
1393                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1394                                               "MSG_ATTRIBUTE_VALUE_NOT_IN_LIST",
1395                                               new Object[]{attributeDecl.name.rawname, attValue, enumValueString},
1396                                               XMLErrorReporter.SEVERITY_ERROR);
1397                }
1398                break;
1399            }
1400
1401        case XMLSimpleType.TYPE_ID: {
1402                try {
1403                    fValID.validate(attValue, fValidationState);
1404                }
1405                catch (InvalidDatatypeValueException ex) {
1406                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1407                                               ex.getKey(),
1408                                               ex.getArgs(),
1409                                               XMLErrorReporter.SEVERITY_ERROR );
1410                }
1411                break;
1412            }
1413
1414        case XMLSimpleType.TYPE_IDREF: {
1415                boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
1416
1417                try {
1418                    if (isAlistAttribute) {
1419                        fValIDRefs.validate(attValue, fValidationState);
1420                    }
1421                    else {
1422                        fValIDRef.validate(attValue, fValidationState);
1423                    }
1424                }
1425                catch (InvalidDatatypeValueException ex) {
1426                    if (isAlistAttribute) {
1427                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1428                                                   "IDREFSInvalid",
1429                                                   new Object[]{attValue},
1430                                                   XMLErrorReporter.SEVERITY_ERROR );
1431                    }
1432                    else {
1433                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1434                                                   ex.getKey(),
1435                                                   ex.getArgs(),
1436                                                   XMLErrorReporter.SEVERITY_ERROR );
1437                    }
1438
1439                }
1440                break;
1441            }
1442
1443        case XMLSimpleType.TYPE_NMTOKEN: {
1444                boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
1445                //changes fTempAttDef
1446                try {
1447                    if (isAlistAttribute) {
1448                        fValNMTOKENS.validate(attValue, fValidationState);
1449                    }
1450                    else {
1451                        fValNMTOKEN.validate(attValue, fValidationState);
1452                    }
1453                }
1454                catch (InvalidDatatypeValueException ex) {
1455                    if (isAlistAttribute) {
1456                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1457                                                   "NMTOKENSInvalid",
1458                                                   new Object[] { attValue},
1459                                                   XMLErrorReporter.SEVERITY_ERROR);
1460                    }
1461                    else {
1462                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1463                                                   "NMTOKENInvalid",
1464                                                   new Object[] { attValue},
1465                                                   XMLErrorReporter.SEVERITY_ERROR);
1466                    }
1467                }
1468                break;
1469            }
1470
1471        } // switch
1472
1473    } // validateDTDattribute(QName,String,XMLAttributeDecl)
1474
1475
1476    /** Returns true if invalid standalone attribute definition. */
1477    protected boolean invalidStandaloneAttDef(QName element, QName attribute) {
1478        // REVISIT: This obviously needs to be fixed! -Ac
1479        boolean state = true;
1480        /*
1481       if (fStandaloneReader == -1) {
1482          return false;
1483       }
1484       // we are normalizing a default att value...  this ok?
1485       if (element.rawname == -1) {
1486          return false;
1487       }
1488       return getAttDefIsExternal(element, attribute);
1489       */
1490        return state;
1491    }
1492
1493
1494    //
1495    // Private methods
1496    //
1497
1498
1499    /**
1500     * Normalize the attribute value of a non CDATA attributes collapsing
1501     * sequences of space characters (x20)
1502     *
1503     * @param attributes The list of attributes
1504     * @param index The index of the attribute to normalize
1505     */
1506    private boolean normalizeAttrValue(XMLAttributes attributes, int index) {
1507        // vars
1508        boolean leadingSpace = true;
1509        boolean spaceStart = false;
1510        boolean readingNonSpace = false;
1511        int count = 0;
1512        int eaten = 0;
1513        String attrValue = attributes.getValue(index);
1514        char[] attValue = new char[attrValue.length()];
1515
1516        fBuffer.setLength(0);
1517        attrValue.getChars(0, attrValue.length(), attValue, 0);
1518        for (int i = 0; i < attValue.length; i++) {
1519
1520            if (attValue[i] == ' ') {
1521
1522                // now the tricky part
1523                if (readingNonSpace) {
1524                    spaceStart = true;
1525                    readingNonSpace = false;
1526                }
1527
1528                if (spaceStart && !leadingSpace) {
1529                    spaceStart = false;
1530                    fBuffer.append(attValue[i]);
1531                    count++;
1532                }
1533                else {
1534                    if (leadingSpace || !spaceStart) {
1535                        eaten ++;
1536                        /*** BUG #3512 ***
1537                        int entityCount = attributes.getEntityCount(index);
1538                        for (int j = 0;  j < entityCount; j++) {
1539                            int offset = attributes.getEntityOffset(index, j);
1540                            int length = attributes.getEntityLength(index, j);
1541                            if (offset <= i-eaten+1) {
1542                                if (offset+length >= i-eaten+1) {
1543                                    if (length > 0)
1544                                        length--;
1545                                }
1546                            }
1547                            else {
1548                                if (offset > 0)
1549                                    offset--;
1550                            }
1551                            attributes.setEntityOffset(index, j, offset);
1552                            attributes.setEntityLength(index, j, length);
1553                        }
1554                        /***/
1555                    }
1556                }
1557
1558            }
1559            else {
1560                readingNonSpace = true;
1561                spaceStart = false;
1562                leadingSpace = false;
1563                fBuffer.append(attValue[i]);
1564                count++;
1565            }
1566        }
1567
1568        // check if the last appended character is a space.
1569        if (count > 0 && fBuffer.charAt(count-1) == ' ') {
1570            fBuffer.setLength(count-1);
1571            /*** BUG #3512 ***
1572            int entityCount = attributes.getEntityCount(index);
1573            for (int j=0;  j < entityCount; j++) {
1574                int offset = attributes.getEntityOffset(index, j);
1575                int length = attributes.getEntityLength(index, j);
1576                if (offset < count-1) {
1577                    if (offset+length == count) {
1578                        length--;
1579                    }
1580                }
1581                else {
1582                    offset--;
1583                }
1584                attributes.setEntityOffset(index, j, offset);
1585                attributes.setEntityLength(index, j, length);
1586            }
1587            /***/
1588        }
1589        String newValue = fBuffer.toString();
1590        attributes.setValue(index, newValue);
1591        return ! attrValue.equals(newValue);
1592    }
1593
1594    /** Root element specified. */
1595    private final void rootElementSpecified(QName rootElement) throws XNIException {
1596        if (fPerformValidation) {
1597            String root1 = fRootElement.rawname;
1598            String root2 = rootElement.rawname;
1599            if (root1 == null || !root1.equals(root2)) {
1600                fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
1601                                            "RootElementTypeMustMatchDoctypedecl",
1602                                            new Object[]{root1, root2},
1603                                            XMLErrorReporter.SEVERITY_ERROR);
1604            }
1605        }
1606    } // rootElementSpecified(QName)
1607
1608    /**
1609     * Check that the content of an element is valid.
1610     * <p>
1611     * This is the method of primary concern to the validator. This method is called
1612     * upon the scanner reaching the end tag of an element. At that time, the
1613     * element's children must be structurally validated, so it calls this method.
1614     * The index of the element being checked (in the decl pool), is provided as
1615     * well as an array of element name indexes of the children. The validator must
1616     * confirm that this element can have these children in this order.
1617     * <p>
1618     * This can also be called to do 'what if' testing of content models just to see
1619     * if they would be valid.
1620     * <p>
1621     * Note that the element index is an index into the element decl pool, whereas
1622     * the children indexes are name indexes, i.e. into the string pool.
1623     * <p>
1624     * A value of -1 in the children array indicates a PCDATA node. All other
1625     * indexes will be positive and represent child elements. The count can be
1626     * zero, since some elements have the EMPTY content model and that must be
1627     * confirmed.
1628     *
1629     * @param elementIndex The index within the <code>ElementDeclPool</code> of this
1630     *                     element.
1631     * @param childCount The number of entries in the <code>children</code> array.
1632     * @param children The children of this element.
1633     *
1634     * @return The value -1 if fully valid, else the 0 based index of the child
1635     *         that first failed. If the value returned is equal to the number
1636     *         of children, then additional content is required to reach a valid
1637     *         ending state.
1638     *
1639     * @exception Exception Thrown on error.
1640     */
1641    private int checkContent(int elementIndex,
1642                             QName[] children,
1643                             int childOffset,
1644                             int childCount) throws XNIException {
1645
1646        fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
1647
1648        // Get the element name index from the element
1649        final String elementType = fCurrentElement.rawname;
1650
1651        // Get out the content spec for this element
1652        final int contentType = fCurrentContentSpecType;
1653
1654
1655        //
1656        //  Deal with the possible types of content. We try to optimized here
1657        //  by dealing specially with content models that don't require the
1658        //  full DFA treatment.
1659        //
1660        if (contentType == XMLElementDecl.TYPE_EMPTY) {
1661            //
1662            //  If the child count is greater than zero, then this is
1663            //  an error right off the bat at index 0.
1664            //
1665            if (childCount != 0) {
1666                return 0;
1667            }
1668        }
1669        else if (contentType == XMLElementDecl.TYPE_ANY) {
1670            //
1671            //  This one is open game so we don't pass any judgement on it
1672            //  at all. Its assumed to fine since it can hold anything.
1673            //
1674        }
1675        else if (contentType == XMLElementDecl.TYPE_MIXED ||
1676                 contentType == XMLElementDecl.TYPE_CHILDREN) {
1677            // Get the content model for this element, faulting it in if needed
1678            ContentModelValidator cmElem = null;
1679            cmElem = fTempElementDecl.contentModelValidator;
1680            int result = cmElem.validate(children, childOffset, childCount);
1681            return result;
1682        }
1683        else if (contentType == -1) {
1684            //REVISIT
1685            /****
1686            reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
1687                                      XMLMessages.VC_ELEMENT_VALID,
1688                                      elementType);
1689            /****/
1690        }
1691        else if (contentType == XMLElementDecl.TYPE_SIMPLE) {
1692
1693            //REVISIT
1694            // this should never be reached in the case of DTD validation.
1695
1696        }
1697        else {
1698            //REVISIT
1699            /****
1700            fErrorReporter.reportError(fErrorReporter.getLocator(),
1701                                       ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
1702                                       ImplementationMessages.VAL_CST,
1703                                       0,
1704                                       null,
1705                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
1706            /****/
1707        }
1708
1709        // We succeeded
1710        return -1;
1711
1712    } // checkContent(int,int,QName[]):int
1713
1714    /** Returns the content spec type for an element index. */
1715    private int getContentSpecType(int elementIndex) {
1716
1717        int contentSpecType = -1;
1718        if (elementIndex > -1) {
1719            if (fDTDGrammar.getElementDecl(elementIndex,fTempElementDecl)) {
1720                contentSpecType = fTempElementDecl.type;
1721            }
1722        }
1723        return contentSpecType;
1724    }
1725
1726    /** Character data in content. */
1727    private void charDataInContent() {
1728
1729        if (DEBUG_ELEMENT_CHILDREN) {
1730            System.out.println("charDataInContent()");
1731        }
1732        if (fElementChildren.length <= fElementChildrenLength) {
1733            QName[] newarray = new QName[fElementChildren.length * 2];
1734            System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
1735            fElementChildren = newarray;
1736        }
1737        QName qname = fElementChildren[fElementChildrenLength];
1738        if (qname == null) {
1739            for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
1740                fElementChildren[i] = new QName();
1741            }
1742            qname = fElementChildren[fElementChildrenLength];
1743        }
1744        qname.clear();
1745        fElementChildrenLength++;
1746
1747    } // charDataInCount()
1748
1749    /** convert attribute type from ints to strings */
1750    private String getAttributeTypeName(XMLAttributeDecl attrDecl) {
1751
1752        switch (attrDecl.simpleType.type) {
1753        case XMLSimpleType.TYPE_ENTITY: {
1754                return attrDecl.simpleType.list ? XMLSymbols.fENTITIESSymbol : XMLSymbols.fENTITYSymbol;
1755            }
1756        case XMLSimpleType.TYPE_ENUMERATION: {
1757                StringBuffer buffer = new StringBuffer();
1758                buffer.append('(');
1759                for (int i=0; i<attrDecl.simpleType.enumeration.length ; i++) {
1760                    if (i > 0) {
1761                        buffer.append('|');
1762                    }
1763                    buffer.append(attrDecl.simpleType.enumeration[i]);
1764                }
1765                buffer.append(')');
1766                return fSymbolTable.addSymbol(buffer.toString());
1767            }
1768        case XMLSimpleType.TYPE_ID: {
1769                return XMLSymbols.fIDSymbol;
1770            }
1771        case XMLSimpleType.TYPE_IDREF: {
1772                return attrDecl.simpleType.list ? XMLSymbols.fIDREFSSymbol : XMLSymbols.fIDREFSymbol;
1773            }
1774        case XMLSimpleType.TYPE_NMTOKEN: {
1775                return attrDecl.simpleType.list ? XMLSymbols.fNMTOKENSSymbol : XMLSymbols.fNMTOKENSymbol;
1776            }
1777        case XMLSimpleType.TYPE_NOTATION: {
1778                return XMLSymbols.fNOTATIONSymbol;
1779            }
1780        }
1781        return XMLSymbols.fCDATASymbol;
1782
1783    } // getAttributeTypeName(XMLAttributeDecl):String
1784
1785    /** initialization */
1786    protected void init() {
1787
1788        // datatype validators
1789        if (fValidation || fDynamicValidation) {
1790            try {
1791                //REVISIT: datatypeRegistry + initialization of datatype
1792                //         why do we cast to ListDatatypeValidator?
1793                fValID       = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDSymbol);
1794                fValIDRef    = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDREFSymbol);
1795                fValIDRefs   = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDREFSSymbol);
1796                fValENTITY   = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fENTITYSymbol);
1797                fValENTITIES = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fENTITIESSymbol);
1798                fValNMTOKEN  = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNMTOKENSymbol);
1799                fValNMTOKENS = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNMTOKENSSymbol);
1800                fValNOTATION = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNOTATIONSymbol);
1801
1802            }
1803            catch (Exception e) {
1804                // should never happen
1805                e.printStackTrace(System.err);
1806            }
1807
1808        }
1809
1810    } // init()
1811
1812    /** ensure element stack capacity */
1813    private void ensureStackCapacity (int newElementDepth) {
1814        if (newElementDepth == fElementQNamePartsStack.length) {
1815
1816            QName[] newStackOfQueue = new QName[newElementDepth * 2];
1817            System.arraycopy(this.fElementQNamePartsStack, 0, newStackOfQueue, 0, newElementDepth );
1818            fElementQNamePartsStack = newStackOfQueue;
1819
1820            QName qname = fElementQNamePartsStack[newElementDepth];
1821            if (qname == null) {
1822                for (int i = newElementDepth; i < fElementQNamePartsStack.length; i++) {
1823                    fElementQNamePartsStack[i] = new QName();
1824                }
1825            }
1826
1827            int[] newStack = new int[newElementDepth * 2];
1828            System.arraycopy(fElementIndexStack, 0, newStack, 0, newElementDepth);
1829            fElementIndexStack = newStack;
1830
1831            newStack = new int[newElementDepth * 2];
1832            System.arraycopy(fContentSpecTypeStack, 0, newStack, 0, newElementDepth);
1833            fContentSpecTypeStack = newStack;
1834
1835        }
1836    } // ensureStackCapacity
1837
1838
1839    //
1840    // Protected methods
1841    //
1842
1843    /** Handle element
1844     * @return true if validator is removed from the pipeline
1845     */
1846    protected boolean handleStartElement(QName element, XMLAttributes attributes, Augmentations augs)
1847                        throws XNIException {
1848
1849
1850        // VC: Root Element Type
1851        // see if the root element's name matches the one in DoctypeDecl
1852        if (!fSeenRootElement) {
1853            // REVISIT: Here are current assumptions about validation features
1854            //          given that XMLSchema validator is in the pipeline
1855            //
1856            // http://xml.org/sax/features/validation = true
1857            // http://apache.org/xml/features/validation/schema = true
1858            //
1859            // [1] XML instance document only has reference to a DTD
1860            //  Outcome: report validation errors only against dtd.
1861            //
1862            // [2] XML instance document has only XML Schema grammars:
1863            //  Outcome: report validation errors only against schemas (no errors produced from DTD validator)
1864            //
1865            // [3] XML instance document has DTD and XML schemas:
1866            // [a] if schema language is not set outcome - validation errors reported against both grammars: DTD and schemas.
1867            // [b] if schema language is set to XML Schema - do not report validation errors
1868            //
1869            // if dynamic validation is on
1870            //            validate only against grammar we've found (depending on settings
1871            //            for schema feature)
1872            //
1873            //
1874            fPerformValidation = validate();
1875            fSeenRootElement = true;
1876            fValidationManager.setEntityState(fDTDGrammar);
1877            fValidationManager.setGrammarFound(fSeenDoctypeDecl);
1878            rootElementSpecified(element);
1879        }
1880        if (fDTDGrammar == null) {
1881
1882            if (!fPerformValidation) {
1883                fCurrentElementIndex = -1;
1884                fCurrentContentSpecType = -1;
1885                fInElementContent = false;
1886            }
1887            if (fPerformValidation) {
1888                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1889                                           "MSG_GRAMMAR_NOT_FOUND",
1890                                           new Object[]{ element.rawname},
1891                                           XMLErrorReporter.SEVERITY_ERROR);
1892            }
1893            // modify pipeline
1894            if (fDocumentSource !=null ) {
1895                fDocumentSource.setDocumentHandler(fDocumentHandler);
1896                if (fDocumentHandler != null)
1897                    fDocumentHandler.setDocumentSource(fDocumentSource);
1898                return true;
1899            }
1900        }
1901        else {
1902            //  resolve the element
1903            fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element);
1904            //changed here.. new function for getContentSpecType
1905            fCurrentContentSpecType = fDTDGrammar.getContentSpecType(fCurrentElementIndex);
1906            if (fCurrentContentSpecType == -1 && fPerformValidation) {
1907                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1908                                           "MSG_ELEMENT_NOT_DECLARED",
1909                                           new Object[]{ element.rawname},
1910                                           XMLErrorReporter.SEVERITY_ERROR);
1911            }
1912
1913            //  0. insert default attributes
1914            //  1. normalize the attributes
1915            //  2. validate the attrivute list.
1916            // TO DO:
1917            //changed here.. also pass element name,
1918            addDTDDefaultAttrsAndValidate(element, fCurrentElementIndex, attributes);
1919        }
1920
1921        // set element content state
1922        fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN;
1923
1924        // increment the element depth, add this element's
1925        // QName to its enclosing element 's children list
1926        fElementDepth++;
1927        if (fPerformValidation) {
1928            // push current length onto stack
1929            if (fElementChildrenOffsetStack.length <= fElementDepth) {
1930                int newarray[] = new int[fElementChildrenOffsetStack.length * 2];
1931                System.arraycopy(fElementChildrenOffsetStack, 0, newarray, 0, fElementChildrenOffsetStack.length);
1932                fElementChildrenOffsetStack = newarray;
1933            }
1934            fElementChildrenOffsetStack[fElementDepth] = fElementChildrenLength;
1935
1936            // add this element to children
1937            if (fElementChildren.length <= fElementChildrenLength) {
1938                QName[] newarray = new QName[fElementChildrenLength * 2];
1939                System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
1940                fElementChildren = newarray;
1941            }
1942            QName qname = fElementChildren[fElementChildrenLength];
1943            if (qname == null) {
1944                for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
1945                    fElementChildren[i] = new QName();
1946                }
1947                qname = fElementChildren[fElementChildrenLength];
1948            }
1949            qname.setValues(element);
1950            fElementChildrenLength++;
1951        }
1952
1953        // save current element information
1954        fCurrentElement.setValues(element);
1955        ensureStackCapacity(fElementDepth);
1956        fElementQNamePartsStack[fElementDepth].setValues(fCurrentElement);
1957        fElementIndexStack[fElementDepth] = fCurrentElementIndex;
1958        fContentSpecTypeStack[fElementDepth] = fCurrentContentSpecType;
1959        startNamespaceScope(element, attributes, augs);
1960        return false;
1961
1962    } // handleStartElement(QName,XMLAttributes)
1963
1964    protected void startNamespaceScope(QName element, XMLAttributes attributes, Augmentations augs){
1965    }
1966
1967    /** Handle end element. */
1968    protected void handleEndElement(QName element,  Augmentations augs, boolean isEmpty)
1969    throws XNIException {
1970
1971        // decrease element depth
1972        fElementDepth--;
1973
1974        // validate
1975        if (fPerformValidation) {
1976            int elementIndex = fCurrentElementIndex;
1977            if (elementIndex != -1 && fCurrentContentSpecType != -1) {
1978                QName children[] = fElementChildren;
1979                int childrenOffset = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
1980                int childrenLength = fElementChildrenLength - childrenOffset;
1981                int result = checkContent(elementIndex,
1982                                          children, childrenOffset, childrenLength);
1983
1984                if (result != -1) {
1985                    fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
1986                    if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
1987                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1988                                                   "MSG_CONTENT_INVALID",
1989                                                   new Object[]{ element.rawname, "EMPTY"},
1990                                                   XMLErrorReporter.SEVERITY_ERROR);
1991                    }
1992                    else {
1993                        String messageKey = result != childrenLength ?
1994                                            "MSG_CONTENT_INVALID" : "MSG_CONTENT_INCOMPLETE";
1995                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1996                                                   messageKey,
1997                                                   new Object[]{ element.rawname,
1998                                                       fDTDGrammar.getContentSpecAsString(elementIndex)},
1999                                                   XMLErrorReporter.SEVERITY_ERROR);
2000                    }
2001                }
2002            }
2003            fElementChildrenLength = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
2004        }
2005
2006        endNamespaceScope(fCurrentElement, augs, isEmpty);
2007
2008        // now pop this element off the top of the element stack
2009        if (fElementDepth < -1) {
2010            throw new RuntimeException("FWK008 Element stack underflow");
2011        }
2012        if (fElementDepth < 0) {
2013            fCurrentElement.clear();
2014            fCurrentElementIndex = -1;
2015            fCurrentContentSpecType = -1;
2016            fInElementContent = false;
2017
2018            // TO DO : fix this
2019            //
2020            // Check after document is fully parsed
2021            // (1) check that there was an element with a matching id for every
2022            //   IDREF and IDREFS attr (V_IDREF0)
2023            //
2024            if (fPerformValidation) {
2025                Iterator invIdRefs = fValidationState.checkIDRefID();
2026                if (invIdRefs != null) {
2027                    while (invIdRefs.hasNext()) {
2028                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
2029                                "MSG_ELEMENT_WITH_ID_REQUIRED",
2030                                new Object[]{invIdRefs.next()},
2031                                XMLErrorReporter.SEVERITY_ERROR );
2032                    }
2033                }
2034            }
2035            return;
2036        }
2037
2038        // If Namespace enable then localName != rawName
2039        fCurrentElement.setValues(fElementQNamePartsStack[fElementDepth]);
2040
2041        fCurrentElementIndex = fElementIndexStack[fElementDepth];
2042        fCurrentContentSpecType = fContentSpecTypeStack[fElementDepth];
2043        fInElementContent = (fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN);
2044
2045    } // handleEndElement(QName,boolean)
2046
2047    protected void endNamespaceScope(QName element,  Augmentations augs, boolean isEmpty){
2048
2049        // call handlers
2050        if (fDocumentHandler != null && !isEmpty) {
2051            // NOTE: The binding of the element doesn't actually happen
2052            //       yet because the namespace binder does that. However,
2053            //       if it does it before this point, then the endPrefix-
2054            //       Mapping calls get made too soon! As long as the
2055            //       rawnames match, we know it'll have a good binding,
2056            //       so we can just use the current element. -Ac
2057            fDocumentHandler.endElement(fCurrentElement, augs);
2058        }
2059    }
2060
2061    // returns whether a character is space according to the
2062    // version of XML this validator supports.
2063    protected boolean isSpace(int c) {
2064        return XMLChar.isSpace(c);
2065    } // isSpace(int):  boolean
2066
2067    public boolean characterData(String data, Augmentations augs) {
2068        characters(new XMLString(data.toCharArray(), 0, data.length()), augs);
2069        return true;
2070    }
2071
2072} // class XMLDTDValidator
2073