blob: aaccef648545d005d2323af22377caab9c04c46d [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.xml.security.c14n.implementations;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.helper.C14nHelper;
import org.apache.xml.security.utils.Constants;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Implements <A HREF="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">Canonical
* XML Version 1.0</A>, a W3C Recommendation from 15 March 2001.
*
* @author Christian Geuer-Pollmann <geuerp@apache.org>
* @version $Revision$
*/
public abstract class Canonicalizer20010315 extends CanonicalizerBase {
boolean firstCall=true;
final SortedSet result= new TreeSet(COMPARE);
static final String XMLNS_URI=Constants.NamespaceSpecNS;
static final String XML_LANG_URI=Constants.XML_LANG_SPACE_SpecNS;
/**
* Constructor Canonicalizer20010315
*
* @param includeComments
*/
public Canonicalizer20010315(boolean includeComments) {
super(includeComments);
}
/**
* Returns the Attr[]s to be outputted for the given element.
* <br>
* The code of this method is a copy of {@link #handleAttributes(Element,
* NameSpaceSymbTable)},
* whereas it takes into account that subtree-c14n is -- well -- subtree-based.
* So if the element in question isRoot of c14n, it's parent is not in the
* node set, as well as all other ancestors.
*
* @param E
* @param ns
* @return the Attr[]s to be outputted
* @throws CanonicalizationException
*/
Iterator handleAttributesSubtree(Element E, NameSpaceSymbTable ns )
throws CanonicalizationException {
if (!E.hasAttributes() && !firstCall) {
return null;
}
// result will contain the attrs which have to be outputted
final SortedSet result = this.result;
result.clear();
NamedNodeMap attrs = E.getAttributes();
int attrsLength = attrs.getLength();
for (int i = 0; i < attrsLength; i++) {
Attr N = (Attr) attrs.item(i);
String NUri =N.getNamespaceURI();
if (XMLNS_URI!=NUri) {
//It's not a namespace attr node. Add to the result and continue.
result.add(N);
continue;
}
String NName=N.getLocalName();
String NValue=N.getValue();
if (XML.equals(NName)
&& XML_LANG_URI.equals(NValue)) {
//The default mapping for xml must not be output.
continue;
}
Node n=ns.addMappingAndRender(NName,NValue,N);
if (n!=null) {
//Render the ns definition
result.add(n);
if (C14nHelper.namespaceIsRelative(N)) {
Object exArgs[] = { E.getTagName(), NName, N.getNodeValue() };
throw new CanonicalizationException(
"c14n.Canonicalizer.RelativeNamespace", exArgs);
}
}
}
if (firstCall) {
//It is the first node of the subtree
//Obtain all the namespaces defined in the parents, and added to the output.
ns.getUnrenderedNodes(result);
//output the attributes in the xml namespace.
addXmlAttributesSubtree(E, result);
firstCall=false;
}
return result.iterator();
}
/**
* Float the xml:* attributes of the parent nodes to the root node of c14n
* @param E the root node.
* @param result the xml:* attributes to output.
*/
private void addXmlAttributesSubtree(Element E, SortedSet result) {
// E is in the node-set
Node parent = E.getParentNode();
Map loa = new HashMap();
if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE)) {
// parent element is not in node set
for (Node ancestor = parent;
(ancestor != null)
&& (ancestor.getNodeType() == Node.ELEMENT_NODE);
ancestor = ancestor.getParentNode()) {
Element el=((Element) ancestor);
if (!el.hasAttributes()) {
continue;
}
// for all ancestor elements
NamedNodeMap ancestorAttrs = el.getAttributes();
int length=ancestorAttrs.getLength();
for (int i = 0; i < length; i++) {
// for all attributes in the ancestor element
Attr currentAncestorAttr = (Attr) ancestorAttrs.item(i);
if (XML_LANG_URI==currentAncestorAttr.getNamespaceURI()) {
String name=currentAncestorAttr.getName();
if (!loa.containsKey(name) ) {
loa.put(name, currentAncestorAttr);
}
}
}
}
}
result.addAll( loa.values());
}
/**
* Returns the Attr[]s to be outputted for the given element.
* <br>
* IMPORTANT: This method expects to work on a modified DOM tree, i.e. a DOM which has
* been prepared using {@link org.apache.xml.security.utils.XMLUtils#circumventBug2650(
* org.w3c.dom.Document)}.
*
* @param E
* @param ns
* @return the Attr[]s to be outputted
* @throws CanonicalizationException
*/
Iterator handleAttributes(Element E, NameSpaceSymbTable ns ) throws CanonicalizationException {
// result will contain the attrs which have to be outputted
boolean isRealVisible=isVisible(E);
NamedNodeMap attrs = null;
int attrsLength = 0;
if (E.hasAttributes()) {
attrs=E.getAttributes();
attrsLength= attrs.getLength();
}
SortedSet result = this.result;
result.clear();
for (int i = 0; i < attrsLength; i++) {
Attr N = (Attr) attrs.item(i);
String NUri =N.getNamespaceURI();
if (XMLNS_URI!=NUri) {
//A non namespace definition node.
if (isRealVisible){
//The node is visible add the attribute to the list of output attributes.
result.add(N);
}
//keep working
continue;
}
String NName=N.getLocalName();
String NValue=N.getValue();
if ("xml".equals(NName)
&& XML_LANG_URI.equals(NValue)) {
/* except omit namespace node with local name xml, which defines
* the xml prefix, if its string value is http://www.w3.org/XML/1998/namespace.
*/
continue;
}
//add the prefix binding to the ns symb table.
//ns.addInclusiveMapping(NName,NValue,N,isRealVisible);
if (isVisible(N)) {
//The xpath select this node output it if needed.
Node n=ns.addMappingAndRenderXNodeSet(NName,NValue,N,isRealVisible);
if (n!=null) {
result.add(n);
if (C14nHelper.namespaceIsRelative(N)) {
Object exArgs[] = { E.getTagName(), NName, N.getNodeValue() };
throw new CanonicalizationException(
"c14n.Canonicalizer.RelativeNamespace", exArgs);
}
}
}
}
if (isRealVisible) {
//The element is visible, handle the xmlns definition
Attr xmlns = E.getAttributeNodeNS(XMLNS_URI, XMLNS);
Node n=null;
if (xmlns == null) {
//No xmlns def just get the already defined.
n=ns.getMapping(XMLNS);
} else if ( !isVisible(xmlns)) {
//There is a definition but the xmlns is not selected by the xpath.
//then xmlns=""
n=ns.addMappingAndRenderXNodeSet(XMLNS,"",nullNode,true);
}
//output the xmlns def if needed.
if (n!=null) {
result.add(n);
}
//Float all xml:* attributes of the unselected parent elements to this one.
addXmlAttributes(E,result);
}
return result.iterator();
}
/**
* Float the xml:* attributes of the unselected parent nodes to the ciurrent node.
* @param E
* @param result
*/
private void addXmlAttributes(Element E, SortedSet result) {
/* The processing of an element node E MUST be modified slightly when an
* XPath node-set is given as input and the element's parent is omitted
* from the node-set. The method for processing the attribute axis of an
* element E in the node-set is enhanced. All element nodes along E's
* ancestor axis are examined for nearest occurrences of attributes in
* the xml namespace, such as xml:lang and xml:space (whether or not they
* are in the node-set). From this list of attributes, remove any that are
* in E's attribute axis (whether or not they are in the node-set). Then,
* lexicographically merge this attribute list with the nodes of E's
* attribute axis that are in the node-set. The result of visiting the
* attribute axis is computed by processing the attribute nodes in this
* merged attribute list.
*/
// E is in the node-set
Node parent = E.getParentNode();
Map loa = new HashMap();
if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE)
&&!isVisible(parent)) {
// parent element is not in node set
for (Node ancestor = parent;
(ancestor != null)
&& (ancestor.getNodeType() == Node.ELEMENT_NODE);
ancestor = ancestor.getParentNode()) {
Element el=((Element) ancestor);
if (!el.hasAttributes()) {
continue;
}
// for all ancestor elements
NamedNodeMap ancestorAttrs =el.getAttributes();
int length=ancestorAttrs.getLength();
for (int i = 0; i < length; i++) {
// for all attributes in the ancestor element
Attr currentAncestorAttr = (Attr) ancestorAttrs.item(i);
if (XML_LANG_URI==currentAncestorAttr.getNamespaceURI()) {
String name=currentAncestorAttr.getName();
if (!loa.containsKey(name) ) {
loa.put(name, currentAncestorAttr);
}
}
}
}
}
result.addAll(loa.values());
}
/**
* Always throws a CanonicalizationException because this is inclusive c14n.
*
* @param xpathNodeSet
* @param inclusiveNamespaces
* @return none it always fails
* @throws CanonicalizationException always
*/
public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet, String inclusiveNamespaces)
throws CanonicalizationException {
/** $todo$ well, should we throw UnsupportedOperationException ? */
throw new CanonicalizationException(
"c14n.Canonicalizer.UnsupportedOperation");
}
/**
* Always throws a CanonicalizationException because this is inclusive c14n.
*
* @param rootNode
* @param inclusiveNamespaces
* @return none it always fails
* @throws CanonicalizationException
*/
public byte[] engineCanonicalizeSubTree(Node rootNode, String inclusiveNamespaces)
throws CanonicalizationException {
/** $todo$ well, should we throw UnsupportedOperationException ? */
throw new CanonicalizationException(
"c14n.Canonicalizer.UnsupportedOperation");
}
}