Mercurial > hg > truffle
comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java @ 20129:5b7db8941fd7
Truffle: make NodeClass and NodeField a top-level class.
author | Christian Humer <christian.humer@gmail.com> |
---|---|
date | Thu, 02 Apr 2015 01:22:41 +0200 |
parents | a0971187a38a |
children | 8dc73c226c63 |
comparison
equal
deleted
inserted
replaced
20128:7ad60a16bbb0 | 20129:5b7db8941fd7 |
---|---|
25 package com.oracle.truffle.api.nodes; | 25 package com.oracle.truffle.api.nodes; |
26 | 26 |
27 import java.io.*; | 27 import java.io.*; |
28 import java.lang.annotation.*; | 28 import java.lang.annotation.*; |
29 import java.lang.reflect.*; | 29 import java.lang.reflect.*; |
30 import java.security.*; | |
31 import java.util.*; | 30 import java.util.*; |
32 | 31 |
33 import sun.misc.*; | 32 import sun.misc.*; |
34 | 33 |
35 import com.oracle.truffle.api.*; | 34 import com.oracle.truffle.api.*; |
36 import com.oracle.truffle.api.instrument.*; | 35 import com.oracle.truffle.api.instrument.*; |
37 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; | 36 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; |
38 import com.oracle.truffle.api.nodes.Node.Child; | 37 import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind; |
39 import com.oracle.truffle.api.nodes.Node.Children; | |
40 import com.oracle.truffle.api.source.*; | 38 import com.oracle.truffle.api.source.*; |
41 | 39 |
42 /** | 40 /** |
43 * Utility class that manages the special access methods for node instances. | 41 * Utility class that manages the special access methods for node instances. |
44 */ | 42 */ |
45 public final class NodeUtil { | 43 public final class NodeUtil { |
46 private static final boolean USE_UNSAFE = Boolean.getBoolean("truffle.unsafe"); | |
47 | 44 |
48 /** | 45 /** |
49 * Interface that allows the customization of field offsets used for {@link Unsafe} field | 46 * Interface that allows the customization of field offsets used for {@link Unsafe} field |
50 * accesses. | 47 * accesses. |
51 */ | 48 */ |
52 public interface FieldOffsetProvider { | 49 public interface FieldOffsetProvider { |
53 | 50 |
54 long objectFieldOffset(Field field); | 51 long objectFieldOffset(Field field); |
55 | 52 |
56 int getTypeSize(Class<?> clazz); | 53 int getTypeSize(Class<?> clazz); |
57 } | |
58 | |
59 private static final FieldOffsetProvider unsafeFieldOffsetProvider = new FieldOffsetProvider() { | |
60 | |
61 @Override | |
62 public long objectFieldOffset(Field field) { | |
63 return unsafe.objectFieldOffset(field); | |
64 } | |
65 | |
66 @Override | |
67 public int getTypeSize(Class<?> clazz) { | |
68 if (!clazz.isPrimitive()) { | |
69 return Unsafe.ARRAY_OBJECT_INDEX_SCALE; | |
70 } else if (clazz == int.class) { | |
71 return Unsafe.ARRAY_INT_INDEX_SCALE; | |
72 } else { | |
73 throw new UnsupportedOperationException("unsupported field type: " + clazz); | |
74 } | |
75 } | |
76 }; | |
77 | |
78 public static enum NodeFieldKind { | |
79 /** The single {@link Node#getParent() parent} field. */ | |
80 PARENT, | |
81 /** A field annotated with {@link Child}. */ | |
82 CHILD, | |
83 /** A field annotated with {@link Children}. */ | |
84 CHILDREN, | |
85 /** A normal non-child data field of the node. */ | |
86 DATA | |
87 } | |
88 | |
89 /** | |
90 * Information about a field in a {@link Node} class. | |
91 */ | |
92 public abstract static class NodeField { | |
93 | |
94 private final NodeFieldKind kind; | |
95 private final String name; | |
96 protected final Class<?> type; | |
97 protected final long offset; | |
98 | |
99 protected NodeField(NodeFieldKind kind, Field field) { | |
100 this.kind = kind; | |
101 this.type = field.getType(); | |
102 this.name = field.getName(); | |
103 this.offset = unsafeFieldOffsetProvider.objectFieldOffset(field); | |
104 } | |
105 | |
106 protected static NodeField create(NodeFieldKind kind, Field field) { | |
107 if (USE_UNSAFE) { | |
108 return new UnsafeNodeField(kind, field); | |
109 } else { | |
110 return new ReflectionNodeField(kind, field); | |
111 } | |
112 } | |
113 | |
114 public NodeFieldKind getKind() { | |
115 return kind; | |
116 } | |
117 | |
118 public Class<?> getType() { | |
119 return type; | |
120 } | |
121 | |
122 public String getName() { | |
123 return name; | |
124 } | |
125 | |
126 public long getOffset() { | |
127 return offset; | |
128 } | |
129 | |
130 public abstract void putObject(Node receiver, Object value); | |
131 | |
132 public abstract Object getObject(Node receiver); | |
133 | |
134 public abstract Object loadValue(Node node); | |
135 | |
136 @Override | |
137 public int hashCode() { | |
138 return kind.hashCode() | type.hashCode() | name.hashCode() | ((Long) offset).hashCode(); | |
139 } | |
140 | |
141 @Override | |
142 public boolean equals(Object obj) { | |
143 if (obj instanceof NodeField) { | |
144 NodeField other = (NodeField) obj; | |
145 return offset == other.offset && name.equals(other.name) && type.equals(other.type) && kind.equals(other.kind); | |
146 } | |
147 return false; | |
148 } | |
149 } | |
150 | |
151 private static final class UnsafeNodeField extends NodeField { | |
152 protected UnsafeNodeField(NodeFieldKind kind, Field field) { | |
153 super(kind, field); | |
154 } | |
155 | |
156 @Override | |
157 public void putObject(Node receiver, Object value) { | |
158 if (!type.isPrimitive() && value == null || type.isInstance(value)) { | |
159 unsafe.putObject(receiver, offset, value); | |
160 } else { | |
161 throw new IllegalArgumentException(); | |
162 } | |
163 } | |
164 | |
165 @Override | |
166 public Object getObject(Node receiver) { | |
167 if (!type.isPrimitive()) { | |
168 return unsafe.getObject(receiver, offset); | |
169 } else { | |
170 throw new IllegalArgumentException(); | |
171 } | |
172 } | |
173 | |
174 @Override | |
175 public Object loadValue(Node node) { | |
176 if (type == boolean.class) { | |
177 return unsafe.getBoolean(node, offset); | |
178 } else if (type == byte.class) { | |
179 return unsafe.getByte(node, offset); | |
180 } else if (type == short.class) { | |
181 return unsafe.getShort(node, offset); | |
182 } else if (type == char.class) { | |
183 return unsafe.getChar(node, offset); | |
184 } else if (type == int.class) { | |
185 return unsafe.getInt(node, offset); | |
186 } else if (type == long.class) { | |
187 return unsafe.getLong(node, offset); | |
188 } else if (type == float.class) { | |
189 return unsafe.getFloat(node, offset); | |
190 } else if (type == double.class) { | |
191 return unsafe.getDouble(node, offset); | |
192 } else { | |
193 return unsafe.getObject(node, offset); | |
194 } | |
195 } | |
196 } | |
197 | |
198 private static final class ReflectionNodeField extends NodeField { | |
199 private final Field field; | |
200 | |
201 protected ReflectionNodeField(NodeFieldKind kind, Field field) { | |
202 super(kind, field); | |
203 this.field = field; | |
204 field.setAccessible(true); | |
205 } | |
206 | |
207 @Override | |
208 public void putObject(Node receiver, Object value) { | |
209 assert !type.isPrimitive() && value == null || type.isInstance(value); | |
210 try { | |
211 field.set(receiver, value); | |
212 } catch (IllegalAccessException e) { | |
213 throw new AssertionError(e); | |
214 } | |
215 } | |
216 | |
217 @Override | |
218 public Object getObject(Node receiver) { | |
219 assert !type.isPrimitive(); | |
220 try { | |
221 return field.get(receiver); | |
222 } catch (IllegalAccessException e) { | |
223 throw new AssertionError(e); | |
224 } | |
225 } | |
226 | |
227 @Override | |
228 public Object loadValue(Node node) { | |
229 try { | |
230 if (type == boolean.class) { | |
231 return field.getBoolean(node); | |
232 } else if (type == byte.class) { | |
233 return field.getByte(node); | |
234 } else if (type == short.class) { | |
235 return field.getShort(node); | |
236 } else if (type == char.class) { | |
237 return field.getChar(node); | |
238 } else if (type == int.class) { | |
239 return field.getInt(node); | |
240 } else if (type == long.class) { | |
241 return field.getLong(node); | |
242 } else if (type == float.class) { | |
243 return field.getFloat(node); | |
244 } else if (type == double.class) { | |
245 return field.getDouble(node); | |
246 } else { | |
247 return field.get(node); | |
248 } | |
249 } catch (IllegalAccessException e) { | |
250 throw new AssertionError(e); | |
251 } | |
252 } | |
253 } | |
254 | |
255 /** | |
256 * Information about a {@link Node} class. A single instance of this class is allocated for | |
257 * every subclass of {@link Node} that is used. | |
258 */ | |
259 public static final class NodeClass { | |
260 private static final ClassValue<NodeClass> nodeClasses = new ClassValue<NodeClass>() { | |
261 @SuppressWarnings("unchecked") | |
262 @Override | |
263 protected NodeClass computeValue(final Class<?> clazz) { | |
264 assert Node.class.isAssignableFrom(clazz); | |
265 return AccessController.doPrivileged(new PrivilegedAction<NodeClass>() { | |
266 public NodeClass run() { | |
267 return new NodeClass((Class<? extends Node>) clazz); | |
268 } | |
269 }); | |
270 } | |
271 }; | |
272 | |
273 // The comprehensive list of all fields. | |
274 private final NodeField[] fields; | |
275 // Separate arrays for the frequently accessed fields. | |
276 private final NodeField parentField; | |
277 private final NodeField[] childFields; | |
278 private final NodeField[] childrenFields; | |
279 private final NodeField[] cloneableFields; | |
280 | |
281 private final Class<? extends Node> clazz; | |
282 | |
283 public static NodeClass get(Class<? extends Node> clazz) { | |
284 return nodeClasses.get(clazz); | |
285 } | |
286 | |
287 public NodeClass(Class<? extends Node> clazz) { | |
288 List<NodeField> fieldsList = new ArrayList<>(); | |
289 NodeField parentFieldTmp = null; | |
290 List<NodeField> childFieldList = new ArrayList<>(); | |
291 List<NodeField> childrenFieldList = new ArrayList<>(); | |
292 List<NodeField> cloneableFieldList = new ArrayList<>(); | |
293 | |
294 for (Field field : getAllFields(clazz)) { | |
295 if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) { | |
296 continue; | |
297 } | |
298 | |
299 NodeField nodeField; | |
300 if (field.getDeclaringClass() == Node.class && field.getName().equals("parent")) { | |
301 assert Node.class.isAssignableFrom(field.getType()); | |
302 nodeField = NodeField.create(NodeFieldKind.PARENT, field); | |
303 parentFieldTmp = nodeField; | |
304 } else if (field.getAnnotation(Child.class) != null) { | |
305 checkChildField(field); | |
306 nodeField = NodeField.create(NodeFieldKind.CHILD, field); | |
307 childFieldList.add(nodeField); | |
308 } else if (field.getAnnotation(Children.class) != null) { | |
309 checkChildrenField(field); | |
310 nodeField = NodeField.create(NodeFieldKind.CHILDREN, field); | |
311 childrenFieldList.add(nodeField); | |
312 } else { | |
313 nodeField = NodeField.create(NodeFieldKind.DATA, field); | |
314 if (NodeCloneable.class.isAssignableFrom(field.getType())) { | |
315 cloneableFieldList.add(nodeField); | |
316 } | |
317 } | |
318 fieldsList.add(nodeField); | |
319 } | |
320 | |
321 if (parentFieldTmp == null) { | |
322 throw new AssertionError("parent field not found"); | |
323 } | |
324 | |
325 this.fields = fieldsList.toArray(new NodeField[fieldsList.size()]); | |
326 this.parentField = parentFieldTmp; | |
327 this.childFields = childFieldList.toArray(new NodeField[childFieldList.size()]); | |
328 this.childrenFields = childrenFieldList.toArray(new NodeField[childrenFieldList.size()]); | |
329 this.cloneableFields = cloneableFieldList.toArray(new NodeField[cloneableFieldList.size()]); | |
330 this.clazz = clazz; | |
331 } | |
332 | |
333 private static boolean isNodeType(Class<?> clazz) { | |
334 return Node.class.isAssignableFrom(clazz) || (clazz.isInterface() && NodeInterface.class.isAssignableFrom(clazz)); | |
335 } | |
336 | |
337 private static void checkChildField(Field field) { | |
338 if (!isNodeType(field.getType())) { | |
339 throw new AssertionError("@Child field type must be a subclass of Node or an interface extending NodeInterface (" + field + ")"); | |
340 } | |
341 if (Modifier.isFinal(field.getModifiers())) { | |
342 throw new AssertionError("@Child field must not be final (" + field + ")"); | |
343 } | |
344 } | |
345 | |
346 private static void checkChildrenField(Field field) { | |
347 if (!(field.getType().isArray() && isNodeType(field.getType().getComponentType()))) { | |
348 throw new AssertionError("@Children field type must be an array of a subclass of Node or an interface extending NodeInterface (" + field + ")"); | |
349 } | |
350 if (!Modifier.isFinal(field.getModifiers())) { | |
351 throw new AssertionError("@Children field must be final (" + field + ")"); | |
352 } | |
353 } | |
354 | |
355 public NodeField[] getFields() { | |
356 return fields; | |
357 } | |
358 | |
359 public NodeField getParentField() { | |
360 return parentField; | |
361 } | |
362 | |
363 public NodeField[] getChildFields() { | |
364 return childFields; | |
365 } | |
366 | |
367 public NodeField[] getChildrenFields() { | |
368 return childrenFields; | |
369 } | |
370 | |
371 @Override | |
372 public int hashCode() { | |
373 return clazz.hashCode(); | |
374 } | |
375 | |
376 @Override | |
377 public boolean equals(Object obj) { | |
378 if (obj instanceof NodeClass) { | |
379 NodeClass other = (NodeClass) obj; | |
380 return clazz.equals(other.clazz); | |
381 } | |
382 return false; | |
383 } | |
384 | |
385 public Iterator<Node> makeIterator(Node node) { | |
386 assert clazz.isInstance(node); | |
387 return new NodeIterator(node); | |
388 } | |
389 | |
390 private final class NodeIterator implements Iterator<Node> { | |
391 private final Node node; | |
392 private final int childrenCount; | |
393 private int index; | |
394 | |
395 protected NodeIterator(Node node) { | |
396 this.node = node; | |
397 this.index = 0; | |
398 this.childrenCount = childrenCount(); | |
399 } | |
400 | |
401 private int childrenCount() { | |
402 int nodeCount = childFields.length; | |
403 for (NodeField childrenField : childrenFields) { | |
404 Object[] children = ((Object[]) childrenField.getObject(node)); | |
405 if (children != null) { | |
406 nodeCount += children.length; | |
407 } | |
408 } | |
409 return nodeCount; | |
410 } | |
411 | |
412 private Node nodeAt(int idx) { | |
413 int nodeCount = childFields.length; | |
414 if (idx < nodeCount) { | |
415 return (Node) childFields[idx].getObject(node); | |
416 } else { | |
417 for (NodeField childrenField : childrenFields) { | |
418 Object[] nodeArray = (Object[]) childrenField.getObject(node); | |
419 if (idx < nodeCount + nodeArray.length) { | |
420 return (Node) nodeArray[idx - nodeCount]; | |
421 } | |
422 nodeCount += nodeArray.length; | |
423 } | |
424 } | |
425 return null; | |
426 } | |
427 | |
428 private void forward() { | |
429 if (index < childrenCount) { | |
430 index++; | |
431 } | |
432 } | |
433 | |
434 public boolean hasNext() { | |
435 return index < childrenCount; | |
436 } | |
437 | |
438 public Node next() { | |
439 try { | |
440 return nodeAt(index); | |
441 } finally { | |
442 forward(); | |
443 } | |
444 } | |
445 | |
446 public void remove() { | |
447 throw new UnsupportedOperationException(); | |
448 } | |
449 } | |
450 } | 54 } |
451 | 55 |
452 static Iterator<Node> makeIterator(Node node) { | 56 static Iterator<Node> makeIterator(Node node) { |
453 return NodeClass.get(node.getClass()).makeIterator(node); | 57 return NodeClass.get(node.getClass()).makeIterator(node); |
454 } | 58 } |
519 public void remove() { | 123 public void remove() { |
520 throw new UnsupportedOperationException(); | 124 throw new UnsupportedOperationException(); |
521 } | 125 } |
522 } | 126 } |
523 | 127 |
524 private static final Unsafe unsafe = getUnsafe(); | |
525 | |
526 private static Unsafe getUnsafe() { | |
527 try { | |
528 return Unsafe.getUnsafe(); | |
529 } catch (SecurityException e) { | |
530 } | |
531 try { | |
532 Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); | |
533 theUnsafeInstance.setAccessible(true); | |
534 return (Unsafe) theUnsafeInstance.get(Unsafe.class); | |
535 } catch (Exception e) { | |
536 throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); | |
537 } | |
538 } | |
539 | |
540 @SuppressWarnings("unchecked") | 128 @SuppressWarnings("unchecked") |
541 public static <T extends Node> T cloneNode(T orig) { | 129 public static <T extends Node> T cloneNode(T orig) { |
542 return (T) orig.deepCopy(); | 130 return (T) orig.deepCopy(); |
543 } | 131 } |
544 | 132 |
545 static Node deepCopyImpl(Node orig) { | 133 static Node deepCopyImpl(Node orig) { |
546 final Node clone = orig.copy(); | 134 final Node clone = orig.copy(); |
547 NodeClass nodeClass = NodeClass.get(clone.getClass()); | 135 NodeClass nodeClass = NodeClass.get(clone.getClass()); |
548 | 136 |
549 nodeClass.parentField.putObject(clone, null); | 137 nodeClass.getParentField().putObject(clone, null); |
550 | 138 |
551 for (NodeField childField : nodeClass.childFields) { | 139 for (NodeFieldAccessor childField : nodeClass.getChildFields()) { |
552 Node child = (Node) childField.getObject(orig); | 140 Node child = (Node) childField.getObject(orig); |
553 if (child != null) { | 141 if (child != null) { |
554 Node clonedChild = child.deepCopy(); | 142 Node clonedChild = child.deepCopy(); |
555 nodeClass.parentField.putObject(clonedChild, clone); | 143 nodeClass.getParentField().putObject(clonedChild, clone); |
556 childField.putObject(clone, clonedChild); | 144 childField.putObject(clone, clonedChild); |
557 } | 145 } |
558 } | 146 } |
559 for (NodeField childrenField : nodeClass.childrenFields) { | 147 for (NodeFieldAccessor childrenField : nodeClass.getChildrenFields()) { |
560 Object[] children = (Object[]) childrenField.getObject(orig); | 148 Object[] children = (Object[]) childrenField.getObject(orig); |
561 if (children != null) { | 149 if (children != null) { |
562 Object[] clonedChildren = (Object[]) Array.newInstance(children.getClass().getComponentType(), children.length); | 150 Object[] clonedChildren = (Object[]) Array.newInstance(children.getClass().getComponentType(), children.length); |
563 for (int i = 0; i < children.length; i++) { | 151 for (int i = 0; i < children.length; i++) { |
564 if (children[i] != null) { | 152 if (children[i] != null) { |
565 Node clonedChild = ((Node) children[i]).deepCopy(); | 153 Node clonedChild = ((Node) children[i]).deepCopy(); |
566 clonedChildren[i] = clonedChild; | 154 clonedChildren[i] = clonedChild; |
567 nodeClass.parentField.putObject(clonedChild, clone); | 155 nodeClass.getParentField().putObject(clonedChild, clone); |
568 } | 156 } |
569 } | 157 } |
570 childrenField.putObject(clone, clonedChildren); | 158 childrenField.putObject(clone, clonedChildren); |
571 } | 159 } |
572 } | 160 } |
573 for (NodeField cloneableField : nodeClass.cloneableFields) { | 161 for (NodeFieldAccessor cloneableField : nodeClass.getCloneableFields()) { |
574 Object cloneable = cloneableField.getObject(clone); | 162 Object cloneable = cloneableField.getObject(clone); |
575 if (cloneable != null && cloneable == cloneableField.getObject(orig)) { | 163 if (cloneable != null && cloneable == cloneableField.getObject(orig)) { |
576 cloneableField.putObject(clone, ((NodeCloneable) cloneable).clone()); | 164 cloneableField.putObject(clone, ((NodeCloneable) cloneable).clone()); |
577 } | 165 } |
578 } | 166 } |
581 | 169 |
582 public static List<Node> findNodeChildren(Node node) { | 170 public static List<Node> findNodeChildren(Node node) { |
583 List<Node> nodes = new ArrayList<>(); | 171 List<Node> nodes = new ArrayList<>(); |
584 NodeClass nodeClass = NodeClass.get(node.getClass()); | 172 NodeClass nodeClass = NodeClass.get(node.getClass()); |
585 | 173 |
586 for (NodeField nodeField : nodeClass.childFields) { | 174 for (NodeFieldAccessor nodeField : nodeClass.getChildFields()) { |
587 Object child = nodeField.getObject(node); | 175 Object child = nodeField.getObject(node); |
588 if (child != null) { | 176 if (child != null) { |
589 nodes.add((Node) child); | 177 nodes.add((Node) child); |
590 } | 178 } |
591 } | 179 } |
592 for (NodeField nodeField : nodeClass.childrenFields) { | 180 for (NodeFieldAccessor nodeField : nodeClass.getChildrenFields()) { |
593 Object[] children = (Object[]) nodeField.getObject(node); | 181 Object[] children = (Object[]) nodeField.getObject(node); |
594 if (children != null) { | 182 if (children != null) { |
595 for (Object child : children) { | 183 for (Object child : children) { |
596 if (child != null) { | 184 if (child != null) { |
597 nodes.add((Node) child); | 185 nodes.add((Node) child); |
609 } | 197 } |
610 | 198 |
611 public static boolean replaceChild(Node parent, Node oldChild, Node newChild) { | 199 public static boolean replaceChild(Node parent, Node oldChild, Node newChild) { |
612 NodeClass nodeClass = NodeClass.get(parent.getClass()); | 200 NodeClass nodeClass = NodeClass.get(parent.getClass()); |
613 | 201 |
614 for (NodeField nodeField : nodeClass.getChildFields()) { | 202 for (NodeFieldAccessor nodeField : nodeClass.getChildFields()) { |
615 if (nodeField.getObject(parent) == oldChild) { | 203 if (nodeField.getObject(parent) == oldChild) { |
616 assert assertAssignable(nodeField, newChild); | 204 assert assertAssignable(nodeField, newChild); |
617 nodeField.putObject(parent, newChild); | 205 nodeField.putObject(parent, newChild); |
618 return true; | 206 return true; |
619 } | 207 } |
620 } | 208 } |
621 | 209 |
622 for (NodeField nodeField : nodeClass.getChildrenFields()) { | 210 for (NodeFieldAccessor nodeField : nodeClass.getChildrenFields()) { |
623 Object arrayObject = nodeField.getObject(parent); | 211 Object arrayObject = nodeField.getObject(parent); |
624 if (arrayObject != null) { | 212 if (arrayObject != null) { |
625 Object[] array = (Object[]) arrayObject; | 213 Object[] array = (Object[]) arrayObject; |
626 for (int i = 0; i < array.length; i++) { | 214 for (int i = 0; i < array.length; i++) { |
627 if (array[i] == oldChild) { | 215 if (array[i] == oldChild) { |
633 } | 221 } |
634 } | 222 } |
635 return false; | 223 return false; |
636 } | 224 } |
637 | 225 |
638 private static boolean assertAssignable(NodeField field, Object newValue) { | 226 private static boolean assertAssignable(NodeFieldAccessor field, Object newValue) { |
639 if (newValue == null) { | 227 if (newValue == null) { |
640 return true; | 228 return true; |
641 } | 229 } |
642 if (field.getKind() == NodeFieldKind.CHILD) { | 230 if (field.getKind() == NodeFieldKind.CHILD) { |
643 if (field.getType().isAssignableFrom(newValue.getClass())) { | 231 if (field.getType().isAssignableFrom(newValue.getClass())) { |
660 /** | 248 /** |
661 * Finds the field in a parent node, if any, that holds a specified child node. | 249 * Finds the field in a parent node, if any, that holds a specified child node. |
662 * | 250 * |
663 * @return the field (possibly an array) holding the child, {@code null} if not found. | 251 * @return the field (possibly an array) holding the child, {@code null} if not found. |
664 */ | 252 */ |
665 public static NodeField findChildField(Node parent, Node child) { | 253 public static NodeFieldAccessor findChildField(Node parent, Node child) { |
666 assert child != null; | 254 assert child != null; |
667 NodeClass parentNodeClass = NodeClass.get(parent.getClass()); | 255 NodeClass parentNodeClass = NodeClass.get(parent.getClass()); |
668 | 256 |
669 for (NodeField field : parentNodeClass.getChildFields()) { | 257 for (NodeFieldAccessor field : parentNodeClass.getChildFields()) { |
670 if (field.getObject(parent) == child) { | 258 if (field.getObject(parent) == child) { |
671 return field; | 259 return field; |
672 } | 260 } |
673 } | 261 } |
674 | 262 |
675 for (NodeField field : parentNodeClass.getChildrenFields()) { | 263 for (NodeFieldAccessor field : parentNodeClass.getChildrenFields()) { |
676 Object arrayObject = field.getObject(parent); | 264 Object arrayObject = field.getObject(parent); |
677 if (arrayObject != null) { | 265 if (arrayObject != null) { |
678 Object[] array = (Object[]) arrayObject; | 266 Object[] array = (Object[]) arrayObject; |
679 for (int i = 0; i < array.length; i++) { | 267 for (int i = 0; i < array.length; i++) { |
680 if (array[i] == child) { | 268 if (array[i] == child) { |
690 * Determines whether a proposed child replacement would be safe: structurally and type. | 278 * Determines whether a proposed child replacement would be safe: structurally and type. |
691 */ | 279 */ |
692 public static boolean isReplacementSafe(Node parent, Node oldChild, Node newChild) { | 280 public static boolean isReplacementSafe(Node parent, Node oldChild, Node newChild) { |
693 assert newChild != null; | 281 assert newChild != null; |
694 if (parent != null) { | 282 if (parent != null) { |
695 final NodeField field = findChildField(parent, oldChild); | 283 final NodeFieldAccessor field = findChildField(parent, oldChild); |
696 if (field != null) { | 284 if (field != null) { |
697 switch (field.getKind()) { | 285 switch (field.getKind()) { |
698 case CHILD: | 286 case CHILD: |
699 return field.getType().isAssignableFrom(newChild.getClass()); | 287 return field.getType().isAssignableFrom(newChild.getClass()); |
700 case CHILDREN: | 288 case CHILDREN: |
714 */ | 302 */ |
715 public static boolean forEachChild(Node parent, NodeVisitor visitor) { | 303 public static boolean forEachChild(Node parent, NodeVisitor visitor) { |
716 Objects.requireNonNull(visitor); | 304 Objects.requireNonNull(visitor); |
717 NodeClass parentNodeClass = NodeClass.get(parent.getClass()); | 305 NodeClass parentNodeClass = NodeClass.get(parent.getClass()); |
718 | 306 |
719 for (NodeField field : parentNodeClass.getChildFields()) { | 307 for (NodeFieldAccessor field : parentNodeClass.getChildFields()) { |
720 Object child = field.getObject(parent); | 308 Object child = field.getObject(parent); |
721 if (child != null) { | 309 if (child != null) { |
722 if (!visitor.visit((Node) child)) { | 310 if (!visitor.visit((Node) child)) { |
723 return false; | 311 return false; |
724 } | 312 } |
725 } | 313 } |
726 } | 314 } |
727 | 315 |
728 for (NodeField field : parentNodeClass.getChildrenFields()) { | 316 for (NodeFieldAccessor field : parentNodeClass.getChildrenFields()) { |
729 Object arrayObject = field.getObject(parent); | 317 Object arrayObject = field.getObject(parent); |
730 if (arrayObject != null) { | 318 if (arrayObject != null) { |
731 Object[] array = (Object[]) arrayObject; | 319 Object[] array = (Object[]) arrayObject; |
732 for (int i = 0; i < array.length; i++) { | 320 for (int i = 0; i < array.length; i++) { |
733 Object child = array[i]; | 321 Object child = array[i]; |
742 | 330 |
743 return true; | 331 return true; |
744 } | 332 } |
745 | 333 |
746 /** Returns all declared fields in the class hierarchy. */ | 334 /** Returns all declared fields in the class hierarchy. */ |
747 private static Field[] getAllFields(Class<? extends Object> clazz) { | 335 static Field[] getAllFields(Class<? extends Object> clazz) { |
748 Field[] declaredFields = clazz.getDeclaredFields(); | 336 Field[] declaredFields = clazz.getDeclaredFields(); |
749 if (clazz.getSuperclass() != null) { | 337 if (clazz.getSuperclass() != null) { |
750 return concat(getAllFields(clazz.getSuperclass()), declaredFields); | 338 return concat(getAllFields(clazz.getSuperclass()), declaredFields); |
751 } | 339 } |
752 return declaredFields; | 340 return declaredFields; |
974 } | 562 } |
975 p.flush(); | 563 p.flush(); |
976 } | 564 } |
977 | 565 |
978 private static String getNodeFieldName(Node parent, Node node, String defaultName) { | 566 private static String getNodeFieldName(Node parent, Node node, String defaultName) { |
979 NodeField[] fields = NodeClass.get(parent.getClass()).fields; | 567 NodeFieldAccessor[] fields = NodeClass.get(parent.getClass()).getFields(); |
980 for (NodeField field : fields) { | 568 for (NodeFieldAccessor field : fields) { |
981 Object value = field.loadValue(parent); | 569 Object value = field.loadValue(parent); |
982 if (field.getKind() == NodeFieldKind.CHILD && value == node) { | 570 if (field.getKind() == NodeFieldKind.CHILD && value == node) { |
983 return field.getName(); | 571 return field.getName(); |
984 } else if (field.getKind() == NodeFieldKind.CHILDREN) { | 572 } else if (field.getKind() == NodeFieldKind.CHILDREN) { |
985 int index = 0; | 573 int index = 0; |
1051 return; | 639 return; |
1052 } | 640 } |
1053 | 641 |
1054 p.print(nodeName(node)); | 642 p.print(nodeName(node)); |
1055 | 643 |
1056 ArrayList<NodeField> childFields = new ArrayList<>(); | 644 ArrayList<NodeFieldAccessor> childFields = new ArrayList<>(); |
1057 String sep = ""; | 645 String sep = ""; |
1058 p.print("("); | 646 p.print("("); |
1059 for (NodeField field : NodeClass.get(node.getClass()).fields) { | 647 for (NodeFieldAccessor field : NodeClass.get(node.getClass()).getFields()) { |
1060 if (field.getKind() == NodeFieldKind.CHILD || field.getKind() == NodeFieldKind.CHILDREN) { | 648 if (field.getKind() == NodeFieldKind.CHILD || field.getKind() == NodeFieldKind.CHILDREN) { |
1061 childFields.add(field); | 649 childFields.add(field); |
1062 } else if (field.getKind() == NodeFieldKind.DATA) { | 650 } else if (field.getKind() == NodeFieldKind.DATA) { |
1063 p.print(sep); | 651 p.print(sep); |
1064 sep = ", "; | 652 sep = ", "; |
1070 } | 658 } |
1071 p.print(")"); | 659 p.print(")"); |
1072 | 660 |
1073 if (childFields.size() != 0) { | 661 if (childFields.size() != 0) { |
1074 p.print(" {"); | 662 p.print(" {"); |
1075 for (NodeField field : childFields) { | 663 for (NodeFieldAccessor field : childFields) { |
1076 printNewLine(p, level); | 664 printNewLine(p, level); |
1077 p.print(field.getName()); | 665 p.print(field.getName()); |
1078 | 666 |
1079 Object value = field.loadValue(node); | 667 Object value = field.loadValue(node); |
1080 if (value == null) { | 668 if (value == null) { |