/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Stuart E. Middleton
//      Created date:           2004/04/30
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.xmlobject;

import java.lang.*;
import java.util.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jdom.*;
import org.jdom.input.*;
import org.jdom.filter.*;
import org.jdom.output.*;
import org.apache.axis.utils.*;
import org.xml.sax.*;
import org.w3c.dom.*;


public class XMLObject extends Object implements Serializable {

	// member variables
	private org.jdom.Document mdocumentXML;

	// logger variable
	private Logger mlogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.xmlobject.XMLObject");
	private final static String mstaticTempPrefix = "TMP";
	private final static String mstaticTempSuffix = ".TMP";

	/**
	 * constructor
	 */
	public XMLObject()
	{
		super();

		// init
		mdocumentXML = null;

		// all done
        return;
	}

	/**
	 * Make an empty XML doc
	 */
	public void init() throws Exception
	{
		org.jdom.Element elementRoot;

		try
		{
			// make XML document using JDOM
			mdocumentXML = new org.jdom.Document();
			elementRoot = new org.jdom.Element( "root" );
			elementRoot.setText( "Default root node" );
			mdocumentXML.setRootElement( elementRoot );

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during init1",ex);
			throw new Exception( "Failed to init XMLObject" );
		}
	}

	/**
	 * Read XML text from file. If null is passed an empty XML doc will be created
	 * @param fileIn Input file from which to read XML
	 */
	public void init( File fileIn ) throws Exception
	{
		SAXBuilder builder;
		org.jdom.Element elementRoot;

		try
		{
			// make XML document using JDOM
			if( fileIn == null ) {
				mdocumentXML = new org.jdom.Document();
				elementRoot = new org.jdom.Element( "root" );
				elementRoot.setText( "Default root node" );
				mdocumentXML.setRootElement( elementRoot );
			}
			else {
				// make XML parser
				builder = new SAXBuilder();
				builder.setValidation( false );
				builder.setIgnoringElementContentWhitespace( false );
				mdocumentXML = builder.build( fileIn );
			}

			// all done
            return;
		}
		catch ( Exception ex )
		{
            mlogger.log( Level.SEVERE,"Exception during init2",ex);
			throw new Exception( "Failed to init XMLObject" );
		}
	}

	/**
	 * Read XML text from a w3c document.
	 * @param w3cDoc Input document from which to get XML
	 */
	public void init( org.w3c.dom.Document w3cDoc ) throws Exception
	{
		DOMBuilder builder;
		try
	  	{
			// make JDOM document
			builder = new DOMBuilder();
			mdocumentXML = builder.build( w3cDoc );

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during init3",ex);
			throw new Exception( "Failed to init XMLObject" );
		}
	}

	/**
	 * Read XML text from String. This will actually write to a file and read in
	 * using the file reading version of this init method. As such this is not very
	 * efficient.
	 * @param strXML input string containing the XML
	 */
	public void init( String strXML ) throws Exception
	{
		SAXBuilder builder;
		org.jdom.Element elementRoot;
		File fileTemp;
		PrintStream printOut;

		try
		{
			// save string to a temp file
			fileTemp = File.createTempFile( mstaticTempPrefix,mstaticTempSuffix );
			printOut = new PrintStream( new FileOutputStream( fileTemp ) );
			printOut.println( strXML );

			// read temp file using init method
			this.init( fileTemp );

			// remove temp file
			fileTemp.delete();

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during init4",ex);
			throw new Exception( "Failed to init XMLObject" );
		}
	}

	/**
	 * Get an XML root leaf value. A null return indicates the leaf name was not present on the XML.
	 * @param strName The name of the tag string associated with the XML object you want
	 * @return value associated with the leaf node
	 */
	synchronized public String getRootLeafValue( String strName )
	{
		org.jdom.Element elementTemp, elementRoot;
		List list;
		int i;

		try
		{
			// get root element
			elementRoot = mdocumentXML.getRootElement();
			if( elementRoot == null )
				return null;

			// get children from root
			list = elementRoot.getChildren();
			if( list == null )
				return null;

			// iterate over list until we find the tag we want
			for( i=0;i<list.size();i++ ) {
				elementTemp = (org.jdom.Element) list.get( i );
				if( elementTemp.getName().equals( strName ) )
					return elementTemp.getText();
			} // next match

			// no match
			return null;
		}
		catch ( Exception ex )
		{
            mlogger.log( Level.SEVERE,"Exception during getRootLeafValue",ex);
			return null;
		}
	}

	/**
	 * Set an XML tag's value (top level element). The XML value is set directly for this tag.
	 * @param strName The name of the tag string associated with the XML object you want
	 * @param strValue The value that should be set for this node
	 */
	synchronized public void setRootLeafValue( String strName, String strValue )
	{
		org.jdom.Element elementTemp, elementTarget, elementRoot;
		ElementFilter filter;
		List list;
		int i;

		try
		{
			// check params
			if( strName == null )
				throw new Exception("null name parameter");
			if( strValue == null )
				throw new Exception("null value parameter");

			// find target element (if there)
			elementTarget = null;

			// get root element
			elementRoot = mdocumentXML.getRootElement();
			if( elementRoot != null ) {
				// get children from root
				list = elementRoot.getChildren();
				if( list != null ) {

					// iterate over list until we find the tag we want
					for( i=0;i<list.size();i++ ) {
						elementTemp = (org.jdom.Element) list.get( i );
						if( elementTemp.getName().equals( strName ) ) {
							elementTarget = elementTemp;
							i = list.size(); // terminate search
						}
					}
				} // null list
			} // null root
			else throw new Exception("null root node");

			// make element (if not found one) and add to document
			if( elementTarget == null ) {
				elementTarget = new org.jdom.Element( strName );
				elementRoot.addContent( elementTarget );
			}

			// set the text value
			elementTarget.setText( strValue );

			// all done
			return;
		}
		catch ( Exception ex )
		{
            mlogger.log( Level.SEVERE,"Exception during setRootLeafValue",ex);
			return;
		}
	}

	/**
	 * output XML data in string format
	 * @return string representation of the XML data
	 */
	synchronized public String toString()
	{
		XMLOutputter outputXML;

		try
		{
			// make the XML output device
			outputXML = new XMLOutputter();
			outputXML.setTextTrim( true );
			outputXML.setNewlines( true );
			outputXML.setIndent("\t");

			// return
			return outputXML.outputString( mdocumentXML );
		}
		catch ( Exception ex )
		{
            mlogger.log( Level.SEVERE,"Exception during toString",ex);
			return null;
		}
	}

	/**
	 * output XML data in w3c docuement format
	 * @return Document w3c format docuemnt object containing XML
	 */
	synchronized public org.w3c.dom.Document toW3C()
	{
		String strXML;
		org.w3c.dom.Document docW3C;
		InputSource inSource;

		try
		{
			// get string version
			strXML = this.toString();

			// make w3c doc version
			inSource = new InputSource( new StringReader( strXML ) );
			return XMLUtils.newDocument( inSource );
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during toW3C",ex);
			return null;
		}
	}


	/**
	 * Write XML to a stream.
	 * @param out Output stream where writes should go
	 */
	synchronized private void writeObject(java.io.ObjectOutputStream out) throws IOException
	{
		try
		{
			// write XML document
			out.writeObject( mdocumentXML );

			// all done
			return;
		}
		catch ( Exception ex )
		{
            mlogger.log( Level.SEVERE,"Exception during writeObject",ex);
			throw new IOException("Failed to write xml document");
		}
	}

	/**
	 * read xml from a stream.
	 * @param in Input stream where to read from
	 */
	synchronized private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
	{
		// init (no constructor)
		mlogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.xmlobject.XMLObject");

		try
		{
			// read document
			mdocumentXML = (org.jdom.Document) in.readObject();

			// all done
			return;
		}
		catch ( Exception ex )
		{
            mlogger.log( Level.SEVERE,"Exception during readObject",ex);
			throw new IOException("Failed to read xml document");
		}
	}


} // end of XMLObject
