Merge "Fix checkstyle if-statements must use braces in yang-model-util"
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / tree / ModifiedNode.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.impl.schema.tree;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Predicate;
13 import java.util.HashMap;
14 import javax.annotation.Nonnull;
15 import org.opendaylight.yangtools.concepts.Identifiable;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
17 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
19 import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
21
22 import javax.annotation.concurrent.GuardedBy;
23 import java.util.LinkedHashMap;
24 import java.util.Map;
25
26 /**
27  * Node Modification Node and Tree
28  *
29  * Tree which structurally resembles data tree and captures client modifications
30  * to the data store tree.
31  *
32  * This tree is lazily created and populated via {@link #modifyChild(PathArgument)}
33  * and {@link StoreMetadataNode} which represents original state {@link #getOriginal()}.
34  */
35 final class ModifiedNode implements StoreTreeNode<ModifiedNode>, Identifiable<PathArgument>, NodeModification {
36
37     public static final Predicate<ModifiedNode> IS_TERMINAL_PREDICATE = new Predicate<ModifiedNode>() {
38         @Override
39         public boolean apply(final @Nonnull ModifiedNode input) {
40             Preconditions.checkNotNull(input);
41             switch (input.getType()) {
42             case DELETE:
43             case MERGE:
44             case WRITE:
45                 return true;
46             case SUBTREE_MODIFIED:
47             case UNMODIFIED:
48                 return false;
49             }
50
51             throw new IllegalArgumentException(String.format("Unhandled modification type %s", input.getType()));
52         }
53     };
54
55     private final Map<PathArgument, ModifiedNode> children;
56     private final Optional<TreeNode> original;
57     private final PathArgument identifier;
58     private ModificationType modificationType = ModificationType.UNMODIFIED;
59     private Optional<TreeNode> snapshotCache;
60     private NormalizedNode<?, ?> value;
61
62     private ModifiedNode(final PathArgument identifier, final Optional<TreeNode> original, boolean isOrdered) {
63         this.identifier = identifier;
64         this.original = original;
65
66         if (isOrdered) {
67             children = new LinkedHashMap<>();
68         } else {
69             children = new HashMap<>();
70         }
71     }
72
73     /**
74      *
75      *
76      * @return
77      */
78     public NormalizedNode<?, ?> getWrittenValue() {
79         return value;
80     }
81
82     @Override
83     public PathArgument getIdentifier() {
84         return identifier;
85     }
86
87     /**
88      *
89      * Returns original store metadata
90      * @return original store metadata
91      */
92     @Override
93     public Optional<TreeNode> getOriginal() {
94         return original;
95     }
96
97     /**
98      * Returns modification type
99      *
100      * @return modification type
101      */
102     @Override
103     public ModificationType getType() {
104         return modificationType;
105     }
106
107     /**
108      *
109      * Returns child modification if child was modified
110      *
111      * @return Child modification if direct child or it's subtree
112      *  was modified.
113      *
114      */
115     @Override
116     public Optional<ModifiedNode> getChild(final PathArgument child) {
117         return Optional.<ModifiedNode> fromNullable(children.get(child));
118     }
119
120     /**
121      *
122      * Returns child modification if child was modified, creates {@link org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ModifiedNode}
123      * for child otherwise.
124      *
125      * If this node's {@link ModificationType} is {@link ModificationType#UNMODIFIED}
126      * changes modification type to {@link ModificationType#SUBTREE_MODIFIED}
127      *
128      * @param child
129      * @return {@link org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ModifiedNode} for specified child, with {@link #getOriginal()}
130      *         containing child metadata if child was present in original data.
131      */
132     public ModifiedNode modifyChild(final PathArgument child, boolean isOrdered) {
133         clearSnapshot();
134         if (modificationType == ModificationType.UNMODIFIED) {
135             updateModificationType(ModificationType.SUBTREE_MODIFIED);
136         }
137         final ModifiedNode potential = children.get(child);
138         if (potential != null) {
139             return potential;
140         }
141
142         final Optional<TreeNode> currentMetadata;
143         if (original.isPresent()) {
144             final TreeNode orig = original.get();
145             currentMetadata = orig.getChild(child);
146         } else {
147             currentMetadata = Optional.absent();
148         }
149
150         ModifiedNode newlyCreated = new ModifiedNode(child, currentMetadata, isOrdered);
151         children.put(child, newlyCreated);
152         return newlyCreated;
153     }
154
155     /**
156      *
157      * Returns all recorded direct child modification
158      *
159      * @return all recorded direct child modifications
160      */
161     @Override
162     public Iterable<ModifiedNode> getChildren() {
163         return children.values();
164     }
165
166     /**
167      *
168      * Records a delete for associated node.
169      *
170      */
171     public void delete() {
172         final ModificationType newType;
173
174         switch (modificationType) {
175         case DELETE:
176         case UNMODIFIED:
177             // We need to record this delete.
178             newType = ModificationType.DELETE;
179             break;
180         case MERGE:
181         case SUBTREE_MODIFIED:
182         case WRITE:
183             /*
184              * We are canceling a previous modification. This is a bit tricky,
185              * as the original write may have just introduced the data, or it
186              * may have modified it.
187              *
188              * As documented in BUG-2470, a delete of data introduced in this
189              * transaction needs to be turned into a no-op.
190              */
191             newType = original.isPresent() ? ModificationType.DELETE : ModificationType.UNMODIFIED;
192             break;
193         default:
194             throw new IllegalStateException("Unhandled deletion of node with " + modificationType);
195         }
196
197         clearSnapshot();
198         children.clear();
199         this.value = null;
200         updateModificationType(newType);
201     }
202
203     /**
204      *
205      * Records a write for associated node.
206      *
207      * @param value
208      */
209     public void write(final NormalizedNode<?, ?> value) {
210         clearSnapshot();
211         updateModificationType(ModificationType.WRITE);
212         children.clear();
213         this.value = value;
214     }
215
216     public void merge(final NormalizedNode<?, ?> data) {
217         clearSnapshot();
218         updateModificationType(ModificationType.MERGE);
219         // FIXME: Probably merge with previous value.
220         this.value = data;
221     }
222
223     void seal() {
224         clearSnapshot();
225         for (ModifiedNode child : children.values()) {
226             child.seal();
227         }
228     }
229
230     private void clearSnapshot() {
231         snapshotCache = null;
232     }
233
234     public Optional<TreeNode> storeSnapshot(final Optional<TreeNode> snapshot) {
235         snapshotCache = snapshot;
236         return snapshot;
237     }
238
239     public Optional<Optional<TreeNode>> getSnapshotCache() {
240         return Optional.fromNullable(snapshotCache);
241     }
242
243     @GuardedBy("this")
244     private void updateModificationType(final ModificationType type) {
245         modificationType = type;
246         clearSnapshot();
247     }
248
249     @Override
250     public String toString() {
251         return "NodeModification [identifier=" + identifier + ", modificationType="
252                 + modificationType + ", childModification=" + children + "]";
253     }
254
255     public static ModifiedNode createUnmodified(final TreeNode metadataTree, boolean isOrdered) {
256         return new ModifiedNode(metadataTree.getIdentifier(), Optional.of(metadataTree), isOrdered);
257     }
258 }