84fb5aae5742f49bcf68cd915906a2b7322a399b
1 package be
.nikiroo
.utils
.serial
;
3 import java
.io
.IOException
;
4 import java
.lang
.reflect
.Field
;
5 import java
.util
.HashMap
;
7 import java
.util
.Scanner
;
9 import be
.nikiroo
.utils
.StringUtils
;
12 * A simple class that can accept the output of {@link Exporter} to recreate
13 * objects as they were sent to said exporter.
15 * This class requires the objects (and their potential enclosing objects) to
16 * have an empty constructor, and does not support inner classes (it does
17 * support nested classes, though).
21 public class Importer
{
24 private Importer child
;
25 private Map
<String
, Object
> map
;
27 private String currentFieldName
;
30 * Create a new {@link Importer}.
33 map
= new HashMap
<String
, Object
>();
34 map
.put("NULL", null);
37 private Importer(Map
<String
, Object
> map
) {
42 * Read some data into this {@link Importer}: it can be the full serialised
43 * content, or a number of lines of it (any given line <b>MUST</b> be
44 * complete though) and accumulate it with the already present data.
49 * @return itself so it can be chained
51 * @throws NoSuchFieldException
52 * if the serialised data contains information about a field
53 * which does actually not exist in the class we know of
54 * @throws NoSuchMethodException
55 * if a class described in the serialised data cannot be created
56 * because it is not compatible with this code
57 * @throws ClassNotFoundException
58 * if a class described in the serialised data cannot be found
60 * if the content cannot be read (for instance, corrupt data)
62 public Importer
read(String data
) throws NoSuchFieldException
,
63 NoSuchMethodException
, ClassNotFoundException
, IOException
{
65 Scanner scan
= new Scanner(data
);
67 scan
.useDelimiter("\n");
68 while (scan
.hasNext()) {
69 String line
= scan
.next();
71 if (line
.startsWith("ZIP:")) {
73 line
= StringUtils
.unbase64s(
74 line
.substring("ZIP:".length()), true);
75 } catch (IOException e
) {
76 throw new IOException(
77 "Internal error when decoding ZIP content: input may be corrupt");
80 } else if (line
.startsWith("B64:")) {
82 line
= StringUtils
.unbase64s(
83 line
.substring("B64:".length()), false);
84 } catch (IOException e
) {
85 throw new IOException(
86 "Internal error when decoding B64 content: input may be corrupt");
101 * Read a single (whole) line of serialised data into this {@link Importer}
102 * and accumulate it with the already present data.
107 * @return TRUE if we are just done with one object or sub-object
109 * @throws NoSuchFieldException
110 * if the serialised data contains information about a field
111 * which does actually not exist in the class we know of
112 * @throws NoSuchMethodException
113 * if a class described in the serialised data cannot be created
114 * because it is not compatible with this code
115 * @throws ClassNotFoundException
116 * if a class described in the serialised data cannot be found
117 * @throws IOException
118 * if the content cannot be read (for instance, corrupt data)
120 private boolean processLine(String line
) throws NoSuchFieldException
,
121 NoSuchMethodException
, ClassNotFoundException
, IOException
{
122 // Defer to latest child if any
124 if (child
.processLine(line
)) {
125 if (currentFieldName
!= null) {
126 setField(currentFieldName
, child
.getValue());
127 currentFieldName
= null;
135 if (line
.equals("{")) { // START: new child if needed
137 child
= new Importer(map
);
139 } else if (line
.equals("}")) { // STOP: report self to parent
141 } else if (line
.startsWith("REF ")) { // REF: create/link self
142 String
[] tab
= line
.substring("REF ".length()).split("@");
143 String type
= tab
[0];
144 tab
= tab
[1].split(":");
147 link
= map
.containsKey(ref
);
151 if (line
.endsWith(":")) {
153 me
= SerialUtils
.createObject(type
);
156 int pos
= line
.indexOf(":");
157 String encodedValue
= line
.substring(pos
+ 1);
158 me
= SerialUtils
.decode(encodedValue
);
162 } else { // FIELD: new field *or* direct simple value
163 if (line
.endsWith(":")) {
164 // field value is compound
165 currentFieldName
= line
.substring(0, line
.length() - 1);
166 } else if (line
.startsWith(":") || !line
.contains(":")
167 || line
.startsWith("\"") || CustomSerializer
.isCustom(line
)) {
168 // not a field value but a direct value
169 me
= SerialUtils
.decode(line
);
171 // field value is direct
172 int pos
= line
.indexOf(":");
173 String fieldName
= line
.substring(0, pos
);
174 String encodedValue
= line
.substring(pos
+ 1);
176 value
= SerialUtils
.decode(encodedValue
);
178 // To support simple types directly:
182 setField(fieldName
, value
);
190 private void setField(String name
, Object value
)
191 throws NoSuchFieldException
{
194 Field field
= me
.getClass().getDeclaredField(name
);
196 field
.setAccessible(true);
197 field
.set(me
, value
);
198 } catch (NoSuchFieldException e
) {
199 throw new NoSuchFieldException(String
.format(
200 "Field \"%s\" was not found in object of type \"%s\".",
201 name
, me
.getClass().getCanonicalName()));
202 } catch (Exception e
) {
203 throw new NoSuchFieldException(String
.format(
204 "Internal error when setting \"%s.%s\": %s", me
.getClass()
205 .getCanonicalName(), name
, e
.getMessage()));
210 * Return the current deserialised value.
212 * @return the current value
214 public Object
getValue() {