ReferenceTracker.java
- /*
- * 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;
- /**
- * <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>
- */
- final class ReferenceTracker {
- /** A map with reference data. */
- private final Map<ImmutableNode, Object> references;
- /** A list with the removed references. */
- private final List<Object> removedReferences;
- /**
- * 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());
- }
- /**
- * 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;
- }
- /**
- * 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);
- }
- /**
- * Gets the reference object associated with the given node.
- *
- * @param node the node
- * @return the reference object for this node or <strong>null</strong>
- */
- public Object getReference(final ImmutableNode node) {
- return references.get(node);
- }
- /**
- * Gets the list with removed references. This list is immutable.
- *
- * @return the list with removed references
- */
- public List<Object> getRemovedReferences() {
- return Collections.unmodifiableList(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.
- *
- * @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;
- }
- }