package com.jxml.quick.ocm; // Open Conversion Model
import com.jxml.quick.*;
import com.jxml.quick.engine.*;
import org.xml.sax.*;
import java.io.*;
import java.util.*;
import com.jxml.protocol.*;
import com.jxml.quick.config.Item;

/**
 * Support class for OCMsink.
<pre>
DTD:

&lt;!ELEMENT sinkSupport (<a href="OCMsinkSupport.html#source">((contextSupport | transcribe | input) | sourceLink)</a>,<a href="OCMsinkSupport.html#validator">(validatorSupport | validateLink)?</a>)>

&lt;!ATTLIST sinkSupport 
&nbsp;&nbsp;&nbsp;&nbsp;<a href="OCMsinkSupport.html#prefix">docNamePrefix</a> CDATA #IMPLIED
&nbsp;&nbsp;&nbsp;&nbsp;<a href="OCMsinkSupport.html#expressDefaults">expressDefaults</a> CDATA "False"
&nbsp;&nbsp;&nbsp;&nbsp;ID CDATA "False"
>
</pre>
 */
public class OCMsinkSupport implements OCMsink, Item
{
	public static long genNbr=0;

	/**
	 * Doc Name Prefix.
	 */
	public String prefix="";

	/**
	 * The validator is applied prior to processing.
	 * May be null.
	 */
	public OCMvalidator validator=null;

	/**
	 * The source of the input document.
 	 */
	public OCMsource source=null;

	/**
	 * Set to true when defaults are to be expressed.
	 */
	public boolean expressDefaults=false;

	// implements an OCMbase method
	public OCMbase.SubBase spawn(HashMap prior)
	{
		OCMbase.SubBase rv=(OCMbase.SubBase)prior.get(this);
		if (rv!=null)
			return rv;
		OCMbase.SubBase base=new SubSinkSupport(prior);
		prior.put(this,base);
		return base;
	}

	// implements OCMsink method
	public void setValidator(OCMvalidator validator)
		throws SAXException, IOException
	{
		this.validator=validator;
	}

	// implements OCMsink method
	public void setSource(OCMsource source)
		throws SAXException, IOException
	{
		this.source=source;
	}

	/**
	 * Returns an OCM sink.
	 */
	public static SubSinkSupport createSink(String sinkSchemaName, String sinkName, ClassLoader cl)
		throws SAXException, IOException
	{
		// Parse the converter Schema
		QDocImpl sinkSchema=(QDocImpl)Quick.parseSchema(sinkSchemaName);
		sinkSchema.cl=cl;

		return createSink(sinkSchema,sinkName);
	}

	/**
	 * Returns an OCM sink.
	 */
	public static SubSinkSupport createSink(QDoc sinkSchema, String sinkName)
		throws SAXException, IOException
	{
		// Parse the sink document
		QDoc sinkDoc=Quick.parse(sinkSchema,sinkName);

		// Create the converter
		OCMsink sink=(OCMsink)sinkDoc.getRoot();

		return (SubSinkSupport)sink.spawn(new HashMap());
	}

	/**
	 * This method can be invoked by config.<br>
	 * sinkSchema - the schema for the sink file (typically classpath:///com/jxml/quick/util/util.qiml)<br>
	 * sinkSchemaClass - the schema class for the sink file (prefered to sinkSchema)<br>
	 * sinkIn - the (required) sink file or url<br>
	 * in - the (required) property naming the QDML document to be updated<br>
	 * out - the (optional) property naming the output file.
	 */
 	public void eval(Map properties, List args, ClassLoader cl)
		throws Exception
	{
		exec(properties,args,cl);
	}

 	public static void exec(Map properties, List args, ClassLoader cl)
		throws Exception
	{
		SubSinkSupport subSink;
		Object scc=properties.get("sinkSchemaClass");
		String sinkIn=properties.get("sinkIn").toString();
		String in=properties.get("in").toString();
		Object out=properties.get("out");
		String sinkSchemaClassName="";
		if (scc!=null)
			sinkSchemaClassName=scc.toString();
		if (!"".equals(sinkSchemaClassName))
		{
			QDoc schema=QSchemaFactory.clCreate(sinkSchemaClassName,cl);
			subSink=createSink(schema,sinkIn);
		}
		else
		{
			Object scf=properties.get("sinkSchema");
			String sinkSchema="";
			if (scf!=null)
				sinkSchema=scf.toString();
			subSink=createSink(sinkSchema,sinkIn,cl);
		}
		subSink.cl=cl;
		subSink.setProperties(properties);
		subSink.setInputDocName(in);
		if(out instanceof StringBuffer)
		{
			StringBuffer sb=(StringBuffer)out;
			sb.append(subSink.express());
		}
		else
		{
			subSink.setDocName(out.toString());
			subSink.expressFile();
		}
	}

	/**
	 * A processing object spawned from an OCMsinkSupport object.
	 */
	public class SubSinkSupport implements OCMsink.SubSink
	{
		public ClassLoader cl=null;

		/**
		 * Properties from config.
		 */
		public Map properties=new HashMap();

		/**
		 * The validator is applied prior to processing.
		 * May be null.
		 */
		public OCMvalidator.SubValidator subValidator=null;

		/**
		 * The source of the input document.
 		 */
		public OCMsource.SubSource subSource=null;

		/**
		 * Document URL.
		 */
		public String docName="";

		/**
		 * The input document to be processed.
		 */
		public QDoc inputDoc=null;

		public SubSinkSupport(HashMap prior)
		{
			if (validator!=null)
				subValidator=(OCMvalidator.SubValidator)validator.spawn(prior);
			subSource=(OCMsource.SubSource)source.spawn(prior);
		}

		// implements SubSink method
		public OCMsource.SubSource getSubSource()
			throws SAXException, IOException
		{
			return subSource;
		}

		public QDoc getInputSchemaDoc()
			throws SAXException, IOException
		{
			return subSource.getSchemaDoc();
		}

		// implements SubSink method
		public void setInputDocName(Object inputName)
			throws SAXException, IOException
		{
			if ("".equals(docName) && !"".equals(prefix))
			{
				docName=prefix+(genNbr++);
			}
			subSource.setInputDocName(inputName);
			if (subValidator!=null)
				subValidator.setInputDocName(inputName);
		}

		// implements SubSink method
		public void setDocName(String docName)
			throws SAXException, IOException
		{
			this.docName=docName;
		}

		// Implement SubSink method.
	        public void expressFile()
	            throws SAXException,IOException
		{
			PrintWriter pw=new PrintWriter(new FileWriter(docName,false),false);
			express(pw);
			pw.flush();
			pw.close();
		}

		// Implement SubSink method.
		public String express()
			throws SAXException, IOException
		{
			return Quick.express(getInputDoc(),expressDefaults);
		}

		// Implement SubSink method.
		public void express(PrintWriter pw)
			throws SAXException, IOException
		{
			Quick.express(getInputDoc(),pw,expressDefaults);
		}

		/**
		 * Returns the input document.
		 */
		protected QDoc getInputDoc()
			throws SAXException, IOException
		{
			if (inputDoc!=null)
				return inputDoc;
			inputDoc=subSource.getDoc();
			if (subValidator!=null)
				subValidator.valid();
			return inputDoc;
		}	

		/**
		 * Returns the config properties
		 */
		public Map getProperties()
		{
			return properties;
		}

		/**
		 * Sets the config properties
		 */
		public void setProperties(Map properties)
		{
			this.properties=properties;
			subSource.setProperties(properties);
			if (subValidator!=null)
			{
				subValidator.setProperties(properties);
			}
		}
	}
}
