package net.winroad.wrdoclet.utils;

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

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class UniversalNamespaceCache implements NamespaceContext {
	private static final String DEFAULT_NS = "DEFAULT";
	private Map<String, String> prefix2Uri = new HashMap<String, String>();
	private Map<String, String> uri2Prefix = new HashMap<String, String>();

	/**
	 * This constructor parses the document and stores all namespaces it can
	 * find. If toplevelOnly is true, only namespaces in the root are used.
	 * 
	 * @param document
	 *            source document
	 * @param toplevelOnly
	 *            restriction of the search to enhance performance
	 */
	public UniversalNamespaceCache(Document document, boolean toplevelOnly) {
		examineNode(document.getFirstChild(), toplevelOnly);
		System.out.println("The list of the cached namespaces:");
		for (String key : prefix2Uri.keySet()) {
			System.out
					.println("prefix " + key + ": uri " + prefix2Uri.get(key));
		}
	}

	/**
	 * A single node is read, the namespace attributes are extracted and stored.
	 * 
	 * @param node
	 *            to examine
	 * @param attributesOnly,
	 *            if true no recursion happens
	 */
	private void examineNode(Node node, boolean attributesOnly) {
		NamedNodeMap attributes = node.getAttributes();
		for (int i = 0; i < attributes.getLength(); i++) {
			Node attribute = attributes.item(i);
			storeAttribute((Attr) attribute);
		}

		if (!attributesOnly) {
			NodeList chields = node.getChildNodes();
			for (int i = 0; i < chields.getLength(); i++) {
				Node chield = chields.item(i);
				if (chield.getNodeType() == Node.ELEMENT_NODE)
					examineNode(chield, false);
			}
		}
	}

	/**
	 * This method looks at an attribute and stores it, if it is a namespace
	 * attribute.
	 * 
	 * @param attribute
	 *            to examine
	 */
	private void storeAttribute(Attr attribute) {
		// examine the attributes in namespace xmlns
		if (attribute.getNamespaceURI() != null
				&& attribute.getNamespaceURI().equals(
						XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
			// Default namespace xmlns="uri goes here"
			if (attribute.getNodeName().equals(XMLConstants.XMLNS_ATTRIBUTE)) {
				putInCache(DEFAULT_NS, attribute.getNodeValue());
			} else {
				// Here are the defined prefixes stored
				putInCache(attribute.getLocalName(), attribute.getNodeValue());
			}
		}

	}

	private void putInCache(String prefix, String uri) {
		prefix2Uri.put(prefix, uri);
		uri2Prefix.put(uri, prefix);
	}

	/**
	 * This method is called by XPath. It returns the default namespace, if the
	 * prefix is null or "".
	 * 
	 * @param prefix
	 *            to search for
	 * @return uri
	 */
	public String getNamespaceURI(String prefix) {
		if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
			return prefix2Uri.get(DEFAULT_NS);
		} else {
			return prefix2Uri.get(prefix);
		}
	}

	/**
	 * This method is not needed in this context, but can be implemented in a
	 * similar way.
	 */
	public String getPrefix(String namespaceURI) {
		return uri2Prefix.get(namespaceURI);
	}

	@SuppressWarnings("rawtypes")
	public Iterator getPrefixes(String namespaceURI) {
		// Not implemented
		return null;
	}

}
@@ -1482,16 +1482,142 @@
 Only) %7B%0A
+%09%09while(node != null && node.getNodeType() == Node.COMMENT_NODE) %7B%0A%09%09%09node = node.getNextSibling();%0A%09%09%7D%0A%09%09if(node != null) %7B%0A%09
 %09%09NamedN
@@ -1658,16 +1658,46 @@
 utes();%0A
+%09%09%09if(attributes != null) %7B%0A%09%09
 %09%09for (i
@@ -1740,24 +1740,26 @@
 ; i++) %7B%0A%09%09%09
+%09
+%09
 Node attribu
@@ -1783,16 +1783,18 @@
 tem(i);%0A
+%09%09
 %09%09%09store
@@ -1824,19 +1824,27 @@
 ute);%0A%09%09
-%7D%0A%0A
+%09%09%7D%0A%09%09%09%7D%0A%0A%09
 %09%09if (!a
@@ -1863,16 +1863,17 @@
 y) %7B%0A%09%09%09
+%09
 NodeList
@@ -1905,16 +1905,17 @@
 odes();%0A
+%09
 %09%09%09for (
@@ -1953,24 +1953,25 @@
 h(); i++) %7B%0A
+%09
 %09%09%09%09Node chi
@@ -1997,16 +1997,17 @@
 i);%0A%09%09%09%09
+%09
 if (chie
@@ -2050,16 +2050,17 @@
 E)%0A%09%09%09%09%09
+%09
 examineN
@@ -2079,16 +2079,22 @@
 false);%0A
+%09%09%09%09%7D%0A
 %09%09%09%7D%0A%09%09%7D

fix null pointer bug when there is comment node in xml file