jDoc
[nikiroo-utils.git] / src / be / nikiroo / utils / serial / SerialUtils.java
index decf57525fb30336ab00dee5be5bb2952d5f8196..114f80b2576a57a08177d944adfe755da2665544 100644 (file)
@@ -1,17 +1,19 @@
 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.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UnknownFormatConversionException;
 
-import be.nikiroo.utils.ImageUtils;
+import be.nikiroo.utils.Image;
 
 /**
  * Small class to help with serialisation.
@@ -37,6 +39,7 @@ import be.nikiroo.utils.ImageUtils;
  * <li>Enum (any enum whose name and value is known by the caller)</li>
  * <li>java.awt.image.BufferedImage (as a {@link CustomSerializer})</li>
  * <li>An array of the above (as a {@link CustomSerializer})</li>
+ * <li>URL</li>
  * </ul>
  * 
  * @author niki
@@ -106,26 +109,46 @@ public class SerialUtils {
                        }
                });
 
-               // Images (this is currently the only supported image type by default)
-               customTypes.put("java.awt.image.BufferedImage", new CustomSerializer() {
+               // URL:
+               customTypes.put("java.net.URL", new CustomSerializer() {
                        @Override
                        protected String toString(Object value) {
-                               try {
-                                       return ImageUtils.toBase64((BufferedImage) value);
-                               } catch (IOException e) {
-                                       throw new UnknownFormatConversionException(e.getMessage());
+                               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.awt.image.BufferedImage";
+                               return "java.net.URL";
+                       }
+               });
+
+               // Images (this is currently the only supported image type by default)
+               customTypes.put("be.nikiroo.utils.Image", new CustomSerializer() {
+                       @Override
+                       protected String toString(Object value) {
+                               return ((Image) value).toBase64();
+                       }
+
+                       @Override
+                       protected String getType() {
+                               return "be.nikiroo.utils.Image";
                        }
 
                        @Override
                        protected Object fromString(String content) {
                                try {
-                                       return ImageUtils.fromBase64(content);
+                                       return new Image(content);
                                } catch (IOException e) {
                                        throw new UnknownFormatConversionException(e.getMessage());
                                }
@@ -149,27 +172,59 @@ public class SerialUtils {
        public static Object createObject(String type)
                        throws ClassNotFoundException, NoSuchMethodException {
 
+               String desc = null;
+
                try {
                        Class<?> clazz = getClass(type);
                        String className = clazz.getName();
-                       Object[] args = null;
+                       List<Object> args = new ArrayList<Object>();
+                       List<Class<?>> classes = new ArrayList<Class<?>>();
                        Constructor<?> ctor = null;
                        if (className.contains("$")) {
-                               Object javaParent = createObject(className.substring(0,
-                                               className.lastIndexOf('$')));
-                               args = new Object[] { javaParent };
-                               ctor = clazz.getDeclaredConstructor(new Class[] { javaParent
-                                               .getClass() });
+                               for (String parentName = className.substring(0,
+                                               className.lastIndexOf('$'));; parentName = parentName
+                                               .substring(0, parentName.lastIndexOf('$'))) {
+                                       Object parent = createObject(parentName);
+                                       args.add(parent);
+                                       classes.add(parent.getClass());
+
+                                       if (!parentName.contains("$")) {
+                                               break;
+                                       }
+                               }
+
+                               // Better error description in case there is no empty
+                               // constructor:
+                               desc = "";
+                               String end = "";
+                               for (Class<?> parent = clazz; parent != null
+                                               && !parent.equals(Object.class); parent = parent
+                                               .getSuperclass()) {
+                                       if (!desc.isEmpty()) {
+                                               desc += " [:";
+                                               end += "]";
+                                       }
+                                       desc += parent;
+                               }
+                               desc += end;
+                               //
+
+                               ctor = clazz.getDeclaredConstructor(classes
+                                               .toArray(new Class[] {}));
+                               desc = null;
                        } else {
-                               args = new Object[] {};
                                ctor = clazz.getDeclaredConstructor();
                        }
 
                        ctor.setAccessible(true);
-                       return ctor.newInstance(args);
+                       return ctor.newInstance(args.toArray());
                } catch (ClassNotFoundException e) {
                        throw e;
                } catch (NoSuchMethodException e) {
+                       if (desc != null) {
+                               throw new NoSuchMethodException("Empty constructor not found: "
+                                               + desc);
+                       }
                        throw e;
                } catch (Exception e) {
                        throw new NoSuchMethodException("Cannot instantiate: " + type);
@@ -220,10 +275,8 @@ public class SerialUtils {
                        fields = o.getClass().getDeclaredFields();
                        type = o.getClass().getCanonicalName();
                        if (type == null) {
-                               throw new NotSerializableException(
-                                               String.format(
-                                                               "Cannot find the class for this object: %s (it could be an inner class, which is not supported)",
-                                                               o));
+                               // Anonymous inner classes support
+                               type = o.getClass().getName();
                        }
                        id = Integer.toString(hash);
                        if (map.containsKey(hash)) {
@@ -273,7 +326,13 @@ public class SerialUtils {
        }
 
        /**
-        * Encode the object into the given builder if possible (if supported).
+        * Encode the object into the given builder if possible and if supported.
+        * <p>
+        * A supported object in this context means an object we can directly
+        * encode, like an Integer or a String. Custom objects and arrays are also
+        * considered supported, but <b>compound objects are not supported here</b>.
+        * <p>
+        * For compound objects, you should use {@link Exporter}.
         * 
         * @param builder
         *            the builder to append to
@@ -286,7 +345,9 @@ public class SerialUtils {
        static boolean encode(StringBuilder builder, Object value) {
                if (value == null) {
                        builder.append("NULL");
-               } else if (value.getClass().getCanonicalName().endsWith("[]")) {
+               } else if (value.getClass().getSimpleName().endsWith("[]")) {
+                       // Simple name does support [] suffix and do not return NULL for
+                       // inner anonymous classes
                        return customTypes.get("[]").encode(builder, value);
                } else if (customTypes.containsKey(value.getClass().getCanonicalName())) {
                        return customTypes.get(value.getClass().getCanonicalName())//
@@ -322,7 +383,13 @@ public class SerialUtils {
        }
 
        /**
-        * Decode the data into an equivalent source object.
+        * Decode the data into an equivalent supported source object.
+        * <p>
+        * A supported object in this context means an object we can directly
+        * encode, like an Integer or a String. Custom objects and arrays are also
+        * considered supported, but <b>compound objects are not supported here</b>.
+        * <p>
+        * For compound objects, you should use {@link Importer}.
         * 
         * @param encodedValue
         *            the encoded data, cannot be NULL
@@ -501,6 +568,6 @@ public class SerialUtils {
                        }
                }
 
-               return builder.substring(1, builder.length() - 1).toString();
+               return builder.substring(1, builder.length() - 1);
        }
 }