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