org.apache.xml.security.c14n.implementations.CanonicalizerBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmlsec Show documentation
Show all versions of xmlsec Show documentation
Apache XML Security for Java supports XML-Signature Syntax and Processing,
W3C Recommendation 12 February 2002, and XML Encryption Syntax and
Processing, W3C Recommendation 10 December 2002. As of version 1.4,
the library supports the standard Java API JSR-105: XML Digital Signature APIs.
/**
* 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.
*/
package org.apache.xml.security.c14n.implementations;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.CanonicalizerSpi;
import org.apache.xml.security.c14n.helper.AttrCompare;
import org.apache.xml.security.signature.NodeFilter;
import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.UnsyncByteArrayOutputStream;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.xml.sax.SAXException;
/**
* Abstract base class for canonicalization algorithms.
*
* @author Christian Geuer-Pollmann
*/
public abstract class CanonicalizerBase extends CanonicalizerSpi {
public static final String XML = "xml";
public static final String XMLNS = "xmlns";
protected static final AttrCompare COMPARE = new AttrCompare();
// Make sure you clone the following mutable arrays before passing to
// potentially untrusted objects such as OutputStreams.
private static final byte[] END_PI = {'?','>'};
private static final byte[] BEGIN_PI = {'<','?'};
private static final byte[] END_COMM = {'-','-','>'};
private static final byte[] BEGIN_COMM = {'<','!','-','-'};
private static final byte[] XA = {'&','#','x','A',';'};
private static final byte[] X9 = {'&','#','x','9',';'};
private static final byte[] QUOT = {'&','q','u','o','t',';'};
private static final byte[] XD = {'&','#','x','D',';'};
private static final byte[] GT = {'&','g','t',';'};
private static final byte[] LT = {'&','l','t',';'};
private static final byte[] END_TAG = {'<','/'};
private static final byte[] AMP = {'&','a','m','p',';'};
private static final byte[] EQUALS_STR = {'=','\"'};
protected static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;
protected static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;
protected static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;
private List nodeFilter;
private boolean includeComments;
private Set xpathNodeSet;
/**
* The node to be skipped/excluded from the DOM tree
* in subtree canonicalizations.
*/
private Node excludeNode;
private OutputStream writer = new ByteArrayOutputStream();
/**
* The null xmlns definition.
*/
private Attr nullNode;
/**
* Constructor CanonicalizerBase
*
* @param includeComments
*/
public CanonicalizerBase(boolean includeComments) {
this.includeComments = includeComments;
}
/**
* Method engineCanonicalizeSubTree
* @inheritDoc
* @param rootNode
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeSubTree(Node rootNode)
throws CanonicalizationException {
return engineCanonicalizeSubTree(rootNode, (Node)null);
}
/**
* Method engineCanonicalizeXPathNodeSet
* @inheritDoc
* @param xpathNodeSet
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet)
throws CanonicalizationException {
this.xpathNodeSet = xpathNodeSet;
return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this.xpathNodeSet));
}
/**
* Canonicalizes a Subtree node.
* @param input the root of the subtree to canicalize
* @return The canonicalize stream.
* @throws CanonicalizationException
*/
public byte[] engineCanonicalize(XMLSignatureInput input) throws CanonicalizationException {
try {
if (input.isExcludeComments()) {
includeComments = false;
}
if (input.isOctetStream()) {
return engineCanonicalize(input.getBytes());
}
if (input.isElement()) {
return engineCanonicalizeSubTree(input.getSubNode(), input.getExcludeNode());
} else if (input.isNodeSet()) {
nodeFilter = input.getNodeFilters();
circumventBugIfNeeded(input);
if (input.getSubNode() != null) {
return engineCanonicalizeXPathNodeSetInternal(input.getSubNode());
} else {
return engineCanonicalizeXPathNodeSet(input.getNodeSet());
}
}
return null;
} catch (CanonicalizationException ex) {
throw new CanonicalizationException("empty", ex);
} catch (ParserConfigurationException ex) {
throw new CanonicalizationException("empty", ex);
} catch (IOException ex) {
throw new CanonicalizationException("empty", ex);
} catch (SAXException ex) {
throw new CanonicalizationException("empty", ex);
}
}
/**
* @param writer The writer to set.
*/
public void setWriter(OutputStream writer) {
this.writer = writer;
}
/**
* Canonicalizes a Subtree node.
*
* @param rootNode
* the root of the subtree to canonicalize
* @param excludeNode
* a node to be excluded from the canonicalize operation
* @return The canonicalize stream.
* @throws CanonicalizationException
*/
protected byte[] engineCanonicalizeSubTree(Node rootNode, Node excludeNode)
throws CanonicalizationException {
this.excludeNode = excludeNode;
try {
NameSpaceSymbTable ns = new NameSpaceSymbTable();
int nodeLevel = NODE_BEFORE_DOCUMENT_ELEMENT;
if (rootNode != null && Node.ELEMENT_NODE == rootNode.getNodeType()) {
//Fills the nssymbtable with the definitions of the parent of the root subnode
getParentNameSpaces((Element)rootNode, ns);
nodeLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
}
this.canonicalizeSubTree(rootNode, ns, rootNode, nodeLevel);
this.writer.flush();
if (this.writer instanceof ByteArrayOutputStream) {
byte[] result = ((ByteArrayOutputStream)this.writer).toByteArray();
if (reset) {
((ByteArrayOutputStream)this.writer).reset();
} else {
this.writer.close();
}
return result;
} else if (this.writer instanceof UnsyncByteArrayOutputStream) {
byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray();
if (reset) {
((UnsyncByteArrayOutputStream)this.writer).reset();
} else {
this.writer.close();
}
return result;
} else {
this.writer.close();
}
return null;
} catch (UnsupportedEncodingException ex) {
throw new CanonicalizationException("empty", ex);
} catch (IOException ex) {
throw new CanonicalizationException("empty", ex);
}
}
/**
* Method canonicalizeSubTree, this function is a recursive one.
*
* @param currentNode
* @param ns
* @param endnode
* @throws CanonicalizationException
* @throws IOException
*/
protected final void canonicalizeSubTree(
Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel
) throws CanonicalizationException, IOException {
if (isVisibleInt(currentNode) == -1) {
return;
}
Node sibling = null;
Node parentNode = null;
final OutputStream writer = this.writer;
final Node excludeNode = this.excludeNode;
final boolean includeComments = this.includeComments;
Map cache = new HashMap();
do {
switch (currentNode.getNodeType()) {
case Node.ENTITY_NODE :
case Node.NOTATION_NODE :
case Node.ATTRIBUTE_NODE :
// illegal node type during traversal
throw new CanonicalizationException("empty");
case Node.DOCUMENT_FRAGMENT_NODE :
case Node.DOCUMENT_NODE :
ns.outputNodePush();
sibling = currentNode.getFirstChild();
break;
case Node.COMMENT_NODE :
if (includeComments) {
outputCommentToWriter((Comment) currentNode, writer, documentLevel);
}
break;
case Node.PROCESSING_INSTRUCTION_NODE :
outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel);
break;
case Node.TEXT_NODE :
case Node.CDATA_SECTION_NODE :
outputTextToWriter(currentNode.getNodeValue(), writer);
break;
case Node.ELEMENT_NODE :
documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
if (currentNode == excludeNode) {
break;
}
Element currentElement = (Element)currentNode;
//Add a level to the nssymbtable. So latter can be pop-back.
ns.outputNodePush();
writer.write('<');
String name = currentElement.getTagName();
UtfHelpper.writeByte(name, writer, cache);
Iterator attrs = this.handleAttributesSubtree(currentElement, ns);
if (attrs != null) {
//we output all Attrs which are available
while (attrs.hasNext()) {
Attr attr = attrs.next();
outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
}
}
writer.write('>');
sibling = currentNode.getFirstChild();
if (sibling == null) {
writer.write(END_TAG.clone());
UtfHelpper.writeStringToUtf8(name, writer);
writer.write('>');
//We finished with this level, pop to the previous definitions.
ns.outputNodePop();
if (parentNode != null) {
sibling = currentNode.getNextSibling();
}
} else {
parentNode = currentElement;
}
break;
case Node.DOCUMENT_TYPE_NODE :
default :
break;
}
while (sibling == null && parentNode != null) {
writer.write(END_TAG.clone());
UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache);
writer.write('>');
//We finished with this level, pop to the previous definitions.
ns.outputNodePop();
if (parentNode == endnode) {
return;
}
sibling = parentNode.getNextSibling();
parentNode = parentNode.getParentNode();
if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) {
documentLevel = NODE_AFTER_DOCUMENT_ELEMENT;
parentNode = null;
}
}
if (sibling == null) {
return;
}
currentNode = sibling;
sibling = currentNode.getNextSibling();
} while(true);
}
private byte[] engineCanonicalizeXPathNodeSetInternal(Node doc)
throws CanonicalizationException {
try {
this.canonicalizeXPathNodeSet(doc, doc);
this.writer.flush();
if (this.writer instanceof ByteArrayOutputStream) {
byte[] sol = ((ByteArrayOutputStream)this.writer).toByteArray();
if (reset) {
((ByteArrayOutputStream)this.writer).reset();
} else {
this.writer.close();
}
return sol;
} else if (this.writer instanceof UnsyncByteArrayOutputStream) {
byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray();
if (reset) {
((UnsyncByteArrayOutputStream)this.writer).reset();
} else {
this.writer.close();
}
return result;
} else {
this.writer.close();
}
return null;
} catch (UnsupportedEncodingException ex) {
throw new CanonicalizationException("empty", ex);
} catch (IOException ex) {
throw new CanonicalizationException("empty", ex);
}
}
/**
* Canonicalizes all the nodes included in the currentNode and contained in the
* xpathNodeSet field.
*
* @param currentNode
* @param endnode
* @throws CanonicalizationException
* @throws IOException
*/
protected final void canonicalizeXPathNodeSet(Node currentNode, Node endnode)
throws CanonicalizationException, IOException {
if (isVisibleInt(currentNode) == -1) {
return;
}
boolean currentNodeIsVisible = false;
NameSpaceSymbTable ns = new NameSpaceSymbTable();
if (currentNode != null && Node.ELEMENT_NODE == currentNode.getNodeType()) {
getParentNameSpaces((Element)currentNode, ns);
}
if (currentNode == null) {
return;
}
Node sibling = null;
Node parentNode = null;
OutputStream writer = this.writer;
int documentLevel = NODE_BEFORE_DOCUMENT_ELEMENT;
Map cache = new HashMap();
do {
switch (currentNode.getNodeType()) {
case Node.ENTITY_NODE :
case Node.NOTATION_NODE :
case Node.ATTRIBUTE_NODE :
// illegal node type during traversal
throw new CanonicalizationException("empty");
case Node.DOCUMENT_FRAGMENT_NODE :
case Node.DOCUMENT_NODE :
ns.outputNodePush();
sibling = currentNode.getFirstChild();
break;
case Node.COMMENT_NODE :
if (this.includeComments && isVisibleDO(currentNode, ns.getLevel()) == 1) {
outputCommentToWriter((Comment) currentNode, writer, documentLevel);
}
break;
case Node.PROCESSING_INSTRUCTION_NODE :
if (isVisible(currentNode)) {
outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel);
}
break;
case Node.TEXT_NODE :
case Node.CDATA_SECTION_NODE :
if (isVisible(currentNode)) {
outputTextToWriter(currentNode.getNodeValue(), writer);
for (Node nextSibling = currentNode.getNextSibling();
nextSibling != null && (nextSibling.getNodeType() == Node.TEXT_NODE
|| nextSibling.getNodeType() == Node.CDATA_SECTION_NODE);
nextSibling = nextSibling.getNextSibling()) {
outputTextToWriter(nextSibling.getNodeValue(), writer);
currentNode = nextSibling;
sibling = currentNode.getNextSibling();
}
}
break;
case Node.ELEMENT_NODE :
documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
Element currentElement = (Element) currentNode;
//Add a level to the nssymbtable. So latter can be pop-back.
String name = null;
int i = isVisibleDO(currentNode, ns.getLevel());
if (i == -1) {
sibling = currentNode.getNextSibling();
break;
}
currentNodeIsVisible = i == 1;
if (currentNodeIsVisible) {
ns.outputNodePush();
writer.write('<');
name = currentElement.getTagName();
UtfHelpper.writeByte(name, writer, cache);
} else {
ns.push();
}
Iterator attrs = handleAttributes(currentElement,ns);
if (attrs != null) {
//we output all Attrs which are available
while (attrs.hasNext()) {
Attr attr = attrs.next();
outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
}
}
if (currentNodeIsVisible) {
writer.write('>');
}
sibling = currentNode.getFirstChild();
if (sibling == null) {
if (currentNodeIsVisible) {
writer.write(END_TAG.clone());
UtfHelpper.writeByte(name, writer, cache);
writer.write('>');
//We finished with this level, pop to the previous definitions.
ns.outputNodePop();
} else {
ns.pop();
}
if (parentNode != null) {
sibling = currentNode.getNextSibling();
}
} else {
parentNode = currentElement;
}
break;
case Node.DOCUMENT_TYPE_NODE :
default :
break;
}
while (sibling == null && parentNode != null) {
if (isVisible(parentNode)) {
writer.write(END_TAG.clone());
UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache);
writer.write('>');
//We finished with this level, pop to the previous definitions.
ns.outputNodePop();
} else {
ns.pop();
}
if (parentNode == endnode) {
return;
}
sibling = parentNode.getNextSibling();
parentNode = parentNode.getParentNode();
if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) {
parentNode = null;
documentLevel = NODE_AFTER_DOCUMENT_ELEMENT;
}
}
if (sibling == null) {
return;
}
currentNode = sibling;
sibling = currentNode.getNextSibling();
} while(true);
}
protected int isVisibleDO(Node currentNode, int level) {
if (nodeFilter != null) {
Iterator it = nodeFilter.iterator();
while (it.hasNext()) {
int i = it.next().isNodeIncludeDO(currentNode, level);
if (i != 1) {
return i;
}
}
}
if (this.xpathNodeSet != null && !this.xpathNodeSet.contains(currentNode)) {
return 0;
}
return 1;
}
protected int isVisibleInt(Node currentNode) {
if (nodeFilter != null) {
Iterator it = nodeFilter.iterator();
while (it.hasNext()) {
int i = it.next().isNodeInclude(currentNode);
if (i != 1) {
return i;
}
}
}
if (this.xpathNodeSet != null && !this.xpathNodeSet.contains(currentNode)) {
return 0;
}
return 1;
}
protected boolean isVisible(Node currentNode) {
if (nodeFilter != null) {
Iterator it = nodeFilter.iterator();
while (it.hasNext()) {
if (it.next().isNodeInclude(currentNode) != 1) {
return false;
}
}
}
if (this.xpathNodeSet != null && !this.xpathNodeSet.contains(currentNode)) {
return false;
}
return true;
}
protected void handleParent(Element e, NameSpaceSymbTable ns) {
if (!e.hasAttributes() && e.getNamespaceURI() == null) {
return;
}
NamedNodeMap attrs = e.getAttributes();
int attrsLength = attrs.getLength();
for (int i = 0; i < attrsLength; i++) {
Attr attribute = (Attr) attrs.item(i);
String NName = attribute.getLocalName();
String NValue = attribute.getNodeValue();
if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI())
&& (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue))) {
ns.addMapping(NName, NValue, attribute);
}
}
if (e.getNamespaceURI() != null) {
String NName = e.getPrefix();
String NValue = e.getNamespaceURI();
String Name;
if (NName == null || NName.equals("")) {
NName = XMLNS;
Name = XMLNS;
} else {
Name = XMLNS + ":" + NName;
}
Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name);
n.setValue(NValue);
ns.addMapping(NName, NValue, n);
}
}
/**
* Adds to ns the definitions from the parent elements of el
* @param el
* @param ns
*/
protected final void getParentNameSpaces(Element el, NameSpaceSymbTable ns) {
Node n1 = el.getParentNode();
if (n1 == null || Node.ELEMENT_NODE != n1.getNodeType()) {
return;
}
//Obtain all the parents of the element
List parents = new ArrayList();
Node parent = n1;
while (parent != null && Node.ELEMENT_NODE == parent.getNodeType()) {
parents.add((Element)parent);
parent = parent.getParentNode();
}
//Visit them in reverse order.
ListIterator it = parents.listIterator(parents.size());
while (it.hasPrevious()) {
Element ele = it.previous();
handleParent(ele, ns);
}
parents.clear();
Attr nsprefix;
if ((nsprefix = ns.getMappingWithoutRendered(XMLNS)) != null
&& "".equals(nsprefix.getValue())) {
ns.addMappingAndRender(
XMLNS, "", getNullNode(nsprefix.getOwnerDocument()));
}
}
/**
* Obtain the attributes to output for this node in XPathNodeSet c14n.
*
* @param element
* @param ns
* @return the attributes nodes to output.
* @throws CanonicalizationException
*/
abstract Iterator handleAttributes(Element element, NameSpaceSymbTable ns)
throws CanonicalizationException;
/**
* Obtain the attributes to output for this node in a Subtree c14n.
*
* @param element
* @param ns
* @return the attributes nodes to output.
* @throws CanonicalizationException
*/
abstract Iterator handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
throws CanonicalizationException;
abstract void circumventBugIfNeeded(XMLSignatureInput input)
throws CanonicalizationException, ParserConfigurationException, IOException, SAXException;
/**
* Outputs an Attribute to the internal Writer.
*
* The string value of the node is modified by replacing
*
* - all ampersands (&) with
&
* - all open angle brackets (<) with
<
* - all quotation mark characters with
"
* - and the whitespace characters
#x9
, #xA, and #xD, with character
* references. The character references are written in uppercase
* hexadecimal with no leading zeroes (for example, #xD
is represented
* by the character reference 
)
*
*
* @param name
* @param value
* @param writer
* @throws IOException
*/
protected static final void outputAttrToWriter(
final String name, final String value,
final OutputStream writer, final Map cache
) throws IOException {
writer.write(' ');
UtfHelpper.writeByte(name, writer, cache);
writer.write(EQUALS_STR.clone());
byte[] toWrite;
final int length = value.length();
int i = 0;
while (i < length) {
char c = value.charAt(i++);
switch (c) {
case '&' :
toWrite = AMP.clone();
break;
case '<' :
toWrite = LT.clone();
break;
case '"' :
toWrite = QUOT.clone();
break;
case 0x09 : // '\t'
toWrite = X9.clone();
break;
case 0x0A : // '\n'
toWrite = XA.clone();
break;
case 0x0D : // '\r'
toWrite = XD.clone();
break;
default :
if (c < 0x80) {
writer.write(c);
} else {
UtfHelpper.writeCharToUtf8(c, writer);
}
continue;
}
writer.write(toWrite);
}
writer.write('\"');
}
/**
* Outputs a PI to the internal Writer.
*
* @param currentPI
* @param writer where to write the things
* @throws IOException
*/
protected void outputPItoWriter(
ProcessingInstruction currentPI, OutputStream writer, int position
) throws IOException {
if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
writer.write('\n');
}
writer.write(BEGIN_PI.clone());
final String target = currentPI.getTarget();
int length = target.length();
for (int i = 0; i < length; i++) {
char c = target.charAt(i);
if (c == 0x0D) {
writer.write(XD.clone());
} else {
if (c < 0x80) {
writer.write(c);
} else {
UtfHelpper.writeCharToUtf8(c, writer);
}
}
}
final String data = currentPI.getData();
length = data.length();
if (length > 0) {
writer.write(' ');
for (int i = 0; i < length; i++) {
char c = data.charAt(i);
if (c == 0x0D) {
writer.write(XD.clone());
} else {
UtfHelpper.writeCharToUtf8(c, writer);
}
}
}
writer.write(END_PI.clone());
if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
writer.write('\n');
}
}
/**
* Method outputCommentToWriter
*
* @param currentComment
* @param writer writer where to write the things
* @throws IOException
*/
protected void outputCommentToWriter(
Comment currentComment, OutputStream writer, int position
) throws IOException {
if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
writer.write('\n');
}
writer.write(BEGIN_COMM.clone());
final String data = currentComment.getData();
final int length = data.length();
for (int i = 0; i < length; i++) {
char c = data.charAt(i);
if (c == 0x0D) {
writer.write(XD.clone());
} else {
if (c < 0x80) {
writer.write(c);
} else {
UtfHelpper.writeCharToUtf8(c, writer);
}
}
}
writer.write(END_COMM.clone());
if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
writer.write('\n');
}
}
/**
* Outputs a Text of CDATA section to the internal Writer.
*
* @param text
* @param writer writer where to write the things
* @throws IOException
*/
protected static final void outputTextToWriter(
final String text, final OutputStream writer
) throws IOException {
final int length = text.length();
byte[] toWrite;
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
switch (c) {
case '&' :
toWrite = AMP.clone();
break;
case '<' :
toWrite = LT.clone();
break;
case '>' :
toWrite = GT.clone();
break;
case 0xD :
toWrite = XD.clone();
break;
default :
if (c < 0x80) {
writer.write(c);
} else {
UtfHelpper.writeCharToUtf8(c, writer);
}
continue;
}
writer.write(toWrite);
}
}
// The null xmlns definition.
protected Attr getNullNode(Document ownerDocument) {
if (nullNode == null) {
try {
nullNode = ownerDocument.createAttributeNS(
Constants.NamespaceSpecNS, XMLNS);
nullNode.setValue("");
} catch (Exception e) {
throw new RuntimeException("Unable to create nullNode: " + e);
}
}
return nullNode;
}
}