/* * reserved comment block * DO NOT REMOVE OR ALTER! */ /* * Copyright 1999-2002,2004, 2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.org.apache.xerces.internal.dom; import java.io.IOException; import java.util.ArrayList; import java.io.StringReader; import java.util.Vector; import com.sun.org.apache.xerces.internal.dom.AbortException; import com.sun.org.apache.xerces.internal.impl.Constants; import com.sun.org.apache.xerces.internal.impl.RevalidationHandler; import com.sun.org.apache.xerces.internal.impl.dtd.DTDGrammar; import com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDDescription; import com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator; import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType; import com.sun.org.apache.xerces.internal.impl.xs.util.SimpleLocator; import com.sun.org.apache.xerces.internal.parsers.XMLGrammarPreparser; import com.sun.org.apache.xerces.internal.util.AugmentationsImpl; import com.sun.org.apache.xerces.internal.util.NamespaceSupport; import com.sun.org.apache.xerces.internal.util.SymbolTable; import com.sun.org.apache.xerces.internal.util.XML11Char; import com.sun.org.apache.xerces.internal.util.XMLChar; import com.sun.org.apache.xerces.internal.util.XMLGrammarPoolImpl; import com.sun.org.apache.xerces.internal.util.XMLSymbols; import com.sun.org.apache.xerces.internal.xni.Augmentations; import com.sun.org.apache.xerces.internal.xni.NamespaceContext; import com.sun.org.apache.xerces.internal.xni.QName; import com.sun.org.apache.xerces.internal.xni.XMLAttributes; import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; import com.sun.org.apache.xerces.internal.xni.XMLLocator; import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; import com.sun.org.apache.xerces.internal.xni.XMLString; import com.sun.org.apache.xerces.internal.xni.XNIException; import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription; import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool; import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; import com.sun.org.apache.xerces.internal.xs.AttributePSVI; import com.sun.org.apache.xerces.internal.xs.ElementPSVI; import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition; import org.w3c.dom.Attr; import org.w3c.dom.Comment; import org.w3c.dom.DOMError; import org.w3c.dom.DOMErrorHandler; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.Entity; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; /** * This class adds implementation for normalizeDocument method. * It acts as if the document was going through a save and load cycle, putting * the document in a "normal" form. The actual result depends on the features being set * and governing what operations actually take place. See setNormalizationFeature for details. * Noticeably this method normalizes Text nodes, makes the document "namespace wellformed", * according to the algorithm described below in pseudo code, by adding missing namespace * declaration attributes and adding or changing namespace prefixes, updates the replacement * tree of EntityReference nodes, normalizes attribute values, etc. * Mutation events, when supported, are generated to reflect the changes occuring on the * document. * See Namespace normalization for details on how namespace declaration attributes and prefixes * are normalized. * * NOTE: There is an initial support for DOM revalidation with XML Schema as a grammar. * The tree might not be validated correctly if entityReferences, CDATA sections are * present in the tree. The PSVI information is not exposed, normalized data (including element * default content is not available). * * @xerces.experimental * * @author Elena Litani, IBM * @author Neeraj Bajaj, Sun Microsystems, inc. */ public class DOMNormalizer implements XMLDocumentHandler { // // constants // /** Debug normalize document*/ protected final static boolean DEBUG_ND = false; /** Debug namespace fix up algorithm*/ protected final static boolean DEBUG = false; /** Debug document handler events */ protected final static boolean DEBUG_EVENTS = false; /** prefix added by namespace fixup algorithm should follow a pattern "NS" + index*/ protected final static String PREFIX = "NS"; // // Data // protected DOMConfigurationImpl fConfiguration = null; protected CoreDocumentImpl fDocument = null; protected final XMLAttributesProxy fAttrProxy = new XMLAttributesProxy(); protected final QName fQName = new QName(); /** Validation handler represents validator instance. */ protected RevalidationHandler fValidationHandler; /** symbol table */ protected SymbolTable fSymbolTable; /** error handler. may be null. */ protected DOMErrorHandler fErrorHandler; /** * Cached {@link DOMError} impl. * The same object is re-used to report multiple errors. */ private final DOMErrorImpl fError = new DOMErrorImpl(); // Validation against namespace aware grammar protected boolean fNamespaceValidation = false; // Update PSVI information in the tree protected boolean fPSVI = false; /** The namespace context of this document: stores namespaces in scope */ protected final NamespaceContext fNamespaceContext = new NamespaceSupport(); /** Stores all namespace bindings on the current element */ protected final NamespaceContext fLocalNSBinder = new NamespaceSupport(); /** list of attributes */ protected final ArrayList fAttributeList = new ArrayList(5); /** DOM Locator - for namespace fixup algorithm */ protected final DOMLocatorImpl fLocator = new DOMLocatorImpl(); /** for setting the PSVI */ protected Node fCurrentNode = null; private QName fAttrQName = new QName(); // attribute value normalization final XMLString fNormalizedValue = new XMLString(new char[16], 0, 0); //DTD validator private XMLDTDValidator fDTDValidator; //Check if element content is all "ignorable whitespace" private boolean allWhitespace = false; // Constructor // public DOMNormalizer(){} /** * Normalizes document. * Note: reset() must be called before this method. */ protected void normalizeDocument(CoreDocumentImpl document, DOMConfigurationImpl config) { fDocument = document; fConfiguration = config; // intialize and reset DOMNormalizer component // fSymbolTable = (SymbolTable) fConfiguration.getProperty(DOMConfigurationImpl.SYMBOL_TABLE); // reset namespace context fNamespaceContext.reset(); fNamespaceContext.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); if ((fConfiguration.features & DOMConfigurationImpl.VALIDATE) != 0) { String schemaLang = (String)fConfiguration.getProperty(DOMConfigurationImpl.JAXP_SCHEMA_LANGUAGE); if(schemaLang != null && schemaLang.equals(Constants.NS_XMLSCHEMA)) { fValidationHandler = CoreDOMImplementationImpl.singleton.getValidator(XMLGrammarDescription.XML_SCHEMA); fConfiguration.setFeature(DOMConfigurationImpl.SCHEMA, true); fConfiguration.setFeature(DOMConfigurationImpl.SCHEMA_FULL_CHECKING, true); // report fatal error on DOM Level 1 nodes fNamespaceValidation = true; // check if we need to fill in PSVI fPSVI = ((fConfiguration.features & DOMConfigurationImpl.PSVI) !=0)?true:false; } fConfiguration.setFeature(DOMConfigurationImpl.XERCES_VALIDATION, true); // reset ID table fDocument.clearIdentifiers(); if(fValidationHandler != null) // reset schema validator ((XMLComponent) fValidationHandler).reset(fConfiguration); } fErrorHandler = (DOMErrorHandler) fConfiguration.getParameter(Constants.DOM_ERROR_HANDLER); if (fValidationHandler != null) { fValidationHandler.setDocumentHandler(this); fValidationHandler.startDocument( new SimpleLocator(fDocument.fDocumentURI, fDocument.fDocumentURI, -1, -1 ), fDocument.encoding, fNamespaceContext, null); } try { Node kid, next; for (kid = fDocument.getFirstChild(); kid != null; kid = next) { next = kid.getNextSibling(); kid = normalizeNode(kid); if (kid != null) { // don't advance next = kid; } } // release resources if (fValidationHandler != null) { fValidationHandler.endDocument(null); CoreDOMImplementationImpl.singleton.releaseValidator( XMLGrammarDescription.XML_SCHEMA, fValidationHandler); fValidationHandler = null; } } catch (AbortException e) { return; } } /** * * This method acts as if the document was going through a save * and load cycle, putting the document in a "normal" form. The actual result * depends on the features being set and governing what operations actually * take place. See setNormalizationFeature for details. Noticeably this method * normalizes Text nodes, makes the document "namespace wellformed", * according to the algorithm described below in pseudo code, by adding missing * namespace declaration attributes and adding or changing namespace prefixes, updates * the replacement tree of EntityReference nodes,normalizes attribute values, etc. * * @param node Modified node or null. If node is returned, we need * to normalize again starting on the node returned. * @return the normalized Node */ protected Node normalizeNode (Node node){ int type = node.getNodeType(); boolean wellformed; fLocator.fRelatedNode=node; switch (type) { case Node.DOCUMENT_TYPE_NODE: { if (DEBUG_ND) { System.out.println("==>normalizeNode:{doctype}"); } DocumentTypeImpl docType = (DocumentTypeImpl)node; fDTDValidator = (XMLDTDValidator)CoreDOMImplementationImpl.singleton.getValidator(XMLGrammarDescription.XML_DTD); fDTDValidator.setDocumentHandler(this); fConfiguration.setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY, createGrammarPool(docType)); fDTDValidator.reset(fConfiguration); fDTDValidator.startDocument( new SimpleLocator(fDocument.fDocumentURI, fDocument.fDocumentURI, -1, -1 ), fDocument.encoding, fNamespaceContext, null); fDTDValidator.doctypeDecl(docType.getName(), docType.getPublicId(), docType.getSystemId(), null); //REVISIT: well-formness encoding info break; } case Node.ELEMENT_NODE: { if (DEBUG_ND) { System.out.println("==>normalizeNode:{element} "+node.getNodeName()); } //do the name check only when version of the document was changed & //application has set the value of well-formed features to true if (fDocument.errorChecking) { if ( ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0) && fDocument.isXMLVersionChanged()){ if (fNamespaceValidation){ wellformed = CoreDocumentImpl.isValidQName(node.getPrefix() , node.getLocalName(), fDocument.isXML11Version()) ; } else { wellformed = CoreDocumentImpl.isXMLName(node.getNodeName() , fDocument.isXML11Version()); } if (!wellformed){ String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "wf-invalid-character-in-node-name", new Object[]{"Element", node.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character-in-node-name"); } } } // push namespace context fNamespaceContext.pushContext(); fLocalNSBinder.reset(); ElementImpl elem = (ElementImpl)node; if (elem.needsSyncChildren()) { elem.synchronizeChildren(); } AttributeMap attributes = (elem.hasAttributes()) ? (AttributeMap) elem.getAttributes() : null; // fix namespaces and remove default attributes if ((fConfiguration.features & DOMConfigurationImpl.NAMESPACES) !=0) { // fix namespaces // normalize attribute values // remove default attributes namespaceFixUp(elem, attributes); if ((fConfiguration.features & DOMConfigurationImpl.NSDECL) == 0 && attributes != null ) { for (int i = 0; i < attributes.getLength(); ++i) { Attr att = (Attr)attributes.getItem(i); if (XMLSymbols.PREFIX_XMLNS.equals(att.getPrefix()) || XMLSymbols.PREFIX_XMLNS.equals(att.getName())) { elem.removeAttributeNode(att); --i; } } } } else { if ( attributes!=null ) { for ( int i=0; i<attributes.getLength(); ++i ) { Attr attr = (Attr)attributes.item(i); //removeDefault(attr, attributes); attr.normalize(); if (fDocument.errorChecking && ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0)){ isAttrValueWF(fErrorHandler, fError, fLocator, attributes, (AttrImpl)attr, attr.getValue(), fDocument.isXML11Version()); if (fDocument.isXMLVersionChanged()){ wellformed=CoreDocumentImpl.isXMLName(node.getNodeName() , fDocument.isXML11Version()); if (!wellformed){ String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "wf-invalid-character-in-node-name", new Object[]{"Attr",node.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character-in-node-name"); } } } } } } if (fValidationHandler != null) { // REVISIT: possible solutions to discard default content are: // either we pass some flag to XML Schema validator // or rely on the PSVI information. fAttrProxy.setAttributes(attributes, fDocument, elem); updateQName(elem, fQName); // updates global qname // set error node in the dom error wrapper // so if error occurs we can report an error node fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; // call re-validation handler fValidationHandler.startElement(fQName, fAttrProxy, null); } if (fDTDValidator != null) { // REVISIT: possible solutions to discard default content are: // either we pass some flag to XML Schema validator // or rely on the PSVI information. fAttrProxy.setAttributes(attributes, fDocument, elem); updateQName(elem, fQName); // updates global qname // set error node in the dom error wrapper // so if error occurs we can report an error node fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; // call re-validation handler fDTDValidator.startElement(fQName, fAttrProxy, null); } // normalize children Node kid, next; for (kid = elem.getFirstChild(); kid != null; kid = next) { next = kid.getNextSibling(); kid = normalizeNode(kid); if (kid !=null) { next = kid; // don't advance } } if (DEBUG_ND) { // normalized subtree System.out.println("***The children of {"+node.getNodeName()+"} are normalized"); for (kid = elem.getFirstChild(); kid != null; kid = next) { next = kid.getNextSibling(); System.out.println(kid.getNodeName() +"["+kid.getNodeValue()+"]"); } } if (fValidationHandler != null) { updateQName(elem, fQName); // updates global qname // // set error node in the dom error wrapper // so if error occurs we can report an error node fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; fValidationHandler.endElement(fQName, null); } if (fDTDValidator != null) { updateQName(elem, fQName); // updates global qname // // set error node in the dom error wrapper // so if error occurs we can report an error node fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; fDTDValidator.endElement(fQName, null); } // pop namespace context fNamespaceContext.popContext(); break; } case Node.COMMENT_NODE: { if (DEBUG_ND) { System.out.println("==>normalizeNode:{comments}"); } if ((fConfiguration.features & DOMConfigurationImpl.COMMENTS) == 0) { Node prevSibling = node.getPreviousSibling(); Node parent = node.getParentNode(); // remove the comment node parent.removeChild(node); if (prevSibling != null && prevSibling.getNodeType() == Node.TEXT_NODE) { Node nextSibling = prevSibling.getNextSibling(); if (nextSibling != null && nextSibling.getNodeType() == Node.TEXT_NODE) { ((TextImpl)nextSibling).insertData(0, prevSibling.getNodeValue()); parent.removeChild(prevSibling); return nextSibling; } } }//if comment node need not be removed else { if (fDocument.errorChecking && ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0)){ String commentdata = ((Comment)node).getData(); // check comments for invalid xml chracter as per the version // of the document isCommentWF(fErrorHandler, fError, fLocator, commentdata, fDocument.isXML11Version()); } }//end-else if comment node is not to be removed. break; } case Node.ENTITY_REFERENCE_NODE: { if (DEBUG_ND) { System.out.println("==>normalizeNode:{entityRef} "+node.getNodeName()); } if ((fConfiguration.features & DOMConfigurationImpl.ENTITIES) == 0) { Node prevSibling = node.getPreviousSibling(); Node parent = node.getParentNode(); ((EntityReferenceImpl)node).setReadOnly(false, true); expandEntityRef (parent, node); parent.removeChild(node); Node next = (prevSibling != null)?prevSibling.getNextSibling():parent.getFirstChild(); // The list of children #text -> &ent; // and entity has a first child as a text // we should not advance if (prevSibling != null && next != null && prevSibling.getNodeType() == Node.TEXT_NODE && next.getNodeType() == Node.TEXT_NODE) { return prevSibling; // Don't advance } return next; } else { if (fDocument.errorChecking && ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0) && fDocument.isXMLVersionChanged()){ CoreDocumentImpl.isXMLName(node.getNodeName() , fDocument.isXML11Version()); } // REVISIT: traverse entity reference and send appropriate calls to the validator // (no normalization should be performed for the children). } break; } case Node.CDATA_SECTION_NODE: { if (DEBUG_ND) { System.out.println("==>normalizeNode:{cdata}"); } if ((fConfiguration.features & DOMConfigurationImpl.CDATA) == 0) { // convert CDATA to TEXT nodes Node prevSibling = node.getPreviousSibling(); if (prevSibling != null && prevSibling.getNodeType() == Node.TEXT_NODE){ ((Text)prevSibling).appendData(node.getNodeValue()); node.getParentNode().removeChild(node); return prevSibling; //don't advance } else { Text text = fDocument.createTextNode(node.getNodeValue()); Node parent = node.getParentNode(); node = parent.replaceChild(text, node); return text; //don't advance } } // send characters call for CDATA if (fValidationHandler != null) { // set error node in the dom error wrapper // so if error occurs we can report an error node fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; fValidationHandler.startCDATA(null); fValidationHandler.characterData(node.getNodeValue(), null); fValidationHandler.endCDATA(null); } if (fDTDValidator != null) { // set error node in the dom error wrapper // so if error occurs we can report an error node fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; fDTDValidator.startCDATA(null); fDTDValidator.characterData(node.getNodeValue(), null); fDTDValidator.endCDATA(null); } String value = node.getNodeValue(); if ((fConfiguration.features & DOMConfigurationImpl.SPLITCDATA) != 0) { int index; Node parent = node.getParentNode(); if (fDocument.errorChecking) { isXMLCharWF(fErrorHandler, fError, fLocator, node.getNodeValue(), fDocument.isXML11Version()); } while ( (index=value.indexOf("]]>")) >= 0 ) { node.setNodeValue(value.substring(0, index+2)); value = value.substring(index +2); Node firstSplitNode = node; Node newChild = fDocument.createCDATASection(value); parent.insertBefore(newChild, node.getNextSibling()); node = newChild; // issue warning fLocator.fRelatedNode = firstSplitNode; String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "cdata-sections-splitted", null); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_WARNING, "cdata-sections-splitted"); } } else if (fDocument.errorChecking) { // check well-formedness isCDataWF(fErrorHandler, fError, fLocator, value, fDocument.isXML11Version()); } break; } case Node.TEXT_NODE: { if (DEBUG_ND) { System.out.println("==>normalizeNode(text):{"+node.getNodeValue()+"}"); } // If node is a text node, we need to check for one of two // conditions: // 1) There is an adjacent text node // 2) There is no adjacent text node, but node is // an empty text node. Node next = node.getNextSibling(); // If an adjacent text node, merge it with this node if ( next!=null && next.getNodeType() == Node.TEXT_NODE ) { ((Text)node).appendData(next.getNodeValue()); node.getParentNode().removeChild( next ); // We don't need to check well-formness here since we are not yet // done with this node. return node; // Don't advance; } else if (node.getNodeValue().length()==0) { // If kid is empty, remove it node.getParentNode().removeChild( node ); } else { // validator.characters() call and well-formness // Don't send characters or check well-formness in the following cases: // 1. entities is false, next child is entity reference: expand tree first // 2. comments is false, and next child is comment // 3. cdata is false, and next child is cdata short nextType = (next != null)?next.getNodeType():-1; if (nextType == -1 || !(((fConfiguration.features & DOMConfigurationImpl.ENTITIES) == 0 && nextType == Node.ENTITY_NODE) || ((fConfiguration.features & DOMConfigurationImpl.COMMENTS) == 0 && nextType == Node.COMMENT_NODE) || ((fConfiguration.features & DOMConfigurationImpl.CDATA) == 0) && nextType == Node.CDATA_SECTION_NODE)) { if (fDocument.errorChecking && ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0) ){ isXMLCharWF(fErrorHandler, fError, fLocator, node.getNodeValue(), fDocument.isXML11Version()); } if (fValidationHandler != null) { fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; fValidationHandler.characterData(node.getNodeValue(), null); if (DEBUG_ND) { System.out.println("=====>characterData(),"+nextType); } } if (fDTDValidator != null) { fConfiguration.fErrorHandlerWrapper.fCurrentNode = node; fCurrentNode = node; fDTDValidator.characterData(node.getNodeValue(), null); if (DEBUG_ND) { System.out.println("=====>characterData(),"+nextType); } if(allWhitespace) { allWhitespace = false; ((TextImpl)node).setIgnorableWhitespace(true); } } } else { if (DEBUG_ND) { System.out.println("=====>don't send characters(),"+nextType); } } } break; } case Node.PROCESSING_INSTRUCTION_NODE: { //do the well-formed valid PI target name , data check when application has set the value of well-formed feature to true if (fDocument.errorChecking && (fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0 ) { ProcessingInstruction pinode = (ProcessingInstruction)node ; String target = pinode.getTarget(); //1.check PI target name if(fDocument.isXML11Version()){ wellformed = XML11Char.isXML11ValidName(target); } else{ wellformed = XMLChar.isValidName(target); } if (!wellformed) { String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "wf-invalid-character-in-node-name", new Object[]{"Element", node.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character-in-node-name"); } //2. check PI data //processing isntruction data may have certain characters //which may not be valid XML character isXMLCharWF(fErrorHandler, fError, fLocator, pinode.getData(), fDocument.isXML11Version()); } }//end case Node.PROCESSING_INSTRUCTION_NODE }//end of switch return null; }//normalizeNode private XMLGrammarPool createGrammarPool(DocumentTypeImpl docType) { XMLGrammarPoolImpl pool = new XMLGrammarPoolImpl(); XMLGrammarPreparser preParser = new XMLGrammarPreparser(fSymbolTable); preParser.registerPreparser(XMLGrammarDescription.XML_DTD, null); preParser.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE, true); preParser.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.VALIDATION_FEATURE, true); preParser.setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY, pool); String internalSubset = docType.getInternalSubset(); XMLInputSource is = new XMLInputSource(docType.getPublicId(), docType.getSystemId(), null, false); if(internalSubset != null) is.setCharacterStream(new StringReader(internalSubset)); try { DTDGrammar g = (DTDGrammar)preParser.preparseGrammar(XMLGrammarDescription.XML_DTD, is); ((XMLDTDDescription)g.getGrammarDescription()).setRootName(docType.getName()); is.setCharacterStream(null); g = (DTDGrammar)preParser.preparseGrammar(XMLGrammarDescription.XML_DTD, is); ((XMLDTDDescription)g.getGrammarDescription()).setRootName(docType.getName()); } catch (XNIException e) { } catch (IOException e) { } return pool; } protected final void expandEntityRef (Node parent, Node reference){ Node kid, next; for (kid = reference.getFirstChild(); kid != null; kid = next) { next = kid.getNextSibling(); parent.insertBefore(kid, reference); } } // fix namespaces // normalize attribute values // remove default attributes // check attribute names if the version of the document changed. protected final void namespaceFixUp (ElementImpl element, AttributeMap attributes){ if (DEBUG) { System.out.println("[ns-fixup] element:" +element.getNodeName()+ " uri: "+element.getNamespaceURI()); } // ------------------------------------ // pick up local namespace declarations // <xsl:stylesheet xmlns:xsl="http://xslt"> // <!-- add the following via DOM // body is bound to http://xslt // --> // <xsl:body xmlns:xsl="http://bound"/> // // ------------------------------------ String value, name, uri, prefix; if (attributes != null) { // Record all valid local declarations for (int k = 0; k < attributes.getLength(); ++k) { Attr attr = (Attr)attributes.getItem(k); //do the name check only when version of the document was changed & //application has set the value of well-formed features to true if (fDocument.errorChecking && ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0) && fDocument.isXMLVersionChanged()) { //checkQName does checking based on the version of the document fDocument.checkQName(attr.getPrefix() , attr.getLocalName()) ; } uri = attr.getNamespaceURI(); if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) { // namespace attribute // "namespace-declarations" == false; Discard all namespace declaration attributes if ((fConfiguration.features & DOMConfigurationImpl.NSDECL) == 0) { continue; } value = attr.getNodeValue(); if (value == null) { value=XMLSymbols.EMPTY_STRING; } // Check for invalid namespace declaration: if (fDocument.errorChecking && value.equals(NamespaceContext.XMLNS_URI)) { //A null value for locale is passed to formatMessage, //which means that the default locale will be used fLocator.fRelatedNode = attr; String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN,"CantBindXMLNS",null ); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "CantBindXMLNS"); } else { // XML 1.0 Attribute value normalization // value = normalizeAttributeValue(value, attr); prefix = attr.getPrefix(); prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix); String localpart = fSymbolTable.addSymbol( attr.getLocalName()); if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix value = fSymbolTable.addSymbol(value); if (value.length() != 0) { fNamespaceContext.declarePrefix(localpart, value); } else { // REVISIT: issue error on invalid declarations // xmlns:foo = "" } //removeDefault (attr, attributes); continue; } else { // (localpart == fXmlnsSymbol && prefix == fEmptySymbol) -- xmlns // empty prefix is always bound ("" or some string) value = fSymbolTable.addSymbol(value); fNamespaceContext.declarePrefix(XMLSymbols.EMPTY_STRING, value); //removeDefault (attr, attributes); continue; } } // end-else: valid declaration } // end-if: namespace attribute } } // --------------------------------------------------------- // Fix up namespaces for element: per DOM L3 // Need to consider the following cases: // // case 1: <xsl:stylesheet xmlns:xsl="http://xsl"> // We create another element body bound to the "http://xsl" namespace // as well as namespace attribute rebounding xsl to another namespace. // <xsl:body xmlns:xsl="http://another"> // Need to make sure that the new namespace decl value is changed to // "http://xsl" // // --------------------------------------------------------- // check if prefix/namespace is correct for current element // --------------------------------------------------------- uri = element.getNamespaceURI(); prefix = element.getPrefix(); // "namespace-declarations" == false? Discard all namespace declaration attributes if ((fConfiguration.features & DOMConfigurationImpl.NSDECL) == 0) { // no namespace declaration == no namespace URI, semantics are to keep prefix uri = null; } else if (uri != null) { // Element has a namespace uri = fSymbolTable.addSymbol(uri); prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix); if (fNamespaceContext.getURI(prefix) == uri) { // The xmlns:prefix=namespace or xmlns="default" was declared at parent. // The binder always stores mapping of empty prefix to "". } else { // the prefix is either undeclared // or // conflict: the prefix is bound to another URI addNamespaceDecl(prefix, uri, element); fLocalNSBinder.declarePrefix(prefix, uri); fNamespaceContext.declarePrefix(prefix, uri); } } else { // Element has no namespace if (element.getLocalName() == null) { // Error: DOM Level 1 node! if (fNamespaceValidation) { String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName", new Object[]{element.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_FATAL_ERROR, "NullLocalElementName"); } else { String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName", new Object[]{element.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "NullLocalElementName"); } } else { // uri=null and no colon (DOM L2 node) uri = fNamespaceContext.getURI(XMLSymbols.EMPTY_STRING); if (uri !=null && uri.length() > 0) { // undeclare default namespace declaration (before that element // bound to non-zero length uir), but adding xmlns="" decl addNamespaceDecl (XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING, element); fLocalNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); fNamespaceContext.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); } } } // ----------------------------------------- // Fix up namespaces for attributes: per DOM L3 // check if prefix/namespace is correct the attributes // ----------------------------------------- if (attributes != null) { // clone content of the attributes attributes.cloneMap(fAttributeList); for (int i = 0; i < fAttributeList.size(); i++) { Attr attr = (Attr) fAttributeList.get(i); fLocator.fRelatedNode = attr; if (DEBUG) { System.out.println("==>[ns-fixup] process attribute: "+attr.getNodeName()); } // normalize attribute value attr.normalize(); value = attr.getValue(); name = attr.getNodeName(); uri = attr.getNamespaceURI(); // make sure that value is never null. if (value == null) { value=XMLSymbols.EMPTY_STRING; } if (uri != null) { // attribute has namespace !=null prefix = attr.getPrefix(); prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix); /*String localpart =*/ fSymbolTable.addSymbol( attr.getLocalName()); // --------------------------------------- // skip namespace declarations // --------------------------------------- // REVISIT: can we assume that "uri" is from some symbol // table, and compare by reference? -SG if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) { continue; } //--------------------------------------- // check if value of the attribute is namespace well-formed //--------------------------------------- if (fDocument.errorChecking && ((fConfiguration.features & DOMConfigurationImpl.WELLFORMED) != 0)) { isAttrValueWF(fErrorHandler, fError, fLocator, attributes, (AttrImpl)attr, attr.getValue(), fDocument.isXML11Version()); if (fDocument.isXMLVersionChanged()){ boolean wellformed=CoreDocumentImpl.isXMLName(attr.getNodeName() , fDocument.isXML11Version()); if (!wellformed){ String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "wf-invalid-character-in-node-name", new Object[]{"Attribute", attr.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character-in-node-name"); } } } // --------------------------------------- // remove default attributes // --------------------------------------- /* if (removeDefault(attr, attributes)) { continue; } */ // XML 1.0 Attribute value normalization //value = normalizeAttributeValue(value, attr); // reset id-attributes ((AttrImpl)attr).setIdAttribute(false); uri = fSymbolTable.addSymbol(uri); // find if for this prefix a URI was already declared String declaredURI = fNamespaceContext.getURI(prefix); if (prefix == XMLSymbols.EMPTY_STRING || declaredURI != uri) { // attribute has no prefix (default namespace decl does not apply to attributes) // OR // attribute prefix is not declared // OR // conflict: attribute has a prefix that conficlicts with a binding // already active in scope name = attr.getNodeName(); // Find if any prefix for attributes namespace URI is available // in the scope String declaredPrefix = fNamespaceContext.getPrefix(uri); if (declaredPrefix !=null && declaredPrefix !=XMLSymbols.EMPTY_STRING) { // use the prefix that was found (declared previously for this URI prefix = declaredPrefix; } else { if (prefix != XMLSymbols.EMPTY_STRING && fLocalNSBinder.getURI(prefix) == null) { // the current prefix is not null and it has no in scope declaration // use this prefix } else { // find a prefix following the pattern "NS" +index (starting at 1) // make sure this prefix is not declared in the current scope. int counter = 1; prefix = fSymbolTable.addSymbol(PREFIX +counter++); while (fLocalNSBinder.getURI(prefix)!=null) { prefix = fSymbolTable.addSymbol(PREFIX +counter++); } } // add declaration for the new prefix addNamespaceDecl(prefix, uri, element); value = fSymbolTable.addSymbol(value); fLocalNSBinder.declarePrefix(prefix, value); fNamespaceContext.declarePrefix(prefix, uri); } // change prefix for this attribute attr.setPrefix(prefix); } } else { // attribute uri == null // XML 1.0 Attribute value normalization //value = normalizeAttributeValue(value, attr); // reset id-attributes ((AttrImpl)attr).setIdAttribute(false); if (attr.getLocalName() == null) { // It is an error if document has DOM L1 nodes. if (fNamespaceValidation) { String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "NullLocalAttrName", new Object[]{attr.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_FATAL_ERROR, "NullLocalAttrName"); } else { String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "NullLocalAttrName", new Object[]{attr.getNodeName()}); reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_ERROR, "NullLocalAttrName"); } } else { // uri=null and no colon // no fix up is needed: default namespace decl does not // --------------------------------------- // remove default attributes // --------------------------------------- // removeDefault(attr, attributes); } } } } // end loop for attributes } /** * Adds a namespace attribute or replaces the value of existing namespace * attribute with the given prefix and value for URI. * In case prefix is empty will add/update default namespace declaration. * * @param prefix * @param uri * @exception IOException */ protected final void addNamespaceDecl(String prefix, String uri, ElementImpl element){ if (DEBUG) { System.out.println("[ns-fixup] addNamespaceDecl ["+prefix+"]"); } if (prefix == XMLSymbols.EMPTY_STRING) { if (DEBUG) { System.out.println("=>add xmlns=\""+uri+"\" declaration"); } element.setAttributeNS(NamespaceContext.XMLNS_URI, XMLSymbols.PREFIX_XMLNS, uri); } else { if (DEBUG) { System.out.println("=>add xmlns:"+prefix+"=\""+uri+"\" declaration"); } element.setAttributeNS(NamespaceContext.XMLNS_URI, "xmlns:"+prefix, uri); } } // // Methods for well-formness checking // /** * Check if CDATA section is well-formed * @param datavalue * @param isXML11Version = true if XML 1.1 */ public static final void isCDataWF(DOMErrorHandler errorHandler, DOMErrorImpl error, DOMLocatorImpl locator, String datavalue, boolean isXML11Version) { if (datavalue == null || (datavalue.length() == 0) ) { return; } char [] dataarray = datavalue.toCharArray(); int datalength = dataarray.length; // version of the document is XML 1.1 if (isXML11Version) { // we need to check all chracters as per production rules of XML11 int i = 0; while(i < datalength){ char c = dataarray[i++]; if ( XML11Char.isXML11Invalid(c) ) { // check if this is a supplemental character if (XMLChar.isHighSurrogate(c) && i < datalength) { char c2 = dataarray[i++]; if (XMLChar.isLowSurrogate(c2) && XMLChar.isSupplemental(XMLChar.supplemental(c, c2))) { continue; } } String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.XML_DOMAIN, "InvalidCharInCDSect", new Object[] { Integer.toString(c, 16)}); reportDOMError( errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } else if (c == ']') { int count = i; if (count < datalength && dataarray[count] == ']') { while (++count < datalength && dataarray[count] == ']') { // do nothing } if (count < datalength && dataarray[count] == '>') { // CDEndInContent String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN, "CDEndInContent", null); reportDOMError(errorHandler, error, locator,msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } } } } } // version of the document is XML 1.0 else { // we need to check all chracters as per production rules of XML 1.0 int i = 0; while (i < datalength) { char c = dataarray[i++]; if( XMLChar.isInvalid(c) ) { // check if this is a supplemental character if (XMLChar.isHighSurrogate(c) && i < datalength) { char c2 = dataarray[i++]; if (XMLChar.isLowSurrogate(c2) && XMLChar.isSupplemental(XMLChar.supplemental(c, c2))) { continue; } } // Note: The key InvalidCharInCDSect from XMLMessages.properties // is being used to obtain the message and DOM error type // "wf-invalid-character" is used. Also per DOM it is error but // as per XML spec. it is fatal error String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.XML_DOMAIN, "InvalidCharInCDSect", new Object[]{Integer.toString(c, 16)}); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } else if (c==']') { int count = i; if ( count< datalength && dataarray[count]==']' ) { while (++count < datalength && dataarray[count]==']' ) { // do nothing } if ( count < datalength && dataarray[count]=='>' ) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN, "CDEndInContent", null); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } } } } } // end-else fDocument.isXMLVersion() } // isCDataWF /** * NON-DOM: check for valid XML characters as per the XML version * @param datavalue * @param isXML11Version = true if XML 1.1 */ public static final void isXMLCharWF(DOMErrorHandler errorHandler, DOMErrorImpl error, DOMLocatorImpl locator, String datavalue, boolean isXML11Version) { if ( datavalue == null || (datavalue.length() == 0) ) { return; } char [] dataarray = datavalue.toCharArray(); int datalength = dataarray.length; // version of the document is XML 1.1 if(isXML11Version){ //we need to check all characters as per production rules of XML11 int i = 0 ; while (i < datalength) { if(XML11Char.isXML11Invalid(dataarray[i++])){ // check if this is a supplemental character char ch = dataarray[i-1]; if (XMLChar.isHighSurrogate(ch) && i < datalength) { char ch2 = dataarray[i++]; if (XMLChar.isLowSurrogate(ch2) && XMLChar.isSupplemental(XMLChar.supplemental(ch, ch2))) { continue; } } String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "InvalidXMLCharInDOM", new Object[]{Integer.toString(dataarray[i-1], 16)}); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } } } // version of the document is XML 1.0 else{ // we need to check all characters as per production rules of XML 1.0 int i = 0 ; while (i < datalength) { if( XMLChar.isInvalid(dataarray[i++]) ) { // check if this is a supplemental character char ch = dataarray[i-1]; if (XMLChar.isHighSurrogate(ch) && i < datalength) { char ch2 = dataarray[i++]; if (XMLChar.isLowSurrogate(ch2) && XMLChar.isSupplemental(XMLChar.supplemental(ch, ch2))) { continue; } } String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "InvalidXMLCharInDOM", new Object[]{Integer.toString(dataarray[i-1], 16)}); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } } } // end-else fDocument.isXMLVersion() } // isXMLCharWF /** * NON-DOM: check if value of the comment is well-formed * @param datavalue * @param isXML11Version = true if XML 1.1 */ public static final void isCommentWF(DOMErrorHandler errorHandler, DOMErrorImpl error, DOMLocatorImpl locator, String datavalue, boolean isXML11Version) { if ( datavalue == null || (datavalue.length() == 0) ) { return; } char [] dataarray = datavalue.toCharArray(); int datalength = dataarray.length ; // version of the document is XML 1.1 if (isXML11Version) { // we need to check all chracters as per production rules of XML11 int i = 0 ; while (i < datalength){ char c = dataarray[i++]; if ( XML11Char.isXML11Invalid(c) ) { // check if this is a supplemental character if (XMLChar.isHighSurrogate(c) && i < datalength) { char c2 = dataarray[i++]; if (XMLChar.isLowSurrogate(c2) && XMLChar.isSupplemental(XMLChar.supplemental(c, c2))) { continue; } } String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN, "InvalidCharInComment", new Object [] {Integer.toString(dataarray[i-1], 16)}); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } else if (c == '-' && i < datalength && dataarray[i] == '-') { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN, "DashDashInComment", null); // invalid: '--' in comment reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } } } // version of the document is XML 1.0 else { // we need to check all chracters as per production rules of XML 1.0 int i = 0; while (i < datalength){ char c = dataarray[i++]; if( XMLChar.isInvalid(c) ){ // check if this is a supplemental character if (XMLChar.isHighSurrogate(c) && i < datalength) { char c2 = dataarray[i++]; if (XMLChar.isLowSurrogate(c2) && XMLChar.isSupplemental(XMLChar.supplemental(c, c2))) { continue; } } String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN, "InvalidCharInComment", new Object [] {Integer.toString(dataarray[i-1], 16)}); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } else if (c == '-' && i<datalength && dataarray[i]=='-'){ String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.XML_DOMAIN, "DashDashInComment", null); // invalid: '--' in comment reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "wf-invalid-character"); } } } // end-else fDocument.isXMLVersion() } // isCommentWF /** NON-DOM: check if attribute value is well-formed * @param attributes * @param a * @param value */ public static final void isAttrValueWF(DOMErrorHandler errorHandler, DOMErrorImpl error, DOMLocatorImpl locator, NamedNodeMap attributes, Attr a, String value, boolean xml11Version) { if (a instanceof AttrImpl && ((AttrImpl)a).hasStringValue()) { isXMLCharWF(errorHandler, error, locator, value, xml11Version); } else { NodeList children = a.getChildNodes(); //check each child node of the attribute's value for (int j = 0; j < children.getLength(); j++) { Node child = children.item(j); //If the attribute's child is an entity refernce if (child.getNodeType() == Node.ENTITY_REFERENCE_NODE) { Document owner = a.getOwnerDocument(); Entity ent = null; //search for the entity in the docType //of the attribute's ownerDocument if (owner != null) { DocumentType docType = owner.getDoctype(); if (docType != null) { NamedNodeMap entities = docType.getEntities(); ent = (Entity) entities.getNamedItemNS( "*", child.getNodeName()); } } //If the entity was not found issue a fatal error if (ent == null) { String msg = DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "UndeclaredEntRefInAttrValue", new Object[]{a.getNodeName()}); reportDOMError(errorHandler, error, locator, msg, DOMError.SEVERITY_ERROR, "UndeclaredEntRefInAttrValue"); } } else { // Text node isXMLCharWF(errorHandler, error, locator, child.getNodeValue(), xml11Version); } } } } /** * Reports a DOM error to the user handler. * * If the error is fatal, the processing will be always aborted. */ public static final void reportDOMError(DOMErrorHandler errorHandler, DOMErrorImpl error, DOMLocatorImpl locator, String message, short severity, String type ) { if( errorHandler!=null ) { error.reset(); error.fMessage = message; error.fSeverity = severity; error.fLocator = locator; error.fType = type; error.fRelatedData = locator.fRelatedNode; if(!errorHandler.handleError(error)) throw new AbortException(); } if( severity==DOMError.SEVERITY_FATAL_ERROR ) throw new AbortException(); } protected final void updateQName (Node node, QName qname){ String prefix = node.getPrefix(); String namespace = node.getNamespaceURI(); String localName = node.getLocalName(); // REVISIT: the symbols are added too often: start/endElement // and in the namespaceFixup. Should reduce number of calls to symbol table. qname.prefix = (prefix!=null && prefix.length()!=0)?fSymbolTable.addSymbol(prefix):null; qname.localpart = (localName != null)?fSymbolTable.addSymbol(localName):null; qname.rawname = fSymbolTable.addSymbol(node.getNodeName()); qname.uri = (namespace != null)?fSymbolTable.addSymbol(namespace):null; } /* REVISIT: remove this method if DOM does not change spec. * Performs partial XML 1.0 attribute value normalization and replaces * attribute value if the value is changed after the normalization. * DOM defines that normalizeDocument acts as if the document was going * through a save and load cycle, given that serializer will not escape * any '\n' or '\r' characters on load those will be normalized. * Thus during normalize document we need to do the following: * - perform "2.11 End-of-Line Handling" * - replace #xD, #xA, #x9 with #x20 (white space). * Note: This alg. won't attempt to resolve entity references or character entity * references, since '&' will be escaped during serialization and during loading * this won't be recognized as entity reference, i.e. attribute value "&foo;" will * be serialized as "&foo;" and thus after loading will be "&foo;" again. * @param value current attribute value * @param attr current attribute * @return String the value (could be original if normalization did not change * the string) */ final String normalizeAttributeValue(String value, Attr attr) { if (!attr.getSpecified()){ // specified attributes should already have a normalized form // since those were added by validator return value; } int end = value.length(); // ensure capacity if (fNormalizedValue.ch.length < end) { fNormalizedValue.ch = new char[end]; } fNormalizedValue.length = 0; boolean normalized = false; for (int i = 0; i < end; i++) { char c = value.charAt(i); if (c==0x0009 || c==0x000A) { fNormalizedValue.ch[fNormalizedValue.length++] = ' '; normalized = true; } else if(c==0x000D){ normalized = true; fNormalizedValue.ch[fNormalizedValue.length++] = ' '; int next = i+1; if (next < end && value.charAt(next)==0x000A) i=next; // skip following xA } else { fNormalizedValue.ch[fNormalizedValue.length++] = c; } } if (normalized){ value = fNormalizedValue.toString(); attr.setValue(value); } return value; } protected final class XMLAttributesProxy implements XMLAttributes { protected AttributeMap fAttributes; protected CoreDocumentImpl fDocument; protected ElementImpl fElement; protected final Vector fAugmentations = new Vector(5); public void setAttributes(AttributeMap attributes, CoreDocumentImpl doc, ElementImpl elem) { fDocument = doc; fAttributes = attributes; fElement = elem; if (attributes != null) { int length = attributes.getLength(); fAugmentations.setSize(length); // REVISIT: this implementation does not store any value in augmentations // and basically not keeping augs in parallel to attributes map // untill all attributes are added (default attributes) for (int i = 0; i < length; i++) { fAugmentations.setElementAt(new AugmentationsImpl(), i); } } else { fAugmentations.setSize(0); } } /** * This method adds default declarations * @see com.sun.org.apache.xerces.internal.xni.XMLAttributes#addAttribute(QName, String, String) */ public int addAttribute(QName qname, String attrType, String attrValue) { int index = fElement.getXercesAttribute(qname.uri, qname.localpart); // add defaults to the tree if (index < 0) { // the default attribute was removed by a user and needed to // be added back AttrImpl attr = (AttrImpl) ((CoreDocumentImpl) fElement.getOwnerDocument()).createAttributeNS( qname.uri, qname.rawname, qname.localpart); // REVISIT: the following should also update ID table attr.setNodeValue(attrValue); index = fElement.setXercesAttributeNode(attr); fAugmentations.insertElementAt(new AugmentationsImpl(), index); attr.setSpecified(false); } else { // default attribute is in the tree // we don't need to do anything since prefix was already fixed // at the namespace fixup time and value must be same value, otherwise // attribute will be treated as specified and we will never reach // this method. } return index; } public void removeAllAttributes(){ // REVISIT: implement } public void removeAttributeAt(int attrIndex){ // REVISIT: implement } public int getLength(){ return(fAttributes != null)?fAttributes.getLength():0; } public int getIndex(String qName){ // REVISIT: implement return -1; } public int getIndex(String uri, String localPart){ // REVISIT: implement return -1; } public void setName(int attrIndex, QName attrName){ // REVISIT: implement } public void getName(int attrIndex, QName attrName){ if (fAttributes !=null) { updateQName((Node)fAttributes.getItem(attrIndex), attrName); } } public String getPrefix(int index){ // REVISIT: implement return null; } public String getURI(int index){ // REVISIT: implement return null; } public String getLocalName(int index){ // REVISIT: implement return null; } public String getQName(int index){ // REVISIT: implement return null; } public QName getQualifiedName(int index){ //return fAttributes.item(index).ge); return null; } public void setType(int attrIndex, String attrType){ // REVISIT: implement } public String getType(int index){ return "CDATA"; } public String getType(String qName){ return "CDATA"; } public String getType(String uri, String localName){ return "CDATA"; } public void setValue(int attrIndex, String attrValue){ // REVISIT: is this desired behaviour? // The values are updated in the case datatype-normalization is turned on // in this case we need to make sure that specified attributes stay specified if (fAttributes != null){ AttrImpl attr = (AttrImpl)fAttributes.getItem(attrIndex); boolean specified = attr.getSpecified(); attr.setValue(attrValue); attr.setSpecified(specified); } } public void setValue(int attrIndex, String attrValue, XMLString value){ setValue(attrIndex, value.toString()); } public String getValue(int index){ return (fAttributes !=null)?fAttributes.item(index).getNodeValue():""; } public String getValue(String qName){ // REVISIT: implement return null; } public String getValue(String uri, String localName){ if (fAttributes != null) { Node node = fAttributes.getNamedItemNS(uri, localName); return(node != null)? node.getNodeValue():null; } return null; } public void setNonNormalizedValue(int attrIndex, String attrValue){ // REVISIT: implement } public String getNonNormalizedValue(int attrIndex){ // REVISIT: implement return null; } public void setSpecified(int attrIndex, boolean specified){ AttrImpl attr = (AttrImpl)fAttributes.getItem(attrIndex); attr.setSpecified(specified); } public boolean isSpecified(int attrIndex){ return((Attr)fAttributes.getItem(attrIndex)).getSpecified(); } public Augmentations getAugmentations (int attributeIndex){ return(Augmentations)fAugmentations.elementAt(attributeIndex); } public Augmentations getAugmentations (String uri, String localPart){ // REVISIT: implement return null; } public Augmentations getAugmentations(String qName){ // REVISIT: implement return null; } /** * Sets the augmentations of the attribute at the specified index. * * @param attrIndex The attribute index. * @param augs The augmentations. */ public void setAugmentations(int attrIndex, Augmentations augs) { fAugmentations.setElementAt(augs, attrIndex); } } // // XMLDocumentHandler methods // /** * The start of the document. * * @param locator The document locator, or null if the document * location cannot be reported during the parsing * of this document. However, it is <em>strongly</em> * recommended that a locator be supplied that can * at least report the system identifier of the * document. * @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 namespaceContext * The namespace context in effect at the * start of this document. * This object represents the current context. * Implementors of this class are responsible * for copying the namespace bindings from the * the current context (and its parent contexts) * if that information is important. * * @param augs Additional information that may include infoset augmentations * @exception XNIException * Thrown by handler to signal an error. */ public void startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException{ } /** * 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 * * @exception XNIException * Thrown by handler to signal an error. */ public void xmlDecl(String version, String encoding, String standalone, Augmentations augs) throws XNIException{ } /** * 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 * * @exception XNIException * Thrown by handler to signal an error. */ public void doctypeDecl(String rootElement, String publicId, String systemId, Augmentations augs) throws XNIException{ } /** * A comment. * * @param text The text in the comment. * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by application to signal an error. */ public void comment(XMLString text, Augmentations augs) throws XNIException{ } /** * 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 * * @exception XNIException * Thrown by handler to signal an error. */ public void processingInstruction(String target, XMLString data, Augmentations augs) throws XNIException{ } /** * 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 * * @exception XNIException * Thrown by handler to signal an error. */ public void startElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { Element currentElement = (Element) fCurrentNode; int attrCount = attributes.getLength(); if (DEBUG_EVENTS) { System.out.println("==>startElement: " +element+ " attrs.length="+attrCount); } for (int i = 0; i < attrCount; i++) { attributes.getName(i, fAttrQName); Attr attr = null; attr = currentElement.getAttributeNodeNS(fAttrQName.uri, fAttrQName.localpart); AttributePSVI attrPSVI = (AttributePSVI) attributes.getAugmentations(i).getItem(Constants.ATTRIBUTE_PSVI); if (attrPSVI != null) { //REVISIT: instead we should be using augmentations: // to set/retrieve Id attributes XSTypeDefinition decl = attrPSVI.getMemberTypeDefinition(); boolean id = false; if (decl != null){ id = ((XSSimpleType)decl).isIDType(); } else{ decl = attrPSVI.getTypeDefinition(); if (decl !=null){ id = ((XSSimpleType)decl).isIDType(); } } if (id){ ((ElementImpl)currentElement).setIdAttributeNode(attr, true); } if (fPSVI) { ((PSVIAttrNSImpl) attr).setPSVI(attrPSVI); } if ((fConfiguration.features & DOMConfigurationImpl.DTNORMALIZATION) != 0) { // datatype-normalization // NOTE: The specified value MUST be set after we set // the node value because that turns the "specified" // flag to "true" which may overwrite a "false" // value from the attribute list. boolean specified = attr.getSpecified(); attr.setValue(attrPSVI.getSchemaNormalizedValue()); if (!specified) { ((AttrImpl) attr).setSpecified(specified); } } } } } /** * An empty element. * * @param element The name of the element. * @param attributes The element attributes. * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by handler to signal an error. */ public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { if (DEBUG_EVENTS) { System.out.println("==>emptyElement: " +element); } startElement(element, attributes, augs); endElement(element, augs); } /** * This method notifies the start of a general 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 general entity. * @param identifier The resource identifier. * @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 * * @exception XNIException Thrown by handler to signal an error. */ public void startGeneralEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs) throws XNIException{ } /** * 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{ } /** * This method notifies the end of a general 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 endGeneralEntity(String name, Augmentations augs) throws XNIException{ } /** * Character content. * * @param text The content. * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by handler to signal an error. */ public void characters(XMLString text, Augmentations augs) throws XNIException{ } /** * 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 * * @exception XNIException * Thrown by handler to signal an error. */ public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException{ allWhitespace = true; } /** * The end of an element. * * @param element The name of the element. * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by handler to signal an error. */ public void endElement(QName element, Augmentations augs) throws XNIException { if (DEBUG_EVENTS) { System.out.println("==>endElement: " + element); } if(augs != null) { ElementPSVI elementPSVI = (ElementPSVI) augs.getItem(Constants.ELEMENT_PSVI); if (elementPSVI != null) { ElementImpl elementNode = (ElementImpl) fCurrentNode; if (fPSVI) { ((PSVIElementNSImpl) fCurrentNode).setPSVI(elementPSVI); } // include element default content (if one is available) String normalizedValue = elementPSVI.getSchemaNormalizedValue(); if ((fConfiguration.features & DOMConfigurationImpl.DTNORMALIZATION) != 0) { if (normalizedValue !=null) elementNode.setTextContent(normalizedValue); } else { // NOTE: this is a hack: it is possible that DOM had an empty element // and validator sent default value using characters(), which we don't // implement. Thus, here we attempt to add the default value. String text = elementNode.getTextContent(); if (text.length() == 0) { // default content could be provided if (normalizedValue !=null) elementNode.setTextContent(normalizedValue); } } } } } /** * The start of a CDATA section. * * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by handler to signal an error. */ public void startCDATA(Augmentations augs) throws XNIException{ } /** * The end of a CDATA section. * * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by handler to signal an error. */ public void endCDATA(Augmentations augs) throws XNIException{ } /** * The end of the document. * * @param augs Additional information that may include infoset augmentations * * @exception XNIException * Thrown by handler to signal an error. */ public void endDocument(Augmentations augs) throws XNIException{ } /** Sets the document source. */ public void setDocumentSource(XMLDocumentSource source){ } /** Returns the document source. */ public XMLDocumentSource getDocumentSource(){ return null; } } // DOMNormalizer class