Mercurial > hg > truffle
comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java @ 7267:a4b84ba6dc2e
Introduction of the Truffle API for efficient implementation of dynamic languages on top of the Graal VM. New projects com.oracle.truffle.api for the API definition and com.oracle.truffle.api.test for API tests and documentation.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Tue, 18 Dec 2012 15:33:55 +0100 |
parents | |
children | 5e3d1a68664e |
comparison
equal
deleted
inserted
replaced
7259:494d99e07614 | 7267:a4b84ba6dc2e |
---|---|
1 /* | |
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. | |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
4 * | |
5 * This code is free software; you can redistribute it and/or modify it | |
6 * under the terms of the GNU General Public License version 2 only, as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
20 * or visit www.oracle.com if you need additional information or have any | |
21 * questions. | |
22 */ | |
23 package com.oracle.truffle.api.nodes; | |
24 | |
25 import java.io.*; | |
26 import java.lang.annotation.*; | |
27 import java.lang.reflect.*; | |
28 import java.util.*; | |
29 | |
30 import sun.misc.*; | |
31 | |
32 /** | |
33 * Utility class that manages the special access methods for node instances. | |
34 */ | |
35 public class NodeUtil { | |
36 | |
37 public static final class NodeClass { | |
38 | |
39 private final Class parentClass; | |
40 private final long[] nodeFieldOffsets; | |
41 private final Class[] nodeFieldClasses; | |
42 private final long[] nodeArrayFieldOffsets; | |
43 private final Class[] nodeArrayFieldClasses; | |
44 private final long parentOffset; | |
45 private final long[] nodeDataFieldOffsets; | |
46 private final Class< ? >[] nodeDataFieldClasses; | |
47 | |
48 private static final Map<Class< ? >, NodeClass> nodeClasses = new IdentityHashMap<>(); | |
49 | |
50 public static NodeClass get(Class< ? > clazz) { | |
51 NodeClass nodeClass = nodeClasses.get(clazz); | |
52 if (nodeClass == null) { | |
53 nodeClass = new NodeClass(clazz); | |
54 nodeClasses.put(clazz, nodeClass); | |
55 } | |
56 return nodeClass; | |
57 } | |
58 | |
59 private NodeClass(Class< ? > clazz) { | |
60 // scan object fields | |
61 Class< ? > parentClassTmp = null; | |
62 List<Long> nodeFieldOffsetsList = new ArrayList<>(); | |
63 List<Class< ? >> nodeFieldClassesList = new ArrayList<>(); | |
64 List<Long> nodeArrayFieldOffsetsList = new ArrayList<>(); | |
65 List<Class< ? >> nodeArrayFieldClassesList = new ArrayList<>(); | |
66 List<Long> nodeDataFieldOffsetList = new ArrayList<>(); | |
67 List<Class< ? >> nodeDataFieldClassList = new ArrayList<>(); | |
68 Field[] fields = getAllFields(clazz); | |
69 long parentOffsetTemp = -1; | |
70 for (Field field : fields) { | |
71 if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) { | |
72 continue; | |
73 } | |
74 | |
75 // Node fields | |
76 if (Node.class.isAssignableFrom(field.getType())) { | |
77 if (!field.getName().equals("parent")) { | |
78 nodeFieldOffsetsList.add(unsafe.objectFieldOffset(field)); | |
79 nodeFieldClassesList.add(field.getType()); | |
80 } else { | |
81 parentOffsetTemp = unsafe.objectFieldOffset(field); | |
82 parentClassTmp = field.getType(); | |
83 } | |
84 } else if (field.getType().getComponentType() != null && Node.class.isAssignableFrom(field.getType().getComponentType())) { | |
85 nodeArrayFieldOffsetsList.add(unsafe.objectFieldOffset(field)); | |
86 nodeArrayFieldClassesList.add(field.getType()); | |
87 } else { | |
88 nodeDataFieldOffsetList.add(unsafe.objectFieldOffset(field)); | |
89 nodeDataFieldClassList.add(field.getType()); | |
90 } | |
91 } | |
92 this.parentClass = parentClassTmp; | |
93 this.nodeFieldOffsets = toLongArray(nodeFieldOffsetsList); | |
94 this.nodeFieldClasses = nodeFieldClassesList.toArray(new Class[nodeFieldClassesList.size()]); | |
95 this.nodeArrayFieldOffsets = toLongArray(nodeArrayFieldOffsetsList); | |
96 this.nodeArrayFieldClasses = nodeArrayFieldClassesList.toArray(new Class[nodeArrayFieldClassesList.size()]); | |
97 this.nodeDataFieldOffsets = toLongArray(nodeDataFieldOffsetList); | |
98 this.nodeDataFieldClasses = nodeDataFieldClassList.toArray(new Class< ? >[nodeDataFieldClassList.size()]); | |
99 | |
100 this.parentOffset = parentOffsetTemp; | |
101 } | |
102 } | |
103 | |
104 public static class NodeIterator implements Iterator<Node> { | |
105 | |
106 private final Node node; | |
107 private final NodeClass nodeClass; | |
108 private final int childrenCount; | |
109 private int index; | |
110 | |
111 public NodeIterator(Node node) { | |
112 this(node, 0); | |
113 } | |
114 | |
115 public NodeIterator(Node node, int index) { | |
116 this.node = node; | |
117 this.index = index; | |
118 this.nodeClass = NodeClass.get(node.getClass()); | |
119 this.childrenCount = childrenCount(); | |
120 } | |
121 | |
122 private int childrenCount() { | |
123 int nodeCount = nodeClass.nodeFieldOffsets.length; | |
124 for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) { | |
125 Node[] children = ((Node[]) unsafe.getObject(node, fieldOffset)); | |
126 if (children != null) { | |
127 nodeCount += children.length; | |
128 } | |
129 } | |
130 return nodeCount; | |
131 } | |
132 | |
133 private Node nodeAt(int idx) { | |
134 int nodeCount = nodeClass.nodeFieldOffsets.length; | |
135 if (idx < nodeCount) { | |
136 return (Node) unsafe.getObject(node, nodeClass.nodeFieldOffsets[idx]); | |
137 } else { | |
138 for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) { | |
139 Node[] nodeArray = (Node[]) unsafe.getObject(node, fieldOffset); | |
140 if (idx < nodeCount + nodeArray.length) { | |
141 return nodeArray[idx - nodeCount]; | |
142 } | |
143 nodeCount += nodeArray.length; | |
144 } | |
145 } | |
146 return null; | |
147 } | |
148 | |
149 private void forward() { | |
150 if (index < childrenCount) { | |
151 index++; | |
152 } | |
153 } | |
154 | |
155 @Override | |
156 public boolean hasNext() { | |
157 return index < childrenCount; | |
158 } | |
159 | |
160 @Override | |
161 public Node next() { | |
162 try { | |
163 return nodeAt(index); | |
164 } finally { | |
165 forward(); | |
166 } | |
167 } | |
168 | |
169 @Override | |
170 public void remove() { | |
171 throw new UnsupportedOperationException(); | |
172 } | |
173 } | |
174 | |
175 private static long[] toLongArray(List<Long> list) { | |
176 long[] array = new long[list.size()]; | |
177 for (int i = 0; i < list.size(); i++) { | |
178 array[i] = list.get(i); | |
179 } | |
180 return array; | |
181 } | |
182 | |
183 private static final Unsafe unsafe = getUnsafe(); | |
184 | |
185 private static Unsafe getUnsafe() { | |
186 try { | |
187 return Unsafe.getUnsafe(); | |
188 } catch (SecurityException e) { | |
189 } | |
190 try { | |
191 Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); | |
192 theUnsafeInstance.setAccessible(true); | |
193 return (Unsafe) theUnsafeInstance.get(Unsafe.class); | |
194 } catch (Exception e) { | |
195 throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); | |
196 } | |
197 } | |
198 | |
199 @SuppressWarnings("unchecked") | |
200 public static <T extends Node> T cloneNode(T orig) { | |
201 Class< ? extends Node> clazz = orig.getClass(); | |
202 NodeClass nodeClass = NodeClass.get(clazz); | |
203 Node clone = orig.copy(); | |
204 if (clone == null) { | |
205 return null; | |
206 } | |
207 | |
208 unsafe.putObject(clone, nodeClass.parentOffset, null); | |
209 | |
210 for (long fieldOffset : nodeClass.nodeFieldOffsets) { | |
211 Node child = (Node) unsafe.getObject(orig, fieldOffset); | |
212 if (child != null) { | |
213 Node clonedChild = cloneNode(child); | |
214 if (clonedChild == null) { | |
215 return null; | |
216 } | |
217 | |
218 unsafe.putObject(clonedChild, nodeClass.parentOffset, clone); | |
219 unsafe.putObject(clone, fieldOffset, clonedChild); | |
220 } | |
221 } | |
222 for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) { | |
223 Node[] children = (Node[]) unsafe.getObject(orig, fieldOffset); | |
224 if (children != null) { | |
225 Node[] clonedChildren = new Node[children.length]; | |
226 for (int i = 0; i < children.length; i++) { | |
227 Node clonedChild = cloneNode(children[i]); | |
228 if (clonedChild == null) { | |
229 return null; | |
230 } | |
231 | |
232 clonedChildren[i] = clonedChild; | |
233 unsafe.putObject(clonedChild, nodeClass.parentOffset, clone); | |
234 } | |
235 unsafe.putObject(clone, fieldOffset, clonedChildren); | |
236 } | |
237 } | |
238 return (T) clone; | |
239 } | |
240 | |
241 public static List<Object> findNodeChildren(Object node) { | |
242 List<Object> nodes = new ArrayList<>(); | |
243 NodeClass nodeClass = NodeClass.get(node.getClass()); | |
244 | |
245 for (long fieldOffset : nodeClass.nodeFieldOffsets) { | |
246 Object child = unsafe.getObject(node, fieldOffset); | |
247 if (child != null) { | |
248 nodes.add(child); | |
249 } | |
250 } | |
251 for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) { | |
252 Object[] children = (Object[]) unsafe.getObject(node, fieldOffset); | |
253 if (children != null) { | |
254 nodes.addAll(Arrays.asList(children)); | |
255 } | |
256 } | |
257 | |
258 return nodes; | |
259 } | |
260 | |
261 public static void replaceChild(Node parent, Node oldChild, Node newChild) { | |
262 NodeClass nodeClass = NodeClass.get(parent.getClass()); | |
263 | |
264 for (long fieldOffset : nodeClass.nodeFieldOffsets) { | |
265 if (unsafe.getObject(parent, fieldOffset) == oldChild) { | |
266 unsafe.putObject(parent, fieldOffset, newChild); | |
267 } | |
268 } | |
269 for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) { | |
270 Node[] array = (Node[]) unsafe.getObject(parent, fieldOffset); | |
271 if (array != null) { | |
272 for (int i = 0; i < array.length; i++) { | |
273 if (array[i] == oldChild) { | |
274 array[i] = newChild; | |
275 return; | |
276 } | |
277 } | |
278 } | |
279 } | |
280 } | |
281 | |
282 public static long[] getNodeDataFieldOffsets(Class< ? > nodeClass) { | |
283 NodeClass clazz = NodeClass.get(nodeClass); | |
284 return Arrays.copyOf(clazz.nodeDataFieldOffsets, clazz.nodeDataFieldClasses.length); | |
285 } | |
286 | |
287 public static Class[] getNodeDataFieldClasses(Class< ? > nodeClass) { | |
288 NodeClass clazz = NodeClass.get(nodeClass); | |
289 return Arrays.copyOf(clazz.nodeDataFieldClasses, clazz.nodeDataFieldClasses.length); | |
290 } | |
291 | |
292 public static long getNodeParentOffset(Class< ? > nodeClass) { | |
293 NodeClass clazz = NodeClass.get(nodeClass); | |
294 return clazz.parentOffset; | |
295 } | |
296 | |
297 /** Returns the number of Node field declarations in the class hierarchy. */ | |
298 public static long[] getNodeFieldOffsets(Class< ? > nodeClass) { | |
299 NodeClass clazz = NodeClass.get(nodeClass); | |
300 return Arrays.copyOf(clazz.nodeFieldOffsets, clazz.nodeFieldOffsets.length); | |
301 } | |
302 | |
303 /** Returns the number of Node[] declaration in the class hierarchy. */ | |
304 public static long[] getNodeFieldArrayOffsets(Class< ? > nodeClass) { | |
305 NodeClass clazz = NodeClass.get(nodeClass); | |
306 return Arrays.copyOf(clazz.nodeArrayFieldOffsets, clazz.nodeArrayFieldOffsets.length); | |
307 } | |
308 | |
309 public static Class[] getNodeFieldArrayClasses(Class< ? > nodeClass) { | |
310 NodeClass clazz = NodeClass.get(nodeClass); | |
311 return Arrays.copyOf(clazz.nodeArrayFieldClasses, clazz.nodeArrayFieldClasses.length); | |
312 } | |
313 | |
314 public static Class getNodeParentClass(Class< ? > nodeClass) { | |
315 return NodeClass.get(nodeClass).parentClass; | |
316 } | |
317 | |
318 public static Class[] getNodeFieldClasses(Class< ? > nodeClass) { | |
319 NodeClass clazz = NodeClass.get(nodeClass); | |
320 return Arrays.copyOf(clazz.nodeFieldClasses, clazz.nodeFieldClasses.length); | |
321 } | |
322 | |
323 /** Returns all declared fields in the class hierarchy. */ | |
324 public static Field[] getAllFields(Class< ? extends Object> clazz) { | |
325 Field[] declaredFields = clazz.getDeclaredFields(); | |
326 if (clazz.getSuperclass() != null) { | |
327 return concat(getAllFields(clazz.getSuperclass()), declaredFields); | |
328 } | |
329 return declaredFields; | |
330 } | |
331 | |
332 public static <T> T[] concat(T[] first, T[] second) { | |
333 T[] result = Arrays.copyOf(first, first.length + second.length); | |
334 System.arraycopy(second, 0, result, first.length, second.length); | |
335 return result; | |
336 } | |
337 | |
338 /** find annotation in class/interface hierarchy. */ | |
339 public static <T extends Annotation> T findAnnotation(Class< ? > clazz, Class<T> annotationClass) { | |
340 if (clazz.getAnnotation(annotationClass) != null) { | |
341 return clazz.getAnnotation(annotationClass); | |
342 } else { | |
343 for (Class< ? > intf : clazz.getInterfaces()) { | |
344 if (intf.getAnnotation(annotationClass) != null) { | |
345 return intf.getAnnotation(annotationClass); | |
346 } | |
347 } | |
348 if (clazz.getSuperclass() != null) { | |
349 return findAnnotation(clazz.getSuperclass(), annotationClass); | |
350 } | |
351 } | |
352 return null; | |
353 } | |
354 | |
355 @SuppressWarnings("unchecked") | |
356 public static <T extends Node> T findParent(final Node start, final Class<T> clazz) { | |
357 assert start != null; | |
358 if (clazz.isInstance(start.getParent())) { | |
359 return (T) start.getParent(); | |
360 } else { | |
361 return start.getParent() != null ? findParent(start.getParent(), clazz) : null; | |
362 } | |
363 } | |
364 | |
365 @SuppressWarnings("unchecked") | |
366 public static <I> I findParentInterface(final Node start, final Class<I> clazz) { | |
367 assert start != null; | |
368 if (clazz.isInstance(start.getParent())) { | |
369 return (I) start.getParent(); | |
370 } else { | |
371 return (start.getParent() != null ? findParentInterface(start.getParent(), clazz) : null); | |
372 } | |
373 } | |
374 | |
375 @SuppressWarnings("unchecked") | |
376 public static <T> T findFirstNodeInstance(Object root, Class<T> clazz) { | |
377 List<Object> childNodes = findNodeChildren(root); | |
378 | |
379 for (Object childNode : childNodes) { | |
380 if (clazz.isInstance(childNode)) { | |
381 return (T) childNode; | |
382 } else { | |
383 return findFirstNodeInstance(childNode, clazz); | |
384 } | |
385 } | |
386 return null; | |
387 } | |
388 | |
389 public static <T extends Node> List<T> findAllNodeInstances(final Node root, final Class<T> clazz) { | |
390 final List<T> nodeList = new ArrayList<>(); | |
391 root.accept(new NodeVisitor() { | |
392 | |
393 @SuppressWarnings("unchecked") | |
394 @Override | |
395 public boolean visit(Node node) { | |
396 if (clazz.isInstance(node)) { | |
397 nodeList.add((T) node); | |
398 } | |
399 return true; | |
400 } | |
401 }); | |
402 return nodeList; | |
403 } | |
404 | |
405 // Don't visit found node instances. | |
406 public static <T extends Node> List<T> findNodeInstancesShallow(final Node root, final Class<T> clazz) { | |
407 final List<T> nodeList = new ArrayList<>(); | |
408 root.accept(new NodeVisitor() { | |
409 | |
410 @SuppressWarnings("unchecked") | |
411 @Override | |
412 public boolean visit(Node node) { | |
413 if (clazz.isInstance(node)) { | |
414 nodeList.add((T) node); | |
415 return false; | |
416 } | |
417 return true; | |
418 } | |
419 }); | |
420 return nodeList; | |
421 } | |
422 | |
423 /** Find node instances within current function only (not in nested functions). */ | |
424 public static <T extends Node> List<T> findNodeInstancesInFunction(final Node root, final Class<T> clazz) { | |
425 final List<T> nodeList = new ArrayList<>(); | |
426 root.accept(new NodeVisitor() { | |
427 | |
428 @SuppressWarnings("unchecked") | |
429 @Override | |
430 public boolean visit(Node node) { | |
431 if (clazz.isInstance(node)) { | |
432 nodeList.add((T) node); | |
433 } else if (node instanceof RootNode && node != root) { | |
434 return false; | |
435 } | |
436 return true; | |
437 } | |
438 }); | |
439 return nodeList; | |
440 } | |
441 | |
442 public static <I> List<I> findNodeInstancesInFunctionInterface(final Node root, final Class<I> clazz) { | |
443 final List<I> nodeList = new ArrayList<>(); | |
444 root.accept(new NodeVisitor() { | |
445 | |
446 @SuppressWarnings("unchecked") | |
447 @Override | |
448 public boolean visit(Node node) { | |
449 if (clazz.isInstance(node)) { | |
450 nodeList.add((I) node); | |
451 } else if (node instanceof RootNode && node != root) { | |
452 return false; | |
453 } | |
454 return true; | |
455 } | |
456 }); | |
457 return nodeList; | |
458 } | |
459 | |
460 public static String printTreeToString(Node node) { | |
461 ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); | |
462 printTree(new PrintStream(byteOut), node); | |
463 try { | |
464 byteOut.flush(); | |
465 } catch (IOException e) { | |
466 } | |
467 return new String(byteOut.toByteArray()); | |
468 } | |
469 | |
470 /** | |
471 * Prints a human readable form of a {@link Node} AST to the given {@link PrintStream}. This print method does not | |
472 * check for cycles in the node structure. | |
473 * | |
474 * @param p the {@link PrintStream} to print to. | |
475 * @param node the root node to write | |
476 */ | |
477 public static void printTree(PrintStream p, Node node) { | |
478 printTree(p, node, new NodeTreeResolver()); | |
479 } | |
480 | |
481 /** | |
482 * Prints a human readable form of a tree to the given {@link PrintStream}. The {@link TreeResolver} interface needs | |
483 * to be implemented to specify how the method can read the tree from plain a object. | |
484 * | |
485 * @param p the {@link PrintStream} to print to. | |
486 * @param o the root object to be printed. | |
487 * @param resolver an implementation of a tree resolver | |
488 */ | |
489 public static void printTree(PrintStream p, Object o, TreeResolver resolver) { | |
490 printTree(p, o, resolver, 1); | |
491 p.println(); | |
492 } | |
493 | |
494 private static void printTree(PrintStream p, Object node, TreeResolver resolver, int level) { | |
495 if (node == null) { | |
496 p.print("null"); | |
497 return; | |
498 } | |
499 | |
500 p.print(node.getClass().getSimpleName()); | |
501 | |
502 Field[] fields = NodeUtil.getAllFields(node.getClass()); | |
503 p.print("("); | |
504 | |
505 ArrayList<Field> childFields = new ArrayList<>(); | |
506 | |
507 boolean first = true; | |
508 for (int i = 0; i < fields.length; i++) { | |
509 Field field = fields[i]; | |
510 if (Modifier.isStatic(field.getModifiers()) || resolver.isFiltered(field)) { | |
511 continue; | |
512 } | |
513 if (resolver.isChildObject(field) || resolver.isChildArrayObject(field)) { | |
514 childFields.add(field); | |
515 } | |
516 if (!resolver.isDataField(field)) { | |
517 continue; | |
518 } | |
519 if (!first) { | |
520 p.print(", "); | |
521 } else { | |
522 first = false; | |
523 } | |
524 | |
525 Object value = getObjectValue(node, field.getType(), unsafe.objectFieldOffset(field)); | |
526 | |
527 p.print(field.getName()); | |
528 p.print(" = "); | |
529 p.print(resolver.toString(value)); | |
530 } | |
531 p.print(")"); | |
532 | |
533 if (childFields.size() != 0) { | |
534 p.print(" {"); | |
535 } | |
536 | |
537 // I refetch the fields to get declaration order. | |
538 for (int i = 0; i < childFields.size(); i++) { | |
539 Field field = childFields.get(i); | |
540 Class< ? > fieldClass = field.getType(); | |
541 String name = field.getName(); | |
542 | |
543 long offset = unsafe.objectFieldOffset(field); | |
544 | |
545 Object value = getObjectValue(node, fieldClass, offset); | |
546 | |
547 printNewLine(p, level); | |
548 p.print(name); | |
549 if (value == null) { | |
550 p.print(" = null "); | |
551 } else if (resolver.isChildObject(field)) { | |
552 p.print(" = "); | |
553 printTree(p, value, resolver, level + 1); | |
554 } else if (resolver.isChildArrayObject(field)) { | |
555 Object[] objectArray = resolver.convertToArray(field, value); | |
556 if (objectArray.length == 0) { | |
557 p.print(" = []"); | |
558 } else { | |
559 p.print(" = ["); | |
560 for (int j = 0; j < objectArray.length; j++) { | |
561 printTree(p, objectArray[j], resolver, level + 1); | |
562 if (j < objectArray.length - 1) { | |
563 p.print(", "); | |
564 } | |
565 } | |
566 p.print("]"); | |
567 } | |
568 } else { | |
569 assert false; | |
570 } | |
571 } | |
572 | |
573 if (childFields.size() != 0) { | |
574 printNewLine(p, level - 1); | |
575 p.print("}"); | |
576 } | |
577 } | |
578 | |
579 private static Object getObjectValue(Object base, Class< ? > fieldClass, long offset) { | |
580 if (fieldClass == boolean.class) { | |
581 return unsafe.getBoolean(base, offset); | |
582 } else if (fieldClass == byte.class) { | |
583 return unsafe.getByte(base, offset); | |
584 } else if (fieldClass == short.class) { | |
585 return unsafe.getShort(base, offset); | |
586 } else if (fieldClass == char.class) { | |
587 return unsafe.getChar(base, offset); | |
588 } else if (fieldClass == int.class) { | |
589 return unsafe.getInt(base, offset); | |
590 } else if (fieldClass == long.class) { | |
591 return unsafe.getLong(base, offset); | |
592 } else if (fieldClass == float.class) { | |
593 return unsafe.getFloat(base, offset); | |
594 } else if (fieldClass == double.class) { | |
595 return unsafe.getDouble(base, offset); | |
596 } else { | |
597 return unsafe.getObject(base, offset); | |
598 } | |
599 } | |
600 | |
601 private static void printNewLine(PrintStream p, int level) { | |
602 p.println(); | |
603 for (int i = 0; i < level; i++) { | |
604 p.print(" "); | |
605 } | |
606 } | |
607 | |
608 private static class NodeTreeResolver implements TreeResolver { | |
609 | |
610 @Override | |
611 public boolean isFiltered(Field f) { | |
612 if (f.getName().equals("parent")) { | |
613 return true; | |
614 } | |
615 return f.isSynthetic(); | |
616 } | |
617 | |
618 @Override | |
619 public boolean isDataField(Field f) { | |
620 return !isChildArrayObject(f) && !isChildObject(f); | |
621 } | |
622 | |
623 @Override | |
624 public boolean isChildObject(Field f) { | |
625 return Node.class.isAssignableFrom(f.getType()); | |
626 } | |
627 | |
628 @Override | |
629 public boolean isChildArrayObject(Field f) { | |
630 return f.getType().getComponentType() != null && Node.class.isAssignableFrom(f.getType().getComponentType()); | |
631 } | |
632 | |
633 @Override | |
634 public Object[] convertToArray(Field f, Object data) { | |
635 return (Object[]) data; | |
636 } | |
637 | |
638 @Override | |
639 public String toString(Object o) { | |
640 return o == null ? "null" : o.toString(); | |
641 } | |
642 } | |
643 | |
644 /** | |
645 * Specifies how a tree can be built from plain objects. | |
646 */ | |
647 public interface TreeResolver { | |
648 | |
649 /** | |
650 * Returns true if a {@link Field} is filtered from the tree. | |
651 * | |
652 * @param f the field to check | |
653 */ | |
654 boolean isFiltered(Field f); | |
655 | |
656 /** | |
657 * Returns true if a {@link Field} is a field that contains a data value which should not be traversed | |
658 * recursively. | |
659 * | |
660 * @param f the field to check | |
661 * @return true if a the given field is a data field else false. | |
662 */ | |
663 boolean isDataField(Field f); | |
664 | |
665 /** | |
666 * Returns true if a {@link Field} is a field that contains an {@link Object} which should be recursively | |
667 * visited. | |
668 * | |
669 * @param f the field to check | |
670 * @return true if a the given field is a child field else false. | |
671 */ | |
672 boolean isChildObject(Field f); | |
673 | |
674 /** | |
675 * Returns true if a {@link Field} is a field that contains any kind of list/array structure which itself holds | |
676 * values that should be recursively visited. | |
677 * | |
678 * @param f the field to check | |
679 * @return true if a the given field is a child array/list field else false. | |
680 */ | |
681 boolean isChildArrayObject(Field f); | |
682 | |
683 /** | |
684 * Converts an given child array object to array which can be traversed. This is especially useful to convert | |
685 * any kind of list structure to a traversable array. | |
686 * | |
687 * @param f the field for meta data needed to convert the data {@link Object}. | |
688 * @param value the actual value of the child array/list object. | |
689 * @return the converted {@link Object} array. | |
690 */ | |
691 Object[] convertToArray(Field f, Object value); | |
692 | |
693 /** | |
694 * Returns a human readable string for any data field object in the tree. | |
695 * | |
696 * @param o the object to convert to string. | |
697 * @return the converted string | |
698 */ | |
699 String toString(Object o); | |
700 } | |
701 | |
702 } |