diff --git a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcRequest.java b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcRequest.java
index 214f0ca..4f06405 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcRequest.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcRequest.java
@@ -20,12 +20,9 @@ package com.silverwrist.dynamo.xmlrpc;
import java.io.*;
import java.text.*;
import java.util.*;
-import javax.mail.*;
-import javax.mail.internet.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.w3c.dom.*;
-import com.silverwrist.util.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.RequestType;
import com.silverwrist.dynamo.Verb;
@@ -42,27 +39,6 @@ import com.silverwrist.dynamo.util.*;
*/
class XmlRpcRequest extends BaseDelegatingServiceProvider implements Request
{
- /*--------------------------------------------------------------------------------
- * Static data members
- *--------------------------------------------------------------------------------
- */
-
- private static final String EMPTY_STRING = "";
-
- // Indicates the types of values associated with the request.
- private static final int ITYP_INTEGER = 0; // integer
- private static final int ITYP_BOOLEAN = 1; // Boolean
- private static final int ITYP_STRING = 2; // string
- private static final int ITYP_DOUBLE = 3; // double
- private static final int ITYP_DATETIME = 4; // date/time
- private static final int ITYP_BINARY = 5; // binary (byte array)
- private static final int ITYP_STRUCT = 6; // struct (Map)
- private static final int ITYP_ARRAY = 7; // array (List)
-
- private static final Map MAP_TYPE; // maps element names to type values
-
- private static final DateFormat s_iso8601; // used to format and parse date/time values
-
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
@@ -120,6 +96,7 @@ class XmlRpcRequest extends BaseDelegatingServiceProvider implements Request
} // end catch
+ XmlRpcSerializer serializer = XmlRpcSerializer.get();
try
{ // load the XML body of the request, get the method name and parameters
Element method_call = loader.getRootElement(request_doc,"methodCall");
@@ -135,7 +112,7 @@ class XmlRpcRequest extends BaseDelegatingServiceProvider implements Request
{ // get the subelement from each and store it
Element elt = (Element)(it.next());
Element value_elt = loader.getSubElement(elt,"value");
- m_params.put(String.valueOf(ndx++),parseValue(value_elt));
+ m_params.put(String.valueOf(ndx++),serializer.deserialize(value_elt));
} // end while
@@ -155,274 +132,6 @@ class XmlRpcRequest extends BaseDelegatingServiceProvider implements Request
} // end constructor
- /*--------------------------------------------------------------------------------
- * Internal operations
- *--------------------------------------------------------------------------------
- */
-
- /**
- * Parses an XML-RPC <value/> element.
- *
- * @param elt The node coresponding to the <value/> element.
- * @return The parsed value.
- * @exception com.silverwrist.dynamo.xmlrpc.FaultCode If an error occurs in parsing the value.
- */
- private static final Object parseValue(Element elt) throws FaultCode
- {
- NodeList nl = elt.getChildNodes();
- Element type_spec = null;
- for (int i=0; i element");
- type_spec = (Element)n;
-
- } // end for
-
- Object rc = null;
- if (type_spec!=null)
- { // figure out what type this element is
- Integer tval = (Integer)(MAP_TYPE.get(type_spec.getTagName()));
- if (tval==null)
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid type \"" + type_spec.getTagName()
- + "\"");
- DOMElementHelper h = new DOMElementHelper(type_spec);
- switch (tval.intValue())
- { // based on the type, act on the contents
- case ITYP_INTEGER:
- try
- { // parse the integer value
- rc = new Integer(Integer.parseInt(h.getElementText(),10));
-
- } // end try
- catch (NumberFormatException e)
- { // value wasn't an integer
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid integer format",e);
-
- } // end catch
- catch (NullPointerException e)
- { // value was null
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"integer value not specified");
-
- } // end catch
- break;
-
- case ITYP_BOOLEAN:
- { // test the Boolean value
- String s = h.getElementText();
- if (s==null)
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"boolean value not specified");
- if (s.equals("1"))
- rc = Boolean.TRUE;
- else if (s.equals("0"))
- rc = Boolean.FALSE;
- else
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid boolean format");
-
- } // end case
- break;
-
- case ITYP_STRING:
- { // get the string value and return it
- rc = h.getElementText();
- if (rc==null)
- rc = EMPTY_STRING;
-
- } // end case
- break;
-
- case ITYP_DOUBLE:
- try
- { // convert to a Double value
- rc = new Double(h.getElementText());
-
- } // end try
- catch (NumberFormatException e)
- { // value wasn't an integer
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid double format",e);
-
- } // end catch
- catch (NullPointerException e)
- { // value was null
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"double value not specified");
-
- } // end catch
- break;
-
- case ITYP_DATETIME:
- try
- { // convert to a Date value
- rc = s_iso8601.parse(h.getElementText());
-
- } // end try
- catch (java.text.ParseException e)
- { // date couldn't be parsed
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid ISO 8601 date format",e);
-
- } // end catch
- catch (NullPointerException e)
- { // no value in there...
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"date value not specified");
-
- } // end catch
- break;
-
- case ITYP_BINARY:
- try
- { // get the string equivalent of the formatted data
- String s = h.getElementText();
- if (s==null)
- { // null string - return null array
- rc = new byte[0];
- break;
-
- } // end if
-
- // get a stream of encoded bytes
- ByteArrayInputStream encoded_stm = new ByteArrayInputStream(s.getBytes("US-ASCII"));
-
- // use the JavaMail MIME decoder to turn it into decoded bytes
- InputStream decoded_stm = MimeUtility.decode(encoded_stm,"base64");
-
- // copy the decoded bytes to a new array
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- IOUtils.copy(decoded_stm,output);
- IOUtils.shutdown(decoded_stm);
- IOUtils.shutdown(encoded_stm);
-
- // retrieve the output array
- rc = output.toByteArray();
- IOUtils.shutdown(output);
-
- } // end try
- catch (UnsupportedEncodingException e)
- { // WTF? shouldn't happen
- throw new SystemFaultCode(SystemFaultCode.INTERNAL_ERROR,"internal error: unsupported encoding",e);
-
- } // end catch
- catch (IOException e)
- { // some sort of error copying binary values around
- throw new SystemFaultCode(SystemFaultCode.INTERNAL_ERROR,"unable to get binary data",e);
-
- } // end catch
- catch (MessagingException e)
- { // error in the MIME parsing - dump it out
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid binary data format",e);
-
- } // end catch
- break;
-
- case ITYP_STRUCT:
- rc = parseStruct(type_spec);
- break;
-
- case ITYP_ARRAY:
- rc = parseArray(type_spec);
- break;
-
- default:
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid type \""
- + type_spec.getTagName() + "\"");
-
- } // end switch
-
- } // end if
- else
- { // if there's no type-specifying element, treat it as a String
- DOMElementHelper h = new DOMElementHelper(elt);
- rc = h.getElementText();
- if (rc==null)
- rc = EMPTY_STRING;
-
- } // end else
-
- return rc;
-
- } // end parseValue
-
- /**
- * Parses an XML-RPC <struct/> element.
- *
- * @param elt The node coresponding to the <struct/> element.
- * @return The parsed value.
- * @exception com.silverwrist.dynamo.xmlrpc.FaultCode If an error occurs in parsing the value.
- */
- private static final Map parseStruct(Element elt) throws FaultCode
- {
- XMLLoader loader = XMLLoader.get();
- HashMap rc = new HashMap();
- try
- { // get all sub-elements and process them
- List l = loader.getMatchingSubElements(elt,"member");
- Iterator it = l.iterator();
- while (it.hasNext())
- { // get each and add its and to the map
- Element x = (Element)(it.next());
- DOMElementHelper h = new DOMElementHelper(x);
- String name = loader.getSubElementText(h,"name");
- Element val_elt = loader.getSubElement(h,"value");
- rc.put(name,parseValue(val_elt));
-
- } // end while
-
- } // end try
- catch (XMLLoadException e)
- { // translate load exception
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,e);
-
- } // end catch
-
- if (rc.isEmpty())
- return Collections.EMPTY_MAP;
- else
- return Collections.unmodifiableMap(rc);
-
- } // end parseStruct
-
- /**
- * Parses an XML-RPC <array/> element.
- *
- * @param elt The node coresponding to the <array/> element.
- * @return The parsed value.
- * @exception com.silverwrist.dynamo.xmlrpc.FaultCode If an error occurs in parsing the value.
- */
- private static final List parseArray(Element elt) throws FaultCode
- {
- XMLLoader loader = XMLLoader.get();
- ArrayList rc = null;
- try
- { // get the sub-element
- Element data_elt = loader.getSubElement(elt,"data");
-
- // get the values it contains
- List l = loader.getMatchingSubElements(data_elt,"value");
- rc = new ArrayList(l.size());
- Iterator it = l.iterator();
- while (it.hasNext())
- { // parse the elements and add them to the list
- Element val_elt = (Element)(it.next());
- rc.add(parseValue(val_elt));
-
- } // end while
-
- } // end try
- catch (XMLLoadException e)
- { // translate load exception
- throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,e);
-
- } // end catch
-
- if ((rc==null) || rc.isEmpty())
- return Collections.EMPTY_LIST;
- else
- return Collections.unmodifiableList(rc);
-
- } // end parseArray
-
/*--------------------------------------------------------------------------------
* Implementations from interface ObjectProvider
*--------------------------------------------------------------------------------
@@ -670,31 +379,4 @@ class XmlRpcRequest extends BaseDelegatingServiceProvider implements Request
} // end getLocales
- /*--------------------------------------------------------------------------------
- * Static initializer
- *--------------------------------------------------------------------------------
- */
-
- static
- { // Initialize the type map.
- HashMap tmp = new HashMap();
- Integer foo = new Integer(ITYP_INTEGER);
- tmp.put("i4",foo);
- tmp.put("int",foo);
- tmp.put("boolean",new Integer(ITYP_BOOLEAN));
- tmp.put("string",new Integer(ITYP_STRING));
- tmp.put("double",new Integer(ITYP_DOUBLE));
- tmp.put("dateTime.iso8601",new Integer(ITYP_DATETIME));
- tmp.put("base64",new Integer(ITYP_BINARY));
- tmp.put("struct",new Integer(ITYP_STRUCT));
- tmp.put("array",new Integer(ITYP_ARRAY));
- MAP_TYPE = Collections.unmodifiableMap(tmp);
-
- // Initialize the ISO 8601 date formatter.
- SimpleDateFormat iso = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
- iso.setCalendar(new GregorianCalendar(new SimpleTimeZone(0,"UTC")));
- s_iso8601 = iso;
-
- } // end static initializer
-
} // end class XmlRpcRequest
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcResult.java b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcResult.java
index eaa8aec..bcf6dd0 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcResult.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcResult.java
@@ -11,40 +11,19 @@
*
* The Initial Developer of the Original Code is Eric J. Bowersox ,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
- * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
+ * Copyright (C) 2002-03 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
*
* Contributor(s):
*/
package com.silverwrist.dynamo.xmlrpc;
import java.io.*;
-import java.lang.ref.Reference;
-import java.sql.Blob;
-import java.sql.SQLException;
-import java.text.*;
-import java.util.*;
-import javax.mail.*;
-import javax.mail.internet.*;
-import com.silverwrist.util.*;
import com.silverwrist.dynamo.HttpStatusCode;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
public class XmlRpcResult implements SelfRenderable, XmlRpcSelfSerializing
{
- /*--------------------------------------------------------------------------------
- * Static data members
- *--------------------------------------------------------------------------------
- */
-
- private static final String SERIALIZED_NULL = "null";
- private static final String START_ARRAY = "\r\n";
- private static final String END_ARRAY = "";
- private static final String START_VALUE = "";
- private static final String END_VALUE = "\r\n";
-
- private static final DateFormat s_iso8601;
-
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
@@ -73,7 +52,7 @@ public class XmlRpcResult implements SelfRenderable, XmlRpcSelfSerializing
// Serialize the output value to a StringWriter.
StringWriter wr = new StringWriter();
wr.write("\r\n");
- serialize(wr,m_obj);
+ XmlRpcSerializer.get().serialize(wr,m_obj);
wr.write("\r\n\r\n");
// Now render the binary equivalent of this structure.
@@ -107,513 +86,8 @@ public class XmlRpcResult implements SelfRenderable, XmlRpcSelfSerializing
public void serializeXmlRpc(Writer wr) throws IOException
{
- serialize(wr,m_obj);
+ XmlRpcSerializer.get().serialize(wr,m_obj);
} // end serializeXmlRpc
- /*--------------------------------------------------------------------------------
- * External static operations
- *--------------------------------------------------------------------------------
- */
-
- public static void serializeBinary(Writer wr, InputStream stm) throws IOException
- {
- try
- { // Encode the data as BASE-64.
- ByteArrayOutputStream internal_stm = new ByteArrayOutputStream();
- OutputStream encode_stm = MimeUtility.encode(internal_stm,"base64");
- IOUtils.copy(stm,encode_stm);
- encode_stm.flush();
-
- // turn our encoded output into an InputStream
- ByteArrayInputStream internal2_stm = new ByteArrayInputStream(internal_stm.toByteArray());
- IOUtils.shutdown(encode_stm);
- IOUtils.shutdown(internal_stm);
-
- // write out the data
- InputStreamReader rd = new InputStreamReader(internal2_stm,"US-ASCII");
- wr.write("");
- IOUtils.copy(rd,wr);
- wr.write("");
- IOUtils.shutdown(rd);
- IOUtils.shutdown(internal2_stm);
-
- } // end try
- catch (MessagingException e)
- { // encoder might have an error
- throw new IOException("error encoding binary data");
-
- } // end catch
-
- } // end serializeBinary
-
- public static void serialize(Writer wr, boolean b) throws IOException
- {
- wr.write("");
- wr.write(b ? "1" : "0");
- wr.write("");
-
- } // end serialize
-
- public static void serialize(Writer wr, boolean[] b) throws IOException
- {
- wr.write(START_ARRAY);
- for (int i=0; i");
- wr.write(c);
- wr.write("");
-
- } // end serialize
-
- public static void serialize(Writer wr, char[] c) throws IOException
- {
- wr.write(START_ARRAY);
- for (int i=0; i");
- wr.write(String.valueOf(i));
- wr.write("");
-
- } // end serialize
-
- public static void serialize(Writer wr, int[] ia) throws IOException
- {
- wr.write(START_ARRAY);
- for (int i=0; i");
- wr.write(String.valueOf(d));
- wr.write("");
-
- } // end serialize
-
- public static void serialize(Writer wr, double[] d) throws IOException
- {
- wr.write(START_ARRAY);
- for (int i=0; i");
-
- // Encode the year first.
- StringBuffer conv = new StringBuffer("0000");
- conv.append(cal.get(Calendar.YEAR));
- String c = conv.toString();
- wr.write(c.substring(c.length()-4));
-
- // Now the month...
- conv.setLength(0);
- conv.append("00").append(cal.get(Calendar.MONTH) - Calendar.JANUARY + 1);
- c = conv.toString();
- wr.write(c.substring(c.length()-2));
-
- // And the day...
- conv.setLength(0);
- conv.append("00").append(cal.get(Calendar.DAY_OF_MONTH));
- c = conv.toString();
- wr.write(c.substring(c.length()-2));
- wr.write("T");
-
- // And the hour...
- conv.setLength(0);
- conv.append("00").append(cal.get(Calendar.HOUR_OF_DAY));
- c = conv.toString();
- wr.write(c.substring(c.length()-2));
- wr.write(":");
-
- // And the minute...
- conv.setLength(0);
- conv.append("00").append(cal.get(Calendar.MINUTE));
- c = conv.toString();
- wr.write(c.substring(c.length()-2));
- wr.write(":");
-
- // And the second...
- conv.setLength(0);
- conv.append("00").append(cal.get(Calendar.SECOND));
- c = conv.toString();
- wr.write(c.substring(c.length()-2));
-
- // And we're done!
- wr.write("");
-
- } // end serializeCalendar
-
- public static void serializeCollection(Writer wr, Collection coll) throws IOException
- {
- serializeIterator(wr,coll.iterator());
-
- } // end serializeCollection
-
- public static void serializeDate(Writer wr, Date d) throws IOException
- {
- wr.write("");
- wr.write(s_iso8601.format(d));
- wr.write("");
-
- } // end serializeDate
-
- public static void serializeEnumeration(Writer wr, Enumeration enum) throws IOException
- {
- wr.write(START_ARRAY);
- while (enum.hasMoreElements())
- { // serialize the values we contain
- wr.write(START_VALUE);
- serialize(wr,enum.nextElement());
- wr.write(END_VALUE);
-
- } // end for
-
- wr.write(END_ARRAY);
-
- } // end serializeIterator
-
- public static void serializeIterator(Writer wr, Iterator it) throws IOException
- {
- wr.write(START_ARRAY);
- while (it.hasNext())
- { // serialize the values we contain
- wr.write(START_VALUE);
- serialize(wr,it.next());
- wr.write(END_VALUE);
-
- } // end for
-
- wr.write(END_ARRAY);
-
- } // end serializeIterator
-
- public static void serializeMap(Writer wr, Map map) throws IOException
- {
- wr.write("\r\n");
- Iterator it = map.entrySet().iterator();
- while (it.hasNext())
- { // write each entry in turn
- Map.Entry ntry = (Map.Entry)(it.next());
- wr.write("\r\n");
- wr.write(StringUtils.encodeHTML(ntry.getKey().toString()));
- wr.write("\r\n");
- serialize(wr,ntry.getValue());
- wr.write("\r\n");
-
- } // end while
-
- wr.write("\r\n");
-
- } // end serializeMap
-
- public static void serializeString(Writer wr, String s) throws IOException
- {
- wr.write("");
- wr.write(StringUtils.encodeHTML(s));
- wr.write("");
-
- } // end serializeString
-
- public static void serialize(Writer wr, Object obj) throws IOException
- {
- if (obj==null)
- { // null object - bye!
- wr.write(SERIALIZED_NULL);
- return;
-
- } // end if
-
- if (obj.getClass().isArray())
- { // for arrays, serialize them specially
- Class component = obj.getClass().getComponentType();
- if (component==Boolean.TYPE)
- serialize(wr,(boolean[])obj);
- else if (component==Byte.TYPE)
- serialize(wr,(byte[])obj);
- else if (component==Character.TYPE)
- serialize(wr,(char[])obj);
- else if (component==Short.TYPE)
- serialize(wr,(short[])obj);
- else if (component==Integer.TYPE)
- serialize(wr,(int[])obj);
- else if (component==Long.TYPE)
- serialize(wr,(long[])obj);
- else if (component==Float.TYPE)
- serialize(wr,(float[])obj);
- else if (component==Double.TYPE)
- serialize(wr,(double[])obj);
- else
- serialize(wr,(Object[])obj);
- return;
-
- } // end if
-
- if (obj instanceof XmlRpcSelfSerializing)
- { // some objects may be self-serializing
- ((XmlRpcSelfSerializing)obj).serializeXmlRpc(wr);
- return;
-
- } // end if
-
- if (obj instanceof DynamicWrapper)
- { // for dynamic wrappers, unwrap them
- serialize(wr,((DynamicWrapper)obj).unwrap());
- return;
-
- } // end if
-
- if (obj instanceof InputStream)
- { // for InputStream, turn it into binary
- serializeBinary(wr,(InputStream)obj);
- return;
-
- } // end if
-
- if (obj instanceof Blob)
- { // serialize the blob as a binary stream
- try
- { // just get its input stream
- serializeBinary(wr,((Blob)obj).getBinaryStream());
- return;
-
- } // end try
- catch (SQLException e)
- { // fault on error here
- throw new IOException("error writing binary data");
-
- } // end catch
-
- } // end if
-
- if (obj instanceof Calendar)
- { // serialize a Calendar
- serializeCalendar(wr,(Calendar)obj);
- return;
-
- } // end if
-
- if (obj instanceof Collection)
- { // serialize a Collection
- serializeCollection(wr,(Collection)obj);
- return;
-
- } // end if
-
- if (obj instanceof Date)
- { // serialize a Date
- serializeDate(wr,(Date)obj);
- return;
-
- } // end if
-
- if (obj instanceof Enumeration)
- { // serialize a Enumeration
- serializeEnumeration(wr,(Enumeration)obj);
- return;
-
- } // end if
-
- if (obj instanceof Iterator)
- { // serialize a Iterator
- serializeIterator(wr,(Iterator)obj);
- return;
-
- } // end if
-
- if (obj instanceof Map)
- { // serialize a Map
- serializeMap(wr,(Map)obj);
- return;
-
- } // end if
-
- if (obj instanceof Reference)
- { // for a Reference, serialize the referent
- serialize(wr,((Reference)obj).get());
- return;
-
- } // end if
-
- if (obj instanceof Boolean)
- { // serialize the boolean value
- serialize(wr,((Boolean)obj).booleanValue());
- return;
-
- } // end if
-
- if (obj instanceof Character)
- { // serialize the character value
- serialize(wr,((Character)obj).charValue());
- return;
-
- } // end if
-
- if ((obj instanceof Byte) || (obj instanceof Short) || (obj instanceof Integer))
- { // serialize the integer value
- serialize(wr,((Number)obj).intValue());
- return;
-
- } // end if
-
- if ((obj instanceof Long) || (obj instanceof Float) || (obj instanceof Double))
- { // serialize the double value
- serialize(wr,((Number)obj).doubleValue());
- return;
-
- } // end if
-
- // String and StringBuffer are handled properly by the fallback mechanism below
-
- // last-ditch fallback method - use toString, get the string, and encode it
- serializeString(wr,obj.toString());
-
- } // end serialize
-
- public static void serialize(Writer wr, Object[] arr) throws IOException
- {
- wr.write(START_ARRAY);
- for (int i=0; i.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
+ * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
+ * language governing rights and limitations under the License.
+ *
+ * The Original Code is the Venice Web Communities System.
+ *
+ * The Initial Developer of the Original Code is Eric J. Bowersox ,
+ * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
+ * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
+ *
+ * Contributor(s):
+ */
+package com.silverwrist.dynamo.xmlrpc;
+
+import java.io.*;
+import java.lang.ref.Reference;
+import java.sql.Blob;
+import java.sql.SQLException;
+import java.text.*;
+import java.util.*;
+import javax.mail.*;
+import javax.mail.internet.*;
+import org.w3c.dom.*;
+import com.silverwrist.util.*;
+import com.silverwrist.util.xml.*;
+import com.silverwrist.dynamo.except.*;
+import com.silverwrist.dynamo.iface.*;
+
+public class XmlRpcSerializer
+{
+ /*--------------------------------------------------------------------------------
+ * Static data members
+ *--------------------------------------------------------------------------------
+ */
+
+ private static XmlRpcSerializer _self = null;
+
+ private static final String SERIALIZED_NULL = "null";
+ private static final String START_ARRAY = "\r\n";
+ private static final String END_ARRAY = "";
+ private static final String START_VALUE = "";
+ private static final String END_VALUE = "\r\n";
+
+ // Indicates the types of values associated with the request.
+ private static final int ITYP_INTEGER = 0; // integer
+ private static final int ITYP_BOOLEAN = 1; // Boolean
+ private static final int ITYP_STRING = 2; // string
+ private static final int ITYP_DOUBLE = 3; // double
+ private static final int ITYP_DATETIME = 4; // date/time
+ private static final int ITYP_BINARY = 5; // binary (byte array)
+ private static final int ITYP_STRUCT = 6; // struct (Map)
+ private static final int ITYP_ARRAY = 7; // array (List)
+
+ private static final String EMPTY_STRING = "";
+
+ /*--------------------------------------------------------------------------------
+ * Attributes
+ *--------------------------------------------------------------------------------
+ */
+
+ private final DateFormat m_iso8601; // the ISO 8601 date format
+ private final Map m_type_map; // mapping of type values to indices
+
+ /*--------------------------------------------------------------------------------
+ * Constructor
+ *--------------------------------------------------------------------------------
+ */
+
+ private XmlRpcSerializer()
+ {
+ // Initialize the ISO 8601 date formatter.
+ SimpleDateFormat iso = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+ iso.setCalendar(new GregorianCalendar(new SimpleTimeZone(0,"UTC")));
+ m_iso8601 = iso;
+
+ // Initialize the type map.
+ HashMap tmp = new HashMap();
+ Integer foo = new Integer(ITYP_INTEGER);
+ tmp.put("i4",foo);
+ tmp.put("int",foo);
+ tmp.put("boolean",new Integer(ITYP_BOOLEAN));
+ tmp.put("string",new Integer(ITYP_STRING));
+ tmp.put("double",new Integer(ITYP_DOUBLE));
+ tmp.put("dateTime.iso8601",new Integer(ITYP_DATETIME));
+ tmp.put("base64",new Integer(ITYP_BINARY));
+ tmp.put("struct",new Integer(ITYP_STRUCT));
+ tmp.put("array",new Integer(ITYP_ARRAY));
+ m_type_map = Collections.unmodifiableMap(tmp);
+
+ } // end constructor
+
+ /*--------------------------------------------------------------------------------
+ * Internal operations
+ *--------------------------------------------------------------------------------
+ */
+
+ /**
+ * Deserializes an XML-RPC <struct/> element.
+ *
+ * @param elt The node coresponding to the <struct/> element.
+ * @return The parsed value.
+ * @exception com.silverwrist.dynamo.xmlrpc.FaultCode If an error occurs in parsing the value.
+ */
+ private final Map deserializeStruct(Element elt) throws FaultCode
+ {
+ XMLLoader loader = XMLLoader.get();
+ HashMap rc = new HashMap();
+ try
+ { // get all sub-elements and process them
+ List l = loader.getMatchingSubElements(elt,"member");
+ Iterator it = l.iterator();
+ while (it.hasNext())
+ { // get each and add its and to the map
+ Element x = (Element)(it.next());
+ DOMElementHelper h = new DOMElementHelper(x);
+ String name = loader.getSubElementText(h,"name");
+ Element val_elt = loader.getSubElement(h,"value");
+ rc.put(name,deserialize(val_elt));
+
+ } // end while
+
+ } // end try
+ catch (XMLLoadException e)
+ { // translate load exception
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,e);
+
+ } // end catch
+
+ if (rc.isEmpty())
+ return Collections.EMPTY_MAP;
+ else
+ return Collections.unmodifiableMap(rc);
+
+ } // end deserializeStruct
+
+ /**
+ * Deserializes an XML-RPC <array/> element.
+ *
+ * @param elt The node coresponding to the <array/> element.
+ * @return The parsed value.
+ * @exception com.silverwrist.dynamo.xmlrpc.FaultCode If an error occurs in parsing the value.
+ */
+ private final List deserializeArray(Element elt) throws FaultCode
+ {
+ XMLLoader loader = XMLLoader.get();
+ ArrayList rc = null;
+ try
+ { // get the sub-element
+ Element data_elt = loader.getSubElement(elt,"data");
+
+ // get the values it contains
+ List l = loader.getMatchingSubElements(data_elt,"value");
+ rc = new ArrayList(l.size());
+ Iterator it = l.iterator();
+ while (it.hasNext())
+ { // parse the elements and add them to the list
+ Element val_elt = (Element)(it.next());
+ rc.add(deserialize(val_elt));
+
+ } // end while
+
+ } // end try
+ catch (XMLLoadException e)
+ { // translate load exception
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,e);
+
+ } // end catch
+
+ if ((rc==null) || rc.isEmpty())
+ return Collections.EMPTY_LIST;
+ else
+ return Collections.unmodifiableList(rc);
+
+ } // end deserializeArray
+
+ /*--------------------------------------------------------------------------------
+ * External operations
+ *--------------------------------------------------------------------------------
+ */
+
+ public void serializeBinary(Writer wr, InputStream stm) throws IOException
+ {
+ try
+ { // Encode the data as BASE-64.
+ ByteArrayOutputStream internal_stm = new ByteArrayOutputStream();
+ OutputStream encode_stm = MimeUtility.encode(internal_stm,"base64");
+ IOUtils.copy(stm,encode_stm);
+ encode_stm.flush();
+
+ // turn our encoded output into an InputStream
+ ByteArrayInputStream internal2_stm = new ByteArrayInputStream(internal_stm.toByteArray());
+ IOUtils.shutdown(encode_stm);
+ IOUtils.shutdown(internal_stm);
+
+ // write out the data
+ InputStreamReader rd = new InputStreamReader(internal2_stm,"US-ASCII");
+ wr.write("");
+ IOUtils.copy(rd,wr);
+ wr.write("");
+ IOUtils.shutdown(rd);
+ IOUtils.shutdown(internal2_stm);
+
+ } // end try
+ catch (MessagingException e)
+ { // encoder might have an error
+ throw new IOException("error encoding binary data");
+
+ } // end catch
+
+ } // end serializeBinary
+
+ public void serialize(Writer wr, boolean b) throws IOException
+ {
+ wr.write("");
+ wr.write(b ? "1" : "0");
+ wr.write("");
+
+ } // end serialize
+
+ public void serialize(Writer wr, boolean[] b) throws IOException
+ {
+ wr.write(START_ARRAY);
+ for (int i=0; i");
+ wr.write(String.valueOf(i));
+ wr.write("");
+
+ } // end serialize
+
+ public void serialize(Writer wr, int[] ia) throws IOException
+ {
+ wr.write(START_ARRAY);
+ for (int i=0; i");
+ wr.write(String.valueOf(d));
+ wr.write("");
+
+ } // end serialize
+
+ public void serialize(Writer wr, double[] d) throws IOException
+ {
+ wr.write(START_ARRAY);
+ for (int i=0; i");
+
+ // Encode the year first.
+ StringBuffer conv = new StringBuffer("0000");
+ conv.append(cal.get(Calendar.YEAR));
+ String c = conv.toString();
+ wr.write(c.substring(c.length()-4));
+
+ // Now the month...
+ conv.setLength(0);
+ conv.append("00").append(cal.get(Calendar.MONTH) - Calendar.JANUARY + 1);
+ c = conv.toString();
+ wr.write(c.substring(c.length()-2));
+
+ // And the day...
+ conv.setLength(0);
+ conv.append("00").append(cal.get(Calendar.DAY_OF_MONTH));
+ c = conv.toString();
+ wr.write(c.substring(c.length()-2));
+ wr.write("T");
+
+ // And the hour...
+ conv.setLength(0);
+ conv.append("00").append(cal.get(Calendar.HOUR_OF_DAY));
+ c = conv.toString();
+ wr.write(c.substring(c.length()-2));
+ wr.write(":");
+
+ // And the minute...
+ conv.setLength(0);
+ conv.append("00").append(cal.get(Calendar.MINUTE));
+ c = conv.toString();
+ wr.write(c.substring(c.length()-2));
+ wr.write(":");
+
+ // And the second...
+ conv.setLength(0);
+ conv.append("00").append(cal.get(Calendar.SECOND));
+ c = conv.toString();
+ wr.write(c.substring(c.length()-2));
+
+ // And we're done!
+ wr.write("");
+
+ } // end serializeCalendar
+
+ public void serializeCollection(Writer wr, Collection coll) throws IOException
+ {
+ serializeIterator(wr,coll.iterator());
+
+ } // end serializeCollection
+
+ public void serializeDate(Writer wr, Date d) throws IOException
+ {
+ wr.write("");
+ wr.write(m_iso8601.format(d));
+ wr.write("");
+
+ } // end serializeDate
+
+ public void serializeEnumeration(Writer wr, Enumeration enum) throws IOException
+ {
+ wr.write(START_ARRAY);
+ while (enum.hasMoreElements())
+ { // serialize the values we contain
+ wr.write(START_VALUE);
+ serialize(wr,enum.nextElement());
+ wr.write(END_VALUE);
+
+ } // end for
+
+ wr.write(END_ARRAY);
+
+ } // end serializeIterator
+
+ public void serializeIterator(Writer wr, Iterator it) throws IOException
+ {
+ wr.write(START_ARRAY);
+ while (it.hasNext())
+ { // serialize the values we contain
+ wr.write(START_VALUE);
+ serialize(wr,it.next());
+ wr.write(END_VALUE);
+
+ } // end for
+
+ wr.write(END_ARRAY);
+
+ } // end serializeIterator
+
+ public void serializeMap(Writer wr, Map map) throws IOException
+ {
+ wr.write("\r\n");
+ Iterator it = map.entrySet().iterator();
+ while (it.hasNext())
+ { // write each entry in turn
+ Map.Entry ntry = (Map.Entry)(it.next());
+ wr.write("\r\n");
+ wr.write(StringUtils.encodeHTML(ntry.getKey().toString()));
+ wr.write("\r\n");
+ serialize(wr,ntry.getValue());
+ wr.write("\r\n");
+
+ } // end while
+
+ wr.write("\r\n");
+
+ } // end serializeMap
+
+ public void serializeString(Writer wr, String s) throws IOException
+ {
+ wr.write("");
+ wr.write(StringUtils.encodeHTML(s));
+ wr.write("");
+
+ } // end serializeString
+
+ public void serialize(Writer wr, Object obj) throws IOException
+ {
+ if (obj==null)
+ { // null object - bye!
+ wr.write(SERIALIZED_NULL);
+ return;
+
+ } // end if
+
+ if (obj.getClass().isArray())
+ { // for arrays, serialize them specially
+ Class component = obj.getClass().getComponentType();
+ if (component==Boolean.TYPE)
+ serialize(wr,(boolean[])obj);
+ else if (component==Byte.TYPE)
+ serialize(wr,(byte[])obj);
+ else if (component==Character.TYPE)
+ serialize(wr,(char[])obj);
+ else if (component==Short.TYPE)
+ serialize(wr,(short[])obj);
+ else if (component==Integer.TYPE)
+ serialize(wr,(int[])obj);
+ else if (component==Long.TYPE)
+ serialize(wr,(long[])obj);
+ else if (component==Float.TYPE)
+ serialize(wr,(float[])obj);
+ else if (component==Double.TYPE)
+ serialize(wr,(double[])obj);
+ else
+ serialize(wr,(Object[])obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof XmlRpcSelfSerializing)
+ { // some objects may be self-serializing
+ ((XmlRpcSelfSerializing)obj).serializeXmlRpc(wr);
+ return;
+
+ } // end if
+
+ if (obj instanceof DynamicWrapper)
+ { // for dynamic wrappers, unwrap them
+ serialize(wr,((DynamicWrapper)obj).unwrap());
+ return;
+
+ } // end if
+
+ if (obj instanceof InputStream)
+ { // for InputStream, turn it into binary
+ serializeBinary(wr,(InputStream)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Blob)
+ { // serialize the blob as a binary stream
+ try
+ { // just get its input stream
+ serializeBinary(wr,((Blob)obj).getBinaryStream());
+ return;
+
+ } // end try
+ catch (SQLException e)
+ { // fault on error here
+ throw new IOException("error writing binary data");
+
+ } // end catch
+
+ } // end if
+
+ if (obj instanceof Calendar)
+ { // serialize a Calendar
+ serializeCalendar(wr,(Calendar)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Collection)
+ { // serialize a Collection
+ serializeCollection(wr,(Collection)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Date)
+ { // serialize a Date
+ serializeDate(wr,(Date)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Enumeration)
+ { // serialize a Enumeration
+ serializeEnumeration(wr,(Enumeration)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Iterator)
+ { // serialize a Iterator
+ serializeIterator(wr,(Iterator)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Map)
+ { // serialize a Map
+ serializeMap(wr,(Map)obj);
+ return;
+
+ } // end if
+
+ if (obj instanceof Reference)
+ { // for a Reference, serialize the referent
+ serialize(wr,((Reference)obj).get());
+ return;
+
+ } // end if
+
+ if (obj instanceof Boolean)
+ { // serialize the boolean value
+ serialize(wr,((Boolean)obj).booleanValue());
+ return;
+
+ } // end if
+
+ if (obj instanceof Character)
+ { // serialize the character value
+ serialize(wr,((Character)obj).charValue());
+ return;
+
+ } // end if
+
+ if ((obj instanceof Byte) || (obj instanceof Short) || (obj instanceof Integer))
+ { // serialize the integer value
+ serialize(wr,((Number)obj).intValue());
+ return;
+
+ } // end if
+
+ if ((obj instanceof Long) || (obj instanceof Float) || (obj instanceof Double))
+ { // serialize the double value
+ serialize(wr,((Number)obj).doubleValue());
+ return;
+
+ } // end if
+
+ // String and StringBuffer are handled properly by the fallback mechanism below
+
+ // last-ditch fallback method - use toString, get the string, and encode it
+ serializeString(wr,obj.toString());
+
+ } // end serialize
+
+ public void serialize(Writer wr, Object[] arr) throws IOException
+ {
+ wr.write(START_ARRAY);
+ for (int i=0; i element");
+ type_spec = (Element)n;
+
+ } // end for
+
+ Object rc = null;
+ if (type_spec!=null)
+ { // figure out what type this element is
+ Integer tval = (Integer)(m_type_map.get(type_spec.getTagName()));
+ if (tval==null)
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid type \"" + type_spec.getTagName() + "\"");
+ DOMElementHelper h = new DOMElementHelper(type_spec);
+ switch (tval.intValue())
+ { // based on the type, act on the contents
+ case ITYP_INTEGER:
+ try
+ { // parse the integer value
+ rc = new Integer(Integer.parseInt(h.getElementText(),10));
+
+ } // end try
+ catch (NumberFormatException e)
+ { // value wasn't an integer
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid integer format",e);
+
+ } // end catch
+ catch (NullPointerException e)
+ { // value was null
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"integer value not specified");
+
+ } // end catch
+ break;
+
+ case ITYP_BOOLEAN:
+ { // test the Boolean value
+ String s = h.getElementText();
+ if (s==null)
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"boolean value not specified");
+ if (s.equals("1"))
+ rc = Boolean.TRUE;
+ else if (s.equals("0"))
+ rc = Boolean.FALSE;
+ else
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid boolean format");
+
+ } // end case
+ break;
+
+ case ITYP_STRING:
+ { // get the string value and return it
+ rc = h.getElementText();
+ if (rc==null)
+ rc = EMPTY_STRING;
+
+ } // end case
+ break;
+
+ case ITYP_DOUBLE:
+ try
+ { // convert to a Double value
+ rc = new Double(h.getElementText());
+
+ } // end try
+ catch (NumberFormatException e)
+ { // value wasn't an integer
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid double format",e);
+
+ } // end catch
+ catch (NullPointerException e)
+ { // value was null
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"double value not specified");
+
+ } // end catch
+ break;
+
+ case ITYP_DATETIME:
+ try
+ { // convert to a Date value
+ rc = m_iso8601.parse(h.getElementText());
+
+ } // end try
+ catch (java.text.ParseException e)
+ { // date couldn't be parsed
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid ISO 8601 date format",e);
+
+ } // end catch
+ catch (NullPointerException e)
+ { // no value in there...
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"date value not specified");
+
+ } // end catch
+ break;
+
+ case ITYP_BINARY:
+ try
+ { // get the string equivalent of the formatted data
+ String s = h.getElementText();
+ if (s==null)
+ { // null string - return null array
+ rc = new byte[0];
+ break;
+
+ } // end if
+
+ // get a stream of encoded bytes
+ ByteArrayInputStream encoded_stm = new ByteArrayInputStream(s.getBytes("US-ASCII"));
+
+ // use the JavaMail MIME decoder to turn it into decoded bytes
+ InputStream decoded_stm = MimeUtility.decode(encoded_stm,"base64");
+
+ // copy the decoded bytes to a new array
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ IOUtils.copy(decoded_stm,output);
+ IOUtils.shutdown(decoded_stm);
+ IOUtils.shutdown(encoded_stm);
+
+ // retrieve the output array
+ rc = output.toByteArray();
+ IOUtils.shutdown(output);
+
+ } // end try
+ catch (UnsupportedEncodingException e)
+ { // WTF? shouldn't happen
+ throw new SystemFaultCode(SystemFaultCode.INTERNAL_ERROR,"internal error: unsupported encoding",e);
+
+ } // end catch
+ catch (IOException e)
+ { // some sort of error copying binary values around
+ throw new SystemFaultCode(SystemFaultCode.INTERNAL_ERROR,"unable to get binary data",e);
+
+ } // end catch
+ catch (MessagingException e)
+ { // error in the MIME parsing - dump it out
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid binary data format",e);
+
+ } // end catch
+ break;
+
+ case ITYP_STRUCT:
+ rc = deserializeStruct(type_spec);
+ break;
+
+ case ITYP_ARRAY:
+ rc = deserializeArray(type_spec);
+ break;
+
+ default:
+ throw new SystemFaultCode(SystemFaultCode.INVALID_REQUEST,"invalid type \""
+ + type_spec.getTagName() + "\"");
+
+ } // end switch
+
+ } // end if
+ else
+ { // if there's no type-specifying element, treat it as a String
+ DOMElementHelper h = new DOMElementHelper(elt);
+ rc = h.getElementText();
+ if (rc==null)
+ rc = EMPTY_STRING;
+
+ } // end else
+
+ return rc;
+
+ } // end deserialize
+
+ /*--------------------------------------------------------------------------------
+ * External static operations
+ *--------------------------------------------------------------------------------
+ */
+
+ public static XmlRpcSerializer get()
+ {
+ if (_self==null)
+ _self = new XmlRpcSerializer();
+ return _self;
+
+ } // end get
+
+} // end class XmlRpcSerializer