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