001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.configuration2.tree;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.Map;
027import java.util.concurrent.atomic.AtomicReference;
028
029import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
030import org.apache.commons.lang3.mutable.Mutable;
031import org.apache.commons.lang3.mutable.MutableObject;
032
033/**
034 * <p>
035 * A specialized node model implementation which operates on {@link ImmutableNode} structures.
036 * </p>
037 * <p>
038 * This {@code NodeModel} implementation keeps all its data as a tree of {@link ImmutableNode} objects in memory. The
039 * managed structure can be manipulated in a thread-safe, non-blocking way. This is achieved by using atomic variables:
040 * The root of the tree is stored in an atomic reference variable. Each update operation causes a new structure to be
041 * constructed (which reuses as much from the original structure as possible). The old root node is then replaced by the
042 * new one using an atomic compare-and-set operation. If this fails, the manipulation has to be done anew on the updated
043 * structure.
044 * </p>
045 *
046 * @since 2.0
047 */
048public class InMemoryNodeModel implements NodeModel<ImmutableNode> {
049    /**
050     * An interface used internally for handling concurrent updates. An implementation has to populate the passed in
051     * {@code ModelTransaction}. The transaction is then executed, and an atomic update of the model's {@code TreeData} is
052     * attempted. If this fails - because another update came across -, the whole operation has to be tried anew.
053     */
054    private interface TransactionInitializer {
055        /**
056         * Initializes the specified transaction for an update operation. The return value indicates whether the transaction
057         * should be executed. A result of <b>false</b> means that the update is to be aborted (maybe another update method was
058         * called).
059         *
060         * @param tx the transaction to be initialized
061         * @return a flag whether the update should continue
062         */
063        boolean initTransaction(ModelTransaction tx);
064    }
065
066    /**
067     * A dummy node handler instance used in operations which require only a limited functionality.
068     */
069    private static final NodeHandler<ImmutableNode> DUMMY_HANDLER = new TreeData(null, Collections.<ImmutableNode, ImmutableNode>emptyMap(),
070        Collections.<ImmutableNode, ImmutableNode>emptyMap(), null, new ReferenceTracker());
071
072    /**
073     * Handles an add property operation if the property to be added is an attribute.
074     *
075     * @param tx the transaction
076     * @param addData the {@code NodeAddData}
077     * @param values the collection with node values
078     */
079    private static void addAttributeProperty(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Iterable<?> values) {
080        if (addData.getPathNodes().isEmpty()) {
081            tx.addAttributeOperation(addData.getParent(), addData.getNewNodeName(), values.iterator().next());
082        } else {
083            final int pathNodeCount = addData.getPathNodes().size();
084            final ImmutableNode childWithAttribute = new ImmutableNode.Builder().name(addData.getPathNodes().get(pathNodeCount - 1))
085                .addAttribute(addData.getNewNodeName(), values.iterator().next()).create();
086            final ImmutableNode newChild = pathNodeCount > 1
087                ? createNodeOnPath(addData.getPathNodes().subList(0, pathNodeCount - 1).iterator(), Collections.singleton(childWithAttribute))
088                : childWithAttribute;
089            tx.addAddNodeOperation(addData.getParent(), newChild);
090        }
091    }
092
093    /**
094     * Handles an add property operation if the property to be added is a node.
095     *
096     * @param tx the transaction
097     * @param addData the {@code NodeAddData}
098     * @param values the collection with node values
099     */
100    private static void addNodeProperty(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Iterable<?> values) {
101        final Collection<ImmutableNode> newNodes = createNodesToAdd(addData.getNewNodeName(), values);
102        addNodesByAddData(tx, addData, newNodes);
103    }
104
105    /**
106     * Initializes a transaction to add a collection of nodes as described by a {@code NodeAddData} object. If necessary,
107     * new path nodes are created. Eventually, the new nodes are added as children to the specified target node.
108     *
109     * @param tx the transaction
110     * @param addData the {@code NodeAddData}
111     * @param newNodes the collection of new child nodes
112     */
113    private static void addNodesByAddData(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Collection<ImmutableNode> newNodes) {
114        if (addData.getPathNodes().isEmpty()) {
115            tx.addAddNodesOperation(addData.getParent(), newNodes);
116        } else {
117            final ImmutableNode newChild = createNodeToAddWithPath(addData, newNodes);
118            tx.addAddNodeOperation(addData.getParent(), newChild);
119        }
120    }
121
122    /**
123     * Creates an exception referring to an invalid key for adding properties. Such an exception is thrown when an operation
124     * tries to add something to an attribute.
125     *
126     * @param key the invalid key causing this exception
127     * @return the exception
128     */
129    private static IllegalArgumentException attributeKeyException(final String key) {
130        return new IllegalArgumentException("New nodes cannot be added to an attribute key: " + key);
131    }
132
133    /**
134     * Checks if the passed in node is defined. Result is <b>true</b> if the node contains any data.
135     *
136     * @param node the node in question
137     * @return <b>true</b> if the node is defined, <b>false</b> otherwise
138     */
139    static boolean checkIfNodeDefined(final ImmutableNode node) {
140        return node.getValue() != null || !node.getChildren().isEmpty() || !node.getAttributes().isEmpty();
141    }
142
143    /**
144     * Creates a new data object with a tracked child node of the given parent node. If such a child node already exists, it
145     * is used. Otherwise, a new one is created.
146     *
147     * @param current the current {@code TreeData} object
148     * @param parent the parent node
149     * @param childName the name of the child node
150     * @param resolver the {@code NodeKeyResolver}
151     * @param refSelector here the newly created {@code NodeSelector} is returned
152     * @return the new {@code TreeData} instance
153     */
154    private static TreeData createDataWithTrackedChildNode(final TreeData current, final ImmutableNode parent, final String childName,
155        final NodeKeyResolver<ImmutableNode> resolver, final MutableObject<NodeSelector> refSelector) {
156        final TreeData newData;
157        final List<ImmutableNode> namedChildren = current.getChildren(parent, childName);
158        if (!namedChildren.isEmpty()) {
159            newData = updateDataWithNewTrackedNode(current, namedChildren.get(0), resolver, refSelector);
160        } else {
161            final ImmutableNode child = new ImmutableNode.Builder().name(childName).create();
162            final ModelTransaction tx = new ModelTransaction(current, null, resolver);
163            tx.addAddNodeOperation(parent, child);
164            newData = updateDataWithNewTrackedNode(tx.execute(), child, resolver, refSelector);
165        }
166        return newData;
167    }
168
169    /**
170     * Recursive helper method for creating a path node for an add operation. All path nodes except for the last have a
171     * single child. The last path node has the new nodes as children.
172     *
173     * @param it the iterator over the names of the path nodes
174     * @param newNodes the collection of new child nodes
175     * @return the newly created path node
176     */
177    private static ImmutableNode createNodeOnPath(final Iterator<String> it, final Collection<ImmutableNode> newNodes) {
178        final String nodeName = it.next();
179        final ImmutableNode.Builder builder;
180        if (it.hasNext()) {
181            builder = new ImmutableNode.Builder(1);
182            builder.addChild(createNodeOnPath(it, newNodes));
183        } else {
184            builder = new ImmutableNode.Builder(newNodes.size());
185            builder.addChildren(newNodes);
186        }
187        return builder.name(nodeName).create();
188    }
189
190    /**
191     * Creates a collection with new nodes with a given name and a value from a given collection.
192     *
193     * @param newNodeName the name of the new nodes
194     * @param values the collection with node values
195     * @return the newly created collection
196     */
197    private static Collection<ImmutableNode> createNodesToAdd(final String newNodeName, final Iterable<?> values) {
198        final Collection<ImmutableNode> nodes = new LinkedList<>();
199        values.forEach(value -> nodes.add(new ImmutableNode.Builder().name(newNodeName).value(value).create()));
200        return nodes;
201    }
202
203    /**
204     * Creates a node structure consisting of the path nodes defined by the passed in {@code NodeAddData} instance and all
205     * new child nodes.
206     *
207     * @param addData the {@code NodeAddData}
208     * @param newNodes the collection of new child nodes
209     * @return the parent node of the newly created hierarchy
210     */
211    private static ImmutableNode createNodeToAddWithPath(final NodeAddData<ImmutableNode> addData, final Collection<ImmutableNode> newNodes) {
212        return createNodeOnPath(addData.getPathNodes().iterator(), newNodes);
213    }
214
215    /**
216     * Creates tracked node entries for the specified nodes and creates the corresponding selectors.
217     *
218     * @param refSelectors the reference where to store the selectors
219     * @param nodes the nodes to be tracked
220     * @param current the current {@code TreeData} object
221     * @param resolver the {@code NodeKeyResolver}
222     * @return the updated {@code TreeData} object
223     */
224    private static TreeData createSelectorsForTrackedNodes(final Mutable<Collection<NodeSelector>> refSelectors, final List<ImmutableNode> nodes,
225            final TreeData current, final NodeKeyResolver<ImmutableNode> resolver) {
226        final List<NodeSelector> selectors = new ArrayList<>(nodes.size());
227        final Map<ImmutableNode, String> cache = new HashMap<>();
228        nodes.forEach(node -> selectors.add(new NodeSelector(resolver.nodeKey(node, cache, current))));
229        refSelectors.setValue(selectors);
230        final NodeTracker newTracker = current.getNodeTracker().trackNodes(selectors, nodes);
231        return current.updateNodeTracker(newTracker);
232    }
233
234    /**
235     * Determines the name of the root node for a merge operation. If a root name is provided, it is used. Otherwise, if the
236     * current root node has no name, the name of the node to be merged is used. A result of <b>null</b> means that no node
237     * name has to be set.
238     *
239     * @param rootNode the current root node
240     * @param node the node to be merged with the root node
241     * @param rootName the name of the resulting node
242     * @return the new name of the root node
243     */
244    private static String determineRootName(final ImmutableNode rootNode, final ImmutableNode node, final String rootName) {
245        if (rootName != null) {
246            return rootName;
247        }
248        if (rootNode.getNodeName() == null) {
249            return node.getNodeName();
250        }
251        return null;
252    }
253
254    /**
255     * Initializes a transaction to clear the values of a property based on the passed in collection of affected results.
256     *
257     * @param tx the transaction to be initialized
258     * @param results a collection with results pointing to the nodes to be cleared
259     * @return a flag whether there are elements to be cleared
260     */
261    private static boolean initializeClearTransaction(final ModelTransaction tx, final Collection<QueryResult<ImmutableNode>> results) {
262        results.forEach(result -> {
263            if (result.isAttributeResult()) {
264                tx.addRemoveAttributeOperation(result.getNode(), result.getAttributeName());
265            } else {
266                tx.addClearNodeValueOperation(result.getNode());
267            }
268        });
269
270        return !results.isEmpty();
271    }
272
273    /**
274     * Initializes a transaction to change the values of some query results based on the passed in map.
275     *
276     * @param tx the transaction to be initialized
277     * @param changedValues the map defining the elements to be changed
278     * @return a flag whether there are elements to be updated
279     */
280    private static boolean initializeUpdateTransaction(final ModelTransaction tx, final Map<QueryResult<ImmutableNode>, Object> changedValues) {
281        changedValues.forEach((k, v) -> {
282            final ImmutableNode node = k.getNode();
283            if (k.isAttributeResult()) {
284                tx.addAttributeOperation(node, k.getAttributeName(), v);
285            } else {
286                tx.addChangeNodeValueOperation(node, v);
287            }
288        });
289
290        return !changedValues.isEmpty();
291    }
292
293    /**
294     * Determines the initial root node of this model. If a root node has been provided, it is used. Otherwise, an empty
295     * dummy root node is created.
296     *
297     * @param providedRoot the passed in root node
298     * @return the root node to be used
299     */
300    private static ImmutableNode initialRootNode(final ImmutableNode providedRoot) {
301        return providedRoot != null ? providedRoot : new ImmutableNode.Builder().create();
302    }
303
304    /**
305     * Adds a tracked node that has already been resolved to the specified data object.
306     *
307     * @param current the current {@code TreeData} object
308     * @param node the node in question
309     * @param resolver the {@code NodeKeyResolver}
310     * @param refSelector here the newly created {@code NodeSelector} is returned
311     * @return the new {@code TreeData} instance
312     */
313    private static TreeData updateDataWithNewTrackedNode(final TreeData current, final ImmutableNode node, final NodeKeyResolver<ImmutableNode> resolver,
314        final MutableObject<NodeSelector> refSelector) {
315        final NodeSelector selector = new NodeSelector(resolver.nodeKey(node, new HashMap<>(), current));
316        refSelector.setValue(selector);
317        final NodeTracker newTracker = current.getNodeTracker().trackNodes(Collections.singleton(selector), Collections.singleton(node));
318        return current.updateNodeTracker(newTracker);
319    }
320
321    /**
322     * Updates the mapping from nodes to their parents for the passed in hierarchy of nodes. This method traverses all
323     * children and grand-children of the passed in root node. For each node in the subtree the parent relation is added to
324     * the map.
325     *
326     * @param parents the map with parent nodes
327     * @param root the root node of the current tree
328     */
329    static void updateParentMapping(final Map<ImmutableNode, ImmutableNode> parents, final ImmutableNode root) {
330        NodeTreeWalker.INSTANCE.walkBFS(root, new ConfigurationNodeVisitorAdapter<ImmutableNode>() {
331            @Override
332            public void visitBeforeChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
333                node.forEach(c -> parents.put(c, node));
334            }
335        }, DUMMY_HANDLER);
336    }
337
338    /**
339     * Checks whether the specified collection with values is not empty.
340     *
341     * @param values the collection with node values
342     * @return <b>true</b> if values are provided, <b>false</b> otherwise
343     */
344    private static boolean valuesNotEmpty(final Iterable<?> values) {
345        return values.iterator().hasNext();
346    }
347
348    /** Stores information about the current nodes structure. */
349    private final AtomicReference<TreeData> structure;
350
351    /**
352     * Creates a new instance of {@code InMemoryNodeModel} which is initialized with an empty root node.
353     */
354    public InMemoryNodeModel() {
355        this(null);
356    }
357
358    /**
359     * Creates a new instance of {@code InMemoryNodeModel} and initializes it from the given root node. If the passed in
360     * node is <b>null</b>, a new, empty root node is created.
361     *
362     * @param root the new root node for this model
363     */
364    public InMemoryNodeModel(final ImmutableNode root) {
365        structure = new AtomicReference<>(createTreeData(initialRootNode(root), null));
366    }
367
368    @Override
369    public void addNodes(final String key, final Collection<? extends ImmutableNode> nodes, final NodeKeyResolver<ImmutableNode> resolver) {
370        addNodes(key, null, nodes, resolver);
371    }
372
373    /**
374     * Adds new nodes using a tracked node as root node. This method works like the normal {@code addNodes()} method, but
375     * the origin of the operation (also for the interpretation of the passed in key) is a tracked node identified by the
376     * passed in {@code NodeSelector}. The selector can be <b>null</b>, then the root node is assumed.
377     *
378     * @param key the key
379     * @param selector the {@code NodeSelector} defining the root node (or <b>null</b>)
380     * @param nodes the collection of new nodes to be added
381     * @param resolver the {@code NodeKeyResolver}
382     * @throws ConfigurationRuntimeException if the selector cannot be resolved
383     */
384    public void addNodes(final String key, final NodeSelector selector, final Collection<? extends ImmutableNode> nodes,
385        final NodeKeyResolver<ImmutableNode> resolver) {
386        if (nodes != null && !nodes.isEmpty()) {
387            updateModel(tx -> {
388                final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, tx.getCurrentData());
389                if (results.size() == 1) {
390                    if (results.get(0).isAttributeResult()) {
391                        throw attributeKeyException(key);
392                    }
393                    tx.addAddNodesOperation(results.get(0).getNode(), nodes);
394                } else {
395                    final NodeAddData<ImmutableNode> addData = resolver.resolveAddKey(tx.getQueryRoot(), key, tx.getCurrentData());
396                    if (addData.isAttribute()) {
397                        throw attributeKeyException(key);
398                    }
399                    final ImmutableNode newNode = new ImmutableNode.Builder(nodes.size()).name(addData.getNewNodeName()).addChildren(nodes).create();
400                    addNodesByAddData(tx, addData, Collections.singleton(newNode));
401                }
402                return true;
403            }, selector, resolver);
404        }
405    }
406
407    @Override
408    public void addProperty(final String key, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
409        addProperty(key, null, values, resolver);
410    }
411
412    /**
413     * Adds new property values using a tracked node as root node. This method works like the normal {@code addProperty()}
414     * method, but the origin of the operation (also for the interpretation of the passed in key) is a tracked node
415     * identified by the passed in {@code NodeSelector}. The selector can be <b>null</b>, then the root node is assumed.
416     *
417     * @param key the key
418     * @param selector the {@code NodeSelector} defining the root node (or <b>null</b>)
419     * @param values the values to be added
420     * @param resolver the {@code NodeKeyResolver}
421     * @throws ConfigurationRuntimeException if the selector cannot be resolved
422     */
423    public void addProperty(final String key, final NodeSelector selector, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
424        if (valuesNotEmpty(values)) {
425            updateModel(tx -> {
426                initializeAddTransaction(tx, key, values, resolver);
427                return true;
428            }, selector, resolver);
429        }
430    }
431
432    /**
433     * {@inheritDoc} A new empty root node is created with the same name as the current root node. Implementation note:
434     * Because this is a hard reset the usual dance for dealing with concurrent updates is not required here.
435     *
436     * @param resolver the {@code NodeKeyResolver}
437     */
438    @Override
439    public void clear(final NodeKeyResolver<ImmutableNode> resolver) {
440        final ImmutableNode newRoot = new ImmutableNode.Builder().name(getRootNode().getNodeName()).create();
441        setRootNode(newRoot);
442    }
443
444    /**
445     * {@inheritDoc} If this operation leaves an affected node in an undefined state, it is removed from the model.
446     */
447    @Override
448    public void clearProperty(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
449        clearProperty(key, null, resolver);
450    }
451
452    /**
453     * Clears a property using a tracked node as root node. This method works like the normal {@code clearProperty()}
454     * method, but the origin of the operation (also for the interpretation of the passed in key) is a tracked node
455     * identified by the passed in {@code NodeSelector}. The selector can be <b>null</b>, then the root node is assumed.
456     *
457     * @param key the key
458     * @param selector the {@code NodeSelector} defining the root node (or <b>null</b>)
459     * @param resolver the {@code NodeKeyResolver}
460     * @throws ConfigurationRuntimeException if the selector cannot be resolved
461     */
462    public void clearProperty(final String key, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
463        updateModel(tx -> {
464            final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, tx.getCurrentData());
465            return initializeClearTransaction(tx, results);
466        }, selector, resolver);
467    }
468
469    /**
470     * {@inheritDoc} This implementation checks whether nodes become undefined after subtrees have been removed. If this is
471     * the case, such nodes are removed, too. Return value is a collection with {@code QueryResult} objects for the elements
472     * to be removed from the model.
473     */
474    @Override
475    public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
476        return clearTree(key, null, resolver);
477    }
478
479    /**
480     * Clears a whole sub tree using a tracked node as root node. This method works like the normal {@code clearTree()}
481     * method, but the origin of the operation (also for the interpretation of the passed in key) is a tracked node
482     * identified by the passed in {@code NodeSelector}. The selector can be <b>null</b>, then the root node is assumed.
483     *
484     * @param key the key
485     * @param selector the {@code NodeSelector} defining the root node (or <b>null</b>)
486     * @param resolver the {@code NodeKeyResolver}
487     * @return a list with the results to be removed
488     * @throws ConfigurationRuntimeException if the selector cannot be resolved
489     */
490    public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
491        final List<QueryResult<ImmutableNode>> removedElements = new LinkedList<>();
492        updateModel(tx -> {
493            boolean changes = false;
494            final TreeData currentStructure = tx.getCurrentData();
495            final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, currentStructure);
496            removedElements.clear();
497            removedElements.addAll(results);
498            for (final QueryResult<ImmutableNode> result : results) {
499                if (result.isAttributeResult()) {
500                    tx.addRemoveAttributeOperation(result.getNode(), result.getAttributeName());
501                } else {
502                    if (result.getNode() == currentStructure.getRootNode()) {
503                        // the whole model is to be cleared
504                        clear(resolver);
505                        return false;
506                    }
507                    tx.addRemoveNodeOperation(currentStructure.getParent(result.getNode()), result.getNode());
508                }
509                changes = true;
510            }
511            return changes;
512        }, selector, resolver);
513
514        return removedElements;
515    }
516
517    /**
518     * Creates the mapping to parent nodes for the nodes structured represented by the passed in root node. Each node is
519     * assigned its parent node. Here an iterative algorithm is used rather than a recursive one to avoid stack overflow for
520     * huge structures.
521     *
522     * @param root the root node of the structure
523     * @return the parent node mapping
524     */
525    private Map<ImmutableNode, ImmutableNode> createParentMapping(final ImmutableNode root) {
526        final Map<ImmutableNode, ImmutableNode> parents = new HashMap<>();
527        updateParentMapping(parents, root);
528        return parents;
529    }
530
531    /**
532     * Creates a {@code TreeData} object for the specified root node.
533     *
534     * @param root the root node of the current tree
535     * @param current the current {@code TreeData} object (may be <b>null</b>)
536     * @return the {@code TreeData} describing the current tree
537     */
538    private TreeData createTreeData(final ImmutableNode root, final TreeData current) {
539        final NodeTracker newTracker = current != null ? current.getNodeTracker().detachAllTrackedNodes() : new NodeTracker();
540        return createTreeDataForRootAndTracker(root, newTracker);
541    }
542
543    /**
544     * Creates a {@code TreeData} object for the specified root node and {@code NodeTracker}. Other parameters are set to
545     * default values.
546     *
547     * @param root the new root node for this model
548     * @param newTracker the new {@code NodeTracker}
549     * @return the new {@code TreeData} object
550     */
551    private TreeData createTreeDataForRootAndTracker(final ImmutableNode root, final NodeTracker newTracker) {
552        return new TreeData(root, createParentMapping(root), Collections.<ImmutableNode, ImmutableNode>emptyMap(), newTracker, new ReferenceTracker());
553    }
554
555    /**
556     * Executes a transaction on the current data of this model. This method is called if an operation is to be executed on
557     * the model's root node or a tracked node which is not yet detached.
558     *
559     * @param txInit the {@code TransactionInitializer}
560     * @param selector an optional {@code NodeSelector} defining the target node
561     * @param currentData the current data of the model
562     * @param resolver the {@code NodeKeyResolver}
563     * @return a flag whether the operation has been completed successfully
564     */
565    private boolean executeTransactionOnCurrentStructure(final TransactionInitializer txInit, final NodeSelector selector, final TreeData currentData,
566        final NodeKeyResolver<ImmutableNode> resolver) {
567        final boolean done;
568        final ModelTransaction tx = new ModelTransaction(currentData, selector, resolver);
569        if (!txInit.initTransaction(tx)) {
570            done = true;
571        } else {
572            final TreeData newData = tx.execute();
573            done = structure.compareAndSet(tx.getCurrentData(), newData);
574        }
575        return done;
576    }
577
578    /**
579     * Tries to execute a transaction on the model of a detached tracked node. This method checks whether the target node of
580     * the transaction is a tracked node and if this node is already detached. If this is the case, the update operation is
581     * independent on this model and has to be executed on the specific model for the detached node.
582     *
583     * @param txInit the {@code TransactionInitializer}
584     * @param selector an optional {@code NodeSelector} defining the target node
585     * @param currentData the current data of the model
586     * @param resolver the {@code NodeKeyResolver} @return a flag whether the transaction could be executed
587     * @throws ConfigurationRuntimeException if the selector cannot be resolved
588     */
589    private boolean executeTransactionOnDetachedTrackedNode(final TransactionInitializer txInit, final NodeSelector selector, final TreeData currentData,
590        final NodeKeyResolver<ImmutableNode> resolver) {
591        if (selector != null) {
592            final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
593            if (detachedNodeModel != null) {
594                detachedNodeModel.updateModel(txInit, null, resolver);
595                return true;
596            }
597        }
598
599        return false;
600    }
601
602    /**
603     * {@inheritDoc} This implementation simply returns the current root node of this model.
604     */
605    @Override
606    public ImmutableNode getInMemoryRepresentation() {
607        return getTreeData().getRootNode();
608    }
609
610    /**
611     * {@inheritDoc} {@code InMemoryNodeModel} implements the {@code NodeHandler} interface itself. So this implementation
612     * just returns the <strong>this</strong> reference.
613     */
614    @Override
615    public NodeHandler<ImmutableNode> getNodeHandler() {
616        return getReferenceNodeHandler();
617    }
618
619    /**
620     * Gets a {@code ReferenceNodeHandler} object for this model. This extended node handler can be used to query
621     * references objects stored for this model.
622     *
623     * @return the {@code ReferenceNodeHandler}
624     */
625    public ReferenceNodeHandler getReferenceNodeHandler() {
626        return getTreeData();
627    }
628
629    /**
630     * Gets the root node of this mode. Note: This method should be used with care. The model may be updated concurrently
631     * which causes the root node to be replaced. If the root node is to be processed further (e.g. by executing queries on
632     * it), the model should be asked for its {@code NodeHandler}, and the root node should be obtained from there. The
633     * connection between a node handler and its root node remain constant because an update of the model causes the whole
634     * node handler to be replaced.
635     *
636     * @return the current root node
637     */
638    public ImmutableNode getRootNode() {
639        return getTreeData().getRootNode();
640    }
641
642    /**
643     * Gets the current {@code ImmutableNode} instance associated with the given {@code NodeSelector}. The node must be a
644     * tracked node, i.e. {@link #trackNode(NodeSelector, NodeKeyResolver)} must have been called before with the given
645     * selector.
646     *
647     * @param selector the {@code NodeSelector} defining the desired node
648     * @return the current {@code ImmutableNode} associated with this selector
649     * @throws ConfigurationRuntimeException if the selector is unknown
650     */
651    public ImmutableNode getTrackedNode(final NodeSelector selector) {
652        return structure.get().getNodeTracker().getTrackedNode(selector);
653    }
654
655    /**
656     * Gets a {@code NodeHandler} for a tracked node. Such a handler may be required for operations on a sub tree of the
657     * model. The handler to be returned depends on the current state of the tracked node. If it is still active, a handler
658     * is used which shares some data (especially the parent mapping) with this model. Detached track nodes in contrast have
659     * their own separate model; in this case a handler associated with this model is returned.
660     *
661     * @param selector the {@code NodeSelector} defining the tracked node
662     * @return a {@code NodeHandler} for this tracked node
663     * @throws ConfigurationRuntimeException if the selector is unknown
664     */
665    public NodeHandler<ImmutableNode> getTrackedNodeHandler(final NodeSelector selector) {
666        final TreeData currentData = structure.get();
667        final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
668        return detachedNodeModel != null ? detachedNodeModel.getNodeHandler()
669            : new TrackedNodeHandler(currentData.getNodeTracker().getTrackedNode(selector), currentData);
670    }
671
672    /**
673     * Gets the current {@code TreeData} object. This object contains all information about the current node structure.
674     *
675     * @return the current {@code TreeData} object
676     */
677    TreeData getTreeData() {
678        return structure.get();
679    }
680
681    /**
682     * Initializes a transaction for an add operation.
683     *
684     * @param tx the transaction to be initialized
685     * @param key the key
686     * @param values the collection with node values
687     * @param resolver the {@code NodeKeyResolver}
688     */
689    private void initializeAddTransaction(final ModelTransaction tx, final String key, final Iterable<?> values,
690        final NodeKeyResolver<ImmutableNode> resolver) {
691        final NodeAddData<ImmutableNode> addData = resolver.resolveAddKey(tx.getQueryRoot(), key, tx.getCurrentData());
692        if (addData.isAttribute()) {
693            addAttributeProperty(tx, addData, values);
694        } else {
695            addNodeProperty(tx, addData, values);
696        }
697    }
698
699    /**
700     * Returns a flag whether the specified tracked node is detached. As long as the {@code NodeSelector} associated with
701     * that node returns a single instance, the tracked node is said to be <em>life</em>. If now an update of the model
702     * happens which invalidates the selector (maybe the target node was removed), the tracked node becomes detached. It is
703     * still possible to query the node; here the latest valid instance is returned. But further changes on the node model
704     * are no longer tracked for this node. So even if there are further changes which would make the {@code NodeSelector}
705     * valid again, the tracked node stays in detached state.
706     *
707     * @param selector the {@code NodeSelector} defining the desired node
708     * @return a flag whether this tracked node is in detached state
709     * @throws ConfigurationRuntimeException if the selector is unknown
710     */
711    public boolean isTrackedNodeDetached(final NodeSelector selector) {
712        return structure.get().getNodeTracker().isTrackedNodeDetached(selector);
713    }
714
715    /**
716     * Merges the root node of this model with the specified node. This method is typically caused by configuration
717     * implementations when a configuration source is loaded, and its data has to be added to the model. It is possible to
718     * define the new name of the root node and to pass in a map with reference objects.
719     *
720     * @param node the node to be merged with the root node
721     * @param rootName the new name of the root node; can be <b>null</b>, then the name of the root node is not changed
722     *        unless it is <b>null</b>
723     * @param references an optional map with reference objects
724     * @param rootRef an optional reference object for the new root node
725     * @param resolver the {@code NodeKeyResolver}
726     */
727    public void mergeRoot(final ImmutableNode node, final String rootName, final Map<ImmutableNode, ?> references, final Object rootRef,
728        final NodeKeyResolver<ImmutableNode> resolver) {
729        updateModel(tx -> {
730            final TreeData current = tx.getCurrentData();
731            final String newRootName = determineRootName(current.getRootNode(), node, rootName);
732            if (newRootName != null) {
733                tx.addChangeNodeNameOperation(current.getRootNode(), newRootName);
734            }
735            tx.addAddNodesOperation(current.getRootNode(), node.getChildren());
736            tx.addAttributesOperation(current.getRootNode(), node.getAttributes());
737            if (node.getValue() != null) {
738                tx.addChangeNodeValueOperation(current.getRootNode(), node.getValue());
739            }
740            if (references != null) {
741                tx.addNewReferences(references);
742            }
743            if (rootRef != null) {
744                tx.addNewReference(current.getRootNode(), rootRef);
745            }
746            return true;
747        }, null, resolver);
748    }
749
750    /**
751     * Replaces an active tracked node. The node then becomes detached.
752     *
753     * @param currentData the current data of the model
754     * @param selector the {@code NodeSelector} defining the tracked node
755     * @param newNode the node replacing the tracked node
756     * @return a flag whether the operation was successful
757     */
758    private boolean replaceActiveTrackedNode(final TreeData currentData, final NodeSelector selector, final ImmutableNode newNode) {
759        final NodeTracker newTracker = currentData.getNodeTracker().replaceAndDetachTrackedNode(selector, newNode);
760        return structure.compareAndSet(currentData, currentData.updateNodeTracker(newTracker));
761    }
762
763    /**
764     * Replaces a tracked node if it is already detached.
765     *
766     * @param currentData the current data of the model
767     * @param selector the {@code NodeSelector} defining the tracked node
768     * @param newNode the node replacing the tracked node
769     * @return a flag whether the operation was successful
770     */
771    private boolean replaceDetachedTrackedNode(final TreeData currentData, final NodeSelector selector, final ImmutableNode newNode) {
772        final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
773        if (detachedNodeModel != null) {
774            detachedNodeModel.setRootNode(newNode);
775            return true;
776        }
777
778        return false;
779    }
780
781    /**
782     * Replaces the root node of this model. This method is similar to {@link #setRootNode(ImmutableNode)}; however, tracked
783     * nodes will not get lost. The model applies the selectors of all tracked nodes on the new nodes hierarchy, so that
784     * corresponding nodes are selected (this may cause nodes to become detached if a select operation fails). This
785     * operation is useful if the new nodes hierarchy to be set is known to be similar to the old one. Note that reference
786     * objects are lost; there is no way to automatically match nodes between the old and the new nodes hierarchy.
787     *
788     * @param newRoot the new root node to be set (must not be <b>null</b>)
789     * @param resolver the {@code NodeKeyResolver}
790     * @throws IllegalArgumentException if the new root node is <b>null</b>
791     */
792    public void replaceRoot(final ImmutableNode newRoot, final NodeKeyResolver<ImmutableNode> resolver) {
793        if (newRoot == null) {
794            throw new IllegalArgumentException("Replaced root node must not be null!");
795        }
796
797        final TreeData current = structure.get();
798        // this step is needed to get a valid NodeHandler
799        final TreeData temp = createTreeDataForRootAndTracker(newRoot, current.getNodeTracker());
800        structure.set(temp.updateNodeTracker(temp.getNodeTracker().update(newRoot, null, resolver, temp)));
801    }
802
803    /**
804     * Replaces a tracked node by another node. If the tracked node is not yet detached, it becomes now detached. The passed
805     * in node (which must not be <b>null</b>) becomes the new root node of an independent model for this tracked node.
806     * Further updates of this model do not affect the tracked node's model and vice versa.
807     *
808     * @param selector the {@code NodeSelector} defining the tracked node
809     * @param newNode the node replacing the tracked node (must not be <b>null</b>)
810     * @throws ConfigurationRuntimeException if the selector cannot be resolved
811     * @throws IllegalArgumentException if the replacement node is <b>null</b>
812     */
813    public void replaceTrackedNode(final NodeSelector selector, final ImmutableNode newNode) {
814        if (newNode == null) {
815            throw new IllegalArgumentException("Replacement node must not be null!");
816        }
817
818        boolean done;
819        do {
820            final TreeData currentData = structure.get();
821            done = replaceDetachedTrackedNode(currentData, selector, newNode) || replaceActiveTrackedNode(currentData, selector, newNode);
822        } while (!done);
823    }
824
825    /**
826     * Allows tracking all nodes selected by a key. This method evaluates the specified key on the current nodes structure.
827     * For all selected nodes corresponding {@code NodeSelector} objects are created, and they are tracked. The returned
828     * collection of {@code NodeSelector} objects can be used for interacting with the selected nodes.
829     *
830     * @param key the key for selecting the nodes to track
831     * @param resolver the {@code NodeKeyResolver}
832     * @return a collection with the {@code NodeSelector} objects for the new tracked nodes
833     */
834    public Collection<NodeSelector> selectAndTrackNodes(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
835        final Mutable<Collection<NodeSelector>> refSelectors = new MutableObject<>();
836        boolean done;
837        do {
838            final TreeData current = structure.get();
839            final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
840            if (nodes.isEmpty()) {
841                return Collections.emptyList();
842            }
843            done = structure.compareAndSet(current, createSelectorsForTrackedNodes(refSelectors, nodes, current, resolver));
844        } while (!done);
845        return refSelectors.getValue();
846    }
847
848    /**
849     * Sets the value of a property using a tracked node as root node. This method works like the normal
850     * {@code setProperty()} method, but the origin of the operation (also for the interpretation of the passed in key) is a
851     * tracked node identified by the passed in {@code NodeSelector}. The selector can be <b>null</b>, then the root node is
852     * assumed.
853     *
854     * @param key the key
855     * @param selector the {@code NodeSelector} defining the root node (or <b>null</b>)
856     * @param value the new value for this property
857     * @param resolver the {@code NodeKeyResolver}
858     * @throws ConfigurationRuntimeException if the selector cannot be resolved
859     */
860    public void setProperty(final String key, final NodeSelector selector, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
861        updateModel(tx -> {
862            boolean added = false;
863            final NodeUpdateData<ImmutableNode> updateData = resolver.resolveUpdateKey(tx.getQueryRoot(), key, value, tx.getCurrentData());
864            if (!updateData.getNewValues().isEmpty()) {
865                initializeAddTransaction(tx, key, updateData.getNewValues(), resolver);
866                added = true;
867            }
868            final boolean cleared = initializeClearTransaction(tx, updateData.getRemovedNodes());
869            final boolean updated = initializeUpdateTransaction(tx, updateData.getChangedValues());
870            return added || cleared || updated;
871        }, selector, resolver);
872    }
873
874    @Override
875    public void setProperty(final String key, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
876        setProperty(key, null, value, resolver);
877    }
878
879    /**
880     * {@inheritDoc} All tracked nodes and reference objects managed by this model are cleared.Care has to be taken when
881     * this method is used and the model is accessed by multiple threads. It is not deterministic which concurrent
882     * operations see the old root and which see the new root node.
883     *
884     * @param newRoot the new root node to be set (can be <b>null</b>, then an empty root node is set)
885     */
886    @Override
887    public void setRootNode(final ImmutableNode newRoot) {
888        structure.set(createTreeData(initialRootNode(newRoot), structure.get()));
889    }
890
891    /**
892     * Tracks all nodes which are children of the node selected by the passed in key. If the key selects exactly one node,
893     * for all children of this node {@code NodeSelector} objects are created, and they become tracked nodes. The returned
894     * collection of {@code NodeSelector} objects can be used for interacting with the selected nodes.
895     *
896     * @param key the key for selecting the parent node whose children are to be tracked
897     * @param resolver the {@code NodeKeyResolver}
898     * @return a collection with the {@code NodeSelector} objects for the new tracked nodes
899     */
900    public Collection<NodeSelector> trackChildNodes(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
901        final Mutable<Collection<NodeSelector>> refSelectors = new MutableObject<>();
902        boolean done;
903        do {
904            refSelectors.setValue(Collections.<NodeSelector>emptyList());
905            final TreeData current = structure.get();
906            final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
907            if (nodes.size() == 1) {
908                final ImmutableNode node = nodes.get(0);
909                done = node.getChildren().isEmpty()
910                    || structure.compareAndSet(current, createSelectorsForTrackedNodes(refSelectors, node.getChildren(), current, resolver));
911            } else {
912                done = true;
913            }
914        } while (!done);
915        return refSelectors.getValue();
916    }
917
918    /**
919     * Tracks a node which is a child of another node selected by the passed in key. If the selected node has a child node
920     * with this name, it is tracked and its selector is returned. Otherwise, a new child node with this name is created
921     * first.
922     *
923     * @param key the key for selecting the parent node
924     * @param childName the name of the child node
925     * @param resolver the {@code NodeKeyResolver}
926     * @return the {@code NodeSelector} for the tracked child node
927     * @throws ConfigurationRuntimeException if the passed in key does not select a single node
928     */
929    public NodeSelector trackChildNodeWithCreation(final String key, final String childName, final NodeKeyResolver<ImmutableNode> resolver) {
930        final MutableObject<NodeSelector> refSelector = new MutableObject<>();
931        boolean done;
932
933        do {
934            final TreeData current = structure.get();
935            final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
936            if (nodes.size() != 1) {
937                throw new ConfigurationRuntimeException("Key does not select a single node: " + key);
938            }
939
940            final ImmutableNode parent = nodes.get(0);
941            final TreeData newData = createDataWithTrackedChildNode(current, parent, childName, resolver, refSelector);
942
943            done = structure.compareAndSet(current, newData);
944        } while (!done);
945
946        return refSelector.getValue();
947    }
948
949    /**
950     * Adds a node to be tracked. After this method has been called with a specific {@code NodeSelector}, the node
951     * associated with this key can be always obtained using {@link #getTrackedNode(NodeSelector)} with the same selector.
952     * This is useful because during updates of a model parts of the structure are replaced. Therefore, it is not a good
953     * idea to simply hold a reference to a node; this might become outdated soon. Rather, the node should be tracked. This
954     * mechanism ensures that always the correct node reference can be obtained.
955     *
956     * @param selector the {@code NodeSelector} defining the desired node
957     * @param resolver the {@code NodeKeyResolver}
958     * @throws ConfigurationRuntimeException if the selector does not select a single node
959     */
960    public void trackNode(final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
961        boolean done;
962        do {
963            final TreeData current = structure.get();
964            final NodeTracker newTracker = current.getNodeTracker().trackNode(current.getRootNode(), selector, resolver, current);
965            done = structure.compareAndSet(current, current.updateNodeTracker(newTracker));
966        } while (!done);
967    }
968
969    /**
970     * Removes a tracked node. This method is the opposite of {@code trackNode()}. It has to be called if there is no longer
971     * the need to track a specific node. Note that for each call of {@code trackNode()} there has to be a corresponding
972     * {@code untrackNode()} call. This ensures that multiple observers can track the same node.
973     *
974     * @param selector the {@code NodeSelector} defining the desired node
975     * @throws ConfigurationRuntimeException if the specified node is not tracked
976     */
977    public void untrackNode(final NodeSelector selector) {
978        boolean done;
979        do {
980            final TreeData current = structure.get();
981            final NodeTracker newTracker = current.getNodeTracker().untrackNode(selector);
982            done = structure.compareAndSet(current, current.updateNodeTracker(newTracker));
983        } while (!done);
984    }
985
986    /**
987     * Performs a non-blocking, thread-safe update of this model based on a transaction initialized by the passed in
988     * initializer. This method uses the atomic reference for the model's current data to ensure that an update was
989     * successful even if the model is concurrently accessed.
990     *
991     * @param txInit the {@code TransactionInitializer}
992     * @param selector an optional {@code NodeSelector} defining the target node of the transaction
993     * @param resolver the {@code NodeKeyResolver}
994     */
995    private void updateModel(final TransactionInitializer txInit, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
996        boolean done;
997
998        do {
999            final TreeData currentData = getTreeData();
1000            done = executeTransactionOnDetachedTrackedNode(txInit, selector, currentData, resolver)
1001                || executeTransactionOnCurrentStructure(txInit, selector, currentData, resolver);
1002        } while (!done);
1003    }
1004}