package org.syrup.helpers;
import org.apache.xml.serializer.OutputPropertiesFactory;
import org.apache.xml.serializer.SerializationHandler;
import org.apache.xml.serializer.Serializer;
import org.apache.xml.serializer.SerializerFactory;
import org.syrup.Context;
import org.syrup.Data;
import org.syrup.LogEntry;
import org.syrup.PTask;
import org.syrup.Port;
import org.syrup.Task;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Properties;
/**
* Helper class that enables the serialization of Syrup types.
*
* @author Robbert van Dalen
*/
public class XMLOutput implements EntityResolver
{
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 final static InputSource defaultEntityResolver = new InputSource(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
/**
* Wraps an OutputStream with a SerializationHandler.
*
* @param o
* The OutputStream to output XML.
* @return The SerializationHandler to send SAX-events to.
*/
public SerializationHandler wrap(OutputStream o) throws Exception
{
Properties p = OutputPropertiesFactory.getDefaultMethodProperties("xml");
p.put("indent", "yes");
p.put("{http://xml.apache.org/xalan}indent-amount", "4");
if (p.containsKey("standalone"))
{
p.remove("standalone");
}
Serializer serializer = SerializerFactory.getSerializer(p);
serializer.setOutputStream(o);
return (SerializationHandler) serializer;
}
/**
* Starts the serialization with a start tag.
*
* @param tag
* The start tag.
* @param handler
* The serialization handler.
*/
public synchronized void startDocument(String tag,
SerializationHandler handler) throws Exception
{
handler.startDocument();
handler.startElement("", "", tag, null);
}
/**
* Ends the serialization with an end tag.
*
* @param tag
* The end tag.
* @param handler
* The serialization handler.
*/
public synchronized void endDocument(String tag,
SerializationHandler handler) throws Exception
{
handler.endElement("", "", tag);
handler.endDocument();
}
/**
* Starts a new element tag.
*
* @param tag
* The element tag
* @param handler
* The serialization handler
*/
public synchronized void start(String tag, SerializationHandler handler)
throws Exception
{
handler.startElement("", "", tag, null);
handler.flushPending();
handler.getWriter().flush();
}
/**
* Ends an element tag.
*
* @param tag
* The element tag
* @param handler
* The serialization handler
*/
public synchronized void end(String tag, SerializationHandler handler)
throws Exception
{
handler.endElement("", "", tag);
handler.flushPending();
handler.getWriter().flush();
}
/**
* Serializes a PTask.
*
* @param t
* The Ptask to be serialized.
* @param handler
* The serialization handler.
*/
public synchronized void output(PTask t, SerializationHandler handler)
throws Exception
{
start(t, handler);
end(t, handler);
}
/**
* Starts the PTask to be serialized.
*
* @param t
* The Ptask to start serializing.
* @param handler
* The serialization handler.
*/
public synchronized void start(PTask t, SerializationHandler handler)
throws Exception
{
handler.startElement("", "", "ptask", taskAttributes(t));
}
/**
* Ends the PTask to be serialized.
*
* @param t
* The Ptask to end serializing.
* @param handler
* The serialization handler.
*/
public synchronized void end(PTask t, SerializationHandler handler)
throws Exception
{
handler.endElement("", "", "ptask");
}
/**
* Serializes a Context.
*
* @param handler
* The serialization handler.
* @param c
* The Context to be serialized
*/
public synchronized void output(Context c, SerializationHandler handler)
throws Exception
{
start(c, handler);
end(c, handler);
handler.flushPending();
handler.getWriter().flush();
}
public synchronized void output(LogEntry l, SerializationHandler handler)
throws Exception
{
start(l, handler);
end(l, handler);
handler.flushPending();
handler.getWriter().flush();
}
public synchronized void start(LogEntry l, SerializationHandler handler)
throws Exception
{
AttributesImpl a = new AttributesImpl();
a.addAttribute("", "", "time", "string", "" + org.syrup.helpers.Utils.asDate(l.time()));
a.addAttribute("", "", "key", "string", l.key());
a.addAttribute("", "", "event", "string", LogEntryImpl.map(l.event()));
a.addAttribute("", "", "worker", "string", l.worker());
handler.startElement("", "", "log-entry", a);
}
public synchronized void end(LogEntry l, SerializationHandler handler)
throws Exception
{
handler.endElement("", "", "log-entry");
}
/**
* Description of the Method
*
* @param c
* Description of the Parameter
* @param handler
* Description of the Parameter
*/
public synchronized void start(Context c, SerializationHandler handler)
throws Exception
{
Attributes a = taskAttributes(c.task());
handler.startElement("", "", "ptask-context", a);
if (c.in_1_link() != null)
{
a = portAttributes(c.in_1_link().from());
handler.startElement("", "", "in-1", a);
output(c.in_1_link().content(), handler);
handler.endElement("", "", "in-1");
}
if (c.in_2_link() != null)
{
a = portAttributes(c.in_2_link().from());
handler.startElement("", "", "in-2", a);
output(c.in_2_link().content(), handler);
handler.endElement("", "", "in-2");
}
if (c.out_1_link() != null)
{
a = portAttributes(c.out_1_link().to());
handler.startElement("", "", "out-1", a);
output(c.out_1_link().content(), handler);
handler.endElement("", "", "out-1");
}
if (c.out_2_link() != null)
{
a = portAttributes(c.out_2_link().to());
handler.startElement("", "", "out-2", a);
output(c.out_2_link().content(), handler);
handler.endElement("", "", "out-2");
}
}
/**
* Description of the Method
*
* @param c
* Description of the Parameter
* @param handler
* Description of the Parameter
*/
public synchronized void end(Context c, SerializationHandler handler)
throws Exception
{
handler.endElement("", "", "ptask-context");
}
/**
* Serializes a Data instance..
*
* @param handler
* The serialization handler.
* @param d
* The Data instance.
*/
public synchronized void output(Data d, SerializationHandler handler)
throws Exception
{
if (d != null)
{
output(d.bytes(), handler);
}
}
/**
* Returns the mapped Attributes of a Task.
*
* @param t
* The Task to map the Attributes to.
* @return The mapped Attributes of a Task.
*/
protected synchronized AttributesImpl taskAttributes(Task t)
{
if (t instanceof PTask)
{
return taskAttributes((PTask) t);
}
return new AttributesImpl();
}
/**
* Returns the mapped Attributes of a Port.
*
* @param p
* The Port to map the Attributes to.
* @return the mapped Attributes of a Port.
*/
protected synchronized AttributesImpl portAttributes(Port p)
{
AttributesImpl a = new AttributesImpl();
if (p != null)
{
if (p.task() instanceof PTask)
{
PTask t = (PTask) p.task();
if (p.isSecond())
{
a.addAttribute("", "", "lkey", "string", t.key()
+ "-2");
}
else
{
a.addAttribute("", "", "lkey", "string", t.key()
+ "-1");
}
a.addAttribute("", "", "lparentKey", "string", t.parentKey());
a.addAttribute("", "", "lname", "string", t.name());
}
}
return a;
}
/**
* Returns the mapped Attributes of a PTask.
*
* @param t
* The PTask to map the Attributes to.
* @return The mapped Attributes of a PTask.
*/
protected synchronized AttributesImpl taskAttributes(PTask t)
{
AttributesImpl a = new AttributesImpl();
if (t != null)
{
a.addAttribute("", "", "key", "string", t.key());
a.addAttribute("", "", "parentKey", "string", t.parentKey());
a.addAttribute("", "", "name", "string", t.name());
a.addAttribute("", "", "functionClass", "string", t.functionClass());
if (t.description() != null)
{
a.addAttribute("", "", "description", "string", t.description());
}
if (t.environment() != null)
{
a.addAttribute("", "", "environment", "string", t.environment());
}
if (t.parameter() != null)
{
a.addAttribute("", "", "parameter", "string", t.parameter());
}
a.addAttribute("", "", "orType", "string", ""
+ t.orType());
a.addAttribute("", "", "executable", "string", ""
+ t.executable());
a.addAttribute("", "", "done", "string", ""
+ t.done());
a.addAttribute("", "", "creationTime", "string", ""
+ org.syrup.helpers.Utils.asDate(t.creationTime()));
a.addAttribute("", "", "modificationTime", "string", ""
+ org.syrup.helpers.Utils.asDate(t.modificationTime()));
a.addAttribute("", "", "modifications", "string", ""
+ t.modifications());
if (t.worker() != null)
{
a.addAttribute("", "", "worker", "string",
t.worker());
}
a.addAttribute("", "", "isParent", "string", ""
+ t.isParent());
}
return a;
}
/**
* Writes the byte array as SAX event(s). If the byte array can be parsed by
* an XML reader, the fired SAX events will be the events generated by the
* XML Reader. <br>
* If the byte array can be turned into UTF-8 String, and a Characters SAX
* Event will be fired with the (encoded) UTF-8 String.<br>
* Otherwise, the byte array will be encoded in base64 and with this encoded
* string a Character event will be fired.
*
* @param b
* The byte array to be mapped to SAX events.
* @param handler
* The handler to send SAX events to.
*/
protected synchronized void output(byte[] b, SerializationHandler handler)
throws Exception
{
if (b != null)
{
XMLReader r = XMLReaderFactory.createXMLReader("org.apache.crimson.parser.XMLReaderImpl");
ByteArrayInputStream i = new ByteArrayInputStream(b);
// [TODO: make this work in a streaming fashion]
try
{
r.setFeature("http://xml.org/sax/features/validation", false);
r.setEntityResolver(this);
InputSource in = new InputSource(i);
in.setSystemId("localhost");
r.parse(in);
}
catch (Exception e)
{
// Could not be parsed
CharBuffer ch = java.nio.charset.Charset.forName("UTF-8").decode(ByteBuffer.wrap(b));
char cbuf[] = ch.array();
for (int m = 0; m < cbuf.length; m++)
{
if (!(cbuf[m] >= 32
|| cbuf[m] == '\n' || cbuf[m] == '\t' || cbuf[m] == '\r'))
{
base64(b, handler);
return;
}
}
AttributesImpl a = new AttributesImpl();
a.addAttribute("", "", "encoding", "string", "text");
handler.startElement("", "", "content", a);
handler.characters(cbuf, 0, cbuf.length);
handler.endElement("", "", "content");
return;
}
i = new ByteArrayInputStream(b);
r.setContentHandler(new PartialContentHandler(handler));
AttributesImpl a = new AttributesImpl();
a.addAttribute("", "", "encoding", "string", "xml");
handler.startElement("", "", "content", a);
InputSource in = new InputSource(i);
in.setSystemId("localhost");
r.parse(in);
handler.endElement("", "", "content");
}
}
/**
* Utility function that writes a Character SAX event with a base64 encoding
* of a byte array.
*
* @param b
* The byte array to be encoded.
* @param handler
* The handler to send SAX events to.
*/
protected void base64(byte[] b, SerializationHandler handler)
throws Exception
{
// [TODO: optimize this to a streaming version]
String s = new sun.misc.BASE64Encoder().encode(b);
AttributesImpl a = new AttributesImpl();
a.addAttribute("", "", "encoding", "string", "base64");
handler.startElement("", "", "content", a);
handler.characters(s.toCharArray(), 0, s.length());
handler.endElement("", "", "content");
}
public InputSource resolveEntity(String s1, String s2)
{
return defaultEntityResolver;
}
/**
* Utility ContentHandler that ignores startDocument and endDocument events.
*
* @author Robbert van Dalen
*/
private class PartialContentHandler extends DefaultHandler
{
private ContentHandler handler;
/**
* Constructor for the PartialContentHandler object
*
* @param h
* The handler that is wrapped.
*/
public PartialContentHandler(ContentHandler h)
{
handler = h;
}
/**
*/
public void setDocumentLocator(Locator locator)
{
handler.setDocumentLocator(locator);
}
/**
*/
public void startDocument() throws org.xml.sax.SAXException
{
// no
}
/**
*/
public void endDocument() throws org.xml.sax.SAXException
{
// no
}
/**
*/
public void startPrefixMapping(java.lang.String prefix,
java.lang.String namespaceUri) throws org.xml.sax.SAXException
{
handler.startPrefixMapping(prefix, namespaceUri);
}
/**
*/
public void endPrefixMapping(java.lang.String prefix)
throws org.xml.sax.SAXException
{
handler.endPrefixMapping(prefix);
}
/**
*/
public void startElement(String namespaceUri, String localName,
String qName, Attributes att) throws org.xml.sax.SAXException
{
handler.startElement(namespaceUri, localName, qName, att);
}
/**
*/
public void endElement(String namespaceUri, String localName,
String qName) throws org.xml.sax.SAXException
{
handler.endElement(namespaceUri, localName, qName);
}
/**
*/
public void characters(char[] arg1, int arg2, int arg3)
throws org.xml.sax.SAXException
{
handler.characters(arg1, arg2, arg3);
}
/**
*/
public void ignorableWhitespace(char[] arg1, int arg2, int arg3)
throws org.xml.sax.SAXException
{
handler.ignorableWhitespace(arg1, arg2, arg3);
}
/**
*/
public void processingInstruction(String target, String data)
throws org.xml.sax.SAXException
{
handler.processingInstruction(target, data);
}
/**
*/
public void skippedEntity(java.lang.String arg1)
throws org.xml.sax.SAXException
{
handler.skippedEntity(arg1);
}
}
}