TreeChangeProcessor { public static JsSet<StateNode> processChanges(StateTree tree, JsonArray changes) { assert !tree .isUpdateInProgress() : "Previous tree change processing has not completed"; try { tree.setUpdateInProgress(true); JsSet<StateNode> nodes = processAttachChanges(tree, changes); int length = changes.length(); for (int i = 0; i < length; i++) { JsonObject change = changes.getObject(i); if (!isAttach(change)) { nodes.add(processChange(tree, change)); } } return nodes; } finally { tree.setUpdateInProgress(false); } } private  TreeChangeProcessor(); static JsSet<StateNode> processChanges(StateTree tree,
            JsonArray changes); static StateNode processChange(StateTree tree, JsonObject change);  }
@Test public void testAttachNodeBeforePut() { int nodeId = 2; JsonArray changes = toArray( putChange(nodeId, ns, myKey, Json.create(myValue)), attachChange(nodeId)); JsSet<StateNode> updatedNodes = TreeChangeProcessor.processChanges(tree, changes); Object value = tree.getNode(nodeId).getMap(ns).getProperty(myKey) .getValue(); Assert.assertEquals(myValue, value); Assert.assertEquals(1, updatedNodes.size()); Assert.assertTrue(updatedNodes.has(tree.getNode(nodeId))); }
@Test public void testDetachRemovesNode() { AtomicInteger unregisterCount = new AtomicInteger(0); StateNode childNode = new StateNode(2, tree); childNode.addUnregisterListener(e -> unregisterCount.incrementAndGet()); tree.registerNode(childNode); Assert.assertSame(childNode, tree.getNode(childNode.getId())); Assert.assertEquals(0, unregisterCount.get()); JsonArray changes = toArray(detachChange(childNode.getId())); JsSet<StateNode> updatedNodes = TreeChangeProcessor.processChanges(tree, changes); Assert.assertNull(tree.getNode(childNode.getId())); Assert.assertEquals(1, unregisterCount.get()); Assert.assertEquals(1, updatedNodes.size()); Assert.assertTrue(updatedNodes.has(childNode)); }