/* * Copyright (C) 2009 JavaRosa * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.openrosa.client.jr.core.util.externalizable; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import org.openrosa.client.java.io.DataInputStream; import org.openrosa.client.java.io.DataOutputStream; import org.openrosa.client.jr.core.services.PrototypeManager; import org.openrosa.client.jr.core.util.OrderedHashtable; public class ExtUtil { public static PrototypeFactory defaultPrototypes () { return new PrototypeFactory(PrototypeManager.getPrototypes()); } public static void write (DataOutputStream out, Object data) throws IOException { if (data instanceof Externalizable) { ((Externalizable)data).writeExternal(out); } else if (data instanceof Byte) { writeNumeric(out, ((Byte)data).byteValue()); } else if (data instanceof Short) { writeNumeric(out, ((Short)data).shortValue()); } else if (data instanceof Integer) { writeNumeric(out, ((Integer)data).intValue()); } else if (data instanceof Long) { writeNumeric(out, ((Long)data).longValue()); } else if (data instanceof Character) { writeChar(out, ((Character)data).charValue()); } else if (data instanceof Float) { writeDecimal(out, ((Float)data).floatValue()); } else if (data instanceof Double) { writeDecimal(out, ((Double)data).doubleValue()); } else if (data instanceof Boolean) { writeBool(out, ((Boolean)data).booleanValue()); } else if (data instanceof String) { writeString(out, (String)data); } else if (data instanceof Date) { writeDate(out, (Date)data); } else { throw new ClassCastException("Not a serializable datatype: " + data.getClass().getName()); } } public static void writeNumeric (DataOutputStream out, long val) throws IOException { writeNumeric(out, val, new ExtWrapIntEncodingUniform()); } public static void writeNumeric (DataOutputStream out, long val, ExtWrapIntEncoding encoding) throws IOException { write(out, encoding.clone(new Long(val))); } public static void writeChar (DataOutputStream out, char val) throws IOException { out.writeChar(val); } public static void writeDecimal (DataOutputStream out, double val) throws IOException { out.writeDouble(val); } public static void writeBool (DataOutputStream out, boolean val) throws IOException { out.writeBoolean(val); } public static void writeString (DataOutputStream out, String val) throws IOException { out.writeUTF(val); //we could easily come up with more efficient default encoding for string } public static void writeDate (DataOutputStream out, Date val) throws IOException { writeNumeric(out, val.getTime()); //time zone? } public static void writeBytes(DataOutputStream out, byte[] bytes) throws IOException { ExtUtil.writeNumeric(out, bytes.length); if (bytes.length > 0) //i think writing zero-length array might close the stream out.write(bytes); } public static void writeInts(DataOutputStream out, int[] ints) throws IOException { ExtUtil.writeNumeric(out, ints.length); for(int i : ints) { ExtUtil.writeNumeric(out, i); } } public static Object read (DataInputStream in, Class type) throws IOException, DeserializationException { return read(in, type, null); } public static Object read (DataInputStream in, Class type, PrototypeFactory pf) throws IOException, DeserializationException { if (true /*Externalizable.class.isAssignableFrom(type)*/) { Externalizable ext = null; //(Externalizable)PrototypeFactory.getInstance(type); ext.readExternal(in, pf == null ? defaultPrototypes() : pf); return ext; } else if (type == Byte.class) { return new Byte(readByte(in)); } else if (type == Short.class) { return new Short(readShort(in)); } else if (type == Integer.class) { return new Integer(readInt(in)); } else if (type == Long.class) { return new Long(readNumeric(in)); } else if (type == Character.class) { return new Character(readChar(in)); } else if (type == Float.class) { return new Float((float)readDecimal(in)); } else if (type == Double.class) { return new Double(readDecimal(in)); } else if (type == Boolean.class) { return new Boolean(readBool(in)); } else if (type == String.class) { return readString(in); } else if (type == Date.class) { return readDate(in); } else { throw new ClassCastException("Not a deserializable datatype: " + type.getName()); } } public static Object read (DataInputStream in, ExternalizableWrapper ew) throws IOException, DeserializationException { return read(in, ew, null); } public static Object read (DataInputStream in, ExternalizableWrapper ew, PrototypeFactory pf) throws IOException, DeserializationException { ew.readExternal(in, pf == null ? defaultPrototypes() : pf); return ew.val; } public static long readNumeric (DataInputStream in) throws IOException { return readNumeric(in, new ExtWrapIntEncodingUniform()); } public static long readNumeric (DataInputStream in, ExtWrapIntEncoding encoding) throws IOException { try { return ((Long)read(in, encoding)).longValue(); } catch (DeserializationException de) { throw new RuntimeException("Shouldn't happen: Base-type encoding wrappers should never touch prototypes"); } } public static int readInt (DataInputStream in) throws IOException { return toInt(readNumeric(in)); } public static short readShort (DataInputStream in) throws IOException { return toShort(readNumeric(in)); } public static byte readByte (DataInputStream in) throws IOException { return toByte(readNumeric(in)); } public static char readChar (DataInputStream in) throws IOException { return in.readChar(); } public static double readDecimal (DataInputStream in) throws IOException { return in.readDouble(); } public static boolean readBool (DataInputStream in) throws IOException { return in.readBoolean(); } public static String readString (DataInputStream in) throws IOException { return in.readUTF(); } public static Date readDate (DataInputStream in) throws IOException { return new Date(readNumeric(in)); //time zone? } public static byte[] readBytes(DataInputStream in) throws IOException { int size = (int)ExtUtil.readNumeric(in); byte[] bytes = new byte[size]; int read = 0; int toread = size; while(read != size) { read = in.read(bytes, 0, toread); toread -= read; } return bytes; } public static int[] readInts(DataInputStream in) throws IOException { int size = (int)ExtUtil.readNumeric(in); int[] ints = new int[size]; for(int i = 0 ; i < size ; ++i ) { ints[i] = (int)ExtUtil.readNumeric(in); } return ints; } public static int toInt (long l) { if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) throw new ArithmeticException("Value (" + l + ") cannot fit into int"); return (int)l; } public static short toShort (long l) { if (l < Short.MIN_VALUE || l > Short.MAX_VALUE) throw new ArithmeticException("Value (" + l + ") cannot fit into short"); return (short)l; } public static byte toByte (long l) { if (l < Byte.MIN_VALUE || l > Byte.MAX_VALUE) throw new ArithmeticException("Value (" + l + ") cannot fit into byte"); return (byte)l; } public static long toLong (Object o) { if (o instanceof Byte) { return ((Byte)o).byteValue(); } else if (o instanceof Short) { return ((Short)o).shortValue(); } else if (o instanceof Integer) { return ((Integer)o).intValue(); } else if (o instanceof Long) { return ((Long)o).longValue(); } else if (o instanceof Character) { return ((Character)o).charValue(); } else { throw new ClassCastException(); } } public static byte[] nullIfEmpty (byte[] ba) { return (ba == null ? null : (ba.length == 0 ? null : ba)); } public static String nullIfEmpty (String s) { return (s == null ? null : (s.length() == 0 ? null : s)); } public static Vector nullIfEmpty (Vector v) { return (v == null ? null : (v.size() == 0 ? null : v)); } public static HashMap nullIfEmpty (HashMap h) { return (h == null ? null : (h.size() == 0 ? null : h)); } public static byte[] emptyIfNull (byte[] ba) { return ba == null ? new byte[0] : ba; } public static String emptyIfNull (String s) { return s == null ? "" : s; } public static Vector emptyIfNull (Vector v) { return v == null ? new Vector() : v; } public static HashMap emptyIfNull (HashMap h) { return h == null ? new HashMap() : h; } public static Object unwrap (Object o) { return (o instanceof ExternalizableWrapper ? ((ExternalizableWrapper)o).baseValue() : o); } public static boolean equals (Object a, Object b) { a = unwrap(a); b = unwrap(b); if (a == null) { return b == null; } else if (a instanceof Vector) { return (b instanceof Vector && vectorEquals((Vector)a, (Vector)b)); } else if (a instanceof HashMap) { return (b instanceof HashMap && hashtableEquals((HashMap)a, (HashMap)b)); } else { return a.equals(b); } } public static boolean vectorEquals (Vector a, Vector b) { if (a.size() != b.size()) { return false; } else { for (int i = 0; i < a.size(); i++) { if (!equals(a.elementAt(i), b.elementAt(i))) { return false; } } return true; } } public static boolean arrayEquals (Object[] a, Object[] b) { if (a.length != b.length) { return false; } else { for (int i = 0; i < a.length; i++) { if (!equals(a[i], b[i])) { return false; } } return true; } } public static boolean hashtableEquals (HashMap a, HashMap b) { if (a.size() != b.size()) { return false; } else if (a instanceof OrderedHashtable != b instanceof OrderedHashtable) { return false; } else { for (Iterator ea = a.keySet().iterator(); ea.hasNext(); ) { Object keyA = ea.next(); if (!equals(a.get(keyA), b.get(keyA))) { return false; } } if (a instanceof OrderedHashtable && b instanceof OrderedHashtable) { Iterator ea = a.keySet().iterator(); Iterator eb = b.keySet().iterator(); while (ea.hasNext()) { Object keyA = ea.next(); Object keyB = eb.next(); if (!keyA.equals(keyB)) { //must use built-in equals for keys, as that's what hashtable uses return false; } } } return true; } } public static String printBytes (byte[] data) { StringBuffer sb = new StringBuffer(); sb.append("["); for (int i = 0; i < data.length; i++) { String hex = Integer.toHexString(data[i]); if (hex.length() == 1) hex = "0" + hex; else hex = hex.substring(hex.length() - 2); sb.append(hex); if (i < data.length - 1) { if ((i + 1) % 30 == 0) sb.append("\n "); else if ((i + 1) % 10 == 0) sb.append(" "); else sb.append(" "); } } sb.append("]"); return sb.toString(); } //**REMOVE THESE TWO FUNCTIONS// //original deserialization API (whose limits made us make this whole new framework!); here for backwards compatibility public static void deserialize (byte[] data, Externalizable ext) throws IOException, DeserializationException { ext.readExternal(new DataInputStream(), defaultPrototypes()); } public static Object deserialize (byte[] data, Class type, PrototypeFactory pf) throws IOException, DeserializationException { return read(new DataInputStream(), type, pf); } //// }