All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.configuration2.tree.ReferenceTracker Maven / Gradle / Ivy

/*
 * 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 {@link 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 * {@code 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. *

* */ class ReferenceTracker { /** A map with reference data. */ private final Map references; /** A list with the removed references. */ private final List removedReferences; /** * 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 refs, final List removedRefs) { references = refs; removedReferences = removedRefs; } /** * Creates a new instance of {@code ReferenceTracker}. This instance does * not yet contain any data about references. */ public ReferenceTracker() { this(Collections. 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. * * @param refs the references to be added * @return the new instance */ public ReferenceTracker addReferences(final Map refs) { final Map 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. * * @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 replacedNodes, final Collection removedNodes) { if (!references.isEmpty()) { Map newRefs = null; for (final Map.Entry 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 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. * * @param node the node * @return the reference object for this node or null */ public Object getReference(final ImmutableNode node) { return references.get(node); } /** * Returns the list with removed references. This list is immutable. * * @return the list with removed references */ public List getRemovedReferences() { return Collections.unmodifiableList(removedReferences); } }