Commit | Line | Data |
---|---|---|
db31c358 NR |
1 | package be.nikiroo.utils.serial; |
2 | ||
db31c358 NR |
3 | import java.util.HashMap; |
4 | import java.util.Map; | |
5 | import java.util.Scanner; | |
aad14586 NR |
6 | import java.lang.reflect.InvocationTargetException; |
7 | import java.lang.reflect.Field; | |
db31c358 NR |
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 { | |
aad14586 | 93 | me = SerialUtils.createObject(line.substring(4).split("@")[0]); |
db31c358 NR |
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 | ||
db31c358 NR |
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 | } |