Package org.apache.xerces.impl.dtd

Source Code of org.apache.xerces.impl.dtd.XMLDTDValidator

/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999,2000,2001 The Apache Software Foundation. 
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment: 
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.xerces.impl.dtd;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.validation.ValidationManager;
import org.apache.xerces.impl.msg.XMLMessageFormatter;

import org.apache.xerces.impl.validation.EntityState;
import org.apache.xerces.impl.dtd.models.ContentModelValidator;
import org.apache.xerces.impl.dv.dtd.DatatypeValidator;
import org.apache.xerces.impl.dv.dtd.DatatypeValidatorFactory;
import org.apache.xerces.impl.validation.GrammarPool;
import org.apache.xerces.impl.dv.dtd.DatatypeValidatorFactoryImpl;
import org.apache.xerces.impl.dv.dtd.ENTITYDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.IDDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.IDREFDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.ListDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.NOTATIONDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.InvalidDatatypeFacetException;
import org.apache.xerces.impl.dv.dtd.InvalidDatatypeValueException;

import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLChar;

import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.XMLDTDHandler;
import org.apache.xerces.xni.XMLDTDContentModelHandler;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLComponent;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.apache.xerces.xni.parser.XMLDocumentFilter;
import org.apache.xerces.xni.parser.XMLDTDFilter;
import org.apache.xerces.xni.parser.XMLDTDContentModelFilter;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.StringTokenizer;

/**
* The DTD validator. The validator implements a document
* filter: receiving document events from the scanner; validating
* the content and structure; augmenting the InfoSet, if applicable;
* and notifying the parser of the information resulting from the
* validation process.
* <p>
* This component requires the following features and properties from the
* component manager that uses it:
* <ul>
<li>http://xml.org/sax/features/namespaces</li>
<li>http://xml.org/sax/features/validation</li>
<li>http://apache.org/xml/features/validation/dynamic</li>
<li>http://apache.org/xml/properties/internal/symbol-table</li>
<li>http://apache.org/xml/properties/internal/error-reporter</li>
<li>http://apache.org/xml/properties/internal/grammar-pool</li>
<li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
* </ul>
*
* @author Eric Ye, IBM
* @author Stubs generated by DesignDoc on Mon Sep 11 11:10:57 PDT 2000
* @author Andy Clark, IBM
* @author Jeffrey Rodriguez IBM
*
* @version $Id: XMLDTDValidator.java,v 1.5 2001/12/14 20:48:51 lmartin Exp $
*/
public class XMLDTDValidator
implements XMLComponent,
XMLDocumentFilter, XMLDTDFilter, XMLDTDContentModelFilter {

    //
    // Constants
    //

    /** Top level scope (-1). */
    private static final int TOP_LEVEL_SCOPE = -1;

    // feature identifiers

    /** Feature identifier: namespaces. */
    protected static final String NAMESPACES =
    Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;

    /** Feature identifier: validation. */
    protected static final String VALIDATION =
    Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;

    /** Feature identifier: dynamic validation. */
    protected static final String DYNAMIC_VALIDATION =
    Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;

    /** Feature identifier: xml schema validation */
    protected static final String SCHEMA_VALIDATION =
    Constants.XERCES_FEATURE_PREFIX +Constants.SCHEMA_VALIDATION_FEATURE;

    // property identifiers

    /** Property identifier: symbol table. */
    protected static final String SYMBOL_TABLE =
    Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;

    /** Property identifier: error reporter. */
    protected static final String ERROR_REPORTER =
    Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;

    /** Property identifier: grammar pool. */
    protected static final String GRAMMAR_POOL =
    Constants.XERCES_PROPERTY_PREFIX + Constants.GRAMMAR_POOL_PROPERTY;

    /** Property identifier: datatype validator factory. */
    protected static final String DATATYPE_VALIDATOR_FACTORY =
    Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY;


    protected static final String VALIDATION_MANAGER =
    Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
    // recognized features and properties

    /** Recognized features. */
    protected static final String[] RECOGNIZED_FEATURES = {
        NAMESPACES,
        VALIDATION,
        DYNAMIC_VALIDATION,
        SCHEMA_VALIDATION
    };

    /** Recognized properties. */
    protected static final String[] RECOGNIZED_PROPERTIES = {
        SYMBOL_TABLE,      
        ERROR_REPORTER,
        GRAMMAR_POOL,      
        DATATYPE_VALIDATOR_FACTORY,
        VALIDATION_MANAGER
    };

    // debugging

    /** Compile to true to debug attributes. */
    private static final boolean DEBUG_ATTRIBUTES = false;

    /** Compile to true to debug element children. */
    private static final boolean DEBUG_ELEMENT_CHILDREN = false;

    //       
    // Data
    //

    // updated during reset
    protected ValidationManager fValidationManager = null;

    // features

    /** Namespaces. */
    protected boolean fNamespaces;

    /** Validation. */
    protected boolean fValidation;

    /** Validation against only DTD */
    protected boolean fDTDValidation;

    /**
     * Dynamic validation. This state of this feature is only useful when
     * the validation feature is set to <code>true</code>.
     */
    protected boolean fDynamicValidation;

    // properties

    /** Symbol table. */
    protected SymbolTable fSymbolTable;

    /** Error reporter. */
    protected XMLErrorReporter fErrorReporter;

    /** Grammar pool. */
    protected GrammarPool fGrammarPool;

    /** Datatype validator factory. */
    protected DatatypeValidatorFactory fDatatypeValidatorFactory;

    // handlers

    /** Document handler. */
    protected XMLDocumentHandler fDocumentHandler;

    /** DTD handler. */
    protected XMLDTDHandler fDTDHandler;

    /** DTD content model handler. */
    protected XMLDTDContentModelHandler fDTDContentModelHandler;

    // grammars

    /** DTD Grammar. */
    protected DTDGrammar fDTDGrammar;

    // state

    /** Perform validation. */
    private boolean fPerformValidation;

    /** Skip validation. */
    private boolean fSkipValidation;

    /** True if in an ignore conditional section of the DTD. */
    protected boolean fInDTDIgnore;

    // information regarding the current element

    /** Current element name. */
    private final QName fCurrentElement = new QName();

    /** Current element index. */
    private int fCurrentElementIndex = -1;

    /** Current content spec type. */
    private int fCurrentContentSpecType = -1;

    /** The root element name. */
    private final QName fRootElement = new QName();

    /** True if seen DOCTYPE declaration. */
    private boolean fSeenDoctypeDecl = false;

    private boolean fInCDATASection = false;
    // element stack

    /** Element index stack. */
    private int[] fElementIndexStack = new int[8];

    /** Content spec type stack. */
    private int[] fContentSpecTypeStack = new int[8];

    /** Element name stack. */
    private QName[] fElementQNamePartsStack = new QName[8];

    // children list and offset stack

    /**
     * Element children. This data structure is a growing stack that
     * holds the children of elements from the root to the current
     * element depth. This structure never gets "deeper" than the
     * deepest element. Space is re-used once each element is closed.
     * <p>
     * <strong>Note:</strong> This is much more efficient use of memory
     * than creating new arrays for each element depth.
     * <p>
     * <strong>Note:</strong> The use of this data structure is for
     * validation "on the way out". If the validation model changes to
     * "on the way in", then this data structure is not needed.
     */
    private QName[] fElementChildren = new QName[32];

    /** Element children count. */
    private int fElementChildrenLength = 0;

    /**
     * Element children offset stack. This stack refers to offsets
     * into the <code>fElementChildren</code> array.
     * @see #fElementChildren
     */
    private int[] fElementChildrenOffsetStack = new int[32];

    /** Element depth. */
    private int fElementDepth = -1;

    // validation states

    /** Validation of a standalone document. */
    private boolean fStandaloneIsYes = false;

    /** True if seen the root element. */
    private boolean fSeenRootElement = false;

    /** True if inside of element content. */
    private boolean fInElementContent = false;

    /** Mixed. */
    private boolean fMixed;

    // temporary variables

    /** Temporary element declaration. */
    private XMLElementDecl fTempElementDecl = new XMLElementDecl();

    /** Temporary atribute declaration. */
    private XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl();

    /** Temporary entity declaration. */
    private XMLEntityDecl fEntityDecl = new XMLEntityDecl();

    /** Temporary qualified name. */
    private QName fTempQName = new QName();

    /** Temporary string buffer for buffering datatype value. */
    //private StringBuffer fDatatypeBuffer = new StringBuffer();

    /** Notation declaration hash. */
    private Hashtable fNDataDeclNotations = new Hashtable();

    /** DTD element declaration name. */
    private String fDTDElementDeclName = null;

    /** Mixed element type "hash". */
    private Vector fMixedElementTypes = new Vector();

    /** Element declarations in DTD. */
    private Vector fDTDElementDecls = new Vector();

    /** Temporary string buffers. */
    private StringBuffer fBuffer = new StringBuffer();

    // symbols: general

    /** Symbol: "EMPTY". */
    private String fEMPTYSymbol;

    /** Symbol: "ANY". */
    private String fANYSymbol;

    /** Symbol: "MIXED". */
    private String fMIXEDSymbol;

    /** Symbol: "CHILDREN". */
    private String fCHILDRENSymbol;

    // symbols: DTD datatype

    /** Symbol: "CDATA". */
    private String fCDATASymbol;

    /** Symbol: "ID". */
    private String fIDSymbol;

    /** Symbol: "IDREF". */
    private String fIDREFSymbol;

    /** Symbol: "IDREFS". */
    private String fIDREFSSymbol;

    /** Symbol: "ENTITY". */
    private String fENTITYSymbol;

    /** Symbol: "ENTITIES". */
    private String fENTITIESSymbol;

    /** Symbol: "NMTOKEN". */
    private String fNMTOKENSymbol;

    /** Symbol: "NMTOKENS". */
    private String fNMTOKENSSymbol;

    /** Symbol: "NOTATION". */
    private String fNOTATIONSymbol;

    /** Symbol: "ENUMERATION". */
    private String fENUMERATIONSymbol;

    /** Symbol: "#IMPLIED. */
    private String fIMPLIEDSymbol;

    /** Symbol: "#REQUIRED". */
    private String fREQUIREDSymbol;

    /** Symbol: "#FIXED". */
    private String fFIXEDSymbol;

    /** Symbol: "&lt;&lt;datatypes>>". */
    private String fDATATYPESymbol;

    // attribute validators

    /** Datatype validator: ID. */
    private IDDatatypeValidator fValID;

    /** Datatype validator: IDREF. */
    private IDREFDatatypeValidator fValIDRef;

    /** Datatype validator: IDREFS. */
    private ListDatatypeValidator fValIDRefs;

    /** Datatype validator: ENTITY. */
    private ENTITYDatatypeValidator fValENTITY;

    /** Datatype validator: ENTITIES. */
    private ListDatatypeValidator fValENTITIES;

    /** Datatype validator: NMTOKEN. */
    private DatatypeValidator fValNMTOKEN;

    /** Datatype validator: NMTOKENS. */
    private DatatypeValidator fValNMTOKENS;

    /** Datatype validator: NOTATION. */
    private NOTATIONDatatypeValidator fValNOTATION;

    /**
     * This table has to be own by instance of XMLValidator and shared
     * among ID, IDREF and IDREFS.
     * <p>
     * <strong>Note:</strong> Only ID has read/write access.
     * <p>
     * <strong>Note:</strong> Should revisit and replace with a ligther
     * structure.
     */
    private Hashtable fTableOfIDs;

    // to check for duplicate ID or ANNOTATION attribute declare in
    // ATTLIST, and misc VCs

    /** ID attribute names. */
    private Hashtable fTableOfIDAttributeNames;

    /** NOTATION attribute names. */
    private Hashtable fTableOfNOTATIONAttributeNames;

    /** NOTATION enumeration values. */
    private Hashtable fNotationEnumVals;

    //
    // Constructors
    //

    /** Default constructor. */
    public XMLDTDValidator() {

        // initialize data
        for (int i = 0; i < fElementQNamePartsStack.length; i++) {
            fElementQNamePartsStack[i] = new QName();
        }

    } // <init>()

    //
    // XMLComponent methods
    //

    /*
     * Resets the component. The component can query the component manager
     * about any features and properties that affect the operation of the
     * component.
     *
     * @param componentManager The component manager.
     *
     * @throws SAXException Thrown by component on finitialization error.
     *                      For example, if a feature or property is
     *                      required for the operation of the component, the
     *                      component manager may throw a
     *                      SAXNotRecognizedException or a
     *                      SAXNotSupportedException.
     */
    public void reset(XMLComponentManager componentManager)
    throws XMLConfigurationException {

        // clear grammars
        fDTDGrammar = null;
        fSeenDoctypeDecl = false;
        fInCDATASection = false;
        // initialize state
        fInDTDIgnore = false;
        fStandaloneIsYes = false;
        fSeenRootElement = false;
        fInElementContent = false;
        fCurrentElementIndex = -1;
        fCurrentContentSpecType = -1;
        fSkipValidation=false;

        fRootElement.clear();

        fNDataDeclNotations.clear();

        // sax features
        try {
            fNamespaces = componentManager.getFeature(NAMESPACES);
        }
        catch (XMLConfigurationException e) {
            fNamespaces = true;
        }
        try {
            fValidation = componentManager.getFeature(VALIDATION);
        }
        catch (XMLConfigurationException e) {
            fValidation = false;
        }
        try {
            fDTDValidation = !(componentManager.getFeature(SCHEMA_VALIDATION));
        }
        catch (XMLConfigurationException e) {
            fValidation = false;
        }

        // Xerces features
        try {
            fDynamicValidation = componentManager.getFeature(DYNAMIC_VALIDATION);
        }
        catch (XMLConfigurationException e) {
            fDynamicValidation = false;
        }

        fValidationManager= (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER);
        fValidationManager.reset();
        // get needed components
        fErrorReporter = (XMLErrorReporter)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.ERROR_REPORTER_PROPERTY);
        fSymbolTable = (SymbolTable)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.SYMBOL_TABLE_PROPERTY);
        fGrammarPool = (GrammarPool)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.GRAMMAR_POOL_PROPERTY);
        fDatatypeValidatorFactory = (DatatypeValidatorFactory)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY);

        fElementDepth = -1;                     
        init();

    } // reset(XMLComponentManager)

    /**
     * Returns a list of feature identifiers that are recognized by
     * this component. This method may return null if no features
     * are recognized by this component.
     */
    public String[] getRecognizedFeatures() {
        return RECOGNIZED_FEATURES;
    } // getRecognizedFeatures():String[]

    /**
     * Sets the state of a feature. This method is called by the component
     * manager any time after reset when a feature changes state.
     * <p>
     * <strong>Note:</strong> Components should silently ignore features
     * that do not affect the operation of the component.
     *
     * @param featureId The feature identifier.
     * @param state     The state of the feature.
     *
     * @throws SAXNotRecognizedException The component should not throw
     *                                   this exception.
     * @throws SAXNotSupportedException The component should not throw
     *                                  this exception.
     */
    public void setFeature(String featureId, boolean state)
    throws XMLConfigurationException {
    } // setFeature(String,boolean)

    /**
     * Returns a list of property identifiers that are recognized by
     * this component. This method may return null if no properties
     * are recognized by this component.
     */
    public String[] getRecognizedProperties() {
        return RECOGNIZED_PROPERTIES;
    } // getRecognizedProperties():String[]

    /**
     * Sets the value of a property. This method is called by the component
     * manager any time after reset when a property changes value.
     * <p>
     * <strong>Note:</strong> Components should silently ignore properties
     * that do not affect the operation of the component.
     *
     * @param propertyId The property identifier.
     * @param value      The value of the property.
     *
     * @throws SAXNotRecognizedException The component should not throw
     *                                   this exception.
     * @throws SAXNotSupportedException The component should not throw
     *                                  this exception.
     */
    public void setProperty(String propertyId, Object value)
    throws XMLConfigurationException {
    } // setProperty(String,Object)

    //
    // XMLDocumentSource methods
    //

    /**
     * Sets the document handler to receive information about the document.
     *
     * @param documentHandler The document handler.
     */
    public void setDocumentHandler(XMLDocumentHandler documentHandler) {
        fDocumentHandler = documentHandler;
    } // setDocumentHandler(XMLDocumentHandler)

    //
    // XMLDTDSource methods
    //

    /**
     * Sets the DTD handler.
     *
     * @param dtdHandler The DTD handler.
     */
    public void setDTDHandler(XMLDTDHandler dtdHandler) {
        fDTDHandler = dtdHandler;
    } // setDTDHandler(XMLDTDHandler)

    //
    // XMLDTDContentModelSource methods
    //

    /**
     * Sets the DTD content model handler.
     *
     * @param dtdContentModelHandler The DTD content model handler.
     */
    public void setDTDContentModelHandler(XMLDTDContentModelHandler dtdContentModelHandler) {
        fDTDContentModelHandler = dtdContentModelHandler;
    } // setDTDContentModelHandler(XMLDTDContentModelHandler)

    //
    // XMLDocumentHandler methods
    //

    /**
     * The start of the document.
     *
     * @param systemId The system identifier of the entity if the entity
     *                 is external, null otherwise.
     * @param encoding The auto-detected IANA encoding name of the entity
     *                 stream. This value will be null in those situations
     *                 where the entity encoding is not auto-detected (e.g.
     *                 internal entities or a document entity that is
     *                 parsed from a java.io.Reader).
     * @param augs   Additional information that may include infoset augmentations
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startDocument(XMLLocator locator, String encoding, Augmentations augs)
    throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.startDocument(locator, encoding, augs);
        }

    } // startDocument(XMLLocator,String)

    /**
     * Notifies of the presence of an XMLDecl line in the document. If
     * present, this method will be called immediately following the
     * startDocument call.
     *
     * @param version    The XML version.
     * @param encoding   The IANA encoding name of the document, or null if
     *                   not specified.
     * @param standalone The standalone value, or null if not specified.    
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
    throws XNIException {

        // save standalone state
        fStandaloneIsYes = standalone != null && standalone.equals("yes");

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
        }

    } // xmlDecl(String,String,String)

    /**
     * Notifies of the presence of the DOCTYPE line in the document.
     *
     * @param rootElement The name of the root element.
     * @param publicId    The public identifier if an external DTD or null
     *                    if the external DTD is specified using SYSTEM.
     * @param systemId    The system identifier if an external DTD, null
     *                    otherwise.    
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void doctypeDecl(String rootElement, String publicId, String systemId,
                            Augmentations augs)
    throws XNIException {

        // save root element state
        fSeenDoctypeDecl = true;
        fRootElement.setValues(null, rootElement, rootElement, null);

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
        }

    } // doctypeDecl(String,String,String)

    /**
     * The start of a namespace prefix mapping. This method will only be
     * called when namespace processing is enabled.
     *
     * @param prefix The namespace prefix.
     * @param uri    The URI bound to the prefix.    
     * @param augs   Additional information that may include infoset augmentations
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startPrefixMapping(String prefix, String uri, Augmentations augs)
    throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.startPrefixMapping(prefix, uri, augs);
        }

    } // startPrefixMapping(String,String)

    /**
     * The start of an element.
     *
     * @param element    The name of the element.
     * @param attributes The element attributes.    
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
    throws XNIException {

        handleStartElement(element, attributes);

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.startElement(element, attributes, augs);

        }

    } // startElement(QName,XMLAttributes)

    /**
     * An empty element.
     *
     * @param element    The name of the element.
     * @param attributes The element attributes.    
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
    throws XNIException {

        handleStartElement(element, attributes);

        if (fDocumentHandler !=null) {
            fDocumentHandler.emptyElement(element, attributes, augs);
        }

        handleEndElement(element, augs, true);

    } // emptyElement(QName,XMLAttributes)

    /**
     * Character content.
     *
     * @param text The content.
     *
     * @param augs   Additional information that may include infoset augmentations
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void characters(XMLString text, Augmentations augs) throws XNIException {

        boolean callNextCharacters = true;

        // REVISIT: [Q] Is there a more efficient way of doing this?
        //          Perhaps if the scanner told us so we don't have to
        //          look at the characters again. -Ac
        boolean allWhiteSpace = true;
        for (int i=text.offset; i< text.offset+text.length; i++) {
            if (!XMLChar.isSpace(text.ch[i])) {
                allWhiteSpace = false;
                break;
            }
        }
        // call the ignoreableWhiteSpace callback
        // never call ignorableWhitespace if we are in cdata section
        if (fInElementContent && allWhiteSpace && !fInCDATASection) {
            if (fDocumentHandler != null) {
                fDocumentHandler.ignorableWhitespace(text, augs);
                callNextCharacters = false;
            }
        }

        // validate
        if (fPerformValidation) {
            if (fInElementContent) {
                if (fStandaloneIsYes &&
                    fDTDGrammar.getElementDeclIsExternal(fCurrentElementIndex)) {
                    if (allWhiteSpace) {
                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
                                                    "MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE",
                                                    null, XMLErrorReporter.SEVERITY_ERROR);
                    }
                }
                if (!allWhiteSpace) {
                    charDataInContent();
                }
            }

            if (fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
                charDataInContent();
            }
        }

        // call handlers
        if (callNextCharacters && fDocumentHandler != null) {
            fDocumentHandler.characters(text, augs);
        }

    } // characters(XMLString)



    /**
     * Ignorable whitespace. For this method to be called, the document
     * source must have some way of determining that the text containing
     * only whitespace characters should be considered ignorable. For
     * example, the validator can determine if a length of whitespace
     * characters in the document are ignorable based on the element
     * content model.
     *
     * @param text The ignorable whitespace.
     *
     * @param augs   Additional information that may include infoset augmentations
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.ignorableWhitespace(text, augs);
        }

    } // ignorableWhitespace(XMLString)

    /**
     * The end of an element.
     *
     * @param element The name of the element.
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endElement(QName element, Augmentations augs) throws XNIException {

        handleEndElement(element,  augs, false);

    } // endElement(QName)

    /**
     * The end of a namespace prefix mapping. This method will only be
     * called when namespace processing is enabled.
     *
     * @param prefix The namespace prefix.
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endPrefixMapping(String prefix, Augmentations augs) throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.endPrefixMapping(prefix, augs);
        }

    } // endPrefixMapping(String)

    /**
     * The start of a CDATA section.
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startCDATA(Augmentations augs) throws XNIException {

        if (fPerformValidation && fInElementContent) {
            charDataInContent();
        }
        fInCDATASection = true;
        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.startCDATA(augs);
        }

    } // startCDATA()

    /**
     * The end of a CDATA section.
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endCDATA(Augmentations augs) throws XNIException {

        fInCDATASection = false;
        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.endCDATA(augs);
        }

    } // endCDATA()

    /**
     * The end of the document.
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endDocument(Augmentations augs) throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.endDocument(augs);
        }

    } // endDocument()

    //
    // XMLDocumentHandler and XMLDTDHandler methods
    //

    /**
     * This method notifies of the start of an entity. The DTD has the
     * pseudo-name of "[dtd]" parameter entity names start with '%'; and
     * general entity names are just the entity name.
     * <p>
     * <strong>Note:</strong> This method is not called for entity references
     * appearing as part of attribute values.
     *
     * @param name     The name of the entity.
     * @param publicId The public identifier of the entity if the entity
     *                 is external, null otherwise.
     * @param systemId The system identifier of the entity if the entity
     *                 is external, null otherwise.
     * @param baseSystemId The base system identifier of the entity if
     *                     the entity is external, null otherwise.
     * @param encoding The auto-detected IANA encoding name of the entity
     *                 stream. This value will be null in those situations
     *                 where the entity encoding is not auto-detected (e.g.
     *                 internal parameter entities).
     *
     * @param augs   Additional information that may include infoset augmentations
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startEntity(String name,
                            String publicId, String systemId,
                            String baseSystemId,
                            String encoding,
                            Augmentations augs) throws XNIException {

        // check VC: Standalone Document Declartion, entities references appear in the document.
        if (fPerformValidation && fDTDGrammar != null) {
            if (fStandaloneIsYes && !name.startsWith("[")) {
                int entIndex = fDTDGrammar.getEntityDeclIndex(name);
                if (entIndex > -1) {
                    fDTDGrammar.getEntityDecl(entIndex, fEntityDecl);
                    if (fEntityDecl.inExternal) {
                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
                                                    "MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
                                                    new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
                    }
                }
            }
        }

        if (fDocumentHandler != null) {
            fDocumentHandler.startEntity(name, publicId, systemId,
                                         baseSystemId, encoding, augs);
        }


    } // startEntity(String,String,String,String,String, Augmentations)


    /**
     * Notifies of the presence of a TextDecl line in an entity. If present,
     * this method will be called immediately following the startEntity call.
     * <p>
     * <strong>Note:</strong> This method will never be called for the
     * document entity; it is only called for external general entities
     * referenced in document content.
     * <p>
     * <strong>Note:</strong> This method is not called for entity references
     * appearing as part of attribute values.
     *
     * @param version  The XML version, or null if not specified.
     * @param encoding The IANA encoding name of the entity.
     * @param augs     Additional information that may include infoset augmentations
     *                
     * @exception XNIException
     *                   Thrown by handler to signal an error.
     */
    public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {

        if (fDocumentHandler != null) {
            fDocumentHandler.textDecl(version, encoding, augs);
        }

    } // textDecl(String,String)


    /**
     * A comment.
     *
     * @param text The text in the comment.
     *
     * @param augs   Additional information that may include infoset augmentations
     * @throws XNIException Thrown by application to signal an error.
     */
    public void comment(XMLString text, Augmentations augs) throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.comment(text, augs);
        }

    } // comment(XMLString)


    /**
     * A processing instruction. Processing instructions consist of a
     * target name and, optionally, text data. The data is only meaningful
     * to the application.
     * <p>
     * Typically, a processing instruction's data will contain a series
     * of pseudo-attributes. These pseudo-attributes follow the form of
     * element attributes but are <strong>not</strong> parsed or presented
     * to the application as anything other than text. The application is
     * responsible for parsing the data.
     *
     * @param target The target.
     * @param data   The data or null if none specified.    
     * @param augs   Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void processingInstruction(String target, XMLString data, Augmentations augs)
    throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.processingInstruction(target, data, augs);
        }
    } // processingInstruction(String,XMLString)


    /**
     * This method notifies the end of an entity.
     * <p>
     * <strong>Note:</strong> This method is not called for entity references
     * appearing as part of attribute values.
     *
     * @param name   The name of the entity.
     * @param augs   Additional information that may include infoset augmentations
     *              
     * @exception XNIException
     *                   Thrown by handler to signal an error.
     */
    public void endEntity(String name, Augmentations augs) throws XNIException {

        // call handlers
        if (fDocumentHandler != null) {
            fDocumentHandler.endEntity(name, augs);
        }

    } // endEntity(String)



    //
    // XMLDTDHandler methods
    //

    /**
     * The start of the DTD.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startDTD(XMLLocator locator) throws XNIException {

        // initialize state
        fNDataDeclNotations.clear();
        fDTDElementDecls.removeAllElements();

        // create DTD grammar
        fDTDGrammar = createDTDGrammar();
        //fDTDGrammar.setDatatypeValidatorFactory(fDatatypeValidatorFactory);
        // REVISIT: should we use the systemId as the key instead?
        fGrammarPool.putGrammar("", fDTDGrammar);

        // call handlers
        fDTDGrammar.startDTD(locator);
        if (fDTDHandler != null) {
            fDTDHandler.startDTD(locator);
        }

    } // startDTD(XMLLocator)

    /**
* A comment.
*
* @param text The text in the comment.
*
* @throws XNIException Thrown by application to signal an error.
*/
    public void comment(XMLString text) throws XNIException {

        // call handlers
        fDTDGrammar.comment(text);
        if (fDTDHandler != null) {
            fDTDHandler.comment(text);
        }
    } 

    /**
     * Character content.
     *
     * @param text The content.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void characters(XMLString text) throws XNIException {

        // ignored characters in DTD
        if (fDTDHandler != null) {
            fDTDHandler.characters(text);
        }
    }

    /**
     * Notifies of the presence of a TextDecl line in an entity. If present,
     * this method will be called immediately following the startEntity call.
     * <p>
     * <strong>Note:</strong> This method is only called for external
     * parameter entities referenced in the DTD.
     *
     * @param version  The XML version, or null if not specified.
     * @param encoding The IANA encoding name of the entity.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void textDecl(String version, String encoding) throws XNIException {

        // call handlers
        fDTDGrammar.textDecl(version, encoding);
        if (fDTDHandler != null) {
            fDTDHandler.textDecl(version, encoding);
        }
    }

    /**
     * This method notifies of the start of an entity. The DTD has the
     * pseudo-name of "[dtd]" and parameter entity names start with '%'.
     *
     * @param name     The name of the entity.
     * @param publicId The public identifier of the entity if the entity
     *                 is external, null otherwise.
     * @param systemId The system identifier of the entity if the entity
     *                 is external, null otherwise.
     * @param baseSystemId The base system identifier of the entity if
     *                     the entity is external, null otherwise.
     * @param encoding The auto-detected IANA encoding name of the entity
     *                 stream. This value will be null in those situations
     *                 where the entity encoding is not auto-detected (e.g.
     *                 internal parameter entities).
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startEntity(String name,
                            String publicId, String systemId,
                            String baseSystemId,
                            String encoding) throws XNIException {

        // call handlers
        fDTDGrammar.startEntity(name, publicId, systemId,
                                baseSystemId, encoding);
        if (fDTDHandler != null) {
            fDTDHandler.startEntity(name, publicId, systemId,
                                    baseSystemId, encoding);
        }
    }
    /**
     * This method notifies the end of an entity. The DTD has the pseudo-name
     * of "[dtd]" and parameter entity names start with '%'.
     *
     * @param name The name of the entity.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endEntity(String name) throws XNIException {

        // call handlers
        fDTDGrammar.endEntity(name);
        if (fDTDHandler != null) {
            fDTDHandler.endEntity(name);
        }
    }

    /**
     * A processing instruction. Processing instructions consist of a
     * target name and, optionally, text data. The data is only meaningful
     * to the application.
     * <p>
     * Typically, a processing instruction's data will contain a series
     * of pseudo-attributes. These pseudo-attributes follow the form of
     * element attributes but are <strong>not</strong> parsed or presented
     * to the application as anything other than text. The application is
     * responsible for parsing the data.
     *
     * @param target The target.
     * @param data   The data or null if none specified.
     * @exception XNIException
     *                   Thrown by handler to signal an error.
     */
    public void processingInstruction(String target, XMLString data)
    throws XNIException {

        // call handlers
        fDTDGrammar.processingInstruction(target, data);
        if (fDTDHandler != null) {
            fDTDHandler.processingInstruction(target, data);
        }
    }
    /**
     * An element declaration.
     *
     * @param name         The name of the element.
     * @param contentModel The element content model.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void elementDecl(String name, String contentModel)
    throws XNIException {

        //check VC: Unique Element Declaration
        if (fValidation) {
            if (fDTDElementDecls.contains(name)) {
                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                           "MSG_ELEMENT_ALREADY_DECLARED",
                                           new Object[]{ name},
                                           XMLErrorReporter.SEVERITY_ERROR);
            }
            else {
                fDTDElementDecls.addElement(name);
            }
        }

        // call handlers
        fDTDGrammar.elementDecl(name, contentModel);
        if (fDTDHandler != null) {
            fDTDHandler.elementDecl(name, contentModel);
        }

    } // elementDecl(String,String)

    /**
     * The start of an attribute list.
     *
     * @param elementName The name of the element that this attribute
     *                    list is associated with.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startAttlist(String elementName) throws XNIException {

        // call handlers
        fDTDGrammar.startAttlist(elementName);
        if (fDTDHandler != null) {
            fDTDHandler.startAttlist(elementName);
        }

    } // startAttlist(String)

    /**
     * An attribute declaration.
     *
     * @param elementName   The name of the element that this attribute
     *                      is associated with.
     * @param attributeName The name of the attribute.
     * @param type          The attribute type. This value will be one of
     *                      the following: "CDATA", "ENTITY", "ENTITIES",
     *                      "ENUMERATION", "ID", "IDREF", "IDREFS",
     *                      "NMTOKEN", "NMTOKENS", or "NOTATION".
     * @param enumeration   If the type has the value "ENUMERATION", this
     *                      array holds the allowed attribute values;
     *                      otherwise, this array is null.
     * @param defaultType   The attribute default type. This value will be
     *                      one of the following: "#FIXED", "#IMPLIED",
     *                      "#REQUIRED", or null.
     * @param defaultValue  The attribute default value, or null if no
     *                      default value is specified.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void attributeDecl(String elementName, String attributeName,
                              String type, String[] enumeration,
                              String defaultType, XMLString defaultValue)
    throws XNIException {

        if (type != fCDATASymbol && defaultValue != null) {
            normalizeDefaultAttrValue(defaultValue);
        }

        if (fValidation) {
            //
            // a) VC: One ID per Element Type, If duplicate ID attribute
            // b) VC: ID attribute Default. if there is a declareared attribute
            //        default for ID it should be of type #IMPLIED or #REQUIRED
            if (type == fIDSymbol) {
                if (defaultValue != null && defaultValue.length != 0) {
                    if (defaultType == null ||
                        !(defaultType == fIMPLIEDSymbol ||
                          defaultType == fREQUIREDSymbol)) {
                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                   "IDDefaultTypeInvalid",
                                                   new Object[]{ attributeName},
                                                   XMLErrorReporter.SEVERITY_ERROR);
                    }
                }

                if (!fTableOfIDAttributeNames.containsKey(elementName)) {
                    fTableOfIDAttributeNames.put(elementName, attributeName);
                }
                else {
                    String previousIDAttributeName = (String)fTableOfIDAttributeNames.get( elementName );//rule a)
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_MORE_THAN_ONE_ID_ATTRIBUTE",
                                               new Object[]{ elementName, previousIDAttributeName, attributeName},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
            }

            //
            //  VC: One Notaion Per Element Type, should check if there is a
            //      duplicate NOTATION attribute

            if (type == fNOTATIONSymbol) {
                // VC: Notation Attributes: all notation names in the
                //     (attribute) declaration must be declared.
                for (int i=0; i<enumeration.length; i++) {
                    fNotationEnumVals.put(enumeration[i], attributeName);
                }

                if (fTableOfNOTATIONAttributeNames.containsKey( elementName ) == false) {
                    fTableOfNOTATIONAttributeNames.put( elementName, attributeName);
                }
                else {
                    String previousNOTATIONAttributeName = (String) fTableOfNOTATIONAttributeNames.get( elementName );
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_MORE_THAN_ONE_NOTATION_ATTRIBUTE",
                                               new Object[]{ elementName, previousNOTATIONAttributeName, attributeName},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
            }

            // VC: Attribute Default Legal
            boolean ok = true;
            if (defaultValue != null &&
                (defaultType == null ||
                 (defaultType != null && defaultType == fFIXEDSymbol))) {

                String value = defaultValue.toString();
                if (type == fNMTOKENSSymbol ||
                    type == fENTITIESSymbol || type == fIDREFSSymbol) {

                    StringTokenizer tokenizer = new StringTokenizer(value);
                    if (tokenizer.hasMoreTokens()) {
                        while (true) {
                            String nmtoken = tokenizer.nextToken();
                            if (type == fNMTOKENSSymbol) {
                                if (!XMLChar.isValidNmtoken(nmtoken)) {
                                    ok = false;
                                    break;
                                }
                            }
                            else if (type == fENTITIESSymbol ||
                                     type == fIDREFSSymbol) {
                                if (!XMLChar.isValidName(nmtoken)) {
                                    ok = false;
                                    break;
                                }
                            }
                            if (!tokenizer.hasMoreTokens()) {
                                break;
                            }
                        }
                    }

                }
                else {
                    if (type == fENTITYSymbol ||
                        type == fIDSymbol ||
                        type == fIDREFSymbol ||
                        type == fNOTATIONSymbol) {

                        if (!XMLChar.isValidName(value)) {
                            ok = false;
                        }

                    }
                    else if (type == fNMTOKENSymbol ||
                             type == fENUMERATIONSymbol) {

                        if (!XMLChar.isValidNmtoken(value)) {
                            ok = false;
                        }
                    }

                    if (type == fNOTATIONSymbol ||
                        type == fENUMERATIONSymbol) {
                        ok = false;
                        for (int i=0; i<enumeration.length; i++) {
                            if (defaultValue.equals(enumeration[i])) {
                                ok = true;
                            }
                        }
                    }

                }
                if (!ok) {
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_ATT_DEFAULT_INVALID",
                                               new Object[]{attributeName, value},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
            }
        }

        // call handlers
        fDTDGrammar.attributeDecl(elementName, attributeName,
                                  type, enumeration,
                                  defaultType, defaultValue);
        if (fDTDHandler != null) {
            fDTDHandler.attributeDecl(elementName, attributeName,
                                      type, enumeration,
                                      defaultType, defaultValue);
        }

    } // attributeDecl(String,String,String,String[],String,XMLString)

    /**
     * The end of an attribute list.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endAttlist() throws XNIException {

        // call handlers
        fDTDGrammar.endAttlist();
        if (fDTDHandler != null) {
            fDTDHandler.endAttlist();
        }

    } // endAttlist()

    /**
     * An internal entity declaration.
     *
     * @param name The name of the entity. Parameter entity names start with
     *             '%', whereas the name of a general entity is just the
     *             entity name.
     * @param text The value of the entity.
     * @param nonNormalizedText The non-normalized value of the entity. This
     *             value contains the same sequence of characters that was in
     *             the internal entity declaration, without any entity
     *             references expanded.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void internalEntityDecl(String name, XMLString text,
                                   XMLString nonNormalizedText)
    throws XNIException {

        // call handlers
        fDTDGrammar.internalEntityDecl(name, text, nonNormalizedText);
        if (fDTDHandler != null) {
            fDTDHandler.internalEntityDecl(name, text, nonNormalizedText);
        }

    } // internalEntityDecl(String,XMLString,XMLString)

    /**
     * An external entity declaration.
     *
     * @param name     The name of the entity. Parameter entity names start
     *                 with '%', whereas the name of a general entity is just
     *                 the entity name.
     * @param publicId The public identifier of the entity or null if the
     *                 the entity was specified with SYSTEM.
     * @param systemId The system identifier of the entity.
     * @param baseSystemId The base system identifier of where the entity
     *                     is declared.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void externalEntityDecl(String name,
                                   String publicId, String systemId,
                                   String baseSystemId) throws XNIException {

        // call handlers
        fDTDGrammar.externalEntityDecl(name, publicId, systemId, baseSystemId);
        if (fDTDHandler != null) {
            fDTDHandler.externalEntityDecl(name, publicId, systemId,
                                           baseSystemId);
        }

    } // externalEntityDecl(String,String,String,String)

    /**
     * An unparsed entity declaration.
     *
     * @param name     The name of the entity.
     * @param publicId The public identifier of the entity, or null if not
     *                 specified.
     * @param systemId The system identifier of the entity, or null if not
     *                 specified.
     * @param notation The name of the notation.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void unparsedEntityDecl(String name,
                                   String publicId, String systemId,
                                   String notation) throws XNIException {

        // VC: Notation declared,  in the production of NDataDecl
        if (fValidation) {
            fNDataDeclNotations.put(name, notation);
        }

        // call handlers
        fDTDGrammar.unparsedEntityDecl(name, publicId, systemId, notation);
        if (fDTDHandler != null) {
            fDTDHandler.unparsedEntityDecl(name, publicId, systemId, notation);
        }

    } // unparsedEntityDecl(String,String,String,String)

    /**
     * A notation declaration
     *
     * @param name     The name of the notation.
     * @param publicId The public identifier of the notation, or null if not
     *                 specified.
     * @param systemId The system identifier of the notation, or null if not
     *                 specified.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void notationDecl(String name, String publicId, String systemId)
    throws XNIException {

        // call handlers
        fDTDGrammar.notationDecl(name, publicId, systemId);
        if (fDTDHandler != null) {
            fDTDHandler.notationDecl(name, publicId, systemId);
        }

    } // notationDecl(String,String,String)

    /**
     * The start of a conditional section.
     *
     * @param type The type of the conditional section. This value will
     *             either be CONDITIONAL_INCLUDE or CONDITIONAL_IGNORE.
     *
     * @throws XNIException Thrown by handler to signal an error.
     *
     * @see CONDITIONAL_INCLUDE
     * @see CONDITIONAL_IGNORE
     */
    public void startConditional(short type) throws XNIException {

        // set state
        fInDTDIgnore = type == XMLDTDHandler.CONDITIONAL_IGNORE;

        // call handlers
        fDTDGrammar.startConditional(type);
        if (fDTDHandler != null) {
            fDTDHandler.startConditional(type);
        }

    } // startConditional(short)

    /**
     * The end of a conditional section.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endConditional() throws XNIException {

        // set state
        fInDTDIgnore = false;

        // call handlers
        fDTDGrammar.endConditional();
        if (fDTDHandler != null) {
            fDTDHandler.endConditional();
        }

    } // endConditional()

    /**
     * The end of the DTD.
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endDTD() throws XNIException {

        // save grammar
        fDTDGrammar.endDTD();

        // check VC: Notation declared,  in the production of NDataDecl
        if (fValidation) {

            fValENTITY.initialize(fDTDGrammar);//Initialize ENTITY, ENTITIES validators
            fValENTITIES.initialize(fDTDGrammar);

            // VC : Notation Declared. for external entity declaration [Production 76].
            Enumeration entities = fNDataDeclNotations.keys();
            while (entities.hasMoreElements()) {
                String entity = (String) entities.nextElement();
                String notation = (String) fNDataDeclNotations.get(entity);
                if (fDTDGrammar.getNotationDeclIndex(notation) == -1) {
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_NOTATION_NOT_DECLARED_FOR_UNPARSED_ENTITYDECL",
                                               new Object[]{entity, notation},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
            }

            // VC: Notation Attributes:
            //     all notation names in the (attribute) declaration must be declared.
            Enumeration notationVals = fNotationEnumVals.keys();
            while (notationVals.hasMoreElements()) {
                String notation = (String) notationVals.nextElement();
                String attributeName = (String) fNotationEnumVals.get(notation);
                if (fDTDGrammar.getNotationDeclIndex(notation) == -1) {
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_NOTATION_NOT_DECLARED_FOR_NOTATIONTYPE_ATTRIBUTE",
                                               new Object[]{attributeName, notation},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
            }

            fTableOfIDAttributeNames = null;//should be safe to release these references
            fTableOfNOTATIONAttributeNames = null;
        }

        // call handlers
        if (fDTDHandler != null) {
            fDTDHandler.endDTD();
        }

    } // endDTD()

    //
    // XMLDTDContentModelHandler methods
    //

    /** Start content model. */
    public void startContentModel(String elementName) throws XNIException {

        if (fValidation) {
            fDTDElementDeclName = elementName;
            fMixedElementTypes.removeAllElements();
        }

        // call handlers
        fDTDGrammar.startContentModel(elementName);
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.startContentModel(elementName);
        }

    } // startContentModel(String)

    /** ANY. */
    public void any() throws XNIException {
        fDTDGrammar.any();
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.any();
        }
    } // any()

    /** EMPTY. */
    public void empty() throws XNIException {
        fDTDGrammar.empty();
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.empty();
        }
    } // empty()

    /** Start group. */
    public void startGroup() throws XNIException {

        fMixed = false;
        // call handlers
        fDTDGrammar.startGroup();
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.startGroup();
        }

    } // startGroup()

    /** #PCDATA. */
    public void pcdata() {
        fMixed = true;
        fDTDGrammar.pcdata();
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.pcdata();
        }
    } // pcdata()

    /** Element. */
    public void element(String elementName) throws XNIException {

        // check VC: No duplicate Types, in a single mixed-content declaration
        if (fMixed && fValidation) {
            if (fMixedElementTypes.contains(elementName)) {
                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                           "DuplicateTypeInMixedContent",
                                           new Object[]{fDTDElementDeclName, elementName},
                                           XMLErrorReporter.SEVERITY_ERROR);
            }
            else {
                fMixedElementTypes.addElement(elementName);
            }
        }

        // call handlers
        fDTDGrammar.element(elementName);
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.element(elementName);
        }

    } // childrenElement(String)

    /** Separator. */
    public void separator(short separator) throws XNIException {

        // call handlers
        fDTDGrammar.separator(separator);
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.separator(separator);
        }

    } // separator(short)

    /** Occurrence. */
    public void occurrence(short occurrence) throws XNIException {

        // call handlers
        fDTDGrammar.occurrence(occurrence);
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.occurrence(occurrence);
        }

    } // occurrence(short)

    /** End group. */
    public void endGroup() throws XNIException {

        // call handlers
        fDTDGrammar.endGroup();
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.endGroup();
        }

    } // endGroup()

    /** End content model. */
    public void endContentModel() throws XNIException {

        // call handlers
        fDTDGrammar.endContentModel();
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.endContentModel();
        }

    } // endContentModel()

    //
    // Private methods
    //

    /** Add default attributes and validate. */
    private void addDTDDefaultAttrsAndValidate(int elementIndex,
                                               XMLAttributes attributes)
    throws XNIException {

        // is there anything to do?
        if (elementIndex == -1 || fDTDGrammar == null) {
            return;
        }

        // get element info
        fDTDGrammar.getElementDecl(elementIndex,fTempElementDecl);
        QName element = fTempElementDecl.name;

        //
        // Check after all specified attrs are scanned
        // (1) report error for REQUIRED attrs that are missing (V_TAGc)
        // (2) add default attrs (FIXED and NOT_FIXED)
        //
        int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);

        while (attlistIndex != -1) {

            fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);

            if (DEBUG_ATTRIBUTES) {
                if (fTempAttDecl != null) {
                    XMLElementDecl elementDecl = new XMLElementDecl();
                    fDTDGrammar.getElementDecl(elementIndex, elementDecl);
                    System.out.println("element: "+(elementDecl.name.localpart));
                    System.out.println("attlistIndex " + attlistIndex + "\n"+
                                       "attName : '"+(fTempAttDecl.name.localpart) + "'\n"
                                       + "attType : "+fTempAttDecl.simpleType.type + "\n"
                                       + "attDefaultType : "+fTempAttDecl.simpleType.defaultType + "\n"
                                       + "attDefaultValue : '"+fTempAttDecl.simpleType.defaultValue + "'\n"
                                       + attributes.getLength() +"\n"
                                      );
                }
            }
            String attPrefix = fTempAttDecl.name.prefix;
            String attLocalpart = fTempAttDecl.name.localpart;
            String attRawName = fTempAttDecl.name.rawname;
            String attType = getAttributeTypeName(fTempAttDecl);
            int attDefaultType =fTempAttDecl.simpleType.defaultType;
            String attValue = null;

            if (fTempAttDecl.simpleType.defaultValue != null) {
                attValue = fTempAttDecl.simpleType.defaultValue;
            }
            boolean specified = false;
            boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED;
            boolean cdata = attType == fCDATASymbol;

            if (!cdata || required || attValue != null) {
                int attrCount = attributes.getLength();
                for (int i = 0; i < attrCount; i++) {
                    if (attributes.getQName(i) == attRawName) {
                        specified = true;
                        break;
                    }
                }
            }

            if (!specified) {
                if (required) {
                    if (fValidation) {
                        Object[] args = {element.localpart, attRawName};
                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                   "MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED", args,
                                                   XMLErrorReporter.SEVERITY_ERROR);
                    }
                }
                else if (attValue != null) {
                    if (fPerformValidation && fStandaloneIsYes) {
                        if (fDTDGrammar.getAttributeDeclIsExternal(attlistIndex)) {

                            Object[] args = { element.localpart, attRawName};
                            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                       "MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED", args,
                                                       XMLErrorReporter.SEVERITY_ERROR);
                        }
                    }

                    // add namespace information
                    if (fNamespaces) {
                        int index = attRawName.indexOf(':');
                        if (index != -1) {
                            attPrefix = attRawName.substring(0, index);
                            attPrefix = fSymbolTable.addSymbol(attPrefix);
                            attLocalpart = attRawName.substring(index + 1);
                            attLocalpart = fSymbolTable.addSymbol(attLocalpart);
                        }
                    }

                    // add attribute
                    fTempQName.setValues(attPrefix, attLocalpart, attRawName, fTempAttDecl.name.uri);
                    int newAttr = attributes.addAttribute(fTempQName, attType, attValue);
                }
            }
            // get next att decl in the Grammar for this element
            attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex);
        }

        // now iterate through the expanded attributes for
        // 1. if every attribute seen is declared in the DTD
        // 2. check if the VC: default_fixed holds
        // 3. validate every attribute.
        int attrCount = attributes.getLength();
        for (int i = 0; i < attrCount; i++) {
            String attrRawName = attributes.getQName(i);
            boolean declared = false;
            if (fPerformValidation) {
                if (fStandaloneIsYes) {
                    // check VC: Standalone Document Declaration, entities
                    // references appear in the document.
                    // REVISIT: this can be combined to a single check in
                    // startEntity if we add one more argument in
                    // startEnity, inAttrValue
                    String nonNormalizedValue = attributes.getNonNormalizedValue(i);
                    if (nonNormalizedValue != null) {
                        String entityName = getExternalEntityRefInAttrValue(nonNormalizedValue);
                        if (entityName != null) {
                            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                       "MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
                                                       new Object[]{entityName},
                                                       XMLErrorReporter.SEVERITY_ERROR);
                        }
                    }
                }
            }
            int attDefIndex = -1;
            int position =
            fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
            while (position != -1) {
                fDTDGrammar.getAttributeDecl(position, fTempAttDecl);
                if (fTempAttDecl.name.rawname == attrRawName) {
                    // found the match att decl,
                    attDefIndex = position;
                    declared = true;
                    break;
                }
                position = fDTDGrammar.getNextAttributeDeclIndex(position);
            }
            if (!declared) {
                if (fPerformValidation) {
                    // REVISIT - cache the elem/attr tuple so that we only
                    // give this error once for each unique occurrence
                    Object[] args = { element.rawname, attrRawName};

                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_ATTRIBUTE_NOT_DECLARED",
                                               args,XMLErrorReporter.SEVERITY_ERROR);  
                }
                continue;
            }
            // attribute is declared

            // fTempAttDecl should have the right value set now, so
            // the following is not needed
            // fGrammar.getAttributeDecl(attDefIndex,fTempAttDecl);

            String type = getAttributeTypeName(fTempAttDecl);
            attributes.setType(i, type);

            boolean changedByNormalization = false;
            String oldValue = attributes.getValue(i);
            String attrValue = oldValue;
            if (attributes.isSpecified(i) && type != fCDATASymbol) {
                changedByNormalization = normalizeAttrValue(attributes, i);
                attrValue = attributes.getValue(i);
                if (fPerformValidation && fStandaloneIsYes
                    && changedByNormalization
                    && fDTDGrammar.getAttributeDeclIsExternal(position)
                   ) {
                    // check VC: Standalone Document Declaration
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE",
                                               new Object[]{attrRawName, oldValue, attrValue},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
            }
            if (!fPerformValidation) {
                continue;
            }
            if (fTempAttDecl.simpleType.defaultType ==
                XMLSimpleType.DEFAULT_TYPE_FIXED) {
                String defaultValue = fTempAttDecl.simpleType.defaultValue;

                if (!attrValue.equals(defaultValue)) {
                    Object[] args = {element.localpart,
                        attrRawName,
                        attrValue,
                        defaultValue};
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_FIXED_ATTVALUE_INVALID",
                                               args, XMLErrorReporter.SEVERITY_ERROR);
                }
            }

            if (fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENTITY ||
                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENUMERATION ||
                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ID ||
                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_IDREF ||
                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NMTOKEN ||
                fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NOTATION
               ) {
                validateDTDattribute(element, attrValue, fTempAttDecl);
            }
        } // for all attributes

    } // addDTDDefaultAttrsAndValidate(int,XMLAttrList)

    /** Checks entities in attribute values for standalone VC. */
    private String getExternalEntityRefInAttrValue(String nonNormalizedValue) {
        int valLength = nonNormalizedValue.length();
        int ampIndex = nonNormalizedValue.indexOf('&');
        while (ampIndex != -1) {
            if (ampIndex + 1 < valLength &&
                nonNormalizedValue.charAt(ampIndex+1) != '#') {
                int semicolonIndex = nonNormalizedValue.indexOf(';', ampIndex+1);
                String entityName = nonNormalizedValue.substring(ampIndex+1, semicolonIndex);
                entityName = fSymbolTable.addSymbol(entityName);
                int entIndex = fDTDGrammar.getEntityDeclIndex(entityName);
                if (entIndex > -1) {
                    fDTDGrammar.getEntityDecl(entIndex, fEntityDecl);
                    if (fEntityDecl.inExternal ||
                        (entityName = getExternalEntityRefInAttrValue(fEntityDecl.value)) != null) {
                        return entityName;
                    }
                }
            }
            ampIndex = nonNormalizedValue.indexOf('&', ampIndex+1);
        }
        return null;
    } // isExternalEntityRefInAttrValue(String):String

    /**
     * Validate attributes in DTD fashion.
     */
    private void validateDTDattribute(QName element, String attValue,
                                      XMLAttributeDecl attributeDecl)
    throws XNIException {

        switch (attributeDecl.simpleType.type) {
        case XMLSimpleType.TYPE_ENTITY: {                           
                // NOTE: Save this information because invalidStandaloneAttDef
                boolean isAlistAttribute = attributeDecl.simpleType.list;

                try {
                    if (isAlistAttribute) {
                        fValENTITIES.validate(attValue, null);
                    }
                    else {
                        fValENTITY.validate(attValue, null);
                    }
                }
                catch (InvalidDatatypeValueException ex) {
                    String  key = ex.getKeyIntoReporter();
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               key,
                                               new Object[]{ ex.getMessage()},
                                               XMLErrorReporter.SEVERITY_ERROR );

                }
                break;
            }

        case XMLSimpleType.TYPE_NOTATION:
        case XMLSimpleType.TYPE_ENUMERATION: {
                boolean found = false;
                String [] enumVals = attributeDecl.simpleType.enumeration;
                if (enumVals == null) {
                    found = false;
                }
                else
                    for (int i = 0; i < enumVals.length; i++) {
                        if (attValue == enumVals[i] || attValue.equals(enumVals[i])) {
                            found = true;
                            break;
                        }
                    }

                if (!found) {
                    StringBuffer enumValueString = new StringBuffer();
                    if (enumVals != null)
                        for (int i = 0; i < enumVals.length; i++) {
                            enumValueString.append(enumVals[i]+" ");
                        }
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               "MSG_ATTRIBUTE_VALUE_NOT_IN_LIST",
                                               new Object[]{attributeDecl.name.rawname, attValue, enumValueString},
                                               XMLErrorReporter.SEVERITY_ERROR);
                }
                break;
            }

        case XMLSimpleType.TYPE_ID: {
                try {
                    fValID.validate(attValue, null);
                }
                catch (InvalidDatatypeValueException ex) {
                    String  key = ex.getKeyIntoReporter();
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               key,
                                               new Object[] { ex.getMessage()},
                                               XMLErrorReporter.SEVERITY_ERROR );
                }
                break;
            }

        case XMLSimpleType.TYPE_IDREF: {
                boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef

                try {
                    if (isAlistAttribute) {
                        //System.out.println("values = >>" + value + "<<" );
                        fValIDRefs.validate(attValue, null);
                    }
                    else {
                        fValIDRef.validate(attValue, null);
                    }
                }
                catch (InvalidDatatypeValueException ex) {
                    String key = ex.getKeyIntoReporter();
                    if (key == null) {
                        key = "IDREFSInvalid";
                    }
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                               key,
                                               new Object[]{ ex.getMessage()},
                                               XMLErrorReporter.SEVERITY_ERROR );

                }
                break;
            }

        case XMLSimpleType.TYPE_NMTOKEN: {
                boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
                //changes fTempAttDef
                try {
                    if (isAlistAttribute) {
                        fValNMTOKENS.validate(attValue, null);
                    }
                    else {
                        fValNMTOKEN.validate(attValue, null);
                    }
                }
                catch (InvalidDatatypeValueException ex) {
                    if (isAlistAttribute) {
                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                   "NMTOKENSInvalid",
                                                   new Object[] { attValue},
                                                   XMLErrorReporter.SEVERITY_ERROR);
                    }
                    else {
                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                   "NMTOKENInvalid",
                                                   new Object[] { attValue},
                                                   XMLErrorReporter.SEVERITY_ERROR);
                    }
                }
                break;
            }

        } // switch

    } // validateDTDattribute(QName,String,XMLAttributeDecl)

    /** Returns true if invalid standalone attribute definition. */
    boolean invalidStandaloneAttDef(QName element, QName attribute) {
        // REVISIT: This obviously needs to be fixed! -Ac
        boolean state = true;
        /*
       if (fStandaloneReader == -1) {
          return false;
       }
       // we are normalizing a default att value...  this ok?
       if (element.rawname == -1) {
          return false;
       }
       return getAttDefIsExternal(element, attribute);
       */
        return state;
    }

    /**
     * Normalize the attribute value of a non CDATA attributes collapsing
     * sequences of space characters (x20)
     *
     * @param attributes The list of attributes
     * @param index The index of the attribute to normalize
     */
    private boolean normalizeAttrValue(XMLAttributes attributes, int index) {
        // vars
        boolean leadingSpace = true;
        boolean spaceStart = false;
        boolean readingNonSpace = false;
        int count = 0;
        int eaten = 0;
        String attrValue = attributes.getValue(index);
        char[] attValue = new char[attrValue.length()];

        fBuffer.setLength(0);
        attrValue.getChars(0, attrValue.length(), attValue, 0);
        for (int i = 0; i < attValue.length; i++) {

            if (attValue[i] == ' ') {

                // now the tricky part
                if (readingNonSpace) {
                    spaceStart = true;
                    readingNonSpace = false;
                }

                if (spaceStart && !leadingSpace) {
                    spaceStart = false;
                    fBuffer.append(attValue[i]);
                    count++;
                }
                else {
                    if (leadingSpace || !spaceStart) {
                        eaten ++;
                        /*** BUG #3512 ***
                        int entityCount = attributes.getEntityCount(index);
                        for (int j = 0;  j < entityCount; j++) {
                            int offset = attributes.getEntityOffset(index, j);
                            int length = attributes.getEntityLength(index, j);
                            if (offset <= i-eaten+1) {
                                if (offset+length >= i-eaten+1) {
                                    if (length > 0)
                                        length--;
                                }
                            }
                            else {
                                if (offset > 0)
                                    offset--;
                            }
                            attributes.setEntityOffset(index, j, offset);
                            attributes.setEntityLength(index, j, length);
                        }
                        /***/
                    }
                }

            }
            else {
                readingNonSpace = true;
                spaceStart = false;
                leadingSpace = false;
                fBuffer.append(attValue[i]);
                count++;
            }
        }

        // check if the last appended character is a space.
        if (count > 0 && fBuffer.charAt(count-1) == ' ') {
            fBuffer.setLength(count-1);
            /*** BUG #3512 ***
            int entityCount = attributes.getEntityCount(index);
            for (int j=0;  j < entityCount; j++) {
                int offset = attributes.getEntityOffset(index, j);
                int length = attributes.getEntityLength(index, j);
                if (offset < count-1) {
                    if (offset+length == count) {
                        length--;
                    }
                }
                else {
                    offset--;
                }
                attributes.setEntityOffset(index, j, offset);
                attributes.setEntityLength(index, j, length);
            }
            /***/
        }
        String newValue = fBuffer.toString();
        attributes.setValue(index, newValue);
        return ! attrValue.equals(newValue);
    }

    /**
     * Normalize the attribute value of a non CDATA default attribute
     * collapsing sequences of space characters (x20)
     *
     * @param value The value to normalize
     * @return Whether the value was changed or not.
     */
    private boolean normalizeDefaultAttrValue(XMLString value) {

        int oldLength = value.length;

        boolean skipSpace = true; // skip leading spaces
        int current = value.offset;
        int end = value.offset + value.length;
        for (int i = value.offset; i < end; i++) {
            if (value.ch[i] == ' ') {
                if (!skipSpace) {
                    // take the first whitespace as a space and skip the others
                    value.ch[current++] = ' ';
                    skipSpace = true;
                }
                else {
                    // just skip it.
                }
            }
            else {
                // simply shift non space chars if needed
                if (current != i) {
                    value.ch[current] = value.ch[i];
                }
                current++;
                skipSpace = false;
            }
        }
        if (current != end) {
            if (skipSpace) {
                // if we finished on a space trim it
                current--;
            }
            // set the new value length
            value.length = current - value.offset;
            return true;
        }
        return false;
    }

    /** Root element specified. */
    private void rootElementSpecified(QName rootElement) throws XNIException {
        if (fPerformValidation) {
            String root1 = fRootElement.rawname;
            String root2 = rootElement.rawname;
            if (root1 == null || !root1.equals(root2)) {
                fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
                                            "RootElementTypeMustMatchDoctypedecl",
                                            new Object[]{root1, root2},
                                            XMLErrorReporter.SEVERITY_ERROR);
            }
        }
    } // rootElementSpecified(QName)

    /**
     * Check that the content of an element is valid.
     * <p>
     * This is the method of primary concern to the validator. This method is called
     * upon the scanner reaching the end tag of an element. At that time, the
     * element's children must be structurally validated, so it calls this method.
     * The index of the element being checked (in the decl pool), is provided as
     * well as an array of element name indexes of the children. The validator must
     * confirm that this element can have these children in this order.
     * <p>
     * This can also be called to do 'what if' testing of content models just to see
     * if they would be valid.
     * <p>
     * Note that the element index is an index into the element decl pool, whereas
     * the children indexes are name indexes, i.e. into the string pool.
     * <p>
     * A value of -1 in the children array indicates a PCDATA node. All other
     * indexes will be positive and represent child elements. The count can be
     * zero, since some elements have the EMPTY content model and that must be
     * confirmed.
     *
     * @param elementIndex The index within the <code>ElementDeclPool</code> of this
     *                     element.
     * @param childCount The number of entries in the <code>children</code> array.
     * @param children The children of this element. 
     *
     * @return The value -1 if fully valid, else the 0 based index of the child
     *         that first failed. If the value returned is equal to the number
     *         of children, then additional content is required to reach a valid
     *         ending state.
     *
     * @exception Exception Thrown on error.
     */
    private int checkContent(int elementIndex,
                             QName[] children,
                             int childOffset,
                             int childCount) throws XNIException {

        fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);

        // Get the element name index from the element
        final String elementType = fCurrentElement.rawname;

        // Get out the content spec for this element
        final int contentType = fCurrentContentSpecType;


        //
        //  Deal with the possible types of content. We try to optimized here
        //  by dealing specially with content models that don't require the
        //  full DFA treatment.
        //
        if (contentType == XMLElementDecl.TYPE_EMPTY) {
            //
            //  If the child count is greater than zero, then this is
            //  an error right off the bat at index 0.
            //
            if (childCount != 0) {
                return 0;
            }
        }
        else if (contentType == XMLElementDecl.TYPE_ANY) {
            //
            //  This one is open game so we don't pass any judgement on it
            //  at all. Its assumed to fine since it can hold anything.
            //
        }
        else if (contentType == XMLElementDecl.TYPE_MIXED || 
                 contentType == XMLElementDecl.TYPE_CHILDREN) {
            // Get the content model for this element, faulting it in if needed
            ContentModelValidator cmElem = null;
            cmElem = fTempElementDecl.contentModelValidator;
            int result = cmElem.validate(children, childOffset, childCount);
            return result;
        }
        else if (contentType == -1) {
            //REVISIT
            /****
            reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
                                      XMLMessages.VC_ELEMENT_VALID,
                                      elementType);
            /****/
        }
        else if (contentType == XMLElementDecl.TYPE_SIMPLE) {

            //REVISIT
            // this should never be reached in the case of DTD validation.

        }
        else {
            //REVISIT
            /****
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
                                       ImplementationMessages.VAL_CST,
                                       0,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
            /****/
        }

        // We succeeded
        return -1;

    } // checkContent(int,int,QName[]):int

    /** Returns the content spec type for an element index. */
    private int getContentSpecType(int elementIndex) {

        int contentSpecType = -1;
        if (elementIndex > -1) {
            if (fDTDGrammar.getElementDecl(elementIndex,fTempElementDecl)) {
                contentSpecType = fTempElementDecl.type;
            }
        }
        return contentSpecType;
    }

    /** Character data in content. */
    private void charDataInContent() {

        if (DEBUG_ELEMENT_CHILDREN) {
            System.out.println("charDataInContent()");
        }
        if (fElementChildren.length <= fElementChildrenLength) {
            QName[] newarray = new QName[fElementChildren.length * 2];
            System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
            fElementChildren = newarray;
        }
        QName qname = fElementChildren[fElementChildrenLength];
        if (qname == null) {
            for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
                fElementChildren[i] = new QName();
            }
            qname = fElementChildren[fElementChildrenLength];
        }
        qname.clear();
        fElementChildrenLength++;

    } // charDataInCount()

    /** convert attribute type from ints to strings */
    private String getAttributeTypeName(XMLAttributeDecl attrDecl) {

        switch (attrDecl.simpleType.type) {
        case XMLSimpleType.TYPE_ENTITY: {
                return attrDecl.simpleType.list ? fENTITIESSymbol : fENTITYSymbol;
            }
        case XMLSimpleType.TYPE_ENUMERATION: {
                StringBuffer buffer = new StringBuffer();
                buffer.append('(');
                for (int i=0; i<attrDecl.simpleType.enumeration.length ; i++) {
                    if (i > 0) {
                        buffer.append("|");
                    }
                    buffer.append(attrDecl.simpleType.enumeration[i]);
                }
                buffer.append(')');
                return fSymbolTable.addSymbol(buffer.toString());
            }
        case XMLSimpleType.TYPE_ID: {
                return fIDSymbol;
            }
        case XMLSimpleType.TYPE_IDREF: {
                return attrDecl.simpleType.list ? fIDREFSSymbol : fIDREFSymbol;
            }
        case XMLSimpleType.TYPE_NMTOKEN: {
                return attrDecl.simpleType.list ? fNMTOKENSSymbol : fNMTOKENSSymbol;
            }
        case XMLSimpleType.TYPE_NOTATION: {
                return fNOTATIONSymbol;
            }
        }
        return fCDATASymbol;

    } // getAttributeTypeName(XMLAttributeDecl):String

    /** intialization */
    private void init() {

        // symbols
        fEMPTYSymbol = fSymbolTable.addSymbol("EMPTY");
        fANYSymbol = fSymbolTable.addSymbol("ANY");
        fMIXEDSymbol = fSymbolTable.addSymbol("MIXED");
        fCHILDRENSymbol = fSymbolTable.addSymbol("CHILDREN");

        fCDATASymbol = fSymbolTable.addSymbol("CDATA");
        fIDSymbol = fSymbolTable.addSymbol("ID");
        fIDREFSymbol = fSymbolTable.addSymbol("IDREF");
        fIDREFSSymbol = fSymbolTable.addSymbol("IDREFS");
        fENTITYSymbol = fSymbolTable.addSymbol("ENTITY");
        fENTITIESSymbol = fSymbolTable.addSymbol("ENTITIES");
        fNMTOKENSymbol = fSymbolTable.addSymbol("NMTOKEN");
        fNMTOKENSSymbol = fSymbolTable.addSymbol("NMTOKENS");
        fNOTATIONSymbol = fSymbolTable.addSymbol("NOTATION");
        fENUMERATIONSymbol = fSymbolTable.addSymbol("ENUMERATION");
        fIMPLIEDSymbol = fSymbolTable.addSymbol("#IMPLIED");
        fREQUIREDSymbol = fSymbolTable.addSymbol("#REQUIRED");
        fFIXEDSymbol = fSymbolTable.addSymbol("#FIXED");
        fDATATYPESymbol = fSymbolTable.addSymbol("<<datatype>>");

        // datatype validators
        if (fValidation) {
            try {
                //REVISIT: datatypeRegistry + initialization of datatype
                //         why do we cast to ListDatatypeValidator?
                ((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).initializeDTDRegistry();
                fValID       = (IDDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("ID" );
                fValIDRef    = (IDREFDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("IDREF" );
                fValIDRefs   = (ListDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("IDREFS" );
                fValENTITY   = (ENTITYDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("ENTITY" );
                fValENTITIES = (ListDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("ENTITIES" );
                fValNMTOKEN  = ((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("NMTOKEN");
                fValNMTOKENS = ((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("NMTOKENS");
                fValNOTATION = (NOTATIONDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("NOTATION" );

            }
            catch (Exception e) {
                // should never happen
                e.printStackTrace(System.err);
            }

            //Initialize ID, IDREF, IDREFS validators
            if (fTableOfIDs == null) {
                fTableOfIDs = new Hashtable();//Initialize table of IDs
            }

            fTableOfIDs.clear();
            fValID.initialize(fTableOfIDs);
            fValIDRef.initialize(fTableOfIDs);
            fValIDRefs.initialize(fTableOfIDs);
            if (fNotationEnumVals == null) {
                fNotationEnumVals = new Hashtable();
            }
            fNotationEnumVals.clear();

            fTableOfIDAttributeNames = new Hashtable();
            fTableOfNOTATIONAttributeNames = new Hashtable();
        }

    } // init()

    /** ensure element stack capacity */
    private void ensureStackCapacity ( int newElementDepth) {
        if (newElementDepth == fElementQNamePartsStack.length) {
            int[] newStack = new int[newElementDepth * 2];

            QName[] newStackOfQueue = new QName[newElementDepth * 2];
            System.arraycopy(this.fElementQNamePartsStack, 0, newStackOfQueue, 0, newElementDepth );
            fElementQNamePartsStack      = newStackOfQueue;

            QName qname = fElementQNamePartsStack[newElementDepth];
            if (qname == null) {
                for (int i = newElementDepth; i < fElementQNamePartsStack.length; i++) {
                    fElementQNamePartsStack[i] = new QName();
                }
            }

            newStack = new int[newElementDepth * 2];
            System.arraycopy(fElementIndexStack, 0, newStack, 0, newElementDepth);
            fElementIndexStack = newStack;

            newStack = new int[newElementDepth * 2];
            System.arraycopy(fContentSpecTypeStack, 0, newStack, 0, newElementDepth);
            fContentSpecTypeStack = newStack;

        }
    } // ensureStackCapacity

    //
    // Protected methods
    //

    /** Handle element. */
    protected void handleStartElement(QName element, XMLAttributes attributes) throws XNIException {

        // REVISIT: Here are current assumptions about validation features
        //          given that XMLSchema validator is in the pipeline
        //
        // http://xml.org/sax/features/validation = true
        // http://apache.org/xml/features/validation/schema = true
        //
        //[1] XML instance document only has reference to a DTD
        //  Outcome: report validation errors only against dtd.
        //
        //[2] XML instance document has only XML Schema grammars:
        //  Outcome: report validation errors only against schemas (no errors produced from DTD validator)
        //
        // [3] XML instance document has DTD and XML schemas:
        // Outcome: validation errors reported against both grammars: DTD and schemas.
        //
        //        
        //         if dynamic validation is on
        //            validate only against grammar we've found (depending on settings
        //            for schema feature)
        //
        // set wether we're performing validation
        fPerformValidation = fValidation && (!fDynamicValidation || fSeenDoctypeDecl
                             && (fDTDValidation || fSeenDoctypeDecl);

        // VC: Root Element Type
        // see if the root element's name matches the one in DoctypeDecl
        if (!fSeenRootElement) {
            fSeenRootElement = true;
            fValidationManager.getValidationState().setEntityState(fDTDGrammar);
            fValidationManager.setGrammarFound(fSeenDoctypeDecl);
            rootElementSpecified(element);
        }

        if (fDTDGrammar == null) {

            if (!fPerformValidation) {
                fCurrentElementIndex = -1;
                fCurrentContentSpecType = -1;
                fInElementContent = false;
            }
            if (fPerformValidation && !fSkipValidation) {
                fSkipValidation = true;
                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                           "MSG_GRAMMAR_NOT_FOUND",
                                           new Object[]{ element.rawname},
                                           XMLErrorReporter.SEVERITY_ERROR);
            }
        }
        else {
            //  resolve the element
            fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element, -1);

            fCurrentContentSpecType = getContentSpecType(fCurrentElementIndex);
            if (fCurrentElementIndex == -1 && fPerformValidation) {
                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                           "MSG_ELEMENT_NOT_DECLARED",
                                           new Object[]{ element.rawname},
                                           XMLErrorReporter.SEVERITY_ERROR);
            }
            else {
                //  0. insert default attributes
                //  1. normalize the attributes
                //  2. validate the attrivute list.
                // TO DO:
                //
                addDTDDefaultAttrsAndValidate(fCurrentElementIndex, attributes);
            }
        }

        // set element content state
        fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN;

        // increment the element depth, add this element's
        // QName to its enclosing element 's children list
        fElementDepth++;
        if (fPerformValidation) {
            // push current length onto stack
            if (fElementChildrenOffsetStack.length < fElementDepth) {
                int newarray[] = new int[fElementChildrenOffsetStack.length * 2];
                System.arraycopy(fElementChildrenOffsetStack, 0, newarray, 0, fElementChildrenOffsetStack.length);
                fElementChildrenOffsetStack = newarray;
            }
            fElementChildrenOffsetStack[fElementDepth] = fElementChildrenLength;

            // add this element to children
            if (fElementChildren.length <= fElementChildrenLength) {
                QName[] newarray = new QName[fElementChildrenLength * 2];
                System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
                fElementChildren = newarray;
            }
            QName qname = fElementChildren[fElementChildrenLength];
            if (qname == null) {
                for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
                    fElementChildren[i] = new QName();
                }
                qname = fElementChildren[fElementChildrenLength];
            }
            qname.setValues(element);
            fElementChildrenLength++;
        }

        // save current element information
        fCurrentElement.setValues(element);
        ensureStackCapacity(fElementDepth);
        fElementQNamePartsStack[fElementDepth].setValues(fCurrentElement);
        fElementIndexStack[fElementDepth] = fCurrentElementIndex;
        fContentSpecTypeStack[fElementDepth] = fCurrentContentSpecType;


    } // handleStartElement(QName,XMLAttributes,boolean)

    /** Handle end element. */
    protected void handleEndElement(QName element,  Augmentations augs, boolean isEmpty)
    throws XNIException {

        // decrease element depth
        fElementDepth--;

        // validate
        if (fPerformValidation) {
            int elementIndex = fCurrentElementIndex;
            if (elementIndex != -1 && fCurrentContentSpecType != -1) {
                QName children[] = fElementChildren;
                int childrenOffset = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
                int childrenLength = fElementChildrenLength - childrenOffset;
                int result = checkContent(elementIndex,
                                          children, childrenOffset, childrenLength);

                if (result != -1) {
                    fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
                    if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                   "MSG_CONTENT_INVALID",
                                                   new Object[]{ element.rawname, "EMPTY"},
                                                   XMLErrorReporter.SEVERITY_ERROR);
                    }
                    else {
                        String messageKey = result != childrenLength ?
                                            "MSG_CONTENT_INVALID" : "MSG_CONTENT_INCOMPLETE";
                        fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                   messageKey,
                                                   new Object[]{ element.rawname,
                                                       fDTDGrammar.getContentSpecAsString(elementIndex)},
                                                   XMLErrorReporter.SEVERITY_ERROR);
                    }
                }
            }
            fElementChildrenLength = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
        }
       
        // call handlers
        if (fDocumentHandler != null && !isEmpty) {
            // NOTE: The binding of the element doesn't actually happen
            //       yet because the namespace binder does that. However,
            //       if it does it before this point, then the endPrefix-
            //       Mapping calls get made too soon! As long as the
            //       rawnames match, we know it'll have a good binding,
            //       so we can just use the current element. -Ac
            fDocumentHandler.endElement(fCurrentElement, augs);
        }
       
        // now pop this element off the top of the element stack
        if (fElementDepth < -1) {
            throw new RuntimeException("FWK008 Element stack underflow");
        }
        if (fElementDepth < 0) {
            fCurrentElement.clear();
            fCurrentElementIndex = -1;
            fCurrentContentSpecType = -1;
            fInElementContent = false;

            // TO DO : fix this
            //
            // Check after document is fully parsed
            // (1) check that there was an element with a matching id for every
            //   IDREF and IDREFS attr (V_IDREF0)
            //
            if (fPerformValidation) {
                try {
                    fValIDRef.validate();//Do final validation of IDREFS against IDs
                    fValIDRefs.validate();
                }
                catch (InvalidDatatypeValueException ex) {
                    String  key = ex.getKeyIntoReporter();

                    fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
                                                key,
                                                new Object[]{ ex.getMessage()},
                                                XMLErrorReporter.SEVERITY_ERROR );
                }
                fTableOfIDs.clear();//Clear table of IDs
            }
            return;
        }

        // If Namespace enable then localName != rawName
        fCurrentElement.setValues(fElementQNamePartsStack[fElementDepth]);

        fCurrentElementIndex = fElementIndexStack[fElementDepth];
        fCurrentContentSpecType = fContentSpecTypeStack[fElementDepth];
        fInElementContent = (fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN);

    } // handleEndElement(QName,boolean)

    /** Factory method for creating a DTD grammar. */
    protected DTDGrammar createDTDGrammar() {
        return new DTDGrammar(fSymbolTable);
    } // createDTDGrammar():DTDGrammar

} // class XMLDTDValidator
TOP

Related Classes of org.apache.xerces.impl.dtd.XMLDTDValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.