// Stan Bak
// XML Node class
// 5-8-04

// This class does all the XML Loading and saving
// the idea is that each XML Node may have a value and many children
// the children are stored as XML nodes in the vector children
// the value is stored as a String in the variable value
// it doesn't read arguements, becuase I never use them

package editor.xml;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Vector;

import javax.swing.JOptionPane;

public class XMLNode
{
	public Vector children = null;

	public String name;

	public String value = "";

	public XMLNode()
	{
	}

	public XMLNode(String myName, BufferedReader in) throws IOException
	{
		String terminator = "/" + myName;
		name = myName;

		int c;

		while (in.ready())
		{
			c = in.read();

			if (c == '<')
			{
				String tag = readTag(in);

				if (tag == null)
					continue;

				if (tag.equals(terminator))
					return;

				XMLNode x = XMLNode.getNode(tag, in);

				if (x != null)
				{
					if (children == null)
						children = new Vector();

					children.add(x);
				}
			}

			if (c == ' ' || c == 10 || c == 13 || c == '\t')
				continue;

			value += (char) c;
		}
	}

	public XMLNode addChild(String name)
	{
		if (name != null)
		{
			if (children == null)
				children = new Vector();

			XMLNode x = new XMLNode();
			x.name = name;

			children.add(x);
			return (XMLNode) children.get(children.size() - 1);
		}

		return null;
	}

	public void addChild(String name, String value)
	{
		if (name != null)
		{
			if (children == null)
				children = new Vector();

			XMLNode x = new XMLNode();
			x.name = name;
			x.value = value;

			children.add(x);
		}
	}

	public XMLNode getChild(String name)
	{
		if (children == null)
			return null;

		for (int x = 0; x < children.size(); ++x)
		{
			XMLNode xml = (XMLNode) children.get(x);

			if (xml.name.equals(name))
				return xml;
		}

		return null;
	}

	public int getIntValue()
	{
		return Integer.parseInt(value);
	}

	public double getDoubleValue()
	{
		return Double.parseDouble(value);
	}

	public boolean getBooleanValue()
	{
		return value.equals("true");
	}

	public static String readTag(BufferedReader in) throws IOException
	{
		String fullTag = "";
		String name = "";
		int c;
		boolean spaceReached = false;
		String lastWord = "";

		while (in.ready())
		{
			c = in.read();

			if (c == '>')
			{
				if (name.length() > 1 && name.charAt(0) == '?'
						&& lastWord.charAt(lastWord.length() - 1) == '?')
				{
					return null; // xml header
				}

				if (name.length() < 3 || !name.substring(0, 3).equals("!--")) // it's
																				// not
																				// a
																				// comment
				{
					if (lastWord.length() >= 1
							&& lastWord.charAt(lastWord.length() - 1) == '/')
						return null; // empty tag

					break;
				}

				if (name.length() >= 3
						&& name.substring(0, 3).equals("!--")
						&& fullTag.length() >= 5
						&& lastWord.length() >= 2
						&& lastWord.substring(lastWord.length() - 2).equals(
								"--")) // end of a comment
					return null;
			}

			if (c == ' ')
			{
				spaceReached = true;
				lastWord = "";
			}
			else
			{
				lastWord += (char) c;
			}

			if (!spaceReached)
				name += (char) c;

			fullTag += (char) c;
		}

		if (name.length() == 0)
			return null;

		return name;
	}

	public static XMLNode readDocument(String filename)
	{
		XMLNode x = null;

		try
		{
			FileReader fr = new FileReader(filename);
			BufferedReader in = new BufferedReader(fr);

			x = new XMLNode("XML Document", in);

			in.close();
		}
		catch (IOException e)
		{
			JOptionPane.showMessageDialog(null, e.toString());
		}

		return x;
	}

	/**
	 * get an XMLNode from a file path, returning null in case of error
	 * 
	 * @param filename
	 *            the path to load from
	 * @return the XMLNode represented by the file at this location
	 */
	public static XMLNode readDocumentNoError(String filename)
	{
		XMLNode x = null;

		try
		{
			FileReader fr = new FileReader(filename);
			BufferedReader in = new BufferedReader(fr);

			x = new XMLNode("XML Document", in);

			in.close();
		}
		catch (IOException e)
		{
			return null;
		}

		return x;
	}

	public static void saveDocument(XMLNode document, String filename)
	{
		try
		{
			FileWriter fw = new FileWriter(filename);
			BufferedWriter out = new BufferedWriter(fw);

			out.write("<?xml version=\'1.0\' encoding=\'utf-8\'?>"); // header
			endLine(out);
			out.write("<!-- Generated by Stan Bak's XML Generator v6-8-04 -->"); // header
			endLine(out);

			saveNode(document, out, 0);

			out.close();
		}
		catch (IOException e)
		{
			JOptionPane.showMessageDialog(null, e.toString());
		}
	}

	public static void saveNode(XMLNode x, BufferedWriter out, int tabs)
			throws IOException
	{
		if (x.children == null)
		{
			for (int c = 0; c < tabs; ++c)
				out.write("\t");

			if (x.value != null)
				out.write(x.value);
			else
				out.write("null");

			endLine(out);
		}
		else
		{
			for (int i = 0; i < x.children.size(); ++i)
			{
				XMLNode ch = (XMLNode) x.children.get(i);

				for (int c = 0; c < tabs; ++c)
					out.write("\t");

				if (ch.name != null)
					out.write("<" + ch.name + ">");
				else
					out.write("<null>");

				endLine(out);

				saveNode(ch, out, tabs + 1);

				for (int c = 0; c < tabs; ++c)
					out.write("\t");

				if (ch.name != null)
					out.write("</" + ch.name + ">");
				else
					out.write("</null>");

				endLine(out);
				endLine(out);
			}
		}
	}

	public static void endLine(BufferedWriter out) throws IOException
	{ // ascii endline... 13,10
		out.write(13);
		out.write(10);
	}

	// PRECONDITION: tag != null
	public static XMLNode getNode(String tag, BufferedReader in)
			throws IOException
	{
		XMLNode x = new XMLNode(tag, in);
		return x;
	}

	public void print(int tabs)
	{
		if (children == null)
		{
			for (int c = 0; c < tabs; ++c)
				System.out.print("\t");

			System.out.println("value of " + name + " is \"" + value + "\"");
		}
		else
		{
			for (int i = 0; i < children.size(); ++i)
			{
				XMLNode ch = (XMLNode) children.get(i);

				for (int c = 0; c < tabs; ++c)
					System.out.print("\t");

				System.out.println(name + " has a child called " + ch.name);
				ch.print(tabs + 1);
			}
		}
	}

	public void print()
	{
		print(0);
	}
}