Updates to DocumentTreeNode + Simple implementation of DocumentTree interface
Change-Id: Icc162201a50de8ae48abdb8e769fb6ed86138a03
Showing
6 changed files
with
503 additions
and
91 deletions
| ... | @@ -16,6 +16,7 @@ | ... | @@ -16,6 +16,7 @@ |
| 16 | 16 | ||
| 17 | package org.onosproject.store.service; | 17 | package org.onosproject.store.service; |
| 18 | 18 | ||
| 19 | +import java.util.Arrays; | ||
| 19 | import java.util.Iterator; | 20 | import java.util.Iterator; |
| 20 | import java.util.List; | 21 | import java.util.List; |
| 21 | import java.util.Objects; | 22 | import java.util.Objects; |
| ... | @@ -71,6 +72,16 @@ public class DocumentPath implements Comparable<DocumentPath> { | ... | @@ -71,6 +72,16 @@ public class DocumentPath implements Comparable<DocumentPath> { |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | /** | 74 | /** |
| 75 | + * Creates a new {@code DocumentPath} from a period delimited path string. | ||
| 76 | + * | ||
| 77 | + * @param path path string | ||
| 78 | + * @return {@code DocumentPath} instance | ||
| 79 | + */ | ||
| 80 | + public static DocumentPath from(String path) { | ||
| 81 | + return new DocumentPath(Arrays.asList(path.split("\\."))); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + /** | ||
| 74 | * Returns a path for the parent of this node. | 85 | * Returns a path for the parent of this node. |
| 75 | * | 86 | * |
| 76 | * @return parent node path. If this path is for the root, returns {@code null}. | 87 | * @return parent node path. If this path is for the root, returns {@code null}. | ... | ... |
| ... | @@ -16,15 +16,16 @@ | ... | @@ -16,15 +16,16 @@ |
| 16 | 16 | ||
| 17 | package org.onosproject.store.service; | 17 | package org.onosproject.store.service; |
| 18 | 18 | ||
| 19 | -import java.util.Iterator; | 19 | +import java.util.Map; |
| 20 | 20 | ||
| 21 | -import org.onosproject.store.primitives.DocumentTreeNode; | 21 | +import javax.annotation.concurrent.NotThreadSafe; |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | * A hierarchical <a href="https://en.wikipedia.org/wiki/Document_Object_Model">document tree</a> data structure. | 24 | * A hierarchical <a href="https://en.wikipedia.org/wiki/Document_Object_Model">document tree</a> data structure. |
| 25 | * | 25 | * |
| 26 | * @param <V> document tree value type | 26 | * @param <V> document tree value type |
| 27 | */ | 27 | */ |
| 28 | +@NotThreadSafe | ||
| 28 | public interface DocumentTree<V> { | 29 | public interface DocumentTree<V> { |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| ... | @@ -35,13 +36,13 @@ public interface DocumentTree<V> { | ... | @@ -35,13 +36,13 @@ public interface DocumentTree<V> { |
| 35 | DocumentPath root(); | 36 | DocumentPath root(); |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | - * Returns an iterator for all the first order descendants of a node. | 39 | + * Returns the child values for this node. |
| 39 | * | 40 | * |
| 40 | * @param path path to the node | 41 | * @param path path to the node |
| 41 | - * @return an iterator for the child nodes of the specified node path | 42 | + * @return mapping from a child name to its value |
| 42 | * @throws NoSuchDocumentPathException if the path does not point to a valid node | 43 | * @throws NoSuchDocumentPathException if the path does not point to a valid node |
| 43 | */ | 44 | */ |
| 44 | - Iterator<DocumentTreeNode<V>> getChildren(DocumentPath path); | 45 | + Map<String, Versioned<V>> getChildren(DocumentPath path); |
| 45 | 46 | ||
| 46 | /** | 47 | /** |
| 47 | * Returns a document tree node. | 48 | * Returns a document tree node. |
| ... | @@ -49,7 +50,7 @@ public interface DocumentTree<V> { | ... | @@ -49,7 +50,7 @@ public interface DocumentTree<V> { |
| 49 | * @param path path to node | 50 | * @param path path to node |
| 50 | * @return node value or {@code null} if path does not point to a valid node | 51 | * @return node value or {@code null} if path does not point to a valid node |
| 51 | */ | 52 | */ |
| 52 | - DocumentTreeNode<V> getNode(DocumentPath path); | 53 | + Versioned<V> get(DocumentPath path); |
| 53 | 54 | ||
| 54 | /** | 55 | /** |
| 55 | * Creates or updates a document tree node. | 56 | * Creates or updates a document tree node. |
| ... | @@ -59,7 +60,7 @@ public interface DocumentTree<V> { | ... | @@ -59,7 +60,7 @@ public interface DocumentTree<V> { |
| 59 | * @return the previous mapping or {@code null} if there was no previous mapping | 60 | * @return the previous mapping or {@code null} if there was no previous mapping |
| 60 | * @throws NoSuchDocumentPathException if the parent node (for the node to create/update) does not exist | 61 | * @throws NoSuchDocumentPathException if the parent node (for the node to create/update) does not exist |
| 61 | */ | 62 | */ |
| 62 | - V putNode(DocumentPath path, V value); | 63 | + Versioned<V> set(DocumentPath path, V value); |
| 63 | 64 | ||
| 64 | /** | 65 | /** |
| 65 | * Creates a document tree node if one does not exist already. | 66 | * Creates a document tree node if one does not exist already. |
| ... | @@ -69,7 +70,7 @@ public interface DocumentTree<V> { | ... | @@ -69,7 +70,7 @@ public interface DocumentTree<V> { |
| 69 | * @return returns {@code true} if the mapping could be added successfully, {@code false} otherwise | 70 | * @return returns {@code true} if the mapping could be added successfully, {@code false} otherwise |
| 70 | * @throws NoSuchDocumentPathException if the parent node (for the node to create) does not exist | 71 | * @throws NoSuchDocumentPathException if the parent node (for the node to create) does not exist |
| 71 | */ | 72 | */ |
| 72 | - boolean createNode(DocumentPath path, V value); | 73 | + boolean create(DocumentPath path, V value); |
| 73 | 74 | ||
| 74 | /** | 75 | /** |
| 75 | * Conditionally updates a tree node if the current version matches a specified version. | 76 | * Conditionally updates a tree node if the current version matches a specified version. |
| ... | @@ -77,7 +78,7 @@ public interface DocumentTree<V> { | ... | @@ -77,7 +78,7 @@ public interface DocumentTree<V> { |
| 77 | * @param path path for the node to create | 78 | * @param path path for the node to create |
| 78 | * @param newValue the non-null value to be associated with the key | 79 | * @param newValue the non-null value to be associated with the key |
| 79 | * @param version current version of the value for update to occur | 80 | * @param version current version of the value for update to occur |
| 80 | - * @return returns {@code true} if the update was made, {@code false} otherwise | 81 | + * @return returns {@code true} if the update was made and the tree was modified, {@code false} otherwise |
| 81 | * @throws NoSuchDocumentPathException if the parent node (for the node to create) does not exist | 82 | * @throws NoSuchDocumentPathException if the parent node (for the node to create) does not exist |
| 82 | */ | 83 | */ |
| 83 | boolean replace(DocumentPath path, V newValue, long version); | 84 | boolean replace(DocumentPath path, V newValue, long version); |
| ... | @@ -88,7 +89,8 @@ public interface DocumentTree<V> { | ... | @@ -88,7 +89,8 @@ public interface DocumentTree<V> { |
| 88 | * @param path path for the node to create | 89 | * @param path path for the node to create |
| 89 | * @param newValue the non-null value to be associated with the key | 90 | * @param newValue the non-null value to be associated with the key |
| 90 | * @param currentValue current value for update to occur | 91 | * @param currentValue current value for update to occur |
| 91 | - * @return returns {@code true} if the update was made, {@code false} otherwise | 92 | + * @return returns {@code true} if the update was made and the tree was modified, {@code false} otherwise. |
| 93 | + * This method returns {@code false} if the newValue and currentValue are same. | ||
| 92 | * @throws NoSuchDocumentPathException if the parent node (for the node to create) does not exist | 94 | * @throws NoSuchDocumentPathException if the parent node (for the node to create) does not exist |
| 93 | */ | 95 | */ |
| 94 | boolean replace(DocumentPath path, V newValue, V currentValue); | 96 | boolean replace(DocumentPath path, V newValue, V currentValue); |
| ... | @@ -96,12 +98,11 @@ public interface DocumentTree<V> { | ... | @@ -96,12 +98,11 @@ public interface DocumentTree<V> { |
| 96 | /** | 98 | /** |
| 97 | * Removes the node with the specified path. | 99 | * Removes the node with the specified path. |
| 98 | * | 100 | * |
| 99 | - * is not a leaf node i.e has one or more children | 101 | + * @param path path for the node to remove |
| 100 | - * @param key path for the node to remove | ||
| 101 | * @return the previous value of the node or {@code null} if it did not exist | 102 | * @return the previous value of the node or {@code null} if it did not exist |
| 102 | * @throws IllegalDocumentModificationException if the remove to be removed | 103 | * @throws IllegalDocumentModificationException if the remove to be removed |
| 103 | */ | 104 | */ |
| 104 | - V removeNode(DocumentPath key); | 105 | + Versioned<V> removeNode(DocumentPath path); |
| 105 | 106 | ||
| 106 | /** | 107 | /** |
| 107 | * Registers a listener to be notified when a subtree rooted at the specified path | 108 | * Registers a listener to be notified when a subtree rooted at the specified path | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package org.onosproject.store.service; | ||
| 18 | + | ||
| 19 | +import java.util.Iterator; | ||
| 20 | + | ||
| 21 | +import javax.annotation.concurrent.NotThreadSafe; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * A {@code DocumentTree} node. | ||
| 25 | + * | ||
| 26 | + * @param <V> value type | ||
| 27 | + */ | ||
| 28 | +@NotThreadSafe | ||
| 29 | +public interface DocumentTreeNode<V> { | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * Returns the path to this node in a {@code DocumentTree}. | ||
| 33 | + * | ||
| 34 | + * @return absolute path | ||
| 35 | + */ | ||
| 36 | + DocumentPath path(); | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * Returns the value of this node. | ||
| 40 | + * | ||
| 41 | + * @return node value (and version) | ||
| 42 | + */ | ||
| 43 | + Versioned<V> value(); | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * Returns the children of this node. | ||
| 47 | + * | ||
| 48 | + * @return iterator for this node's children | ||
| 49 | + */ | ||
| 50 | + Iterator<DocumentTreeNode<V>> children(); | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * Returns the child node of this node with the specified relative path name. | ||
| 54 | + * | ||
| 55 | + * @param relativePath relative path name for the child node. | ||
| 56 | + * @return child node; this method returns {@code null} if no such child exists | ||
| 57 | + */ | ||
| 58 | + DocumentTreeNode<V> child(String relativePath); | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Returns if this node has one or more children. | ||
| 62 | + * @return {@code true} if yes, {@code false} otherwise | ||
| 63 | + */ | ||
| 64 | + default boolean hasChildren() { | ||
| 65 | + return children().hasNext(); | ||
| 66 | + } | ||
| 67 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package org.onosproject.store.primitives.resources.impl; | ||
| 18 | + | ||
| 19 | +import java.util.Iterator; | ||
| 20 | +import java.util.Map; | ||
| 21 | +import java.util.Objects; | ||
| 22 | +import java.util.concurrent.atomic.AtomicInteger; | ||
| 23 | + | ||
| 24 | +import org.onosproject.store.service.DocumentPath; | ||
| 25 | +import org.onosproject.store.service.DocumentTree; | ||
| 26 | +import org.onosproject.store.service.DocumentTreeListener; | ||
| 27 | +import org.onosproject.store.service.DocumentTreeNode; | ||
| 28 | +import org.onosproject.store.service.IllegalDocumentModificationException; | ||
| 29 | +import org.onosproject.store.service.NoSuchDocumentPathException; | ||
| 30 | +import org.onosproject.store.service.Versioned; | ||
| 31 | + | ||
| 32 | +import com.google.common.base.Preconditions; | ||
| 33 | +import com.google.common.collect.Maps; | ||
| 34 | + | ||
| 35 | +/** | ||
| 36 | + * Simple implementation of a {@link DocumentTree}. | ||
| 37 | + * | ||
| 38 | + * @param <V> tree node value type | ||
| 39 | + */ | ||
| 40 | +public class DefaultDocumentTree<V> implements DocumentTree<V> { | ||
| 41 | + | ||
| 42 | + private static final DocumentPath ROOT_PATH = DocumentPath.from("root"); | ||
| 43 | + private final DefaultDocumentTreeNode<V> root; | ||
| 44 | + private final AtomicInteger versionCounter = new AtomicInteger(0); | ||
| 45 | + | ||
| 46 | + public DefaultDocumentTree() { | ||
| 47 | + root = new DefaultDocumentTreeNode<V>(ROOT_PATH, null, nextVersion(), null); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @Override | ||
| 51 | + public DocumentPath root() { | ||
| 52 | + return ROOT_PATH; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + public Map<String, Versioned<V>> getChildren(DocumentPath path) { | ||
| 57 | + DocumentTreeNode<V> node = getNode(path); | ||
| 58 | + if (node != null) { | ||
| 59 | + Map<String, Versioned<V>> childrenValues = Maps.newHashMap(); | ||
| 60 | + node.children().forEachRemaining(n -> childrenValues.put(simpleName(n.path()), n.value())); | ||
| 61 | + return childrenValues; | ||
| 62 | + } | ||
| 63 | + throw new NoSuchDocumentPathException(); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + @Override | ||
| 67 | + public Versioned<V> get(DocumentPath path) { | ||
| 68 | + DocumentTreeNode<V> currentNode = getNode(path); | ||
| 69 | + return currentNode != null ? currentNode.value() : null; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Override | ||
| 73 | + public Versioned<V> set(DocumentPath path, V value) { | ||
| 74 | + checkRootModification(path); | ||
| 75 | + DefaultDocumentTreeNode<V> node = getNode(path); | ||
| 76 | + if (node != null) { | ||
| 77 | + return node.update(value, nextVersion()); | ||
| 78 | + } else { | ||
| 79 | + create(path, value); | ||
| 80 | + return null; | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Override | ||
| 85 | + public boolean create(DocumentPath path, V value) { | ||
| 86 | + checkRootModification(path); | ||
| 87 | + DocumentTreeNode<V> node = getNode(path); | ||
| 88 | + if (node != null) { | ||
| 89 | + return false; | ||
| 90 | + } | ||
| 91 | + DocumentPath parentPath = path.parent(); | ||
| 92 | + DefaultDocumentTreeNode<V> parentNode = getNode(parentPath); | ||
| 93 | + if (parentNode == null) { | ||
| 94 | + throw new IllegalDocumentModificationException(); | ||
| 95 | + } | ||
| 96 | + parentNode.addChild(simpleName(path), value, nextVersion()); | ||
| 97 | + return true; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @Override | ||
| 101 | + public boolean replace(DocumentPath path, V newValue, long version) { | ||
| 102 | + checkRootModification(path); | ||
| 103 | + DocumentTreeNode<V> node = getNode(path); | ||
| 104 | + if (node != null && node.value() != null && node.value().version() == version) { | ||
| 105 | + if (!Objects.equals(newValue, node.value().value())) { | ||
| 106 | + set(path, newValue); | ||
| 107 | + return true; | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + return false; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + @Override | ||
| 114 | + public boolean replace(DocumentPath path, V newValue, V currentValue) { | ||
| 115 | + checkRootModification(path); | ||
| 116 | + if (Objects.equals(newValue, currentValue)) { | ||
| 117 | + return false; | ||
| 118 | + } | ||
| 119 | + DocumentTreeNode<V> node = getNode(path); | ||
| 120 | + if (node != null && Objects.equals(Versioned.valueOrNull(node.value()), currentValue)) { | ||
| 121 | + set(path, newValue); | ||
| 122 | + return true; | ||
| 123 | + } | ||
| 124 | + return false; | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + @Override | ||
| 128 | + public Versioned<V> removeNode(DocumentPath path) { | ||
| 129 | + checkRootModification(path); | ||
| 130 | + DefaultDocumentTreeNode<V> nodeToRemove = getNode(path); | ||
| 131 | + if (nodeToRemove == null) { | ||
| 132 | + throw new NoSuchDocumentPathException(); | ||
| 133 | + } | ||
| 134 | + if (nodeToRemove.hasChildren()) { | ||
| 135 | + throw new IllegalDocumentModificationException(); | ||
| 136 | + } | ||
| 137 | + DefaultDocumentTreeNode<V> parent = (DefaultDocumentTreeNode<V>) nodeToRemove.parent(); | ||
| 138 | + parent.removeChild(simpleName(path)); | ||
| 139 | + return nodeToRemove.value(); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @Override | ||
| 143 | + public void addListener(DocumentPath path, DocumentTreeListener<V> listener) { | ||
| 144 | + // TODO Auto-generated method stub | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + @Override | ||
| 148 | + public void removeListener(DocumentTreeListener<V> listener) { | ||
| 149 | + // TODO Auto-generated method stub | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + private DefaultDocumentTreeNode<V> getNode(DocumentPath path) { | ||
| 153 | + Iterator<String> pathElements = path.pathElements().iterator(); | ||
| 154 | + DefaultDocumentTreeNode<V> currentNode = root; | ||
| 155 | + Preconditions.checkState("root".equals(pathElements.next()), "Path should start with root"); | ||
| 156 | + while (pathElements.hasNext() && currentNode != null) { | ||
| 157 | + currentNode = (DefaultDocumentTreeNode<V>) currentNode.child(pathElements.next()); | ||
| 158 | + } | ||
| 159 | + return currentNode; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + private long nextVersion() { | ||
| 163 | + return versionCounter.incrementAndGet(); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + private String simpleName(DocumentPath path) { | ||
| 167 | + return path.pathElements().get(path.pathElements().size() - 1); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + private void checkRootModification(DocumentPath path) { | ||
| 171 | + if (ROOT_PATH.equals(path)) { | ||
| 172 | + throw new IllegalDocumentModificationException(); | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | +} |
| ... | @@ -14,121 +14,105 @@ | ... | @@ -14,121 +14,105 @@ |
| 14 | * limitations under the License. | 14 | * limitations under the License. |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | -package org.onosproject.store.primitives; | 17 | +package org.onosproject.store.primitives.resources.impl; |
| 18 | 18 | ||
| 19 | import static com.google.common.base.Preconditions.checkNotNull; | 19 | import static com.google.common.base.Preconditions.checkNotNull; |
| 20 | 20 | ||
| 21 | -import java.util.Comparator; | ||
| 22 | import java.util.Iterator; | 21 | import java.util.Iterator; |
| 23 | import java.util.Objects; | 22 | import java.util.Objects; |
| 24 | -import java.util.TreeSet; | 23 | +import java.util.TreeMap; |
| 25 | 24 | ||
| 26 | import org.onosproject.store.service.DocumentPath; | 25 | import org.onosproject.store.service.DocumentPath; |
| 26 | +import org.onosproject.store.service.DocumentTreeNode; | ||
| 27 | +import org.onosproject.store.service.Versioned; | ||
| 27 | 28 | ||
| 28 | import com.google.common.base.MoreObjects; | 29 | import com.google.common.base.MoreObjects; |
| 30 | +import com.google.common.collect.ImmutableList; | ||
| 31 | +import com.google.common.collect.Maps; | ||
| 29 | import com.google.common.collect.Sets; | 32 | import com.google.common.collect.Sets; |
| 30 | 33 | ||
| 31 | /** | 34 | /** |
| 32 | * A {@code DocumentTree} node. | 35 | * A {@code DocumentTree} node. |
| 33 | */ | 36 | */ |
| 34 | -public class DocumentTreeNode<V> { | 37 | +public class DefaultDocumentTreeNode<V> implements DocumentTreeNode<V> { |
| 35 | private final DocumentPath key; | 38 | private final DocumentPath key; |
| 36 | - private V value; | 39 | + private Versioned<V> value; |
| 37 | - private long version; | 40 | + private final TreeMap<String, DocumentTreeNode<V>> children = Maps.newTreeMap(); |
| 38 | - private final TreeSet<DocumentTreeNode<V>> children = | ||
| 39 | - Sets.newTreeSet(new Comparator<DocumentTreeNode<V>>() { | ||
| 40 | - @Override | ||
| 41 | - public int compare(DocumentTreeNode<V> o1, | ||
| 42 | - DocumentTreeNode<V> o2) { | ||
| 43 | - return o1.getKey().compareTo(o2.getKey()); | ||
| 44 | - } | ||
| 45 | - }); | ||
| 46 | private final DocumentTreeNode<V> parent; | 41 | private final DocumentTreeNode<V> parent; |
| 47 | 42 | ||
| 48 | - public DocumentTreeNode(DocumentPath key, | 43 | + public DefaultDocumentTreeNode(DocumentPath key, |
| 49 | V value, | 44 | V value, |
| 50 | long version, | 45 | long version, |
| 51 | DocumentTreeNode<V> parent) { | 46 | DocumentTreeNode<V> parent) { |
| 52 | this.key = checkNotNull(key); | 47 | this.key = checkNotNull(key); |
| 53 | - this.value = checkNotNull(value); | 48 | + this.value = new Versioned<>(value, version); |
| 54 | - this.version = version; | ||
| 55 | this.parent = parent; | 49 | this.parent = parent; |
| 56 | } | 50 | } |
| 57 | 51 | ||
| 58 | - /** | 52 | + @Override |
| 59 | - * Returns this node's key. | 53 | + public DocumentPath path() { |
| 60 | - * | ||
| 61 | - * @return the key | ||
| 62 | - */ | ||
| 63 | - public DocumentPath getKey() { | ||
| 64 | return key; | 54 | return key; |
| 65 | } | 55 | } |
| 66 | 56 | ||
| 67 | - /** | 57 | + @Override |
| 68 | - * Returns this node's value. | 58 | + public Versioned<V> value() { |
| 69 | - * | ||
| 70 | - * @return the value | ||
| 71 | - */ | ||
| 72 | - public V getValue() { | ||
| 73 | return value; | 59 | return value; |
| 74 | } | 60 | } |
| 75 | 61 | ||
| 76 | - /** | 62 | + @Override |
| 77 | - * Returns this node's version. | 63 | + public Iterator<DocumentTreeNode<V>> children() { |
| 78 | - * | 64 | + return ImmutableList.copyOf(children.values()).iterator(); |
| 79 | - * @return the version | ||
| 80 | - */ | ||
| 81 | - public long getVersion() { | ||
| 82 | - return version; | ||
| 83 | } | 65 | } |
| 84 | 66 | ||
| 85 | - /** | 67 | + @Override |
| 86 | - * Updates this node. | 68 | + public DocumentTreeNode<V> child(String name) { |
| 87 | - * | 69 | + return children.get(name); |
| 88 | - * @param newValue new value to be set | ||
| 89 | - * @param newVersion new version to be set | ||
| 90 | - */ | ||
| 91 | - public void update(V newValue, long newVersion) { | ||
| 92 | - this.value = newValue; | ||
| 93 | - this.version = newVersion; | ||
| 94 | } | 70 | } |
| 95 | 71 | ||
| 96 | - /** | 72 | + |
| 97 | - * Returns a collection of the children of this node. | 73 | + public DocumentTreeNode<V> parent() { |
| 98 | - * | 74 | + return parent; |
| 99 | - * @return iterator for the children of this node. | ||
| 100 | - */ | ||
| 101 | - public Iterator<DocumentTreeNode<V>> getChildren() { | ||
| 102 | - return children.iterator(); | ||
| 103 | } | 75 | } |
| 104 | 76 | ||
| 105 | /** | 77 | /** |
| 106 | - * Adds a child to this node. | 78 | + * Adds a new child only if one does not exist with the name. |
| 107 | - * | 79 | + * @param name relative path name of the child node |
| 108 | - * @param child the child node to be added | 80 | + * @param newValue new value to set |
| 109 | - * @return {@code true} if the child set was modified as a result of this call, {@code false} otherwise | 81 | + * @param newVersion new version to set |
| 82 | + * @return previous value; can be {@code null} if no child currently exists with that relative path name. | ||
| 83 | + * a non null return value indicates child already exists and no modification occured. | ||
| 110 | */ | 84 | */ |
| 111 | - public boolean addChild(DocumentTreeNode<V> child) { | 85 | + public Versioned<V> addChild(String name, V newValue, long newVersion) { |
| 112 | - return children.add(child); | 86 | + DefaultDocumentTreeNode<V> child = (DefaultDocumentTreeNode<V>) children.get(name); |
| 87 | + if (child != null) { | ||
| 88 | + return child.value(); | ||
| 89 | + } | ||
| 90 | + children.put(name, new DefaultDocumentTreeNode<>(new DocumentPath(name, path()), newValue, newVersion, this)); | ||
| 91 | + return null; | ||
| 113 | } | 92 | } |
| 114 | 93 | ||
| 115 | /** | 94 | /** |
| 116 | - * Removes a child node. | 95 | + * Updates the node value. |
| 117 | * | 96 | * |
| 118 | - * @param child the child node to be removed | 97 | + * @param newValue new value to set |
| 119 | - * @return {@code true} if the child set was modified as a result of this call, {@code false} otherwise | 98 | + * @param newVersion new version to set |
| 99 | + * @return previous value | ||
| 120 | */ | 100 | */ |
| 121 | - public boolean removeChild(String child) { | 101 | + public Versioned<V> update(V newValue, long newVersion) { |
| 122 | - return children.remove(child); | 102 | + Versioned<V> previousValue = value; |
| 103 | + value = new Versioned<>(newValue, newVersion); | ||
| 104 | + return previousValue; | ||
| 123 | } | 105 | } |
| 124 | 106 | ||
| 107 | + | ||
| 125 | /** | 108 | /** |
| 126 | - * Returns the parent of this node. | 109 | + * Removes a child node. |
| 127 | * | 110 | * |
| 128 | - * @return the parent node of this node, which may be null | 111 | + * @param name the name of child node to be removed |
| 112 | + * @return {@code true} if the child set was modified as a result of this call, {@code false} otherwise | ||
| 129 | */ | 113 | */ |
| 130 | - public DocumentTreeNode<V> getParent() { | 114 | + public boolean removeChild(String name) { |
| 131 | - return parent; | 115 | + return children.remove(name) != null; |
| 132 | } | 116 | } |
| 133 | 117 | ||
| 134 | @Override | 118 | @Override |
| ... | @@ -138,16 +122,11 @@ public class DocumentTreeNode<V> { | ... | @@ -138,16 +122,11 @@ public class DocumentTreeNode<V> { |
| 138 | 122 | ||
| 139 | @Override | 123 | @Override |
| 140 | public boolean equals(Object obj) { | 124 | public boolean equals(Object obj) { |
| 141 | - if (obj instanceof DocumentTreeNode) { | 125 | + if (obj instanceof DefaultDocumentTreeNode) { |
| 142 | - DocumentTreeNode<V> that = (DocumentTreeNode<V>) obj; | 126 | + DefaultDocumentTreeNode<V> that = (DefaultDocumentTreeNode<V>) obj; |
| 143 | if (this.parent.equals(that.parent)) { | 127 | if (this.parent.equals(that.parent)) { |
| 144 | if (this.children.size() == that.children.size()) { | 128 | if (this.children.size() == that.children.size()) { |
| 145 | - for (DocumentTreeNode<V> child : this.children) { | 129 | + return Sets.symmetricDifference(this.children.keySet(), that.children.keySet()).isEmpty(); |
| 146 | - if (!that.children.contains(child)) { | ||
| 147 | - return false; | ||
| 148 | - } | ||
| 149 | - } | ||
| 150 | - return true; | ||
| 151 | } | 130 | } |
| 152 | } | 131 | } |
| 153 | } | 132 | } |
| ... | @@ -161,10 +140,9 @@ public class DocumentTreeNode<V> { | ... | @@ -161,10 +140,9 @@ public class DocumentTreeNode<V> { |
| 161 | .add("parent", this.parent) | 140 | .add("parent", this.parent) |
| 162 | .add("key", this.key) | 141 | .add("key", this.key) |
| 163 | .add("value", this.value); | 142 | .add("value", this.value); |
| 164 | - for (DocumentTreeNode<V> child : children) { | 143 | + for (DocumentTreeNode<V> child : children.values()) { |
| 165 | - helper = helper.add("child", child.key); | 144 | + helper = helper.add("child", child); |
| 166 | } | 145 | } |
| 167 | return helper.toString(); | 146 | return helper.toString(); |
| 168 | } | 147 | } |
| 169 | - | ||
| 170 | } | 148 | } | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package org.onosproject.store.primitives.resources.impl; | ||
| 18 | + | ||
| 19 | +import org.junit.Assert; | ||
| 20 | +import org.junit.Test; | ||
| 21 | +import org.onosproject.store.service.DocumentPath; | ||
| 22 | +import org.onosproject.store.service.DocumentTree; | ||
| 23 | +import org.onosproject.store.service.IllegalDocumentModificationException; | ||
| 24 | +import org.onosproject.store.service.NoSuchDocumentPathException; | ||
| 25 | +import org.onosproject.store.service.Versioned; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * Tests for {@code DefaultDocumentTree}. | ||
| 29 | + */ | ||
| 30 | +public class DefaultDocumentTreeTest { | ||
| 31 | + | ||
| 32 | + @Test | ||
| 33 | + public void testTreeConstructor() { | ||
| 34 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 35 | + Assert.assertEquals(tree.root(), path("root")); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Test | ||
| 39 | + public void testCreateNodeAtRoot() { | ||
| 40 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 41 | + Assert.assertTrue(tree.create(path("root.a"), "bar")); | ||
| 42 | + Assert.assertFalse(tree.create(path("root.a"), "baz")); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Test | ||
| 46 | + public void testCreateNodeAtNonRoot() { | ||
| 47 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 48 | + tree.create(path("root.a"), "bar"); | ||
| 49 | + Assert.assertTrue(tree.create(path("root.a.b"), "baz")); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Test(expected=IllegalDocumentModificationException.class) | ||
| 53 | + public void testCreateNodeFailure() { | ||
| 54 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 55 | + tree.create(path("root.a.b"), "bar"); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Test | ||
| 59 | + public void testGetRootValue() { | ||
| 60 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 61 | + tree.create(path("root.a"), "bar"); | ||
| 62 | + tree.create(path("root.a.b"), "baz"); | ||
| 63 | + Versioned<String> root = tree.get(path("root")); | ||
| 64 | + Assert.assertNotNull(root); | ||
| 65 | + Assert.assertNull(root.value()); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Test | ||
| 69 | + public void testGetInnerNode() { | ||
| 70 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 71 | + tree.create(path("root.a"), "bar"); | ||
| 72 | + tree.create(path("root.a.b"), "baz"); | ||
| 73 | + Versioned<String> nodeValue = tree.get(path("root.a")); | ||
| 74 | + Assert.assertNotNull(nodeValue); | ||
| 75 | + Assert.assertEquals("bar", nodeValue.value()); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Test | ||
| 79 | + public void testGetLeafNode() { | ||
| 80 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 81 | + tree.create(path("root.a"), "bar"); | ||
| 82 | + tree.create(path("root.a.b"), "baz"); | ||
| 83 | + Versioned<String> nodeValue = tree.get(path("root.a.b")); | ||
| 84 | + Assert.assertNotNull(nodeValue); | ||
| 85 | + Assert.assertEquals("baz", nodeValue.value()); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Test | ||
| 89 | + public void getMissingNode() { | ||
| 90 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 91 | + tree.create(path("root.a"), "bar"); | ||
| 92 | + tree.create(path("root.a.b"), "baz"); | ||
| 93 | + Assert.assertNull(tree.get(path("root.x"))); | ||
| 94 | + Assert.assertNull(tree.get(path("root.a.x"))); | ||
| 95 | + Assert.assertNull(tree.get(path("root.a.b.x"))); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + @Test | ||
| 99 | + public void testGetChildren() { | ||
| 100 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 101 | + tree.create(path("root.a"), "bar"); | ||
| 102 | + tree.create(path("root.a.b"), "alpha"); | ||
| 103 | + tree.create(path("root.a.c"), "beta"); | ||
| 104 | + Assert.assertEquals(2, tree.getChildren(path("root.a")).size()); | ||
| 105 | + Assert.assertEquals(0, tree.getChildren(path("root.a.b")).size()); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Test(expected=NoSuchDocumentPathException.class) | ||
| 109 | + public void testGetChildrenFailure() { | ||
| 110 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 111 | + tree.create(path("root.a"), "bar"); | ||
| 112 | + tree.getChildren(path("root.a.b")); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + @Test(expected=IllegalDocumentModificationException.class) | ||
| 116 | + public void testSetRootFailure() { | ||
| 117 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 118 | + tree.set(tree.root(), "bar"); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Test | ||
| 122 | + public void testSet() { | ||
| 123 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 124 | + tree.create(path("root.a"), "bar"); | ||
| 125 | + Assert.assertNull(tree.set(path("root.a.b"), "alpha")); | ||
| 126 | + Assert.assertEquals("alpha", tree.set(path("root.a.b"), "beta").value()); | ||
| 127 | + Assert.assertEquals("beta", tree.get(path("root.a.b")).value()); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @Test(expected=IllegalDocumentModificationException.class) | ||
| 131 | + public void testSetInvalidNode() { | ||
| 132 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 133 | + tree.set(path("root.a.b"), "alpha"); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + public void testReplaceWithVersion() { | ||
| 137 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 138 | + tree.create(path("root.a"), "bar"); | ||
| 139 | + tree.create(path("root.a.b"), "alpha"); | ||
| 140 | + Versioned<String> value = tree.get(path("root.a.b")); | ||
| 141 | + Assert.assertTrue(tree.replace(path("root.a.b"), "beta", value.version())); | ||
| 142 | + Assert.assertFalse(tree.replace(path("root.a.b"), "beta", value.version())); | ||
| 143 | + Assert.assertFalse(tree.replace(path("root.x"), "beta", 1)); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + public void testReplaceWithValue() { | ||
| 147 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 148 | + tree.create(path("root.a"), "bar"); | ||
| 149 | + tree.create(path("root.a.b"), "alpha"); | ||
| 150 | + Assert.assertTrue(tree.replace(path("root.a.b"), "beta", "alpha")); | ||
| 151 | + Assert.assertFalse(tree.replace(path("root.a.b"), "beta", "alpha")); | ||
| 152 | + Assert.assertFalse(tree.replace(path("root.x"), "beta", "bar")); | ||
| 153 | + Assert.assertTrue(tree.replace(path("root.x"), "beta", null)); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + @Test(expected=IllegalDocumentModificationException.class) | ||
| 157 | + public void testRemoveRoot() { | ||
| 158 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 159 | + tree.removeNode(tree.root()); | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + @Test | ||
| 163 | + public void testRemove() { | ||
| 164 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 165 | + tree.create(path("root.a"), "bar"); | ||
| 166 | + tree.create(path("root.a.b"), "alpha"); | ||
| 167 | + Assert.assertEquals("alpha", tree.removeNode(path("root.a.b")).value()); | ||
| 168 | + Assert.assertEquals(0, tree.getChildren(path("root.a")).size()); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + @Test(expected=NoSuchDocumentPathException.class) | ||
| 172 | + public void testRemoveInvalidNode() { | ||
| 173 | + DocumentTree<String> tree = new DefaultDocumentTree<>(); | ||
| 174 | + tree.removeNode(path("root.a")); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + private static DocumentPath path(String path) { | ||
| 178 | + return DocumentPath.from(path); | ||
| 179 | + } | ||
| 180 | +} |
-
Please register or login to post a comment