    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
    036 * {@link ImmutableNode} structures.
    037 * </p>
    038 * <p>
    039 * This {@code NodeModel} implementation keeps all its data as a tree of
    040 * {@link ImmutableNode} objects in memory. The managed structure can be
    041 * manipulated in a thread-safe, non-blocking way. This is achieved by using
    042 * atomic variables: The root of the tree is stored in an atomic reference
    043 * variable. Each update operation causes a new structure to be constructed
    044 * (which reuses as much from the original structure as possible). The old root
    045 * node is then replaced by the new one using an atomic compare-and-set
    046 * operation. If this fails, the manipulation has to be done anew on the updated
    047 * structure.
    048 * </p>
    049 *
    050 * @since 2.0
    051 */
    052public class InMemoryNodeModel implements NodeModel<ImmutableNode>
    053{
    054    /**
    055     * A dummy node handler instance used in operations which require only a
    056     * limited functionality.
    057     */
    058    private static final NodeHandler<ImmutableNode> DUMMY_HANDLER =
    059            new TreeData(null,
    060                    Collections.<ImmutableNode, ImmutableNode> emptyMap(),
    061                    Collections.<ImmutableNode, ImmutableNode> emptyMap(), null, new ReferenceTracker());
    062
    063    /** Stores information about the current nodes structure. */
    064    private final AtomicReference<TreeData> structure;
    065
    066    /**
    067     * Creates a new instance of {@code InMemoryNodeModel} which is initialized
    068     * with an empty root node.
    069     */
    070    public InMemoryNodeModel()
    071    {
    072        this(null);
    073    }
    074
    075    /**
    076     * Creates a new instance of {@code InMemoryNodeModel} and initializes it
    077     * from the given root node. If the passed in node is <b>null</b>, a new,
    078     * empty root node is created.
    079     *
    080     * @param root the new root node for this model
    081     */
    082    public InMemoryNodeModel(final ImmutableNode root)
    083    {
    084        structure =
    085                new AtomicReference<>(
    086                        createTreeData(initialRootNode(root), null));
    087    }
    088
    089    /**
    090     * Returns the root node of this mode. Note: This method should be used with
    091     * care. The model may be updated concurrently which causes the root node to
    092     * be replaced. If the root node is to be processed further (e.g. by
    093     * executing queries on it), the model should be asked for its
    094     * {@code NodeHandler}, and the root node should be obtained from there. The
    095     * connection between a node handler and its root node remain constant
    096     * because an update of the model causes the whole node handler to be
    097     * replaced.
    098     *
    099     * @return the current root node
    100     */
    101    public ImmutableNode getRootNode()
    102    {
    103        return getTreeData().getRootNode();
    104    }
    105
    106    /**
    107     * {@inheritDoc} {@code InMemoryNodeModel} implements the
    108     * {@code NodeHandler} interface itself. So this implementation just returns
    109     * the <strong>this</strong> reference.
    110     */
    111    @Override
    112    public NodeHandler<ImmutableNode> getNodeHandler()
    113    {
    114        return getReferenceNodeHandler();
    115    }
    116
    117    @Override
    118    public void addProperty(final String key, final Iterable<?> values,
    119            final NodeKeyResolver<ImmutableNode> resolver)
    120    {
    121        addProperty(key, null, values, resolver);
    122    }
    123
    124    /**
    125     * Adds new property values using a tracked node as root node. This method
    126     * works like the normal {@code addProperty()} method, but the origin of the
    127     * operation (also for the interpretation of the passed in key) is a tracked
    128     * node identified by the passed in {@code NodeSelector}. The selector can
    129     * be <b>null</b>, then the root node is assumed.
    130     *
    131     * @param key the key
    132     * @param selector the {@code NodeSelector} defining the root node (or
    133     *        <b>null</b>)
    134     * @param values the values to be added
    135     * @param resolver the {@code NodeKeyResolver}
    136     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    137     */
    138    public void addProperty(final String key, final NodeSelector selector,
    139            final Iterable<?> values,
    140            final NodeKeyResolver<ImmutableNode> resolver)
    141    {
    142        if (valuesNotEmpty(values))
    143        {
    144            updateModel(tx -> {
    145                initializeAddTransaction(tx, key, values, resolver);
    146                return true;
    147            }, selector, resolver);
    148        }
    149    }
    150
    151    @Override
    152    public void addNodes(final String key, final Collection<? extends ImmutableNode> nodes,
    153            final NodeKeyResolver<ImmutableNode> resolver)
    154    {
    155        addNodes(key, null, nodes, resolver);
    156    }
    157
    158    /**
    159     * Adds new nodes using a tracked node as root node. This method works like
    160     * the normal {@code addNodes()} method, but the origin of the operation
    161     * (also for the interpretation of the passed in key) is a tracked node
    162     * identified by the passed in {@code NodeSelector}. The selector can be
    163     * <b>null</b>, then the root node is assumed.
    164     *
    165     * @param key the key
    166     * @param selector the {@code NodeSelector} defining the root node (or
    167     *        <b>null</b>)
    168     * @param nodes the collection of new nodes to be added
    169     * @param resolver the {@code NodeKeyResolver}
    170     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    171     */
    172    public void addNodes(final String key, final NodeSelector selector,
    173            final Collection<? extends ImmutableNode> nodes,
    174            final NodeKeyResolver<ImmutableNode> resolver)
    175    {
    176        if (nodes != null && !nodes.isEmpty())
    177        {
    178            updateModel(tx -> {
    179                final List<QueryResult<ImmutableNode>> results =
    180                        resolver.resolveKey(tx.getQueryRoot(), key,
    181                                tx.getCurrentData());
    182                if (results.size() == 1)
    183                {
    184                    if (results.get(0).isAttributeResult())
    185                    {
    186                        throw attributeKeyException(key);
    187                    }
    188                    tx.addAddNodesOperation(results.get(0).getNode(), nodes);
    189                }
    190                else
    191                {
    192                    final NodeAddData<ImmutableNode> addData =
    193                            resolver.resolveAddKey(tx.getQueryRoot(), key,
    194                                    tx.getCurrentData());
    195                    if (addData.isAttribute())
    196                    {
    197                        throw attributeKeyException(key);
    198                    }
    199                    final ImmutableNode newNode =
    200                            new ImmutableNode.Builder(nodes.size())
    201                                    .name(addData.getNewNodeName())
    202                                    .addChildren(nodes).create();
    203                    addNodesByAddData(tx, addData,
    204                            Collections.singleton(newNode));
    205                }
    206                return true;
    207            }, selector, resolver);
    208        }
    209    }
    210
    211    @Override
    212    public void setProperty(final String key, final Object value,
    213            final NodeKeyResolver<ImmutableNode> resolver)
    214    {
    215        setProperty(key, null, value, resolver);
    216    }
    217
    218    /**
    219     * Sets the value of a property using a tracked node as root node. This
    220     * method works like the normal {@code setProperty()} method, but the origin
    221     * of the operation (also for the interpretation of the passed in key) is a
    222     * tracked node identified by the passed in {@code NodeSelector}. The
    223     * selector can be <b>null</b>, then the root node is assumed.
    224     *
    225     * @param key the key
    226     * @param selector the {@code NodeSelector} defining the root node (or
    227     *        <b>null</b>)
    228     * @param value the new value for this property
    229     * @param resolver the {@code NodeKeyResolver}
    230     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    231     */
    232    public void setProperty(final String key, final NodeSelector selector,
    233            final Object value, final NodeKeyResolver<ImmutableNode> resolver)
    234    {
    235        updateModel(tx -> {
    236            boolean added = false;
    237            final NodeUpdateData<ImmutableNode> updateData =
    238                    resolver.resolveUpdateKey(tx.getQueryRoot(), key,
    239                            value, tx.getCurrentData());
    240            if (!updateData.getNewValues().isEmpty())
    241            {
    242                initializeAddTransaction(tx, key,
    243                        updateData.getNewValues(), resolver);
    244                added = true;
    245            }
    246            final boolean cleared =
    247                    initializeClearTransaction(tx,
    248                            updateData.getRemovedNodes());
    249            final boolean updated =
    250                    initializeUpdateTransaction(tx,
    251                            updateData.getChangedValues());
    252            return added || cleared || updated;
    253        }, selector, resolver);
    254    }
    255
    256    /**
    257     * {@inheritDoc} This implementation checks whether nodes become undefined
    258     * after subtrees have been removed. If this is the case, such nodes are
    259     * removed, too. Return value is a collection with {@code QueryResult}
    260     * objects for the elements to be removed from the model.
    261     */
    262    @Override
    263    public List<QueryResult<ImmutableNode>> clearTree(final String key,
    264            final NodeKeyResolver<ImmutableNode> resolver)
    265    {
    266        return clearTree(key, null, resolver);
    267    }
    268
    269    /**
    270     * Clears a whole sub tree using a tracked node as root node. This method
    271     * works like the normal {@code clearTree()} method, but the origin of the
    272     * operation (also for the interpretation of the passed in key) is a tracked
    273     * node identified by the passed in {@code NodeSelector}. The selector can
    274     * be <b>null</b>, then the root node is assumed.
    275     *
    276     * @param key the key
    277     * @param selector the {@code NodeSelector} defining the root node (or
    278     *        <b>null</b>)
    279     * @param resolver the {@code NodeKeyResolver}
    280     * @return a list with the results to be removed
    281     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    282     */
    283    public List<QueryResult<ImmutableNode>> clearTree(final String key,
    284            final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver)
    285    {
    286        final List<QueryResult<ImmutableNode>> removedElements =
    287                new LinkedList<>();
    288        updateModel(tx -> {
    289            boolean changes = false;
    290            final TreeData currentStructure = tx.getCurrentData();
    291            final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(
    292                    tx.getQueryRoot(), key, currentStructure);
    293            removedElements.clear();
    294            removedElements.addAll(results);
    295            for (final QueryResult<ImmutableNode> result : results)
    296            {
    297                if (result.isAttributeResult())
    298                {
    299                    tx.addRemoveAttributeOperation(result.getNode(),
    300                            result.getAttributeName());
    301                }
    302                else
    303                {
    304                    if (result.getNode() == currentStructure.getRootNode())
    305                    {
    306                        // the whole model is to be cleared
    307                        clear(resolver);
    308                        return false;
    309                    }
    310                    tx.addRemoveNodeOperation(
    311                            currentStructure.getParent(result.getNode()),
    312                            result.getNode());
    313                }
    314                changes = true;
    315            }
    316            return changes;
    317        }, selector, resolver);
    318
    319        return removedElements;
    320    }
    321
    322    /**
    323     * {@inheritDoc} If this operation leaves an affected node in an undefined
    324     * state, it is removed from the model.
    325     */
    326    @Override
    327    public void clearProperty(final String key,
    328            final NodeKeyResolver<ImmutableNode> resolver)
    329    {
    330        clearProperty(key, null, resolver);
    331    }
    332
    333    /**
    334     * Clears a property using a tracked node as root node. This method works
    335     * like the normal {@code clearProperty()} method, but the origin of the
    336     * operation (also for the interpretation of the passed in key) is a tracked
    337     * node identified by the passed in {@code NodeSelector}. The selector can
    338     * be <b>null</b>, then the root node is assumed.
    339     *
    340     * @param key the key
    341     * @param selector the {@code NodeSelector} defining the root node (or
    342     *        <b>null</b>)
    343     * @param resolver the {@code NodeKeyResolver}
    344     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    345     */
    346    public void clearProperty(final String key, final NodeSelector selector,
    347            final NodeKeyResolver<ImmutableNode> resolver)
    348    {
    349        updateModel(tx -> {
    350            final List<QueryResult<ImmutableNode>> results =
    351                    resolver.resolveKey(tx.getQueryRoot(), key,
    352                            tx.getCurrentData());
    353            return initializeClearTransaction(tx, results);
    354        }, selector, resolver);
    355    }
    356
    357    /**
    358     * {@inheritDoc} A new empty root node is created with the same name as the
    359     * current root node. Implementation note: Because this is a hard reset the
    360     * usual dance for dealing with concurrent updates is not required here.
    361     *
    362     * @param resolver the {@code NodeKeyResolver}
    363     */
    364    @Override
    365    public void clear(final NodeKeyResolver<ImmutableNode> resolver)
    366    {
    367        final ImmutableNode newRoot =
    368                new ImmutableNode.Builder().name(getRootNode().getNodeName())
    369                        .create();
    370        setRootNode(newRoot);
    371    }
    372
    373    /**
    374     * {@inheritDoc} This implementation simply returns the current root node of this
    375     * model.
    376     */
    377    @Override
    378    public ImmutableNode getInMemoryRepresentation()
    379    {
    380        return getTreeData().getRootNode();
    381    }
    382
    383    /**
    384     * {@inheritDoc} All tracked nodes and reference objects managed by this
    385     * model are cleared.Care has to be taken when this method is used and the
    386     * model is accessed by multiple threads. It is not deterministic which
    387     * concurrent operations see the old root and which see the new root node.
    388     *
    389     * @param newRoot the new root node to be set (can be <b>null</b>, then an
    390     *        empty root node is set)
    391     */
    392    @Override
    393    public void setRootNode(final ImmutableNode newRoot)
    394    {
    395        structure.set(createTreeData(initialRootNode(newRoot), structure.get()));
    396    }
    397
    398    /**
    399     * Replaces the root node of this model. This method is similar to
    400     * {@link #setRootNode(ImmutableNode)}; however, tracked nodes will not get
    401     * lost. The model applies the selectors of all tracked nodes on the new
    402     * nodes hierarchy, so that corresponding nodes are selected (this may cause
    403     * nodes to become detached if a select operation fails). This operation is
    404     * useful if the new nodes hierarchy to be set is known to be similar to the
    405     * old one. Note that reference objects are lost; there is no way to
    406     * automatically match nodes between the old and the new nodes hierarchy.
    407     *
    408     * @param newRoot the new root node to be set (must not be <b>null</b>)
    409     * @param resolver the {@code NodeKeyResolver}
    410     * @throws IllegalArgumentException if the new root node is <b>null</b>
    411     */
    412    public void replaceRoot(final ImmutableNode newRoot,
    413            final NodeKeyResolver<ImmutableNode> resolver)
    414    {
    415        if (newRoot == null)
    416        {
    417            throw new IllegalArgumentException(
    418                    "Replaced root node must not be null!");
    419        }
    420
    421        final TreeData current = structure.get();
    422        // this step is needed to get a valid NodeHandler
    423        final TreeData temp =
    424                createTreeDataForRootAndTracker(newRoot,
    425                        current.getNodeTracker());
    426        structure.set(temp.updateNodeTracker(temp.getNodeTracker().update(
    427                newRoot, null, resolver, temp)));
    428    }
    429
    430    /**
    431     * Merges the root node of this model with the specified node. This method
    432     * is typically caused by configuration implementations when a configuration
    433     * source is loaded, and its data has to be added to the model. It is
    434     * possible to define the new name of the root node and to pass in a map
    435     * with reference objects.
    436     *
    437     * @param node the node to be merged with the root node
    438     * @param rootName the new name of the root node; can be <b>null</b>, then
    439     *        the name of the root node is not changed unless it is <b>null</b>
    440     * @param references an optional map with reference objects
    441     * @param rootRef an optional reference object for the new root node
    442     * @param resolver the {@code NodeKeyResolver}
    443     */
    444    public void mergeRoot(final ImmutableNode node, final String rootName,
    445            final Map<ImmutableNode, ?> references, final Object rootRef,
    446            final NodeKeyResolver<ImmutableNode> resolver)
    447    {
    448        updateModel(tx -> {
    449            final TreeData current = tx.getCurrentData();
    450            final String newRootName =
    451                    determineRootName(current.getRootNode(), node, rootName);
    452            if (newRootName != null)
    453            {
    454                tx.addChangeNodeNameOperation(current.getRootNode(),
    455                        newRootName);
    456            }
    457            tx.addAddNodesOperation(current.getRootNode(),
    458                    node.getChildren());
    459            tx.addAttributesOperation(current.getRootNode(),
    460                    node.getAttributes());
    461            if (node.getValue() != null)
    462            {
    463                tx.addChangeNodeValueOperation(current.getRootNode(),
    464                        node.getValue());
    465            }
    466            if (references != null)
    467            {
    468                tx.addNewReferences(references);
    469            }
    470            if (rootRef != null)
    471            {
    472                tx.addNewReference(current.getRootNode(), rootRef);
    473            }
    474            return true;
    475        }, null, resolver);
    476    }
    477
    478    /**
    479     * Adds a node to be tracked. After this method has been called with a
    480     * specific {@code NodeSelector}, the node associated with this key can be
    481     * always obtained using {@link #getTrackedNode(NodeSelector)} with the same
    482     * selector. This is useful because during updates of a model parts of the
    483     * structure are replaced. Therefore, it is not a good idea to simply hold a
    484     * reference to a node; this might become outdated soon. Rather, the node
    485     * should be tracked. This mechanism ensures that always the correct node
    486     * reference can be obtained.
    487     *
    488     * @param selector the {@code NodeSelector} defining the desired node
    489     * @param resolver the {@code NodeKeyResolver}
    490     * @throws ConfigurationRuntimeException if the selector does not select a
    491     *         single node
    492     */
    493    public void trackNode(final NodeSelector selector,
    494            final NodeKeyResolver<ImmutableNode> resolver)
    495    {
    496        boolean done;
    497        do
    498        {
    499            final TreeData current = structure.get();
    500            final NodeTracker newTracker =
    501                    current.getNodeTracker().trackNode(current.getRootNode(),
    502                            selector, resolver, current);
    503            done =
    504                    structure.compareAndSet(current,
    505                            current.updateNodeTracker(newTracker));
    506        } while (!done);
    507    }
    508
    509    /**
    510     * Allows tracking all nodes selected by a key. This method evaluates the
    511     * specified key on the current nodes structure. For all selected nodes
    512     * corresponding {@code NodeSelector} objects are created, and they are
    513     * tracked. The returned collection of {@code NodeSelector} objects can be
    514     * used for interacting with the selected nodes.
    515     *
    516     * @param key the key for selecting the nodes to track
    517     * @param resolver the {@code NodeKeyResolver}
    518     * @return a collection with the {@code NodeSelector} objects for the new
    519     *         tracked nodes
    520     */
    521    public Collection<NodeSelector> selectAndTrackNodes(final String key,
    522            final NodeKeyResolver<ImmutableNode> resolver)
    523    {
    524        final Mutable<Collection<NodeSelector>> refSelectors =
    525                new MutableObject<>();
    526        boolean done;
    527        do
    528        {
    529            final TreeData current = structure.get();
    530            final List<ImmutableNode> nodes =
    531                    resolver.resolveNodeKey(current.getRootNode(), key, current);
    532            if (nodes.isEmpty())
    533            {
    534                return Collections.emptyList();
    535            }
    536            done =
    537                    structure.compareAndSet(
    538                            current,
    539                            createSelectorsForTrackedNodes(refSelectors, nodes,
    540                                    current, resolver));
    541        } while (!done);
    542        return refSelectors.getValue();
    543    }
    544
    545    /**
    546     * Tracks all nodes which are children of the node selected by the passed in
    547     * key. If the key selects exactly one node, for all children of this node
    548     * {@code NodeSelector} objects are created, and they become tracked nodes.
    549     * The returned collection of {@code NodeSelector} objects can be used for
    550     * interacting with the selected nodes.
    551     *
    552     * @param key the key for selecting the parent node whose children are to be
    553     *        tracked
    554     * @param resolver the {@code NodeKeyResolver}
    555     * @return a collection with the {@code NodeSelector} objects for the new
    556     *         tracked nodes
    557     */
    558    public Collection<NodeSelector> trackChildNodes(final String key,
    559            final NodeKeyResolver<ImmutableNode> resolver)
    560    {
    561        final Mutable<Collection<NodeSelector>> refSelectors =
    562                new MutableObject<>();
    563        boolean done;
    564        do
    565        {
    566            refSelectors.setValue(Collections.<NodeSelector> emptyList());
    567            final TreeData current = structure.get();
    568            final List<ImmutableNode> nodes =
    569                    resolver.resolveNodeKey(current.getRootNode(), key, current);
    570            if (nodes.size() == 1)
    571            {
    572                final ImmutableNode node = nodes.get(0);
    573                done =
    574                        node.getChildren().isEmpty()
    575                                || structure.compareAndSet(
    576                                        current,
    577                                        createSelectorsForTrackedNodes(
    578                                                refSelectors,
    579                                                node.getChildren(), current,
    580                                                resolver));
    581            }
    582            else
    583            {
    584                done = true;
    585            }
    586        } while (!done);
    587        return refSelectors.getValue();
    588    }
    589
    590    /**
    591     * Tracks a node which is a child of another node selected by the passed in
    592     * key. If the selected node has a child node with this name, it is tracked
    593     * and its selector is returned. Otherwise, a new child node with this name
    594     * is created first.
    595     *
    596     * @param key the key for selecting the parent node
    597     * @param childName the name of the child node
    598     * @param resolver the {@code NodeKeyResolver}
    599     * @return the {@code NodeSelector} for the tracked child node
    600     * @throws ConfigurationRuntimeException if the passed in key does not
    601     *         select a single node
    602     */
    603    public NodeSelector trackChildNodeWithCreation(final String key,
    604            final String childName, final NodeKeyResolver<ImmutableNode> resolver)
    605    {
    606        final MutableObject<NodeSelector> refSelector =
    607                new MutableObject<>();
    608        boolean done;
    609
    610        do
    611        {
    612            final TreeData current = structure.get();
    613            final List<ImmutableNode> nodes =
    614                    resolver.resolveNodeKey(current.getRootNode(), key, current);
    615            if (nodes.size() != 1)
    616            {
    617                throw new ConfigurationRuntimeException(
    618                        "Key does not select a single node: " + key);
    619            }
    620
    621            final ImmutableNode parent = nodes.get(0);
    622            final TreeData newData =
    623                    createDataWithTrackedChildNode(current, parent, childName,
    624                            resolver, refSelector);
    625
    626            done = structure.compareAndSet(current, newData);
    627        } while (!done);
    628
    629        return refSelector.getValue();
    630    }
    631
    632    /**
    633     * Returns the current {@code ImmutableNode} instance associated with the
    634     * given {@code NodeSelector}. The node must be a tracked node, i.e.
    635     * {@link #trackNode(NodeSelector, NodeKeyResolver)} must have been called
    636     * before with the given selector.
    637     *
    638     * @param selector the {@code NodeSelector} defining the desired node
    639     * @return the current {@code ImmutableNode} associated with this selector
    640     * @throws ConfigurationRuntimeException if the selector is unknown
    641     */
    642    public ImmutableNode getTrackedNode(final NodeSelector selector)
    643    {
    644        return structure.get().getNodeTracker().getTrackedNode(selector);
    645    }
    646
    647    /**
    648     * Replaces a tracked node by another node. If the tracked node is not yet
    649     * detached, it becomes now detached. The passed in node (which must not be
    650     * <b>null</b>) becomes the new root node of an independent model for this
    651     * tracked node. Further updates of this model do not affect the tracked
    652     * node's model and vice versa.
    653     *
    654     * @param selector the {@code NodeSelector} defining the tracked node
    655     * @param newNode the node replacing the tracked node (must not be
    656     *        <b>null</b>)
    657     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    658     * @throws IllegalArgumentException if the replacement node is <b>null</b>
    659     */
    660    public void replaceTrackedNode(final NodeSelector selector, final ImmutableNode newNode)
    661    {
    662        if (newNode == null)
    663        {
    664            throw new IllegalArgumentException(
    665                    "Replacement node must not be null!");
    666        }
    667
    668        boolean done;
    669        do
    670        {
    671            final TreeData currentData = structure.get();
    672            done =
    673                    replaceDetachedTrackedNode(currentData, selector, newNode)
    674                            || replaceActiveTrackedNode(currentData, selector,
    675                                    newNode);
    676        } while (!done);
    677    }
    678
    679    /**
    680     * Returns a {@code NodeHandler} for a tracked node. Such a handler may be
    681     * required for operations on a sub tree of the model. The handler to be
    682     * returned depends on the current state of the tracked node. If it is still
    683     * active, a handler is used which shares some data (especially the parent
    684     * mapping) with this model. Detached track nodes in contrast have their own
    685     * separate model; in this case a handler associated with this model is
    686     * returned.
    687     *
    688     * @param selector the {@code NodeSelector} defining the tracked node
    689     * @return a {@code NodeHandler} for this tracked node
    690     * @throws ConfigurationRuntimeException if the selector is unknown
    691     */
    692    public NodeHandler<ImmutableNode> getTrackedNodeHandler(
    693            final NodeSelector selector)
    694    {
    695        final TreeData currentData = structure.get();
    696        final InMemoryNodeModel detachedNodeModel =
    697                currentData.getNodeTracker().getDetachedNodeModel(selector);
    698        return detachedNodeModel != null ? detachedNodeModel.getNodeHandler()
    699                : new TrackedNodeHandler(currentData.getNodeTracker()
    700                        .getTrackedNode(selector), currentData);
    701    }
    702
    703    /**
    704     * Returns a flag whether the specified tracked node is detached. As long as
    705     * the {@code NodeSelector} associated with that node returns a single
    706     * instance, the tracked node is said to be <em>life</em>. If now an update
    707     * of the model happens which invalidates the selector (maybe the target
    708     * node was removed), the tracked node becomes detached. It is still
    709     * possible to query the node; here the latest valid instance is returned.
    710     * But further changes on the node model are no longer tracked for this
    711     * node. So even if there are further changes which would make the
    712     * {@code NodeSelector} valid again, the tracked node stays in detached
    713     * state.
    714     *
    715     * @param selector the {@code NodeSelector} defining the desired node
    716     * @return a flag whether this tracked node is in detached state
    717     * @throws ConfigurationRuntimeException if the selector is unknown
    718     */
    719    public boolean isTrackedNodeDetached(final NodeSelector selector)
    720    {
    721        return structure.get().getNodeTracker().isTrackedNodeDetached(selector);
    722    }
    723
    724    /**
    725     * Removes a tracked node. This method is the opposite of
    726     * {@code trackNode()}. It has to be called if there is no longer the need
    727     * to track a specific node. Note that for each call of {@code trackNode()}
    728     * there has to be a corresponding {@code untrackNode()} call. This ensures
    729     * that multiple observers can track the same node.
    730     *
    731     * @param selector the {@code NodeSelector} defining the desired node
    732     * @throws ConfigurationRuntimeException if the specified node is not
    733     *         tracked
    734     */
    735    public void untrackNode(final NodeSelector selector)
    736    {
    737        boolean done;
    738        do
    739        {
    740            final TreeData current = structure.get();
    741            final NodeTracker newTracker =
    742                    current.getNodeTracker().untrackNode(selector);
    743            done =
    744                    structure.compareAndSet(current,
    745                            current.updateNodeTracker(newTracker));
    746        } while (!done);
    747    }
    748
    749    /**
    750     * Returns a {@code ReferenceNodeHandler} object for this model. This
    751     * extended node handler can be used to query references objects stored for
    752     * this model.
    753     *
    754     * @return the {@code ReferenceNodeHandler}
    755     */
    756    public ReferenceNodeHandler getReferenceNodeHandler()
    757    {
    758        return getTreeData();
    759    }
    760
    761    /**
    762     * Returns the current {@code TreeData} object. This object contains all
    763     * information about the current node structure.
    764     *
    765     * @return the current {@code TreeData} object
    766     */
    767    TreeData getTreeData()
    768    {
    769        return structure.get();
    770    }
    771
    772    /**
    773     * Updates the mapping from nodes to their parents for the passed in
    774     * hierarchy of nodes. This method traverses all children and grand-children
    775     * of the passed in root node. For each node in the subtree the parent
    776     * relation is added to the map.
    777     *
    778     * @param parents the map with parent nodes
    779     * @param root the root node of the current tree
    780     */
    781    static void updateParentMapping(final Map<ImmutableNode, ImmutableNode> parents,
    782            final ImmutableNode root)
    783    {
    784        NodeTreeWalker.INSTANCE.walkBFS(root,
    785                new ConfigurationNodeVisitorAdapter<ImmutableNode>()
    786                {
    787                    @Override
    788                    public void visitBeforeChildren(final ImmutableNode node,
    789                            final NodeHandler<ImmutableNode> handler)
    790                    {
    791                        for (final ImmutableNode c : node.getChildren())
    792                        {
    793                            parents.put(c, node);
    794                        }
    795                    }
    796                }, DUMMY_HANDLER);
    797    }
    798
    799    /**
    800     * Checks if the passed in node is defined. Result is <b>true</b> if the
    801     * node contains any data.
    802     *
    803     * @param node the node in question
    804     * @return <b>true</b> if the node is defined, <b>false</b> otherwise
    805     */
    806    static boolean checkIfNodeDefined(final ImmutableNode node)
    807    {
    808        return node.getValue() != null || !node.getChildren().isEmpty()
    809                || !node.getAttributes().isEmpty();
    810    }
    811
    812    /**
    813     * Initializes a transaction for an add operation.
    814     *
    815     * @param tx the transaction to be initialized
    816     * @param key the key
    817     * @param values the collection with node values
    818     * @param resolver the {@code NodeKeyResolver}
    819     */
    820    private void initializeAddTransaction(final ModelTransaction tx, final String key,
    821            final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver)
    822    {
    823        final NodeAddData<ImmutableNode> addData =
    824                resolver.resolveAddKey(tx.getQueryRoot(), key,
    825                        tx.getCurrentData());
    826        if (addData.isAttribute())
    827        {
    828            addAttributeProperty(tx, addData, values);
    829        }
    830        else
    831        {
    832            addNodeProperty(tx, addData, values);
    833        }
    834    }
    835
    836    /**
    837     * Creates a {@code TreeData} object for the specified root node.
    838     *
    839     * @param root the root node of the current tree
    840     * @param current the current {@code TreeData} object (may be <b>null</b>)
    841     * @return the {@code TreeData} describing the current tree
    842     */
    843    private TreeData createTreeData(final ImmutableNode root, final TreeData current)
    844    {
    845        final NodeTracker newTracker =
    846                current != null ? current.getNodeTracker()
    847                        .detachAllTrackedNodes() : new NodeTracker();
    848        return createTreeDataForRootAndTracker(root, newTracker);
    849    }
    850
    851    /**
    852     * Creates a {@code TreeData} object for the specified root node and
    853     * {@code NodeTracker}. Other parameters are set to default values.
    854     *
    855     * @param root the new root node for this model
    856     * @param newTracker the new {@code NodeTracker}
    857     * @return the new {@code TreeData} object
    858     */
    859    private TreeData createTreeDataForRootAndTracker(final ImmutableNode root,
    860            final NodeTracker newTracker)
    861    {
    862        return new TreeData(root, createParentMapping(root),
    863                Collections.<ImmutableNode, ImmutableNode> emptyMap(),
    864                newTracker, new ReferenceTracker());
    865    }
    866
    867    /**
    868     * Handles an add property operation if the property to be added is a node.
    869     *
    870     * @param tx the transaction
    871     * @param addData the {@code NodeAddData}
    872     * @param values the collection with node values
    873     */
    874    private static void addNodeProperty(final ModelTransaction tx,
    875            final NodeAddData<ImmutableNode> addData, final Iterable<?> values)
    876    {
    877        final Collection<ImmutableNode> newNodes =
    878                createNodesToAdd(addData.getNewNodeName(), values);
    879        addNodesByAddData(tx, addData, newNodes);
    880    }
    881
    882    /**
    883     * Initializes a transaction to add a collection of nodes as described by a
    884     * {@code NodeAddData} object. If necessary, new path nodes are created.
    885     * Eventually, the new nodes are added as children to the specified target
    886     * node.
    887     *
    888     * @param tx the transaction
    889     * @param addData the {@code NodeAddData}
    890     * @param newNodes the collection of new child nodes
    891     */
    892    private static void addNodesByAddData(final ModelTransaction tx,
    893            final NodeAddData<ImmutableNode> addData,
    894            final Collection<ImmutableNode> newNodes)
    895    {
    896        if (addData.getPathNodes().isEmpty())
    897        {
    898            tx.addAddNodesOperation(addData.getParent(), newNodes);
    899        }
    900        else
    901        {
    902            final ImmutableNode newChild = createNodeToAddWithPath(addData, newNodes);
    903            tx.addAddNodeOperation(addData.getParent(), newChild);
    904        }
    905    }
    906
    907    /**
    908     * Handles an add property operation if the property to be added is an
    909     * attribute.
    910     *
    911     * @param tx the transaction
    912     * @param addData the {@code NodeAddData}
    913     * @param values the collection with node values
    914     */
    915    private static void addAttributeProperty(final ModelTransaction tx,
    916            final NodeAddData<ImmutableNode> addData, final Iterable<?> values)
    917    {
    918        if (addData.getPathNodes().isEmpty())
    919        {
    920            tx.addAttributeOperation(addData.getParent(),
    921                    addData.getNewNodeName(), values.iterator().next());
    922        }
    923        else
    924        {
    925            final int pathNodeCount = addData.getPathNodes().size();
    926            final ImmutableNode childWithAttribute =
    927                    new ImmutableNode.Builder()
    928                            .name(addData.getPathNodes().get(pathNodeCount - 1))
    929                            .addAttribute(addData.getNewNodeName(),
    930                                    values.iterator().next()).create();
    931            final ImmutableNode newChild =
    932                    pathNodeCount > 1 ? createNodeOnPath(addData
    933                            .getPathNodes().subList(0, pathNodeCount - 1)
    934                            .iterator(),
    935                            Collections.singleton(childWithAttribute))
    936                            : childWithAttribute;
    937            tx.addAddNodeOperation(addData.getParent(), newChild);
    938        }
    939    }
    940
    941    /**
    942     * Creates a collection with new nodes with a given name and a value from a
    943     * given collection.
    944     *
    945     * @param newNodeName the name of the new nodes
    946     * @param values the collection with node values
    947     * @return the newly created collection
    948     */
    949    private static Collection<ImmutableNode> createNodesToAdd(
    950            final String newNodeName, final Iterable<?> values)
    951    {
    952        final Collection<ImmutableNode> nodes = new LinkedList<>();
    953        for (final Object value : values)
    954        {
    955            nodes.add(new ImmutableNode.Builder().name(newNodeName)
    956                    .value(value).create());
    957        }
    958        return nodes;
    959    }
    960
    961    /**
    962     * Creates a node structure consisting of the path nodes defined by the
    963     * passed in {@code NodeAddData} instance and all new child nodes.
    964     *
    965     * @param addData the {@code NodeAddData}
    966     * @param newNodes the collection of new child nodes
    967     * @return the parent node of the newly created hierarchy
    968     */
    969    private static ImmutableNode createNodeToAddWithPath(
    970            final NodeAddData<ImmutableNode> addData,
    971            final Collection<ImmutableNode> newNodes)
    972    {
    973        return createNodeOnPath(addData.getPathNodes().iterator(), newNodes);
    974    }
    975
    976    /**
    977     * Recursive helper method for creating a path node for an add operation.
    978     * All path nodes except for the last have a single child. The last path
    979     * node has the new nodes as children.
    980     *
    981     * @param it the iterator over the names of the path nodes
    982     * @param newNodes the collection of new child nodes
    983     * @return the newly created path node
    984     */
    985    private static ImmutableNode createNodeOnPath(final Iterator<String> it,
    986            final Collection<ImmutableNode> newNodes)
    987    {
    988        final String nodeName = it.next();
    989        ImmutableNode.Builder builder;
    990        if (it.hasNext())
    991        {
    992            builder = new ImmutableNode.Builder(1);
    993            builder.addChild(createNodeOnPath(it, newNodes));
    994        }
    995        else
    996        {
    997            builder = new ImmutableNode.Builder(newNodes.size());
    998            builder.addChildren(newNodes);
    999        }
    1000        return builder.name(nodeName).create();
    1001    }
    1002
    1003    /**
    1004     * Initializes a transaction to clear the values of a property based on the
    1005     * passed in collection of affected results.
    1006     *
    1007     * @param tx the transaction to be initialized
    1008     * @param results a collection with results pointing to the nodes to be
    1009     *        cleared
    1010     * @return a flag whether there are elements to be cleared
    1011     */
    1012    private static boolean initializeClearTransaction(final ModelTransaction tx,
    1013            final Collection<QueryResult<ImmutableNode>> results)
    1014    {
    1015        for (final QueryResult<ImmutableNode> result : results)
    1016        {
    1017            if (result.isAttributeResult())
    1018            {
    1019                tx.addRemoveAttributeOperation(result.getNode(),
    1020                        result.getAttributeName());
    1021            }
    1022            else
    1023            {
    1024                tx.addClearNodeValueOperation(result.getNode());
    1025            }
    1026        }
    1027
    1028        return !results.isEmpty();
    1029    }
    1030
    1031    /**
    1032     * Initializes a transaction to change the values of some query results
    1033     * based on the passed in map.
    1034     *
    1035     * @param tx the transaction to be initialized
    1036     * @param changedValues the map defining the elements to be changed
    1037     * @return a flag whether there are elements to be updated
    1038     */
    1039    private static boolean initializeUpdateTransaction(final ModelTransaction tx,
    1040            final Map<QueryResult<ImmutableNode>, Object> changedValues)
    1041    {
    1042        for (final Map.Entry<QueryResult<ImmutableNode>, Object> e : changedValues
    1043                .entrySet())
    1044        {
    1045            if (e.getKey().isAttributeResult())
    1046            {
    1047                tx.addAttributeOperation(e.getKey().getNode(), e.getKey()
    1048                        .getAttributeName(), e.getValue());
    1049            }
    1050            else
    1051            {
    1052                tx.addChangeNodeValueOperation(e.getKey().getNode(),
    1053                        e.getValue());
    1054            }
    1055        }
    1056
    1057        return !changedValues.isEmpty();
    1058    }
    1059
    1060    /**
    1061     * Determines the initial root node of this model. If a root node has been
    1062     * provided, it is used. Otherwise, an empty dummy root node is created.
    1063     *
    1064     * @param providedRoot the passed in root node
    1065     * @return the root node to be used
    1066     */
    1067    private static ImmutableNode initialRootNode(final ImmutableNode providedRoot)
    1068    {
    1069        return providedRoot != null ? providedRoot
    1070                : new ImmutableNode.Builder().create();
    1071    }
    1072
    1073    /**
    1074     * Determines the name of the root node for a merge operation. If a root
    1075     * name is provided, it is used. Otherwise, if the current root node has no
    1076     * name, the name of the node to be merged is used. A result of <b>null</b>
    1077     * means that no node name has to be set.
    1078     *
    1079     * @param rootNode the current root node
    1080     * @param node the node to be merged with the root node
    1081     * @param rootName the name of the resulting node
    1082     * @return the new name of the root node
    1083     */
    1084    private static String determineRootName(final ImmutableNode rootNode,
    1085            final ImmutableNode node, final String rootName)
    1086    {
    1087        if (rootName != null)
    1088        {
    1089            return rootName;
    1090        }
    1091        if (rootNode.getNodeName() == null)
    1092        {
    1093            return node.getNodeName();
    1094        }
    1095        return null;
    1096    }
    1097
    1098    /**
    1099     * Creates the mapping to parent nodes for the nodes structured represented
    1100     * by the passed in root node. Each node is assigned its parent node. Here
    1101     * an iterative algorithm is used rather than a recursive one to avoid stack
    1102     * overflow for huge structures.
    1103     *
    1104     * @param root the root node of the structure
    1105     * @return the parent node mapping
    1106     */
    1107    private Map<ImmutableNode, ImmutableNode> createParentMapping(
    1108            final ImmutableNode root)
    1109    {
    1110        final Map<ImmutableNode, ImmutableNode> parents =
    1111                new HashMap<>();
    1112        updateParentMapping(parents, root);
    1113        return parents;
    1114    }
    1115
    1116    /**
    1117     * Performs a non-blocking, thread-safe update of this model based on a
    1118     * transaction initialized by the passed in initializer. This method uses
    1119     * the atomic reference for the model's current data to ensure that an
    1120     * update was successful even if the model is concurrently accessed.
    1121     *
    1122     * @param txInit the {@code TransactionInitializer}
    1123     * @param selector an optional {@code NodeSelector} defining the target node
    1124     *        of the transaction
    1125     * @param resolver the {@code NodeKeyResolver}
    1126     */
    1127    private void updateModel(final TransactionInitializer txInit,
    1128            final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver)
    1129    {
    1130        boolean done;
    1131
    1132        do
    1133        {
    1134            final TreeData currentData = getTreeData();
    1135            done =
    1136                    executeTransactionOnDetachedTrackedNode(txInit, selector,
    1137                            currentData, resolver)
    1138                            || executeTransactionOnCurrentStructure(txInit,
    1139                                    selector, currentData, resolver);
    1140        } while (!done);
    1141    }
    1142
    1143    /**
    1144     * Executes a transaction on the current data of this model. This method is
    1145     * called if an operation is to be executed on the model's root node or a
    1146     * tracked node which is not yet detached.
    1147     *
    1148     * @param txInit the {@code TransactionInitializer}
    1149     * @param selector an optional {@code NodeSelector} defining the target node
    1150     * @param currentData the current data of the model
    1151     * @param resolver the {@code NodeKeyResolver}
    1152     * @return a flag whether the operation has been completed successfully
    1153     */
    1154    private boolean executeTransactionOnCurrentStructure(
    1155            final TransactionInitializer txInit, final NodeSelector selector,
    1156            final TreeData currentData, final NodeKeyResolver<ImmutableNode> resolver)
    1157    {
    1158        boolean done;
    1159        final ModelTransaction tx =
    1160                new ModelTransaction(currentData, selector, resolver);
    1161        if (!txInit.initTransaction(tx))
    1162        {
    1163            done = true;
    1164        }
    1165        else
    1166        {
    1167            final TreeData newData = tx.execute();
    1168            done = structure.compareAndSet(tx.getCurrentData(), newData);
    1169        }
    1170        return done;
    1171    }
    1172
    1173    /**
    1174     * Tries to execute a transaction on the model of a detached tracked node.
    1175     * This method checks whether the target node of the transaction is a
    1176     * tracked node and if this node is already detached. If this is the case,
    1177     * the update operation is independent on this model and has to be executed
    1178     * on the specific model for the detached node.
    1179     *
    1180     * @param txInit the {@code TransactionInitializer}
    1181     * @param selector an optional {@code NodeSelector} defining the target node
    1182     * @param currentData the current data of the model
    1183     * @param resolver the {@code NodeKeyResolver} @return a flag whether the
    1184     *        transaction could be executed
    1185     * @throws ConfigurationRuntimeException if the selector cannot be resolved
    1186     */
    1187    private boolean executeTransactionOnDetachedTrackedNode(
    1188            final TransactionInitializer txInit, final NodeSelector selector,
    1189            final TreeData currentData, final NodeKeyResolver<ImmutableNode> resolver)
    1190    {
    1191        if (selector != null)
    1192        {
    1193            final InMemoryNodeModel detachedNodeModel =
    1194                    currentData.getNodeTracker().getDetachedNodeModel(selector);
    1195            if (detachedNodeModel != null)
    1196            {
    1197                detachedNodeModel.updateModel(txInit, null, resolver);
    1198                return true;
    1199            }
    1200        }
    1201
    1202        return false;
    1203    }
    1204
    1205    /**
    1206     * Replaces a tracked node if it is already detached.
    1207     *
    1208     * @param currentData the current data of the model
    1209     * @param selector the {@code NodeSelector} defining the tracked node
    1210     * @param newNode the node replacing the tracked node
    1211     * @return a flag whether the operation was successful
    1212     */
    1213    private boolean replaceDetachedTrackedNode(final TreeData currentData,
    1214            final NodeSelector selector, final ImmutableNode newNode)
    1215    {
    1216        final InMemoryNodeModel detachedNodeModel =
    1217                currentData.getNodeTracker().getDetachedNodeModel(selector);
    1218        if (detachedNodeModel != null)
    1219        {
    1220            detachedNodeModel.setRootNode(newNode);
    1221            return true;
    1222        }
    1223
    1224        return false;
    1225    }
    1226
    1227    /**
    1228     * Replaces an active tracked node. The node then becomes detached.
    1229     *
    1230     * @param currentData the current data of the model
    1231     * @param selector the {@code NodeSelector} defining the tracked node
    1232     * @param newNode the node replacing the tracked node
    1233     * @return a flag whether the operation was successful
    1234     */
    1235    private boolean replaceActiveTrackedNode(final TreeData currentData,
    1236            final NodeSelector selector, final ImmutableNode newNode)
    1237    {
    1238        final NodeTracker newTracker =
    1239                currentData.getNodeTracker().replaceAndDetachTrackedNode(
    1240                        selector, newNode);
    1241        return structure.compareAndSet(currentData,
    1242                currentData.updateNodeTracker(newTracker));
    1243    }
    1244
    1245    /**
    1246     * Creates tracked node entries for the specified nodes and creates the
    1247     * corresponding selectors.
    1248     *
    1249     * @param refSelectors the reference where to store the selectors
    1250     * @param nodes the nodes to be tracked
    1251     * @param current the current {@code TreeData} object
    1252     * @param resolver the {@code NodeKeyResolver}
    1253     * @return the updated {@code TreeData} object
    1254     */
    1255    private static TreeData createSelectorsForTrackedNodes(
    1256            final Mutable<Collection<NodeSelector>> refSelectors,
    1257            final List<ImmutableNode> nodes, final TreeData current,
    1258            final NodeKeyResolver<ImmutableNode> resolver)
    1259    {
    1260        final List<NodeSelector> selectors =
    1261                new ArrayList<>(nodes.size());
    1262        final Map<ImmutableNode, String> cache = new HashMap<>();
    1263        for (final ImmutableNode node : nodes)
    1264        {
    1265            selectors.add(new NodeSelector(resolver.nodeKey(node, cache,
    1266                    current)));
    1267        }
    1268        refSelectors.setValue(selectors);
    1269        final NodeTracker newTracker =
    1270                current.getNodeTracker().trackNodes(selectors, nodes);
    1271        return current.updateNodeTracker(newTracker);
    1272    }
    1273
    1274    /**
    1275     * Adds a tracked node that has already been resolved to the specified data
    1276     * object.
    1277     *
    1278     * @param current the current {@code TreeData} object
    1279     * @param node the node in question
    1280     * @param resolver the {@code NodeKeyResolver}
    1281     * @param refSelector here the newly created {@code NodeSelector} is
    1282     *        returned
    1283     * @return the new {@code TreeData} instance
    1284     */
    1285    private static TreeData updateDataWithNewTrackedNode(final TreeData current,
    1286            final ImmutableNode node, final NodeKeyResolver<ImmutableNode> resolver,
    1287            final MutableObject<NodeSelector> refSelector)
    1288    {
    1289        final NodeSelector selector =
    1290                new NodeSelector(resolver.nodeKey(node,
    1291                        new HashMap<ImmutableNode, String>(), current));
    1292        refSelector.setValue(selector);
    1293        final NodeTracker newTracker =
    1294                current.getNodeTracker().trackNodes(
    1295                        Collections.singleton(selector),
    1296                        Collections.singleton(node));
    1297        return current.updateNodeTracker(newTracker);
    1298    }
    1299
    1300    /**
    1301     * Creates a new data object with a tracked child node of the given parent
    1302     * node. If such a child node already exists, it is used. Otherwise, a new
    1303     * one is created.
    1304     *
    1305     * @param current the current {@code TreeData} object
    1306     * @param parent the parent node
    1307     * @param childName the name of the child node
    1308     * @param resolver the {@code NodeKeyResolver}
    1309     * @param refSelector here the newly created {@code NodeSelector} is
    1310     *        returned
    1311     * @return the new {@code TreeData} instance
    1312     */
    1313    private static TreeData createDataWithTrackedChildNode(final TreeData current,
    1314            final ImmutableNode parent, final String childName,
    1315            final NodeKeyResolver<ImmutableNode> resolver,
    1316            final MutableObject<NodeSelector> refSelector)
    1317    {
    1318        TreeData newData;
    1319        final List<ImmutableNode> namedChildren =
    1320                current.getChildren(parent, childName);
    1321        if (!namedChildren.isEmpty())
    1322        {
    1323            newData =
    1324                    updateDataWithNewTrackedNode(current, namedChildren.get(0),
    1325                            resolver, refSelector);
    1326        }
    1327        else
    1328        {
    1329            final ImmutableNode child =
    1330                    new ImmutableNode.Builder().name(childName).create();
    1331            final ModelTransaction tx = new ModelTransaction(current, null, resolver);
    1332            tx.addAddNodeOperation(parent, child);
    1333            newData =
    1334                    updateDataWithNewTrackedNode(tx.execute(), child, resolver,
    1335                            refSelector);
    1336        }
    1337        return newData;
    1338    }
    1339
    1340    /**
    1341     * Checks whether the specified collection with values is not empty.
    1342     *
    1343     * @param values the collection with node values
    1344     * @return <b>true</b> if values are provided, <b>false</b> otherwise
    1345     */
    1346    private static boolean valuesNotEmpty(final Iterable<?> values)
    1347    {
    1348        return values.iterator().hasNext();
    1349    }
    1350
    1351    /**
    1352     * Creates an exception referring to an invalid key for adding properties.
    1353     * Such an exception is thrown when an operation tries to add something to
    1354     * an attribute.
    1355     *
    1356     * @param key the invalid key causing this exception
    1357     * @return the exception
    1358     */
    1359    private static RuntimeException attributeKeyException(final String key)
    1360    {
    1361        return new IllegalArgumentException(
    1362                "New nodes cannot be added to an attribute key: " + key);
    1363    }
    1364
    1365    /**
    1366     * An interface used internally for handling concurrent updates. An
    1367     * implementation has to populate the passed in {@code ModelTransaction}.
    1368     * The transaction is then executed, and an atomic update of the model's
    1369     * {@code TreeData} is attempted. If this fails - because another update
    1370     * came across -, the whole operation has to be tried anew.
    1371     */
    1372    private interface TransactionInitializer
    1373    {
    1374        /**
    1375         * Initializes the specified transaction for an update operation. The
    1376         * return value indicates whether the transaction should be executed. A
    1377         * result of <b>false</b> means that the update is to be aborted (maybe
    1378         * another update method was called).
    1379         *
    1380         * @param tx the transaction to be initialized
    1381         * @return a flag whether the update should continue
    1382         */
    1383        boolean initTransaction(ModelTransaction tx);
    1384    }
    1385}