X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fserial%2FSerialUtils.java;h=ad3b5d4da2a8dae4955e12ce28f2cfd9a51fc5f7;hb=505be508ae7d3fb48122be548b310a238cfb91eb;hp=a6a02a8e06205cb5996b0a94c1124056ce2cbf60;hpb=08f80ac5fa60738d3ad74c4b5390a0b79ae313d4;p=fanfix.git diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java index a6a02a8..ad3b5d4 100644 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ b/src/be/nikiroo/utils/serial/SerialUtils.java @@ -1,11 +1,9 @@ package be.nikiroo.utils.serial; -import java.io.ByteArrayInputStream; 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; @@ -20,6 +18,9 @@ import java.util.UnknownFormatConversionException; import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.Image; import be.nikiroo.utils.StringUtils; +import be.nikiroo.utils.streams.Base64InputStream; +import be.nikiroo.utils.streams.Base64OutputStream; +import be.nikiroo.utils.streams.BufferedInputStream; import be.nikiroo.utils.streams.NextableInputStream; import be.nikiroo.utils.streams.NextableInputStreamStep; @@ -64,8 +65,6 @@ public class SerialUtils { protected void toStream(OutputStream out, Object value) throws IOException { - // TODO: we use \n to separate, and b64 to un-\n - // -- but we could use \\n ? String type = value.getClass().getCanonicalName(); type = type.substring(0, type.length() - 2); // remove the [] @@ -78,11 +77,11 @@ public class SerialUtils { write(out, "\r"); if (!SerialUtils.encode(out, item)) { try { - // TODO: bad escaping? write(out, "B64:"); - OutputStream bout = StringUtils.base64(out, - false, false); - new Exporter(bout).append(item); + OutputStream out64 = new Base64OutputStream( + out, true); + new Exporter(out64).append(item); + out64.flush(); } catch (NotSerializableException e) { throw new UnknownFormatConversionException(e .getMessage()); @@ -140,7 +139,7 @@ public class SerialUtils { val = ((URL) value).toString(); } - out.write(val.getBytes("UTF-8")); + out.write(StringUtils.getBytes(val)); } @Override @@ -165,7 +164,7 @@ public class SerialUtils { protected void toStream(OutputStream out, Object value) throws IOException { Image img = (Image) value; - OutputStream encoded = StringUtils.base64(out, false, false); + OutputStream encoded = new Base64OutputStream(out, true); try { InputStream in = img.newInputStream(); try { @@ -188,7 +187,7 @@ public class SerialUtils { protected Object fromStream(InputStream in) throws IOException { try { // Cannot close it! - InputStream decoded = StringUtils.unbase64(in, false); + InputStream decoded = new Base64InputStream(in, false); return new Image(decoded); } catch (IOException e) { throw new UnknownFormatConversionException(e.getMessage()); @@ -358,7 +357,7 @@ public class SerialUtils { continue; } - write(out, "\n"); + write(out, "\n^"); write(out, field.getName()); write(out, ":"); @@ -417,26 +416,28 @@ public class SerialUtils { } else if (value instanceof Boolean) { write(out, value); } else if (value instanceof Byte) { - write(out, value); write(out, "b"); + write(out, value); } else if (value instanceof Character) { - encodeString(out, "" + value); write(out, "c"); + encodeString(out, "" + value); } else if (value instanceof Short) { - write(out, value); write(out, "s"); + write(out, value); } else if (value instanceof Integer) { + write(out, "i"); write(out, value); } else if (value instanceof Long) { + write(out, "l"); write(out, value); - write(out, "L"); } else if (value instanceof Float) { + write(out, "f"); write(out, value); - write(out, "F"); } else if (value instanceof Double) { - write(out, value); write(out, "d"); + write(out, value); } else if (value instanceof Enum) { + write(out, "E:"); String type = value.getClass().getCanonicalName(); write(out, type); write(out, "."); @@ -449,12 +450,83 @@ public class SerialUtils { return true; } + static boolean isDirectValue(BufferedInputStream encodedValue) + throws IOException { + if (CustomSerializer.isCustom(encodedValue)) { + return false; + } + + for (String fullValue : new String[] { "NULL", "null", "true", "false" }) { + if (encodedValue.is(fullValue)) { + return true; + } + } + + for (String prefix : new String[] { "c\"", "\"", "b", "s", "i", "l", + "f", "d", "E:" }) { + if (encodedValue.startsWith(prefix)) { + return true; + } + } + + return false; + } + /** * 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. + * encode, like an Integer or a String (see + * {@link SerialUtils#decode(String)}. + *

+ * Custom objects and arrays are also considered supported here, but + * compound objects are not. + *

+ * For compound objects, you should use {@link Importer}. + * + * @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(BufferedInputStream encodedValue) throws IOException { + if (CustomSerializer.isCustom(encodedValue)) { + // custom^TYPE^ENCODED_VALUE + NextableInputStream content = new NextableInputStream(encodedValue, + new NextableInputStreamStep('^')); + try { + content.next(); + @SuppressWarnings("unused") + String custom = IOUtils.readSmallStream(content); + content.next(); + String type = IOUtils.readSmallStream(content); + content.nextAll(); + if (customTypes.containsKey(type)) { + return customTypes.get(type).decode(content); + } + content.end(); + throw new IOException("Unknown custom type: " + type); + } finally { + content.close(false); + encodedValue.end(); + } + } + + String encodedString = IOUtils.readSmallStream(encodedValue); + return decode(encodedString); + } + + /** + * 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. + *

+ * For custom objects and arrays, you should use + * {@link SerialUtils#decode(InputStream)} or directly {@link Importer}. *

* For compound objects, you should use {@link Importer}. * @@ -470,48 +542,36 @@ public class SerialUtils { try { String cut = ""; if (encodedValue.length() > 1) { - cut = encodedValue.substring(0, encodedValue.length() - 1); + cut = encodedValue.substring(1); } - if (CustomSerializer.isCustom(encodedValue)) { - // custom:TYPE_NAME:"content is String-encoded" - String type = CustomSerializer.typeOf(encodedValue); - if (customTypes.containsKey(type)) { - // TODO: we should start with a stream - InputStream streamEncodedValue = new ByteArrayInputStream( - encodedValue.getBytes("UTF-8")); - try { - return customTypes.get(type).decode(streamEncodedValue); - } finally { - streamEncodedValue.close(); - } - } - throw new IOException("Unknown custom type: " + type); - } else if (encodedValue.equals("NULL") - || encodedValue.equals("null")) { + if (encodedValue.equals("NULL") || encodedValue.equals("null")) { return null; - } else if (encodedValue.endsWith("\"")) { + } else if (encodedValue.startsWith("\"")) { return decodeString(encodedValue); } else if (encodedValue.equals("true")) { return true; } else if (encodedValue.equals("false")) { return false; - } else if (encodedValue.endsWith("b")) { + } else if (encodedValue.startsWith("b")) { return Byte.parseByte(cut); - } else if (encodedValue.endsWith("c")) { + } else if (encodedValue.startsWith("c")) { return decodeString(cut).charAt(0); - } else if (encodedValue.endsWith("s")) { + } else if (encodedValue.startsWith("s")) { return Short.parseShort(cut); - } else if (encodedValue.endsWith("L")) { + } else if (encodedValue.startsWith("l")) { return Long.parseLong(cut); - } else if (encodedValue.endsWith("F")) { + } else if (encodedValue.startsWith("f")) { return Float.parseFloat(cut); - } else if (encodedValue.endsWith("d")) { + } else if (encodedValue.startsWith("d")) { return Double.parseDouble(cut); - } else if (encodedValue.endsWith(";")) { - return decodeEnum(encodedValue); + } else if (encodedValue.startsWith("i")) { + return Integer.parseInt(cut); + } else if (encodedValue.startsWith("E:")) { + cut = cut.substring(1); + return decodeEnum(cut); } else { - return Integer.parseInt(encodedValue); + throw new IOException("Unrecognized value: " + encodedValue); } } catch (Exception e) { if (e instanceof IOException) { @@ -534,12 +594,7 @@ public class SerialUtils { * 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(); - } + out.write(StringUtils.getBytes(data.toString())); } /** @@ -611,28 +666,13 @@ public class SerialUtils { static void encodeString(OutputStream out, String raw) throws IOException { // TODO: not. efficient. out.write('\"'); - // TODO !! utf-8 required for (char car : raw.toCharArray()) { encodeString(out, car); } out.write('\"'); } - // aa bb -> "aa\tbb" - static void encodeString(OutputStream out, InputStream raw) - throws IOException { - out.write('\"'); - byte buffer[] = new byte[4096]; - 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]); - } - } - out.write('\"'); - } - - // for encode string, NOT to encode a char by itself! + // for encoding string, NOT to encode a char by itself! static void encodeString(OutputStream out, char raw) throws IOException { switch (raw) { case '\\':