blob: 0b624230b8d73a473f616ec572e4c99f6bdff2c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*
* $Id: $
*/
package org.apache.xml.serializer.dom3;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import org.apache.xml.serializer.dom3.NamespaceSupport;
import org.apache.xml.serializer.OutputPropertiesFactory;
import org.apache.xml.serializer.SerializationHandler;
import org.apache.xml.serializer.utils.MsgKey;
import org.apache.xml.serializer.utils.Utils;
import org.apache.xml.serializer.utils.XML11Char;
import org.apache.xml.serializer.utils.XMLChar;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
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.EntityReference;
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;
import org.w3c.dom.ls.LSSerializerFilter;
import org.w3c.dom.traversal.NodeFilter;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.LocatorImpl;
/**
* Built on org.apache.xml.serializer.TreeWalker and adds functionality to
* traverse and serialize a DOM Node (Level 2 or Level 3) as specified in
* the DOM Level 3 LS Recommedation by evaluating and applying DOMConfiguration
* parameters and filters if any during serialization.
*
* @xsl.usage internal
*/
final class DOM3TreeWalker {
/**
* The SerializationHandler, it extends ContentHandler and when
* this class is instantiated via the constructor provided, a
* SerializationHandler object is passed to it.
*/
private SerializationHandler fSerializer = null;
/** We do not need DOM2Helper since DOM Level 3 LS applies to DOM Level 2 or newer */
/** Locator object for this TreeWalker */
private LocatorImpl fLocator = new LocatorImpl();
/** ErrorHandler */
private DOMErrorHandler fErrorHandler = null;
/** LSSerializerFilter */
private LSSerializerFilter fFilter = null;
/** If the serializer is an instance of a LexicalHandler */
private LexicalHandler fLexicalHandler = null;
private int fWhatToShowFilter;
/** New Line character to use in serialization */
private String fNewLine = null;
/** DOMConfiguration Properties */
private Properties fDOMConfigProperties = null;
/** Keeps track if we are in an entity reference when entities=true */
private boolean fInEntityRef = false;
/** Stores the version of the XML document to be serialize */
private String fXMLVersion = null;
/** XML Version, default 1.0 */
private boolean fIsXMLVersion11 = false;
/** Is the Node a Level 3 DOM node */
private boolean fIsLevel3DOM = false;
/** DOM Configuration Parameters */
private int fFeatures = 0;
/** Flag indicating whether following text to be processed is raw text */
boolean fNextIsRaw = false;
//
private static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
//
private static final String XMLNS_PREFIX = "xmlns";
//
private static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
//
private static final String XML_PREFIX = "xml";
/** stores namespaces in scope */
protected NamespaceSupport fNSBinder;
/** stores all namespace bindings on the current element */
protected NamespaceSupport fLocalNSBinder;
/** stores the current element depth */
private int fElementDepth = 0;
// ***********************************************************************
// DOMConfiguration paramter settings
// ***********************************************************************
// Parameter canonical-form, true [optional] - NOT SUPPORTED
private final static int CANONICAL = 0x1 << 0;
// Parameter cdata-sections, true [required] (default)
private final static int CDATA = 0x1 << 1;
// Parameter check-character-normalization, true [optional] - NOT SUPPORTED
private final static int CHARNORMALIZE = 0x1 << 2;
// Parameter comments, true [required] (default)
private final static int COMMENTS = 0x1 << 3;
// Parameter datatype-normalization, true [optional] - NOT SUPPORTED
private final static int DTNORMALIZE = 0x1 << 4;
// Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED
private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5;
// Parameter entities, true [required] (default)
private final static int ENTITIES = 0x1 << 6;
// Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer
private final static int INFOSET = 0x1 << 7;
// Parameter namespaces, true [required] (default)
private final static int NAMESPACES = 0x1 << 8;
// Parameter namespace-declarations, true [required] (default)
private final static int NAMESPACEDECLS = 0x1 << 9;
// Parameter normalize-characters, true [optional] - NOT SUPPORTED
private final static int NORMALIZECHARS = 0x1 << 10;
// Parameter split-cdata-sections, true [required] (default)
private final static int SPLITCDATA = 0x1 << 11;
// Parameter validate, true [optional] - NOT SUPPORTED
private final static int VALIDATE = 0x1 << 12;
// Parameter validate-if-schema, true [optional] - NOT SUPPORTED
private final static int SCHEMAVALIDATE = 0x1 << 13;
// Parameter split-cdata-sections, true [required] (default)
private final static int WELLFORMED = 0x1 << 14;
// Parameter discard-default-content, true [required] (default)
// Not sure how this will be used in level 2 Documents
private final static int DISCARDDEFAULT = 0x1 << 15;
// Parameter format-pretty-print, true [optional]
private final static int PRETTY_PRINT = 0x1 << 16;
// Parameter ignore-unknown-character-denormalizations, true [required] (default)
// We currently do not support XML 1.1 character normalization
private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17;
// Parameter discard-default-content, true [required] (default)
private final static int XMLDECL = 0x1 << 18;
/**
* Constructor.
* @param contentHandler serialHandler The implemention of the SerializationHandler interface
*/
DOM3TreeWalker(
SerializationHandler serialHandler,
DOMErrorHandler errHandler,
LSSerializerFilter filter,
String newLine) {
fSerializer = serialHandler;
//fErrorHandler = errHandler == null ? new DOMErrorHandlerImpl() : errHandler; // Should we be using the default?
fErrorHandler = errHandler;
fFilter = filter;
fLexicalHandler = null;
fNewLine = newLine;
fNSBinder = new NamespaceSupport();
fLocalNSBinder = new NamespaceSupport();
fDOMConfigProperties = fSerializer.getOutputFormat();
fSerializer.setDocumentLocator(fLocator);
initProperties(fDOMConfigProperties);
try {
// Bug see Bugzilla 26741
fLocator.setSystemId(
System.getProperty("user.dir") + File.separator + "dummy.xsl");
} catch (SecurityException se) { // user.dir not accessible from applet
}
}
/**
* Perform a pre-order traversal non-recursive style.
*
* Note that TreeWalker assumes that the subtree is intended to represent
* a complete (though not necessarily well-formed) document and, during a
* traversal, startDocument and endDocument will always be issued to the
* SAX listener.
*
* @param pos Node in the tree where to start traversal
*
* @throws TransformerException
*/
public void traverse(Node pos) throws org.xml.sax.SAXException {
this.fSerializer.startDocument();
// Determine if the Node is a DOM Level 3 Core Node.
if (pos.getNodeType() != Node.DOCUMENT_NODE) {
Document ownerDoc = pos.getOwnerDocument();
if (ownerDoc != null
&& ownerDoc.getImplementation().hasFeature("Core", "3.0")) {
fIsLevel3DOM = true;
}
} else {
if (((Document) pos)
.getImplementation()
.hasFeature("Core", "3.0")) {
fIsLevel3DOM = true;
}
}
if (fSerializer instanceof LexicalHandler) {
fLexicalHandler = ((LexicalHandler) this.fSerializer);
}
if (fFilter != null)
fWhatToShowFilter = fFilter.getWhatToShow();
Node top = pos;
while (null != pos) {
startNode(pos);
Node nextNode = null;
nextNode = pos.getFirstChild();
while (null == nextNode) {
endNode(pos);
if (top.equals(pos))
break;
nextNode = pos.getNextSibling();
if (null == nextNode) {
pos = pos.getParentNode();
if ((null == pos) || (top.equals(pos))) {
if (null != pos)
endNode(pos);
nextNode = null;
break;
}
}
}
pos = nextNode;
}
this.fSerializer.endDocument();
}
/**
* Perform a pre-order traversal non-recursive style.
* Note that TreeWalker assumes that the subtree is intended to represent
* a complete (though not necessarily well-formed) document and, during a
* traversal, startDocument and endDocument will always be issued to the
* SAX listener.
*
* @param pos Node in the tree where to start traversal
* @param top Node in the tree where to end traversal
*
* @throws TransformerException
*/
public void traverse(Node pos, Node top) throws org.xml.sax.SAXException {
this.fSerializer.startDocument();
// Determine if the Node is a DOM Level 3 Core Node.
if (pos.getNodeType() != Node.DOCUMENT_NODE) {
Document ownerDoc = pos.getOwnerDocument();
if (ownerDoc != null
&& ownerDoc.getImplementation().hasFeature("Core", "3.0")) {
fIsLevel3DOM = true;
}
} else {
if (((Document) pos)
.getImplementation()
.hasFeature("Core", "3.0")) {
fIsLevel3DOM = true;
}
}
if (fSerializer instanceof LexicalHandler) {
fLexicalHandler = ((LexicalHandler) this.fSerializer);
}
if (fFilter != null)
fWhatToShowFilter = fFilter.getWhatToShow();
while (null != pos) {
startNode(pos);
Node nextNode = null;
nextNode = pos.getFirstChild();
while (null == nextNode) {
endNode(pos);
if ((null != top) && top.equals(pos))
break;
nextNode = pos.getNextSibling();
if (null == nextNode) {
pos = pos.getParentNode();
if ((null == pos) || ((null != top) && top.equals(pos))) {
nextNode = null;
break;
}
}
}
pos = nextNode;
}
this.fSerializer.endDocument();
}
/**
* Optimized dispatch of characters.
*/
private final void dispatachChars(Node node)
throws org.xml.sax.SAXException {
if (fSerializer != null) {
this.fSerializer.characters(node);
} else {
String data = ((Text) node).getData();
this.fSerializer.characters(data.toCharArray(), 0, data.length());
}
}
/**
* Start processing given node
*
* @param node Node to process
*
* @throws org.xml.sax.SAXException
*/
protected void startNode(Node node) throws org.xml.sax.SAXException {
if (node instanceof Locator) {
Locator loc = (Locator) node;
fLocator.setColumnNumber(loc.getColumnNumber());
fLocator.setLineNumber(loc.getLineNumber());
fLocator.setPublicId(loc.getPublicId());
fLocator.setSystemId(loc.getSystemId());
} else {
fLocator.setColumnNumber(0);
fLocator.setLineNumber(0);
}
switch (node.getNodeType()) {
case Node.DOCUMENT_TYPE_NODE :
serializeDocType((DocumentType) node, true);
break;
case Node.COMMENT_NODE :
serializeComment((Comment) node);
break;
case Node.DOCUMENT_FRAGMENT_NODE :
// Children are traversed
break;
case Node.DOCUMENT_NODE :
break;
case Node.ELEMENT_NODE :
serializeElement((Element) node, true);
break;
case Node.PROCESSING_INSTRUCTION_NODE :
serializePI((ProcessingInstruction) node);
break;
case Node.CDATA_SECTION_NODE :
serializeCDATASection((CDATASection) node);
break;
case Node.TEXT_NODE :
serializeText((Text) node);
break;
case Node.ENTITY_REFERENCE_NODE :
serializeEntityReference((EntityReference) node, true);
break;
default :
}
}
/**
* End processing of given node
*
*
* @param node Node we just finished processing
*
* @throws org.xml.sax.SAXException
*/
protected void endNode(Node node) throws org.xml.sax.SAXException {
switch (node.getNodeType()) {
case Node.DOCUMENT_NODE :
break;
case Node.DOCUMENT_TYPE_NODE :
serializeDocType((DocumentType) node, false);
break;
case Node.ELEMENT_NODE :
serializeElement((Element) node, false);
break;
case Node.CDATA_SECTION_NODE :
break;
case Node.ENTITY_REFERENCE_NODE :
serializeEntityReference((EntityReference) node, false);
break;
default :
}
}
// ***********************************************************************
// Node serialization methods
// ***********************************************************************
/**
* Applies a filter on the node to serialize
*
* @param node The Node to serialize
* @return True if the node is to be serialized else false if the node
* is to be rejected or skipped.
*/
protected boolean applyFilter(Node node, int nodeType) {
if (fFilter != null && (fWhatToShowFilter & nodeType) != 0) {
short code = fFilter.acceptNode(node);
switch (code) {
case NodeFilter.FILTER_REJECT :
case NodeFilter.FILTER_SKIP :
return false; // skip the node
default : // fall through..
}
}
return true;
}
/**
* Serializes a Document Type Node.
*
* @param node The Docuemnt Type Node to serialize
* @param bStart Invoked at the start or end of node. Default true.
*/
protected void serializeDocType(DocumentType node, boolean bStart)
throws SAXException {
// The DocType and internalSubset can not be modified in DOM and is
// considered to be well-formed as the outcome of successful parsing.
String docTypeName = node.getNodeName();
String publicId = node.getPublicId();
String systemId = node.getSystemId();
String internalSubset = node.getInternalSubset();
//DocumentType nodes are never passed to the filter
if (internalSubset != null && !"".equals(internalSubset)) {
if (bStart) {
try {
// The Serializer does not provide a way to write out the
// DOCTYPE internal subset via an event call, so we write it
// out here.
Writer writer = fSerializer.getWriter();
StringBuffer dtd = new StringBuffer();
dtd.append("<!DOCTYPE ");
dtd.append(docTypeName);
if (null != publicId) {
dtd.append(" PUBLIC \"");
dtd.append(publicId);
dtd.append('\"');
}
if (null != systemId) {
if (null == publicId) {
dtd.append(" SYSTEM \"");
} else {
dtd.append(" \"");
}
dtd.append(systemId);
dtd.append('\"');
}
dtd.append(" [ ");
dtd.append(fNewLine);
dtd.append(internalSubset);
dtd.append("]>");
dtd.append(new String(fNewLine));
writer.write(dtd.toString());
writer.flush();
} catch (IOException e) {
throw new SAXException(Utils.messages.createMessage(
MsgKey.ER_WRITING_INTERNAL_SUBSET, null), e);
}
} // else if !bStart do nothing
} else {
if (bStart) {
if (fLexicalHandler != null) {
fLexicalHandler.startDTD(docTypeName, publicId, systemId);
}
} else {
if (fLexicalHandler != null) {
fLexicalHandler.endDTD();
}
}
}
}
/**
* Serializes a Comment Node.
*
* @param node The Comment Node to serialize
*/
protected void serializeComment(Comment node) throws SAXException {
// comments=true
if ((fFeatures & COMMENTS) != 0) {
String data = node.getData();
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isCommentWellFormed(data);
}
if (fLexicalHandler != null) {
// apply the LSSerializer filter after the operations requested by the
// DOMConfiguration parameters have been applied
if (!applyFilter(node, NodeFilter.SHOW_COMMENT)) {
return;
}
fLexicalHandler.comment(data.toCharArray(), 0, data.length());
}
}
}
/**
* Serializes an Element Node.
*
* @param node The Element Node to serialize
* @param bStart Invoked at the start or end of node.
*/
protected void serializeElement(Element node, boolean bStart)
throws SAXException {
if (bStart) {
fElementDepth++;
// We use the Xalan specific startElement and starPrefixMapping calls
// (and addAttribute and namespaceAfterStartElement) as opposed to
// SAX specific, for performance reasons as they reduce the overhead
// of creating an AttList object upfront.
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isElementWellFormed(node);
}
// REVISIT: We apply the LSSerializer filter for elements before
// namesapce fixup
if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) {
return;
}
// namespaces=true, record and fixup namspaced element
if ((fFeatures & NAMESPACES) != 0) {
fNSBinder.pushContext();
fLocalNSBinder.reset();
recordLocalNSDecl(node);
fixupElementNS(node);
}
// Namespace normalization
fSerializer.startElement(
node.getNamespaceURI(),
node.getLocalName(),
node.getNodeName());
serializeAttList(node);
} else {
fElementDepth--;
// apply the LSSerializer filter
if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) {
return;
}
this.fSerializer.endElement(
node.getNamespaceURI(),
node.getLocalName(),
node.getNodeName());
// since endPrefixMapping was not used by SerializationHandler it was removed
// for performance reasons.
if ((fFeatures & NAMESPACES) != 0 ) {
fNSBinder.popContext();
}
}
}
/**
* Serializes the Attr Nodes of an Element.
*
* @param node The OwnerElement whose Attr Nodes are to be serialized.
*/
protected void serializeAttList(Element node) throws SAXException {
NamedNodeMap atts = node.getAttributes();
int nAttrs = atts.getLength();
for (int i = 0; i < nAttrs; i++) {
Node attr = atts.item(i);
String localName = attr.getLocalName();
String attrName = attr.getNodeName();
String attrPrefix = attr.getPrefix() == null ? "" : attr.getPrefix();
String attrValue = attr.getNodeValue();
// Determine the Attr's type.
String type = null;
if (fIsLevel3DOM) {
type = ((Attr) attr).getSchemaTypeInfo().getTypeName();
}
type = type == null ? "CDATA" : type;
String attrNS = attr.getNamespaceURI();
if (attrNS !=null && attrNS.length() == 0) {
attrNS=null;
// we must remove prefix for this attribute
attrName=attr.getLocalName();
}
boolean isSpecified = ((Attr) attr).getSpecified();
boolean addAttr = true;
boolean applyFilter = false;
boolean xmlnsAttr =
attrName.equals("xmlns") || attrName.startsWith("xmlns:");
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isAttributeWellFormed(attr);
}
//-----------------------------------------------------------------
// start Attribute namespace fixup
//-----------------------------------------------------------------
// namespaces=true, normalize all non-namespace attributes
// Step 3. Attribute
if ((fFeatures & NAMESPACES) != 0 && !xmlnsAttr) {
// If the Attr has a namespace URI
if (attrNS != null) {
attrPrefix = attrPrefix == null ? "" : attrPrefix;
String declAttrPrefix = fNSBinder.getPrefix(attrNS);
String declAttrNS = fNSBinder.getURI(attrPrefix);
// 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 conflicts with a binding
if ("".equals(attrPrefix) || "".equals(declAttrPrefix)
|| !attrPrefix.equals(declAttrPrefix)) {
// namespaceURI matches an in scope declaration of one or
// more prefixes
if (declAttrPrefix != null && !"".equals(declAttrPrefix)) {
// pick the prefix that was found and change attribute's
// prefix and nodeName.
attrPrefix = declAttrPrefix;
if (declAttrPrefix.length() > 0 ) {
attrName = declAttrPrefix + ":" + localName;
} else {
attrName = localName;
}
} else {
// The current prefix is not null and it has no in scope
// declaration
if (attrPrefix != null && !"".equals(attrPrefix)
&& declAttrNS == null) {
// declare this prefix
if ((fFeatures & NAMESPACEDECLS) != 0) {
fSerializer.addAttribute(XMLNS_URI, attrPrefix,
XMLNS_PREFIX + ":" + attrPrefix, "CDATA",
attrNS);
fNSBinder.declarePrefix(attrPrefix, attrNS);
fLocalNSBinder.declarePrefix(attrPrefix, attrNS);
}
} 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;
attrPrefix = "NS" + counter++;
while (fLocalNSBinder.getURI(attrPrefix) != null) {
attrPrefix = "NS" + counter++;
}
// change attribute's prefix and Name
attrName = attrPrefix + ":" + localName;
// create a local namespace declaration attribute
// Add the xmlns declaration attribute
if ((fFeatures & NAMESPACEDECLS) != 0) {
fSerializer.addAttribute(XMLNS_URI, attrPrefix,
XMLNS_PREFIX + ":" + attrPrefix, "CDATA",
attrNS);
fNSBinder.declarePrefix(attrPrefix, attrNS);
fLocalNSBinder.declarePrefix(attrPrefix, attrNS);
}
}
}
}
} else { // if the Attr has no namespace URI
// Attr has no localName
if (localName == null) {
// DOM Level 1 node!
String msg = Utils.messages.createMessage(
MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
new Object[] { attrName });
if (fErrorHandler != null) {
fErrorHandler
.handleError(new DOMErrorImpl(
DOMError.SEVERITY_ERROR, msg,
MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, null,
null, null));
}
} else { // uri=null and no colon
// attr has no namespace URI and no prefix
// no action is required, since attrs don't use default
}
}
}
// discard-default-content=true
// Default attr's are not passed to the filter and this contraint
// is applied only when discard-default-content=true
// What about default xmlns attributes???? check for xmlnsAttr
if ((((fFeatures & DISCARDDEFAULT) != 0) && isSpecified)
|| ((fFeatures & DISCARDDEFAULT) == 0)) {
applyFilter = true;
} else {
addAttr = false;
}
if (applyFilter) {
// apply the filter for Attributes that are not default attributes
// or namespace decl attributes
if (fFilter != null
&& (fFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)
!= 0) {
if (!xmlnsAttr) {
short code = fFilter.acceptNode(attr);
switch (code) {
case NodeFilter.FILTER_REJECT :
case NodeFilter.FILTER_SKIP :
addAttr = false;
break;
default : //fall through..
}
}
}
}
// if the node is a namespace node
if (addAttr && xmlnsAttr) {
// If namespace-declarations=true, add the node , else don't add it
if ((fFeatures & NAMESPACEDECLS) != 0) {
// The namespace may have been fixed up, in that case don't add it.
if (localName != null && !"".equals(localName)) {
fSerializer.addAttribute(attrNS, localName, attrName, type, attrValue);
}
}
} else if (
addAttr && !xmlnsAttr) { // if the node is not a namespace node
// If namespace-declarations=true, add the node with the Attr nodes namespaceURI
// else add the node setting it's namespace to null or else the serializer will later
// attempt to add a xmlns attr for the prefixed attribute
if (((fFeatures & NAMESPACEDECLS) != 0) && (attrNS != null)) {
fSerializer.addAttribute(
attrNS,
localName,
attrName,
type,
attrValue);
} else {
fSerializer.addAttribute(
"",
localName,
attrName,
type,
attrValue);
}
}
//
if (xmlnsAttr && ((fFeatures & NAMESPACEDECLS) != 0)) {
int index;
// Use "" instead of null, as Xerces likes "" for the
// name of the default namespace. Fix attributed
// to "Steven Murray" <smurray@ebt.com>.
String prefix =
(index = attrName.indexOf(":")) < 0
? ""
: attrName.substring(index + 1);
if (!"".equals(prefix)) {
fSerializer.namespaceAfterStartElement(prefix, attrValue);
}
}
}
}
/**
* Serializes an ProcessingInstruction Node.
*
* @param node The ProcessingInstruction Node to serialize
*/
protected void serializePI(ProcessingInstruction node)
throws SAXException {
ProcessingInstruction pi = node;
String name = pi.getNodeName();
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isPIWellFormed(node);
}
// apply the LSSerializer filter
if (!applyFilter(node, NodeFilter.SHOW_PROCESSING_INSTRUCTION)) {
return;
}
// String data = pi.getData();
if (name.equals("xslt-next-is-raw")) {
fNextIsRaw = true;
} else {
this.fSerializer.processingInstruction(name, pi.getData());
}
}
/**
* Serializes an CDATASection Node.
*
* @param node The CDATASection Node to serialize
*/
protected void serializeCDATASection(CDATASection node)
throws SAXException {
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isCDATASectionWellFormed(node);
}
// cdata-sections = true
if ((fFeatures & CDATA) != 0) {
// split-cdata-sections = true
// Assumption: This parameter has an effect only when
// cdata-sections=true
// ToStream, by default splits cdata-sections. Hence the check
// below.
String nodeValue = node.getNodeValue();
int endIndex = nodeValue.indexOf("]]>");
if ((fFeatures & SPLITCDATA) != 0) {
if (endIndex >= 0) {
// The first node split will contain the ]] markers
String relatedData = nodeValue.substring(0, endIndex + 2);
String msg =
Utils.messages.createMessage(
MsgKey.ER_CDATA_SECTIONS_SPLIT,
null);
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_WARNING,
msg,
MsgKey.ER_CDATA_SECTIONS_SPLIT,
null,
relatedData,
null));
}
}
} else {
if (endIndex >= 0) {
// The first node split will contain the ]] markers
String relatedData = nodeValue.substring(0, endIndex + 2);
String msg =
Utils.messages.createMessage(
MsgKey.ER_CDATA_SECTIONS_SPLIT,
null);
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_ERROR,
msg,
MsgKey.ER_CDATA_SECTIONS_SPLIT));
}
// Report an error and return. What error???
return;
}
}
// apply the LSSerializer filter
if (!applyFilter(node, NodeFilter.SHOW_CDATA_SECTION)) {
return;
}
// splits the cdata-section
if (fLexicalHandler != null) {
fLexicalHandler.startCDATA();
}
dispatachChars(node);
if (fLexicalHandler != null) {
fLexicalHandler.endCDATA();
}
} else {
dispatachChars(node);
}
}
/**
* Serializes an Text Node.
*
* @param node The Text Node to serialize
*/
protected void serializeText(Text node) throws SAXException {
if (fNextIsRaw) {
fNextIsRaw = false;
fSerializer.processingInstruction(
javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING,
"");
dispatachChars(node);
fSerializer.processingInstruction(
javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING,
"");
} else {
// keep track of dispatch or not to avoid duplicaiton of filter code
boolean bDispatch = false;
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isTextWellFormed(node);
}
// if the node is whitespace
// Determine the Attr's type.
boolean isElementContentWhitespace = false;
if (fIsLevel3DOM) {
isElementContentWhitespace =
node.isElementContentWhitespace();
}
if (isElementContentWhitespace) {
// element-content-whitespace=true
if ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) {
bDispatch = true;
}
} else {
bDispatch = true;
}
// apply the LSSerializer filter
if (!applyFilter(node, NodeFilter.SHOW_TEXT)) {
return;
}
if (bDispatch) {
dispatachChars(node);
}
}
}
/**
* Serializes an EntityReference Node.
*
* @param node The EntityReference Node to serialize
* @param bStart Inicates if called from start or endNode
*/
protected void serializeEntityReference(
EntityReference node,
boolean bStart)
throws SAXException {
if (bStart) {
EntityReference eref = node;
// entities=true
if ((fFeatures & ENTITIES) != 0) {
// perform well-formedness and other checking only if
// entities = true
// well-formed=true
if ((fFeatures & WELLFORMED) != 0) {
isEntityReferneceWellFormed(node);
}
// check "unbound-prefix-in-entity-reference" [fatal]
// Raised if the configuration parameter "namespaces" is set to true
if ((fFeatures & NAMESPACES) != 0) {
checkUnboundPrefixInEntRef(node);
}
// The filter should not apply in this case, since the
// EntityReference is not being expanded.
// should we pass entity reference nodes to the filter???
}
if (fLexicalHandler != null) {
// startEntity outputs only Text but not Element, Attr, Comment
// and PI child nodes. It does so by setting the m_inEntityRef
// in ToStream and using this to decide if a node is to be
// serialized or not.
fLexicalHandler.startEntity(eref.getNodeName());
}
} else {
EntityReference eref = node;
// entities=true or false,
if (fLexicalHandler != null) {
fLexicalHandler.endEntity(eref.getNodeName());
}
}
}
// ***********************************************************************
// Methods to check well-formedness
// ***********************************************************************
/**
* Taken from org.apache.xerces.dom.CoreDocumentImpl
*
* Check the string against XML's definition of acceptable names for
* elements and attributes and so on using the XMLCharacterProperties
* utility class
*/
protected boolean isXMLName(String s, boolean xml11Version) {
if (s == null) {
return false;
}
if (!xml11Version)
return XMLChar.isValidName(s);
else
return XML11Char.isXML11ValidName(s);
}
/**
* Taken from org.apache.xerces.dom.CoreDocumentImpl
*
* Checks if the given qualified name is legal with respect
* to the version of XML to which this document must conform.
*
* @param prefix prefix of qualified name
* @param local local part of qualified name
*/
protected boolean isValidQName(
String prefix,
String local,
boolean xml11Version) {
// check that both prefix and local part match NCName
if (local == null)
return false;
boolean validNCName = false;
if (!xml11Version) {
validNCName =
(prefix == null || XMLChar.isValidNCName(prefix))
&& XMLChar.isValidNCName(local);
} else {
validNCName =
(prefix == null || XML11Char.isXML11ValidNCName(prefix))
&& XML11Char.isXML11ValidNCName(local);
}
return validNCName;
}
/**
* Checks if a XML character is well-formed
*
* @param characters A String of characters to be checked for Well-Formedness
* @param refInvalidChar A reference to the character to be returned that was determined invalid.
*/
protected boolean isWFXMLChar(String chardata, Character refInvalidChar) {
if (chardata == null || (chardata.length() == 0)) {
return true;
}
char[] dataarray = chardata.toCharArray();
int datalength = dataarray.length;
// version of the document is XML 1.1
if (fIsXMLVersion11) {
//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;
}
}
// Reference to invalid character which is returned
refInvalidChar = new Character(ch);
return false;
}
}
} // 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;
}
}
// Reference to invalid character which is returned
refInvalidChar = new Character(ch);
return false;
}
}
} // end-else fDocument.isXMLVersion()
return true;
} // isXMLCharWF
/**
* Checks if a XML character is well-formed. If there is a problem with
* the character a non-null Character is returned else null is returned.
*
* @param characters A String of characters to be checked for Well-Formedness
* @return Character A reference to the character to be returned that was determined invalid.
*/
protected Character isWFXMLChar(String chardata) {
Character refInvalidChar;
if (chardata == null || (chardata.length() == 0)) {
return null;
}
char[] dataarray = chardata.toCharArray();
int datalength = dataarray.length;
// version of the document is XML 1.1
if (fIsXMLVersion11) {
//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;
}
}
// Reference to invalid character which is returned
refInvalidChar = new Character(ch);
return refInvalidChar;
}
}
} // 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;
}
}
// Reference to invalid character which is returned
refInvalidChar = new Character(ch);
return refInvalidChar;
}
}
} // end-else fDocument.isXMLVersion()
return null;
} // isXMLCharWF
/**
* Checks if a comment node is well-formed
*
* @param data The contents of the comment node
* @return a boolean indiacating if the comment is well-formed or not.
*/
protected void isCommentWellFormed(String data) {
if (data == null || (data.length() == 0)) {
return;
}
char[] dataarray = data.toCharArray();
int datalength = dataarray.length;
// version of the document is XML 1.1
if (fIsXMLVersion11) {
// 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 =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
new Object[] { new Character(c)});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
} else if (c == '-' && i < datalength && dataarray[i] == '-') {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_DASH_IN_COMMENT,
null);
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
}
}
} // 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 =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
new Object[] { new Character(c)});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
} else if (c == '-' && i < datalength && dataarray[i] == '-') {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_DASH_IN_COMMENT,
null);
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
}
}
}
return;
}
/**
* Checks if an element node is well-formed, by checking its Name for well-formedness.
*
* @param data The contents of the comment node
* @return a boolean indiacating if the comment is well-formed or not.
*/
protected void isElementWellFormed(Node node) {
boolean isNameWF = false;
if ((fFeatures & NAMESPACES) != 0) {
isNameWF =
isValidQName(
node.getPrefix(),
node.getLocalName(),
fIsXMLVersion11);
} else {
isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11);
}
if (!isNameWF) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
new Object[] { "Element", node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
null,
null,
null));
}
}
}
/**
* Checks if an attr node is well-formed, by checking it's Name and value
* for well-formedness.
*
* @param data The contents of the comment node
* @return a boolean indiacating if the comment is well-formed or not.
*/
protected void isAttributeWellFormed(Node node) {
boolean isNameWF = false;
if ((fFeatures & NAMESPACES) != 0) {
isNameWF =
isValidQName(
node.getPrefix(),
node.getLocalName(),
fIsXMLVersion11);
} else {
isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11);
}
if (!isNameWF) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
new Object[] { "Attr", node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
null,
null,
null));
}
}
// Check the Attr's node value
// WFC: No < in Attribute Values
String value = node.getNodeValue();
if (value.indexOf('<') >= 0) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_LT_IN_ATTVAL,
new Object[] {
((Attr) node).getOwnerElement().getNodeName(),
node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_LT_IN_ATTVAL,
null,
null,
null));
}
}
// we need to loop through the children of attr nodes and check their values for
// well-formedness
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
// An attribute node with no text or entity ref child for example
// doc.createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ns");
// followes by
// element.setAttributeNodeNS(attribute);
// can potentially lead to this situation. If the attribute
// was a prefix Namespace attribute declaration then then DOM Core
// should have some exception defined for this.
if (child == null) {
// we should probably report an error
continue;
}
switch (child.getNodeType()) {
case Node.TEXT_NODE :
isTextWellFormed((Text) child);
break;
case Node.ENTITY_REFERENCE_NODE :
isEntityReferneceWellFormed((EntityReference) child);
break;
default :
}
}
// TODO:
// WFC: Check if the attribute prefix is bound to
// http://www.w3.org/2000/xmlns/
// WFC: Unique Att Spec
// Perhaps pass a seen boolean value to this method. serializeAttList will determine
// if the attr was seen before.
}
/**
* Checks if a PI node is well-formed, by checking it's Name and data
* for well-formedness.
*
* @param data The contents of the comment node
*/
protected void isPIWellFormed(ProcessingInstruction node) {
// Is the PI Target a valid XML name
if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
new Object[] { "ProcessingInstruction", node.getTarget()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
null,
null,
null));
}
}
// Does the PI Data carry valid XML characters
// REVISIT: Should we check if the PI DATA contains a ?> ???
Character invalidChar = isWFXMLChar(node.getData());
if (invalidChar != null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
}
}
/**
* Checks if an CDATASection node is well-formed, by checking it's data
* for well-formedness. Note that the presence of a CDATA termination mark
* in the contents of a CDATASection is handled by the parameter
* spli-cdata-sections
*
* @param data The contents of the comment node
*/
protected void isCDATASectionWellFormed(CDATASection node) {
// Does the data valid XML character data
Character invalidChar = isWFXMLChar(node.getData());
//if (!isWFXMLChar(node.getData(), invalidChar)) {
if (invalidChar != null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
}
}
/**
* Checks if an Text node is well-formed, by checking if it contains invalid
* XML characters.
*
* @param data The contents of the comment node
*/
protected void isTextWellFormed(Text node) {
// Does the data valid XML character data
Character invalidChar = isWFXMLChar(node.getData());
if (invalidChar != null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER,
null,
null,
null));
}
}
}
/**
* Checks if an EntityRefernece node is well-formed, by checking it's node name. Then depending
* on whether it is referenced in Element content or in an Attr Node, checks if the EntityReference
* references an unparsed entity or a external entity and if so throws raises the
* appropriate well-formedness error.
*
* @param data The contents of the comment node
* @parent The parent of the EntityReference Node
*/
protected void isEntityReferneceWellFormed(EntityReference node) {
// Is the EntityReference name a valid XML name
if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
new Object[] { "EntityReference", node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
null,
null,
null));
}
}
// determine the parent node
Node parent = node.getParentNode();
// Traverse the declared entities and check if the nodeName and namespaceURI
// of the EntityReference matches an Entity. If so, check the if the notationName
// is not null, if so, report an error.
DocumentType docType = node.getOwnerDocument().getDoctype();
if (docType != null) {
NamedNodeMap entities = docType.getEntities();
for (int i = 0; i < entities.getLength(); i++) {
Entity ent = (Entity) entities.item(i);
String nodeName =
node.getNodeName() == null ? "" : node.getNodeName();
String nodeNamespaceURI =
node.getNamespaceURI() == null
? ""
: node.getNamespaceURI();
String entName =
ent.getNodeName() == null ? "" : ent.getNodeName();
String entNamespaceURI =
ent.getNamespaceURI() == null ? "" : ent.getNamespaceURI();
// If referenced in Element content
// WFC: Parsed Entity
if (parent.getNodeType() == Node.ELEMENT_NODE) {
if (entNamespaceURI.equals(nodeNamespaceURI)
&& entName.equals(nodeName)) {
if (ent.getNotationName() != null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
new Object[] { node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
null,
null,
null));
}
}
}
} // end if WFC: Parsed Entity
// If referenced in an Attr value
// WFC: No External Entity References
if (parent.getNodeType() == Node.ATTRIBUTE_NODE) {
if (entNamespaceURI.equals(nodeNamespaceURI)
&& entName.equals(nodeName)) {
if (ent.getPublicId() != null
|| ent.getSystemId() != null
|| ent.getNotationName() != null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
new Object[] { node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
null,
null,
null));
}
}
}
} //end if WFC: No External Entity References
}
}
} // isEntityReferneceWellFormed
/**
* If the configuration parameter "namespaces" is set to true, this methods
* checks if an entity whose replacement text contains unbound namespace
* prefixes is referenced in a location where there are no bindings for
* the namespace prefixes and if so raises a LSException with the error-type
* "unbound-prefix-in-entity-reference"
*
* @param Node, The EntityReference nodes whose children are to be checked
*/
protected void checkUnboundPrefixInEntRef(Node node) {
Node child, next;
for (child = node.getFirstChild(); child != null; child = next) {
next = child.getNextSibling();
if (child.getNodeType() == Node.ELEMENT_NODE) {
//If a NamespaceURI is not declared for the current
//node's prefix, raise a fatal error.
String prefix = child.getPrefix();
if (prefix != null
&& fNSBinder.getURI(prefix) == null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
new Object[] {
node.getNodeName(),
child.getNodeName(),
prefix });
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
null,
null,
null));
}
}
NamedNodeMap attrs = child.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
String attrPrefix = attrs.item(i).getPrefix();
if (attrPrefix != null
&& fNSBinder.getURI(attrPrefix) == null) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
new Object[] {
node.getNodeName(),
child.getNodeName(),
attrs.item(i)});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_FATAL_ERROR,
msg,
MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
null,
null,
null));
}
}
}
}
if (child.hasChildNodes()) {
checkUnboundPrefixInEntRef(child);
}
}
}
// ***********************************************************************
// Namespace normalization
// ***********************************************************************
/**
* Records local namespace declarations, to be used for normalization later
*
* @param Node, The element node, whose namespace declarations are to be recorded
*/
protected void recordLocalNSDecl(Node node) {
NamedNodeMap atts = ((Element) node).getAttributes();
int length = atts.getLength();
for (int i = 0; i < length; i++) {
Node attr = atts.item(i);
String localName = attr.getLocalName();
String attrPrefix = attr.getPrefix();
String attrValue = attr.getNodeValue();
String attrNS = attr.getNamespaceURI();
localName =
localName == null
|| XMLNS_PREFIX.equals(localName) ? "" : localName;
attrPrefix = attrPrefix == null ? "" : attrPrefix;
attrValue = attrValue == null ? "" : attrValue;
attrNS = attrNS == null ? "" : attrNS;
// check if attribute is a namespace decl
if (XMLNS_URI.equals(attrNS)) {
// No prefix may be bound to http://www.w3.org/2000/xmlns/.
if (XMLNS_URI.equals(attrValue)) {
String msg =
Utils.messages.createMessage(
MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
new Object[] { attrPrefix, XMLNS_URI });
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_ERROR,
msg,
MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
null,
null,
null));
}
} else {
// store the namespace-declaration
if (XMLNS_PREFIX.equals(attrPrefix) ) {
// record valid decl
if (attrValue.length() != 0) {
fNSBinder.declarePrefix(localName, attrValue);
} else {
// Error; xmlns:prefix=""
}
} else { // xmlns
// empty prefix is always bound ("" or some string)
fNSBinder.declarePrefix("", attrValue);
}
}
}
}
}
/**
* Fixes an element's namespace
*
* @param Node, The element node, whose namespace is to be fixed
*/
protected void fixupElementNS(Node node) throws SAXException {
String namespaceURI = ((Element) node).getNamespaceURI();
String prefix = ((Element) node).getPrefix();
String localName = ((Element) node).getLocalName();
if (namespaceURI != null) {
//if ( Element's prefix/namespace pair (or default namespace,
// if no prefix) are within the scope of a binding )
prefix = prefix == null ? "" : prefix;
String inScopeNamespaceURI = fNSBinder.getURI(prefix);
if ((inScopeNamespaceURI != null
&& inScopeNamespaceURI.equals(namespaceURI))) {
// do nothing, declaration in scope is inherited
} else {
// Create a local namespace declaration attr for this namespace,
// with Element's current prefix (or a default namespace, if
// no prefix). If there's a conflicting local declaration
// already present, change its value to use this namespace.
// Add the xmlns declaration attribute
//fNSBinder.pushNamespace(prefix, namespaceURI, fElementDepth);
if ((fFeatures & NAMESPACEDECLS) != 0) {
if ("".equals(prefix) || "".equals(namespaceURI)) {
((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, namespaceURI);
} else {
((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX + ":" + prefix, namespaceURI);
}
}
fLocalNSBinder.declarePrefix(prefix, namespaceURI);
fNSBinder.declarePrefix(prefix, namespaceURI);
}
} else {
// Element has no namespace
// DOM Level 1
if (localName == null || "".equals(localName)) {
// DOM Level 1 node!
String msg =
Utils.messages.createMessage(
MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
new Object[] { node.getNodeName()});
if (fErrorHandler != null) {
fErrorHandler.handleError(
new DOMErrorImpl(
DOMError.SEVERITY_ERROR,
msg,
MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
null,
null,
null));
}
} else {
namespaceURI = fNSBinder.getURI("");
if (namespaceURI !=null && namespaceURI.length() > 0) {
((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, "");
fLocalNSBinder.declarePrefix("", "");
fNSBinder.declarePrefix("", "");
}
}
}
}
/**
* This table is a quick lookup of a property key (String) to the integer that
* is the bit to flip in the fFeatures field, so the integers should have
* values 1,2,4,8,16...
*
*/
private static final Hashtable s_propKeys = new Hashtable();
static {
// Initialize the mappings of property keys to bit values (Integer objects)
// or mappings to a String object "", which indicates we are interested
// in the property, but it does not have a simple bit value to flip
// cdata-sections
int i = CDATA;
Integer val = new Integer(i);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS,
val);
// comments
int i1 = COMMENTS;
val = new Integer(i1);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS,
val);
// element-content-whitespace
int i2 = ELEM_CONTENT_WHITESPACE;
val = new Integer(i2);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS
+ DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,
val);
int i3 = ENTITIES;
// entities
val = new Integer(i3);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES,
val);
// namespaces
int i4 = NAMESPACES;
val = new Integer(i4);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES,
val);
// namespace-declarations
int i5 = NAMESPACEDECLS;
val = new Integer(i5);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS
+ DOMConstants.DOM_NAMESPACE_DECLARATIONS,
val);
// split-cdata-sections
int i6 = SPLITCDATA;
val = new Integer(i6);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA,
val);
// discard-default-content
int i7 = WELLFORMED;
val = new Integer(i7);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED,
val);
// discard-default-content
int i8 = DISCARDDEFAULT;
val = new Integer(i8);
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS
+ DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,
val);
// We are interested in these properties, but they don't have a simple
// bit value to deal with.
s_propKeys.put(
DOMConstants.S_DOM3_PROPERTIES_NS
+ DOMConstants.DOM_FORMAT_PRETTY_PRINT,
"");
s_propKeys.put(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "");
s_propKeys.put(
DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION,
"");
s_propKeys.put(DOMConstants.S_XSL_OUTPUT_ENCODING, "");
s_propKeys.put(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, "");
}
/**
* Initializes fFeatures based on the DOMConfiguration Parameters set.
*
* @param properties DOMConfiguraiton properties that were set and which are
* to be used while serializing the DOM.
*/
protected void initProperties(Properties properties) {
for (Enumeration keys = properties.keys(); keys.hasMoreElements();) {
final String key = (String) keys.nextElement();
// caonical-form
// Other features will be enabled or disabled when this is set to true or false.
// error-handler; set via the constructor
// infoset
// Other features will be enabled or disabled when this is set to true
// A quick lookup for the given set of properties (cdata-sections ...)
final Object iobj = s_propKeys.get(key);
if (iobj != null) {
if (iobj instanceof Integer) {
// Dealing with a property that has a simple bit value that
// we need to set
// cdata-sections
// comments
// element-content-whitespace
// entities
// namespaces
// namespace-declarations
// split-cdata-sections
// well-formed
// discard-default-content
final int BITFLAG = ((Integer) iobj).intValue();
if ((properties.getProperty(key).endsWith("yes"))) {
fFeatures = fFeatures | BITFLAG;
} else {
fFeatures = fFeatures & ~BITFLAG;
}
} else {
// We are interested in the property, but it is not
// a simple bit that we need to set.
if ((DOMConstants.S_DOM3_PROPERTIES_NS
+ DOMConstants.DOM_FORMAT_PRETTY_PRINT)
.equals(key)) {
// format-pretty-print; set internally on the serializers via xsl:output properties in LSSerializer
if ((properties.getProperty(key).endsWith("yes"))) {
fSerializer.setIndent(true);
fSerializer.setIndentAmount(3);
} else {
fSerializer.setIndent(false);
}
} else if (
(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL).equals(
key)) {
// omit-xml-declaration; set internally on the serializers via xsl:output properties in LSSerializer
if ((properties.getProperty(key).endsWith("yes"))) {
fSerializer.setOmitXMLDeclaration(true);
} else {
fSerializer.setOmitXMLDeclaration(false);
}
} else if (
(
DOMConstants.S_XERCES_PROPERTIES_NS
+ DOMConstants.S_XML_VERSION).equals(
key)) {
// Retreive the value of the XML Version attribute via the xml-version
String version = properties.getProperty(key);
if ("1.1".equals(version)) {
fIsXMLVersion11 = true;
fSerializer.setVersion(version);
} else {
fSerializer.setVersion("1.0");
}
} else if (
(DOMConstants.S_XSL_OUTPUT_ENCODING).equals(key)) {
// Retreive the value of the XML Encoding attribute
String encoding = properties.getProperty(key);
if (encoding != null) {
fSerializer.setEncoding(encoding);
}
} else if ((DOMConstants.S_XERCES_PROPERTIES_NS
+ DOMConstants.DOM_ENTITIES).equals(key)) {
// Preserve entity references in the document
if ((properties.getProperty(key).endsWith("yes"))) {
fSerializer.setDTDEntityExpansion(false);
}
else {
fSerializer.setDTDEntityExpansion(true);
}
} else {
// We shouldn't get here, ever, now what?
}
}
}
}
// Set the newLine character to use
if (fNewLine != null) {
fSerializer.setOutputProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR, fNewLine);
}
}
} //TreeWalker