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 map
= new HashMap
<String
, Object
>();
31 map
.put("NULL", null);
34 private Importer(Map
<String
, Object
> map
) {
39 * Read some data into this {@link Importer}: it can be the full serialised
40 * content, or a number of lines of it (any given line <b>MUST</b> be
41 * complete though) and accumulate it with the already present data.
46 * @return itself so it can be chained
48 * @throws NoSuchFieldException
49 * if the serialised data contains information about a field
50 * which does actually not exist in the class we know of
51 * @throws NoSuchMethodException
52 * if a class described in the serialised data cannot be created
53 * because it is not compatible with this code
54 * @throws ClassNotFoundException
55 * if a class described in the serialised data cannot be found
57 public Importer
read(String data
) throws NoSuchFieldException
,
58 NoSuchMethodException
, ClassNotFoundException
{
61 Scanner scan
= new Scanner(data
);
62 scan
.useDelimiter("\n");
63 while (scan
.hasNext()) {
64 String line
= scan
.next();
66 if (line
.startsWith("ZIP:")) {
67 line
= StringUtils
.unzip64(line
.substring("ZIP:".length()));
73 } catch (IOException e
) {
74 throw new NoSuchMethodException(
75 "Internal error when decoding ZIP content: input may be corrupt");
82 * Read a single (whole) line of serialised data into this {@link Importer}
83 * and accumulate it with the already present data.
88 * @return TRUE if we are just done with one object or sub-object
90 * @throws NoSuchFieldException
91 * if the serialised data contains information about a field
92 * which does actually not exist in the class we know of
93 * @throws NoSuchMethodException
94 * if a class described in the serialised data cannot be created
95 * because it is not compatible with this code
96 * @throws ClassNotFoundException
97 * if a class described in the serialised data cannot be found
99 private boolean processLine(String line
) throws NoSuchFieldException
,
100 NoSuchMethodException
, ClassNotFoundException
{
101 // Defer to latest child if any
103 if (child
.processLine(line
)) {
104 if (currentFieldName
!= null) {
105 setField(currentFieldName
, child
.getValue());
106 currentFieldName
= null;
114 if (line
.equals("{")) { // START: new child if needed
116 child
= new Importer(map
);
118 } else if (line
.equals("}")) { // STOP: report self to parent
120 } else if (line
.startsWith("REF ")) { // REF: create/link self
121 String ref
= line
.substring(4).split("@")[1];
122 link
= map
.containsKey(ref
);
126 me
= SerialUtils
.createObject(line
.substring(4).split("@")[0]);
129 } else { // FIELD: new field
130 if (line
.endsWith(":")) {
131 // field value is compound
132 currentFieldName
= line
.substring(0, line
.length() - 1);
134 // field value is direct
135 int pos
= line
.indexOf(":");
136 String fieldName
= line
.substring(0, pos
);
137 String encodedValue
= line
.substring(pos
+ 1);
139 value
= SerialUtils
.decode(encodedValue
);
141 // To support simple types directly:
145 setField(fieldName
, value
);
153 private void setField(String name
, Object value
)
154 throws NoSuchFieldException
{
157 Field field
= me
.getClass().getDeclaredField(name
);
159 field
.setAccessible(true);
160 field
.set(me
, value
);
161 } catch (NoSuchFieldException e
) {
162 throw new NoSuchFieldException(String
.format(
163 "Field \"%s\" was not found in object of type \"%s\".",
164 name
, me
.getClass().getCanonicalName()));
165 } catch (Exception e
) {
166 throw new NoSuchFieldException(String
.format(
167 "Internal error when setting \"%s.%s\": %s", me
.getClass()
168 .getCanonicalName(), name
, e
.getMessage()));
173 * Return the current deserialised value.
175 * @return the current value
177 public Object
getValue() {