Aaron Kruglikov

Adding an initial API for the DomTree data structure.

Change-Id: I55da78c11f49c1e5843cfefbe0a5eed02c59498b
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Sets;
import org.onosproject.store.service.DocumentPath;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.TreeSet;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A tree node made for {@code DocumentTree}
* implementations that keeps records of its parent and children.
*/
public class DocumentTreeNode<V> {
private final DocumentPath key;
private V value;
private final TreeSet<DocumentTreeNode<V>> children =
Sets.newTreeSet(new Comparator<DocumentTreeNode<V>>() {
@Override
public int compare(DocumentTreeNode<V> o1,
DocumentTreeNode<V> o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
private DocumentTreeNode parent;
public DocumentTreeNode(DocumentPath key, V value,
DocumentTreeNode parent) {
this.key = checkNotNull(key);
this.value = checkNotNull(value);
this.parent = parent;
}
/**
* Returns this objects key.
*
* @return the key
*/
public DocumentPath getKey() {
return key;
}
/**
* Returns this objects value.
*
* @return the value
*/
public V getValue() {
return value;
}
/**
* Sets this objects value.
*
* @param value the value to be set
*/
public void setValue(V value) {
this.value = value;
}
/**
* Returns a collection of the children of this node.
*
* @return a sorted iterator for the children of this node.
*/
public Iterator<DocumentTreeNode<V>> getChildren() {
return children.iterator();
}
/**
* Adds a child to this node.
*
* @param child the child node to be added
* @return true if the child set was modified as a result of this call,
* false otherwise
*/
public boolean addChild(DocumentTreeNode<V> child) {
return children.add(child);
}
/**
* Removes a child from the children of this node.
*
* @param child the child node to be removed
* @return true if the child set was modified as a result of this call,
* false otherwise
*/
public boolean removeChild(String child) {
return children.remove(child);
}
/**
* Returns the parent of this node.
*
* @return the parent node of this node, which may be null
*/
public DocumentTreeNode<V> getParent() {
return parent;
}
@Override
public int hashCode() {
return Objects.hash(this.key);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DocumentTreeNode) {
DocumentTreeNode that = (DocumentTreeNode) obj;
if (this.parent.equals(that.parent)) {
if (this.children.size() == that.children.size()) {
for (DocumentTreeNode child : this.children) {
if (!that.children.contains(child)) {
return false;
}
}
return true;
}
}
}
return false;
}
@Override
public String toString() {
MoreObjects.ToStringHelper helper =
MoreObjects.toStringHelper(getClass())
.add("parent", this.parent)
.add("key", this.key)
.add("value", this.value);
for (DocumentTreeNode child : children) {
helper = helper.add("child", child.key);
}
return helper.toString();
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.service;
/**
* Exceptions for use by the {@code DocumentTree} and {@code DocumentPath}.
*/
public class DocumentException extends RuntimeException {
public DocumentException() {
super();
}
public DocumentException(String message) {
super(message);
}
public DocumentException(String message, Throwable cause) {
super(message, cause);
}
public DocumentException(Throwable cause) {
super(cause);
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.service;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
/**
* A path class for identifying nodes within the {@code DocumentTree}.
*
* Note: indexing is ONE based so the root is the 1st level, to retrieve the
* root one should query level 1.
*/
public class DocumentPath implements Comparable {
private final ArrayList<String> tokens = Lists.newArrayList();
/**
* Private utility constructor for internal generation of partial paths only.
*
* @param path
*/
private DocumentPath(List<String> path) {
Preconditions.checkNotNull(path);
this.tokens.addAll(path);
}
/**
* Constructor to generate new {@DocumentPath}, new paths must contain at
* least one name and string names may NOT contain any '.'s. If one field
* is null that field will be ignored.
*
* @throws IllegalDocumentNameException if both parameters are null or the string
* name contains an illegal character ('.')
* @param nodeName the name of the last level of this path
* @param parentPath the path representing the parents leading up to this
* node, in the case of the root this should be null
*/
public DocumentPath(String nodeName, DocumentPath parentPath) {
if (nodeName.contains(".")) {
throw new IllegalDocumentNameException(
"Periods are not allowed in names.");
}
if (parentPath != null) {
tokens.addAll(parentPath.path());
}
if (nodeName != null) {
tokens.add(nodeName);
}
if (tokens.isEmpty()) {
throw new IllegalDocumentNameException("A document path must contain at" +
"least one non-null" +
"element.");
}
}
/**
* Returns a path from the root to the parent of this node, if this node is
* the root this call returns null.
*
* @return a {@code DocumentPath} representing a path to this paths parent,
* null if this a root path
*/
public DocumentPath parent() {
if (tokens.size() <= 1) {
return null;
}
return new DocumentPath(this.tokens.subList(0, tokens.size() - 1));
}
/**
* Returns the complete list of tokens representing this path in correct
* order.
*
* @return a list of strings representing this path
*/
public List<String> path() {
return ImmutableList.copyOf(tokens);
}
@Override
public int hashCode() {
return Objects.hash(tokens);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DocumentPath) {
DocumentPath that = (DocumentPath) obj;
return this.tokens.equals(that.tokens);
}
return false;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
Iterator<String> iter = tokens.iterator();
while (iter.hasNext()) {
stringBuilder.append(iter.next());
if (iter.hasNext()) {
stringBuilder.append(".");
}
}
return stringBuilder.toString();
}
@Override
public int compareTo(Object o) {
if (o instanceof DocumentPath) {
DocumentPath that = (DocumentPath) o;
int shorterLength = this.tokens.size() > that.tokens.size() ?
that.tokens.size() : this.tokens.size();
for (int i = 0; i < shorterLength; i++) {
if (this.tokens.get(i).compareTo(that.tokens.get(i)) != 0) {
return this.tokens.get(i).compareTo(that.tokens.get(i));
}
}
if (this.tokens.size() > that.tokens.size()) {
return 1;
} else if (that.tokens.size() > this.tokens.size()) {
return -1;
} else {
return 0;
}
}
throw new IllegalArgumentException("Compare can only compare objects" +
"of the same type.");
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.service;
import org.onosproject.store.primitives.DocumentTreeNode;
import java.util.Iterator;
/**
* Interface for a tree with structure wherein keys hold information
* about the hierarchical structure of the tree (name of parent,
* grandparent etc.).
*/
public interface DocumentTree<V> {
/**
* Returns the root of the tree. This will be the first part of any fully
* qualified path and will enable discovery of the entire tree.
*
* @return a string that is the name of the root of the tree.
*/
DocumentPath root();
/**
* Returns a sorted list containing all key value pairs that are direct
* descendants of the supplied parent. The returned list will be immutable.
* If the specified parent does not exist this method will fail with an
* exception.
*
* @throws NoSuchDocumentPathException if the parent does not exist
* @param parentPath the path to the parent of the desired nodes
* @return an iterator of the children of the specified parent
*/
Iterator<DocumentTreeNode<V>> getChildren(DocumentPath parentPath);
/**
* Returns the value associated with the supplied key or null if no such
* node exists.
*
* @param key the key to query
* @return a value or null
*/
DocumentTreeNode<V> getNode(DocumentPath key);
/**
* Takes a string that specifies the complete path to the mapping to be
* added or updated and creates the key value mapping or updates the value.
* If the specified parent cannot be found the operation fails with an
* error.
*
* @throws NoSuchDocumentPathException if the specified parent does not
* exist.
* @param key the fully qualified key of the entry to be added or updated
* @param value the non-null value to be associated with the key
* @return the previous mapping or null if there was no previous mapping
*/
V putNode(DocumentPath key, V value);
/**
* Takes the fully qualified name of the node to be added along with
* the value to be added. If the specified key already exists it doesnot
* update anything & returns false. If the parent does not exist the
* operation fails with an exception.
*
* @throws NoSuchDocumentPathException if the specified parent does not
* exist.
* @param key the fully qualified key of the entry to be added or updated
* @param value the non-null value to be associated with the key
* @return returns true if the mapping could be added successfully
*/
boolean createNode(DocumentPath key, V value);
/**
* Removes the node with the specified fully qualified key. Returns null if
* the node did not exist. This method will throw an exception if called on
* a non-leaf node.
*
* @throws IllegalDocumentModificationException if the node had children.
* @param key the fully qualified key of the node to be removed
* @return the previous value of the node or null if it did not exist
*/
V removeNode(DocumentPath key);
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.service;
/**
* An exception to be thrown when a node cannot be removed normally because
* it does not exist or because it is not a leaf node.
*/
public class IllegalDocumentModificationException extends DocumentException {
public IllegalDocumentModificationException() {
}
public IllegalDocumentModificationException(String message) {
super(message);
}
public IllegalDocumentModificationException(String message,
Throwable cause) {
super(message, cause);
}
public IllegalDocumentModificationException(Throwable cause) {
super(cause);
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.service;
/**
* An exception thrown when an illegally named node is submitted.
*/
public class IllegalDocumentNameException extends DocumentException {
public IllegalDocumentNameException() {
}
public IllegalDocumentNameException(String message) {
super(message);
}
public IllegalDocumentNameException(String message, Throwable cause) {
super(message, cause);
}
public IllegalDocumentNameException(Throwable cause) {
super(cause);
}
}
\ No newline at end of file
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.service;
/**
* An exception to be thrown when an invalid path is passed to the
* {@code DocumentTree}.
*/
public class NoSuchDocumentPathException extends DocumentException {
public NoSuchDocumentPathException() {
}
public NoSuchDocumentPathException(String message) {
super(message);
}
public NoSuchDocumentPathException(String message, Throwable cause) {
super(message, cause);
}
public NoSuchDocumentPathException(Throwable cause) {
super(cause);
}
}