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