/*
 * XmlTools.java
 *
 * Oystein
 * ... not Johan's XmlTools.java
 * source: <http://www.cafeconleche.org/books/xmljava/chapters/ch11.html>
 */

package aksis.alignment;

import org.w3c.dom.*;
import java.util.*;
import javax.swing.*;   // ### not xml utility, but...
import java.util.regex.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
//import javax.xml.transform.OutputKeys;
import java.io.*;
import org.w3c.dom.*;

class XmlTools {

	public static String getXmlContent(Node node) {

		// use a Transformer to convert element to xml string

		String xmlString = "*** error in method getXmlContent() ***";

		try {

			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");   // 2006-09-19

			StreamResult result = new StreamResult(new StringWriter());
			DOMSource source = new DOMSource(node);
			transformer.transform(source, result);
			xmlString = result.getWriter().toString();
			Pattern pattern = Pattern.compile("[ \\n\\r]+");   // 2006-09-19
			Matcher matcher = pattern.matcher(xmlString);
			xmlString = matcher.replaceAll(" ");

		} catch (TransformerConfigurationException tce) {

			// Error generated by the parser
			System.out.println ("*** Transformer Factory error: " + tce.getMessage());

			// Use the contained exception, if any
			Throwable x = tce;
			if (tce.getException() != null) {
				x = tce.getException();
			}
			x.printStackTrace();

		} catch (TransformerException te) {

			// Error generated by the parser
			System.out.println ("*** Transformation error: " + te.getMessage());

			// Use the contained exception, if any
			Throwable x = te;
			if (te.getException() != null) {
				x = te.getException();
			}
			x.printStackTrace();

		}

		return xmlString;

	}

	public static NodeList getElementsByTagNames(Document doc, String[] tags, String specialCharacters) throws Exception {

		// 2006-10-03.
		// specialCharacters contains the characters that should be stripped off words
		// when segmenting text into words..
		// this method must know these characters to be able to discover empty alignable elements
		// = alignable elements containing no words

		// the standard Element method getElementsByTagName()
		// returns a NodeList of all descendant Elements
		// with a given tag name, in document order.
		// the special tag value "*" matches all tags.
		// so it can be used to get a list of all elements,
		// or all elements with a certain tag.
		// this method gets a list of elements satisfying more than one tag.
		// but it can only be used on a Document

		Pattern pattern = Pattern.compile("[ \\n\\r\\t]+");

		// first get a list of all elements
		NodeList list = doc.getElementsByTagName("*");

		// then extract the desired elements to a new list
		List newList = new ArrayList();
		Element nextElement;
		List tagList = Arrays.asList(tags);
		for (int i = 0; i < list.getLength(); i++) {

			nextElement = (Element)(list.item(i));
			String tag = nextElement.getTagName();
			if (tagList.contains(tag)) {
				// test if empty, i.e, if contains no words
				Matcher matcher = pattern.matcher(nextElement.getTextContent());
				String test = matcher.replaceAll("");
				// now after all whitespace has been removed check for remaining special characters
				boolean empty = true;
				for (int j = 0; j < test.length(); j++) {
					if (specialCharacters.indexOf(test.charAt(j)) == -1) {
						// found a non-special character
						empty = false;
						break;
					}
				}
				if (empty) {
					// the element is empty
					throw new Exception("empty element " + getXmlContent(nextElement));
				}
				newList.add(nextElement);
			}

		}

		final List finalList = newList;

		NodeList nodeList = new NodeList() {
			// return # of items in the list
			public int getLength() {
				return finalList.size();
			}
			// return the i-th item
			public Node item(int index) {
				return (index < finalList.size()) ? (Node)finalList.get(index) : null;
			}
		};

		return nodeList;

	}

	public static Node getNextRelevantSiblingElement(Node node, HashMap relevantNames) {

		// get nearest next sibling that
		// (1) is an Element, and
		// (2) has a name among those in relevantNames

		Node next = node.getNextSibling();
		while (next != null) {
			if (next.getNodeType() == Node.ELEMENT_NODE) {
				if (relevantNames.containsKey(next.getNodeName())) {
					return next;
				}
			}
			next = next.getNextSibling();
		}
		return null;

	}

	public static Node getPreviousRelevantSiblingElement(Node node, HashMap relevantNames) {

		// get nearest previous sibling that
		// (1) is an Element, and
		// (2) has a name among those in relevantNames

		Node prev = node.getPreviousSibling();
		while (prev != null) {
			if (prev.getNodeType() == Node.ELEMENT_NODE) {
				if (relevantNames.containsKey(prev.getNodeName())) {
					return prev;
				}
			}
			prev = prev.getPreviousSibling();
		}
		return null;

	}

	public static Node getRelevantAncestorElement(Node node, HashMap relevantNames) {

		// get the nearest ancestor that
		// (1) is an Element, and
		// (2) has a name among those in relevantNames

		Node parent = node.getParentNode();
		while (parent != null) {
			if (parent.getNodeType() == Node.ELEMENT_NODE) {
				if (relevantNames.containsKey(parent.getNodeName())) {
					return parent;
				}
			}
			parent = parent.getParentNode();
		}
		return null;

	}

	public static Node getRightMostDescendant(Node node) {

		Node temp = node.getLastChild();

		// 2006-10-04. gikk feil omkring her. ser suspekt ut. setter inn test
		if (temp == null) {
			// node is leaf node. no descendants at all
			return null;
		}
		// end 2006-10-04

		while (temp.getLastChild() != null) {
			temp = temp.getLastChild();
		}
		return temp;
	}

	public static Node getRelevantLastDescendantElement(Node node, HashMap relevantNames) {

		// get the last (rightmost) descendant that
		// (1) is an Element, and
		// (2) has a name among those in relevantNames

		Node current = getRightMostDescendant(node);

		if (current == null) {
			// node is leaf node. no descendants at all
			return null;
		}

		while (current != node) {
			if (current == current.getParentNode().getLastChild()) {
				// inspect current's parent
				Node parent = current.getParentNode();
				if (parent.getNodeType() == Node.ELEMENT_NODE) {
					if (relevantNames.containsKey(parent.getNodeName())) {
						return parent;
					}
				}
			}
			if (current.getPreviousSibling() != null) {
				Node sibling = current.getPreviousSibling();
				if (sibling.hasChildNodes()) {
					current = getRightMostDescendant(sibling);
				} else {
					current = sibling;
				}
				// inspect current
				if (current.getNodeType() == Node.ELEMENT_NODE) {
					if (relevantNames.containsKey(current.getNodeName())) {
						return current;
					}
				}
			} else {
				current = current.getParentNode();
			}
		}
		return null;

	}

	// ### not xml utility, but...

	// used to search in unaligned.elements[t]
	public static Element getElementByIdInDefaultListModel(DefaultListModel elements, String id) {
		for (int i=0; i<elements.size(); i++) {
			AElement aEl = (AElement)elements.get(i);
			Element el = (Element)aEl.element;
			String elId = el.getAttribute("id");
			if (elId.equals(id)) {
				return el;
			}
		}
		return null;
	}

	public static Node getElementByIdInNodeList(NodeList nodes, String id) {
		for (int i=0; i<nodes.getLength(); i++) {
			if (((Element)nodes.item(i)).getAttribute("id").equals(id)) {
				return nodes.item(i);
			}
		}
		return null;
	}

	public static int getIndexOfElementByIdInNodeList(NodeList nodes, String id) {
		for (int i=0; i<nodes.getLength(); i++) {
			if (((Element)nodes.item(i)).getAttribute("id").equals(id)) {
				return i;
			}
		}
		return -1;
	}

}