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 java.util.ArrayList;
25import java.util.HashMap;
26import java.util.Iterator;
27import java.util.Locale;
28import java.util.Map;
29import java.util.StringTokenizer;
30
31import com.sun.org.apache.xerces.internal.impl.Constants;
32import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
33import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
34import com.sun.org.apache.xerces.internal.util.SymbolTable;
35import com.sun.org.apache.xerces.internal.util.XMLChar;
36import com.sun.org.apache.xerces.internal.util.XMLSymbols;
37import com.sun.org.apache.xerces.internal.xni.Augmentations;
38import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
39import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
40import com.sun.org.apache.xerces.internal.xni.XMLLocator;
41import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
42import com.sun.org.apache.xerces.internal.xni.XMLString;
43import com.sun.org.apache.xerces.internal.xni.XNIException;
44import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
45import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
46import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
47import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
48import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
49import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
50import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDContentModelFilter;
51import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDContentModelSource;
52import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDFilter;
53import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource;
54
55/**
56 * The DTD processor. The processor implements a DTD
57 * filter: receiving DTD events from the DTD scanner; validating
58 * the content and structure; building a grammar, if applicable;
59 * and notifying the DTDHandler of the information resulting from the
60 * process.
61 * <p>
62 * This component requires the following features and properties from the
63 * component manager that uses it:
64 * <ul>
65 *  <li>http://xml.org/sax/features/namespaces</li>
66 *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
67 *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
68 *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
69 *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
70 * </ul>
71 *
72 * @xerces.internal
73 *
74 * @author Neil Graham, IBM
75 *
76 */
77public class XMLDTDProcessor
78        implements XMLComponent, XMLDTDFilter, XMLDTDContentModelFilter {
79
80    //
81    // Constants
82    //
83
84    /** Top level scope (-1). */
85    private static final int TOP_LEVEL_SCOPE = -1;
86
87    // feature identifiers
88
89    /** Feature identifier: validation. */
90    protected static final String VALIDATION =
91        Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
92
93    /** Feature identifier: notify character references. */
94    protected static final String NOTIFY_CHAR_REFS =
95        Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_CHAR_REFS_FEATURE;
96
97    /** Feature identifier: warn on duplicate attdef */
98    protected static final String WARN_ON_DUPLICATE_ATTDEF =
99        Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ATTDEF_FEATURE;
100
101    /** Feature identifier: warn on undeclared element referenced in content model. */
102    protected static final String WARN_ON_UNDECLARED_ELEMDEF =
103        Constants.XERCES_FEATURE_PREFIX + Constants.WARN_ON_UNDECLARED_ELEMDEF_FEATURE;
104
105        protected static final String PARSER_SETTINGS =
106        Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
107
108    // property identifiers
109
110    /** Property identifier: symbol table. */
111    protected static final String SYMBOL_TABLE =
112        Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
113
114    /** Property identifier: error reporter. */
115    protected static final String ERROR_REPORTER =
116        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
117
118    /** Property identifier: grammar pool. */
119    protected static final String GRAMMAR_POOL =
120        Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
121
122    /** Property identifier: validator . */
123    protected static final String DTD_VALIDATOR =
124        Constants.XERCES_PROPERTY_PREFIX + Constants.DTD_VALIDATOR_PROPERTY;
125
126    // recognized features and properties
127
128    /** Recognized features. */
129    private static final String[] RECOGNIZED_FEATURES = {
130        VALIDATION,
131        WARN_ON_DUPLICATE_ATTDEF,
132        WARN_ON_UNDECLARED_ELEMDEF,
133        NOTIFY_CHAR_REFS,
134    };
135
136    /** Feature defaults. */
137    private static final Boolean[] FEATURE_DEFAULTS = {
138        null,
139        Boolean.FALSE,
140        Boolean.FALSE,
141        null,
142    };
143
144    /** Recognized properties. */
145    private static final String[] RECOGNIZED_PROPERTIES = {
146        SYMBOL_TABLE,
147        ERROR_REPORTER,
148        GRAMMAR_POOL,
149        DTD_VALIDATOR,
150    };
151
152    /** Property defaults. */
153    private static final Object[] PROPERTY_DEFAULTS = {
154        null,
155        null,
156        null,
157        null,
158    };
159
160    // debugging
161
162    //
163    // Data
164    //
165
166    // features
167
168    /** Validation. */
169    protected boolean fValidation;
170
171    /** Validation against only DTD */
172    protected boolean fDTDValidation;
173
174    /** warn on duplicate attribute definition, this feature works only when validation is true */
175    protected boolean fWarnDuplicateAttdef;
176
177    /** warn on undeclared element referenced in content model, this feature only works when valiation is true */
178    protected boolean fWarnOnUndeclaredElemdef;
179
180    // properties
181
182    /** Symbol table. */
183    protected SymbolTable fSymbolTable;
184
185    /** Error reporter. */
186    protected XMLErrorReporter fErrorReporter;
187
188    /** Grammar bucket. */
189    protected DTDGrammarBucket fGrammarBucket;
190
191    // the validator to which we look for our grammar bucket (the
192    // validator needs to hold the bucket so that it can initialize
193    // the grammar with details like whether it's for a standalone document...
194    protected XMLDTDValidator fValidator;
195
196    // the grammar pool we'll try to add the grammar to:
197    protected XMLGrammarPool fGrammarPool;
198
199    // what's our Locale?
200    protected Locale fLocale;
201
202    // handlers
203
204    /** DTD handler. */
205    protected XMLDTDHandler fDTDHandler;
206
207    /** DTD source. */
208    protected XMLDTDSource fDTDSource;
209
210    /** DTD content model handler. */
211    protected XMLDTDContentModelHandler fDTDContentModelHandler;
212
213    /** DTD content model source. */
214    protected XMLDTDContentModelSource fDTDContentModelSource;
215
216    // grammars
217
218    /** DTD Grammar. */
219    protected DTDGrammar fDTDGrammar;
220
221    // state
222
223    /** Perform validation. */
224    private boolean fPerformValidation;
225
226    /** True if in an ignore conditional section of the DTD. */
227    protected boolean fInDTDIgnore;
228
229    // information regarding the current element
230
231    // validation states
232
233    /** Mixed. */
234    private boolean fMixed;
235
236    // temporary variables
237
238    /** Temporary entity declaration. */
239    private final XMLEntityDecl fEntityDecl = new XMLEntityDecl();
240
241    /** Notation declaration hash. */
242    private final HashMap fNDataDeclNotations = new HashMap();
243
244    /** DTD element declaration name. */
245    private String fDTDElementDeclName = null;
246
247    /** Mixed element type "hash". */
248    private final ArrayList fMixedElementTypes = new ArrayList();
249
250    /** Element declarations in DTD. */
251    private final ArrayList fDTDElementDecls = new ArrayList();
252
253    // to check for duplicate ID or ANNOTATION attribute declare in
254    // ATTLIST, and misc VCs
255
256    /** ID attribute names. */
257    private HashMap fTableOfIDAttributeNames;
258
259    /** NOTATION attribute names. */
260    private HashMap fTableOfNOTATIONAttributeNames;
261
262    /** NOTATION enumeration values. */
263    private HashMap fNotationEnumVals;
264
265    //
266    // Constructors
267    //
268
269    /** Default constructor. */
270    public XMLDTDProcessor() {
271
272        // initialize data
273
274    } // <init>()
275
276    //
277    // XMLComponent methods
278    //
279
280    /*
281     * Resets the component. The component can query the component manager
282     * about any features and properties that affect the operation of the
283     * component.
284     *
285     * @param componentManager The component manager.
286     *
287     * @throws SAXException Thrown by component on finitialization error.
288     *                      For example, if a feature or property is
289     *                      required for the operation of the component, the
290     *                      component manager may throw a
291     *                      SAXNotRecognizedException or a
292     *                      SAXNotSupportedException.
293     */
294    public void reset(XMLComponentManager componentManager) throws XMLConfigurationException {
295
296        boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
297
298        if (!parser_settings) {
299            // parser settings have not been changed
300            reset();
301            return;
302        }
303
304        // sax features
305        fValidation = componentManager.getFeature(VALIDATION, false);
306
307        fDTDValidation =
308                !(componentManager
309                    .getFeature(
310                        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE, false));
311
312        // Xerces features
313
314        fWarnDuplicateAttdef = componentManager.getFeature(WARN_ON_DUPLICATE_ATTDEF, false);
315        fWarnOnUndeclaredElemdef = componentManager.getFeature(WARN_ON_UNDECLARED_ELEMDEF, false);
316
317        // get needed components
318        fErrorReporter =
319            (XMLErrorReporter) componentManager.getProperty(
320                Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);
321        fSymbolTable =
322            (SymbolTable) componentManager.getProperty(
323                Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY);
324
325        fGrammarPool = (XMLGrammarPool) componentManager.getProperty(GRAMMAR_POOL, null);
326
327        try {
328            fValidator = (XMLDTDValidator) componentManager.getProperty(DTD_VALIDATOR, null);
329        } catch (ClassCastException e) {
330            fValidator = null;
331        }
332        // we get our grammarBucket from the validator...
333        if (fValidator != null) {
334            fGrammarBucket = fValidator.getGrammarBucket();
335        } else {
336            fGrammarBucket = null;
337        }
338        reset();
339
340    } // reset(XMLComponentManager)
341
342    protected void reset() {
343        // clear grammars
344        fDTDGrammar = null;
345        // initialize state
346        fInDTDIgnore = false;
347
348        fNDataDeclNotations.clear();
349
350        // datatype validators
351        if (fValidation) {
352
353            if (fNotationEnumVals == null) {
354                fNotationEnumVals = new HashMap();
355            }
356            fNotationEnumVals.clear();
357
358            fTableOfIDAttributeNames = new HashMap();
359            fTableOfNOTATIONAttributeNames = new HashMap();
360        }
361
362    }
363    /**
364     * Returns a list of feature identifiers that are recognized by
365     * this component. This method may return null if no features
366     * are recognized by this component.
367     */
368    public String[] getRecognizedFeatures() {
369        return (String[])(RECOGNIZED_FEATURES.clone());
370    } // getRecognizedFeatures():String[]
371
372    /**
373     * Sets the state of a feature. This method is called by the component
374     * manager any time after reset when a feature changes state.
375     * <p>
376     * <strong>Note:</strong> Components should silently ignore features
377     * that do not affect the operation of the component.
378     *
379     * @param featureId The feature identifier.
380     * @param state     The state of the feature.
381     *
382     * @throws SAXNotRecognizedException The component should not throw
383     *                                   this exception.
384     * @throws SAXNotSupportedException The component should not throw
385     *                                  this exception.
386     */
387    public void setFeature(String featureId, boolean state)
388            throws XMLConfigurationException {
389    } // setFeature(String,boolean)
390
391    /**
392     * Returns a list of property identifiers that are recognized by
393     * this component. This method may return null if no properties
394     * are recognized by this component.
395     */
396    public String[] getRecognizedProperties() {
397        return (String[])(RECOGNIZED_PROPERTIES.clone());
398    } // getRecognizedProperties():String[]
399
400    /**
401     * Sets the value of a property. This method is called by the component
402     * manager any time after reset when a property changes value.
403     * <p>
404     * <strong>Note:</strong> Components should silently ignore properties
405     * that do not affect the operation of the component.
406     *
407     * @param propertyId The property identifier.
408     * @param value      The value of the property.
409     *
410     * @throws SAXNotRecognizedException The component should not throw
411     *                                   this exception.
412     * @throws SAXNotSupportedException The component should not throw
413     *                                  this exception.
414     */
415    public void setProperty(String propertyId, Object value)
416            throws XMLConfigurationException {
417    } // setProperty(String,Object)
418
419    /**
420     * Returns the default state for a feature, or null if this
421     * component does not want to report a default value for this
422     * feature.
423     *
424     * @param featureId The feature identifier.
425     *
426     * @since Xerces 2.2.0
427     */
428    public Boolean getFeatureDefault(String featureId) {
429        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
430            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
431                return FEATURE_DEFAULTS[i];
432            }
433        }
434        return null;
435    } // getFeatureDefault(String):Boolean
436
437    /**
438     * Returns the default state for a property, or null if this
439     * component does not want to report a default value for this
440     * property.
441     *
442     * @param propertyId The property identifier.
443     *
444     * @since Xerces 2.2.0
445     */
446    public Object getPropertyDefault(String propertyId) {
447        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
448            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
449                return PROPERTY_DEFAULTS[i];
450            }
451        }
452        return null;
453    } // getPropertyDefault(String):Object
454
455    //
456    // XMLDTDSource methods
457    //
458
459    /**
460     * Sets the DTD handler.
461     *
462     * @param dtdHandler The DTD handler.
463     */
464    public void setDTDHandler(XMLDTDHandler dtdHandler) {
465        fDTDHandler = dtdHandler;
466    } // setDTDHandler(XMLDTDHandler)
467
468    /**
469     * Returns the DTD handler.
470     *
471     * @return The DTD handler.
472     */
473    public XMLDTDHandler getDTDHandler() {
474        return fDTDHandler;
475    } // getDTDHandler():  XMLDTDHandler
476
477    //
478    // XMLDTDContentModelSource methods
479    //
480
481    /**
482     * Sets the DTD content model handler.
483     *
484     * @param dtdContentModelHandler The DTD content model handler.
485     */
486    public void setDTDContentModelHandler(XMLDTDContentModelHandler dtdContentModelHandler) {
487        fDTDContentModelHandler = dtdContentModelHandler;
488    } // setDTDContentModelHandler(XMLDTDContentModelHandler)
489
490    /**
491     * Gets the DTD content model handler.
492     *
493     * @return dtdContentModelHandler The DTD content model handler.
494     */
495    public XMLDTDContentModelHandler getDTDContentModelHandler() {
496        return fDTDContentModelHandler;
497    } // getDTDContentModelHandler():  XMLDTDContentModelHandler
498
499    //
500    // XMLDTDContentModelHandler and XMLDTDHandler methods
501    //
502
503    /**
504     * The start of the DTD external subset.
505     *
506     * @param augs Additional information that may include infoset
507     *                      augmentations.
508     *
509     * @throws XNIException Thrown by handler to signal an error.
510     */
511    public void startExternalSubset(XMLResourceIdentifier identifier,
512                                    Augmentations augs) throws XNIException {
513        if(fDTDGrammar != null)
514            fDTDGrammar.startExternalSubset(identifier, augs);
515        if(fDTDHandler != null){
516            fDTDHandler.startExternalSubset(identifier, augs);
517        }
518    }
519
520    /**
521     * The end of the DTD external subset.
522     *
523     * @param augs Additional information that may include infoset
524     *                      augmentations.
525     *
526     * @throws XNIException Thrown by handler to signal an error.
527     */
528    public void endExternalSubset(Augmentations augs) throws XNIException {
529        if(fDTDGrammar != null)
530            fDTDGrammar.endExternalSubset(augs);
531        if(fDTDHandler != null){
532            fDTDHandler.endExternalSubset(augs);
533        }
534    }
535
536    /**
537     * Check standalone entity reference.
538     * Made static to make common between the validator and loader.
539     *
540     * @param name
541     *@param grammar    grammar to which entity belongs
542     * @param tempEntityDecl    empty entity declaration to put results in
543     * @param errorReporter     error reporter to send errors to
544     *
545     * @throws XNIException Thrown by application to signal an error.
546     */
547    protected static void checkStandaloneEntityRef(String name, DTDGrammar grammar,
548                    XMLEntityDecl tempEntityDecl, XMLErrorReporter errorReporter) throws XNIException {
549        // check VC: Standalone Document Declartion, entities references appear in the document.
550        int entIndex = grammar.getEntityDeclIndex(name);
551        if (entIndex > -1) {
552            grammar.getEntityDecl(entIndex, tempEntityDecl);
553            if (tempEntityDecl.inExternal) {
554                errorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
555                                            "MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
556                                            new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
557            }
558        }
559    }
560
561    /**
562     * A comment.
563     *
564     * @param text The text in the comment.
565     * @param augs   Additional information that may include infoset augmentations
566     *
567     * @throws XNIException Thrown by application to signal an error.
568     */
569    public void comment(XMLString text, Augmentations augs) throws XNIException {
570
571        // call handlers
572        if(fDTDGrammar != null)
573            fDTDGrammar.comment(text, augs);
574        if (fDTDHandler != null) {
575            fDTDHandler.comment(text, augs);
576        }
577
578    } // comment(XMLString)
579
580
581    /**
582     * A processing instruction. Processing instructions consist of a
583     * target name and, optionally, text data. The data is only meaningful
584     * to the application.
585     * <p>
586     * Typically, a processing instruction's data will contain a series
587     * of pseudo-attributes. These pseudo-attributes follow the form of
588     * element attributes but are <strong>not</strong> parsed or presented
589     * to the application as anything other than text. The application is
590     * responsible for parsing the data.
591     *
592     * @param target The target.
593     * @param data   The data or null if none specified.
594     * @param augs   Additional information that may include infoset augmentations
595     *
596     * @throws XNIException Thrown by handler to signal an error.
597     */
598    public void processingInstruction(String target, XMLString data, Augmentations augs)
599    throws XNIException {
600
601        // call handlers
602        if(fDTDGrammar != null)
603            fDTDGrammar.processingInstruction(target, data, augs);
604        if (fDTDHandler != null) {
605            fDTDHandler.processingInstruction(target, data, augs);
606        }
607    } // processingInstruction(String,XMLString)
608
609    //
610    // XMLDTDHandler methods
611    //
612
613    /**
614     * The start of the DTD.
615     *
616     * @param locator  The document locator, or null if the document
617     *                 location cannot be reported during the parsing of
618     *                 the document DTD. However, it is <em>strongly</em>
619     *                 recommended that a locator be supplied that can
620     *                 at least report the base system identifier of the
621     *                 DTD.
622     * @param augs Additional information that may include infoset
623     *                      augmentations.
624     *
625     * @throws XNIException Thrown by handler to signal an error.
626     */
627    public void startDTD(XMLLocator locator, Augmentations augs) throws XNIException {
628
629
630        // initialize state
631        fNDataDeclNotations.clear();
632        fDTDElementDecls.clear();
633
634        // the grammar bucket's DTDGrammar will now be the
635        // one we want, whether we're constructing it or not.
636        // if we're not constructing it, then we should not have a reference
637        // to it!
638       if( !fGrammarBucket.getActiveGrammar().isImmutable()) {
639            fDTDGrammar = fGrammarBucket.getActiveGrammar();
640        }
641
642        // call handlers
643        if(fDTDGrammar != null )
644            fDTDGrammar.startDTD(locator, augs);
645        if (fDTDHandler != null) {
646            fDTDHandler.startDTD(locator, augs);
647        }
648
649    } // startDTD(XMLLocator)
650
651    /**
652     * Characters within an IGNORE conditional section.
653     *
654     * @param text The ignored text.
655     * @param augs Additional information that may include infoset
656     *                      augmentations.
657     *
658     * @throws XNIException Thrown by handler to signal an error.
659     */
660    public void ignoredCharacters(XMLString text, Augmentations augs) throws XNIException {
661
662        // ignored characters in DTD
663        if(fDTDGrammar != null )
664            fDTDGrammar.ignoredCharacters(text, augs);
665        if (fDTDHandler != null) {
666            fDTDHandler.ignoredCharacters(text, augs);
667        }
668    }
669
670    /**
671     * Notifies of the presence of a TextDecl line in an entity. If present,
672     * this method will be called immediately following the startParameterEntity call.
673     * <p>
674     * <strong>Note:</strong> This method is only called for external
675     * parameter entities referenced in the DTD.
676     *
677     * @param version  The XML version, or null if not specified.
678     * @param encoding The IANA encoding name of the entity.
679     * @param augs Additional information that may include infoset
680     *                      augmentations.
681     *
682     * @throws XNIException Thrown by handler to signal an error.
683     */
684    public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
685
686        // call handlers
687        if(fDTDGrammar != null )
688            fDTDGrammar.textDecl(version, encoding, augs);
689        if (fDTDHandler != null) {
690            fDTDHandler.textDecl(version, encoding, augs);
691        }
692    }
693
694    /**
695     * This method notifies of the start of a parameter entity. The parameter
696     * entity name start with a '%' character.
697     *
698     * @param name     The name of the parameter entity.
699     * @param identifier The resource identifier.
700     * @param encoding The auto-detected IANA encoding name of the entity
701     *                 stream. This value will be null in those situations
702     *                 where the entity encoding is not auto-detected (e.g.
703     *                 internal parameter entities).
704     * @param augs Additional information that may include infoset
705     *                      augmentations.
706     *
707     * @throws XNIException Thrown by handler to signal an error.
708     */
709    public void startParameterEntity(String name,
710                                     XMLResourceIdentifier identifier,
711                                     String encoding,
712                                     Augmentations augs) throws XNIException {
713
714        if (fPerformValidation && fDTDGrammar != null &&
715                fGrammarBucket.getStandalone()) {
716            checkStandaloneEntityRef(name, fDTDGrammar, fEntityDecl, fErrorReporter);
717        }
718        // call handlers
719        if(fDTDGrammar != null )
720            fDTDGrammar.startParameterEntity(name, identifier, encoding, augs);
721        if (fDTDHandler != null) {
722            fDTDHandler.startParameterEntity(name, identifier, encoding, augs);
723        }
724    }
725
726    /**
727     * This method notifies the end of a parameter entity. Parameter entity
728     * names begin with a '%' character.
729     *
730     * @param name The name of the parameter entity.
731     * @param augs Additional information that may include infoset
732     *                      augmentations.
733     *
734     * @throws XNIException Thrown by handler to signal an error.
735     */
736    public void endParameterEntity(String name, Augmentations augs) throws XNIException {
737
738        // call handlers
739        if(fDTDGrammar != null )
740            fDTDGrammar.endParameterEntity(name, augs);
741        if (fDTDHandler != null) {
742            fDTDHandler.endParameterEntity(name, augs);
743        }
744    }
745
746    /**
747     * An element declaration.
748     *
749     * @param name         The name of the element.
750     * @param contentModel The element content model.
751     * @param augs Additional information that may include infoset
752     *                      augmentations.
753     *
754     * @throws XNIException Thrown by handler to signal an error.
755     */
756    public void elementDecl(String name, String contentModel, Augmentations augs)
757    throws XNIException {
758
759        //check VC: Unique Element Declaration
760        if (fValidation) {
761            if (fDTDElementDecls.contains(name)) {
762                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
763                                           "MSG_ELEMENT_ALREADY_DECLARED",
764                                           new Object[]{ name},
765                                           XMLErrorReporter.SEVERITY_ERROR);
766            }
767            else {
768                fDTDElementDecls.add(name);
769            }
770        }
771
772        // call handlers
773        if(fDTDGrammar != null )
774            fDTDGrammar.elementDecl(name, contentModel, augs);
775        if (fDTDHandler != null) {
776            fDTDHandler.elementDecl(name, contentModel, augs);
777        }
778
779    } // elementDecl(String,String)
780
781    /**
782     * The start of an attribute list.
783     *
784     * @param elementName The name of the element that this attribute
785     *                    list is associated with.
786     * @param augs Additional information that may include infoset
787     *                      augmentations.
788     *
789     * @throws XNIException Thrown by handler to signal an error.
790     */
791    public void startAttlist(String elementName, Augmentations augs)
792        throws XNIException {
793
794        // call handlers
795        if(fDTDGrammar != null )
796            fDTDGrammar.startAttlist(elementName, augs);
797        if (fDTDHandler != null) {
798            fDTDHandler.startAttlist(elementName, augs);
799        }
800
801    } // startAttlist(String)
802
803    /**
804     * An attribute declaration.
805     *
806     * @param elementName   The name of the element that this attribute
807     *                      is associated with.
808     * @param attributeName The name of the attribute.
809     * @param type          The attribute type. This value will be one of
810     *                      the following: "CDATA", "ENTITY", "ENTITIES",
811     *                      "ENUMERATION", "ID", "IDREF", "IDREFS",
812     *                      "NMTOKEN", "NMTOKENS", or "NOTATION".
813     * @param enumeration   If the type has the value "ENUMERATION" or
814     *                      "NOTATION", this array holds the allowed attribute
815     *                      values; otherwise, this array is null.
816     * @param defaultType   The attribute default type. This value will be
817     *                      one of the following: "#FIXED", "#IMPLIED",
818     *                      "#REQUIRED", or null.
819     * @param defaultValue  The attribute default value, or null if no
820     *                      default value is specified.
821     * @param nonNormalizedDefaultValue  The attribute default value with no normalization
822     *                      performed, or null if no default value is specified.
823     * @param augs Additional information that may include infoset
824     *                      augmentations.
825     *
826     * @throws XNIException Thrown by handler to signal an error.
827     */
828    public void attributeDecl(String elementName, String attributeName,
829                              String type, String[] enumeration,
830                              String defaultType, XMLString defaultValue,
831                              XMLString nonNormalizedDefaultValue, Augmentations augs) throws XNIException {
832
833        if (type != XMLSymbols.fCDATASymbol && defaultValue != null) {
834            normalizeDefaultAttrValue(defaultValue);
835        }
836
837        if (fValidation) {
838
839                boolean duplicateAttributeDef = false ;
840
841                //Get Grammar index to grammar array
842                DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar:fGrammarBucket.getActiveGrammar());
843                int elementIndex       = grammar.getElementDeclIndex( elementName);
844                if (grammar.getAttributeDeclIndex(elementIndex, attributeName) != -1) {
845                    //more than one attribute definition is provided for the same attribute of a given element type.
846                    duplicateAttributeDef = true ;
847
848                    //this feature works only when validation is true.
849                    if(fWarnDuplicateAttdef){
850                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
851                                                 "MSG_DUPLICATE_ATTRIBUTE_DEFINITION",
852                                                 new Object[]{ elementName, attributeName },
853                                                 XMLErrorReporter.SEVERITY_WARNING );
854                    }
855                }
856
857
858            //
859            // a) VC: One ID per Element Type, If duplicate ID attribute
860            // b) VC: ID attribute Default. if there is a declareared attribute
861            //        default for ID it should be of type #IMPLIED or #REQUIRED
862            if (type == XMLSymbols.fIDSymbol) {
863                if (defaultValue != null && defaultValue.length != 0) {
864                    if (defaultType == null ||
865                        !(defaultType == XMLSymbols.fIMPLIEDSymbol ||
866                          defaultType == XMLSymbols.fREQUIREDSymbol)) {
867                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
868                                                   "IDDefaultTypeInvalid",
869                                                   new Object[]{ attributeName},
870                                                   XMLErrorReporter.SEVERITY_ERROR);
871                    }
872                }
873
874                if (!fTableOfIDAttributeNames.containsKey(elementName)) {
875                    fTableOfIDAttributeNames.put(elementName, attributeName);
876                }
877                else {
878                        //we should not report an error, when there is duplicate attribute definition for given element type
879                        //according to XML 1.0 spec, When more than one definition is provided for the same attribute of a given
880                        //element type, the first declaration is binding and later declaration are *ignored*. So processor should
881                        //ignore the second declarations, however an application would be warned of the duplicate attribute defintion
882                        // if http://apache.org/xml/features/validation/warn-on-duplicate-attdef feature is set to true,
883                        // one typical case where this could be a  problem, when any XML file
884                        // provide the ID type information through internal subset so that it is available to the parser which read
885                        //only internal subset. Now that attribute declaration(ID Type) can again be part of external parsed entity
886                        //referenced. At that time if parser doesn't make this distinction it will throw an error for VC One ID per
887                        //Element Type, which (second defintion) actually should be ignored. Application behavior may differ on the
888                        //basis of error or warning thrown. - nb.
889
890                        if(!duplicateAttributeDef){
891                                String previousIDAttributeName = (String)fTableOfIDAttributeNames.get( elementName );//rule a)
892                                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
893                                               "MSG_MORE_THAN_ONE_ID_ATTRIBUTE",
894                                               new Object[]{ elementName, previousIDAttributeName, attributeName},
895                                               XMLErrorReporter.SEVERITY_ERROR);
896                        }
897                }
898            }
899
900            //
901            //  VC: One Notation Per Element Type, should check if there is a
902            //      duplicate NOTATION attribute
903
904            if (type == XMLSymbols.fNOTATIONSymbol) {
905                // VC: Notation Attributes: all notation names in the
906                //     (attribute) declaration must be declared.
907                for (int i=0; i<enumeration.length; i++) {
908                    fNotationEnumVals.put(enumeration[i], attributeName);
909                }
910
911                if (fTableOfNOTATIONAttributeNames.containsKey( elementName ) == false) {
912                    fTableOfNOTATIONAttributeNames.put( elementName, attributeName);
913                }
914                else {
915                        //we should not report an error, when there is duplicate attribute definition for given element type
916                        //according to XML 1.0 spec, When more than one definition is provided for the same attribute of a given
917                        //element type, the first declaration is binding and later declaration are *ignored*. So processor should
918                        //ignore the second declarations, however an application would be warned of the duplicate attribute definition
919                        // if http://apache.org/xml/features/validation/warn-on-duplicate-attdef feature is set to true, Application behavior may differ on the basis of error or
920                        //warning thrown. - nb.
921
922                        if(!duplicateAttributeDef){
923
924                                String previousNOTATIONAttributeName = (String) fTableOfNOTATIONAttributeNames.get( elementName );
925                                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
926                                               "MSG_MORE_THAN_ONE_NOTATION_ATTRIBUTE",
927                                               new Object[]{ elementName, previousNOTATIONAttributeName, attributeName},
928                                               XMLErrorReporter.SEVERITY_ERROR);
929                         }
930                }
931            }
932
933            // VC: No Duplicate Tokens
934            // XML 1.0 SE Errata - E2
935            if (type == XMLSymbols.fENUMERATIONSymbol || type == XMLSymbols.fNOTATIONSymbol) {
936                outer:
937                    for (int i = 0; i < enumeration.length; ++i) {
938                        for (int j = i + 1; j < enumeration.length; ++j) {
939                            if (enumeration[i].equals(enumeration[j])) {
940                                // Only report the first uniqueness violation. There could be others,
941                                // but additional overhead would be incurred tracking unique tokens
942                                // that have already been encountered. -- mrglavas
943                                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
944                                               type == XMLSymbols.fENUMERATIONSymbol
945                                                   ? "MSG_DISTINCT_TOKENS_IN_ENUMERATION"
946                                                   : "MSG_DISTINCT_NOTATION_IN_ENUMERATION",
947                                               new Object[]{ elementName, enumeration[i], attributeName },
948                                               XMLErrorReporter.SEVERITY_ERROR);
949                                break outer;
950                            }
951                        }
952                    }
953            }
954
955            // VC: Attribute Default Legal
956            boolean ok = true;
957            if (defaultValue != null &&
958                (defaultType == null ||
959                 (defaultType != null && defaultType == XMLSymbols.fFIXEDSymbol))) {
960
961                String value = defaultValue.toString();
962                if (type == XMLSymbols.fNMTOKENSSymbol ||
963                    type == XMLSymbols.fENTITIESSymbol ||
964                    type == XMLSymbols.fIDREFSSymbol) {
965
966                    StringTokenizer tokenizer = new StringTokenizer(value," ");
967                    if (tokenizer.hasMoreTokens()) {
968                        while (true) {
969                            String nmtoken = tokenizer.nextToken();
970                            if (type == XMLSymbols.fNMTOKENSSymbol) {
971                                if (!isValidNmtoken(nmtoken)) {
972                                    ok = false;
973                                    break;
974                                }
975                            }
976                            else if (type == XMLSymbols.fENTITIESSymbol ||
977                                     type == XMLSymbols.fIDREFSSymbol) {
978                                if (!isValidName(nmtoken)) {
979                                    ok = false;
980                                    break;
981                                }
982                            }
983                            if (!tokenizer.hasMoreTokens()) {
984                                break;
985                            }
986                        }
987                    }
988
989                }
990                else {
991                    if (type == XMLSymbols.fENTITYSymbol ||
992                        type == XMLSymbols.fIDSymbol ||
993                        type == XMLSymbols.fIDREFSymbol ||
994                        type == XMLSymbols.fNOTATIONSymbol) {
995
996                        if (!isValidName(value)) {
997                            ok = false;
998                        }
999
1000                    }
1001                    else if (type == XMLSymbols.fNMTOKENSymbol ||
1002                             type == XMLSymbols.fENUMERATIONSymbol) {
1003
1004                        if (!isValidNmtoken(value)) {
1005                            ok = false;
1006                        }
1007                    }
1008
1009                    if (type == XMLSymbols.fNOTATIONSymbol ||
1010                        type == XMLSymbols.fENUMERATIONSymbol) {
1011                        ok = false;
1012                        for (int i=0; i<enumeration.length; i++) {
1013                            if (defaultValue.equals(enumeration[i])) {
1014                                ok = true;
1015                            }
1016                        }
1017                    }
1018
1019                }
1020                if (!ok) {
1021                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1022                                               "MSG_ATT_DEFAULT_INVALID",
1023                                               new Object[]{attributeName, value},
1024                                               XMLErrorReporter.SEVERITY_ERROR);
1025                }
1026            }
1027        }
1028
1029        // call handlers
1030        if(fDTDGrammar != null)
1031            fDTDGrammar.attributeDecl(elementName, attributeName,
1032                                  type, enumeration,
1033                                  defaultType, defaultValue, nonNormalizedDefaultValue, augs);
1034        if (fDTDHandler != null) {
1035            fDTDHandler.attributeDecl(elementName, attributeName,
1036                                      type, enumeration,
1037                                      defaultType, defaultValue, nonNormalizedDefaultValue, augs);
1038        }
1039
1040    } // attributeDecl(String,String,String,String[],String,XMLString, XMLString, Augmentations)
1041
1042    /**
1043     * The end of an attribute list.
1044     *
1045     * @param augs Additional information that may include infoset
1046     *                      augmentations.
1047     *
1048     * @throws XNIException Thrown by handler to signal an error.
1049     */
1050    public void endAttlist(Augmentations augs) throws XNIException {
1051
1052        // call handlers
1053        if(fDTDGrammar != null)
1054            fDTDGrammar.endAttlist(augs);
1055        if (fDTDHandler != null) {
1056            fDTDHandler.endAttlist(augs);
1057        }
1058
1059    } // endAttlist()
1060
1061    /**
1062     * An internal entity declaration.
1063     *
1064     * @param name The name of the entity. Parameter entity names start with
1065     *             '%', whereas the name of a general entity is just the
1066     *             entity name.
1067     * @param text The value of the entity.
1068     * @param nonNormalizedText The non-normalized value of the entity. This
1069     *             value contains the same sequence of characters that was in
1070     *             the internal entity declaration, without any entity
1071     *             references expanded.
1072     * @param augs Additional information that may include infoset
1073     *                      augmentations.
1074     *
1075     * @throws XNIException Thrown by handler to signal an error.
1076     */
1077    public void internalEntityDecl(String name, XMLString text,
1078                                   XMLString nonNormalizedText,
1079                                   Augmentations augs) throws XNIException {
1080
1081        DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar: fGrammarBucket.getActiveGrammar());
1082        int index = grammar.getEntityDeclIndex(name) ;
1083
1084        //If the same entity is declared more than once, the first declaration
1085        //encountered is binding, SAX requires only effective(first) declaration
1086        //to be reported to the application
1087
1088        //REVISIT: Does it make sense to pass duplicate Entity information across
1089        //the pipeline -- nb?
1090
1091        //its a new entity and hasn't been declared.
1092        if(index == -1){
1093            //store internal entity declaration in grammar
1094            if(fDTDGrammar != null)
1095                fDTDGrammar.internalEntityDecl(name, text, nonNormalizedText, augs);
1096            // call handlers
1097            if (fDTDHandler != null) {
1098                fDTDHandler.internalEntityDecl(name, text, nonNormalizedText, augs);
1099            }
1100        }
1101
1102    } // internalEntityDecl(String,XMLString,XMLString)
1103
1104
1105    /**
1106     * An external entity declaration.
1107     *
1108     * @param name     The name of the entity. Parameter entity names start
1109     *                 with '%', whereas the name of a general entity is just
1110     *                 the entity name.
1111     * @param identifier    An object containing all location information
1112     *                      pertinent to this external entity.
1113     * @param augs Additional information that may include infoset
1114     *                      augmentations.
1115     *
1116     * @throws XNIException Thrown by handler to signal an error.
1117     */
1118    public void externalEntityDecl(String name, XMLResourceIdentifier identifier,
1119                                   Augmentations augs) throws XNIException {
1120
1121        DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar:  fGrammarBucket.getActiveGrammar());
1122        int index = grammar.getEntityDeclIndex(name) ;
1123
1124        //If the same entity is declared more than once, the first declaration
1125        //encountered is binding, SAX requires only effective(first) declaration
1126        //to be reported to the application
1127
1128        //REVISIT: Does it make sense to pass duplicate entity information across
1129        //the pipeline -- nb?
1130
1131        //its a new entity and hasn't been declared.
1132        if(index == -1){
1133            //store external entity declaration in grammar
1134            if(fDTDGrammar != null)
1135                fDTDGrammar.externalEntityDecl(name, identifier, augs);
1136            // call handlers
1137            if (fDTDHandler != null) {
1138                fDTDHandler.externalEntityDecl(name, identifier, augs);
1139            }
1140        }
1141
1142    } // externalEntityDecl(String,XMLResourceIdentifier, Augmentations)
1143
1144    /**
1145     * An unparsed entity declaration.
1146     *
1147     * @param name     The name of the entity.
1148     * @param identifier    An object containing all location information
1149     *                      pertinent to this entity.
1150     * @param notation The name of the notation.
1151     * @param augs Additional information that may include infoset
1152     *                      augmentations.
1153     *
1154     * @throws XNIException Thrown by handler to signal an error.
1155     */
1156    public void unparsedEntityDecl(String name, XMLResourceIdentifier identifier,
1157                                   String notation,
1158                                   Augmentations augs) throws XNIException {
1159
1160        // VC: Notation declared,  in the production of NDataDecl
1161        if (fValidation) {
1162            fNDataDeclNotations.put(name, notation);
1163        }
1164
1165        // call handlers
1166        if(fDTDGrammar != null)
1167            fDTDGrammar.unparsedEntityDecl(name, identifier, notation, augs);
1168        if (fDTDHandler != null) {
1169            fDTDHandler.unparsedEntityDecl(name, identifier, notation, augs);
1170        }
1171
1172    } // unparsedEntityDecl(String,XMLResourceIdentifier,String,Augmentations)
1173
1174    /**
1175     * A notation declaration
1176     *
1177     * @param name     The name of the notation.
1178     * @param identifier    An object containing all location information
1179     *                      pertinent to this notation.
1180     * @param augs Additional information that may include infoset
1181     *                      augmentations.
1182     *
1183     * @throws XNIException Thrown by handler to signal an error.
1184     */
1185    public void notationDecl(String name, XMLResourceIdentifier identifier,
1186                             Augmentations augs) throws XNIException {
1187
1188        // VC: Unique Notation Name
1189        if (fValidation) {
1190            DTDGrammar grammar = (fDTDGrammar != null ? fDTDGrammar : fGrammarBucket.getActiveGrammar());
1191            if (grammar.getNotationDeclIndex(name) != -1) {
1192                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1193                                           "UniqueNotationName",
1194                                           new Object[]{name},
1195                                           XMLErrorReporter.SEVERITY_ERROR);
1196            }
1197        }
1198
1199        // call handlers
1200        if(fDTDGrammar != null)
1201            fDTDGrammar.notationDecl(name, identifier, augs);
1202        if (fDTDHandler != null) {
1203            fDTDHandler.notationDecl(name, identifier, augs);
1204        }
1205
1206    } // notationDecl(String,XMLResourceIdentifier, Augmentations)
1207
1208    /**
1209     * The start of a conditional section.
1210     *
1211     * @param type The type of the conditional section. This value will
1212     *             either be CONDITIONAL_INCLUDE or CONDITIONAL_IGNORE.
1213     * @param augs Additional information that may include infoset
1214     *                      augmentations.
1215     *
1216     * @throws XNIException Thrown by handler to signal an error.
1217     *
1218     * @see #CONDITIONAL_INCLUDE
1219     * @see #CONDITIONAL_IGNORE
1220     */
1221    public void startConditional(short type, Augmentations augs) throws XNIException {
1222
1223        // set state
1224        fInDTDIgnore = type == XMLDTDHandler.CONDITIONAL_IGNORE;
1225
1226        // call handlers
1227        if(fDTDGrammar != null)
1228            fDTDGrammar.startConditional(type, augs);
1229        if (fDTDHandler != null) {
1230            fDTDHandler.startConditional(type, augs);
1231        }
1232
1233    } // startConditional(short)
1234
1235    /**
1236     * The end of a conditional section.
1237     *
1238     * @param augs Additional information that may include infoset
1239     *                      augmentations.
1240     *
1241     * @throws XNIException Thrown by handler to signal an error.
1242     */
1243    public void endConditional(Augmentations augs) throws XNIException {
1244
1245        // set state
1246        fInDTDIgnore = false;
1247
1248        // call handlers
1249        if(fDTDGrammar != null)
1250            fDTDGrammar.endConditional(augs);
1251        if (fDTDHandler != null) {
1252            fDTDHandler.endConditional(augs);
1253        }
1254
1255    } // endConditional()
1256
1257    /**
1258     * The end of the DTD.
1259     *
1260     * @param augs Additional information that may include infoset
1261     *                      augmentations.
1262     *
1263     * @throws XNIException Thrown by handler to signal an error.
1264     */
1265    public void endDTD(Augmentations augs) throws XNIException {
1266
1267
1268        // save grammar
1269        if(fDTDGrammar != null) {
1270            fDTDGrammar.endDTD(augs);
1271            if(fGrammarPool != null)
1272                fGrammarPool.cacheGrammars(XMLGrammarDescription.XML_DTD, new Grammar[] {fDTDGrammar});
1273        }
1274        if (fValidation) {
1275            DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar: fGrammarBucket.getActiveGrammar());
1276
1277            // VC : Notation Declared. for external entity declaration [Production 76].
1278            Iterator entities = fNDataDeclNotations.entrySet().iterator();
1279            while (entities.hasNext()) {
1280                Map.Entry entry = (Map.Entry) entities.next();
1281                String notation = (String) entry.getValue();
1282                if (grammar.getNotationDeclIndex(notation) == -1) {
1283                    String entity = (String) entry.getKey();
1284                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1285                                               "MSG_NOTATION_NOT_DECLARED_FOR_UNPARSED_ENTITYDECL",
1286                                               new Object[]{entity, notation},
1287                                               XMLErrorReporter.SEVERITY_ERROR);
1288                }
1289            }
1290
1291            // VC: Notation Attributes:
1292            //     all notation names in the (attribute) declaration must be declared.
1293            Iterator notationVals = fNotationEnumVals.entrySet().iterator();
1294            while (notationVals.hasNext()) {
1295                Map.Entry entry = (Map.Entry) notationVals.next();
1296                String notation = (String) entry.getKey();
1297                if (grammar.getNotationDeclIndex(notation) == -1) {
1298                    String attributeName = (String) entry.getValue();
1299                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1300                                               "MSG_NOTATION_NOT_DECLARED_FOR_NOTATIONTYPE_ATTRIBUTE",
1301                                               new Object[]{attributeName, notation},
1302                                               XMLErrorReporter.SEVERITY_ERROR);
1303                }
1304            }
1305
1306            // VC: No Notation on Empty Element
1307            // An attribute of type NOTATION must not be declared on an element declared EMPTY.
1308            Iterator elementsWithNotations = fTableOfNOTATIONAttributeNames.entrySet().iterator();
1309            while (elementsWithNotations.hasNext()) {
1310                Map.Entry entry = (Map.Entry) elementsWithNotations.next();
1311                String elementName = (String) entry.getKey();
1312                int elementIndex = grammar.getElementDeclIndex(elementName);
1313                if (grammar.getContentSpecType(elementIndex) == XMLElementDecl.TYPE_EMPTY) {
1314                    String attributeName = (String) entry.getValue();
1315                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1316                                               "NoNotationOnEmptyElement",
1317                                               new Object[]{elementName, attributeName},
1318                                               XMLErrorReporter.SEVERITY_ERROR);
1319                }
1320            }
1321
1322            // should be safe to release these references
1323            fTableOfIDAttributeNames = null;
1324            fTableOfNOTATIONAttributeNames = null;
1325
1326            // check whether each element referenced in a content model is declared
1327            if (fWarnOnUndeclaredElemdef) {
1328                checkDeclaredElements(grammar);
1329            }
1330        }
1331
1332        // call handlers
1333        if (fDTDHandler != null) {
1334            fDTDHandler.endDTD(augs);
1335        }
1336
1337    } // endDTD()
1338
1339    // sets the XMLDTDSource of this handler
1340    public void setDTDSource(XMLDTDSource source ) {
1341        fDTDSource = source;
1342    } // setDTDSource(XMLDTDSource)
1343
1344    // returns the XMLDTDSource of this handler
1345    public XMLDTDSource getDTDSource() {
1346        return fDTDSource;
1347    } // getDTDSource():  XMLDTDSource
1348
1349    //
1350    // XMLDTDContentModelHandler methods
1351    //
1352
1353    // sets the XMLContentModelDTDSource of this handler
1354    public void setDTDContentModelSource(XMLDTDContentModelSource source ) {
1355        fDTDContentModelSource = source;
1356    } // setDTDContentModelSource(XMLDTDContentModelSource)
1357
1358    // returns the XMLDTDSource of this handler
1359    public XMLDTDContentModelSource getDTDContentModelSource() {
1360        return fDTDContentModelSource;
1361    } // getDTDContentModelSource():  XMLDTDContentModelSource
1362
1363
1364    /**
1365     * The start of a content model. Depending on the type of the content
1366     * model, specific methods may be called between the call to the
1367     * startContentModel method and the call to the endContentModel method.
1368     *
1369     * @param elementName The name of the element.
1370     * @param augs Additional information that may include infoset
1371     *                      augmentations.
1372     *
1373     * @throws XNIException Thrown by handler to signal an error.
1374     */
1375    public void startContentModel(String elementName, Augmentations augs)
1376        throws XNIException {
1377
1378        if (fValidation) {
1379            fDTDElementDeclName = elementName;
1380            fMixedElementTypes.clear();
1381        }
1382
1383        // call handlers
1384        if(fDTDGrammar != null)
1385            fDTDGrammar.startContentModel(elementName, augs);
1386        if (fDTDContentModelHandler != null) {
1387            fDTDContentModelHandler.startContentModel(elementName, augs);
1388        }
1389
1390    } // startContentModel(String)
1391
1392    /**
1393     * A content model of ANY.
1394     *
1395     * @param augs Additional information that may include infoset
1396     *                      augmentations.
1397     *
1398     * @throws XNIException Thrown by handler to signal an error.
1399     *
1400     * @see #empty
1401     * @see #startGroup
1402     */
1403    public void any(Augmentations augs) throws XNIException {
1404        if(fDTDGrammar != null)
1405            fDTDGrammar.any(augs);
1406        if (fDTDContentModelHandler != null) {
1407            fDTDContentModelHandler.any(augs);
1408        }
1409    } // any()
1410
1411    /**
1412     * A content model of EMPTY.
1413     *
1414     * @param augs Additional information that may include infoset
1415     *                      augmentations.
1416     *
1417     * @throws XNIException Thrown by handler to signal an error.
1418     *
1419     * @see #any
1420     * @see #startGroup
1421     */
1422    public void empty(Augmentations augs) throws XNIException {
1423        if(fDTDGrammar != null)
1424            fDTDGrammar.empty(augs);
1425        if (fDTDContentModelHandler != null) {
1426            fDTDContentModelHandler.empty(augs);
1427        }
1428    } // empty()
1429
1430    /**
1431     * A start of either a mixed or children content model. A mixed
1432     * content model will immediately be followed by a call to the
1433     * <code>pcdata()</code> method. A children content model will
1434     * contain additional groups and/or elements.
1435     *
1436     * @param augs Additional information that may include infoset
1437     *                      augmentations.
1438     *
1439     * @throws XNIException Thrown by handler to signal an error.
1440     *
1441     * @see #any
1442     * @see #empty
1443     */
1444    public void startGroup(Augmentations augs) throws XNIException {
1445
1446        fMixed = false;
1447        // call handlers
1448        if(fDTDGrammar != null)
1449            fDTDGrammar.startGroup(augs);
1450        if (fDTDContentModelHandler != null) {
1451            fDTDContentModelHandler.startGroup(augs);
1452        }
1453
1454    } // startGroup()
1455
1456    /**
1457     * The appearance of "#PCDATA" within a group signifying a
1458     * mixed content model. This method will be the first called
1459     * following the content model's <code>startGroup()</code>.
1460     *
1461     * @param augs Additional information that may include infoset
1462     *                      augmentations.
1463     *
1464     * @throws XNIException Thrown by handler to signal an error.
1465     *
1466     * @see #startGroup
1467     */
1468    public void pcdata(Augmentations augs) {
1469        fMixed = true;
1470        if(fDTDGrammar != null)
1471            fDTDGrammar.pcdata(augs);
1472        if (fDTDContentModelHandler != null) {
1473            fDTDContentModelHandler.pcdata(augs);
1474        }
1475    } // pcdata()
1476
1477    /**
1478     * A referenced element in a mixed or children content model.
1479     *
1480     * @param elementName The name of the referenced element.
1481     * @param augs Additional information that may include infoset
1482     *                      augmentations.
1483     *
1484     * @throws XNIException Thrown by handler to signal an error.
1485     */
1486    public void element(String elementName, Augmentations augs) throws XNIException {
1487
1488        // check VC: No duplicate Types, in a single mixed-content declaration
1489        if (fMixed && fValidation) {
1490            if (fMixedElementTypes.contains(elementName)) {
1491                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1492                                           "DuplicateTypeInMixedContent",
1493                                           new Object[]{fDTDElementDeclName, elementName},
1494                                           XMLErrorReporter.SEVERITY_ERROR);
1495            }
1496            else {
1497                fMixedElementTypes.add(elementName);
1498            }
1499        }
1500
1501        // call handlers
1502        if(fDTDGrammar != null)
1503            fDTDGrammar.element(elementName, augs);
1504        if (fDTDContentModelHandler != null) {
1505            fDTDContentModelHandler.element(elementName, augs);
1506        }
1507
1508    } // childrenElement(String)
1509
1510    /**
1511     * The separator between choices or sequences of a mixed or children
1512     * content model.
1513     *
1514     * @param separator The type of children separator.
1515     * @param augs Additional information that may include infoset
1516     *                      augmentations.
1517     *
1518     * @throws XNIException Thrown by handler to signal an error.
1519     *
1520     * @see #SEPARATOR_CHOICE
1521     * @see #SEPARATOR_SEQUENCE
1522     */
1523    public void separator(short separator, Augmentations augs)
1524        throws XNIException {
1525
1526        // call handlers
1527        if(fDTDGrammar != null)
1528            fDTDGrammar.separator(separator, augs);
1529        if (fDTDContentModelHandler != null) {
1530            fDTDContentModelHandler.separator(separator, augs);
1531        }
1532
1533    } // separator(short)
1534
1535    /**
1536     * The occurrence count for a child in a children content model or
1537     * for the mixed content model group.
1538     *
1539     * @param occurrence The occurrence count for the last element
1540     *                   or group.
1541     * @param augs Additional information that may include infoset
1542     *                      augmentations.
1543     *
1544     * @throws XNIException Thrown by handler to signal an error.
1545     *
1546     * @see #OCCURS_ZERO_OR_ONE
1547     * @see #OCCURS_ZERO_OR_MORE
1548     * @see #OCCURS_ONE_OR_MORE
1549     */
1550    public void occurrence(short occurrence, Augmentations augs)
1551        throws XNIException {
1552
1553        // call handlers
1554        if(fDTDGrammar != null)
1555            fDTDGrammar.occurrence(occurrence, augs);
1556        if (fDTDContentModelHandler != null) {
1557            fDTDContentModelHandler.occurrence(occurrence, augs);
1558        }
1559
1560    } // occurrence(short)
1561
1562    /**
1563     * The end of a group for mixed or children content models.
1564     *
1565     * @param augs Additional information that may include infoset
1566     *                      augmentations.
1567     *
1568     * @throws XNIException Thrown by handler to signal an error.
1569     */
1570    public void endGroup(Augmentations augs) throws XNIException {
1571
1572        // call handlers
1573        if(fDTDGrammar != null)
1574            fDTDGrammar.endGroup(augs);
1575        if (fDTDContentModelHandler != null) {
1576            fDTDContentModelHandler.endGroup(augs);
1577        }
1578
1579    } // endGroup()
1580
1581    /**
1582     * The end of a content model.
1583     *
1584     * @param augs Additional information that may include infoset
1585     *                      augmentations.
1586     *
1587     * @throws XNIException Thrown by handler to signal an error.
1588     */
1589    public void endContentModel(Augmentations augs) throws XNIException {
1590
1591        // call handlers
1592        if(fDTDGrammar != null)
1593            fDTDGrammar.endContentModel(augs);
1594        if (fDTDContentModelHandler != null) {
1595            fDTDContentModelHandler.endContentModel(augs);
1596        }
1597
1598    } // endContentModel()
1599
1600    //
1601    // Private methods
1602    //
1603
1604    /**
1605     * Normalize the attribute value of a non CDATA default attribute
1606     * collapsing sequences of space characters (x20)
1607     *
1608     * @param value The value to normalize
1609     * @return Whether the value was changed or not.
1610     */
1611    private boolean normalizeDefaultAttrValue(XMLString value) {
1612
1613        boolean skipSpace = true; // skip leading spaces
1614        int current = value.offset;
1615        int end = value.offset + value.length;
1616        for (int i = value.offset; i < end; i++) {
1617            if (value.ch[i] == ' ') {
1618                if (!skipSpace) {
1619                    // take the first whitespace as a space and skip the others
1620                    value.ch[current++] = ' ';
1621                    skipSpace = true;
1622                }
1623                else {
1624                    // just skip it.
1625                }
1626            }
1627            else {
1628                // simply shift non space chars if needed
1629                if (current != i) {
1630                    value.ch[current] = value.ch[i];
1631                }
1632                current++;
1633                skipSpace = false;
1634            }
1635        }
1636        if (current != end) {
1637            if (skipSpace) {
1638                // if we finished on a space trim it
1639                current--;
1640            }
1641            // set the new value length
1642            value.length = current - value.offset;
1643            return true;
1644        }
1645        return false;
1646    }
1647
1648    protected boolean isValidNmtoken(String nmtoken) {
1649        return XMLChar.isValidNmtoken(nmtoken);
1650    } // isValidNmtoken(String):  boolean
1651
1652    protected boolean isValidName(String name) {
1653        return XMLChar.isValidName(name);
1654    } // isValidName(String):  boolean
1655
1656    /**
1657     * Checks that all elements referenced in content models have
1658     * been declared. This method calls out to the error handler
1659     * to indicate warnings.
1660     */
1661    private void checkDeclaredElements(DTDGrammar grammar) {
1662        int elementIndex = grammar.getFirstElementDeclIndex();
1663        XMLContentSpec contentSpec = new XMLContentSpec();
1664        while (elementIndex >= 0) {
1665            int type = grammar.getContentSpecType(elementIndex);
1666            if (type == XMLElementDecl.TYPE_CHILDREN || type == XMLElementDecl.TYPE_MIXED) {
1667                checkDeclaredElements(grammar,
1668                        elementIndex,
1669                        grammar.getContentSpecIndex(elementIndex),
1670                        contentSpec);
1671            }
1672            elementIndex = grammar.getNextElementDeclIndex(elementIndex);
1673        }
1674    }
1675
1676    /**
1677     * Does a recursive (if necessary) check on the specified element's
1678     * content spec to make sure that all children refer to declared
1679     * elements.
1680     */
1681    private void checkDeclaredElements(DTDGrammar grammar, int elementIndex,
1682            int contentSpecIndex, XMLContentSpec contentSpec) {
1683        grammar.getContentSpec(contentSpecIndex, contentSpec);
1684        if (contentSpec.type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
1685            String value = (String) contentSpec.value;
1686            if (value != null && grammar.getElementDeclIndex(value) == -1) {
1687                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1688                        "UndeclaredElementInContentSpec",
1689                        new Object[]{grammar.getElementDeclName(elementIndex).rawname, value},
1690                        XMLErrorReporter.SEVERITY_WARNING);
1691            }
1692        }
1693        // It's not a leaf, so we have to recurse its left and maybe right
1694        // nodes. Save both values before we recurse and trash the node.
1695        else if ((contentSpec.type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
1696                || (contentSpec.type == XMLContentSpec.CONTENTSPECNODE_SEQ)) {
1697            final int leftNode = ((int[])contentSpec.value)[0];
1698            final int rightNode = ((int[])contentSpec.otherValue)[0];
1699            //  Recurse on both children.
1700            checkDeclaredElements(grammar, elementIndex, leftNode, contentSpec);
1701            checkDeclaredElements(grammar, elementIndex, rightNode, contentSpec);
1702        }
1703        else if (contentSpec.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
1704                || contentSpec.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE
1705                || contentSpec.type == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE) {
1706            final int leftNode = ((int[])contentSpec.value)[0];
1707            checkDeclaredElements(grammar, elementIndex, leftNode, contentSpec);
1708        }
1709    }
1710
1711} // class XMLDTDProcessor
1712