X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fserial%2FSerialUtils.java;h=e2d8decea64113bfe76ac0962196e325a2fcf2d8;hb=68532958426cd3c6187aa89eae4f137bc057293b;hp=61bca4f6d1a9b0e238c44b72c4589327d53d09e8;hpb=72648e757f648cd152bc00dfb83f895260f037a0;p=nikiroo-utils.git diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java index 61bca4f..e2d8dec 100644 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ b/src/be/nikiroo/utils/serial/SerialUtils.java @@ -1,7 +1,10 @@ package be.nikiroo.utils.serial; import java.io.IOException; +import java.io.InputStream; import java.io.NotSerializableException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -66,8 +69,8 @@ public class SerialUtils { if (!SerialUtils.encode(builder, item)) { try { // use ZIP: if not - builder.append(new Exporter().append(item) - .toString(true)); + new Exporter().append(item).appendTo(builder, + true, false); } catch (NotSerializableException e) { throw new UnknownFormatConversionException(e .getMessage()); @@ -173,7 +176,6 @@ public class SerialUtils { throws ClassNotFoundException, NoSuchMethodException { String desc = null; - try { Class> clazz = getClass(type); String className = clazz.getName(); @@ -183,11 +185,11 @@ public class SerialUtils { if (className.contains("$")) { for (String parentName = className.substring(0, className.lastIndexOf('$'));; parentName = parentName - .substring(0, parentName.lastIndexOf('$'))) { + .substring(0, parentName.lastIndexOf('$'))) { Object parent = createObject(parentName); args.add(parent); classes.add(parent.getClass()); - + if (!parentName.contains("$")) { break; } @@ -199,7 +201,7 @@ public class SerialUtils { String end = ""; for (Class> parent = clazz; parent != null && !parent.equals(Object.class); parent = parent - .getSuperclass()) { + .getSuperclass()) { if (!desc.isEmpty()) { desc += " [:"; end += "]"; @@ -209,8 +211,18 @@ public class SerialUtils { desc += end; // - ctor = clazz.getDeclaredConstructor( - classes.toArray(new Class[] {})); + try { + ctor = clazz.getDeclaredConstructor(classes + .toArray(new Class[] {})); + } catch (NoSuchMethodException nsme) { + // TODO: it seems e do not always need a parameter for each + // level, so we currently try "ALL" levels or "FIRST" level + // only -> we should check the actual rule and use it + ctor = clazz.getDeclaredConstructor(classes.get(0)); + Object firstParent = args.get(0); + args.clear(); + args.add(firstParent); + } desc = null; } else { ctor = clazz.getDeclaredConstructor(); @@ -222,8 +234,8 @@ public class SerialUtils { throw e; } catch (NoSuchMethodException e) { if (desc != null) { - throw new NoSuchMethodException( - "Empty constructor not found: " + desc); + throw new NoSuchMethodException("Empty constructor not found: " + + desc); } throw e; } catch (Exception e) { @@ -243,14 +255,14 @@ public class SerialUtils { } /** - * Serialise the given object into this {@link StringBuilder}. + * Serialise the given object into this {@link OutputStream}. *
* 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 out
+ * the output {@link OutputStream} to serialise to
* @param o
* the object to serialise
* @param map
@@ -262,9 +274,11 @@ public class SerialUtils {
* if the object cannot be serialised (in this case, the
* {@link StringBuilder} can contain bad, most probably not
* importable data)
+ * @throws IOException
+ * in case of I/O errors
*/
- static void append(StringBuilder builder, Object o, Map
+ * 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 compound objects are not supported here.
+ *
+ * For compound objects, you should use {@link Exporter}.
*
- * @param builder
- * the builder to append to
+ * @param out
+ * the {@link OutputStream} 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)
+ * @return TRUE if success, FALSE if not (the content of the
+ * {@link OutputStream} won't be changed in case of failure)
+ *
+ * @throws IOException
+ * in case of I/O error
*/
- static boolean encode(StringBuilder builder, Object value) {
+ static boolean encode(OutputStream out, Object value) throws IOException {
if (value == null) {
- builder.append("NULL");
+ write(out, "NULL");
} 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);
+ return customTypes.get("[]").encode(out, value);
} else if (customTypes.containsKey(value.getClass().getCanonicalName())) {
return customTypes.get(value.getClass().getCanonicalName())//
- .encode(builder, value);
+ .encode(out, value);
} else if (value instanceof String) {
- encodeString(builder, (String) value);
+ encodeString(out, (String) value);
} else if (value instanceof Boolean) {
- builder.append(value);
+ write(out, value);
} else if (value instanceof Byte) {
- builder.append(value).append('b');
+ write(out, value);
+ write(out, "b");
} else if (value instanceof Character) {
- encodeString(builder, "" + value);
- builder.append('c');
+ encodeString(out, "" + value);
+ write(out, "c");
} else if (value instanceof Short) {
- builder.append(value).append('s');
+ write(out, value);
+ write(out, "s");
} else if (value instanceof Integer) {
- builder.append(value);
+ write(out, value);
} else if (value instanceof Long) {
- builder.append(value).append('L');
+ write(out, value);
+ write(out, "L");
} else if (value instanceof Float) {
- builder.append(value).append('F');
+ write(out, value);
+ write(out, "F");
} else if (value instanceof Double) {
- builder.append(value).append('d');
+ write(out, value);
+ write(out, "d");
} else if (value instanceof Enum) {
String type = value.getClass().getCanonicalName();
- builder.append(type).append(".").append(((Enum>) value).name())
- .append(";");
+ write(out, type);
+ write(out, ".");
+ write(out, ((Enum>) value).name());
+ write(out, ";");
} else {
return false;
}
@@ -377,7 +411,13 @@ public class SerialUtils {
}
/**
- * Decode the data into an equivalent source object.
+ * Decode the data into an equivalent supported source object.
+ *
+ * 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 compound objects are not supported here.
+ *
+ * For compound objects, you should use {@link Importer}.
*
* @param encodedValue
* the encoded data, cannot be NULL
@@ -435,6 +475,27 @@ public class SerialUtils {
}
}
+ /**
+ * Write the given {@link String} into the given {@link OutputStream} in
+ * UTF-8.
+ *
+ * @param out
+ * the {@link OutputStream}
+ * @param data
+ * the data to write, cannot be NULL
+ *
+ * @throws IOException
+ * in case of I/O error
+ */
+ static void write(OutputStream out, Object data) throws IOException {
+ try {
+ out.write(data.toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ // A conforming JVM is required to support UTF-8
+ e.printStackTrace();
+ }
+ }
+
/**
* Return the corresponding class or throw an {@link Exception} if it
* cannot.
@@ -486,7 +547,7 @@ public class SerialUtils {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
- private static Enum> decodeEnum(String escaped) {
+ static private Enum> decodeEnum(String escaped) {
// escaped: be.xxx.EnumType.VALUE;
int pos = escaped.lastIndexOf(".");
String type = escaped.substring(0, pos);
@@ -501,32 +562,56 @@ public class SerialUtils {
}
// aa bb -> "aa\tbb"
- private static void encodeString(StringBuilder builder, String raw) {
- builder.append('\"');
+ static void encodeString(OutputStream out, String raw) throws IOException {
+ out.write('\"');
+ // TODO !! utf-8 required
for (char car : raw.toCharArray()) {
- switch (car) {
- case '\\':
- builder.append("\\\\");
- break;
- case '\r':
- builder.append("\\r");
- break;
- case '\n':
- builder.append("\\n");
- break;
- case '"':
- builder.append("\\\"");
- break;
- default:
- builder.append(car);
- break;
+ encodeString(out, car);
+ }
+ out.write('\"');
+ }
+
+ // aa bb -> "aa\tbb"
+ static void encodeString(OutputStream out, InputStream raw)
+ throws IOException {
+ out.write('\"');
+ byte buffer[] = new byte[4069];
+ for (int len = 0; (len = raw.read(buffer)) > 0;) {
+ for (int i = 0; i < len; i++) {
+ // TODO: not 100% correct, look up howto for UTF-8
+ encodeString(out, (char) buffer[i]);
}
}
- builder.append('\"');
+ out.write('\"');
+ }
+
+ // for encode string, NOT to encode a char by itself!
+ static void encodeString(OutputStream out, char raw) throws IOException {
+ switch (raw) {
+ case '\\':
+ out.write('\\');
+ out.write('\\');
+ break;
+ case '\r':
+ out.write('\\');
+ out.write('r');
+ break;
+ case '\n':
+ out.write('\\');
+ out.write('n');
+ break;
+ case '"':
+ out.write('\\');
+ out.write('\"');
+ break;
+ default:
+ out.write(raw);
+ break;
+ }
}
// "aa\tbb" -> aa bb
- private static String decodeString(String escaped) {
+ static String decodeString(String escaped) {
StringBuilder builder = new StringBuilder();
boolean escaping = false;