X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fserial%2FSerialUtils.java;h=a1105a90d441352d71fa51faf5d56f8294f35318;hb=f4053377fa15da2f11e82955bfab86e673fa371c;hp=7bf1b17a3a651a4f41f5f8cd072be1cd160dfca3;hpb=ce0974c4b695f842fa7ec81f3c53d016d1959854;p=nikiroo-utils.git diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java index 7bf1b17..a1105a9 100644 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ b/src/be/nikiroo/utils/serial/SerialUtils.java @@ -1,19 +1,45 @@ package be.nikiroo.utils.serial; +import java.awt.image.BufferedImage; +import java.io.IOException; import java.io.NotSerializableException; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.util.ArrayList; +import java.lang.reflect.Modifier; +import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.UnknownFormatConversionException; +import be.nikiroo.utils.ImageUtils; + /** * Small class to help with serialisation. *

* Note that we do not support inner classes (but we do support nested classes) * and all objects require an empty constructor to be deserialised. + *

+ * It is possible to add support to custom types (both the encoder and the + * decoder will require the custom classes) -- see {@link CustomSerializer}. + *

+ * Default supported types are: + *

* * @author niki */ @@ -22,7 +48,6 @@ public class SerialUtils { static { customTypes = new HashMap(); - // TODO: add "default" custom serialisers if any (Bitmap?) // Array types: customTypes.put("[]", new CustomSerializer() { @@ -62,7 +87,7 @@ public class SerialUtils { } @Override - protected Object fromString(String content) { + protected Object fromString(String content) throws IOException { String[] tab = content.split("\n"); try { @@ -75,6 +100,59 @@ public class SerialUtils { return array; } catch (Exception e) { + if (e instanceof IOException) { + throw (IOException) e; + } + throw new IOException(e.getMessage()); + } + } + }); + + // URL: + customTypes.put("java.net.URL", new CustomSerializer() { + @Override + protected String toString(Object value) { + if (value != null) { + return ((URL) value).toString(); + } + return null; + } + + @Override + protected Object fromString(String content) throws IOException { + if (content != null) { + return new URL(content); + } + return null; + } + + @Override + protected String getType() { + return "java.net.URL"; + } + }); + + // Images (this is currently the only supported image type by default) + customTypes.put("java.awt.image.BufferedImage", new CustomSerializer() { + @Override + protected String toString(Object value) { + try { + return ImageUtils.toBase64((BufferedImage) value); + } catch (IOException e) { + throw new UnknownFormatConversionException(e.getMessage()); + } + } + + @Override + protected String getType() { + return "java.awt.image.BufferedImage"; + } + + @Override + protected Object fromString(String content) { + try { + return ImageUtils.fromBase64(content); + } catch (IOException e) { throw new UnknownFormatConversionException(e.getMessage()); } } @@ -188,8 +266,12 @@ public class SerialUtils { for (Field field : fields) { field.setAccessible(true); - if (field.getName().startsWith("this$")) { + if (field.getName().startsWith("this$") + || field.isSynthetic() + || (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC) { // Do not keep this links of nested classes + // Do not keep synthetic fields + // Do not keep final fields continue; } @@ -216,14 +298,24 @@ public class SerialUtils { builder.append("\n}"); } - // return true if encoded (supported) + /** + * Encode the object into the given builder if possible (if supported). + * + * @param builder + * the builder to append to + * @param value + * the object to encode (can be NULL, which will be encoded) + * + * @return TRUE if success, FALSE if not (the content of the builder won't + * be changed in case of failure) + */ static boolean encode(StringBuilder builder, Object value) { if (value == null) { builder.append("NULL"); } else if (value.getClass().getCanonicalName().endsWith("[]")) { - customTypes.get("[]").encode(builder, value); + return customTypes.get("[]").encode(builder, value); } else if (customTypes.containsKey(value.getClass().getCanonicalName())) { - customTypes.get(value.getClass().getCanonicalName())// + return customTypes.get(value.getClass().getCanonicalName())// .encode(builder, value); } else if (value instanceof String) { encodeString(builder, (String) value); @@ -244,6 +336,10 @@ public class SerialUtils { builder.append(value).append('F'); } else if (value instanceof Double) { builder.append(value).append('d'); + } else if (value instanceof Enum) { + String type = value.getClass().getCanonicalName(); + builder.append(type).append(".").append(((Enum) value).name()) + .append(";"); } else { return false; } @@ -251,43 +347,62 @@ public class SerialUtils { return true; } - static Object decode(String encodedValue) { - String cut = ""; - if (encodedValue.length() > 1) { - cut = encodedValue.substring(0, encodedValue.length() - 1); - } + /** + * Decode the data into an equivalent source object. + * + * @param encodedValue + * the encoded data, cannot be NULL + * + * @return the object (can be NULL for NULL encoded values) + * + * @throws IOException + * if the content cannot be converted + */ + static Object decode(String encodedValue) throws IOException { + try { + String cut = ""; + if (encodedValue.length() > 1) { + cut = encodedValue.substring(0, encodedValue.length() - 1); + } - if (CustomSerializer.isCustom(encodedValue)) { - // custom:TYPE_NAME:"content is String-encoded" - String type = CustomSerializer.typeOf(encodedValue); - if (customTypes.containsKey(type)) { - return customTypes.get(type).decode(encodedValue); + if (CustomSerializer.isCustom(encodedValue)) { + // custom:TYPE_NAME:"content is String-encoded" + String type = CustomSerializer.typeOf(encodedValue); + if (customTypes.containsKey(type)) { + return customTypes.get(type).decode(encodedValue); + } + throw new IOException("Unknown custom type: " + type); + } else if (encodedValue.equals("NULL") + || encodedValue.equals("null")) { + return null; + } else if (encodedValue.endsWith("\"")) { + return decodeString(encodedValue); + } else if (encodedValue.equals("true")) { + return true; + } else if (encodedValue.equals("false")) { + return false; + } else if (encodedValue.endsWith("b")) { + return Byte.parseByte(cut); + } else if (encodedValue.endsWith("c")) { + return decodeString(cut).charAt(0); + } else if (encodedValue.endsWith("s")) { + return Short.parseShort(cut); + } else if (encodedValue.endsWith("L")) { + return Long.parseLong(cut); + } else if (encodedValue.endsWith("F")) { + return Float.parseFloat(cut); + } else if (encodedValue.endsWith("d")) { + return Double.parseDouble(cut); + } else if (encodedValue.endsWith(";")) { + return decodeEnum(encodedValue); } else { - throw new UnknownFormatConversionException( - "Unknown custom type: " + type); + return Integer.parseInt(encodedValue); } - } else if (encodedValue.equals("NULL") || encodedValue.equals("null")) { - return null; - } else if (encodedValue.endsWith("\"")) { - return decodeString(encodedValue); - } else if (encodedValue.equals("true")) { - return true; - } else if (encodedValue.equals("false")) { - return false; - } else if (encodedValue.endsWith("b")) { - return Byte.parseByte(cut); - } else if (encodedValue.endsWith("c")) { - return decodeString(cut).charAt(0); - } else if (encodedValue.endsWith("s")) { - return Short.parseShort(cut); - } else if (encodedValue.endsWith("L")) { - return Long.parseLong(cut); - } else if (encodedValue.endsWith("F")) { - return Float.parseFloat(cut); - } else if (encodedValue.endsWith("d")) { - return Double.parseDouble(cut); - } else { - return Integer.parseInt(encodedValue); + } catch (Exception e) { + if (e instanceof IOException) { + throw (IOException) e; + } + throw new IOException(e.getMessage()); } } @@ -341,6 +456,21 @@ public class SerialUtils { return clazz; } + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static Enum decodeEnum(String escaped) { + // escaped: be.xxx.EnumType.VALUE; + int pos = escaped.lastIndexOf("."); + String type = escaped.substring(0, pos); + String name = escaped.substring(pos + 1, escaped.length() - 1); + + try { + return Enum.valueOf((Class) getClass(type), name); + } catch (Exception e) { + throw new UnknownFormatConversionException("Unknown enum: <" + type + + "> " + name); + } + } + // aa bb -> "aa\tbb" private static void encodeString(StringBuilder builder, String raw) { builder.append('\"');