blob: 0aff16cbb6bfaa74deb75203897825c71baa8ad6 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "<WebSig>" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Institute for
* Data Communications Systems, <http://www.nue.et-inf.uni-siegen.de/>.
* The development of this software was partly funded by the European
* Commission in the <WebSig> project in the ISIS Programme.
* For more information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xml.security.signature;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.c14n.implementations.Canonicalizer20010315OmitComments;
import org.apache.xml.security.utils.JavaUtils;
import org.apache.xml.security.utils.XMLUtils;
import org.apache.xpath.CachedXPathAPI;
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.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.xml.sax.SAXException;
/**
* Class XMLSignatureInput
*
* @author Christian Geuer-Pollmann
* $todo$ check whether an XMLSignatureInput can be _both_, octet stream _and_ node set?
*/
public class XMLSignatureInput {
/**
* Some InputStreams do not support the {@link java.io.InputStream#reset}
* method, so we read it in completely and work on our Proxy.
*/
InputStream _inputOctetStreamProxy = null;
/**
* The original NodeSet for this XMLSignatureInput
*/
Set _inputNodeSet = null;
/** Field _cxpathAPI */
CachedXPathAPI _cxpathAPI;
/**
* Construct a XMLSignatureInput from an octet array.
* <p>
* This is a comfort method, which internally converts the byte[] array into an InputStream
*
* @param inputOctets an octet array which including XML document or node
*/
public XMLSignatureInput(byte[] inputOctets) {
// defensive copy
byte[] copy = new byte[inputOctets.length];
System.arraycopy(inputOctets, 0, copy, 0, inputOctets.length);
this._inputOctetStreamProxy = new ByteArrayInputStream(copy);
this._cxpathAPI = new CachedXPathAPI();
}
/**
* Constructs a <code>XMLSignatureInput</code> from an octet stream. The
* stream is directly read.
*
* @param inputOctetStream
* @throws IOException
*/
public XMLSignatureInput(InputStream inputOctetStream) throws IOException {
this(JavaUtils.getBytesFromStream(inputOctetStream));
inputOctetStream = null; // free object reference
}
/**
* Construct a XMLSignatureInput from a String.
* <p>
* This is a comfort method, which internally converts the String into a byte[] array using the {@link java.lang.String#getBytes} method.
*
* @param inputStr the input String which including XML document or node
*/
public XMLSignatureInput(String inputStr) {
this(inputStr.getBytes());
}
/**
* Construct a XMLSignatureInput from a String with a given encoding.
* <p>
* This is a comfort method, which internally converts the String into a byte[] array using the {@link java.lang.String#getBytes} method.
*
* @param inputStr the input String with encoding <code>encoding</code>
* @param encoding the encoding of <code>inputStr</code>
* @throws UnsupportedEncodingException
*/
public XMLSignatureInput(String inputStr, String encoding)
throws UnsupportedEncodingException {
this(inputStr.getBytes(encoding));
}
/**
* Construct a XMLSignatureInput from a subtree rooted by rootNode. This
* method included the node and <I>all</I> his descendants in the output.
*
* @param rootNode
* @param usedXPathAPI
* @throws TransformerException
*/
public XMLSignatureInput(Node rootNode, CachedXPathAPI usedXPathAPI)
throws TransformerException {
this._cxpathAPI = usedXPathAPI;
// get the Document and make all namespace nodes visible in DOM space
Document doc = XMLUtils.getOwnerDocument(rootNode);
XMLUtils.circumventBug2650(doc);
NodeList result = this._cxpathAPI.selectNodeList(rootNode,
Canonicalizer.XPATH_C14N_WITH_COMMENTS_SINGLE_NODE);
this._inputNodeSet = XMLUtils.convertNodelistToSet(result);
}
/**
* Construct a XMLSignatureInput from a subtree rooted by rootNode. This method included the node
* and <I>all</I> his descendants in the output.
*
* @param rootNode
* @throws TransformerException
*/
public XMLSignatureInput(Node rootNode) throws TransformerException {
this(rootNode, new CachedXPathAPI());
}
/**
* Constructor XMLSignatureInput
*
* @param inputNodeSet
* @param usedXPathAPI
*/
public XMLSignatureInput(Set inputNodeSet, CachedXPathAPI usedXPathAPI) {
this._inputNodeSet = inputNodeSet;
this._cxpathAPI = usedXPathAPI;
}
/**
* Constructor XMLSignatureInput
*
* @param inputNodeSet
*/
public XMLSignatureInput(Set inputNodeSet) {
this(inputNodeSet, new CachedXPathAPI());
}
/**
* Constructor XMLSignatureInput
*
* @param inputNodeSet
* @deprecated Use {@link Set}s instead of {@link NodeList}s.
*/
public XMLSignatureInput(NodeList inputNodeSet) {
this(XMLUtils.convertNodelistToSet(inputNodeSet), new CachedXPathAPI());
}
/**
* Constructor XMLSignatureInput
*
* @param inputNodeSet
* @param usedXPathAPI
* @deprecated Use {@link Set}s instead of {@link NodeList}s.
*/
public XMLSignatureInput(NodeList inputNodeSet,
CachedXPathAPI usedXPathAPI) {
this(XMLUtils.convertNodelistToSet(inputNodeSet), usedXPathAPI);
}
/**
* Returns the node set from input which was specified as the parameter of {@link XMLSignatureInput} constructor
*
* @return the node set
* @throws CanonicalizationException
* @throws IOException
* @throws InvalidCanonicalizerException
* @throws ParserConfigurationException
* @throws SAXException
*/
public Set getNodeSet()
throws ParserConfigurationException, IOException, SAXException,
CanonicalizationException, InvalidCanonicalizerException {
if (this.isNodeSet()) {
return this._inputNodeSet;
} else if (this.isOctetStream()) {
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
dfactory.setValidating(false);
dfactory.setNamespaceAware(true);
DocumentBuilder db = dfactory.newDocumentBuilder();
try {
db.setErrorHandler(new org.apache.xml.security.utils
.IgnoreAllErrorHandler());
Document doc = db.parse(this.getOctetStream());
XMLUtils.circumventBug2650(doc);
// select all nodes, also the comments.
NodeList nodeList =
this._cxpathAPI
.selectNodeList(doc,
Canonicalizer
.XPATH_C14N_WITH_COMMENTS_SINGLE_NODE);
return XMLUtils.convertNodelistToSet(nodeList);
} catch (TransformerException ex) {
throw new CanonicalizationException("generic.EmptyMessage", ex);
} catch (SAXException ex) {
// if a not-wellformed nodeset exists, put a container around it...
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("<container>".getBytes());
baos.write(this.getBytes());
baos.write("</container>".getBytes());
byte result[] = baos.toByteArray();
Document document = db.parse(new ByteArrayInputStream(result));
XMLUtils.circumventBug2650(document);
try {
NodeList nodeList = this._cxpathAPI.selectNodeList(
document,
"(//. | //@* | //namespace::*)[not(self::node()=/) and not(self::node=/container)]");
return XMLUtils.convertNodelistToSet(nodeList);
} catch (TransformerException ex2) {
throw new CanonicalizationException("generic.EmptyMessage", ex2);
}
}
}
throw new RuntimeException(
"getNodeSet() called but no input data present");
}
/**
* Returns the Octect stream(byte Stream) from input which was specified as the parameter of {@link XMLSignatureInput} constructor
*
* @return the Octect stream(byte Stream) from input which was specified as the parameter of {@link XMLSignatureInput} constructor
* @throws CanonicalizationException
* @throws IOException
* @throws InvalidCanonicalizerException
*/
public InputStream getOctetStream()
throws IOException, CanonicalizationException,
InvalidCanonicalizerException {
if (this.isOctetStream()) {
this._inputOctetStreamProxy.reset();
return this._inputOctetStreamProxy;
} else if (this.isNodeSet()) {
/* If we have a node set but an octet stream is needed, we MUST c14nize
* without any comments.
*
* We don't use the factory because direct instantiation should be a
* little bit faster...
*/
Canonicalizer20010315OmitComments c14nizer =
new Canonicalizer20010315OmitComments();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (this._inputNodeSet.size() == 0) {
// empty nodeset
return new ByteArrayInputStream(baos.toByteArray());
}
try {
Set nodes = this.getNodeSet();
byte bytes[] = c14nizer.engineCanonicalizeXPathNodeSet(nodes);
baos.write(bytes);
/** $todo$ Clarify behavior. If isNodeSet() and we getOctetStream, do we have to this._inputOctetStream=xxx ? */
/*
this._inputOctetStream = new ByteArrayInputStream(baos.toByteArray());
this._inputNodeSet = null;
return this._inputOctetStream;
*/
return new ByteArrayInputStream(baos.toByteArray());
} catch (SAXException ex) {
throw new CanonicalizationException("empty", ex);
} catch (ParserConfigurationException ex) {
throw new CanonicalizationException("empty", ex);
}
}
throw new RuntimeException(
"getOctetStream() called but no input data present");
}
/**
* Returns the byte array from input which was specified as the parameter of {@link XMLSignatureInput} constructor
*
* @return the byte[] from input which was specified as the parameter of {@link XMLSignatureInput} constructor
*
* @throws CanonicalizationException
* @throws IOException
* @throws InvalidCanonicalizerException
*/
public byte[] getBytes()
throws IOException, CanonicalizationException,
InvalidCanonicalizerException {
InputStream is = this.getOctetStream();
int available = is.available();
byte[] data = new byte[available];
is.read(data);
if (available != data.length) {
throw new IOException("Not enough bytes read");
}
return data;
}
/**
* Determines if the object has been set up with a Node set
*
* @return true is the object has been set up with a Node set
*/
public boolean isNodeSet() {
return ((this._inputOctetStreamProxy == null)
&& (this._inputNodeSet != null));
}
/**
* Determines if the object has been set up with an octet stream
*
* @return true is the object has been set up with an octet stream
*/
public boolean isOctetStream() {
return ((this._inputOctetStreamProxy != null)
&& (this._inputNodeSet == null));
}
/**
* Is the object correctly set up?
*
* @return true if the object has been set up correctly
*/
public boolean isInitialized() {
return (this.isOctetStream() || this.isNodeSet());
}
/**
* Some Transforms may require explicit MIME type, charset (IANA registered
* "character set"), or other such information concerning the data they
* are receiving from an earlier Transform or the source data, although no
* Transform algorithm specified in this document needs such explicit
* information. Such data characteristics are provided as parameters to the
* Transform algorithm and should be described in the specification for the
* algorithm.
*/
private String _MIMEType = null;
/**
* Returns MIMEType
*
* @return MIMEType
*/
public String getMIMEType() {
return this._MIMEType;
}
/**
* Sets MIMEType
*
* @param MIMEType
*/
public void setMIMEType(String MIMEType) {
this._MIMEType = MIMEType;
}
/** Field _SourceURI */
private String _SourceURI = null;
/**
* Return SourceURI
*
* @return SourceURI
*/
public String getSourceURI() {
return this._SourceURI;
}
/**
* Sets SourceURI
*
* @param SourceURI
*/
public void setSourceURI(String SourceURI) {
this._SourceURI = SourceURI;
}
/**
* This method gives access to an {@link org.apache.xpath.CachedXPathAPI}
* object which was used for creating the internal node set and which MUST be
* used for subsequent operations on this node set.
*
* @return an existing {@link org.apache.xpath.CachedXPathAPI}
*/
public CachedXPathAPI getCachedXPathAPI() {
return this._cxpathAPI;
}
/**
* Method toString
*
*
*/
public String toString() {
if (this.isNodeSet()) {
try {
return "XMLSignatureInput/NodeSet/" + this._inputNodeSet.size()
+ " nodes/" + this.getSourceURI();
} catch (Exception ex) {
return "XMLSignatureInput/NodeSet//" + this.getSourceURI();
}
} else {
try {
return "XMLSignatureInput/OctetStream/" + this.getBytes().length
+ " octets/" + this.getSourceURI();
} catch (Exception ex) {
return "XMLSignatureInput/OctetStream//" + this.getSourceURI();
}
}
}
/**
* Method getHTMLRepresentation
*
* @throws XMLSignatureException
*/
public String getHTMLRepresentation() throws XMLSignatureException {
XMLSignatureInputDebugger db = new XMLSignatureInputDebugger(this);
return db.getHTMLRepresentation();
}
/**
* Method getHTMLRepresentation
*
* @param inclusiveNamespaces
* @throws XMLSignatureException
*/
public String getHTMLRepresentation(Set inclusiveNamespaces)
throws XMLSignatureException {
XMLSignatureInputDebugger db = new XMLSignatureInputDebugger(this,
inclusiveNamespaces);
return db.getHTMLRepresentation();
}
/**
* Class XMLSignatureInputDebugger
*
* @author $Author$
* @version $Revision$
*/
public class XMLSignatureInputDebugger {
/** Field _xmlSignatureInput */
private Set _xpathNodeSet;
private Set _inclusiveNamespaces;
/** Field _doc */
private Document _doc = null;
/** Field _documentElement */
private Element _documentElement = null;
/** Field _writer */
private Writer _writer = null;
//J-
// public static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"><html><head><style type=\"text/css\"><!-- .INCLUDED { color: #000000; background-color: #FFFFFF; font-weight: bold; } .EXCLUDED { color: #666666; background-color: #999999; } .INCLUDEDINCLUSIVENAMESPACE { color: #0000FF; background-color: #FFFFFF; font-weight: bold; font-style: italic; } .EXCLUDEDINCLUSIVENAMESPACE { color: #0000FF; background-color: #999999; font-style: italic; } --> </style> </head><body bgcolor=\"#999999\"><pre>";
public static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>Caninical XML node set</title>\n"
+ "<style type=\"text/css\">\n"
+ "<!-- \n"
+ ".INCLUDED { \n"
+ " color: #000000; \n"
+ " background-color: \n"
+ " #FFFFFF; \n"
+ " font-weight: bold; } \n"
+ ".EXCLUDED { \n"
+ " color: #666666; \n"
+ " background-color: \n"
+ " #999999; } \n"
+ ".INCLUDEDINCLUSIVENAMESPACE { \n"
+ " color: #0000FF; \n"
+ " background-color: #FFFFFF; \n"
+ " font-weight: bold; \n"
+ " font-style: italic; } \n"
+ ".EXCLUDEDINCLUSIVENAMESPACE { \n"
+ " color: #0000FF; \n"
+ " background-color: #999999; \n"
+ " font-style: italic; } \n"
+ "--> \n"
+ "</style> \n"
+ "</head>\n"
+ "<body bgcolor=\"#999999\">\n"
+ "<h1>Explanation of the output</h1>\n"
+ "<p>The following text contains the nodeset of the given Reference before it is canonicalized. There exist four different styles to indicate how a given node is treated.</p>\n"
+ "<ul>\n"
+ "<li class=\"INCLUDED\">A node which is in the node set is labeled using the INCLUDED style.</li>\n"
+ "<li class=\"EXCLUDED\">A node which is <em>NOT</em> in the node set is labeled EXCLUDED style.</li>\n"
+ "<li class=\"INCLUDEDINCLUSIVENAMESPACE\">A namespace which is in the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n"
+ "<li class=\"EXCLUDEDINCLUSIVENAMESPACE\">A namespace which is in NOT the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n"
+ "</ul>\n"
+ "<h1>Output</h1>\n"
+ "<pre>\n" ;
public static final String HTMLSuffix = "</pre></body></html>";
public static final String HTMLExcludePrefix = "<span class=\"EXCLUDED\">";
public static final String HTMLExcludeSuffix = "</span>";
public static final String HTMLIncludePrefix = "<span class=\"INCLUDED\">";
public static final String HTMLIncludeSuffix = "</span>";
public static final String HTMLIncludedInclusiveNamespacePrefix = "<span class=\"INCLUDEDINCLUSIVENAMESPACE\">";
public static final String HTMLIncludedInclusiveNamespaceSuffix = "</span>";
public static final String HTMLExcludedInclusiveNamespacePrefix = "<span class=\"EXCLUDEDINCLUSIVENAMESPACE\">";
public static final String HTMLExcludedInclusiveNamespaceSuffix = "</span>";
private static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;
private static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;
private static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;
//J+
private XMLSignatureInputDebugger() {
;
}
/**
* Constructor XMLSignatureInputDebugger
*
* @param xmlSignatureInput
*/
public XMLSignatureInputDebugger(XMLSignatureInput xmlSignatureInput)
throws XMLSignatureException {
if (!xmlSignatureInput.isNodeSet()) {
this._xpathNodeSet = null;
} else {
this._xpathNodeSet = xmlSignatureInput._inputNodeSet;
}
}
/**
* Constructor XMLSignatureInputDebugger
*
* @param xmlSignatureInput
* @param inclusiveNamespace
* @throws XMLSignatureException
*/
public XMLSignatureInputDebugger(
XMLSignatureInput xmlSignatureInput, Set inclusiveNamespace)
throws XMLSignatureException {
this(xmlSignatureInput);
this._inclusiveNamespaces = inclusiveNamespace;
}
/**
* Method getHTMLRepresentation
*
*/
public String getHTMLRepresentation() throws XMLSignatureException {
if ((this._xpathNodeSet == null) || (this._xpathNodeSet.size() == 0)) {
return HTMLPrefix + "<blink>no node set, sorry</blink>"
+ HTMLSuffix;
}
{
// get only a single node as anchor to fetch the owner document
Node n = (Node) this._xpathNodeSet.iterator().next();
this._doc = XMLUtils.getOwnerDocument(n);
this._documentElement = this._doc.getDocumentElement();
}
try {
this._writer = new StringWriter();
this.canonicalizeXPathNodeSet(this._doc);
this._writer.close();
return this._writer.toString();
} catch (IOException ex) {
throw new XMLSignatureException("empty", ex);
} finally {
this._xpathNodeSet = null;
this._doc = null;
this._documentElement = null;
this._writer = null;
}
}
/**
* Method canonicalizeXPathNodeSet
*
* @param currentNode
* @throws XMLSignatureException
*/
private void canonicalizeXPathNodeSet(Node currentNode)
throws XMLSignatureException, IOException {
int currentNodeType = currentNode.getNodeType();
boolean currentNodeIsVisible =
this._xpathNodeSet.contains(currentNode);
switch (currentNodeType) {
case Node.DOCUMENT_TYPE_NODE :
default :
break;
case Node.ENTITY_NODE :
case Node.NOTATION_NODE :
case Node.DOCUMENT_FRAGMENT_NODE :
case Node.ATTRIBUTE_NODE :
throw new XMLSignatureException("empty");
case Node.DOCUMENT_NODE :
this._writer.write(HTMLPrefix);
for (Node currentChild = currentNode.getFirstChild();
currentChild != null;
currentChild = currentChild.getNextSibling()) {
this.canonicalizeXPathNodeSet(currentChild);
}
this._writer.write(HTMLSuffix);
break;
case Node.COMMENT_NODE :
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludePrefix);
} else {
this._writer.write(HTMLExcludePrefix);
}
int position = getPositionRelativeToDocumentElement(currentNode);
if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
this._writer.write("\n");
}
this.outputCommentToWriter((Comment) currentNode);
if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
this._writer.write("\n");
}
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludeSuffix);
} else {
this._writer.write(HTMLExcludeSuffix);
}
break;
case Node.PROCESSING_INSTRUCTION_NODE :
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludePrefix);
} else {
this._writer.write(HTMLExcludePrefix);
}
position = getPositionRelativeToDocumentElement(currentNode);
if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
this._writer.write("\n");
}
this.outputPItoWriter((ProcessingInstruction) currentNode);
if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
this._writer.write("\n");
}
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludeSuffix);
} else {
this._writer.write(HTMLExcludeSuffix);
}
break;
case Node.TEXT_NODE :
case Node.CDATA_SECTION_NODE :
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludePrefix);
} else {
this._writer.write(HTMLExcludePrefix);
}
outputTextToWriter(currentNode.getNodeValue());
for (Node nextSibling =
currentNode
.getNextSibling(); (nextSibling != null) && ((nextSibling
.getNodeType() == Node.TEXT_NODE) || (nextSibling
.getNodeType() == Node
.CDATA_SECTION_NODE)); nextSibling =
nextSibling.getNextSibling()) {
/* The XPath data model allows to select only the first of a
* sequence of mixed text and CDATA nodes. But we must output
* them all, so we must search:
*
* @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329
*/
this.outputTextToWriter(nextSibling.getNodeValue());
}
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludeSuffix);
} else {
this._writer.write(HTMLExcludeSuffix);
}
break;
case Node.ELEMENT_NODE :
Element currentElement = (Element) currentNode;
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludePrefix);
} else {
this._writer.write(HTMLExcludePrefix);
}
this._writer.write("&lt;");
this._writer.write(currentElement.getTagName());
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludeSuffix);
} else {
this._writer.write(HTMLExcludeSuffix);
}
// we output all Attrs which are available
NamedNodeMap attrs = currentElement.getAttributes();
int attrsLength = attrs.getLength();
Object attrs2[] = new Object[attrsLength];
for (int i = 0; i < attrsLength; i++) {
attrs2[i] = attrs.item(i);
}
Object attrs3[] =
org.apache.xml.security.c14n.helper.C14nHelper
.sortAttributes(attrs2);
for (int i = 0; i < attrsLength; i++) {
Attr a = (Attr) attrs3[i];
boolean included = this._xpathNodeSet.contains(a);
boolean inclusive =
this._inclusiveNamespaces.contains(a.getName());
if (included) {
if (inclusive) {
// included and inclusive
this._writer.write(HTMLIncludedInclusiveNamespacePrefix);
} else {
// included and not inclusive
this._writer.write(HTMLIncludePrefix);
}
} else {
if (inclusive) {
// excluded and inclusive
this._writer.write(HTMLExcludedInclusiveNamespacePrefix);
} else {
// excluded and not inclusive
this._writer.write(HTMLExcludePrefix);
}
}
this.outputAttrToWriter(a.getNodeName(), a.getNodeValue());
if (included) {
if (inclusive) {
// included and inclusive
this._writer.write(HTMLIncludedInclusiveNamespaceSuffix);
} else {
// included and not inclusive
this._writer.write(HTMLIncludeSuffix);
}
} else {
if (inclusive) {
// excluded and inclusive
this._writer.write(HTMLExcludedInclusiveNamespaceSuffix);
} else {
// excluded and not inclusive
this._writer.write(HTMLExcludeSuffix);
}
}
}
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludePrefix);
} else {
this._writer.write(HTMLExcludePrefix);
}
this._writer.write("&gt;");
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludeSuffix);
} else {
this._writer.write(HTMLExcludeSuffix);
}
// traversal
for (Node currentChild = currentNode.getFirstChild();
currentChild != null;
currentChild = currentChild.getNextSibling()) {
this.canonicalizeXPathNodeSet(currentChild);
}
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludePrefix);
} else {
this._writer.write(HTMLExcludePrefix);
}
this._writer.write("&lt;/");
this._writer.write(currentElement.getTagName());
this._writer.write("&gt;");
if (this._xpathNodeSet.contains(currentNode)) {
this._writer.write(HTMLIncludeSuffix);
} else {
this._writer.write(HTMLExcludeSuffix);
}
break;
}
}
/**
* Checks whether a Comment or ProcessingInstruction is before or after the
* document element. This is needed for prepending or appending "\n"s.
*
* @param currentNode comment or pi to check
* @return NODE_BEFORE_DOCUMENT_ELEMENT, NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or NODE_AFTER_DOCUMENT_ELEMENT
* @see NODE_BEFORE_DOCUMENT_ELEMENT
* @see NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT
* @see NODE_AFTER_DOCUMENT_ELEMENT
*/
private int getPositionRelativeToDocumentElement(Node currentNode) {
if (currentNode == null) {
return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
}
Document doc = currentNode.getOwnerDocument();
if (currentNode.getParentNode() != doc) {
return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
}
Element documentElement = doc.getDocumentElement();
if (documentElement == null) {
return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
}
if (documentElement == currentNode) {
return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
}
for (Node x = currentNode; x != null; x = x.getNextSibling()) {
if (x == documentElement) {
return NODE_BEFORE_DOCUMENT_ELEMENT;
}
}
return NODE_AFTER_DOCUMENT_ELEMENT;
}
/**
* Normalizes an {@link Attr}ibute value
*
* The string value of the node is modified by replacing
* <UL>
* <LI>all ampersands (&) with <CODE>&amp;amp;</CODE></LI>
* <LI>all open angle brackets (<) with <CODE>&amp;lt;</CODE></LI>
* <LI>all quotation mark characters with <CODE>&amp;quot;</CODE></LI>
* <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, with character
* references. The character references are written in uppercase
* hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> is represented
* by the character reference <CODE>&amp;#xD;</CODE>)</LI>
* </UL>
*
* @param name
* @param value
* @throws IOException
*/
private void outputAttrToWriter(String name, String value)
throws IOException {
this._writer.write(" ");
this._writer.write(name);
this._writer.write("=\"");
int length = value.length();
for (int i = 0; i < length; i++) {
char c = value.charAt(i);
switch (c) {
case '&' :
this._writer.write("&amp;amp;");
break;
case '<' :
this._writer.write("&amp;lt;");
break;
case '"' :
this._writer.write("&amp;quot;");
break;
case 0x09 : // '\t'
this._writer.write("&amp;#x9;");
break;
case 0x0A : // '\n'
this._writer.write("&amp;#xA;");
break;
case 0x0D : // '\r'
this._writer.write("&amp;#xD;");
break;
default :
this._writer.write(c);
break;
}
}
this._writer.write("\"");
}
/**
* Normalizes a {@link org.w3c.dom.Comment} value
*
* @param currentPI
* @throws IOException
*/
private void outputPItoWriter(ProcessingInstruction currentPI)
throws IOException {
if (currentPI == null) {
return;
}
this._writer.write("&lt;?");
String target = currentPI.getTarget();
int length = target.length();
for (int i = 0; i < length; i++) {
char c = target.charAt(i);
switch (c) {
case 0x0D :
this._writer.write("&amp;#xD;");
break;
case ' ' :
this._writer.write("&middot;");
break;
case '\n' :
this._writer.write("&para;\n");
break;
default :
this._writer.write(c);
break;
}
}
String data = currentPI.getData();
length = data.length();
if ((data != null) && (length > 0)) {
this._writer.write(" ");
for (int i = 0; i < length; i++) {
char c = data.charAt(i);
switch (c) {
case 0x0D :
this._writer.write("&amp;#xD;");
break;
default :
this._writer.write(c);
break;
}
}
}
this._writer.write("?&gt;");
}
/**
* Method outputCommentToWriter
*
* @param currentComment
* @throws IOException
*/
private void outputCommentToWriter(Comment currentComment)
throws IOException {
if (currentComment == null) {
return;
}
this._writer.write("&lt;!--");
String data = currentComment.getData();
int length = data.length();
for (int i = 0; i < length; i++) {
char c = data.charAt(i);
switch (c) {
case 0x0D :
this._writer.write("&amp;#xD;");
break;
case ' ' :
this._writer.write("&middot;");
break;
case '\n' :
this._writer.write("&para;\n");
break;
default :
this._writer.write(c);
break;
}
}
this._writer.write("--&gt;");
}
/**
* Method outputTextToWriter
*
* @param text
* @throws IOException
*/
private void outputTextToWriter(String text) throws IOException {
if (text == null) {
return;
}
int length = text.length();
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
switch (c) {
case '&' :
this._writer.write("&amp;amp;");
break;
case '<' :
this._writer.write("&amp;lt;");
break;
case '>' :
this._writer.write("&amp;gt;");
break;
case 0xD :
this._writer.write("&amp;#xD;");
break;
case ' ' :
this._writer.write("&middot;");
break;
case '\n' :
this._writer.write("&para;\n");
break;
default :
this._writer.write(c);
break;
}
}
}
}
}