Currently showing src/net/jtank/io/WXMLSection.java
package net.jtank.io;

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.io.*;
import java.net.*;

/**
 * The primary data storage and retrieveal unit for the WXML structure are
 * WXMLSections.
 *
 * WXMLSections are the data storage classes that are the heart of the WXML
 * tools.  Reading and writing the files aside, most operations are performed on
 * these classes.  Each WXMLSection contains a List of sub sections,
 * which are WXMLSection objects, and a list of attributes which are
 * WXMLAttribute objects.  There are various methods for adding, removing,
 * retrieving and changing these elemnts.  The following is one such example
 * of how to do this.
 *
 * Example:<br>
 * <pre>
 * ---exampleWXML.wxml---
 * <example>
 *    <title>foo</title>
 *    <level name="1">
 *       <points>10</points>
 *    </level>
 *    <level name="2">
 *       <points>10</points>
 *    </level>
 * </example>
 * ---EOF---
 * </pre>
 *
 * You could load the file as such:<br>
 * <code>
 * String directoryBase = (new File("")).getAbsolutePath() + "/";<br>
 * try {<br>
 *      WXML example = WXML.loadFile(directoryBase + "exampleWXML.wxml", null);<br>
 *  } catch (IOException e) {<br>
 *      System.out.println("Error reading file.")'<br>
 *  }<br>
 * </code>
 * <br>
 * To access the main WXMLSection object you will need to retrieve it from the
 * WXML object, like so:<br>
 * <code>
 * WXMLSection main = example.getSubSection("example");<br>
 * </code>
 *
 * Now that you have that, printing out the title say is quite easy.
 * Notice that title is an attribute as it is a tag with no sub-section.
 * The "name=1" also represents an attribute for the level in the above example
 * but this time, it is an in-line attribute.<br>
 * <code>
 * System.out.println(main.getAttributeValue("title"));<br>
 * </code>
 * <br>
 * To get an array of all the levels is also easy:<br>
 * <code>
 * WXMLSection [] levels = main.getSubSectionGroup("level")<br>
 * levels[0].getAttributeValue("name"); //print out the name of the first level<br>
 * </code>
 * <br>
 * Hopefully with these examples, you can see the power of the WXML data storage
 * tools.<br>
 *
 * @author William Denniss
 * @version 1.1 - 3 April 2002
 *
 * Version History:
 * (As per WXML.java)
 *
 * The tanksoftware package, it's binaries and source code are all licensed
 * under the GNU GPL.  See the accompanying file COPYING for details.
 *
 * William Denniss has asserted his right under the Australian Copyright,
 * Designs and Patents Act 1988 to be identified as the author of this work.
 */

public class WXMLSection {

	private WXMLSection parent = null;
	private List subsections = new ArrayList();
	private List attributes = new ArrayList();

	//A list contianing all tags (that is sections and non-inline attributes) in the order they were found.
	private List inOrderSectionTags = new ArrayList();
	private String name;
	public String text;

	public List getInOrderSectionTags () {
		return inOrderSectionTags;
	}

	public void addInOrderSectionTag (WXMLSection toAdd) {
		inOrderSectionTags.add(toAdd);
	}

    /**
     * Creates a WXML Section (or tag) with no subsections, nor attributes in it.
     *@param name The name of the section.  This will also be the name in the tag eg <name1> when this object is
     * printed.
     *@param parent The WXMLSection that contains this one.  This can be null if this is the root section.
     */
        public WXMLSection (String name, WXMLSection parent) {
	        this.name = name;
	        this.parent = parent;
	}

    /**
     * Adds an attribute to this WXMLSection.
     *@param toAdd The attribute to add to this WXMLSection.
     */
	public void addAttribute (WXMLAttribute toAdd) {
		attributes.add(toAdd);
	}

    /**
     * Removes an attribute from this WXMLSection.
     *@param toRemove The attribute to be removed.
     */
    public void removeAttribute (WXMLAttribute toRemove) {
		attributes.remove(toRemove);
	}

	public void purgeAll () {
		subsections = new ArrayList();
		attributes = new ArrayList();
	}

    /**
     * Adds a List of attributes to this WXMLSection.  Note: the elements of the List need to be WXMLAttribute objects.
     * @param toAdd The List of WXMLAttributes to add to this WXMLSection.
     */
    //todo, add checking
	public void addAttribute (List toAdd) {
		if (toAdd == null)
			return;
		attributes.addAll(toAdd);
	}


	/**
	 * Adds an WXMLSection as a subsection to this WXMLSection object.
	 *
	 * @param toAdd The section to add.
	 */
	public void addSubSection (WXMLSection toAdd) {

		//as the last element is normally the empty element, ignore it.

		if (toAdd == null)
			return;

		Object lastSubSection = null; //added
		if (subsections.size() > 0) { //added
			lastSubSection = subsections.get(subsections.size()-1);//added
			subsections.remove(lastSubSection);//added
		}

		subsections.add(toAdd);

		if (lastSubSection != null) {//added
			subsections.add(lastSubSection);//added
		} else {

			subsections.add(new WXMLSection("-=attribute=-", this));
		}
	}

	//use only when ititialiseing, see the above method for after it is initialised
	public void addSubSection (List toAdd) {
		subsections.addAll(toAdd);
	}


	public void removeSubSection (WXMLSection toRemove) {
		subsections.remove(toRemove);
	}

	/**
	 * Returns the name of this WXMLSection, normally it's tag name.
	 * @return The name of the tag that this object represents.
	 */
	public String getName () {
		return name;
	}

	/**
	 * Searches though the subsections for the one whose name equals the
	 * parsed String.
	 *
	 * @param toFind The name of the sub section that will be searched for.
	 * @return The WXMLSection that is found, or null if none is found.
	 */
	public WXMLSection getSubSection (String toFind) {

		for (int i = 0; i < subsections.size(); i++) {
			WXMLSection current = (WXMLSection) subsections.get(i);
			if (current.getName().equals(toFind))
				return current;
		}

		return null;
	}

	/**
	 * This should be used instead of getSubSection(String toFind) whenever
	 * there are multiple occurances of the same tag.  It returns an array
	 * containing every sub section that shares the same name as the one you
	 * are searching for, as opposed to just the first instance.
	 * @param groupName The name for the group of sub-sections.
	 * @return An array containing all sub sections whose name is equal to
	 * the one that is being searched for.
	 */
	public WXMLSection [] getSubSectionGroup (String groupName) {

		List groups = new LinkedList();

		for (int i = 0; i < subsections.size(); i++) {
			WXMLSection current = (WXMLSection) subsections.get(i);
			if (current.getName().equals(groupName))
				groups.add(current);
		}


		Object [] objects = groups.toArray();
		WXMLSection [] toReturn = new WXMLSection[objects.length];
		for (int i = 0; i < objects.length; i++) {
			toReturn[i] = (WXMLSection) objects[i];
		}

		return toReturn;

	}

	/**
	 * Returns the value of the given attribute key.
	 * In the case of an inline attribute eg. foo="bar", bar would be
	 * returned.  In the case of a text-only tag eg. <foo>bar</foo> bar
	 * would be returned.
	 *
	 * @param key The key whose value will be returned.
	 * @return The value of the given attribute key or null if it doesn't
	 * exist.
	 */
	public String getAttributeValue (String key) {

		for (int i = 0; i < attributes.size(); i++) {
			WXMLAttribute current = (WXMLAttribute) attributes.get(i);
			if (current.getKey().equals(key))
				return current.getvalue();
		}

		return null;
	}

	/**
	 * Changes the value of the given attribute key, or creates a new
	 * attribute with the given key/value pair should it not already exist.
	 * refer to the getAttributeValue(String) method above for more details
	 * on how attributes work.
	 *
	 * @param key The key of the attribute that will be changed/added.
	 * @param value The value that the key will be changed to.
	 */
	public void setAttributeValue (String key, String value) {

		for (int i = 0; i < attributes.size(); i++) {
			WXMLAttribute current = (WXMLAttribute) attributes.get(i);
			if (current.getKey().equals(key)) {
				current.setValue(value);
				return;
			}
		}

		attributes.add(new WXMLAttribute(key, value, true));
		return;
	}

	/**
	 * Same as getAttributeValue(String), but instead of returning null
	 * should the attribute not be found, it returns the given defualt
	 * value.
	 *
	 * @param key The name of the attribute you want to retrieve.
	 * @param defaultValue The default value if that attribute does not
	 * exist.
	 * @return The value of the attribute if found, or the default value if
	 * it isn't.
	 */
	public String getAttributeValueWDefault (String key, String defaultValue) {
		String toReturn = getAttributeValue(key);
		if (toReturn == null)
			return defaultValue;
		else
			return toReturn;
	}

	/**
	 * Same as getAttributeValueWDefault(String, String), but with an int
	 * being returned, and passed as the default.
	 *
	 * @param key The name of the attribute you want to retrieve.
	 * @param defaultValue The default value that is returned if the
	 * attribute is not found.
	 * @return The attribute value, or if it isn't found, the default value.
	 */
	public int getAttributeValueWDefaultInt (String key, int defaultValue) {
		String toReturn = getAttributeValue(key);
		if (toReturn == null)
			return defaultValue;
		else
			return Integer.parseInt(toReturn);
	}

	/**
	 * Converts subsections with no subsections of their own to Attributes.
	 * Eg <title>example</title> would become title=example.
	 */
	public void resolveSubSectionAttributes () {

		//before the tags are resolved, preserve the order
		inOrderSectionTags = this.subsections;

		List newsections = new ArrayList ();

		for (int i = 0; i < subsections.size(); i++) {

			WXMLSection currentSection = (WXMLSection) subsections.get(i);

			try {
				WXMLSection firstSubSectionOfCurrent = (WXMLSection) currentSection.subsections.get(0);

				// A section is an attribute if it has no attributes, and only contains the text -=attribute=-
				// This is because any section containing only 1 full line attribute will after that one is resolved, be left with the text -=attribute=- in position 0
				// this is because ALL TAGS have their last subsections as the empty subsection.
				if ( firstSubSectionOfCurrent.name.equals("-=attribute=-") && currentSection.attributes.size() == 0) {
					//System.out.println("attributefound");
					String atext = WXML.replaceChar(firstSubSectionOfCurrent.text,'½',';');
					atext = WXML.replaceChar(atext,'½','>');
					attributes.add( new WXMLAttribute (currentSection.getName(), atext, false));

				} else {
					newsections.add(currentSection);
				}
			} catch (IndexOutOfBoundsException e) {
					newsections.add(currentSection);
			}

		}

		subsections = newsections;

	}

	/**
	 * Returns the String representation of this WXMLSection, starting at indent 0. For more
	 * details, see the print (int indent) method.
	 *
	 * @return The String representation of this WXMLSection.
	 */
	public String toString () {
		return print (0);
	}

	/**
	 * Returns the String representation of this WXMLSection.  Note: this is
	 * not a WXML format String, and should only be used for debugging
	 * purpouses.
	 *
	 * @param indent The number of tab characters in this section will
	 * start.
	 * @return The String representation of this WXMLSection.
	 */
	public String print (int indent) {
		String toreturn = "";

		for (int i = 0; i < indent; i++)
			toreturn += "\t";

		String attributeString = " attributes:";
		for (int i = 0; i < attributes.size(); i++)
			attributeString += ((WXMLAttribute) attributes.get(i)).toString();


		toreturn+= "<" + name + ">" + attributeString + "\n";


		// As all subsections end with the empty subsection, no need to print it
		for (int i = 0; i < subsections.size()-1; i++)
			toreturn += ((WXMLSection) subsections.get(i)).print(indent+1);

		return toreturn;
	}


	/**
	 * Returns the WXML String representation of this WXMLSection, starting at indent 0. For more
	 * details, see the printOrderedWXMLString (int indent) method.
	 *
	 * @return The WXML String representation of this WXMLSection.
	 */

	public String toOrderedWXMLString () {
		return printOrderedWXMLString(0);
	}

	/**
	 * Returns the orded WXML String representation of this WXMLSection.
	 * All attributes and sub-sections are pritned out, in the order that
	 * they were read in.  Note: This method is still experimental, and not
	 * garenteed to work.
	 *
	 * @param indent The number of tab characters in this section will
	 * start.
	 * @return The Ordered WXML String representation of this WXMLSection.
	 */
	public String printOrderedWXMLString (int indent) {
		//if (name != null) {


		String toreturn = "";


		String indentString = "";
		for (int i = 0; i < indent; i++)
			indentString += "\t";

		toreturn += indentString;

		String inLineAttributeString = "";
		for (int i = 0; i < attributes.size(); i++) {
			WXMLAttribute current = (WXMLAttribute) attributes.get(i);
			if (current.isInLineAttribute())
				inLineAttributeString += current.toString() + " ";
		}

		//start tag;
		toreturn += "<" + this.name + " " + inLineAttributeString + ">\n";

		for (int i = 0; i < this.inOrderSectionTags.size(); i++) {
			WXMLSection current = (WXMLSection) inOrderSectionTags.get(i);

			if (current.name.equals("-=attribute=-")) {
				//System.out.println("---88&&&&&&&&&&******");
				String atext = current.text;
				atext = WXML.replaceCharDouble(atext,';','\',';');
				//System.out.println(atext);
				toreturn += indentString + atext + "\n";
			} else {
				toreturn += indentString + current.printOrderedWXMLString(indent+1);
			}

		}


		toreturn += indentString + "</" + this.name + ">\n";
		return toreturn;
	}

	/**
	 * Returns the WXML String representation of this WXMLSection, starting at indent 0. For more
	 * details, see the printWXMLString (int indent) method.
	 *
	 * @return The WXML String representation of this WXMLSection.
	 */
	public String toWXMLString () {
		return printWXMLString(0);
	}

	/**
	 * Returns the WXML String representation of this WXMLSection.  This consists
	 * of the tag name, its attributes and all of its sub-sections. All
	 * inline attributes are output as inline attributes, and text only tag
	 * ones are printed as text only tags.  However, all attributes are printed
	 * before the sub0section.  If you do not want this
	 * see the printOrderedWXMLString() method.
	 *
	 * @param indent The number of tab characters in this section will
	 * start.
	 * @return The WXML String representation of this WXMLSection.
	 */
	 public String printWXMLString (int indent) {

		String toreturn = "";

		if (name != null) {

			String indentString = "";
			for (int i = 0; i < indent; i++)
				indentString += "\t";

			toreturn += indentString;

			String inLineAttributeString = "";
			String subSectionAttributeString = "";
			for (int i = 0; i < attributes.size(); i++) {
				WXMLAttribute current = (WXMLAttribute) attributes.get(i);
				if (current.isInLineAttribute()) {
					inLineAttributeString += current.toString();
				} else {
					//System.out.println("---888******************");
					subSectionAttributeString += "<" + current.key + ">" + WXML.replaceCharDouble(current.value,';','\',';') + "</" + current.key + ">\n";
				}
			}


			toreturn+= "<" + name + " " + inLineAttributeString + ">" + "\n";
			toreturn+= subSectionAttributeString;

			// As all subsections end with the empty subsection, no need to print it
			for (int i = 0; i < subsections.size()-1; i++)
				toreturn += ((WXMLSection) subsections.get(i)).printWXMLString(indent+1);

			toreturn += indentString + "</" + name + ">\n";

			return toreturn;
		} else {
			for (int i = 0; i < subsections.size()-1; i++)
				toreturn += ((WXMLSection) subsections.get(i)).printWXMLString(indent);

			return toreturn;


		}
	}

	/**
	 * A WXML Key-Value pair (eg "width=10") that can either be embedded in a tag, or have one of it's own.
 	 */
	public static class WXMLAttribute {

		private String key;
		private String value;
		private boolean inLineAttribute;

		/**
		 * Creates a new WXMLAttribute.
		 *
		 * @param key The name, or key of this attribute.
		 * @param value The value associated with that name.
         	 * @param inLineAttribute Whether or not this attribute is in the tag eg <tag size=1>
		 */
		public WXMLAttribute (String key, String value, boolean inLineAttribute) {
			this.key = key;
			this.value = value;
			this.inLineAttribute = inLineAttribute;
//			this.
//			this.name = "!@Attribute@!";
		}

		/**
		 * Changes the current key to the new specified new one.
		 *
		 * @param key The new name, or key of this attribute.
		 */
		public void setKey (String key) {
			this.key = key;
		}

		/**
		 * Changes the current value to the new specified new one.
		 *
		 * @param value The new value of this attribute.
		 *
		 */
		public void setValue (String value) {
			this.value = value;
		}


		/**
		 * Gets the key of this WXMLAttribute
		 *
		 *@return The key of this WXMLattribute
		 */
		public String getKey () {
			return key;
		}

		/**
		 * Gets the value of this WXMLAttribute
		 *
		 *@return The value of this WXMLattribute
		 */
		public String getvalue () {
			return value;
		}

		/**
		 * Returns the String representation of this object.
		 *
		 *@return The String representation of this object.
		 */
		public String toString () {
			String avalue = WXML.replaceCharDouble(value,'"','\','"');
			avalue = WXML.replaceCharDouble(avalue,'>','\','>');
			return key + "=\"" + avalue + "\" ";

		}

		/**
		 * Returns true if this attribute is an inline attribute,
		 * false if it is not.
		 *
		 * @return true if this attribute is an inline attribute,
		 * false if it is not.
		 */
		public boolean isInLineAttribute () {
			return this.inLineAttribute;
		}

	}

}
     

Total 618 Lines of Code.
Source code formatted using showsrc by William Denniss