Manage your torrents from your Android device
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

291 lines
9.2 KiB

package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCClient;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.
* It takes an object, determine its type and let the responsible handler serialize it.
* For deserialization it looks at the xml tag around the element.
* The class is designed as a kind of singleton, so it can be accessed from anywhere in
* the library.
*
* @author Tim Roes
*/
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_BOOLEAN = "boolean";
public static final String TYPE_INT = "int";
public static final String TYPE_INT2 = "i4";
public static final String TYPE_LONG = "i8";
public static final String TYPE_DOUBLE = "double";
public static final String TYPE_DATETIME = "dateTime.iso8601";
public static final String TYPE_STRUCT = "struct";
public static final String TYPE_ARRAY = "array";
public static final String TYPE_BASE64 = "base64";
public static final String TYPE_NULL = "nil";
private static SerializerHandler instance;
/**
* Initialize the serialization handler. This method must be called before
* the get method returns any object.
*
* @param flags The flags that has been set in the XMLRPCClient.
* @see XMLRPCClient
*/
public static void initialize(int flags) {
instance = new SerializerHandler(flags);
}
/**
* Return the instance of the SerializerHandler.
* It must have been initialized with initialize() before.
*
* @return The instance of the SerializerHandler.
*/
public static SerializerHandler getDefault() {
if(instance == null) {
throw new XMLRPCRuntimeException("The SerializerHandler has not been initialized.");
}
return instance;
}
private StringSerializer string;
private BooleanSerializer bool = new BooleanSerializer();
private IntSerializer integer = new IntSerializer();
private LongSerializer long8 = new LongSerializer();
private StructSerializer struct = new StructSerializer();
private DoubleSerializer floating = new DoubleSerializer();
private DateTimeSerializer datetime = new DateTimeSerializer();
private ArraySerializer array = new ArraySerializer();
private Base64Serializer base64 = new Base64Serializer();
private NullSerializer nil = new NullSerializer();
private int flags;
/**
* Generates the SerializerHandler.
* This method can only called from within the class (the initialize method).
*
* @param flags The flags to use.
*/
private SerializerHandler(int flags) {
this.flags = flags;
string = new StringSerializer((flags & XMLRPCClient.FLAGS_NO_STRING_ENCODE) == 0);
}
/**
* Deserialize an incoming xml to a java object.
* The type of the returning object depends on the type tag.
*
* @param parser Initialized parser.
* @return The deserialized object.
* @throws XmlPullParserException
* @throws IOException
* @throws NumberFormatException
*/
public static Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException, NumberFormatException {
parser.require(XmlPullParser.START_TAG, null, TAG_VALUE);
parser.nextTag();
String typeNodeName = parser.getName();
Object obj;
if (typeNodeName.equals(TYPE_INT) || typeNodeName.equals(TYPE_INT2)) {
String value = parser.nextText();
try {
obj = Integer.parseInt(value);
} catch (NumberFormatException nfe) {
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);
parser.nextTag();
List<Object> list = new ArrayList<Object>();
while (parser.getName().equals(TAG_VALUE)) {
list.add(deserialize(parser));
parser.nextTag();
}
parser.require(XmlPullParser.END_TAG, null, TAG_DATA);
parser.nextTag(); // TAG_ARRAY (</array>)
parser.require(XmlPullParser.END_TAG, null, TYPE_ARRAY);
obj = list.toArray();
} else
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 {
throw new IOException("Cannot deserialize " + parser.getName());
}
parser.nextTag(); // TAG_VALUE (</value>)
parser.require(XmlPullParser.END_TAG, null, TAG_VALUE);
return obj;
}
/**
* 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.
*
* @param object The object that should be serialized.
* @return The xml representation of this object.
* @throws XMLRPCException Will be thrown, if an error occurs (e.g. the object
* cannot be serialized to an xml element.
*/
public XmlElement serialize(Object object) throws XMLRPCException {
Serializer s = null;
if((flags & XMLRPCClient.FLAGS_NIL) != 0 && object == null) {
s = nil;
} else if(object instanceof String) {
s = string;
} else if(object instanceof Boolean) {
s = bool;
} else if(object instanceof Double || object instanceof Float
|| object instanceof BigDecimal) {
s = floating;
} else if (object instanceof Integer || object instanceof Short
|| object instanceof Byte) {
s = integer;
} else if(object instanceof Long) {
// Check whether the 8 byte integer flag was set.
if((flags & XMLRPCClient.FLAGS_8BYTE_INT) != 0) {
s = long8;
} else {
// Allow long values as long as their fit within the 4 byte integer range.
long l = (Long)object;
if(l > Integer.MAX_VALUE || l < Integer.MIN_VALUE) {
throw new XMLRPCException("FLAGS_8BYTE_INT must be set, if values "
+ "outside the 4 byte integer range should be transfered.");
} else {
s = integer;
}
}
} else if(object instanceof Date) {
s = datetime;
} else if(object instanceof Calendar) {
object = ((Calendar)object).getTime();
s = datetime;
} else if (object instanceof Map) {
s = struct;
} else if(object instanceof byte[]) {
byte[] old = (byte[])object;
Byte[] boxed = new Byte[old.length];
for(int i = 0; i < boxed.length; i++) {
boxed[i] = new Byte(old[i]);
}
object = boxed;
s = base64;
} else if(object instanceof Byte[]) {
s = base64;
} else if(object instanceof Iterable<?>) {
s = array;
} else {
throw new XMLRPCException("No serializer found for type '"
+ object.getClass().getName() + "'.");
}
return s.serialize(object);
}
}