/*
* JBoss, Home of Professional Open Source
* Copyright 2009 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.tree;
import org.infinispan.AdvancedCache;
import org.infinispan.atomic.AtomicHashMapProxy;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.batch.BatchContainer;
import org.infinispan.context.Flag;
import org.infinispan.util.Immutables;
import org.infinispan.util.Util;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Implementation backed by an {@link AtomicMap}
*
* @author Manik Surtani (<a href="mailto:manik AT jboss DOT org">manik AT jboss DOT org</a>)
* @since 4.0
*/
public class NodeImpl<K, V> extends TreeStructureSupport implements Node<K, V> {
Fqn fqn;
NodeKey dataKey, structureKey;
public NodeImpl(Fqn fqn, AdvancedCache<?, ?> cache, BatchContainer batchContainer) {
super(cache, batchContainer);
this.fqn = fqn;
dataKey = new NodeKey(fqn, NodeKey.Type.DATA);
structureKey = new NodeKey(fqn, NodeKey.Type.STRUCTURE);
}
@Override
public Node<K, V> getParent() {
if (fqn.isRoot()) return this;
return new NodeImpl<K, V>(fqn.getParent(), cache, batchContainer);
}
@Override
public Node<K, V> getParent(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getParent();
}
@Override
public Set<Node<K, V>> getChildren() {
startAtomic();
try {
Set<Node<K, V>> result = new HashSet<Node<K, V>>();
for (Fqn f : getStructure().values()) {
NodeImpl<K, V> n = new NodeImpl<K, V>(f, cache, batchContainer);
result.add(n);
}
return Immutables.immutableSetWrap(result);
}
finally {
endAtomic();
}
}
@Override
public Set<Node<K, V>> getChildren(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getChildren();
}
@Override
public Set<Object> getChildrenNames() {
return Immutables.immutableSetCopy(getStructure().keySet());
}
@Override
public Set<Object> getChildrenNames(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getChildrenNames();
}
@Override
@SuppressWarnings("unchecked")
public Map<K, V> getData() {
return Collections.unmodifiableMap(new HashMap(getDataInternal()));
}
@Override
public Map<K, V> getData(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getData();
}
@Override
public Set<K> getKeys() {
startAtomic();
try {
return getData().keySet();
}
finally {
endAtomic();
}
}
@Override
public Set<K> getKeys(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getKeys();
}
@Override
public Fqn getFqn() {
return fqn;
}
@Override
public Node<K, V> addChild(Fqn f) {
startAtomic();
try {
Fqn absoluteChildFqn = Fqn.fromRelativeFqn(fqn, f);
//1) first register it with the parent
AtomicMap<Object, Fqn> structureMap = getStructure();
structureMap.put(f.getLastElement(), absoluteChildFqn);
//2) then create the structure and data maps
createNodeInCache(absoluteChildFqn);
return new NodeImpl<K, V>(absoluteChildFqn, cache, batchContainer);
}
finally {
endAtomic();
}
}
@Override
public Node<K, V> addChild(Fqn f, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return addChild(f);
}
@Override
public boolean removeChild(Fqn f) {
return removeChild(f.getLastElement());
}
@Override
public boolean removeChild(Fqn f, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return removeChild(f);
}
@Override
public boolean removeChild(Object childName) {
startAtomic();
try {
AtomicMap<Object, Fqn> s = getStructure();
Fqn childFqn = s.remove(childName);
if (childFqn != null) {
Node<K, V> child = new NodeImpl<K, V>(childFqn, cache, batchContainer);
child.removeChildren();
child.clearData(); // this is necessary in case we have a remove and then an add on the same node, in the same tx.
cache.remove(new NodeKey(childFqn, NodeKey.Type.DATA));
cache.remove(new NodeKey(childFqn, NodeKey.Type.STRUCTURE));
return true;
}
return false;
}
finally {
endAtomic();
}
}
@Override
public boolean removeChild(Object childName, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return removeChild(childName);
}
@Override
public Node<K, V> getChild(Fqn f) {
startAtomic();
try {
if (hasChild(f))
return new NodeImpl<K, V>(Fqn.fromRelativeFqn(fqn, f), cache, batchContainer);
else
return null;
}
finally {
endAtomic();
}
}
@Override
public Node<K, V> getChild(Fqn f, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getChild(f);
}
@Override
public Node<K, V> getChild(Object name) {
startAtomic();
try {
if (hasChild(name))
return new NodeImpl<K, V>(Fqn.fromRelativeElements(fqn, name), cache, batchContainer);
else
return null;
}
finally {
endAtomic();
}
}
@Override
public Node<K, V> getChild(Object name, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return getChild(name);
}
@Override
public V put(K key, V value) {
startAtomic();
try {
AtomicHashMapProxy<K, V> map = (AtomicHashMapProxy<K, V>) getDataInternal();
return map.put(key, value);
}
finally {
endAtomic();
}
}
@Override
public V put(K key, V value, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return put(key, value);
}
@Override
public V putIfAbsent(K key, V value) {
startAtomic();
try {
AtomicMap<K, V> data = getDataInternal();
if (!data.containsKey(key)) return data.put(key, value);
return null;
}
finally {
endAtomic();
}
}
@Override
public V putIfAbsent(K key, V value, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return putIfAbsent(key, value);
}
@Override
public V replace(K key, V value) {
startAtomic();
try {
AtomicMap<K, V> map = getAtomicMap(dataKey);
if (map.containsKey(key))
return map.put(key, value);
else
return null;
}
finally {
endAtomic();
}
}
@Override
public V replace(K key, V value, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return replace(key, value);
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
startAtomic();
try {
AtomicMap<K, V> data = getDataInternal();
V old = data.get(key);
if (Util.safeEquals(oldValue, old)) {
data.put(key, newValue);
return true;
}
return false;
}
finally {
endAtomic();
}
}
@Override
public boolean replace(K key, V oldValue, V value, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return replace(key, oldValue, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
startAtomic();
try {
getDataInternal().putAll(map);
}
finally {
endAtomic();
}
}
@Override
public void putAll(Map<? extends K, ? extends V> map, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
putAll(map);
}
@Override
public void replaceAll(Map<? extends K, ? extends V> map) {
startAtomic();
try {
AtomicMap<K, V> data = getDataInternal();
data.clear();
data.putAll(map);
}
finally {
endAtomic();
}
}
@Override
public void replaceAll(Map<? extends K, ? extends V> map, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
replaceAll(map);
}
@Override
public V get(K key) {
return getData().get(key);
}
@Override
public V get(K key, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return get(key);
}
@Override
public V remove(K key) {
startAtomic();
try {
return getDataInternal().remove(key);
}
finally {
endAtomic();
}
}
@Override
public V remove(K key, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return remove(key);
}
@Override
public void clearData() {
getDataInternal().clear();
}
@Override
public void clearData(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
clearData();
}
@Override
public int dataSize() {
return getData().size();
}
@Override
public int dataSize(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return dataSize();
}
@Override
public boolean hasChild(Fqn f) {
if (f.size() > 1) {
// indirect child.
Fqn absoluteFqn = Fqn.fromRelativeFqn(fqn, f);
return exists(absoluteFqn);
} else {
return hasChild(f.getLastElement());
}
}
@Override
public boolean hasChild(Fqn f, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return hasChild(f);
}
@Override
public boolean hasChild(Object o) {
return getStructure().containsKey(o);
}
@Override
public boolean hasChild(Object o, Flag... flags) {
tcc.createTreeContext().addFlags(flags);
return hasChild(o);
}
@Override
public boolean isValid() {
return cache.containsKey(dataKey);
}
@Override
public void removeChildren() {
startAtomic();
try {
Map<Object, Fqn> s = getStructure();
for (Object o : Immutables.immutableSetCopy(s.keySet())) removeChild(o);
}
finally {
endAtomic();
}
}
@Override
public void removeChildren(Flag... flags) {
tcc.createTreeContext().addFlags(flags);
removeChildren();
}
@SuppressWarnings("unchecked")
AtomicMap<K, V> getDataInternal() {
return getAtomicMap(dataKey);
}
@SuppressWarnings("unchecked")
AtomicMap<Object, Fqn> getStructure() {
return getAtomicMap(structureKey);
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NodeImpl<?, ?> node = (NodeImpl<?, ?>) o;
if (fqn != null ? !fqn.equals(node.fqn) : node.fqn != null) return false;
return true;
}
public int hashCode() {
return (fqn != null ? fqn.hashCode() : 0);
}
@Override
public String toString() {
return "NodeImpl{" +
"fqn=" + fqn +
'}';
}
}