1 package be
.nikiroo
.utils
.serial
;
3 import java
.io
.NotSerializableException
;
4 import java
.lang
.reflect
.Field
;
5 import java
.util
.HashMap
;
9 * Small class to help serialise/deserialise objects.
11 * Note that we do not support inner classes (but we do support nested classes)
12 * and all objects require an empty constructor to be deserialised.
17 private static Map
<String
, CustomSerializer
> customTypes
;
20 customTypes
= new HashMap
<String
, CustomSerializer
>();
21 // TODO: add "default" custom serialisers
24 static public void addCustomSerializer(CustomSerializer serializer
) {
25 customTypes
.put(serializer
.getType(), serializer
);
28 static void append(StringBuilder builder
, Object o
, Map
<Integer
, Object
> map
)
29 throws NotSerializableException
{
31 Field
[] fields
= new Field
[] {};
36 int hash
= System
.identityHashCode(o
);
37 fields
= o
.getClass().getDeclaredFields();
38 type
= o
.getClass().getCanonicalName();
40 throw new NotSerializableException(
42 "Cannot find the class for this object: %s (it could be an inner class, which is not supported)",
45 id
= Integer
.toString(hash
);
46 if (map
.containsKey(hash
)) {
47 fields
= new Field
[] {};
53 builder
.append("{\nREF ").append(type
).append("@").append(id
);
55 for (Field field
: fields
) {
56 field
.setAccessible(true);
58 if (field
.getName().startsWith("this$")) {
59 // Do not keep this links of nested classes
64 builder
.append(field
.getName());
70 if (!encode(builder
, value
)) {
72 append(builder
, value
, map
);
75 } catch (IllegalArgumentException e
) {
76 e
.printStackTrace(); // should not happen (see setAccessible)
77 } catch (IllegalAccessException e
) {
78 e
.printStackTrace(); // should not happen (see setAccessible)
80 builder
.append("\n}");
83 // return true if encoded (supported)
84 static boolean encode(StringBuilder builder
, Object value
) {
86 builder
.append("NULL");
87 } else if (customTypes
.containsKey(value
.getClass().getCanonicalName())) {
88 customTypes
.get(value
.getClass().getCanonicalName())//
89 .encode(builder
, value
);
90 } else if (value
instanceof String
) {
91 encodeString(builder
, (String
) value
);
92 } else if (value
instanceof Boolean
) {
93 builder
.append(value
);
94 } else if (value
instanceof Byte
) {
95 builder
.append(value
).append('b');
96 } else if (value
instanceof Character
) {
97 encodeString(builder
, (String
) value
);
99 } else if (value
instanceof Short
) {
100 builder
.append(value
).append('s');
101 } else if (value
instanceof Integer
) {
102 builder
.append(value
);
103 } else if (value
instanceof Long
) {
104 builder
.append(value
).append('L');
105 } else if (value
instanceof Float
) {
106 builder
.append(value
).append('F');
107 } else if (value
instanceof Double
) {
108 builder
.append(value
).append('d');
116 static Object
decode(String encodedValue
) {
118 if (encodedValue
.length() > 1) {
119 cut
= encodedValue
.substring(0, encodedValue
.length() - 1);
122 if (CustomSerializer
.isCustom(encodedValue
)) {
123 // custom:TYPE_NAME:"content is String-encoded"
124 String type
= CustomSerializer
.typeOf(encodedValue
);
125 if (customTypes
.containsKey(type
)) {
126 return customTypes
.get(type
).decode(encodedValue
);
128 throw new java
.util
.UnknownFormatConversionException(
129 "Unknown custom type: " + type
);
131 } else if (encodedValue
.equals("NULL") || encodedValue
.equals("null")) {
133 } else if (encodedValue
.endsWith("\"")) {
134 return decodeString(encodedValue
);
135 } else if (encodedValue
.equals("true")) {
137 } else if (encodedValue
.equals("false")) {
139 } else if (encodedValue
.endsWith("b")) {
140 return Byte
.parseByte(cut
);
141 } else if (encodedValue
.endsWith("c")) {
142 return decodeString(cut
).charAt(0);
143 } else if (encodedValue
.endsWith("s")) {
144 return Short
.parseShort(cut
);
145 } else if (encodedValue
.endsWith("L")) {
146 return Long
.parseLong(cut
);
147 } else if (encodedValue
.endsWith("F")) {
148 return Float
.parseFloat(cut
);
149 } else if (encodedValue
.endsWith("d")) {
150 return Double
.parseDouble(cut
);
152 return Integer
.parseInt(encodedValue
);
157 private static void encodeString(StringBuilder builder
, String raw
) {
158 builder
.append('\"');
159 for (char car
: raw
.toCharArray()) {
162 builder
.append("\\\\");
165 builder
.append("\\r");
168 builder
.append("\\n");
171 builder
.append("\\\"");
178 builder
.append('\"');
182 private static String
decodeString(String escaped
) {
183 StringBuilder builder
= new StringBuilder();
185 boolean escaping
= false;
186 for (char car
: escaped
.toCharArray()) {
196 builder
.append('\\');
199 builder
.append('\r');
202 builder
.append('\n');
212 return builder
.substring(1, builder
.length() - 1).toString();