From 8c8da42a501ff223ee1340ab71d03df481649e78 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Fri, 30 Jun 2017 18:46:42 +0200 Subject: [PATCH] Serialisation utilities update - SerialUtils is now public and can be used to dynamically create an Object - The Importer is now easier to use --- Makefile.base | 2 +- VERSION | 2 +- changelog | 8 ++ src/be/nikiroo/utils/serial/Importer.java | 87 +++++++++++++----- src/be/nikiroo/utils/serial/SerialUtils.java | 95 ++++++++++++++------ 5 files changed, 140 insertions(+), 54 deletions(-) diff --git a/Makefile.base b/Makefile.base index 54dbf1a..ef0b926 100644 --- a/Makefile.base +++ b/Makefile.base @@ -108,7 +108,7 @@ test-resources: resources libs: bin @[ -e bin/libs -o ! -d libs ] || echo Extracting sources from libs... - @[ -e bin/libs -o ! -d libs ] || (cd src && for lib in ../libs/*-sources.jar; do \ + @[ -e bin/libs -o ! -d libs ] || (cd src && for lib in ../libs/*-sources.jar ../libs/*-sources.patch.jar; do \ basename "$$lib"; \ jar xf "$$lib"; \ done ) diff --git a/VERSION b/VERSION index bc80560..26ca594 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.0 +1.5.1 diff --git a/changelog b/changelog index 0fc8a93..4dc7341 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,11 @@ +Version 1.5.1 +------------- + +Serialisation utilities + SerialUtils is now public and can be used to dynamically create an + Object + The Importer is now easier to use + Version 1.5.0 ------------- diff --git a/src/be/nikiroo/utils/serial/Importer.java b/src/be/nikiroo/utils/serial/Importer.java index 0709a96..61093f7 100644 --- a/src/be/nikiroo/utils/serial/Importer.java +++ b/src/be/nikiroo/utils/serial/Importer.java @@ -1,12 +1,11 @@ package be.nikiroo.utils.serial; +import java.io.IOException; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import java.util.Scanner; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Field; -import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.StringUtils; /** @@ -36,35 +35,69 @@ public class Importer { this.map = map; } - public Importer readLine(String line) { - try { - processLine(line); - } catch (Exception e) { - throw new IllegalArgumentException(e); - } - return this; - } + /** + * Read some data into this {@link Importer}: it can be the full serialised + * content, or a number of lines of it (any given line MUST be + * complete though) and accumulate it with the already present data. + * + * @param data + * the data to parse + * + * @return itself so it can be chained + * + * @throws NoSuchFieldException + * if the serialised data contains information about a field + * which does actually not exist in the class we know of + * @throws NoSuchMethodException + * if a class described in the serialised data cannot be created + * because it is not compatible with this code + * @throws ClassNotFoundException + * if a class described in the serialised data cannot be found + */ + public Importer read(String data) throws NoSuchFieldException, + NoSuchMethodException, ClassNotFoundException { - public Importer read(String data) { try { - if (data.startsWith("ZIP:")) { - data = StringUtils.unzip64(data.substring("ZIP:".length())); - } Scanner scan = new Scanner(data); scan.useDelimiter("\n"); while (scan.hasNext()) { - processLine(scan.next()); + String line = scan.next(); + + if (line.startsWith("ZIP:")) { + line = StringUtils.unzip64(line.substring("ZIP:".length())); + } + processLine(line); + } scan.close(); - } catch (Exception e) { - throw new IllegalArgumentException(e); + } catch (IOException e) { + throw new NoSuchMethodException( + "Internal error when decoding ZIP content: input may be corrupt"); } + return this; } - public boolean processLine(String line) throws IllegalArgumentException, - NoSuchFieldException, SecurityException, IllegalAccessException, - NoSuchMethodException, InstantiationException, ClassNotFoundException, InvocationTargetException { + /** + * Read a single (whole) line of serialised data into this {@link Importer} + * and accumulate it with the already present data. + * + * @param line + * the line to parse + * + * @return TRUE if we are just done with one object or sub-object + * + * @throws NoSuchFieldException + * if the serialised data contains information about a field + * which does actually not exist in the class we know of + * @throws NoSuchMethodException + * if a class described in the serialised data cannot be created + * because it is not compatible with this code + * @throws ClassNotFoundException + * if a class described in the serialised data cannot be found + */ + private boolean processLine(String line) throws NoSuchFieldException, + NoSuchMethodException, ClassNotFoundException { // Defer to latest child if any if (child != null) { if (child.processLine(line)) { @@ -118,8 +151,7 @@ public class Importer { } private void setField(String name, Object value) - throws NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException { + throws NoSuchFieldException { try { Field field = me.getClass().getDeclaredField(name); @@ -130,9 +162,18 @@ public class Importer { throw new NoSuchFieldException(String.format( "Field \"%s\" was not found in object of type \"%s\".", name, me.getClass().getCanonicalName())); + } catch (Exception e) { + throw new NoSuchFieldException(String.format( + "Internal error when setting \"%s.%s\": %s", me.getClass() + .getCanonicalName(), name, e.getMessage())); } } + /** + * Return the current deserialised value. + * + * @return the current value + */ public Object getValue() { return me; } diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java index 3770c58..49817b2 100644 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ b/src/be/nikiroo/utils/serial/SerialUtils.java @@ -1,15 +1,13 @@ package be.nikiroo.utils.serial; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.io.NotSerializableException; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; /** - * Small class to help serialise/deserialise objects. + * 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. @@ -21,9 +19,9 @@ public class SerialUtils { static { customTypes = new HashMap(); - // TODO: add "default" custom serialisers + // TODO: add "default" custom serialisers if any (Bitmap?) } - + /** * Create an empty object of the given type. * @@ -32,18 +30,16 @@ public class SerialUtils { * * @return the new object * - * @throws NoSuchMethodException if the given class is not compatible with this code - * @throws ClassNotFoundException if the class cannot be found or created + * @throws ClassNotFoundException + * if the class cannot be found + * @throws NoSuchMethodException + * if the given class is not compatible with this code */ - public static Object createObject(String type) throws NoSuchMethodException, - ClassNotFoundException { + public static Object createObject(String type) + throws ClassNotFoundException, NoSuchMethodException { try { Class clazz = getClass(type); - if (clazz == null) { - throw new ClassNotFoundException("Class not found: " + type); - } - String className = clazz.getName(); Object[] args = null; Constructor ctor = null; @@ -60,25 +56,47 @@ public class SerialUtils { ctor.setAccessible(true); return ctor.newInstance(args); + } catch (ClassNotFoundException e) { + throw e; } catch (NoSuchMethodException e) { - throw new NoSuchMethodException( - String.format( - "Objects of type \"%s\" cannot be created by this code: maybe the class" - + " or its enclosing class doesn't have an empty constructor?", - type)); - + throw e; + } catch (Exception e) { + throw new NoSuchMethodException("Cannot instantiate: " + type); } - catch (SecurityException e) { throw new ClassNotFoundException("Cannot instantiate: " + type, e); } - catch (InstantiationException e) { throw new ClassNotFoundException("Cannot instantiate: " + type, e); } - catch (IllegalAccessException e) { throw new ClassNotFoundException("Cannot instantiate: " + type, e); } - catch (IllegalArgumentException e) { throw new ClassNotFoundException("Cannot instantiate: " + type, e); } - catch (InvocationTargetException e) { throw new ClassNotFoundException("Cannot instantiate: " + type, e); } } + /** + * Insert a custom serialiser that will take precedence over the default one + * or the target class. + * + * @param serializer + * the custom serialiser + */ static public void addCustomSerializer(CustomSerializer serializer) { customTypes.put(serializer.getType(), serializer); } + /** + * Serialise the given object into this {@link StringBuilder}. + *

+ * Important: If the operation fails (with a + * {@link NotSerializableException}), the {@link StringBuilder} will be + * corrupted (will contain bad, most probably not importable data). + * + * @param builder + * the output {@link StringBuilder} to serialise to + * @param o + * the object to serialise + * @param map + * the map of already serialised objects (if the given object or + * one of its descendant is already present in it, only an ID + * will be serialised) + * + * @throws NotSerializableException + * if the object cannot be serialised (in this case, the + * {@link StringBuilder} can contain bad, most probably not + * importable data) + */ static void append(StringBuilder builder, Object o, Map map) throws NotSerializableException { @@ -206,9 +224,24 @@ public class SerialUtils { return Integer.parseInt(encodedValue); } } - - static private Class getClass(String type) throws ClassNotFoundException, - NoSuchMethodException { + + /** + * Return the corresponding class or throw an {@link Exception} if it + * cannot. + * + * @param type + * the class name to look for + * + * @return the class (will never be NULL) + * + * @throws ClassNotFoundException + * if the class cannot be found + * @throws NoSuchMethodException + * if the class cannot be created (usually because it or its + * enclosing class doesn't have an empty constructor) + */ + static private Class getClass(String type) + throws ClassNotFoundException, NoSuchMethodException { Class clazz = null; try { clazz = Class.forName(type); @@ -235,9 +268,13 @@ public class SerialUtils { } } + if (clazz == null) { + throw new ClassNotFoundException("Class not found: " + type); + } + return clazz; } - + // aa bb -> "aa\tbb" private static void encodeString(StringBuilder builder, String raw) { builder.append('\"'); -- 2.27.0