1 | static bool structure_showTiming, structure_checkTokenCount; |
2 | |
3 | static String structure(Object o) { |
4 | new structure_Data d; |
5 | structure_1(o, d); |
6 | long time = now(); |
7 | S s = str(d.out); |
8 | if (structure_checkTokenCount) { |
9 | print("token count=" + d.n); |
10 | assertEquals("token count", l(javaTokC(s)), d.n); |
11 | } |
12 | ret s; |
13 | } |
14 | |
15 | // leave to false, unless unstructure() breaks |
16 | static boolean structure_allowShortening = false; |
17 | |
18 | static int structure_shareStringsLongerThan = 20; |
19 | |
20 | static class structure_Data { |
21 | new StringBuilder out; |
22 | int stringSizeLimit; |
23 | new IdentityHashMap<O, Integer> seen; |
24 | new BitSet refd; |
25 | new HashMap<S, Int> strings; |
26 | new HashSet<S> concepts; |
27 | HashMap<Class, L<Field>> fieldsByClass = new HashMap; |
28 | Class conceptClass = findClass("Concept"); |
29 | int n; // token count |
30 | |
31 | // append single token |
32 | structure_Data append(S token) { out.append(token); ++n; ret this; } |
33 | structure_Data append(int i) { out.append(i); ++n; ret this; } |
34 | |
35 | // append multiple tokens |
36 | structure_Data append(S token, int tokCount) { out.append(token); n += tokCount; ret this; } |
37 | |
38 | // extend last token |
39 | structure_Data app(S token) { out.append(token); ret this; } |
40 | structure_Data app(int i) { out.append(i); ret this; } |
41 | } |
42 | |
43 | static void structure_1(Object o, structure_Data d) { |
44 | StringBuilder out = d.out; |
45 | |
46 | if (o == null) { d.append("null"); ret; } |
47 | |
48 | Class c = o.getClass(); |
49 | S name = c.getName(); |
50 | S dynName = shortDynamicClassName(o); |
51 | bool concept = d.conceptClass != null && d.conceptClass.isInstance(o); |
52 | L<Field> lFields = d.fieldsByClass.get(c); |
53 | |
54 | if (lFields == null) { |
55 | // these are never back-referenced (for readability) |
56 | |
57 | if (o instanceof Number) { |
58 | if (o instanceof Integer) { out.append((int) o); d.n++; ret; } |
59 | if (o instanceof Long) { out.append((long) o).append("L"); d.n++; ret; } |
60 | if (o instanceof Float) { d.append("fl ", 2); quote_impl(str(o), out); ret; } |
61 | if (o instanceof Double) { d.append("d(", 3); quote_impl(str(o), out); d.append(")"); ret; } |
62 | if (o instanceof BigInteger) { out.append("bigint(").append(o).append(")"); d.n += 4; ret; } |
63 | } |
64 | |
65 | if (o instanceof Boolean) { |
66 | d.append(((Boolean) o).booleanValue() ? "t" : "f"); ret; |
67 | } |
68 | |
69 | if (o instanceof Character) { |
70 | d.append(quoteCharacter((Character) o)); ret; |
71 | } |
72 | |
73 | if (o instanceof File) { |
74 | d.append("File ").append(quote(((File) o).getPath())); ret; |
75 | } |
76 | |
77 | // referencable objects follow |
78 | |
79 | Integer ref = d.seen.get(o); |
80 | if (o instanceof S && ref == null) |
81 | ref = d.strings.get((S) o); |
82 | |
83 | if (ref != null) { |
84 | d.refd.set(ref); |
85 | d.append("t").app(ref); ret; |
86 | } |
87 | |
88 | ref = d.n; //d.seen.size()+1; |
89 | d.seen.put(o, ref); |
90 | //d.append("m").app(ref).app(" "); // marker |
91 | |
92 | if (o instanceof S) { |
93 | S s = d.stringSizeLimit != 0 ? shorten((S) o, d.stringSizeLimit) : (S) o; |
94 | if (l(s) >= structure_shareStringsLongerThan) |
95 | d.strings.put(s, ref); |
96 | quote_impl(s, out); d.n++; ret; |
97 | } |
98 | |
99 | if (o instanceof HashSet) { |
100 | d.append("hashset "); |
101 | structure_1(new ArrayList((Set) o), d); |
102 | ret; |
103 | } |
104 | |
105 | if (o instanceof TreeSet) { |
106 | d.append("treeset "); |
107 | structure_1(new ArrayList((Set) o), d); |
108 | ret; |
109 | } |
110 | |
111 | if (o instanceof Collection && neq(name, "main$Concept$RefL")) { |
112 | d.append("["); |
113 | int l = out.length(); |
114 | for (Object x : (Collection) o) { |
115 | if (out.length() != l) d.append(", "); |
116 | structure_1(x, d); |
117 | } |
118 | d.append("]"); |
119 | ret; |
120 | } |
121 | |
122 | if (o instanceof Map) { |
123 | if (o instanceof HashMap) d.append("hm"); |
124 | d.append("{"); |
125 | int l = out.length(); |
126 | for (Object e : ((Map) o).entrySet()) { |
127 | if (out.length() != l) d.append(", "); |
128 | structure_1(((Map.Entry) e).getKey(), d); |
129 | d.append("="); |
130 | structure_1(((Map.Entry) e).getValue(), d); |
131 | } |
132 | d.append("}"); |
133 | ret; |
134 | } |
135 | |
136 | if (c.isArray()) { |
137 | if (o instanceof byte[]) { |
138 | d.append("ba ").append(quote(bytesToHex((byte[]) o))); ret; |
139 | } |
140 | |
141 | int n = Array.getLength(o); |
142 | |
143 | if (o instanceof bool[]) { |
144 | S hex = boolArrayToHex((bool[]) o); |
145 | int i = l(hex); |
146 | while (i > 0 && hex.charAt(i-1) == '0' && hex.charAt(i-2) == '0') i -= 2; |
147 | d.append("boolarray ").append(n).app(" ").append(quote(substring(hex, 0, i))); ret; |
148 | } |
149 | |
150 | S atype = "array", sep = ", "; |
151 | |
152 | if (o instanceof int[]) { |
153 | //ret "intarray " + quote(intArrayToHex((int[]) o)); |
154 | atype = "intarray"; |
155 | sep = " "; |
156 | } |
157 | |
158 | d.append(atype).append("{"); |
159 | for (int i = 0; i < n; i++) { |
160 | if (i != 0) d.append(sep); |
161 | structure_1(Array.get(o, i), d); |
162 | } |
163 | d.append("}"); ret; |
164 | } |
165 | |
166 | if (o instanceof Class) { |
167 | d.append("class(", 2).append(quote(((Class) o).getName())).append(")"); ret; |
168 | } |
169 | |
170 | if (o instanceof Throwable) { |
171 | d.append("exception(", 2).append(quote(((Throwable) o).getMessage())).append(")"); ret; |
172 | } |
173 | |
174 | if (o instanceof BitSet) { |
175 | BitSet bs = (BitSet) o; |
176 | d.append("bitset{", 2); |
177 | int l = out.length(); |
178 | for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { |
179 | if (out.length() != l) d.append(", "); |
180 | d.append(i); |
181 | } |
182 | d.append("}"); ret; |
183 | } |
184 | |
185 | // Need more cases? This should cover all library classes... |
186 | if (name.startsWith("java.") || name.startsWith("javax.")) { |
187 | d.append("j ").append(quote(str(o))); ret; // Hm. this is not unstructure-able |
188 | } |
189 | |
190 | if (name.equals("main$Lisp")) { |
191 | d.append("l(", 2); |
192 | structure_1(getOpt(o, "head"), d); |
193 | L args = cast getOpt(o, "args"); |
194 | if (nempty(args)) |
195 | for (int i = 0; i < l(args); i++) { |
196 | d.append(", "); |
197 | O arg = args.get(i); |
198 | |
199 | // sweet shortening |
200 | if (arg != null && eq(arg.getClass().getName(), "main$Lisp") && isTrue(call(arg, "isEmpty"))) |
201 | arg = get(arg, "head"); |
202 | |
203 | structure_1(arg, d); |
204 | } |
205 | d.append(")"); ret; |
206 | } |
207 | |
208 | if (concept && !d.concepts.contains(dynName)) { |
209 | d.concepts.add(dynName); |
210 | d.append("c "); |
211 | } |
212 | |
213 | // serialize an object with fields. |
214 | // first, collect all fields and values in fv. |
215 | |
216 | TreeSet<Field> fields = new TreeSet<Field>(new Comparator<Field>() { |
217 | public int compare(Field a, Field b) { |
218 | ret stdcompare(a.getName(), b.getName()); |
219 | } |
220 | }); |
221 | |
222 | while (c != Object.class) { |
223 | for (Field field : getDeclaredFields_cached(c)) { |
224 | if ((field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) |
225 | continue; |
226 | S fieldName = field.getName(); |
227 | |
228 | fields.add(field); |
229 | |
230 | // put special cases here... |
231 | } |
232 | |
233 | c = c.getSuperclass(); |
234 | } |
235 | |
236 | lFields = asList(fields); |
237 | |
238 | // put this$1 first |
239 | for (int i = 0; i < l(lFields); i++) { |
240 | Field f = lFields.get(i); |
241 | if (f.getName().equals("this$1")) { |
242 | lFields.remove(i); |
243 | lFields.add(0, f); |
244 | break; |
245 | } |
246 | } |
247 | |
248 | d.fieldsByClass.put(c, lFields); |
249 | } |
250 | |
251 | new LinkedHashMap<S, O> fv; |
252 | for (Field f : lFields) { |
253 | Object value; |
254 | try { |
255 | value = f.get(o); |
256 | } catch (Exception e) { |
257 | value = "?"; |
258 | } |
259 | |
260 | if (value != null) |
261 | fv.put(f.getName(), value); |
262 | } |
263 | |
264 | String shortName = dropPrefix("main$", name); |
265 | |
266 | // Now we have fields & values. Process fieldValues if it's a DynamicObject. |
267 | |
268 | // omit field "className" if equal to class's name |
269 | if (concept && eq(fv.get("className"), shortName)) |
270 | fv.remove("className"); |
271 | |
272 | if (o instanceof DynamicObject) { |
273 | fv.putAll((Map) fv.get("fieldValues")); |
274 | fv.remove("fieldValues"); |
275 | shortName = dynName; |
276 | fv.remove("className"); |
277 | } |
278 | |
279 | S singleField = fv.size() == 1 ? first(fv.keySet()) : null; |
280 | |
281 | // Render this$1 first because unstructure needs it for constructor call. |
282 | |
283 | d.append(shortName).append("("); |
284 | int l = out.length(); |
285 | |
286 | for (S fieldName : fv.keySet()) { |
287 | if (out.length() != l) d.append(", "); |
288 | d.append(fieldName).append("="); |
289 | structure_1(fv.get(fieldName), d); |
290 | } |
291 | |
292 | if (out.length() == l) { out.setLength(l-1); d.n--; } // drop "(" |
293 | else d.append(")"); |
294 | } |
Began life as a copy of #1005596
download show line numbers debug dex old transpilations
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1005603 |
Snippet name: | structure function (v10, counting tokens, new backrefs) |
Eternal ID of this version: | #1005603/1 |
Text MD5: | bf5be7c6fcb52853ec93a58f4caf7015 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2016-12-01 00:25:11 |
Source code size: | 8666 bytes / 294 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 460 / 452 |
Referenced in: | [show references] |