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;
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;
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 []
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());
val = ((URL) value).toString();
}
- out.write(val.getBytes("UTF-8"));
+ out.write(StringUtils.getBytes(val));
}
@Override
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 {
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());
continue;
}
- write(out, "\n");
+ write(out, "\n^");
write(out, field.getName());
write(out, ":");
} 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, ".");
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.
* <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>.
+ * encode, like an Integer or a String (see
+ * {@link SerialUtils#decode(String)}.
+ * <p>
+ * Custom objects and arrays are also considered supported here, but
+ * <b>compound objects are not</b>.
+ * <p>
+ * 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.
+ * <p>
+ * A supported object in this context means an object we can directly
+ * encode, like an Integer or a String.
+ * <p>
+ * For custom objects and arrays, you should use
+ * {@link SerialUtils#decode(InputStream)} or directly {@link Importer}.
* <p>
* For compound objects, you should use {@link Importer}.
*
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) {
* 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()));
}
/**
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 '\\':