blob: c4268dc4a5334ac68462f50e04f9c84129aa0a89 [file] [log] [blame]
/*
* 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.
* @version $Id: DOMNormalizer.java,v 1.9 2010-11-01 04:39:38 joehw Exp $
*/
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;
} catch (RuntimeException e) {
throw e; // otherwise re-throw.
}
}
/**
*
* 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);
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 "&amp;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