Browse Source

Merge pull request #350 from fbarriga/fbarriga/axmlrpc_update

rtorrent: axmlrpc updated
pull/311/merge
Eric Kok 8 years ago committed by GitHub
parent
commit
ad7ac3efc6
  1. 125
      app/src/main/java/de/timroes/axmlrpc/ResponseParser.java
  2. 8
      app/src/main/java/de/timroes/axmlrpc/XMLRPCClient.java
  3. 99
      app/src/main/java/de/timroes/axmlrpc/XMLUtil.java
  4. 38
      app/src/main/java/de/timroes/axmlrpc/serializer/ArraySerializer.java
  5. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/Base64Serializer.java
  6. 7
      app/src/main/java/de/timroes/axmlrpc/serializer/BooleanSerializer.java
  7. 15
      app/src/main/java/de/timroes/axmlrpc/serializer/DateTimeSerializer.java
  8. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/DoubleSerializer.java
  9. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/IntSerializer.java
  10. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/LongSerializer.java
  11. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/NullSerializer.java
  12. 11
      app/src/main/java/de/timroes/axmlrpc/serializer/Serializer.java
  13. 191
      app/src/main/java/de/timroes/axmlrpc/serializer/SerializerHandler.java
  14. 13
      app/src/main/java/de/timroes/axmlrpc/serializer/StringSerializer.java
  15. 63
      app/src/main/java/de/timroes/axmlrpc/serializer/StructSerializer.java

125
app/src/main/java/de/timroes/axmlrpc/ResponseParser.java

@ -1,12 +1,15 @@
package de.timroes.axmlrpc; package de.timroes.axmlrpc;
import de.timroes.axmlrpc.serializer.SerializerHandler; import de.timroes.axmlrpc.serializer.SerializerHandler;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Map; import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import org.apache.http.HttpEntity;
import org.w3c.dom.Document; import org.xmlpull.v1.XmlPullParser;
import org.w3c.dom.Element; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
/** /**
* The ResponseParser parses the response of an XMLRPC server to an object. * The ResponseParser parses the response of an XMLRPC server to an object.
@ -18,6 +21,30 @@ class ResponseParser {
private static final String FAULT_CODE = "faultCode"; private static final String FAULT_CODE = "faultCode";
private static final String FAULT_STRING = "faultString"; private static final String FAULT_STRING = "faultString";
/**
* Deallocate Http Entity and close streams
*/
private static void consumeHttpEntity(InputStream response, HttpEntity entity) {
// Ideally we should use EntityUtils.consume(), introduced in apache http utils 4.1 - not available in
// Android yet
if (entity != null) {
try {
entity.consumeContent();
} catch (IOException e) {
// ignore exception (could happen if Content-Length is wrong)
}
}
if (response != null) {
try {
response.close();
} catch (Exception e) {
// ignore exception
}
}
}
/** /**
* The given InputStream must contain the xml response from an xmlrpc server. * The given InputStream must contain the xml response from an xmlrpc server.
* This method extract the content of it as an object. * This method extract the content of it as an object.
@ -27,70 +54,50 @@ class ResponseParser {
* @throws XMLRPCException Will be thrown whenever something fails. * @throws XMLRPCException Will be thrown whenever something fails.
* @throws XMLRPCServerException Will be thrown, if the server returns an error. * @throws XMLRPCServerException Will be thrown, if the server returns an error.
*/ */
public Object parse(InputStream response) throws XMLRPCException { public Object parse(InputStream response, HttpEntity entity) throws XMLRPCException {
try { try {
XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); pullParser.setInput(response, "UTF-8");
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder(); pullParser.nextTag();
Document dom = builder.parse(response); pullParser.require(XmlPullParser.START_TAG, null, XMLRPCClient.METHOD_RESPONSE);
Element e = dom.getDocumentElement();
pullParser.nextTag(); // either TAG_PARAMS (<params>) or TAG_FAULT (<fault>)
// Check for root tag String tag = pullParser.getName();
if(!e.getNodeName().equals(XMLRPCClient.METHOD_RESPONSE)) { if (tag.equals(XMLRPCClient.PARAMS)) {
throw new XMLRPCException("MethodResponse root tag is missing."); // normal response
} pullParser.nextTag(); // TAG_PARAM (<param>)
pullParser.require(XmlPullParser.START_TAG, null, XMLRPCClient.PARAM);
e = XMLUtil.getOnlyChildElement(e.getChildNodes()); pullParser.nextTag(); // TAG_VALUE (<value>)
// no parser.require() here since its called in XMLRPCSerializer.deserialize() below
if(e.getNodeName().equals(XMLRPCClient.PARAMS)) { // deserialize result
Object obj = SerializerHandler.getDefault().deserialize(pullParser);
e = XMLUtil.getOnlyChildElement(e.getChildNodes()); consumeHttpEntity(response, entity);
return obj;
if(!e.getNodeName().equals(XMLRPCClient.PARAM)) { } else if (tag.equals(XMLRPCClient.FAULT)) {
throw new XMLRPCException("The params tag must contain a param tag."); // fault response
pullParser.nextTag(); // TAG_VALUE (<value>)
Map<String, Object> map = (Map<String, Object>) SerializerHandler.getDefault().deserialize(pullParser);
consumeHttpEntity(response, entity);
//Check that required tags are in the response
if (!map.containsKey(FAULT_STRING) || !map.containsKey(FAULT_CODE)) {
throw new XMLRPCException("Bad XMLRPC Fault response received - <faultCode> and/or <faultString> missing!");
} }
throw new XMLRPCServerException((String) map.get(FAULT_STRING), (Integer) map.get(FAULT_CODE));
return getReturnValueFromElement(e); } else {
throw new XMLRPCException("Bad tag <" + tag + "> in XMLRPC response - neither <params> nor <fault>");
} else if(e.getNodeName().equals(XMLRPCClient.FAULT)) {
@SuppressWarnings("unchecked")
Map<String,Object> o = (Map<String,Object>)getReturnValueFromElement(e);
throw new XMLRPCServerException((String)o.get(FAULT_STRING), (Integer)o.get(FAULT_CODE));
} }
throw new XMLRPCException("The methodResponse tag must contain a fault or params tag."); } catch (XmlPullParserException ex) {
consumeHttpEntity(response, entity);
throw new XMLRPCException("Error parsing response.", ex);
} catch (Exception ex) { } catch (Exception ex) {
consumeHttpEntity(response, entity);
if(ex instanceof XMLRPCServerException) if(ex instanceof XMLRPCServerException)
throw (XMLRPCServerException)ex; throw (XMLRPCServerException)ex;
else else
throw new XMLRPCException("Error getting result from server.", ex); throw new XMLRPCException("Error getting result from server.", ex);
} }
} }
/**
* This method takes an element (must be a param or fault element) and
* returns the deserialized object of this param tag.
*
* @param element An param element.
* @return The deserialized object within the given param element.
* @throws XMLRPCException Will be thrown when the structure of the document
* doesn't match the XML-RPC specification.
*/
private Object getReturnValueFromElement(Element element) throws XMLRPCException {
element = XMLUtil.getOnlyChildElement(element.getChildNodes());
return SerializerHandler.getDefault().deserialize(element);
}
} }

8
app/src/main/java/de/timroes/axmlrpc/XMLRPCClient.java

@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
@ -356,7 +357,7 @@ public class XMLRPCClient {
* Read the README file delivered with the source code of this library for more * Read the README file delivered with the source code of this library for more
* information. * information.
* *
* @param method A method name to call. * @param methodName A method name to call.
* @param params An array of parameters for the method. * @param params An array of parameters for the method.
* @return The result of the server. * @return The result of the server.
* @throws XMLRPCException Will be thrown if an error occurred during the call. * @throws XMLRPCException Will be thrown if an error occurred during the call.
@ -369,8 +370,9 @@ public class XMLRPCClient {
Call c = createCall(methodName, params); Call c = createCall(methodName, params);
// Prepare POST request // Prepare POST request
// FIXME: where creating a new HttpPost so calling #cancel isn't going to do anything
HttpPost post = new HttpPost(url); HttpPost post = new HttpPost(url);
post.getParams().setParameter("http.protocol.handle-redirects", false); post.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
post.setHeader(CONTENT_TYPE, TYPE_XML); post.setHeader(CONTENT_TYPE, TYPE_XML);
StringEntity entity = new StringEntity(c.getXML(), HTTP.UTF_8); StringEntity entity = new StringEntity(c.getXML(), HTTP.UTF_8);
entity.setContentType(TYPE_XML); entity.setContentType(TYPE_XML);
@ -444,7 +446,7 @@ public class XMLRPCClient {
} }
} }
return responseParser.parse(istream); return responseParser.parse(istream, entity);
} catch(SocketTimeoutException ex) { } catch(SocketTimeoutException ex) {
throw new XMLRPCTimeoutException("The XMLRPC call timed out."); throw new XMLRPCTimeoutException("The XMLRPC call timed out.");

99
app/src/main/java/de/timroes/axmlrpc/XMLUtil.java

@ -1,9 +1,6 @@
package de.timroes.axmlrpc; package de.timroes.axmlrpc;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/** /**
* This class provides some utility methods for the use with the Java DOM parser. * This class provides some utility methods for the use with the Java DOM parser.
@ -12,102 +9,6 @@ import org.w3c.dom.NodeList;
*/ */
public class XMLUtil { public class XMLUtil {
/**
* Returns the only child element in a given NodeList.
* Will throw an error if there is more then one child element or any other
* child that is not an element or an empty text string (whitespace are normal).
*
* @param list A NodeList of children nodes.
* @return The only child element in the given node list.
* @throws XMLRPCException Will be thrown if there is more then one child element
* except empty text nodes.
*/
public static Element getOnlyChildElement(NodeList list) throws XMLRPCException {
Element e = null;
Node n;
for(int i = 0; i < list.getLength(); i++) {
n = list.item(i);
// Strip only whitespace text elements and comments
if((n.getNodeType() == Node.TEXT_NODE
&& n.getNodeValue().trim().length() <= 0)
|| n.getNodeType() == Node.COMMENT_NODE)
continue;
// Check if there is anything else than an element node.
if(n.getNodeType() != Node.ELEMENT_NODE) {
throw new XMLRPCException("Only element nodes allowed.");
}
// If there was already an element, throw exception.
if(e != null) {
throw new XMLRPCException("Element has more than one children.");
}
e = (Element)n;
}
return e;
}
/**
* Returns the text node from a given NodeList. If the list contains
* more then just text nodes, an exception will be thrown.
*
* @param list The given list of nodes.
* @return The text of the given node list.
* @throws XMLRPCException Will be thrown if there is more than just one
* text node within the list.
*/
public static String getOnlyTextContent(NodeList list) throws XMLRPCException {
StringBuilder builder = new StringBuilder();
Node n;
for(int i = 0; i < list.getLength(); i++) {
n = list.item(i);
// Skip comments inside text tag.
if(n.getNodeType() == Node.COMMENT_NODE) {
continue;
}
if(n.getNodeType() != Node.TEXT_NODE) {
throw new XMLRPCException("Element must contain only text elements.");
}
builder.append(n.getNodeValue());
}
return builder.toString();
}
/**
* Checks if the given {@link NodeList} contains a child element.
*
* @param list The {@link NodeList} to check.
* @return Whether the {@link NodeList} contains children.
*/
public static boolean hasChildElement(NodeList list) {
Node n;
for(int i = 0; i < list.getLength(); i++) {
n = list.item(i);
if(n.getNodeType() == Node.ELEMENT_NODE) {
return true;
}
}
return false;
}
/** /**
* Creates an xml tag with a given type and content. * Creates an xml tag with a given type and content.
* *

38
app/src/main/java/de/timroes/axmlrpc/serializer/ArraySerializer.java

@ -2,12 +2,7 @@ package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/** /**
* *
@ -18,39 +13,6 @@ public class ArraySerializer implements Serializer {
private static final String ARRAY_DATA = "data"; private static final String ARRAY_DATA = "data";
private static final String ARRAY_VALUE = "value"; private static final String ARRAY_VALUE = "value";
public Object deserialize(Element content) throws XMLRPCException {
List<Object> list = new ArrayList<Object>();
Element data = XMLUtil.getOnlyChildElement(content.getChildNodes());
if(!ARRAY_DATA.equals(data.getNodeName())) {
throw new XMLRPCException("The array must contain one data tag.");
}
// Deserialize every array element
Node value;
for(int i = 0; i < data.getChildNodes().getLength(); i++) {
value = data.getChildNodes().item(i);
// Strip only whitespace text elements and comments
if(value == null || (value.getNodeType() == Node.TEXT_NODE
&& value.getNodeValue().trim().length() <= 0)
|| value.getNodeType() == Node.COMMENT_NODE)
continue;
if(value.getNodeType() != Node.ELEMENT_NODE) {
throw new XMLRPCException("Wrong element inside of array.");
}
list.add(SerializerHandler.getDefault().deserialize((Element)value));
}
return list.toArray();
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
Iterable<?> iter = (Iterable<?>)object; Iterable<?> iter = (Iterable<?>)object;

6
app/src/main/java/de/timroes/axmlrpc/serializer/Base64Serializer.java

@ -1,10 +1,8 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import de.timroes.base64.Base64; import de.timroes.base64.Base64;
import org.w3c.dom.Element;
/** /**
* *
@ -12,10 +10,6 @@ import org.w3c.dom.Element;
*/ */
public class Base64Serializer implements Serializer { public class Base64Serializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Base64.decode(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BASE64, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BASE64,
Base64.encode((Byte[])object)); Base64.encode((Byte[])object));

7
app/src/main/java/de/timroes/axmlrpc/serializer/BooleanSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,11 +9,6 @@ import org.w3c.dom.Element;
*/ */
public class BooleanSerializer implements Serializer { public class BooleanSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return (XMLUtil.getOnlyTextContent(content.getChildNodes()).equals("1"))
? Boolean.TRUE : Boolean.FALSE;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BOOLEAN, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BOOLEAN,
((Boolean)object == true) ? "1" : "0"); ((Boolean)object == true) ? "1" : "0");

15
app/src/main/java/de/timroes/axmlrpc/serializer/DateTimeSerializer.java

@ -1,11 +1,8 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import org.w3c.dom.Element;
/** /**
* *
@ -13,16 +10,8 @@ import org.w3c.dom.Element;
*/ */
public class DateTimeSerializer implements Serializer { public class DateTimeSerializer implements Serializer {
private static final String DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss"; public static final String DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss";
private static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat(DATETIME_FORMAT); public static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat(DATETIME_FORMAT);
public Object deserialize(Element content) throws XMLRPCException {
try {
return DATE_FORMATER.parse(XMLUtil.getOnlyTextContent(content.getChildNodes()));
} catch (ParseException ex) {
throw new XMLRPCException("Unable to parse given date.", ex);
}
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_DATETIME, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_DATETIME,

6
app/src/main/java/de/timroes/axmlrpc/serializer/DoubleSerializer.java

@ -1,10 +1,8 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.w3c.dom.Element;
/** /**
* This serializer is responsible for floating point numbers. * This serializer is responsible for floating point numbers.
@ -13,10 +11,6 @@ import org.w3c.dom.Element;
*/ */
public class DoubleSerializer implements Serializer { public class DoubleSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Double.valueOf(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
// Turn double value of object into a BigDecimal to get the // Turn double value of object into a BigDecimal to get the
// right decimal point format. // right decimal point format.

6
app/src/main/java/de/timroes/axmlrpc/serializer/IntSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,10 +9,6 @@ import org.w3c.dom.Element;
*/ */
public class IntSerializer implements Serializer { public class IntSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Integer.parseInt(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_INT, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_INT,
object.toString()); object.toString());

6
app/src/main/java/de/timroes/axmlrpc/serializer/LongSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,10 +9,6 @@ import org.w3c.dom.Element;
*/ */
class LongSerializer implements Serializer { class LongSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Long.parseLong(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_LONG, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_LONG,
((Long)object).toString()); ((Long)object).toString());

6
app/src/main/java/de/timroes/axmlrpc/serializer/NullSerializer.java

@ -1,8 +1,6 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -10,10 +8,6 @@ import org.w3c.dom.Element;
*/ */
public class NullSerializer implements Serializer { public class NullSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return null;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return new XmlElement(SerializerHandler.TYPE_NULL); return new XmlElement(SerializerHandler.TYPE_NULL);
} }

11
app/src/main/java/de/timroes/axmlrpc/serializer/Serializer.java

@ -1,8 +1,6 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* A Serializer is responsible to serialize a specific type of data to * A Serializer is responsible to serialize a specific type of data to
@ -12,15 +10,6 @@ import org.w3c.dom.Element;
*/ */
public interface Serializer { public interface Serializer {
/**
* This method takes an xml type element and deserialize it to an object.
*
* @param content Must be an xml element of a specific type.
* @return The deserialized content.
* @throws XMLRPCException Will be thrown whenervt the deserialization fails.
*/
public Object deserialize(Element content) throws XMLRPCException;
/** /**
* This method takes an object and returns a representation as a string * This method takes an object and returns a representation as a string
* containing the right xml type tag. The returning string must be useable * containing the right xml type tag. The returning string must be useable

191
app/src/main/java/de/timroes/axmlrpc/serializer/SerializerHandler.java

@ -3,13 +3,27 @@ package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCClient; import de.timroes.axmlrpc.XMLRPCClient;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.w3c.dom.Element; import java.util.SimpleTimeZone;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.util.Base64;
import android.util.Log;
/** /**
* The serializer handler serializes and deserialized objects. * The serializer handler serializes and deserialized objects.
@ -21,6 +35,18 @@ import org.w3c.dom.Element;
* @author Tim Roes * @author Tim Roes
*/ */
public class SerializerHandler { public class SerializerHandler {
private static final String LOG_NAME = "SerializerHandler";
public static final String TAG_NAME = "name";
public static final String TAG_MEMBER = "member";
public static final String TAG_VALUE = "value";
public static final String TAG_DATA = "data";
public static final String TYPE_DATE_TIME_ISO8601 = "dateTime.iso8601";
static SimpleDateFormat dateFormat = DateTimeSerializer.DATE_FORMATER;
static Calendar cal = Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
public static final String TYPE_STRING = "string"; public static final String TYPE_STRING = "string";
public static final String TYPE_BOOLEAN = "boolean"; public static final String TYPE_BOOLEAN = "boolean";
@ -81,83 +107,120 @@ public class SerializerHandler {
*/ */
private SerializerHandler(int flags) { private SerializerHandler(int flags) {
this.flags = flags; this.flags = flags;
string = new StringSerializer( string = new StringSerializer((flags & XMLRPCClient.FLAGS_NO_STRING_ENCODE) == 0);
(flags & XMLRPCClient.FLAGS_NO_STRING_ENCODE) == 0,
(flags & XMLRPCClient.FLAGS_NO_STRING_DECODE) == 0
);
} }
/** /**
* Deserializes an incoming xml element to an java object. * Deserialize an incoming xml to a java object.
* The xml element must be the value element around the type element.
* The type of the returning object depends on the type tag. * The type of the returning object depends on the type tag.
* *
* @param element An type element from within a value tag. * @param parser Initialized parser.
* @return The deserialized object. * @return The deserialized object.
* @throws XMLRPCException Will be thrown whenever an error occurs. * @throws XmlPullParserException
* @throws IOException
* @throws NumberFormatException
*/ */
public Object deserialize(Element element) throws XMLRPCException { public static Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException, NumberFormatException {
parser.require(XmlPullParser.START_TAG, null, TAG_VALUE);
if(!XMLRPCClient.VALUE.equals(element.getNodeName())) { parser.nextTag();
throw new XMLRPCException("Value tag is missing around value."); String typeNodeName = parser.getName();
}
if(!XMLUtil.hasChildElement(element.getChildNodes())) {
// Value element doesn't contain a child element
if((flags & XMLRPCClient.FLAGS_DEFAULT_TYPE_STRING) != 0) {
return string.deserialize(element);
} else {
throw new XMLRPCException("Missing type element inside of value element.");
}
}
// Grep type element from inside value element
element = XMLUtil.getOnlyChildElement(element.getChildNodes());
Serializer s = null;
String type; Object obj;
if (typeNodeName.equals(TYPE_INT) || typeNodeName.equals(TYPE_INT2)) {
// If FLAGS_IGNORE_NAMESPACE has been set, only use local name. String value = parser.nextText();
if((flags & XMLRPCClient.FLAGS_IGNORE_NAMESPACES) != 0) { try {
type = element.getLocalName() == null ? element.getNodeName() : element.getLocalName(); obj = Integer.parseInt(value);
} else { } catch (NumberFormatException nfe) {
type = element.getNodeName(); Log.w(LOG_NAME, "Server replied with an invalid 4 bytes int value, trying to parse it as 8 bytes long.");
} obj = Long.parseLong(value);
}
} else
if (typeNodeName.equals(TYPE_LONG)) {
String value = parser.nextText();
obj = Long.parseLong(value);
} else
if (typeNodeName.equals(TYPE_DOUBLE)) {
String value = parser.nextText();
obj = Double.parseDouble(value);
} else
if (typeNodeName.equals(TYPE_BOOLEAN)) {
String value = parser.nextText();
obj = value.equals("1") ? Boolean.TRUE : Boolean.FALSE;
} else
if (typeNodeName.equals(TYPE_STRING)) {
obj = parser.nextText();
} else
if (typeNodeName.equals(TYPE_DATE_TIME_ISO8601)) {
dateFormat.setCalendar(cal);
String value = parser.nextText();
try {
obj = dateFormat.parseObject(value);
} catch (ParseException e) {
Log.e(LOG_NAME, "Error parsing date, using non-parsed string.");
obj = value;
}
} else
if (typeNodeName.equals(TYPE_BASE64)) {
String value = parser.nextText();
BufferedReader reader = new BufferedReader(new StringReader(value));
String line;
StringBuffer sb = new StringBuffer();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
obj = Base64.decode(sb.toString(), Base64.DEFAULT);
} else
if (typeNodeName.equals(TYPE_ARRAY)) {
parser.nextTag(); // TAG_DATA (<data>)
parser.require(XmlPullParser.START_TAG, null, TAG_DATA);
if((flags & XMLRPCClient.FLAGS_NIL) != 0 && TYPE_NULL.equals(type)) { parser.nextTag();
s = nil; List<Object> list = new ArrayList<Object>();
} else if(TYPE_STRING.equals(type)) { while (parser.getName().equals(TAG_VALUE)) {
s = string; list.add(deserialize(parser));
} else if(TYPE_BOOLEAN.equals(type)) { parser.nextTag();
s = bool;
} else if(TYPE_DOUBLE.equals(type)) {
s = floating;
} else if (TYPE_INT.equals(type) || TYPE_INT2.equals(type)) {
s = integer;
} else if(TYPE_DATETIME.equals(type)) {
s = datetime;
} else if (TYPE_LONG.equals(type)) {
if((flags & XMLRPCClient.FLAGS_8BYTE_INT) != 0) {
s = long8;
} else {
throw new XMLRPCException("8 byte integer is not in the specification. "
+ "You must use FLAGS_8BYTE_INT to enable the i8 tag.");
} }
} else if(TYPE_STRUCT.equals(type)) { parser.require(XmlPullParser.END_TAG, null, TAG_DATA);
s = struct; parser.nextTag(); // TAG_ARRAY (</array>)
} else if(TYPE_ARRAY.equals(type)) { parser.require(XmlPullParser.END_TAG, null, TYPE_ARRAY);
s = array; obj = list.toArray();
} else if(TYPE_BASE64.equals(type)) { } else
s = base64; if (typeNodeName.equals(TYPE_STRUCT)) {
parser.nextTag();
Map<String, Object> map = new HashMap<String, Object>();
while (parser.getName().equals(TAG_MEMBER)) {
String memberName = null;
Object memberValue = null;
while (true) {
parser.nextTag();
String name = parser.getName();
if (name.equals(TAG_NAME)) {
memberName = parser.nextText();
} else
if (name.equals(TAG_VALUE)) {
memberValue = deserialize(parser);
} else {
break;
}
}
if (memberName != null && memberValue != null) {
map.put(memberName, memberValue);
}
parser.require(XmlPullParser.END_TAG, null, TAG_MEMBER);
parser.nextTag();
}
parser.require(XmlPullParser.END_TAG, null, TYPE_STRUCT);
obj = map;
} else { } else {
throw new XMLRPCException("No deserializer found for type '" + type + "'."); throw new IOException("Cannot deserialize " + parser.getName());
} }
parser.nextTag(); // TAG_VALUE (</value>)
return s.deserialize(element); parser.require(XmlPullParser.END_TAG, null, TAG_VALUE);
return obj;
} }
/** /**
* Serialize an object to its representation as an xml element. * Serialize an object to its representation as an xml element.
* The xml element will be the type element for the use within a value tag. * The xml element will be the type element for the use within a value tag.

13
app/src/main/java/de/timroes/axmlrpc/serializer/StringSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,21 +9,12 @@ import org.w3c.dom.Element;
*/ */
public class StringSerializer implements Serializer { public class StringSerializer implements Serializer {
private boolean decodeStrings;
private boolean encodeStrings; private boolean encodeStrings;
public StringSerializer(boolean encodeStrings, boolean decodeStrings) { public StringSerializer(boolean encodeStrings) {
this.decodeStrings = decodeStrings;
this.encodeStrings = encodeStrings; this.encodeStrings = encodeStrings;
} }
public Object deserialize(Element content) throws XMLRPCException {
String text = XMLUtil.getOnlyTextContent(content.getChildNodes());
if(decodeStrings) {
text = text.replaceAll("&lt;", "<").replaceAll("&amp;", "&");
}
return text;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
String content = object.toString(); String content = object.toString();

63
app/src/main/java/de/timroes/axmlrpc/serializer/StructSerializer.java

@ -2,12 +2,8 @@ package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/** /**
* *
@ -19,65 +15,6 @@ public class StructSerializer implements Serializer {
private static final String STRUCT_NAME = "name"; private static final String STRUCT_NAME = "name";
private static final String STRUCT_VALUE = "value"; private static final String STRUCT_VALUE = "value";
public Object deserialize(Element content) throws XMLRPCException {
Map<String, Object> map = new HashMap<String, Object>();
Node n, m;
String s;
Object o;
for(int i = 0; i < content.getChildNodes().getLength(); i++) {
n = content.getChildNodes().item(i);
// Strip only whitespace text elements and comments
if((n.getNodeType() == Node.TEXT_NODE
&& n.getNodeValue().trim().length() <= 0)
|| n.getNodeType() == Node.COMMENT_NODE)
continue;
if(n.getNodeType() != Node.ELEMENT_NODE
|| !STRUCT_MEMBER.equals(n.getNodeName())) {
throw new XMLRPCException("Only struct members allowed within a struct.");
}
// Grep name and value from member
s = null; o = null;
for(int j = 0; j < n.getChildNodes().getLength(); j++) {
m = n.getChildNodes().item(j);
// Strip only whitespace text elements and comments
if((m.getNodeType() == Node.TEXT_NODE
&& m.getNodeValue().trim().length() <= 0)
|| m.getNodeType() == Node.COMMENT_NODE)
continue;
if(STRUCT_NAME.equals(m.getNodeName())) {
if(s != null) {
throw new XMLRPCException("Name of a struct member cannot be set twice.");
} else {
s = XMLUtil.getOnlyTextContent(m.getChildNodes());
}
} else if(m.getNodeType() == Node.ELEMENT_NODE && STRUCT_VALUE.equals(m.getNodeName())) {
if(o != null) {
throw new XMLRPCException("Value of a struct member cannot be set twice.");
} else {
o = SerializerHandler.getDefault().deserialize((Element)m);
}
} else {
throw new XMLRPCException("A struct member must only contain one name and one value.");
}
}
map.put(s, o);
}
return map;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
XmlElement struct = new XmlElement(SerializerHandler.TYPE_STRUCT); XmlElement struct = new XmlElement(SerializerHandler.TYPE_STRUCT);

Loading…
Cancel
Save