286N/A/*
286N/A * reserved comment block
286N/A * DO NOT REMOVE OR ALTER!
286N/A */
286N/A/*
286N/A * The Apache Software License, Version 1.1
286N/A *
286N/A *
286N/A * Copyright (c) 1999-2003 The Apache Software Foundation.
286N/A * All rights reserved.
286N/A *
286N/A * Redistribution and use in source and binary forms, with or without
286N/A * modification, are permitted provided that the following conditions
286N/A * are met:
286N/A *
286N/A * 1. Redistributions of source code must retain the above copyright
286N/A * notice, this list of conditions and the following disclaimer.
286N/A *
286N/A * 2. Redistributions in binary form must reproduce the above copyright
286N/A * notice, this list of conditions and the following disclaimer in
286N/A * the documentation and/or other materials provided with the
286N/A * distribution.
286N/A *
286N/A * 3. The end-user documentation included with the redistribution,
286N/A * if any, must include the following acknowledgment:
286N/A * "This product includes software developed by the
286N/A * Apache Software Foundation (http://www.apache.org/)."
286N/A * Alternately, this acknowledgment may appear in the software itself,
286N/A * if and wherever such third-party acknowledgments normally appear.
286N/A *
286N/A * 4. The names "Xerces" and "Apache Software Foundation" must
286N/A * not be used to endorse or promote products derived from this
286N/A * software without prior written permission. For written
286N/A * permission, please contact apache@apache.org.
286N/A *
286N/A * 5. Products derived from this software may not be called "Apache",
286N/A * nor may "Apache" appear in their name, without prior written
286N/A * permission of the Apache Software Foundation.
286N/A *
286N/A * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
286N/A * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
286N/A * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
286N/A * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
286N/A * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
286N/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
286N/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
286N/A * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
286N/A * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
286N/A * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
286N/A * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
286N/A * SUCH DAMAGE.
286N/A * ====================================================================
286N/A *
286N/A * This software consists of voluntary contributions made by many
286N/A * individuals on behalf of the Apache Software Foundation and was
286N/A * originally based on software copyright (c) 2002, International
286N/A * Business Machines, Inc., http://www.apache.org. For more
286N/A * information on the Apache Software Foundation, please see
286N/A * <http://www.apache.org/>.
286N/A */
286N/A
286N/Apackage com.sun.org.apache.xerces.internal.impl;
286N/A
286N/Aimport java.io.IOException;
286N/A
286N/Aimport com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidatorFilter;
286N/Aimport com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
286N/Aimport com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
286N/Aimport com.sun.org.apache.xerces.internal.util.XMLSymbols;
286N/Aimport com.sun.org.apache.xerces.internal.xni.NamespaceContext;
286N/Aimport com.sun.org.apache.xerces.internal.xni.QName;
286N/Aimport com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
286N/Aimport com.sun.org.apache.xerces.internal.xni.XNIException;
286N/Aimport com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
286N/Aimport com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
286N/Aimport com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
286N/Aimport javax.xml.stream.events.XMLEvent;
286N/A
286N/A
286N/A/**
286N/A * The scanner acts as the source for the document
286N/A * information which is communicated to the document handler.
286N/A *
286N/A * This class scans an XML document, checks if document has a DTD, and if
286N/A * DTD is not found the scanner will remove the DTD Validator from the pipeline and perform
286N/A * namespace binding.
286N/A *
286N/A * Note: This scanner should only be used when the namespace processing is on!
286N/A *
286N/A * <p>
286N/A * This component requires the following features and properties from the
286N/A * component manager that uses it:
286N/A * <ul>
286N/A * <li>http://xml.org/sax/features/namespaces {true} -- if the value of this
286N/A * feature is set to false this scanner must not be used.</li>
286N/A * <li>http://xml.org/sax/features/validation</li>
286N/A * <li>http://apache.org/xml/features/nonvalidating/load-external-dtd</li>
286N/A * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
286N/A * <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
286N/A * <li>http://apache.org/xml/properties/internal/symbol-table</li>
286N/A * <li>http://apache.org/xml/properties/internal/error-reporter</li>
286N/A * <li>http://apache.org/xml/properties/internal/entity-manager</li>
286N/A * <li>http://apache.org/xml/properties/internal/dtd-scanner</li>
286N/A * </ul>
286N/A *
286N/A * @xerces.internal
286N/A *
286N/A * @author Elena Litani, IBM
286N/A * @author Michael Glavassevich, IBM
286N/A * @author Sunitha Reddy, Sun Microsystems
286N/A */
286N/Apublic class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
286N/A
286N/A /**
286N/A * If is true, the dtd validator is no longer in the pipeline
286N/A * and the scanner should bind namespaces
286N/A */
286N/A protected boolean fBindNamespaces;
286N/A
286N/A /**
286N/A * If validating parser, make sure we report an error in the
286N/A * scanner if DTD grammar is missing.
286N/A */
286N/A protected boolean fPerformValidation;
286N/A
286N/A // private data
286N/A //
286N/A
286N/A /** DTD validator */
286N/A private XMLDTDValidatorFilter fDTDValidator;
286N/A
286N/A /**
286N/A * Saw spaces after element name or between attributes.
286N/A *
286N/A * This is reserved for the case where scanning of a start element spans
286N/A * several methods, as is the case when scanning the start of a root element
286N/A * where a DTD external subset may be read after scanning the element name.
286N/A */
286N/A private boolean fSawSpace;
286N/A
286N/A
286N/A /**
286N/A * The scanner is responsible for removing DTD validator
286N/A * from the pipeline if it is not needed.
286N/A *
286N/A * @param validator the DTD validator from the pipeline
286N/A */
286N/A public void setDTDValidator(XMLDTDValidatorFilter validator) {
286N/A fDTDValidator = validator;
286N/A }
286N/A
286N/A /**
286N/A * Scans a start element. This method will handle the binding of
286N/A * namespace information and notifying the handler of the start
286N/A * of the element.
286N/A * <p>
286N/A * <pre>
286N/A * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
286N/A * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
286N/A * </pre>
286N/A * <p>
286N/A * <strong>Note:</strong> This method assumes that the leading
286N/A * '&lt;' character has been consumed.
286N/A * <p>
286N/A * <strong>Note:</strong> This method uses the fElementQName and
286N/A * fAttributes variables. The contents of these variables will be
286N/A * destroyed. The caller should copy important information out of
286N/A * these variables before calling this method.
286N/A *
286N/A * @return True if element is empty. (i.e. It matches
286N/A * production [44].
286N/A */
286N/A protected boolean scanStartElement() throws IOException, XNIException {
286N/A
286N/A if (DEBUG_START_END_ELEMENT)
286N/A System.out.println(">>> scanStartElementNS()");
286N/A // Note: namespace processing is on by default
286N/A fEntityScanner.scanQName(fElementQName);
286N/A // REVISIT - [Q] Why do we need this local variable? -- mrglavas
286N/A String rawname = fElementQName.rawname;
286N/A if (fBindNamespaces) {
286N/A fNamespaceContext.pushContext();
286N/A if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
286N/A if (fPerformValidation) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XML_DOMAIN,
286N/A "MSG_GRAMMAR_NOT_FOUND",
286N/A new Object[] { rawname },
286N/A XMLErrorReporter.SEVERITY_ERROR);
286N/A
286N/A if (fDoctypeName == null
286N/A || !fDoctypeName.equals(rawname)) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XML_DOMAIN,
286N/A "RootElementTypeMustMatchDoctypedecl",
286N/A new Object[] { fDoctypeName, rawname },
286N/A XMLErrorReporter.SEVERITY_ERROR);
286N/A }
286N/A }
286N/A }
286N/A }
286N/A
286N/A // push element stack
286N/A fCurrentElement = fElementStack.pushElement(fElementQName);
286N/A
286N/A // attributes
286N/A boolean empty = false;
286N/A fAttributes.removeAllAttributes();
286N/A do {
286N/A // spaces
286N/A boolean sawSpace = fEntityScanner.skipSpaces();
286N/A
286N/A // end tag?
286N/A int c = fEntityScanner.peekChar();
286N/A if (c == '>') {
286N/A fEntityScanner.scanChar();
286N/A break;
286N/A } else if (c == '/') {
286N/A fEntityScanner.scanChar();
286N/A if (!fEntityScanner.skipChar('>')) {
286N/A reportFatalError(
286N/A "ElementUnterminated",
286N/A new Object[] { rawname });
286N/A }
286N/A empty = true;
286N/A break;
286N/A } else if (!isValidNameStartChar(c) || !sawSpace) {
286N/A // Second chance. Check if this character is a high
286N/A // surrogate of a valid name start character.
286N/A if (!isValidNameStartHighSurrogate(c) || !sawSpace) {
286N/A reportFatalError(
286N/A "ElementUnterminated",
286N/A new Object[] { rawname });
286N/A }
286N/A }
286N/A
286N/A // attributes
286N/A scanAttribute(fAttributes);
286N/A if (fSecurityManager != null && fAttributes.getLength() > fElementAttributeLimit){
286N/A fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
286N/A "ElementAttributeLimit",
286N/A new Object[]{rawname, new Integer(fElementAttributeLimit) },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR );
286N/A }
286N/A
286N/A } while (true);
286N/A
286N/A if (fBindNamespaces) {
286N/A // REVISIT: is it required? forbit xmlns prefix for element
286N/A if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "ElementXMLNSPrefix",
286N/A new Object[] { fElementQName.rawname },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A
286N/A // bind the element
286N/A String prefix =
286N/A fElementQName.prefix != null
286N/A ? fElementQName.prefix
286N/A : XMLSymbols.EMPTY_STRING;
286N/A // assign uri to the element
286N/A fElementQName.uri = fNamespaceContext.getURI(prefix);
286N/A // make sure that object in the element stack is updated as well
286N/A fCurrentElement.uri = fElementQName.uri;
286N/A
286N/A if (fElementQName.prefix == null && fElementQName.uri != null) {
286N/A fElementQName.prefix = XMLSymbols.EMPTY_STRING;
286N/A // making sure that the object in the element stack is updated too.
286N/A fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
286N/A }
286N/A if (fElementQName.prefix != null && fElementQName.uri == null) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "ElementPrefixUnbound",
286N/A new Object[] {
286N/A fElementQName.prefix,
286N/A fElementQName.rawname },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A
286N/A // bind attributes (xmlns are already bound bellow)
286N/A int length = fAttributes.getLength();
286N/A for (int i = 0; i < length; i++) {
286N/A fAttributes.getName(i, fAttributeQName);
286N/A
286N/A String aprefix =
286N/A fAttributeQName.prefix != null
286N/A ? fAttributeQName.prefix
286N/A : XMLSymbols.EMPTY_STRING;
286N/A String uri = fNamespaceContext.getURI(aprefix);
286N/A // REVISIT: try removing the first "if" and see if it is faster.
286N/A //
286N/A if (fAttributeQName.uri != null
286N/A && fAttributeQName.uri == uri) {
286N/A continue;
286N/A }
286N/A if (aprefix != XMLSymbols.EMPTY_STRING) {
286N/A fAttributeQName.uri = uri;
286N/A if (uri == null) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "AttributePrefixUnbound",
286N/A new Object[] {
286N/A fElementQName.rawname,
286N/A fAttributeQName.rawname,
286N/A aprefix },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A fAttributes.setURI(i, uri);
286N/A }
286N/A }
286N/A
286N/A if (length > 1) {
286N/A QName name = fAttributes.checkDuplicatesNS();
286N/A if (name != null) {
286N/A if (name.uri != null) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "AttributeNSNotUnique",
286N/A new Object[] {
286N/A fElementQName.rawname,
286N/A name.localpart,
286N/A name.uri },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A } else {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "AttributeNotUnique",
286N/A new Object[] {
286N/A fElementQName.rawname,
286N/A name.rawname },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A }
286N/A }
286N/A }
286N/A
286N/A // call handler
286N/A
286N/A if (empty) {
286N/A
286N/A //decrease the markup depth..
286N/A fMarkupDepth--;
286N/A
286N/A // check that this element was opened in the same entity
286N/A if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
286N/A reportFatalError(
286N/A "ElementEntityMismatch",
286N/A new Object[] { fCurrentElement.rawname });
286N/A }
286N/A
286N/A fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
286N/A
286N/A /*if (fBindNamespaces) {
286N/A fNamespaceContext.popContext();
286N/A }*/
286N/A fScanEndElement = true;
286N/A
286N/A //pop the element off the stack..
286N/A fElementStack.popElement();
286N/A } else {
286N/A
286N/A if(dtdGrammarUtil != null)
286N/A dtdGrammarUtil.startElement(fElementQName, fAttributes);
286N/A
286N/A if (fDocumentHandler != null)
286N/A fDocumentHandler.startElement(fElementQName, fAttributes, null);
286N/A }
286N/A
286N/A if (DEBUG_START_END_ELEMENT)
286N/A System.out.println("<<< scanStartElement(): " + empty);
286N/A return empty;
286N/A
286N/A } // scanStartElement():boolean
286N/A
286N/A /**
286N/A * Scans the name of an element in a start or empty tag.
286N/A *
286N/A * @see #scanStartElement()
286N/A */
286N/A protected void scanStartElementName ()
286N/A throws IOException, XNIException {
286N/A // Note: namespace processing is on by default
286N/A fEntityScanner.scanQName(fElementQName);
286N/A // Must skip spaces here because the DTD scanner
286N/A // would consume them at the end of the external subset.
286N/A fSawSpace = fEntityScanner.skipSpaces();
286N/A } // scanStartElementName()
286N/A
286N/A /**
286N/A * Scans the remainder of a start or empty tag after the element name.
286N/A *
286N/A * @see #scanStartElement
286N/A * @return True if element is empty.
286N/A */
286N/A protected boolean scanStartElementAfterName()
286N/A throws IOException, XNIException {
286N/A
286N/A // REVISIT - [Q] Why do we need this local variable? -- mrglavas
286N/A String rawname = fElementQName.rawname;
286N/A if (fBindNamespaces) {
286N/A fNamespaceContext.pushContext();
286N/A if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
286N/A if (fPerformValidation) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XML_DOMAIN,
286N/A "MSG_GRAMMAR_NOT_FOUND",
286N/A new Object[] { rawname },
286N/A XMLErrorReporter.SEVERITY_ERROR);
286N/A
286N/A if (fDoctypeName == null
286N/A || !fDoctypeName.equals(rawname)) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XML_DOMAIN,
286N/A "RootElementTypeMustMatchDoctypedecl",
286N/A new Object[] { fDoctypeName, rawname },
286N/A XMLErrorReporter.SEVERITY_ERROR);
286N/A }
286N/A }
286N/A }
286N/A }
286N/A
286N/A // push element stack
286N/A fCurrentElement = fElementStack.pushElement(fElementQName);
286N/A
286N/A // attributes
286N/A boolean empty = false;
286N/A fAttributes.removeAllAttributes();
286N/A do {
286N/A
286N/A // end tag?
286N/A int c = fEntityScanner.peekChar();
286N/A if (c == '>') {
286N/A fEntityScanner.scanChar();
286N/A break;
286N/A } else if (c == '/') {
286N/A fEntityScanner.scanChar();
286N/A if (!fEntityScanner.skipChar('>')) {
286N/A reportFatalError(
286N/A "ElementUnterminated",
286N/A new Object[] { rawname });
286N/A }
286N/A empty = true;
286N/A break;
286N/A } else if (!isValidNameStartChar(c) || !fSawSpace) {
286N/A // Second chance. Check if this character is a high
286N/A // surrogate of a valid name start character.
286N/A if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
286N/A reportFatalError(
286N/A "ElementUnterminated",
286N/A new Object[] { rawname });
286N/A }
286N/A }
286N/A
286N/A // attributes
286N/A scanAttribute(fAttributes);
286N/A
286N/A // spaces
286N/A fSawSpace = fEntityScanner.skipSpaces();
286N/A
286N/A } while (true);
286N/A
286N/A if (fBindNamespaces) {
286N/A // REVISIT: is it required? forbit xmlns prefix for element
286N/A if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "ElementXMLNSPrefix",
286N/A new Object[] { fElementQName.rawname },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A
286N/A // bind the element
286N/A String prefix =
286N/A fElementQName.prefix != null
286N/A ? fElementQName.prefix
286N/A : XMLSymbols.EMPTY_STRING;
286N/A // assign uri to the element
286N/A fElementQName.uri = fNamespaceContext.getURI(prefix);
286N/A // make sure that object in the element stack is updated as well
286N/A fCurrentElement.uri = fElementQName.uri;
286N/A
286N/A if (fElementQName.prefix == null && fElementQName.uri != null) {
286N/A fElementQName.prefix = XMLSymbols.EMPTY_STRING;
286N/A // making sure that the object in the element stack is updated too.
286N/A fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
286N/A }
286N/A if (fElementQName.prefix != null && fElementQName.uri == null) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "ElementPrefixUnbound",
286N/A new Object[] {
286N/A fElementQName.prefix,
286N/A fElementQName.rawname },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A
286N/A // bind attributes (xmlns are already bound bellow)
286N/A int length = fAttributes.getLength();
286N/A for (int i = 0; i < length; i++) {
286N/A fAttributes.getName(i, fAttributeQName);
286N/A
286N/A String aprefix =
286N/A fAttributeQName.prefix != null
286N/A ? fAttributeQName.prefix
286N/A : XMLSymbols.EMPTY_STRING;
286N/A String uri = fNamespaceContext.getURI(aprefix);
286N/A // REVISIT: try removing the first "if" and see if it is faster.
286N/A //
286N/A if (fAttributeQName.uri != null
286N/A && fAttributeQName.uri == uri) {
286N/A continue;
286N/A }
286N/A if (aprefix != XMLSymbols.EMPTY_STRING) {
286N/A fAttributeQName.uri = uri;
286N/A if (uri == null) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "AttributePrefixUnbound",
286N/A new Object[] {
286N/A fElementQName.rawname,
286N/A fAttributeQName.rawname,
286N/A aprefix },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A fAttributes.setURI(i, uri);
286N/A }
286N/A }
286N/A
286N/A if (length > 1) {
286N/A QName name = fAttributes.checkDuplicatesNS();
286N/A if (name != null) {
286N/A if (name.uri != null) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "AttributeNSNotUnique",
286N/A new Object[] {
286N/A fElementQName.rawname,
286N/A name.localpart,
286N/A name.uri },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A } else {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "AttributeNotUnique",
286N/A new Object[] {
286N/A fElementQName.rawname,
286N/A name.rawname },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A }
286N/A }
286N/A }
286N/A
286N/A // call handler
286N/A if (fDocumentHandler != null) {
286N/A if (empty) {
286N/A
286N/A //decrease the markup depth..
286N/A fMarkupDepth--;
286N/A
286N/A // check that this element was opened in the same entity
286N/A if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
286N/A reportFatalError(
286N/A "ElementEntityMismatch",
286N/A new Object[] { fCurrentElement.rawname });
286N/A }
286N/A
286N/A fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
286N/A
286N/A if (fBindNamespaces) {
286N/A fNamespaceContext.popContext();
286N/A }
286N/A //pop the element off the stack..
286N/A fElementStack.popElement();
286N/A } else {
286N/A fDocumentHandler.startElement(fElementQName, fAttributes, null);
286N/A }
286N/A }
286N/A
286N/A if (DEBUG_START_END_ELEMENT)
286N/A System.out.println("<<< scanStartElementAfterName(): " + empty);
286N/A return empty;
286N/A
286N/A } // scanStartElementAfterName()
286N/A
286N/A /**
286N/A * Scans an attribute.
286N/A * <p>
286N/A * <pre>
286N/A * [41] Attribute ::= Name Eq AttValue
286N/A * </pre>
286N/A * <p>
286N/A * <strong>Note:</strong> This method assumes that the next
286N/A * character on the stream is the first character of the attribute
286N/A * name.
286N/A * <p>
286N/A * <strong>Note:</strong> This method uses the fAttributeQName and
286N/A * fQName variables. The contents of these variables will be
286N/A * destroyed.
286N/A *
286N/A * @param attributes The attributes list for the scanned attribute.
286N/A */
286N/A protected void scanAttribute(XMLAttributesImpl attributes)
286N/A throws IOException, XNIException {
286N/A if (DEBUG_START_END_ELEMENT)
286N/A System.out.println(">>> scanAttribute()");
286N/A
286N/A // name
286N/A fEntityScanner.scanQName(fAttributeQName);
286N/A
286N/A // equals
286N/A fEntityScanner.skipSpaces();
286N/A if (!fEntityScanner.skipChar('=')) {
286N/A reportFatalError(
286N/A "EqRequiredInAttribute",
286N/A new Object[] {
286N/A fCurrentElement.rawname,
286N/A fAttributeQName.rawname });
286N/A }
286N/A fEntityScanner.skipSpaces();
286N/A
286N/A // content
286N/A int attrIndex;
286N/A
286N/A if (fBindNamespaces) {
286N/A attrIndex = attributes.getLength();
286N/A attributes.addAttributeNS(
286N/A fAttributeQName,
286N/A XMLSymbols.fCDATASymbol,
286N/A null);
286N/A } else {
286N/A int oldLen = attributes.getLength();
286N/A attrIndex =
286N/A attributes.addAttribute(
286N/A fAttributeQName,
286N/A XMLSymbols.fCDATASymbol,
286N/A null);
286N/A
286N/A // WFC: Unique Att Spec
286N/A if (oldLen == attributes.getLength()) {
286N/A reportFatalError(
286N/A "AttributeNotUnique",
286N/A new Object[] {
286N/A fCurrentElement.rawname,
286N/A fAttributeQName.rawname });
286N/A }
286N/A }
286N/A
286N/A //REVISIT: one more case needs to be included: external PE and standalone is no
286N/A boolean isVC = fHasExternalDTD && !fStandalone;
286N/A
286N/A // REVISIT: it seems that this function should not take attributes, and length
286N/A scanAttributeValue(
286N/A this.fTempString,
286N/A fTempString2,
286N/A fAttributeQName.rawname,
286N/A isVC,
286N/A fCurrentElement.rawname);
286N/A String value = fTempString.toString();
286N/A attributes.setValue(attrIndex, value);
286N/A attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
286N/A attributes.setSpecified(attrIndex, true);
286N/A
286N/A // record namespace declarations if any.
286N/A if (fBindNamespaces) {
286N/A
286N/A String localpart = fAttributeQName.localpart;
286N/A String prefix =
286N/A fAttributeQName.prefix != null
286N/A ? fAttributeQName.prefix
286N/A : XMLSymbols.EMPTY_STRING;
286N/A // when it's of form xmlns="..." or xmlns:prefix="...",
286N/A // it's a namespace declaration. but prefix:xmlns="..." isn't.
286N/A if (prefix == XMLSymbols.PREFIX_XMLNS
286N/A || prefix == XMLSymbols.EMPTY_STRING
286N/A && localpart == XMLSymbols.PREFIX_XMLNS) {
286N/A
286N/A // get the internalized value of this attribute
286N/A String uri = fSymbolTable.addSymbol(value);
286N/A
286N/A // 1. "xmlns" can't be bound to any namespace
286N/A if (prefix == XMLSymbols.PREFIX_XMLNS
286N/A && localpart == XMLSymbols.PREFIX_XMLNS) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "CantBindXMLNS",
286N/A new Object[] { fAttributeQName },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A
286N/A // 2. the namespace for "xmlns" can't be bound to any prefix
286N/A if (uri == NamespaceContext.XMLNS_URI) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "CantBindXMLNS",
286N/A new Object[] { fAttributeQName },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A
286N/A // 3. "xml" can't be bound to any other namespace than it's own
286N/A if (localpart == XMLSymbols.PREFIX_XML) {
286N/A if (uri != NamespaceContext.XML_URI) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "CantBindXML",
286N/A new Object[] { fAttributeQName },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A }
286N/A // 4. the namespace for "xml" can't be bound to any other prefix
286N/A else {
286N/A if (uri == NamespaceContext.XML_URI) {
286N/A fErrorReporter.reportError(
286N/A XMLMessageFormatter.XMLNS_DOMAIN,
286N/A "CantBindXML",
286N/A new Object[] { fAttributeQName },
286N/A XMLErrorReporter.SEVERITY_FATAL_ERROR);
286N/A }
286N/A }
286N/A
286N/A prefix =
286N/A localpart != XMLSymbols.PREFIX_XMLNS
286N/A ? localpart
286N/A : XMLSymbols.EMPTY_STRING;
286N/A
286N/A // Declare prefix in context. Removing the association between a prefix and a
286N/A // namespace name is permitted in XML 1.1, so if the uri value is the empty string,
286N/A // the prefix is being unbound. -- mrglavas
286N/A fNamespaceContext.declarePrefix(
286N/A prefix,
286N/A uri.length() != 0 ? uri : null);
286N/A // bind namespace attribute to a namespace
286N/A attributes.setURI(
286N/A attrIndex,
286N/A fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS));
286N/A
286N/A } else {
286N/A // attempt to bind attribute
286N/A if (fAttributeQName.prefix != null) {
286N/A attributes.setURI(
286N/A attrIndex,
286N/A fNamespaceContext.getURI(fAttributeQName.prefix));
286N/A }
286N/A }
286N/A }
286N/A
286N/A if (DEBUG_START_END_ELEMENT)
286N/A System.out.println("<<< scanAttribute()");
286N/A } // scanAttribute(XMLAttributes)
286N/A
286N/A /**
286N/A * Scans an end element.
286N/A * <p>
286N/A * <pre>
286N/A * [42] ETag ::= '&lt;/' Name S? '>'
286N/A * </pre>
286N/A * <p>
286N/A * <strong>Note:</strong> This method uses the fElementQName variable.
286N/A * The contents of this variable will be destroyed. The caller should
286N/A * copy the needed information out of this variable before calling
286N/A * this method.
286N/A *
286N/A * @return The element depth.
286N/A */
286N/A protected int scanEndElement() throws IOException, XNIException {
286N/A if (DEBUG_START_END_ELEMENT)
286N/A System.out.println(">>> scanEndElement()");
286N/A
286N/A // pop context
286N/A QName endElementName = fElementStack.popElement();
286N/A
286N/A // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
286N/A //In scanners most of the time is consumed on checks done for XML characters, we can
286N/A // optimize on it and avoid the checks done for endElement,
286N/A //we will also avoid symbol table lookup - neeraj.bajaj@sun.com
286N/A
286N/A // this should work both for namespace processing true or false...
286N/A
286N/A //REVISIT: if the string is not the same as expected.. we need to do better error handling..
286N/A //We can skip this for now... In any case if the string doesn't match -- document is not well formed.
286N/A
286N/A if (!fEntityScanner.skipString(endElementName.rawname)) {
286N/A reportFatalError(
286N/A "ETagRequired",
286N/A new Object[] { endElementName.rawname });
286N/A }
286N/A
286N/A // end
286N/A fEntityScanner.skipSpaces();
286N/A if (!fEntityScanner.skipChar('>')) {
286N/A reportFatalError(
286N/A "ETagUnterminated",
286N/A new Object[] { endElementName.rawname });
286N/A }
286N/A fMarkupDepth--;
286N/A
286N/A //we have increased the depth for two markup "<" characters
286N/A fMarkupDepth--;
286N/A
286N/A // check that this element was opened in the same entity
286N/A if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
286N/A reportFatalError(
286N/A "ElementEntityMismatch",
286N/A new Object[] { endElementName.rawname });
286N/A }
286N/A
286N/A // call handler
286N/A if (fDocumentHandler != null) {
286N/A fDocumentHandler.endElement(endElementName, null);
286N/A
286N/A /*if (fBindNamespaces) {
286N/A fNamespaceContext.popContext();
286N/A }*/
286N/A
286N/A }
286N/A
286N/A if(dtdGrammarUtil != null)
286N/A dtdGrammarUtil.endElement(endElementName);
286N/A
286N/A return fMarkupDepth;
286N/A
286N/A } // scanEndElement():int
286N/A
286N/A public void reset(XMLComponentManager componentManager)
286N/A throws XMLConfigurationException {
286N/A
286N/A super.reset(componentManager);
286N/A fPerformValidation = false;
286N/A fBindNamespaces = false;
286N/A }
286N/A
286N/A /** Creates a content Driver. */
286N/A protected Driver createContentDriver() {
286N/A return new NS11ContentDriver();
286N/A } // createContentDriver():Driver
286N/A
286N/A
286N/A /** return the next state on the input
286N/A *
286N/A * @return int
286N/A */
286N/A
286N/A public int next() throws IOException, XNIException {
286N/A //since namespace context should still be valid when the parser is at the end element state therefore
286N/A //we pop the context only when next() has been called after the end element state was encountered. - nb.
286N/A
286N/A if((fScannerLastState == XMLEvent.END_ELEMENT) && fBindNamespaces){
286N/A fScannerLastState = -1;
286N/A fNamespaceContext.popContext();
286N/A }
286N/A
286N/A return fScannerLastState = super.next();
286N/A }
286N/A
286N/A
286N/A /**
286N/A * Driver to handle content scanning.
286N/A */
286N/A protected final class NS11ContentDriver extends ContentDriver {
286N/A /**
286N/A * Scan for root element hook. This method is a hook for
286N/A * subclasses to add code that handles scanning for the root
286N/A * element. This method will also attempt to remove DTD validator
286N/A * from the pipeline, if there is no DTD grammar. If DTD validator
286N/A * is no longer in the pipeline bind namespaces in the scanner.
286N/A *
286N/A *
286N/A * @return True if the caller should stop and return true which
286N/A * allows the scanner to switch to a new scanning
286N/A * Driver. A return value of false indicates that
286N/A * the content Driver should continue as normal.
286N/A */
286N/A protected boolean scanRootElementHook()
286N/A throws IOException, XNIException {
286N/A
286N/A if (fExternalSubsetResolver != null && !fSeenDoctypeDecl
286N/A && !fDisallowDoctype && (fValidation || fLoadExternalDTD)) {
286N/A scanStartElementName();
286N/A resolveExternalSubsetAndRead();
286N/A reconfigurePipeline();
286N/A if (scanStartElementAfterName()) {
286N/A setScannerState(SCANNER_STATE_TRAILING_MISC);
286N/A setDriver(fTrailingMiscDriver);
286N/A return true;
286N/A }
286N/A }
286N/A else {
286N/A reconfigurePipeline();
286N/A if (scanStartElement()) {
286N/A setScannerState(SCANNER_STATE_TRAILING_MISC);
286N/A setDriver(fTrailingMiscDriver);
286N/A return true;
286N/A }
286N/A }
286N/A return false;
286N/A
286N/A } // scanRootElementHook():boolean
286N/A
286N/A /**
286N/A * Re-configures pipeline by removing the DTD validator
286N/A * if no DTD grammar exists. If no validator exists in the
286N/A * pipeline or there is no DTD grammar, namespace binding
286N/A * is performed by the scanner in the enclosing class.
286N/A */
286N/A private void reconfigurePipeline() {
286N/A if (fDTDValidator == null) {
286N/A fBindNamespaces = true;
286N/A }
286N/A else if (!fDTDValidator.hasGrammar()) {
286N/A fBindNamespaces = true;
286N/A fPerformValidation = fDTDValidator.validate();
286N/A // re-configure pipeline
286N/A XMLDocumentSource source = fDTDValidator.getDocumentSource();
286N/A XMLDocumentHandler handler = fDTDValidator.getDocumentHandler();
286N/A source.setDocumentHandler(handler);
286N/A if (handler != null)
286N/A handler.setDocumentSource(source);
286N/A fDTDValidator.setDocumentSource(null);
286N/A fDTDValidator.setDocumentHandler(null);
286N/A }
286N/A } // reconfigurePipeline()
286N/A }
286N/A}