Update SerialUtils to be public
[nikiroo-utils.git] / src / be / nikiroo / utils / serial / Importer.java
1 package be.nikiroo.utils.serial;
2
3 import java.util.HashMap;
4 import java.util.Map;
5 import java.util.Scanner;
6 import java.lang.reflect.InvocationTargetException;
7 import java.lang.reflect.Field;
8
9 import be.nikiroo.utils.IOUtils;
10 import be.nikiroo.utils.StringUtils;
11
12 /**
13 * A simple class that can accept the output of {@link Exporter} to recreate
14 * objects as they were sent to said exporter.
15 * <p>
16 * This class requires the objects (and their potential enclosing objects) to
17 * have an empty constructor, and does not support inner classes (it does
18 * support nested classes, though).
19 *
20 * @author niki
21 */
22 public class Importer {
23 private Boolean link;
24 private Object me;
25 private Importer child;
26 private Map<String, Object> map;
27
28 private String currentFieldName;
29
30 public Importer() {
31 map = new HashMap<String, Object>();
32 map.put("NULL", null);
33 }
34
35 private Importer(Map<String, Object> map) {
36 this.map = map;
37 }
38
39 public Importer readLine(String line) {
40 try {
41 processLine(line);
42 } catch (Exception e) {
43 throw new IllegalArgumentException(e);
44 }
45 return this;
46 }
47
48 public Importer read(String data) {
49 try {
50 if (data.startsWith("ZIP:")) {
51 data = StringUtils.unzip64(data.substring("ZIP:".length()));
52 }
53 Scanner scan = new Scanner(data);
54 scan.useDelimiter("\n");
55 while (scan.hasNext()) {
56 processLine(scan.next());
57 }
58 scan.close();
59 } catch (Exception e) {
60 throw new IllegalArgumentException(e);
61 }
62 return this;
63 }
64
65 public boolean processLine(String line) throws IllegalArgumentException,
66 NoSuchFieldException, SecurityException, IllegalAccessException,
67 NoSuchMethodException, InstantiationException, ClassNotFoundException, InvocationTargetException {
68 // Defer to latest child if any
69 if (child != null) {
70 if (child.processLine(line)) {
71 if (currentFieldName != null) {
72 setField(currentFieldName, child.getValue());
73 currentFieldName = null;
74 }
75 child = null;
76 }
77
78 return false;
79 }
80
81 if (line.equals("{")) { // START: new child if needed
82 if (link != null) {
83 child = new Importer(map);
84 }
85 } else if (line.equals("}")) { // STOP: report self to parent
86 return true;
87 } else if (line.startsWith("REF ")) { // REF: create/link self
88 String ref = line.substring(4).split("@")[1];
89 link = map.containsKey(ref);
90 if (link) {
91 me = map.get(ref);
92 } else {
93 me = SerialUtils.createObject(line.substring(4).split("@")[0]);
94 map.put(ref, me);
95 }
96 } else { // FIELD: new field
97 if (line.endsWith(":")) {
98 // field value is compound
99 currentFieldName = line.substring(0, line.length() - 1);
100 } else {
101 // field value is direct
102 int pos = line.indexOf(":");
103 String fieldName = line.substring(0, pos);
104 String encodedValue = line.substring(pos + 1);
105 Object value = null;
106 value = SerialUtils.decode(encodedValue);
107
108 // To support simple types directly:
109 if (me == null) {
110 me = value;
111 } else {
112 setField(fieldName, value);
113 }
114 }
115 }
116
117 return false;
118 }
119
120 private void setField(String name, Object value)
121 throws NoSuchFieldException, SecurityException,
122 IllegalArgumentException, IllegalAccessException {
123
124 try {
125 Field field = me.getClass().getDeclaredField(name);
126
127 field.setAccessible(true);
128 field.set(me, value);
129 } catch (NoSuchFieldException e) {
130 throw new NoSuchFieldException(String.format(
131 "Field \"%s\" was not found in object of type \"%s\".",
132 name, me.getClass().getCanonicalName()));
133 }
134 }
135
136 public Object getValue() {
137 return me;
138 }
139 }