InMemoryNodeModel.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.configuration2.tree;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.Iterator;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.concurrent.atomic.AtomicReference;

  27. import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
  28. import org.apache.commons.lang3.mutable.Mutable;
  29. import org.apache.commons.lang3.mutable.MutableObject;

  30. /**
  31.  * <p>
  32.  * A specialized node model implementation which operates on {@link ImmutableNode} structures.
  33.  * </p>
  34.  * <p>
  35.  * This {@code NodeModel} implementation keeps all its data as a tree of {@link ImmutableNode} objects in memory. The
  36.  * managed structure can be manipulated in a thread-safe, non-blocking way. This is achieved by using atomic variables:
  37.  * The root of the tree is stored in an atomic reference variable. Each update operation causes a new structure to be
  38.  * constructed (which reuses as much from the original structure as possible). The old root node is then replaced by the
  39.  * new one using an atomic compare-and-set operation. If this fails, the manipulation has to be done anew on the updated
  40.  * structure.
  41.  * </p>
  42.  *
  43.  * @since 2.0
  44.  */
  45. public class InMemoryNodeModel implements NodeModel<ImmutableNode> {
  46.     /**
  47.      * An interface used internally for handling concurrent updates. An implementation has to populate the passed in
  48.      * {@code ModelTransaction}. The transaction is then executed, and an atomic update of the model's {@code TreeData} is
  49.      * attempted. If this fails - because another update came across -, the whole operation has to be tried anew.
  50.      */
  51.     private interface TransactionInitializer {
  52.         /**
  53.          * Initializes the specified transaction for an update operation. The return value indicates whether the transaction
  54.          * should be executed. A result of <strong>false</strong> means that the update is to be aborted (maybe another update method was
  55.          * called).
  56.          *
  57.          * @param tx the transaction to be initialized
  58.          * @return a flag whether the update should continue
  59.          */
  60.         boolean initTransaction(ModelTransaction tx);
  61.     }

  62.     /**
  63.      * A dummy node handler instance used in operations which require only a limited functionality.
  64.      */
  65.     private static final NodeHandler<ImmutableNode> DUMMY_HANDLER = new TreeData(null, Collections.<ImmutableNode, ImmutableNode>emptyMap(),
  66.         Collections.<ImmutableNode, ImmutableNode>emptyMap(), null, new ReferenceTracker());

  67.     /**
  68.      * Handles an add property operation if the property to be added is an attribute.
  69.      *
  70.      * @param tx the transaction
  71.      * @param addData the {@code NodeAddData}
  72.      * @param values the collection with node values
  73.      */
  74.     private static void addAttributeProperty(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Iterable<?> values) {
  75.         if (addData.getPathNodes().isEmpty()) {
  76.             tx.addAttributeOperation(addData.getParent(), addData.getNewNodeName(), values.iterator().next());
  77.         } else {
  78.             final int pathNodeCount = addData.getPathNodes().size();
  79.             final ImmutableNode childWithAttribute = new ImmutableNode.Builder().name(addData.getPathNodes().get(pathNodeCount - 1))
  80.                 .addAttribute(addData.getNewNodeName(), values.iterator().next()).create();
  81.             final ImmutableNode newChild = pathNodeCount > 1
  82.                 ? createNodeOnPath(addData.getPathNodes().subList(0, pathNodeCount - 1).iterator(), Collections.singleton(childWithAttribute))
  83.                 : childWithAttribute;
  84.             tx.addAddNodeOperation(addData.getParent(), newChild);
  85.         }
  86.     }

  87.     /**
  88.      * Handles an add property operation if the property to be added is a node.
  89.      *
  90.      * @param tx the transaction
  91.      * @param addData the {@code NodeAddData}
  92.      * @param values the collection with node values
  93.      */
  94.     private static void addNodeProperty(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Iterable<?> values) {
  95.         final Collection<ImmutableNode> newNodes = createNodesToAdd(addData.getNewNodeName(), values);
  96.         addNodesByAddData(tx, addData, newNodes);
  97.     }

  98.     /**
  99.      * Initializes a transaction to add a collection of nodes as described by a {@code NodeAddData} object. If necessary,
  100.      * new path nodes are created. Eventually, the new nodes are added as children to the specified target node.
  101.      *
  102.      * @param tx the transaction
  103.      * @param addData the {@code NodeAddData}
  104.      * @param newNodes the collection of new child nodes
  105.      */
  106.     private static void addNodesByAddData(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Collection<ImmutableNode> newNodes) {
  107.         if (addData.getPathNodes().isEmpty()) {
  108.             tx.addAddNodesOperation(addData.getParent(), newNodes);
  109.         } else {
  110.             final ImmutableNode newChild = createNodeToAddWithPath(addData, newNodes);
  111.             tx.addAddNodeOperation(addData.getParent(), newChild);
  112.         }
  113.     }

  114.     /**
  115.      * Creates an exception referring to an invalid key for adding properties. Such an exception is thrown when an operation
  116.      * tries to add something to an attribute.
  117.      *
  118.      * @param key the invalid key causing this exception
  119.      * @return the exception
  120.      */
  121.     private static IllegalArgumentException attributeKeyException(final String key) {
  122.         return new IllegalArgumentException("New nodes cannot be added to an attribute key: " + key);
  123.     }

  124.     /**
  125.      * Checks if the passed in node is defined. Result is <strong>true</strong> if the node contains any data.
  126.      *
  127.      * @param node the node in question
  128.      * @return <strong>true</strong> if the node is defined, <strong>false</strong> otherwise
  129.      */
  130.     static boolean checkIfNodeDefined(final ImmutableNode node) {
  131.         return node.getValue() != null || !node.getChildren().isEmpty() || !node.getAttributes().isEmpty();
  132.     }

  133.     /**
  134.      * Creates a new data object with a tracked child node of the given parent node. If such a child node already exists, it
  135.      * is used. Otherwise, a new one is created.
  136.      *
  137.      * @param current the current {@code TreeData} object
  138.      * @param parent the parent node
  139.      * @param childName the name of the child node
  140.      * @param resolver the {@code NodeKeyResolver}
  141.      * @param refSelector here the newly created {@code NodeSelector} is returned
  142.      * @return the new {@code TreeData} instance
  143.      */
  144.     private static TreeData createDataWithTrackedChildNode(final TreeData current, final ImmutableNode parent, final String childName,
  145.         final NodeKeyResolver<ImmutableNode> resolver, final MutableObject<NodeSelector> refSelector) {
  146.         final TreeData newData;
  147.         final List<ImmutableNode> namedChildren = current.getChildren(parent, childName);
  148.         if (!namedChildren.isEmpty()) {
  149.             newData = updateDataWithNewTrackedNode(current, namedChildren.get(0), resolver, refSelector);
  150.         } else {
  151.             final ImmutableNode child = new ImmutableNode.Builder().name(childName).create();
  152.             final ModelTransaction tx = new ModelTransaction(current, null, resolver);
  153.             tx.addAddNodeOperation(parent, child);
  154.             newData = updateDataWithNewTrackedNode(tx.execute(), child, resolver, refSelector);
  155.         }
  156.         return newData;
  157.     }

  158.     /**
  159.      * Recursive helper method for creating a path node for an add operation. All path nodes except for the last have a
  160.      * single child. The last path node has the new nodes as children.
  161.      *
  162.      * @param it the iterator over the names of the path nodes
  163.      * @param newNodes the collection of new child nodes
  164.      * @return the newly created path node
  165.      */
  166.     private static ImmutableNode createNodeOnPath(final Iterator<String> it, final Collection<ImmutableNode> newNodes) {
  167.         final String nodeName = it.next();
  168.         final ImmutableNode.Builder builder;
  169.         if (it.hasNext()) {
  170.             builder = new ImmutableNode.Builder(1);
  171.             builder.addChild(createNodeOnPath(it, newNodes));
  172.         } else {
  173.             builder = new ImmutableNode.Builder(newNodes.size());
  174.             builder.addChildren(newNodes);
  175.         }
  176.         return builder.name(nodeName).create();
  177.     }

  178.     /**
  179.      * Creates a collection with new nodes with a given name and a value from a given collection.
  180.      *
  181.      * @param newNodeName the name of the new nodes
  182.      * @param values the collection with node values
  183.      * @return the newly created collection
  184.      */
  185.     private static Collection<ImmutableNode> createNodesToAdd(final String newNodeName, final Iterable<?> values) {
  186.         final Collection<ImmutableNode> nodes = new LinkedList<>();
  187.         values.forEach(value -> nodes.add(new ImmutableNode.Builder().name(newNodeName).value(value).create()));
  188.         return nodes;
  189.     }

  190.     /**
  191.      * Creates a node structure consisting of the path nodes defined by the passed in {@code NodeAddData} instance and all
  192.      * new child nodes.
  193.      *
  194.      * @param addData the {@code NodeAddData}
  195.      * @param newNodes the collection of new child nodes
  196.      * @return the parent node of the newly created hierarchy
  197.      */
  198.     private static ImmutableNode createNodeToAddWithPath(final NodeAddData<ImmutableNode> addData, final Collection<ImmutableNode> newNodes) {
  199.         return createNodeOnPath(addData.getPathNodes().iterator(), newNodes);
  200.     }

  201.     /**
  202.      * Creates tracked node entries for the specified nodes and creates the corresponding selectors.
  203.      *
  204.      * @param refSelectors the reference where to store the selectors
  205.      * @param nodes the nodes to be tracked
  206.      * @param current the current {@code TreeData} object
  207.      * @param resolver the {@code NodeKeyResolver}
  208.      * @return the updated {@code TreeData} object
  209.      */
  210.     private static TreeData createSelectorsForTrackedNodes(final Mutable<Collection<NodeSelector>> refSelectors, final List<ImmutableNode> nodes,
  211.             final TreeData current, final NodeKeyResolver<ImmutableNode> resolver) {
  212.         final List<NodeSelector> selectors = new ArrayList<>(nodes.size());
  213.         final Map<ImmutableNode, String> cache = new HashMap<>();
  214.         nodes.forEach(node -> selectors.add(new NodeSelector(resolver.nodeKey(node, cache, current))));
  215.         refSelectors.setValue(selectors);
  216.         final NodeTracker newTracker = current.getNodeTracker().trackNodes(selectors, nodes);
  217.         return current.updateNodeTracker(newTracker);
  218.     }

  219.     /**
  220.      * Determines the name of the root node for a merge operation. If a root name is provided, it is used. Otherwise, if the
  221.      * current root node has no name, the name of the node to be merged is used. A result of <strong>null</strong> means that no node
  222.      * name has to be set.
  223.      *
  224.      * @param rootNode the current root node
  225.      * @param node the node to be merged with the root node
  226.      * @param rootName the name of the resulting node
  227.      * @return the new name of the root node
  228.      */
  229.     private static String determineRootName(final ImmutableNode rootNode, final ImmutableNode node, final String rootName) {
  230.         if (rootName != null) {
  231.             return rootName;
  232.         }
  233.         if (rootNode.getNodeName() == null) {
  234.             return node.getNodeName();
  235.         }
  236.         return null;
  237.     }

  238.     /**
  239.      * Initializes a transaction to clear the values of a property based on the passed in collection of affected results.
  240.      *
  241.      * @param tx the transaction to be initialized
  242.      * @param results a collection with results pointing to the nodes to be cleared
  243.      * @return a flag whether there are elements to be cleared
  244.      */
  245.     private static boolean initializeClearTransaction(final ModelTransaction tx, final Collection<QueryResult<ImmutableNode>> results) {
  246.         results.forEach(result -> {
  247.             if (result.isAttributeResult()) {
  248.                 tx.addRemoveAttributeOperation(result.getNode(), result.getAttributeName());
  249.             } else {
  250.                 tx.addClearNodeValueOperation(result.getNode());
  251.             }
  252.         });

  253.         return !results.isEmpty();
  254.     }

  255.     /**
  256.      * Initializes a transaction to change the values of some query results based on the passed in map.
  257.      *
  258.      * @param tx the transaction to be initialized
  259.      * @param changedValues the map defining the elements to be changed
  260.      * @return a flag whether there are elements to be updated
  261.      */
  262.     private static boolean initializeUpdateTransaction(final ModelTransaction tx, final Map<QueryResult<ImmutableNode>, Object> changedValues) {
  263.         changedValues.forEach((k, v) -> {
  264.             final ImmutableNode node = k.getNode();
  265.             if (k.isAttributeResult()) {
  266.                 tx.addAttributeOperation(node, k.getAttributeName(), v);
  267.             } else {
  268.                 tx.addChangeNodeValueOperation(node, v);
  269.             }
  270.         });

  271.         return !changedValues.isEmpty();
  272.     }

  273.     /**
  274.      * Determines the initial root node of this model. If a root node has been provided, it is used. Otherwise, an empty
  275.      * dummy root node is created.
  276.      *
  277.      * @param providedRoot the passed in root node
  278.      * @return the root node to be used
  279.      */
  280.     private static ImmutableNode initialRootNode(final ImmutableNode providedRoot) {
  281.         return providedRoot != null ? providedRoot : new ImmutableNode.Builder().create();
  282.     }

  283.     /**
  284.      * Adds a tracked node that has already been resolved to the specified data object.
  285.      *
  286.      * @param current the current {@code TreeData} object
  287.      * @param node the node in question
  288.      * @param resolver the {@code NodeKeyResolver}
  289.      * @param refSelector here the newly created {@code NodeSelector} is returned
  290.      * @return the new {@code TreeData} instance
  291.      */
  292.     private static TreeData updateDataWithNewTrackedNode(final TreeData current, final ImmutableNode node, final NodeKeyResolver<ImmutableNode> resolver,
  293.         final MutableObject<NodeSelector> refSelector) {
  294.         final NodeSelector selector = new NodeSelector(resolver.nodeKey(node, new HashMap<>(), current));
  295.         refSelector.setValue(selector);
  296.         final NodeTracker newTracker = current.getNodeTracker().trackNodes(Collections.singleton(selector), Collections.singleton(node));
  297.         return current.updateNodeTracker(newTracker);
  298.     }

  299.     /**
  300.      * Updates the mapping from nodes to their parents for the passed in hierarchy of nodes. This method traverses all
  301.      * children and grand-children of the passed in root node. For each node in the subtree the parent relation is added to
  302.      * the map.
  303.      *
  304.      * @param parents the map with parent nodes
  305.      * @param root the root node of the current tree
  306.      */
  307.     static void updateParentMapping(final Map<ImmutableNode, ImmutableNode> parents, final ImmutableNode root) {
  308.         NodeTreeWalker.INSTANCE.walkBFS(root, new ConfigurationNodeVisitorAdapter<ImmutableNode>() {
  309.             @Override
  310.             public void visitBeforeChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
  311.                 node.forEach(c -> parents.put(c, node));
  312.             }
  313.         }, DUMMY_HANDLER);
  314.     }

  315.     /**
  316.      * Checks whether the specified collection with values is not empty.
  317.      *
  318.      * @param values the collection with node values
  319.      * @return <strong>true</strong> if values are provided, <strong>false</strong> otherwise
  320.      */
  321.     private static boolean valuesNotEmpty(final Iterable<?> values) {
  322.         return values.iterator().hasNext();
  323.     }

  324.     /** Stores information about the current nodes structure. */
  325.     private final AtomicReference<TreeData> structure;

  326.     /**
  327.      * Creates a new instance of {@code InMemoryNodeModel} which is initialized with an empty root node.
  328.      */
  329.     public InMemoryNodeModel() {
  330.         this(null);
  331.     }

  332.     /**
  333.      * Creates a new instance of {@code InMemoryNodeModel} and initializes it from the given root node. If the passed in
  334.      * node is <strong>null</strong>, a new, empty root node is created.
  335.      *
  336.      * @param root the new root node for this model
  337.      */
  338.     public InMemoryNodeModel(final ImmutableNode root) {
  339.         structure = new AtomicReference<>(createTreeData(initialRootNode(root), null));
  340.     }

  341.     @Override
  342.     public void addNodes(final String key, final Collection<? extends ImmutableNode> nodes, final NodeKeyResolver<ImmutableNode> resolver) {
  343.         addNodes(key, null, nodes, resolver);
  344.     }

  345.     /**
  346.      * Adds new nodes using a tracked node as root node. This method works like the normal {@code addNodes()} method, but
  347.      * the origin of the operation (also for the interpretation of the passed in key) is a tracked node identified by the
  348.      * passed in {@code NodeSelector}. The selector can be <strong>null</strong>, then the root node is assumed.
  349.      *
  350.      * @param key the key
  351.      * @param selector the {@code NodeSelector} defining the root node (or <strong>null</strong>)
  352.      * @param nodes the collection of new nodes to be added
  353.      * @param resolver the {@code NodeKeyResolver}
  354.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  355.      */
  356.     public void addNodes(final String key, final NodeSelector selector, final Collection<? extends ImmutableNode> nodes,
  357.         final NodeKeyResolver<ImmutableNode> resolver) {
  358.         if (nodes != null && !nodes.isEmpty()) {
  359.             updateModel(tx -> {
  360.                 final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, tx.getCurrentData());
  361.                 if (results.size() == 1) {
  362.                     if (results.get(0).isAttributeResult()) {
  363.                         throw attributeKeyException(key);
  364.                     }
  365.                     tx.addAddNodesOperation(results.get(0).getNode(), nodes);
  366.                 } else {
  367.                     final NodeAddData<ImmutableNode> addData = resolver.resolveAddKey(tx.getQueryRoot(), key, tx.getCurrentData());
  368.                     if (addData.isAttribute()) {
  369.                         throw attributeKeyException(key);
  370.                     }
  371.                     final ImmutableNode newNode = new ImmutableNode.Builder(nodes.size()).name(addData.getNewNodeName()).addChildren(nodes).create();
  372.                     addNodesByAddData(tx, addData, Collections.singleton(newNode));
  373.                 }
  374.                 return true;
  375.             }, selector, resolver);
  376.         }
  377.     }

  378.     @Override
  379.     public void addProperty(final String key, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
  380.         addProperty(key, null, values, resolver);
  381.     }

  382.     /**
  383.      * Adds new property values using a tracked node as root node. This method works like the normal {@code addProperty()}
  384.      * method, but the origin of the operation (also for the interpretation of the passed in key) is a tracked node
  385.      * identified by the passed in {@code NodeSelector}. The selector can be <strong>null</strong>, then the root node is assumed.
  386.      *
  387.      * @param key the key
  388.      * @param selector the {@code NodeSelector} defining the root node (or <strong>null</strong>)
  389.      * @param values the values to be added
  390.      * @param resolver the {@code NodeKeyResolver}
  391.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  392.      */
  393.     public void addProperty(final String key, final NodeSelector selector, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
  394.         if (valuesNotEmpty(values)) {
  395.             updateModel(tx -> {
  396.                 initializeAddTransaction(tx, key, values, resolver);
  397.                 return true;
  398.             }, selector, resolver);
  399.         }
  400.     }

  401.     /**
  402.      * {@inheritDoc} A new empty root node is created with the same name as the current root node. Implementation note:
  403.      * Because this is a hard reset the usual dance for dealing with concurrent updates is not required here.
  404.      *
  405.      * @param resolver the {@code NodeKeyResolver}
  406.      */
  407.     @Override
  408.     public void clear(final NodeKeyResolver<ImmutableNode> resolver) {
  409.         final ImmutableNode newRoot = new ImmutableNode.Builder().name(getRootNode().getNodeName()).create();
  410.         setRootNode(newRoot);
  411.     }

  412.     /**
  413.      * {@inheritDoc} If this operation leaves an affected node in an undefined state, it is removed from the model.
  414.      */
  415.     @Override
  416.     public void clearProperty(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
  417.         clearProperty(key, null, resolver);
  418.     }

  419.     /**
  420.      * Clears a property using a tracked node as root node. This method works like the normal {@code clearProperty()}
  421.      * method, but the origin of the operation (also for the interpretation of the passed in key) is a tracked node
  422.      * identified by the passed in {@code NodeSelector}. The selector can be <strong>null</strong>, then the root node is assumed.
  423.      *
  424.      * @param key the key
  425.      * @param selector the {@code NodeSelector} defining the root node (or <strong>null</strong>)
  426.      * @param resolver the {@code NodeKeyResolver}
  427.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  428.      */
  429.     public void clearProperty(final String key, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
  430.         updateModel(tx -> {
  431.             final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, tx.getCurrentData());
  432.             return initializeClearTransaction(tx, results);
  433.         }, selector, resolver);
  434.     }

  435.     /**
  436.      * {@inheritDoc} This implementation checks whether nodes become undefined after subtrees have been removed. If this is
  437.      * the case, such nodes are removed, too. Return value is a collection with {@code QueryResult} objects for the elements
  438.      * to be removed from the model.
  439.      */
  440.     @Override
  441.     public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
  442.         return clearTree(key, null, resolver);
  443.     }

  444.     /**
  445.      * Clears a whole sub tree using a tracked node as root node. This method works like the normal {@code clearTree()}
  446.      * method, but the origin of the operation (also for the interpretation of the passed in key) is a tracked node
  447.      * identified by the passed in {@code NodeSelector}. The selector can be <strong>null</strong>, then the root node is assumed.
  448.      *
  449.      * @param key the key
  450.      * @param selector the {@code NodeSelector} defining the root node (or <strong>null</strong>)
  451.      * @param resolver the {@code NodeKeyResolver}
  452.      * @return a list with the results to be removed
  453.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  454.      */
  455.     public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
  456.         final List<QueryResult<ImmutableNode>> removedElements = new LinkedList<>();
  457.         updateModel(tx -> {
  458.             boolean changes = false;
  459.             final TreeData currentStructure = tx.getCurrentData();
  460.             final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, currentStructure);
  461.             removedElements.clear();
  462.             removedElements.addAll(results);
  463.             for (final QueryResult<ImmutableNode> result : results) {
  464.                 if (result.isAttributeResult()) {
  465.                     tx.addRemoveAttributeOperation(result.getNode(), result.getAttributeName());
  466.                 } else {
  467.                     if (result.getNode() == currentStructure.getRootNode()) {
  468.                         // the whole model is to be cleared
  469.                         clear(resolver);
  470.                         return false;
  471.                     }
  472.                     tx.addRemoveNodeOperation(currentStructure.getParent(result.getNode()), result.getNode());
  473.                 }
  474.                 changes = true;
  475.             }
  476.             return changes;
  477.         }, selector, resolver);

  478.         return removedElements;
  479.     }

  480.     /**
  481.      * Creates the mapping to parent nodes for the nodes structured represented by the passed in root node. Each node is
  482.      * assigned its parent node. Here an iterative algorithm is used rather than a recursive one to avoid stack overflow for
  483.      * huge structures.
  484.      *
  485.      * @param root the root node of the structure
  486.      * @return the parent node mapping
  487.      */
  488.     private Map<ImmutableNode, ImmutableNode> createParentMapping(final ImmutableNode root) {
  489.         final Map<ImmutableNode, ImmutableNode> parents = new HashMap<>();
  490.         updateParentMapping(parents, root);
  491.         return parents;
  492.     }

  493.     /**
  494.      * Creates a {@code TreeData} object for the specified root node.
  495.      *
  496.      * @param root the root node of the current tree
  497.      * @param current the current {@code TreeData} object (may be <strong>null</strong>)
  498.      * @return the {@code TreeData} describing the current tree
  499.      */
  500.     private TreeData createTreeData(final ImmutableNode root, final TreeData current) {
  501.         final NodeTracker newTracker = current != null ? current.getNodeTracker().detachAllTrackedNodes() : new NodeTracker();
  502.         return createTreeDataForRootAndTracker(root, newTracker);
  503.     }

  504.     /**
  505.      * Creates a {@code TreeData} object for the specified root node and {@code NodeTracker}. Other parameters are set to
  506.      * default values.
  507.      *
  508.      * @param root the new root node for this model
  509.      * @param newTracker the new {@code NodeTracker}
  510.      * @return the new {@code TreeData} object
  511.      */
  512.     private TreeData createTreeDataForRootAndTracker(final ImmutableNode root, final NodeTracker newTracker) {
  513.         return new TreeData(root, createParentMapping(root), Collections.<ImmutableNode, ImmutableNode>emptyMap(), newTracker, new ReferenceTracker());
  514.     }

  515.     /**
  516.      * Executes a transaction on the current data of this model. This method is called if an operation is to be executed on
  517.      * the model's root node or a tracked node which is not yet detached.
  518.      *
  519.      * @param txInit the {@code TransactionInitializer}
  520.      * @param selector an optional {@code NodeSelector} defining the target node
  521.      * @param currentData the current data of the model
  522.      * @param resolver the {@code NodeKeyResolver}
  523.      * @return a flag whether the operation has been completed successfully
  524.      */
  525.     private boolean executeTransactionOnCurrentStructure(final TransactionInitializer txInit, final NodeSelector selector, final TreeData currentData,
  526.         final NodeKeyResolver<ImmutableNode> resolver) {
  527.         final boolean done;
  528.         final ModelTransaction tx = new ModelTransaction(currentData, selector, resolver);
  529.         if (!txInit.initTransaction(tx)) {
  530.             done = true;
  531.         } else {
  532.             final TreeData newData = tx.execute();
  533.             done = structure.compareAndSet(tx.getCurrentData(), newData);
  534.         }
  535.         return done;
  536.     }

  537.     /**
  538.      * Tries to execute a transaction on the model of a detached tracked node. This method checks whether the target node of
  539.      * the transaction is a tracked node and if this node is already detached. If this is the case, the update operation is
  540.      * independent on this model and has to be executed on the specific model for the detached node.
  541.      *
  542.      * @param txInit the {@code TransactionInitializer}
  543.      * @param selector an optional {@code NodeSelector} defining the target node
  544.      * @param currentData the current data of the model
  545.      * @param resolver the {@code NodeKeyResolver} @return a flag whether the transaction could be executed
  546.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  547.      */
  548.     private boolean executeTransactionOnDetachedTrackedNode(final TransactionInitializer txInit, final NodeSelector selector, final TreeData currentData,
  549.         final NodeKeyResolver<ImmutableNode> resolver) {
  550.         if (selector != null) {
  551.             final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
  552.             if (detachedNodeModel != null) {
  553.                 detachedNodeModel.updateModel(txInit, null, resolver);
  554.                 return true;
  555.             }
  556.         }

  557.         return false;
  558.     }

  559.     /**
  560.      * {@inheritDoc} This implementation simply returns the current root node of this model.
  561.      */
  562.     @Override
  563.     public ImmutableNode getInMemoryRepresentation() {
  564.         return getTreeData().getRootNode();
  565.     }

  566.     /**
  567.      * {@inheritDoc} {@code InMemoryNodeModel} implements the {@code NodeHandler} interface itself. So this implementation
  568.      * just returns the <strong>this</strong> reference.
  569.      */
  570.     @Override
  571.     public NodeHandler<ImmutableNode> getNodeHandler() {
  572.         return getReferenceNodeHandler();
  573.     }

  574.     /**
  575.      * Gets a {@code ReferenceNodeHandler} object for this model. This extended node handler can be used to query
  576.      * references objects stored for this model.
  577.      *
  578.      * @return the {@code ReferenceNodeHandler}
  579.      */
  580.     public ReferenceNodeHandler getReferenceNodeHandler() {
  581.         return getTreeData();
  582.     }

  583.     /**
  584.      * Gets the root node of this mode. Note: This method should be used with care. The model may be updated concurrently
  585.      * which causes the root node to be replaced. If the root node is to be processed further (for example by executing queries on
  586.      * it), the model should be asked for its {@code NodeHandler}, and the root node should be obtained from there. The
  587.      * connection between a node handler and its root node remain constant because an update of the model causes the whole
  588.      * node handler to be replaced.
  589.      *
  590.      * @return the current root node
  591.      */
  592.     public ImmutableNode getRootNode() {
  593.         return getTreeData().getRootNode();
  594.     }

  595.     /**
  596.      * Gets the current {@code ImmutableNode} instance associated with the given {@code NodeSelector}. The node must be a
  597.      * tracked node, i.e. {@link #trackNode(NodeSelector, NodeKeyResolver)} must have been called before with the given
  598.      * selector.
  599.      *
  600.      * @param selector the {@code NodeSelector} defining the desired node
  601.      * @return the current {@code ImmutableNode} associated with this selector
  602.      * @throws ConfigurationRuntimeException if the selector is unknown
  603.      */
  604.     public ImmutableNode getTrackedNode(final NodeSelector selector) {
  605.         return structure.get().getNodeTracker().getTrackedNode(selector);
  606.     }

  607.     /**
  608.      * Gets a {@code NodeHandler} for a tracked node. Such a handler may be required for operations on a sub tree of the
  609.      * model. The handler to be returned depends on the current state of the tracked node. If it is still active, a handler
  610.      * is used which shares some data (especially the parent mapping) with this model. Detached track nodes in contrast have
  611.      * their own separate model; in this case a handler associated with this model is returned.
  612.      *
  613.      * @param selector the {@code NodeSelector} defining the tracked node
  614.      * @return a {@code NodeHandler} for this tracked node
  615.      * @throws ConfigurationRuntimeException if the selector is unknown
  616.      */
  617.     public NodeHandler<ImmutableNode> getTrackedNodeHandler(final NodeSelector selector) {
  618.         final TreeData currentData = structure.get();
  619.         final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
  620.         return detachedNodeModel != null ? detachedNodeModel.getNodeHandler()
  621.             : new TrackedNodeHandler(currentData.getNodeTracker().getTrackedNode(selector), currentData);
  622.     }

  623.     /**
  624.      * Gets the current {@code TreeData} object. This object contains all information about the current node structure.
  625.      *
  626.      * @return the current {@code TreeData} object
  627.      */
  628.     TreeData getTreeData() {
  629.         return structure.get();
  630.     }

  631.     /**
  632.      * Initializes a transaction for an add operation.
  633.      *
  634.      * @param tx the transaction to be initialized
  635.      * @param key the key
  636.      * @param values the collection with node values
  637.      * @param resolver the {@code NodeKeyResolver}
  638.      */
  639.     private void initializeAddTransaction(final ModelTransaction tx, final String key, final Iterable<?> values,
  640.         final NodeKeyResolver<ImmutableNode> resolver) {
  641.         final NodeAddData<ImmutableNode> addData = resolver.resolveAddKey(tx.getQueryRoot(), key, tx.getCurrentData());
  642.         if (addData.isAttribute()) {
  643.             addAttributeProperty(tx, addData, values);
  644.         } else {
  645.             addNodeProperty(tx, addData, values);
  646.         }
  647.     }

  648.     /**
  649.      * Returns a flag whether the specified tracked node is detached. As long as the {@code NodeSelector} associated with
  650.      * that node returns a single instance, the tracked node is said to be <em>life</em>. If now an update of the model
  651.      * happens which invalidates the selector (maybe the target node was removed), the tracked node becomes detached. It is
  652.      * still possible to query the node; here the latest valid instance is returned. But further changes on the node model
  653.      * are no longer tracked for this node. So even if there are further changes which would make the {@code NodeSelector}
  654.      * valid again, the tracked node stays in detached state.
  655.      *
  656.      * @param selector the {@code NodeSelector} defining the desired node
  657.      * @return a flag whether this tracked node is in detached state
  658.      * @throws ConfigurationRuntimeException if the selector is unknown
  659.      */
  660.     public boolean isTrackedNodeDetached(final NodeSelector selector) {
  661.         return structure.get().getNodeTracker().isTrackedNodeDetached(selector);
  662.     }

  663.     /**
  664.      * Merges the root node of this model with the specified node. This method is typically caused by configuration
  665.      * implementations when a configuration source is loaded, and its data has to be added to the model. It is possible to
  666.      * define the new name of the root node and to pass in a map with reference objects.
  667.      *
  668.      * @param node the node to be merged with the root node
  669.      * @param rootName the new name of the root node; can be <strong>null</strong>, then the name of the root node is not changed
  670.      *        unless it is <strong>null</strong>
  671.      * @param references an optional map with reference objects
  672.      * @param rootRef an optional reference object for the new root node
  673.      * @param resolver the {@code NodeKeyResolver}
  674.      */
  675.     public void mergeRoot(final ImmutableNode node, final String rootName, final Map<ImmutableNode, ?> references, final Object rootRef,
  676.         final NodeKeyResolver<ImmutableNode> resolver) {
  677.         updateModel(tx -> {
  678.             final TreeData current = tx.getCurrentData();
  679.             final String newRootName = determineRootName(current.getRootNode(), node, rootName);
  680.             if (newRootName != null) {
  681.                 tx.addChangeNodeNameOperation(current.getRootNode(), newRootName);
  682.             }
  683.             tx.addAddNodesOperation(current.getRootNode(), node.getChildren());
  684.             tx.addAttributesOperation(current.getRootNode(), node.getAttributes());
  685.             if (node.getValue() != null) {
  686.                 tx.addChangeNodeValueOperation(current.getRootNode(), node.getValue());
  687.             }
  688.             if (references != null) {
  689.                 tx.addNewReferences(references);
  690.             }
  691.             if (rootRef != null) {
  692.                 tx.addNewReference(current.getRootNode(), rootRef);
  693.             }
  694.             return true;
  695.         }, null, resolver);
  696.     }

  697.     /**
  698.      * Replaces an active tracked node. The node then becomes detached.
  699.      *
  700.      * @param currentData the current data of the model
  701.      * @param selector the {@code NodeSelector} defining the tracked node
  702.      * @param newNode the node replacing the tracked node
  703.      * @return a flag whether the operation was successful
  704.      */
  705.     private boolean replaceActiveTrackedNode(final TreeData currentData, final NodeSelector selector, final ImmutableNode newNode) {
  706.         final NodeTracker newTracker = currentData.getNodeTracker().replaceAndDetachTrackedNode(selector, newNode);
  707.         return structure.compareAndSet(currentData, currentData.updateNodeTracker(newTracker));
  708.     }

  709.     /**
  710.      * Replaces a tracked node if it is already detached.
  711.      *
  712.      * @param currentData the current data of the model
  713.      * @param selector the {@code NodeSelector} defining the tracked node
  714.      * @param newNode the node replacing the tracked node
  715.      * @return a flag whether the operation was successful
  716.      */
  717.     private boolean replaceDetachedTrackedNode(final TreeData currentData, final NodeSelector selector, final ImmutableNode newNode) {
  718.         final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
  719.         if (detachedNodeModel != null) {
  720.             detachedNodeModel.setRootNode(newNode);
  721.             return true;
  722.         }

  723.         return false;
  724.     }

  725.     /**
  726.      * Replaces the root node of this model. This method is similar to {@link #setRootNode(ImmutableNode)}; however, tracked
  727.      * nodes will not get lost. The model applies the selectors of all tracked nodes on the new nodes hierarchy, so that
  728.      * corresponding nodes are selected (this may cause nodes to become detached if a select operation fails). This
  729.      * operation is useful if the new nodes hierarchy to be set is known to be similar to the old one. Note that reference
  730.      * objects are lost; there is no way to automatically match nodes between the old and the new nodes hierarchy.
  731.      *
  732.      * @param newRoot the new root node to be set (must not be <strong>null</strong>)
  733.      * @param resolver the {@code NodeKeyResolver}
  734.      * @throws IllegalArgumentException if the new root node is <strong>null</strong>
  735.      */
  736.     public void replaceRoot(final ImmutableNode newRoot, final NodeKeyResolver<ImmutableNode> resolver) {
  737.         if (newRoot == null) {
  738.             throw new IllegalArgumentException("Replaced root node must not be null!");
  739.         }

  740.         final TreeData current = structure.get();
  741.         // this step is needed to get a valid NodeHandler
  742.         final TreeData temp = createTreeDataForRootAndTracker(newRoot, current.getNodeTracker());
  743.         structure.set(temp.updateNodeTracker(temp.getNodeTracker().update(newRoot, null, resolver, temp)));
  744.     }

  745.     /**
  746.      * Replaces a tracked node by another node. If the tracked node is not yet detached, it becomes now detached. The passed
  747.      * in node (which must not be <strong>null</strong>) becomes the new root node of an independent model for this tracked node.
  748.      * Further updates of this model do not affect the tracked node's model and vice versa.
  749.      *
  750.      * @param selector the {@code NodeSelector} defining the tracked node
  751.      * @param newNode the node replacing the tracked node (must not be <strong>null</strong>)
  752.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  753.      * @throws IllegalArgumentException if the replacement node is <strong>null</strong>
  754.      */
  755.     public void replaceTrackedNode(final NodeSelector selector, final ImmutableNode newNode) {
  756.         if (newNode == null) {
  757.             throw new IllegalArgumentException("Replacement node must not be null!");
  758.         }

  759.         boolean done;
  760.         do {
  761.             final TreeData currentData = structure.get();
  762.             done = replaceDetachedTrackedNode(currentData, selector, newNode) || replaceActiveTrackedNode(currentData, selector, newNode);
  763.         } while (!done);
  764.     }

  765.     /**
  766.      * Allows tracking all nodes selected by a key. This method evaluates the specified key on the current nodes structure.
  767.      * For all selected nodes corresponding {@code NodeSelector} objects are created, and they are tracked. The returned
  768.      * collection of {@code NodeSelector} objects can be used for interacting with the selected nodes.
  769.      *
  770.      * @param key the key for selecting the nodes to track
  771.      * @param resolver the {@code NodeKeyResolver}
  772.      * @return a collection with the {@code NodeSelector} objects for the new tracked nodes
  773.      */
  774.     public Collection<NodeSelector> selectAndTrackNodes(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
  775.         final Mutable<Collection<NodeSelector>> refSelectors = new MutableObject<>();
  776.         boolean done;
  777.         do {
  778.             final TreeData current = structure.get();
  779.             final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
  780.             if (nodes.isEmpty()) {
  781.                 return Collections.emptyList();
  782.             }
  783.             done = structure.compareAndSet(current, createSelectorsForTrackedNodes(refSelectors, nodes, current, resolver));
  784.         } while (!done);
  785.         return refSelectors.getValue();
  786.     }

  787.     /**
  788.      * Sets the value of a property using a tracked node as root node. This method works like the normal
  789.      * {@code setProperty()} method, but the origin of the operation (also for the interpretation of the passed in key) is a
  790.      * tracked node identified by the passed in {@code NodeSelector}. The selector can be <strong>null</strong>, then the root node is
  791.      * assumed.
  792.      *
  793.      * @param key the key
  794.      * @param selector the {@code NodeSelector} defining the root node (or <strong>null</strong>)
  795.      * @param value the new value for this property
  796.      * @param resolver the {@code NodeKeyResolver}
  797.      * @throws ConfigurationRuntimeException if the selector cannot be resolved
  798.      */
  799.     public void setProperty(final String key, final NodeSelector selector, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
  800.         updateModel(tx -> {
  801.             boolean added = false;
  802.             final NodeUpdateData<ImmutableNode> updateData = resolver.resolveUpdateKey(tx.getQueryRoot(), key, value, tx.getCurrentData());
  803.             if (!updateData.getNewValues().isEmpty()) {
  804.                 initializeAddTransaction(tx, key, updateData.getNewValues(), resolver);
  805.                 added = true;
  806.             }
  807.             final boolean cleared = initializeClearTransaction(tx, updateData.getRemovedNodes());
  808.             final boolean updated = initializeUpdateTransaction(tx, updateData.getChangedValues());
  809.             return added || cleared || updated;
  810.         }, selector, resolver);
  811.     }

  812.     @Override
  813.     public void setProperty(final String key, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
  814.         setProperty(key, null, value, resolver);
  815.     }

  816.     /**
  817.      * {@inheritDoc} All tracked nodes and reference objects managed by this model are cleared.Care has to be taken when
  818.      * this method is used and the model is accessed by multiple threads. It is not deterministic which concurrent
  819.      * operations see the old root and which see the new root node.
  820.      *
  821.      * @param newRoot the new root node to be set (can be <strong>null</strong>, then an empty root node is set)
  822.      */
  823.     @Override
  824.     public void setRootNode(final ImmutableNode newRoot) {
  825.         structure.set(createTreeData(initialRootNode(newRoot), structure.get()));
  826.     }

  827.     /**
  828.      * Tracks all nodes which are children of the node selected by the passed in key. If the key selects exactly one node,
  829.      * for all children of this node {@code NodeSelector} objects are created, and they become tracked nodes. The returned
  830.      * collection of {@code NodeSelector} objects can be used for interacting with the selected nodes.
  831.      *
  832.      * @param key the key for selecting the parent node whose children are to be tracked
  833.      * @param resolver the {@code NodeKeyResolver}
  834.      * @return a collection with the {@code NodeSelector} objects for the new tracked nodes
  835.      */
  836.     public Collection<NodeSelector> trackChildNodes(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
  837.         final Mutable<Collection<NodeSelector>> refSelectors = new MutableObject<>();
  838.         boolean done;
  839.         do {
  840.             refSelectors.setValue(Collections.<NodeSelector>emptyList());
  841.             final TreeData current = structure.get();
  842.             final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
  843.             if (nodes.size() == 1) {
  844.                 final ImmutableNode node = nodes.get(0);
  845.                 done = node.getChildren().isEmpty()
  846.                     || structure.compareAndSet(current, createSelectorsForTrackedNodes(refSelectors, node.getChildren(), current, resolver));
  847.             } else {
  848.                 done = true;
  849.             }
  850.         } while (!done);
  851.         return refSelectors.getValue();
  852.     }

  853.     /**
  854.      * Tracks a node which is a child of another node selected by the passed in key. If the selected node has a child node
  855.      * with this name, it is tracked and its selector is returned. Otherwise, a new child node with this name is created
  856.      * first.
  857.      *
  858.      * @param key the key for selecting the parent node
  859.      * @param childName the name of the child node
  860.      * @param resolver the {@code NodeKeyResolver}
  861.      * @return the {@code NodeSelector} for the tracked child node
  862.      * @throws ConfigurationRuntimeException if the passed in key does not select a single node
  863.      */
  864.     public NodeSelector trackChildNodeWithCreation(final String key, final String childName, final NodeKeyResolver<ImmutableNode> resolver) {
  865.         final MutableObject<NodeSelector> refSelector = new MutableObject<>();
  866.         boolean done;

  867.         do {
  868.             final TreeData current = structure.get();
  869.             final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
  870.             if (nodes.size() != 1) {
  871.                 throw new ConfigurationRuntimeException("Key does not select a single node: " + key);
  872.             }

  873.             final ImmutableNode parent = nodes.get(0);
  874.             final TreeData newData = createDataWithTrackedChildNode(current, parent, childName, resolver, refSelector);

  875.             done = structure.compareAndSet(current, newData);
  876.         } while (!done);

  877.         return refSelector.getValue();
  878.     }

  879.     /**
  880.      * Adds a node to be tracked. After this method has been called with a specific {@code NodeSelector}, the node
  881.      * associated with this key can be always obtained using {@link #getTrackedNode(NodeSelector)} with the same selector.
  882.      * This is useful because during updates of a model parts of the structure are replaced. Therefore, it is not a good
  883.      * idea to simply hold a reference to a node; this might become outdated soon. Rather, the node should be tracked. This
  884.      * mechanism ensures that always the correct node reference can be obtained.
  885.      *
  886.      * @param selector the {@code NodeSelector} defining the desired node
  887.      * @param resolver the {@code NodeKeyResolver}
  888.      * @throws ConfigurationRuntimeException if the selector does not select a single node
  889.      */
  890.     public void trackNode(final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
  891.         boolean done;
  892.         do {
  893.             final TreeData current = structure.get();
  894.             final NodeTracker newTracker = current.getNodeTracker().trackNode(current.getRootNode(), selector, resolver, current);
  895.             done = structure.compareAndSet(current, current.updateNodeTracker(newTracker));
  896.         } while (!done);
  897.     }

  898.     /**
  899.      * Removes a tracked node. This method is the opposite of {@code trackNode()}. It has to be called if there is no longer
  900.      * the need to track a specific node. Note that for each call of {@code trackNode()} there has to be a corresponding
  901.      * {@code untrackNode()} call. This ensures that multiple observers can track the same node.
  902.      *
  903.      * @param selector the {@code NodeSelector} defining the desired node
  904.      * @throws ConfigurationRuntimeException if the specified node is not tracked
  905.      */
  906.     public void untrackNode(final NodeSelector selector) {
  907.         boolean done;
  908.         do {
  909.             final TreeData current = structure.get();
  910.             final NodeTracker newTracker = current.getNodeTracker().untrackNode(selector);
  911.             done = structure.compareAndSet(current, current.updateNodeTracker(newTracker));
  912.         } while (!done);
  913.     }

  914.     /**
  915.      * Performs a non-blocking, thread-safe update of this model based on a transaction initialized by the passed in
  916.      * initializer. This method uses the atomic reference for the model's current data to ensure that an update was
  917.      * successful even if the model is concurrently accessed.
  918.      *
  919.      * @param txInit the {@code TransactionInitializer}
  920.      * @param selector an optional {@code NodeSelector} defining the target node of the transaction
  921.      * @param resolver the {@code NodeKeyResolver}
  922.      */
  923.     private void updateModel(final TransactionInitializer txInit, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
  924.         boolean done;

  925.         do {
  926.             final TreeData currentData = getTreeData();
  927.             done = executeTransactionOnDetachedTrackedNode(txInit, selector, currentData, resolver)
  928.                 || executeTransactionOnCurrentStructure(txInit, selector, currentData, resolver);
  929.         } while (!done);
  930.     }
  931. }