Java tutorial
/** * 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.utils; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.WeakHashMap; import java.util.Map; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * Purpose of this class is to enable the XML Parser to keep track of ID * attributes. This is done by 'registering' attributes of type ID at the * IdResolver. This is necessary if we create a document from scratch and we * sign some resources with a URI using a fragment identifier... * <BR /> * The problem is that if you do not validate a document, you cannot use the * <CODE>getElementByID</CODE> functionality. So this modules uses some implicit * knowledge on selected Schemas and DTDs to pick the right Element for a given * ID: We know that all <CODE>@Id</CODE> attributes in an Element from the XML * Signature namespace are of type <CODE>ID</CODE>. * * @see <A HREF="http://www.xml.com/lpt/a/2001/11/07/id.html">"Identity Crisis" on xml.com</A> */ public class IdResolver { /** {@link org.apache.commons.logging} logging facility */ private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory .getLog(IdResolver.class); private static Map<Document, Map<String, WeakReference<Element>>> docMap = new WeakHashMap<Document, Map<String, WeakReference<Element>>>(); private static java.util.List<String> names; private static int namesLength; static { String namespaces[] = { Constants.SignatureSpecNS, EncryptionConstants.EncryptionSpecNS, "http://schemas.xmlsoap.org/soap/security/2000-12", "http://www.w3.org/2002/03/xkms#", "urn:oasis:names:tc:SAML:1.0:assertion", "urn:oasis:names:tc:SAML:1.0:protocol" }; names = Arrays.asList(namespaces); namesLength = names.size(); } /** * Constructor IdResolver * */ private IdResolver() { // we don't allow instantiation } /** * Method registerElementById * * @param element the element to register * @param idValue the value of the ID attribute */ public static void registerElementById(Element element, String idValue) { Document doc = element.getOwnerDocument(); Map<String, WeakReference<Element>> elementMap; synchronized (docMap) { elementMap = docMap.get(doc); if (elementMap == null) { elementMap = new WeakHashMap<String, WeakReference<Element>>(); docMap.put(doc, elementMap); } } elementMap.put(idValue, new WeakReference<Element>(element)); } /** * Force a removal of a registered document. Any element id associated * with this document will be removed from the weak reference map. * * @param doc the DOM document that is to be removed from the map. */ public static void unregisterDocument(Document doc) { synchronized (docMap) { docMap.remove(doc); } } /** * Method registerElementById * * @param element the element to register * @param id the ID attribute */ public static void registerElementById(Element element, Attr id) { IdResolver.registerElementById(element, id.getNodeValue()); } /** * Method getElementById * * @param doc the document * @param id the value of the ID * @return the element obtained by the id, or null if it is not found. */ public static Element getElementById(Document doc, String id) { Element result = IdResolver.getElementByIdType(doc, id); if (result != null) { if (log.isDebugEnabled()) { log.debug("I could find an Element using the simple getElementByIdType method: " + result.getTagName()); } return result; } result = IdResolver.getElementByIdUsingDOM(doc, id); if (result != null) { if (log.isDebugEnabled()) { log.debug("I could find an Element using the simple getElementByIdUsingDOM method: " + result.getTagName()); } return result; } result = IdResolver.getElementBySearching(doc, id); if (result != null) { IdResolver.registerElementById(result, id); return result; } return null; } /** * Method getElementByIdUsingDOM * * @param doc the document * @param id the value of the ID * @return the element obtained by the id, or null if it is not found. */ private static Element getElementByIdUsingDOM(Document doc, String id) { if (log.isDebugEnabled()) { log.debug("getElementByIdUsingDOM() Search for ID " + id); } return doc.getElementById(id); } /** * Method getElementByIdType * * @param doc the document * @param id the value of the ID * @return the element obtained by the id, or null if it is not found. */ private static Element getElementByIdType(Document doc, String id) { if (log.isDebugEnabled()) { log.debug("getElementByIdType() Search for ID " + id); } Map<String, WeakReference<Element>> elementMap; synchronized (docMap) { elementMap = docMap.get(doc); } if (elementMap != null) { WeakReference<Element> weakReference = (WeakReference<Element>) elementMap.get(id); if (weakReference != null) { return (Element) weakReference.get(); } } return null; } private static Element getElementBySearching(Node root, String id) { Element[] els = new Element[namesLength + 1]; getEl(root, id, els); for (int i = 0; i < els.length; i++) { if (els[i] != null) { return els[i]; } } return null; } private static int getEl(Node currentNode, String id, Element[] els) { Node sibling = null; Node parentNode = null; do { switch (currentNode.getNodeType()) { case Node.DOCUMENT_FRAGMENT_NODE: case Node.DOCUMENT_NODE: sibling = currentNode.getFirstChild(); break; case Node.ELEMENT_NODE: Element currentElement = (Element) currentNode; if (isElement(currentElement, id, els) == 1) { return 1; } sibling = currentNode.getFirstChild(); if (sibling == null) { if (parentNode != null) { sibling = currentNode.getNextSibling(); } } else { parentNode = currentElement; } break; } while (sibling == null && parentNode != null) { sibling = parentNode.getNextSibling(); parentNode = parentNode.getParentNode(); if (parentNode != null && Node.ELEMENT_NODE != parentNode.getNodeType()) { parentNode = null; } } if (sibling == null) { return 1; } currentNode = sibling; sibling = currentNode.getNextSibling(); } while (true); } public static int isElement(Element el, String id, Element[] els) { if (!el.hasAttributes()) { return 0; } NamedNodeMap ns = el.getAttributes(); int elementIndex = names.indexOf(el.getNamespaceURI()); elementIndex = (elementIndex < 0) ? namesLength : elementIndex; for (int length = ns.getLength(), i = 0; i < length; i++) { Attr n = (Attr) ns.item(i); String s = n.getNamespaceURI(); int index = s == null ? elementIndex : names.indexOf(n.getNamespaceURI()); index = (index < 0) ? namesLength : index; String name = n.getLocalName(); if (name == null) { name = n.getName(); } if (name.length() > 2) { continue; } String value = n.getNodeValue(); if (name.charAt(0) == 'I') { char ch = name.charAt(1); if (ch == 'd' && value.equals(id)) { els[index] = el; if (index == 0) { return 1; } } else if (ch == 'D' && value.endsWith(id)) { if (index != 3) { index = namesLength; } els[index] = el; } } else if ("id".equals(name) && value.equals(id)) { if (index != 2) { index = namesLength; } els[index] = el; } } //For an element namespace search for importants if ((elementIndex == 3) && (el.getAttribute("OriginalRequestID").equals(id) || el.getAttribute("RequestID").equals(id) || el.getAttribute("ResponseID").equals(id))) { els[3] = el; } else if ((elementIndex == 4) && (el.getAttribute("AssertionID").equals(id))) { els[4] = el; } else if ((elementIndex == 5) && (el.getAttribute("RequestID").equals(id) || el.getAttribute("ResponseID").equals(id))) { els[5] = el; } return 0; } }