/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.configuration2.tree;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
A class which allows an InMemoryNodeModel
to associate arbitrary objects with nodes.
Some special configuration implementations need additional data to be stored
with their nodes structure. We call such data "references" because
objects required by a configuration are referenced. In such constellations,
it is necessary to keep track about the nodes associated with references even
if they are replaced by others during an update operation of the model. This
is the task of this class.
Basically, an instance manages a map associating nodes with reference objects. When a node is replaced the map gets updated. References becoming orphans because the nodes pointing to them were removed are tracked, too. They may be of importance for special configuration implementations as they might require additional updates. A concrete use case for this class is XMLConfiguration
which stores the DOM nodes represented by configuration nodes as references.
Implementation note: This class is intended to work in a concurrent
environment. Instances are immutable. The represented state can be updated by
creating new instances which are then stored by the owning node model.
/**
* <p>
* A class which allows an {@link InMemoryNodeModel} to associate arbitrary
* objects with nodes.
* </p>
* <p>
* Some special configuration implementations need additional data to be stored
* with their nodes structure. We call such data "references" because
* objects required by a configuration are referenced. In such constellations,
* it is necessary to keep track about the nodes associated with references even
* if they are replaced by others during an update operation of the model. This
* is the task of this class.
* </p>
* <p>
* Basically, an instance manages a map associating nodes with reference
* objects. When a node is replaced the map gets updated. References becoming
* orphans because the nodes pointing to them were removed are tracked, too.
* They may be of importance for special configuration implementations as they
* might require additional updates. A concrete use case for this class is
* {@code XMLConfiguration} which stores the DOM nodes represented by
* configuration nodes as references.
* </p>
* <p>
* Implementation note: This class is intended to work in a concurrent
* environment. Instances are immutable. The represented state can be updated by
* creating new instances which are then stored by the owning node model.
* </p>
*
*/
class ReferenceTracker
{
A map with reference data. /** A map with reference data. */
private final Map<ImmutableNode, Object> references;
A list with the removed references. /** A list with the removed references. */
private final List<Object> removedReferences;
Creates a new instance of ReferenceTracker
and sets the data to be managed. This constructor is used internally when references are updated. Params: - refs – the references
- removedRefs – the removed references
/**
* Creates a new instance of {@code ReferenceTracker} and sets the data to
* be managed. This constructor is used internally when references are
* updated.
*
* @param refs the references
* @param removedRefs the removed references
*/
private ReferenceTracker(final Map<ImmutableNode, Object> refs,
final List<Object> removedRefs)
{
references = refs;
removedReferences = removedRefs;
}
Creates a new instance of ReferenceTracker
. This instance does not yet contain any data about references. /**
* Creates a new instance of {@code ReferenceTracker}. This instance does
* not yet contain any data about references.
*/
public ReferenceTracker()
{
this(Collections.<ImmutableNode, Object> emptyMap(), Collections
.emptyList());
}
Adds all references stored in the passed in map to the managed
references. A new instance is created managing this new set of
references.
Params: - refs – the references to be added
Returns: the new instance
/**
* Adds all references stored in the passed in map to the managed
* references. A new instance is created managing this new set of
* references.
*
* @param refs the references to be added
* @return the new instance
*/
public ReferenceTracker addReferences(final Map<ImmutableNode, ?> refs)
{
final Map<ImmutableNode, Object> newRefs =
new HashMap<>(references);
newRefs.putAll(refs);
return new ReferenceTracker(newRefs, removedReferences);
}
Updates the references managed by this object at the end of a model
transaction. This method is called by the transaction with the nodes that
have been replaced by others and the nodes that have been removed. The
internal data structures are updated correspondingly.
Params: - replacedNodes – the map with nodes that have been replaced
- removedNodes – the list with nodes that have been removed
Returns: the new instance
/**
* Updates the references managed by this object at the end of a model
* transaction. This method is called by the transaction with the nodes that
* have been replaced by others and the nodes that have been removed. The
* internal data structures are updated correspondingly.
*
* @param replacedNodes the map with nodes that have been replaced
* @param removedNodes the list with nodes that have been removed
* @return the new instance
*/
public ReferenceTracker updateReferences(
final Map<ImmutableNode, ImmutableNode> replacedNodes,
final Collection<ImmutableNode> removedNodes)
{
if (!references.isEmpty())
{
Map<ImmutableNode, Object> newRefs = null;
for (final Map.Entry<ImmutableNode, ImmutableNode> e : replacedNodes
.entrySet())
{
final Object ref = references.get(e.getKey());
if (ref != null)
{
if (newRefs == null)
{
newRefs =
new HashMap<>(references);
}
newRefs.put(e.getValue(), ref);
newRefs.remove(e.getKey());
}
}
List<Object> newRemovedRefs =
newRefs != null ? new LinkedList<>(
removedReferences) : null;
for (final ImmutableNode node : removedNodes)
{
final Object ref = references.get(node);
if (ref != null)
{
if (newRefs == null)
{
newRefs =
new HashMap<>(references);
}
newRefs.remove(node);
if (newRemovedRefs == null)
{
newRemovedRefs =
new LinkedList<>(removedReferences);
}
newRemovedRefs.add(ref);
}
}
if (newRefs != null)
{
return new ReferenceTracker(newRefs, newRemovedRefs);
}
}
return this;
}
Returns the reference object associated with the given node.
Params: - node – the node
Returns: the reference object for this node or null
/**
* Returns the reference object associated with the given node.
*
* @param node the node
* @return the reference object for this node or <b>null</b>
*/
public Object getReference(final ImmutableNode node)
{
return references.get(node);
}
Returns the list with removed references. This list is immutable.
Returns: the list with removed references
/**
* Returns the list with removed references. This list is immutable.
*
* @return the list with removed references
*/
public List<Object> getRemovedReferences()
{
return Collections.unmodifiableList(removedReferences);
}
}