Bug 1258: Implement DataTree partial indexing
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / schema / tree / spi / ContainerNode.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
9
10 import java.util.HashMap;
11 import java.util.Map;
12
13 import org.opendaylight.yangtools.util.MapAdaptor;
14 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
15 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
16 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
17 import org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer;
18
19 import com.google.common.base.Optional;
20 import com.google.common.base.Preconditions;
21
22 /**
23  * A TreeNode capable of holding child nodes. The fact that any of the children
24  * changed is tracked by the subtree version.
25  */
26 final class ContainerNode extends AbstractTreeNode {
27     private final Map<PathArgument, TreeNode> children;
28     private final Version subtreeVersion;
29
30     protected ContainerNode(final NormalizedNode<?, ?> data, final Version version,
31             final Map<PathArgument, TreeNode> children, final Version subtreeVersion) {
32         super(data, version);
33         this.children = Preconditions.checkNotNull(children);
34         this.subtreeVersion = Preconditions.checkNotNull(subtreeVersion);
35     }
36
37     @Override
38     public Version getSubtreeVersion() {
39         return subtreeVersion;
40     }
41
42     @Override
43     public Optional<TreeNode> getChild(final PathArgument key) {
44         Optional<TreeNode> explicitNode = Optional.fromNullable(children.get(key));
45         if (explicitNode.isPresent()) {
46             return explicitNode;
47         }
48         final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> castedData = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) getData();
49         Optional<NormalizedNode<?, ?>> value = castedData.getChild(key);
50         if (value.isPresent()) {
51             //FIXME: consider caching created Tree Nodes.
52             //We are safe to not to cache them, since written Tree Nodes are in read only snapshot.
53             return Optional.of(TreeNodeFactory.createTreeNode(value.get(), getVersion()));
54         }
55         return Optional.absent();
56     }
57
58     @Override
59     public MutableTreeNode mutable() {
60         return new Mutable(this);
61     }
62
63     private static final class Mutable implements MutableTreeNode {
64         private final Version version;
65         private Map<PathArgument, TreeNode> children;
66         private NormalizedNode<?, ?> data;
67         private Version subtreeVersion;
68
69         private Mutable(final ContainerNode parent) {
70             this.data = parent.getData();
71             this.children = MapAdaptor.getDefaultInstance().takeSnapshot(parent.children);
72             this.subtreeVersion = parent.getSubtreeVersion();
73             this.version = parent.getVersion();
74             materializeChildVersion();
75         }
76
77         /**
78          * Traverse whole data tree and instantiate children for each data node. Set version of each MutableTreeNode
79          * accordingly to version in data node.
80          *
81          * Use this method if TreeNode is lazy initialized.
82          */
83         private void materializeChildVersion() {
84             Preconditions.checkState(data instanceof NormalizedNodeContainer);
85             NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> castedData = (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) data;
86
87             for(NormalizedNode<?, ?> childData : castedData.getValue()) {
88                 PathArgument id = childData.getIdentifier();
89
90                 if (!children.containsKey(id)) {
91                     children.put(id, TreeNodeFactory.createTreeNode(childData, version));
92                 }
93             }
94         }
95
96         @Override
97         public Optional<TreeNode> getChild(final PathArgument child) {
98             return Optional.fromNullable(children.get(child));
99         }
100
101         @Override
102         public void setSubtreeVersion(final Version subtreeVersion) {
103             this.subtreeVersion = Preconditions.checkNotNull(subtreeVersion);
104         }
105
106         @Override
107         public void addChild(final TreeNode child) {
108             children.put(child.getIdentifier(), child);
109         }
110
111         @Override
112         public void removeChild(final PathArgument id) {
113             children.remove(id);
114         }
115
116         @Override
117         public TreeNode seal() {
118             final TreeNode ret = new ContainerNode(data, version, MapAdaptor.getDefaultInstance().optimize(children), subtreeVersion);
119
120             // This forces a NPE if this class is accessed again. Better than corruption.
121             children = null;
122             return ret;
123         }
124
125         @Override
126         public void setData(final NormalizedNode<?, ?> data) {
127             this.data = Preconditions.checkNotNull(data);
128         }
129     }
130
131     /**
132      * Method creates and returns Container root Node and whole subtree for each child node specified in children nodes.
133      * <br>
134      * Reason why is method used recursively is that for each child in children nodes there is call to
135      * {@link TreeNodeFactory#createTreeNodeRecursively}. Each call to <code>createTreeNodeRecursively</code>
136      * calls either {@link #createNormalizedNodeRecursively} or {@link #createOrderedNodeRecursively}
137      * which depends on type of child node.
138      * <br> The root node that is returned holds reference to data node and whole subtree of children also containing references
139      * to data nodes.
140      *
141      * @param version version of indexed data
142      * @param data reference to data node
143      * @param children direct children of root node that is being created
144      * @return Root node with reference to data node and whole subtree of child nodes
145      */
146     private static ContainerNode createNodeRecursively(final Version version, final NormalizedNode<?, ?> data,
147         final Iterable<NormalizedNode<?, ?>> children) {
148
149         final Map<PathArgument, TreeNode> map = new HashMap<>();
150         for (NormalizedNode<?, ?> child : children) {
151             map.put(child.getIdentifier(), TreeNodeFactory.createTreeNodeRecursively(child, version));
152         }
153
154         return new ContainerNode(data, version, map, version);
155     }
156
157     /**
158      * Method creates and returns Normalized Node Container as root and recursively creates whole subtree
159      * from all of the container child iterables stored in {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer#getValue()}
160      * <br>
161      * The reason why is this method called recursively is that in background method calls {@link TreeNodeFactory#createTreeNodeRecursively}
162      * for each child stored in NormalizedNode and after each child is created the method calls again {@link #createNormalizedNodeRecursively} method
163      * until all of the children are resolved.
164      *
165      * @param version version of indexed data
166      * @param container Normalized Node Container
167      * @return Normalized Node Container as root and all whole subtree created from container iterables.
168      */
169     public static ContainerNode createNormalizedNodeRecursively(final Version version,
170         final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> container) {
171         return createNodeRecursively(version, container, container.getValue());
172     }
173
174     /**
175      * Method creates and returns Ordered Node Container as root and recursively creates whole subtree
176      * from all of the container child iterables stored in {@link org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer#getValue()}
177      * <br>
178      * The reason why is this method called recursively is that in background method calls {@link TreeNodeFactory#createTreeNodeRecursively}
179      * for each child stored in NormalizedNode and after each child is created the method calls again {@link #createNormalizedNodeRecursively} method
180      * until all of the children are resolved.
181      *
182      * @param version version of indexed data
183      * @param container Ordered Node Container
184      * @return Normalized Ordered Container as root and all whole subtree created from container iterables.
185      */
186     public static ContainerNode createOrderedNodeRecursively(final Version version,
187         final OrderedNodeContainer<NormalizedNode<?, ?>> container) {
188         return createNodeRecursively(version, container, container.getValue());
189     }
190
191     /**
192      * Creates and returns single instance of Normalized Node Container with provided version and data reference stored in NormalizedNodeContainer.
193      *
194      * @param version version of indexed data
195      * @param container Normalized Node Container
196      * @return single instance of Normalized node with provided version and data reference stored in NormalizedNodeContainer
197      */
198     public static ContainerNode createNormalizedNode(final Version version,
199         final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> container) {
200         return createNode(version, container);
201     }
202
203     /**
204      * Creates and returns single instance of Ordered Node Container with provided version and data reference stored in OrderedNodeContainer.
205      *
206      * @param version version of indexed data
207      * @param container Ordered Node Container
208      * @return single instance of Ordered Node Container with provided version and data reference stored in OrderedNodeContainer.
209      */
210     public static ContainerNode createOrderedNode(final Version version,
211         final OrderedNodeContainer<NormalizedNode<?, ?>> container) {
212         return createNode(version, container);
213     }
214
215     /**
216      * Creates and returns single instance of {@link ContainerNode} with provided version and data reference stored in NormalizedNode.
217      *
218      * @param version version of indexed data
219      * @param data NormalizedNode data container
220      * @return single instance of {@link ContainerNode} with provided version and data reference stored in NormalizedNode.
221      */
222     private static ContainerNode createNode(final Version version, final NormalizedNode<?, ?> data) {
223         final Map<PathArgument, TreeNode> map = new HashMap<>();
224         return new ContainerNode(data, version, map, version);
225     }
226 }