import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ArrayList;

import com.sun.xml.parser.Resolver;
import com.sun.xml.tree.XmlDocument;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/***
 * Simple class to read an XML file and create corresponding 
 * java classes from the elements.  Each element MUST have a
 * twin java class.  
 * This uses the sun ProjectX parser and the org.xml exceptions.
 * @author mitch fincher, February 2000, mitch[at]fincher[dot]org
 * You will still need a factory class with something like the following:
 *	try {
 *	    input = Resolver.createInputSource(new File(filename));
 *	    doc = XmlDocument.createXmlDocument(input, true);
 *	}
 *	catch(Exception e) {
 *	    e.printStackTrace(System.out);
 *	    System.out.println("Problem opening file \"" + filename +"\".\n<br>"+ e);
 *	    throw(e);
 *	}
 *
 *	MyTopObject myTopObject = new MyTopObject();
 *	myTopObject.load(doc.getDocumentElement());
 *	//myTopObject.test(); a good thing to add until dtd's are better
 *
 *
**/
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
public class XMLObject 
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
{
    protected Element thisElement;
    protected String value = "";
    protected ArrayList childrenArrayList = new ArrayList();
    protected HashMap attributeHashMap = new HashMap();
    protected XMLObject parentObject = null;
    protected static int level = 0; // used for prettyprinting the structure
    public static String packageString = null;

////////////////////////////////////////////////////////////////
public XMLObject()
////////////////////////////////////////////////////////////////
    {
    }
////////////////////////////////////////////////////////////////
public void load(Element thisElement) throws Exception
////////////////////////////////////////////////////////////////
    {
	this.thisElement = thisElement;
	loadAttributes();
	loadChildren();
    }

////////////////////////////////////////////////////////////////
protected void loadAttributes()
////////////////////////////////////////////////////////////////
    {
	NamedNodeMap namedNodeMap = thisElement.getAttributes();
	for(int i=0;i<namedNodeMap.getLength();i++) {
	    Node node = namedNodeMap.item(i);
	    setAttribute(node.getNodeName(),node.getNodeValue());
	}
	setAttribute("xmltype",thisElement.getTagName());
    }
////////////////////////////////////////////////////////////////
public void loadChildren() throws Exception
////////////////////////////////////////////////////////////////
    {
	NodeList nodeList = 
	    thisElement.getChildNodes();
	int size = nodeList.getLength();
	for(int i=0;i<size;i++) { // loop over all children
	    Node node = nodeList.item(i);
	    if(node instanceof Element) {
		String xmltype = ((Element)node).getTagName();
		xmltype = packageString+xmltype;
		XMLObject xmlobject = 
		    (XMLObject)(Class.forName(xmltype)).newInstance();
		xmlobject.load( (Element)node);
		xmlobject.setParent(this);
		childrenArrayList.add(xmlobject);
	    } else if(node instanceof org.w3c.dom.Text) {
		value = node.getNodeValue();
	    } else {
		System.out.println("unknown type of node in loadChildren: " + node.getClass().getName());
	    }
	}
    }
////////////////////////////////////////////////////////////////
public String getValue()
////////////////////////////////////////////////////////////////
    {
	return(value);
    }
/***
 * returns the first instance of a matching child
 * @param type xmlobject type, eg, Block, Question,...
 * @param attribute something like newpage
 * @param value the value of the attribute, like "yes"
 * for example getChild("Block","newpage","yes") would return the first
 * Block that starts a new page
**/
////////////////////////////////////////////////////////////////
public XMLObject getChild(String type, String attribute, String value)
////////////////////////////////////////////////////////////////
    {
	XMLObject xmlobject = null;
	for (java.util.Iterator e = getChildren(); e.hasNext() ;) {
	    xmlobject = (XMLObject)e.next();
	    if(xmlobject.getAttribute("xmltype").equals(type) && 
	       xmlobject.getAttribute(attribute).equals(value)) {
		return(xmlobject);
	    }
	}
	return null;
    }
/***
 * returns the first object of the requested type, otherwise throws error
 * @param type, the type of the child, eg, Text, ChoiceList
**/
////////////////////////////////////////////////////////////////
public XMLObject getChild(String type) throws Exception
////////////////////////////////////////////////////////////////
    {
	XMLObject xmlObject = null;
	for (java.util.Iterator e = getChildren(); e.hasNext() ;) {
	    xmlObject = (XMLObject)e.next();
	    if(xmlObject.getAttribute("xmltype").equals(type)) {
		return(xmlObject);
	    }
	}
	throw new Exception("child object of type \""+type+"\" not found.");
    }
////////////////////////////////////////////////////////////////
protected void setAttribute(String attribute, String value)
////////////////////////////////////////////////////////////////
    {
	attributeHashMap.put(attribute,value);
    }

////////////////////////////////////////////////////////////////
public String getAttribute(String attribute)
////////////////////////////////////////////////////////////////
    {
	return((String)getAttribute(attribute , false));
    }
////////////////////////////////////////////////////////////////
public String getAttribute(String attribute, boolean recurseUp)
////////////////////////////////////////////////////////////////
    {
	String value = (String)attributeHashMap.get(attribute);
	if(value != null || recurseUp == false) {
	    return value;
	}
	if(value == null && recurseUp) {
	    if(getParent() != null) {
		return(getParent().getAttribute(attribute,true));
	    }
	}
	return(null);
    }
/***
 * returns the number of children
 * @param 
**/
////////////////////////////////////////////////////////////////
public int size()
////////////////////////////////////////////////////////////////
    {
	return(childrenArrayList.size());
    }
/***
 * returns the number of children of a particular type
 * @param 
**/
////////////////////////////////////////////////////////////////
public int size(String xmltype)
////////////////////////////////////////////////////////////////
{
    int count = 0;
    XMLObject xmlobject = null;
    for (java.util.Iterator e = getChildren(); e.hasNext() ;) {
	xmlobject = (XMLObject)e.next();
	if(xmlobject.getAttribute("xmltype").equals(xmltype)) {
	    count++;
	}
    }
    return(count);
}
////////////////////////////////////////////////////////////////
public XMLObject getParent()
////////////////////////////////////////////////////////////////
    {
	return(parentObject);
    }
////////////////////////////////////////////////////////////////
protected void setParent(XMLObject parent)
////////////////////////////////////////////////////////////////
    {
	parentObject = parent;
    }
////////////////////////////////////////////////////////////////
public String toString()
////////////////////////////////////////////////////////////////
    {
	StringBuffer desc = new StringBuffer("");
	StringBuffer spaces = new StringBuffer("");
	for(int i=0;i<level;i++){ spaces.append("   "); }

	desc.append("\n"+spaces+"<"+getAttribute("xmltype") + " name=\""+getAttribute("name")+"\">"); 
	//desc.append("<"+this.getClass().getName()+">\n"); 
	desc.append(spaces+value);
	//desc.append(""+spaces+   "attributeHashMap="+attributeHashMap);
	++level;
	for(int i=0;i<childrenArrayList.size();i++)
	    {
		desc.append(""+childrenArrayList.get(i));
	    }
	--level;
	if(value.length() < 1) {
	    desc.append("\n"+spaces+"</"+getAttribute("xmltype") + ">"); 
	} else {
	    desc.append("</"+getAttribute("xmltype") + ">"); 
	}
	return new String(desc);
    }
////////////////////////////////////////////////////////////////
public ArrayList getChildren(String xmltype)
////////////////////////////////////////////////////////////////
    {
	ArrayList arrayList = new ArrayList();
	XMLObject xmlobject = null;
	for (java.util.Iterator e = getChildren(); e.hasNext() ;) {
	    xmlobject = (XMLObject)e.next();
	    if(xmlobject.getAttribute("xmltype").equals(xmltype)) {
		arrayList.add(xmlobject);
	    }
	}
	return arrayList;
    }
////////////////////////////////////////////////////////////////
public java.util.Iterator getChildren()
////////////////////////////////////////////////////////////////
    {
	return(childrenArrayList.iterator());
    }
/***
 * This returns whether an attribute has a particular value.
 * This function exists because sometimes an attribute might not exist
 * and each function would then have to test to see if the attribute exists,
 * and then test it. So to minimize code we put it here.
 * @param attribute attribute to test, eg, "name"
 * @param text string to test

**/
////////////////////////////////////////////////////////////////
public boolean attributeEquals(String attribute, String text)
////////////////////////////////////////////////////////////////
    {
	boolean test = false;
	if(getAttribute(attribute) != null) {
	    test = getAttribute(attribute).equals(text);
	}
	return(test);
    }

/***
 * returns an integer value from an attribute.  If a problem occurs
 * we return a zero.
 * @param attribute the attribute that should be an integer
**/
////////////////////////////////////////////////////////////////
public int getAttributeInt(String attribute)
////////////////////////////////////////////////////////////////
    {
	int value = 0;
	try {
	    value  = Integer.parseInt(getAttribute(attribute));
	} catch (Exception e){ 
	    value = 0;
	}
	return(value);
    }

////////////////////////////////////////////////////////////////
public boolean isValid(int userID) throws Exception
////////////////////////////////////////////////////////////////
    {
	XMLObject xmlobject = null;
	for (java.util.Iterator e = getChildren(); e.hasNext() ;) {
	    xmlobject = (XMLObject)e.next();
	    if(xmlobject.isValid(userID) == false)
		{return(false);}
	}
	return true;
    }
////////////////////////////////////////////////////////////////
public String toHTML(String language)
////////////////////////////////////////////////////////////////
    {
	StringBuffer desc = new StringBuffer("");
	desc.append("\n "+getAttribute("xmltype")+"name=\""+getAttribute("name")+"\""); 
	
	for(int i=0;i<childrenArrayList.size();i++) {
	    desc.append(((XMLObject)childrenArrayList.get(i)).toHTML(language));
	}
	
	return new String(desc);
    }

} // XMLObject
