package org.syrup.helpers;
import org.apache.xml.serializer.OutputPropertiesFactory;
import org.apache.xml.serializer.Serializer;
import org.apache.xml.serializer.SerializerFactory;
import org.syrup.Function;
import org.syrup.InPort;
import org.syrup.OutPort;
import org.syrup.Task;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* XML parser that parses a Workflow definition into a Network.
*
* @author Robbert van Dalen
*/
public class NetworkParser extends DefaultHandler
{
static final String COPYRIGHT = "Copyright 2005 Robbert van Dalen."
+ "At your option, you may copy, distribute, or make derivative works under "
+ "the terms of The Artistic License. This License may be found at "
+ "http://www.opensource.org/licenses/artistic-license.php. "
+ "THERE IS NO WARRANTY; USE THIS PRODUCT AT YOUR OWN RISK.";
private HashMap tasks = new HashMap();
private ArrayList links = new ArrayList();
private BindingStruct binding = new BindingStruct(null, null, null, null);
private int depth;
private int mark;
private boolean inElement;
private Struct currentElement;
private ContentHandler subDocument = new DefaultHandler();
private boolean rootElement = false;
private Serializer serializer;
private ByteArrayOutputStream outArray;
/**
* The main program for the NetworkParser class
*
* @param args
* No arguments should be given.
*/
public static void main(String[] args) throws Exception
{
try
{
NetworkImpl n = new NetworkParser().parse(System.in);
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Parses a XML network description deserialized from a byte array.
*
* @param b
* The byte array to be deserialized.
* @return The NetworkImpl if succesful.
*/
public NetworkImpl parse(byte[] b) throws Exception
{
return parse(new ByteArrayInputStream(b));
}
/**
* Parses a XML network description, fed by an InputStream.
*
* @param in The InputStream to be parsed.
*
* @return The NetworkImpl if succesful.
*/
public NetworkImpl parse(InputStream in) throws Exception
{
XMLReader r = XMLReaderFactory.createXMLReader("org.apache.crimson.parser.XMLReaderImpl");
r.setContentHandler(this);
r.parse(new InputSource(in));
Set keys = tasks.keySet();
Iterator kIt = keys.iterator();
HashMap p = new HashMap();
int i = 0;
while (kIt.hasNext())
{
TaskStruct pp = (TaskStruct) tasks.get((String) kIt.next());
Class s = null;
s = Class.forName(pp.functionClass);
Function f = (Function) s.newInstance();
boolean orType = false;
if (pp.orType != null)
{
if (pp.orType.equals("true"))
{
orType = true;
}
}
p.put(pp.name, new TaskImpl(orType, pp.functionClass, pp.name, pp.description, pp.environment, pp.parameter));
}
Iterator cs = links.iterator();
LinkImpl c[] = new LinkImpl[links.size()];
i = 0;
while (cs.hasNext())
{
LinkStruct cc = (LinkStruct) cs.next();
Task from = pr(cc.from, p);
Task to = pr(cc.to, p);
if (from == null
&& to == null)
{
throw new Exception("from or to must be a reference");
}
c[i++] = new LinkImpl(new OutPortImpl(from, pr_port(cc.from)), new InPortImpl(to, pr_port(cc.to)), new DataImpl(cc.content));
}
InPort in1 = null;
InPort in2 = null;
OutPort out1 = null;
OutPort out2 = null;
if (binding != null)
{
in1 = new InPortImpl(pr(binding.in_1, p), pr_port(binding.in_1));
in2 = new InPortImpl(pr(binding.in_2, p), pr_port(binding.in_2));
out1 = new OutPortImpl(pr(binding.out_1, p), pr_port(binding.out_1));
out2 = new OutPortImpl(pr(binding.out_2, p), pr_port(binding.out_2));
}
Iterator p_v = p.values().iterator();
Task pa[] = new Task[p.values().size()];
i = 0;
while (p_v.hasNext())
{
pa[i++] = (Task) p_v.next();
}
return new NetworkImpl(in1, in2, out1, out2, pa, c);
}
/**
* Called at the start of a subdocument (contained by a Link)
*/
private void startSubDocument() throws SAXException
{
startSubDocument("xml");
}
/**
* Called at the start of a subdocument (contained by a Link)
*
* @param encoding
* The encoding of the subdocument (xml, text or base64)
*/
private void startSubDocument(String encoding) throws SAXException
{
outArray = new ByteArrayOutputStream();
if (encoding == null)
{
encoding = "xml";
}
if (encoding.equals("xml")
|| encoding.equals("text"))
{
Properties props = OutputPropertiesFactory.getDefaultMethodProperties(encoding);
if (encoding.equals("xml"))
{
props.put("indent", "yes");
props.put("{http://xml.apache.org/xalan}indent-amount", "4");
props.remove("standalone");
}
serializer = SerializerFactory.getSerializer(props);
serializer.setOutputStream(outArray);
try
{
subDocument = serializer.asContentHandler();
subDocument.startDocument();
}
catch (Exception e)
{
throw new SAXException("Could not serialize sub-stream");
}
}
else if (encoding.equals("base64"))
{
subDocument = new BASE64ContentHandler(outArray);
}
else
{
throw new SAXException("encoding "
+ encoding + " not supported");
}
}
/**
* Called at the end of a subdocument (contained by a Link)
*/
private void endSubDocument() throws SAXException
{
subDocument.endDocument();
if (rootElement)
{
if (outArray.size() > 0)
{
currentElement.content = outArray.toByteArray();
}
}
subDocument = new DefaultHandler();
}
/**
* Returns a Task identified by it's name.
*
* @param k
* A Task's name.
* @param m
* The Map holding all Tasks indentified by name.
* @return The Task, indentified by it's name.
*/
private static Task pr_get(String k, Map m) throws Exception
{
Task p = (Task) m.get(k);
if (p == null)
{
throw new Exception("Reference to task '"
+ k + "' not found");
}
return p;
}
/**
* Returns a Task identified by it's name + port.
*
* @param k
* A Task's name + port.
* @param m
* The Map holding all Tasks indentified by name.
* @return The Task, indentified by it's name.
*/
private static Task pr(String s, Map m) throws Exception
{
if (s == null)
{
return null;
}
String t = s.trim();
if (s.endsWith("-1")
|| s.endsWith("-2"))
{
String k = t.substring(0, t.length() - 2);
return pr_get(k, m);
}
return pr_get(t, m);
}
/**
* Returns the boolean encoding of a Port specification.
*
* @param s
* The Port specification
* @return The boolean encoding of the Port specification.
*/
private static boolean pr_port(String s)
{
if (s != null)
{
if (s.endsWith("-2"))
{
return true;
}
}
return false;
}
/**
*/
public void startPrefixMapping(java.lang.String prefix,
java.lang.String namespaceUri) throws org.xml.sax.SAXException
{
if (rootElement)
{
subDocument.startPrefixMapping(prefix, namespaceUri);
}
}
/**
*/
public void endPrefixMapping(java.lang.String prefix)
throws org.xml.sax.SAXException
{
if (rootElement)
{
subDocument.endPrefixMapping(prefix);
}
}
/**
*/
public void startElement(String namespaceUri, String localName,
String qName, Attributes att) throws org.xml.sax.SAXException
{
depth++;
if (inElement)
{
if (!rootElement)
{
mark = depth;
rootElement = true;
startSubDocument(att.getValue("encoding"));
}
else
{
subDocument.startElement(namespaceUri, localName, qName, att);
}
}
else
{
rootElement = false;
mark = depth;
if (localName.equals("binding"))
{
inElement = true;
String in1 = att.getValue("in-1");
String in2 = att.getValue("in-2");
String out1 = att.getValue("out-1");
String out2 = att.getValue("out-2");
binding = new BindingStruct(in1, in2, out1, out2);
currentElement = binding;
}
else if (localName.equals("task"))
{
inElement = true;
String name = att.getValue("name");
if (name != null)
{
if (tasks.get(name) == null)
{
String functionClass = att.getValue("functionClass");
String orType = att.getValue("orType");
String descr = att.getValue("description");
String environment = att.getValue("environment");
String parameter = att.getValue("parameter");
currentElement = new TaskStruct(name, functionClass, orType, descr, environment, parameter);
tasks.put(name, currentElement);
}
else
{
throw new SAXException("Duplicate task name: "
+ name);
}
}
else
{
throw new SAXException("Cannot process Tasks without a name");
}
}
else if (localName.equals("link"))
{
inElement = true;
String from = att.getValue("from");
String to = att.getValue("to");
currentElement = new LinkStruct(from, to);
links.add(currentElement);
}
}
}
/**
*/
public void endElement(String namespaceUri, String localName, String qName)
throws org.xml.sax.SAXException
{
if (depth == mark
&& inElement)
{
endSubDocument();
inElement = false;
rootElement = false;
}
else
{
subDocument.endElement(namespaceUri, localName, qName);
}
depth--;
}
/**
*/
public void characters(char[] arg1, int arg2, int arg3)
throws org.xml.sax.SAXException
{
if (rootElement)
{
subDocument.characters(arg1, arg2, arg3);
}
}
/**
*/
public void ignorableWhitespace(char[] arg1, int arg2, int arg3)
throws org.xml.sax.SAXException
{
if (rootElement)
{
subDocument.ignorableWhitespace(arg1, arg2, arg3);
}
}
/**
*/
public void processingInstruction(String target, String data)
throws org.xml.sax.SAXException
{
if (rootElement)
{
subDocument.processingInstruction(target, data);
}
}
/**
*/
public void skippedEntity(java.lang.String arg1)
throws org.xml.sax.SAXException
{
if (rootElement)
{
subDocument.skippedEntity(arg1);
}
}
/**
* Description of the Class
*
* @author Robbert van Dalen
*/
private class BASE64ContentHandler extends DefaultHandler
{
OutputStream output;
sun.misc.BASE64Decoder decoder = null;
/**
* Constructor for the BASE64ContentHandler object
*
* @param o
* Description of the Parameter
*/
public BASE64ContentHandler(OutputStream o)
{
decoder = new sun.misc.BASE64Decoder();
output = o;
}
/**
* Description of the Method
*
* @param arg1
* Description of the Parameter
* @param arg2
* Description of the Parameter
* @param arg3
* Description of the Parameter
* @exception org.xml.sax.SAXException
* Description of the Exception
*/
public void characters(char[] arg1, int arg2, int arg3)
throws org.xml.sax.SAXException
{
String s = new String(arg1, arg2, arg3);
try
{
byte[] b = decoder.decodeBuffer(s);
output.write(b);
}
catch (Exception e)
{
throw new SAXException("Could not decode/write to output");
}
}
/**
* Description of the Method
*
* @param arg1
* Description of the Parameter
* @param arg2
* Description of the Parameter
* @param arg3
* Description of the Parameter
* @exception org.xml.sax.SAXException
* Description of the Exception
*/
public void ignorableWhitespace(char[] arg1, int arg2, int arg3)
throws org.xml.sax.SAXException
{
}
}
/**
* Temporary data-structure to hold content.
*
* @author Robbert van Dalen
*/
private class Struct
{
/**
* Description of the Field
*/
protected byte[] content;
}
/**
* Temporary data-structure to hold Task content.
*
* @author Robbert van Dalen
*/
private class TaskStruct extends Struct
{
/**
* Description of the Field
*/
public final String name;
/**
* Description of the Field
*/
public final String functionClass;
/**
* Description of the Field
*/
public final String orType;
/**
* Description of the Field
*/
public final String description;
/**
* Description of the Field
*/
public final String environment;
/**
* Description of the Field
*/
public final String parameter;
/**
* Constructor for the TaskStruct object
*
* @param n
* Description of the Parameter
* @param f
* Description of the Parameter
* @param t
* Description of the Parameter
* @param d
* Description of the Parameter
* @param c
* Description of the Parameter
* @param p
* Description of the Parameter
*/
public TaskStruct(String n, String f, String t, String d, String c, String p)
{
name = n;
functionClass = f;
orType = t;
description = d;
environment = c;
parameter = p;
}
}
/**
* Temporary data-structure to hold Link content.
*
* @author Robbert van Dalen
*/
private class LinkStruct extends Struct
{
/**
* Description of the Field
*/
public final String from;
/**
* Description of the Field
*/
public final String to;
/**
* Constructor for the LinkStruct object
*
* @param f
* Description of the Parameter
* @param t
* Description of the Parameter
*/
public LinkStruct(String f, String t)
{
from = f;
to = t;
}
}
/**
* Temporary data-structure to hold Binding content.
*
* @author Robbert van Dalen
*/
private class BindingStruct extends Struct
{
/**
* Description of the Field
*/
public final String in_1;
/**
* Description of the Field
*/
public final String in_2;
/**
* Description of the Field
*/
public final String out_1;
/**
* Description of the Field
*/
public final String out_2;
/**
* Constructor for the BindingStruct object
*
* @param in1
* Description of the Parameter
* @param in2
* Description of the Parameter
* @param out1
* Description of the Parameter
* @param out2
* Description of the Parameter
*/
public BindingStruct(String in1, String in2, String out1, String out2)
{
in_1 = in1;
in_2 = in2;
out_1 = out1;
out_2 = out2;
}
}
}