package me.nallar.tickthreading.util;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import me.nallar.tickthreading.Log;
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;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public enum DomUtil {
	;

	public static List<Element> getElementsByTag(Element element, String tagName) {
		return elementList(element.getElementsByTagName(tagName));
	}

	public static List<Element> elementList(NodeList nodeList) {
		List<Node> nodes = nodeList(nodeList);
		ArrayList<Element> elements = new ArrayList<Element>(nodeList.getLength());
		for (Node node : nodes) {
			if (node instanceof Element) {
				elements.add((Element) node);
			}
		}
		elements.trimToSize();
		return elements;
	}

	public static List<Node> nodeList(NodeList nodeList) {
		int length = nodeList.getLength();
		List<Node> nodes = new ArrayList<Node>(length);
		for (int i = 0; i < length; i++) {
			nodes.add(nodeList.item(i));
		}
		return nodes;
	}

	public static int getHash(Element node) {
		int hash = 5381;
		for (Element child : elementList(node.getChildNodes())) {
			hash += (hash << 5) + getHash(child);
		}
		hash += (hash << 5) + node.getTagName().length();
		hash += (hash << 5) + getAttributes(node).size();
		return hash;
	}

	public static Map<String, String> getAttributes(Node node) {
		NamedNodeMap attributeMap = node.getAttributes();
		HashMap<String, String> attributes = new HashMap<String, String>(attributeMap.getLength());
		for (int i = 0; i < attributeMap.getLength(); i++) {
			Node attr = attributeMap.item(i);
			if (attr instanceof Attr) {
				attributes.put(((Attr) attr).getName(), ((Attr) attr).getValue());
			}
		}
		return attributes;
	}

	public static Document readDocumentFromInputStream(InputStream configInputStream) throws IOException, SAXException {
		if (configInputStream == null) {
			throw new NullPointerException("configInputStream");
		}
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			return docBuilder.parse(configInputStream);
		} catch (ParserConfigurationException e) {
			//This exception is thrown, and no shorthand way of getting a DocumentBuilder without it.
			//Should not be thrown, as we do not do anything to the DocumentBuilderFactory.
			Log.severe("Java was bad, this shouldn't happen. DocBuilder instantiation via default docBuilderFactory failed", e);
			configInputStream.close();
		}
		return null;
	}
}

@@ -241,16 +241,48 @@
 tream;%0D%0A
+import java.util.AbstractList;%0D%0A
 import j
@@ -298,24 +298,24 @@
 ArrayList;%0D%0A
-
 import java.
@@ -969,30 +969,22 @@
 nt%3E(node
-List.getLength
+s.size
 ());%0D%0A%09%09
@@ -1206,185 +1206,57 @@
 %0D%0A%09%09
-int length = nodeList.getLength();%0D%0A%09%09List%3CNode%3E nodes = new ArrayList%3CNode%3E(length);%0D%0A%09%09for (int i = 0; i %3C length; i++) %7B%0D%0A%09%09%09nodes.add(nodeList.item(i));%0D%0A%09%09%7D%0D%0A%09%09return nodes
+return new NodeListWhichIsActuallyAList(nodeList)
 ;%0D%0A%09
@@ -1509,16 +1509,21 @@
 %3C%3C 5) + 
+node.
 getAttri
@@ -1532,18 +1532,19 @@
 tes(
-node).size
+).getLength
 ();%0D
@@ -2828,15 +2828,413 @@
  null;%0D%0A
-
 %09%7D%0D%0A
+%0D%0A%09private static class NodeListWhichIsActuallyAList extends AbstractList%3CNode%3E implements List%3CNode%3E %7B%0D%0A%09%09private final NodeList nodeList;%0D%0A%0D%0A%09%09NodeListWhichIsActuallyAList(NodeList nodeList) %7B%0D%0A%09%09%09this.nodeList = nodeList;%0D%0A%09%09%7D%0D%0A%0D%0A%09%09@Override%0D%0A%09%09public Node get(int index) %7B%0D%0A%09%09%09return nodeList.item(index);%0D%0A%09%09%7D%0D%0A%0D%0A%09%09@Override%0D%0A%09%09public int size() %7B%0D%0A%09%09%09return nodeList.getLength();%0D%0A%09%09%7D%0D%0A%09%7D%0D%0A%0D%0A
 %7D%0D%0A

Use new list class instead of ArrayList in DomUtil.nodeList