blob: 2d5832ce93d621d78b7beaec6871fd2667a3e100 [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Foundation
*
* Licensed 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.onosproject.store.primitives.resources.impl;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import org.onosproject.store.service.DocumentPath;
import org.onosproject.store.service.DocumentTree;
import org.onosproject.store.service.DocumentTreeListener;
import org.onosproject.store.service.DocumentTreeNode;
import org.onosproject.store.service.IllegalDocumentModificationException;
import org.onosproject.store.service.NoSuchDocumentPathException;
import org.onosproject.store.service.Ordering;
import org.onosproject.store.service.Versioned;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps;
/**
* Simple implementation of a {@link DocumentTree}.
*
* @param <V> tree node value type
*/
public class DefaultDocumentTree<V> implements DocumentTree<V> {
private static final DocumentPath ROOT_PATH = DocumentPath.from("root");
final DefaultDocumentTreeNode<V> root;
private final Supplier<Long> versionSupplier;
public DefaultDocumentTree() {
AtomicLong versionCounter = new AtomicLong(0);
versionSupplier = versionCounter::incrementAndGet;
root = new DefaultDocumentTreeNode<>(ROOT_PATH, null, versionSupplier.get(), Ordering.NATURAL, null);
}
public DefaultDocumentTree(Supplier<Long> versionSupplier, Ordering ordering) {
root = new DefaultDocumentTreeNode<>(ROOT_PATH, null, versionSupplier.get(), ordering, null);
this.versionSupplier = versionSupplier;
}
DefaultDocumentTree(Supplier<Long> versionSupplier, DefaultDocumentTreeNode<V> root) {
this.root = root;
this.versionSupplier = versionSupplier;
}
@Override
public String name() {
return null;
}
@Override
public DocumentPath root() {
return ROOT_PATH;
}
@Override
public Map<String, Versioned<V>> getChildren(DocumentPath path) {
DocumentTreeNode<V> node = getNode(path);
if (node != null) {
Map<String, Versioned<V>> childrenValues = Maps.newLinkedHashMap();
node.children().forEachRemaining(n -> childrenValues.put(simpleName(n.path()), n.value()));
return childrenValues;
}
throw new NoSuchDocumentPathException();
}
@Override
public Versioned<V> get(DocumentPath path) {
DocumentTreeNode<V> currentNode = getNode(path);
return currentNode != null ? currentNode.value() : null;
}
@Override
public Versioned<V> set(DocumentPath path, V value) {
checkRootModification(path);
DefaultDocumentTreeNode<V> node = getNode(path);
if (node != null) {
return node.update(value, versionSupplier.get());
} else {
create(path, value);
return null;
}
}
@Override
public boolean create(DocumentPath path, V value) {
checkRootModification(path);
DocumentTreeNode<V> node = getNode(path);
if (node != null) {
return false;
}
DocumentPath parentPath = path.parent();
DefaultDocumentTreeNode<V> parentNode = getNode(parentPath);
if (parentNode == null) {
throw new IllegalDocumentModificationException();
}
parentNode.addChild(simpleName(path), value, versionSupplier.get());
return true;
}
@Override
public boolean createRecursive(DocumentPath path, V value) {
checkRootModification(path);
DocumentTreeNode<V> node = getNode(path);
if (node != null) {
return false;
}
DocumentPath parentPath = path.parent();
if (getNode(parentPath) == null) {
createRecursive(parentPath, null);
}
DefaultDocumentTreeNode<V> parentNode = getNode(parentPath);
if (parentNode == null) {
throw new IllegalDocumentModificationException();
}
parentNode.addChild(simpleName(path), value, versionSupplier.get());
return true;
}
@Override
public boolean replace(DocumentPath path, V newValue, long version) {
checkRootModification(path);
DocumentTreeNode<V> node = getNode(path);
if (node != null && node.value() != null && node.value().version() == version) {
set(path, newValue);
return true;
}
return false;
}
@Override
public boolean replace(DocumentPath path, V newValue, V expectedValue) {
checkRootModification(path);
if (Objects.equals(newValue, expectedValue)) {
return false;
}
DocumentTreeNode<V> node = getNode(path);
V prevValue = Optional.ofNullable(node)
.map(DocumentTreeNode::value)
.map(Versioned::valueOrNull)
.orElse(null);
if (Objects.equals(prevValue, expectedValue)) {
set(path, newValue);
return true;
}
return false;
}
@Override
public Versioned<V> removeNode(DocumentPath path) {
checkRootModification(path);
DefaultDocumentTreeNode<V> nodeToRemove = getNode(path);
if (nodeToRemove == null) {
throw new NoSuchDocumentPathException();
}
if (nodeToRemove.hasChildren()) {
throw new IllegalDocumentModificationException();
}
DefaultDocumentTreeNode<V> parent = (DefaultDocumentTreeNode<V>) nodeToRemove.parent();
parent.removeChild(simpleName(path));
return nodeToRemove.value();
}
@Override
public void addListener(DocumentPath path, DocumentTreeListener<V> listener) {
// TODO Auto-generated method stub
}
@Override
public void removeListener(DocumentTreeListener<V> listener) {
// TODO Auto-generated method stub
}
private DefaultDocumentTreeNode<V> getNode(DocumentPath path) {
Iterator<String> pathElements = path.pathElements().iterator();
DefaultDocumentTreeNode<V> currentNode = root;
checkArgument("root".equals(pathElements.next()), "Path should start with root: %s", path);
while (pathElements.hasNext() && currentNode != null) {
currentNode = (DefaultDocumentTreeNode<V>) currentNode.child(pathElements.next());
}
return currentNode;
}
private String simpleName(DocumentPath path) {
return path.pathElements().get(path.pathElements().size() - 1);
}
private void checkRootModification(DocumentPath path) {
if (ROOT_PATH.equals(path)) {
throw new IllegalDocumentModificationException();
}
}
}