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
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); |
|
|
|
} |
|
|
|
} |