/*
 * Created on 15/04/2003
 *
 */
package com.quantum.sql.metadata;

import java.util.Vector;

import com.quantum.util.StringMatrix;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;


/**
 * @author panic
 * Handles interface between an ObjectMetaData and XML storage.
 * It can write an existing metadata object to XML and generate
 * a new ObjectMetaData from existing XML.
 */
public class MetaDataXMLInterface {

	/**
	 * Exports an ObjectMetaData to an XML document, under a root Element
	 * @param metadata The ObjectMetaData to export
	 * @param doc A valid XML document to receive the export
	 * @param root The Element under which all the XML data will be written
	 */
	public static void metaDataToXML(ObjectMetaData metadata, Document doc, Element root) {
		StringMatrix columns = metadata.getColumns();
		if (columns != null)
			stringMatrixToXML(columns, doc, root, "COLUMN"); //$NON-NLS-1$
		StringMatrix primaryKeys = metadata.getPrimaryKeys();
		if (primaryKeys != null)
			stringMatrixToXML(primaryKeys, doc, root, "PRIMARY_KEY_ITEM"); //$NON-NLS-1$
		StringMatrix foreignKeys = metadata.getForeignKeys();
		if (foreignKeys != null)
			stringMatrixToXML(foreignKeys, doc, root, "FOREIGN_KEY_ITEM"); //$NON-NLS-1$
		StringMatrix indexInfo = metadata.getIndexInfo();
		if (indexInfo != null)
			stringMatrixToXML(indexInfo, doc, root, "INDEX_ITEM"); //$NON-NLS-1$
	}

	/**
	 * Imports an  XML element to an ObjectMetaData. The format has to be the same as extracted by
	 * the MetaDataToXML function.
	 * @param metadata The ObjectMetaData to fill up. Usually empty.
	 * @param root The Element having all the XML data
	 */
	public static void xmlToMetaData(ObjectMetaData metadata, Element root) {
		StringMatrix columns = new StringMatrix();
		MetaDataXMLInterface.xmlToStringMatrix(columns, root, "COLUMN"); //$NON-NLS-1$
		metadata.setColumns(columns);
		StringMatrix primaryKeys = new StringMatrix();
		MetaDataXMLInterface.xmlToStringMatrix(primaryKeys, root, "PRIMARY_KEY_ITEM"); //$NON-NLS-1$
		metadata.setPrimaryKeys(primaryKeys);
		StringMatrix foreignKeys = new StringMatrix();
		MetaDataXMLInterface.xmlToStringMatrix(foreignKeys, root, "FOREIGN_KEY_ITEM"); //$NON-NLS-1$
		metadata.setForeignKeys(foreignKeys);
		StringMatrix indexInfo = new StringMatrix();
		MetaDataXMLInterface.xmlToStringMatrix(indexInfo, root, "INDEX_ITEM"); //$NON-NLS-1$
		metadata.setIndexInfo(indexInfo);
	}

	/**
	 * Adds a StringMatrix to an XML document
	 * @param matrix The StringMatrix to add
	 * @param doc The XmlDocument to which it will be added
	 * @param root An element of the previous document under which the info will be added
	 * @param sub A key under which each row of the StringMatrix will be added
	 */
	public static void stringMatrixToXML(StringMatrix matrix, Document doc, Element root, String sub) {
		for (int i = 0; i < matrix.size(); i++) {
			Element localRoot = (Element) root.appendChild(doc.createElement(sub));
			for (int j = 0; j < matrix.getNumColumns(); j++) {
				String key = matrix.getHeaderColumn(j);
				Element tableName = (Element) localRoot.appendChild(doc.createElement(key));
				String value = matrix.get(key, i);
				if (value != null)
					tableName.appendChild(doc.createTextNode(value));
			}
		}
	}

	/**
	* Fills a StringMatrix with the data from XML, usually extracted with the StringMatrixToXML function
	* @param matrix The matrix to fill up, usually empty.
	* @param root The Element with all the data in XML DOM
	* @param sub The String to select the Nodes which interest us. Only the selected nodes will be added
	* to the StringMatrix.
	*/
	private static void xmlToStringMatrix(StringMatrix matrix, Element root, String sub) {
		NodeList columns = root.getElementsByTagName(sub);
		for (int i = 0; i < columns.getLength(); i++) {
			Node column = columns.item(i);
			NodeList columnList = column.getChildNodes();
			for (int j = 0; j < columnList.getLength(); j++) {
				Node node = columnList.item(j);
				String header = node.getNodeName();
				if (header.equals("#text")) //$NON-NLS-1$
					continue;
				String value = null;
				if (node != null && node.hasChildNodes()) {
					Node valueNode = node.getFirstChild();
					if (valueNode instanceof Text) {
						value = valueNode.getNodeValue();
					}
				}
				if (!matrix.contains(header))
					matrix.addHeader(header);
				matrix.addAt(header, value, i);
			}
		}
	}
	/**
	 * Creates a new Element with a text value
	 * @param root
	 * @param key
	 * @param value
	 * @return
	 */
	public static Element createElementText(Element root, String key, String value){
		//	get the XmlDocument for use as a factory
		Document doc = root.getOwnerDocument();
		Element newElement = doc.createElement(key);
	
		root.appendChild(newElement);
		if (value != null && value.length() > 0)
		{
		   Text valueText = doc.createTextNode(value);
		   newElement.appendChild(valueText);
		}
		return newElement;
	}
      
	public static String getElementText(Element root, String key){
		return getElementText(root, key, "");
	}
	/**
	 * gets the text value from an element or a child of it
	 * @param root element root
	 * @param key key to search
	 * @param defValue default string value if not found
	 * @return
	 */
	public static String getElementText(Element root, String key, String defValue){
	//	get the XmlDocument for use as a factory
		String value = defValue;
		if (root.getNodeName().equals(key)){
			value = extractText(root, defValue);
		} else {
			NodeList children = root.getElementsByTagName(key);
			if (children.getLength() > 0) {
				Element column = (Element) children.item(0);
				value = extractText(column, defValue);
			}
		}
		return value;
	}

	public static String extractText(Element node, String defValue){
		String value = defValue;
		if (node != null && node.hasChildNodes()) {
			Node valueNode = node.getFirstChild();
			if (valueNode instanceof Text) {
				value = valueNode.getNodeValue();
			}
		}
		return value;
	}
	
	/**
	 * Gets a Vector with the String values of the keys
	 *  that are children of 'root' and that match 'key'
	 * @param root
	 * @param key
	 * @return
	 */
	public static Vector getVectorText(Element root, String key) {
		Vector result = new Vector();
		NodeList children = root.getElementsByTagName(key);
		for (int i = 0; i < children.getLength(); i++) {
			Element column = (Element) children.item(i);
			result.add(extractText(column, ""));
		}
		return result;
	}
}