
package org.biomoby.shared;

import org.w3c.dom.*;

import javax.xml.namespace.NamespaceContext;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * This class is used to provide namespace context for XPath expression evaluation
 * in the Seahawk data mapping rules file, and includes default mappings for 
 * prefixes such as moby:
 */
public class NamespaceContextImpl implements NamespaceContext{
    private Map<String, String> prefixes;
    private Map<String, String> nsURIs;

    /**
     * Sets just one prefix, the one provided, which also gets mapped to the blank prefix
     */
    public NamespaceContextImpl(String ns, String nsPrefix){
	prefixes = new HashMap<String, String>();
	nsURIs = new HashMap<String, String>();

	prefixes.put(ns, nsPrefix);
	nsURIs.put(nsPrefix, ns);
	nsURIs.put("", ns);
    }

    /**
     * Traverses up the DOM from the given element to enumerate all of the prefix <-> namespace mappings
     * valid for this node.  Note that the DOM does not explicitly contain the xmlns attributes
     * therefore we glean this info from the qualified elements and attributes.
     */
    public NamespaceContextImpl(Node contextNode, String defaultNSPrefix){
	prefixes = new HashMap<String, String>();
	nsURIs = new HashMap<String, String>();

	for(Node currentNode = contextNode; currentNode != null; currentNode = currentNode.getParentNode()){
	    if(currentNode instanceof Element){
		Element el = (Element) currentNode; 
		String uri = el.getNamespaceURI();
		String prefix = el.getPrefix();
		if(uri != null && uri.length() != 0){
		    if(prefix == null || prefix.length() == 0){
			nsURIs.put(defaultNSPrefix, uri);
		    }
		    if(!nsURIs.containsKey(prefix)){
			nsURIs.put(prefix, uri);
		    }
		}

		NamedNodeMap xmlnsAttrs = ((Element) currentNode).getAttributes();
		for(int i = 0; i < xmlnsAttrs.getLength(); i++){
		    Attr xmlnsDecl = (Attr) xmlnsAttrs.item(i);
		    if(xmlnsDecl.getNamespaceURI() == null ||
		       xmlnsDecl.getPrefix() == null){
			continue;
		    }

		    if(xmlnsDecl.getPrefix().equals("xmlns")){
			uri = xmlnsDecl.getValue();
			prefix = xmlnsDecl.getLocalName();
		    }
		    else{
			uri = xmlnsDecl.getNamespaceURI();
			prefix = xmlnsDecl.getPrefix();
		    }

		    if(!prefixes.containsKey(uri)){
			prefixes.put(uri, prefix);
		    }
		    if(!nsURIs.containsKey(prefix)){
			nsURIs.put(prefix, uri);
		    }
		}
	    }
	}

	// If no defaultNS is declared, bind it to the last one we encountered, i.e. (for RPC WS data)
	if(!nsURIs.containsKey(defaultNSPrefix)){
	    nsURIs.put(defaultNSPrefix, "");
	}

	// As a bonus, always include the "fn" prefix, which allows access to XPath functions
	prefixes.put("http://www.w3.org/2005/xpath-functions", "fn");
	nsURIs.put("fn", "http://www.w3.org/2005/xpath-functions");
    }

    /**
     * Populates the mapping table with an assortment of prefixes and namespaces used in Moby (see MobyPrefixResolver)
     */
    public NamespaceContextImpl(){
	prefixes = new HashMap<String, String>();
	nsURIs = new HashMap<String, String>();

	// Populate with default mappings (Mainly for XPath usage)
	prefixes.put(MobyPrefixResolver.MOBY_XML_NAMESPACE, MobyPrefixResolver.MOBY_XML_PREFIX);
	prefixes.put(MobyPrefixResolver.MOBY_XML_NAMESPACE_INVALID, MobyPrefixResolver.MOBY_XML_PREFIX);
	prefixes.put(MobyPrefixResolver.MOBY_TRANSPORT_NAMESPACE, MobyPrefixResolver.MOBY_TRANSPORT_PREFIX);
	prefixes.put(MobyPrefixResolver.XSI_NAMESPACE1999, MobyPrefixResolver.XSI1999_PREFIX);
	prefixes.put(MobyPrefixResolver.XSI_NAMESPACE2001, MobyPrefixResolver.XSI2001_PREFIX);
	prefixes.put(MobyPrefixResolver.SOAP_ENC_NAMESPACE, MobyPrefixResolver.SOAP_ENC_PREFIX);
	prefixes.put(MobyPrefixResolver.WS_ADDRESSING_NAMESPACE, MobyPrefixResolver.WS_ADDRESSING_PREFIX); 
	prefixes.put(MobyPrefixResolver.WSRP_NAMESPACE, MobyPrefixResolver.WSRP_PREFIX);	
	prefixes.put(MobyPrefixResolver.XHTML_NAMESPACE, MobyPrefixResolver.XHTML_PREFIX);
	prefixes.put(MobyPrefixResolver.XLINK_NAMESPACE, MobyPrefixResolver.XLINK_PREFIX);
	prefixes.put(MobyPrefixResolver.WSDL_NAMESPACE, MobyPrefixResolver.WSDL_PREFIX);
	prefixes.put(MobyPrefixResolver.HTTP_NAMESPACE, MobyPrefixResolver.HTTP_PREFIX);
	prefixes.put(MobyPrefixResolver.SAWSDL_NAMESPACE, MobyPrefixResolver.SAWSDL_PREFIX);
	prefixes.put(MobyPrefixResolver.LSID_NAMESPACE, MobyPrefixResolver.LSID_PREFIX);
	prefixes.put(MobyPrefixResolver.XSD_NAMESPACE, MobyPrefixResolver.XSD_PREFIX);
	prefixes.put(MobyPrefixResolver.DUBLIN_CORE_NAMESPACE, MobyPrefixResolver.DUBLIN_CORE_PREFIX);

	// Reverse map prefix -> nsURI
	nsURIs.put(MobyPrefixResolver.XSI_PREFIX, MobyPrefixResolver.XSI_NAMESPACE2001);
	for(Map.Entry<String, String> entry: prefixes.entrySet()){
	    nsURIs.put(entry.getValue(), entry.getKey());
	}
    }

    // JAX NamespaceContext requirements
    public String getNamespaceURI(String prefix){
	//System.err.println("Asked for ns of '" + prefix + "', giving " + nsURIs.get(prefix));
	return nsURIs.get(prefix);
    }

    public String getPrefix(String namespaceURI){
	return prefixes.get(namespaceURI);
    }

    public void setPrefix(String namespaceURI, String prefix){
	prefixes.put(namespaceURI, prefix);
	nsURIs.put(prefix, namespaceURI);
    }

    public Iterator getPrefixes(String namespaceURI){
	return prefixes.keySet().iterator();
    }
}
