package com.jxml.quick;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.beans.PropertyEditor;
import java.net.URL;
import com.jxml.quick.editors.*;
import java.math.BigDecimal;
import com.jxml.protocol.*;
import java.io.*;
import java.util.*;
import com.jxml.quick.config.Item;

/**
 * Helper class for QConverter.
 * When building a conversion tree for transforming data models,
 * the classes of all the nodes in the tree can subclass QConverterSupport.
 */
public class QConverterSupport implements QConverter, Item
{
	public DocumentHandler destination; // destination of generated SAX events
	public AttributeListImpl al=new AttributeListImpl(); // used by start element events
	public String SYSTEM="";
	public String PUBLIC="";
	public String rootTag="";

	/**
	 * Converts from one markup language to another:<br>
	 * arg[0] - Input schema URL or file<br>
	 * arg[1] - Document input URL or file<br>
	 * arg[2] - Output file (Optional).
	 */
	public static void main(String[] args)
	{
		try
		{
			// Include classpath protocol
			Protocol.addJXMLProtocolPackage();

			// Parse the QJML Schema
			QDoc schema=Quick.parseSchema(args[0]);

			// Parse the a file
			QDoc doc=Quick.parse(schema,args[1]);

			// Re-Express the XML
			int sargs=args.length;
			if (sargs>2)
				Quick.express(doc,args[2]);
			else
				System.out.println(Quick.express(doc));
		}
		catch (Exception pe)
		{
			QPE.display(pe);
		}
	}

	/**
	 * This method can be invoked by config.<br>
	 * schema - Input schema URL or file<br>
	 * schemaClass - Input schema class (prefered over schema)<br>
	 * in - Document input URL or file<br>
	 * out - Output file (Optional).
	 */
 	public void eval(Map properties, List args, ClassLoader cl)
		throws Exception
	{
		Object scc=properties.get("schemaClass");
		String in=properties.get("in").toString();
		String out=properties.get("out").toString();
		String schemaClassName="";
		if (scc!=null)
			schemaClassName=scc.toString();
		QDoc schema=null;
		if (!"".equals(schemaClassName))
		{
			schema=QSchemaFactory.create(schemaClassName);
		}
		else
		{
			Object scf=properties.get("schema");
			String schemaName="";
			if (scf!=null)
				schemaName=scf.toString();
			schema=Quick.parseSchema(schemaName);
		}

		// Parse the a file
		QDoc doc=Quick.parse(schema,in);
		// Re-Express the XML
		if (!"".equals(out))
			Quick.express(doc,out);
		else
			System.out.println(Quick.express(doc));
	}

	/**
	 * Set the destination for the generated SAX events.
	 * Called by eval for the root node, but must be called
	 * by application logic for all other nodes before generating any events.
	 * @param destination Destination for all generated SAX events.
	 */
	public void setDestination(DocumentHandler destination)
	{
		this.destination=destination;
	}

	/**
	 * Generates a sequence of SAX events.
	 * Called by Quick.convert and Quick.express for the root node.
	 * @param destination Destination for all generated SAX events.
	 */
	public void eval(DocumentHandler destination)
		throws SAXException, IOException
	{
		setDestination(destination); // save the event destination
		destination.startDocument(); // generate a start document event.
		evalRoot(); // call application logic to generate all element events
		destination.endDocument(); // generate an end document event
	}


	/**
	 * Generate element events.
	 * Called by the eval method on the root object.
	 * Must be overridden by application logic on the root object.
	 */
	public void evalRoot()
		throws SAXException, IOException
	{
		throw new Error
("Please override QConverterSupport.evalRoot on the root object of the conversion tree");
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addStringAttribute(String name, String value)
	{
		if (!"".equals(value))
			al.addAttribute(name,"CDATA",value);
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addBooleanAttribute(String name, Boolean value)
	{
		String s=value.toString();
		al.addAttribute(name,"CDATA",s);
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addBooleanAttribute(String name, boolean value)
	{
		Boolean v=new Boolean(value);
		String s=v.toString();
		al.addAttribute(name,"CDATA",s);
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addIntAttribute(String name, Integer value)
	{
		al.addAttribute(name,"CDATA",value.toString());
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addIntAttribute(String name, int value)
	{
		al.addAttribute(name,"CDATA",Integer.toString(value));
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addLongAttribute(String name, Long value)
	{
		al.addAttribute(name,"CDATA",value.toString());
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addLongAttribute(String name, long value)
	{
		al.addAttribute(name,"CDATA",Long.toString(value));
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addShortAttribute(String name, Short value)
	{
		al.addAttribute(name,"CDATA",value.toString());
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addShortAttribute(String name, short value)
	{
		al.addAttribute(name,"CDATA",Short.toString(value));
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addFloatAttribute(String name, Float value)
	{
		al.addAttribute(name,"CDATA",value.toString());
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addFloatAttribute(String name, float value)
	{
		al.addAttribute(name,"CDATA",Float.toString(value));
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addByteAttribute(String name, Byte value)
	{
		al.addAttribute(name,"CDATA",value.toString());
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addByteAttribute(String name, byte value)
	{
		al.addAttribute(name,"CDATA",Byte.toString(value));
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addDoubleAttribute(String name, Double value)
	{
		al.addAttribute(name,"CDATA",value.toString());
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addDoubleAttribute(String name, double value)
	{
		al.addAttribute(name,"CDATA",Double.toString(value));
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addURLAttribute(String name, URL value)
	{
		PropertyEditor pe=new URLEditor();
		pe.setValue(value);
		String s=pe.getAsText();
		al.addAttribute(name,"CDATA",s);
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addBase64Attribute(String name, byte[] value)
	{
		PropertyEditor pe=new Base64Editor();
		pe.setValue(value);
		String s=pe.getAsText();
		al.addAttribute(name,"CDATA",s);
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addCharAttribute(String name, Character value)
	{
		PropertyEditor pe=new CharEditor();
		pe.setValue(value);
		String s=pe.getAsText();
		al.addAttribute(name,"CDATA",s);
	}

	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addCharAttribute(String name, char value)
	{
		PropertyEditor pe=new CharEditor();
		pe.setValue(new Character(value));
		String s=pe.getAsText();
		al.addAttribute(name,"CDATA",s);
	}


	/**
	 * Generate an attribute.
	 * The attribute will be attached to the next start element event.
	 * @param name The attribute's name
	 * @param value The attribute's value
	 */
	public void addBigDecimalAttribute(String name, BigDecimal value)
	{
		PropertyEditor pe=new BigDecimalEditor();
		pe.setValue(value);
		String s=pe.getAsText();
		al.addAttribute(name,"CDATA",s);
	}
	/**
	 * Generate a start element event.
	 * @param name The element's tag name.
	 */
	public void startElement(String name)
		throws SAXException
	{
		destination.startElement(name,al);
		al.clear();
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addStringContent(String value)
		throws SAXException
	{
		int l=value.length();
		char[] c=new char[l];
		value.getChars(0,l,c,0);
		destination.characters(c,0,l);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addBooleanContent(Boolean value)
		throws SAXException
	{
		String s=value.toString();
		addStringContent(s);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addBooleanContent(boolean value)
		throws SAXException
	{
		Boolean v=new Boolean(value);
		String s=v.toString();
		addStringContent(s);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addIntContent(Integer value)
		throws SAXException
	{
		addStringContent(value.toString());
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addIntContent(int value)
		throws SAXException
	{
		addStringContent(Integer.toString(value));
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addLongContent(Long value)
		throws SAXException
	{
		addStringContent(value.toString());
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addLongContent(long value)
		throws SAXException
	{
		addStringContent(Long.toString(value));
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addShortContent(Short value)
		throws SAXException
	{
		addStringContent(value.toString());
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addShortContent(short value)
		throws SAXException
	{
		addStringContent(Short.toString(value));
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addFloatContent(Float value)
		throws SAXException
	{
		addStringContent(value.toString());
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addFloatContent(float value)
		throws SAXException
	{
		addStringContent(Float.toString(value));
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addByteContent(Byte value)
		throws SAXException
	{
		addStringContent(value.toString());
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addByteContent(byte value)
		throws SAXException
	{
		addStringContent(Byte.toString(value));
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addDoubleContent(Double value)
		throws SAXException
	{
		addStringContent(value.toString());
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addDoubleContent(double value)
		throws SAXException
	{
		addStringContent(Double.toString(value));
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addURLContent(URL value)
		throws SAXException
	{
		PropertyEditor pe=new URLEditor();
		pe.setValue(value);
		String s=pe.getAsText();
		addStringContent(s);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addBase64Content(byte[] value)
		throws SAXException
	{
		PropertyEditor pe=new Base64Editor();
		pe.setValue(value);
		String s=pe.getAsText();
		addStringContent(s);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addCharContent(Character value)
		throws SAXException
	{
		PropertyEditor pe=new CharEditor();
		pe.setValue(value);
		String s=pe.getAsText();
		addStringContent(s);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addCharContent(char value)
		throws SAXException
	{
		PropertyEditor pe=new CharEditor();
		pe.setValue(new Character(value));
		String s=pe.getAsText();
		addStringContent(s);
	}

	/**
	 * Add text to the content of the current element.
	 * Must be called between startElement and endElement.
	 * @param value Text to be added to the element's content.
	 */
	public void addBigDecimalContent(BigDecimal value)
		throws SAXException
	{
		PropertyEditor pe=new BigDecimalEditor();
		pe.setValue(value);
		String s=pe.getAsText();
		addStringContent(s);
	}

	/**
	 * Generate an end element event.
	 * Must be called only after the startElement for the same tag name.
	 * @param name The element's tag name.
	 */
	public void endElement(String name)
		throws SAXException
	{
		destination.endElement(name);
	}

	/**
	 * Generate an start and end element events for an empty element.
	 * @param name The element's tag name.
	 */
	public void emptyElement(String name)
		throws SAXException
	{
		startElement(name);
		endElement(name);
	}

	public String getPUBLIC()
		throws SAXException, IOException
	{
		return PUBLIC;
	}

	public String getSYSTEM()
		throws SAXException, IOException
	{
		return SYSTEM;
	}

	public String getRootTag()
		throws SAXException, IOException
	{
		return rootTag;
	}
}
