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