/*
 * Copyright (c) 2007-2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * 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 com.sun.org.apache.xml.internal.security.utils;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.WeakHashMap;
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 fragent identifier...
 * 
 * The problem is that if you do not validate a document, you cannot use the
 * getElementByID 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 @Id attributes in an Element from the XML
 * Signature namespace are of type ID.
 *
 * @author $Author: mullan $
 * @see "Identity Crisis" on xml.com
 */
public class IdResolver {
    /** {@link java.util.logging} logging facility */
    private static java.util.logging.Logger log =
        java.util.logging.Logger.getLogger(IdResolver.class.getName());
    private static WeakHashMap docMap = new WeakHashMap();
    /**
     * 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();
        WeakHashMap elementMap;
        synchronized (docMap) {
            elementMap = (WeakHashMap) docMap.get(doc);
            if (elementMap == null) {
                elementMap = new WeakHashMap();
                docMap.put(doc, elementMap);
            }
        }
        elementMap.put(idValue, new WeakReference(element));
    }
    /**
     * 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) {
            log.log(java.util.logging.Level.FINE,
            "I could find an Element using the simple getElementByIdType method: "
            + result.getTagName());
            return result;
        }
        result = IdResolver.getElementByIdUsingDOM(doc, id);
        if (result != null) {
            log.log(java.util.logging.Level.FINE,
             "I could find an Element using the simple getElementByIdUsingDOM method: "
            + result.getTagName());
            return result;
        }
        // this must be done so that Xalan can catch ALL namespaces
        //XMLUtils.circumventBug2650(doc);
        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.isLoggable(java.util.logging.Level.FINE))
            log.log(java.util.logging.Level.FINE, "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.isLoggable(java.util.logging.Level.FINE))
            log.log(java.util.logging.Level.FINE, "getElementByIdType() Search for ID " + id);
        WeakHashMap elementMap;
        synchronized (docMap) {
            elementMap = (WeakHashMap) docMap.get(doc);
        }
        if (elementMap != null) {
            WeakReference weakReference = (WeakReference) elementMap.get(id);
            if (weakReference != null) {
                return (Element) weakReference.get();
            }
        }
        return null;
    }
    private static java.util.List 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();
    }
    private static Element getElementBySearching(Node root,String id) {
        Element []els=new Element[namesLength + 1];
        getEl(root,id,els);
        for (int i=0;i2)
                        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;
    }
}