Implement edit-config operations for NormalizedNode yang-data-api
[yangtools.git] / yang / yang-data-operations / src / main / java / org / opendaylight / yangtools / yang / data / operations / AbstractContainerNodeModification.java
1 /*
2  * Copyright (c) 2013 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.operations;
9
10 import java.util.List;
11 import java.util.Set;
12
13 import org.opendaylight.yangtools.yang.common.QName;
14 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
15 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
16 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
19 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
23 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
25 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29
30 import com.google.common.base.Optional;
31 import com.google.common.base.Preconditions;
32 import com.google.common.collect.Lists;
33 import com.google.common.collect.Sets;
34
35 abstract class AbstractContainerNodeModification<S, N extends DataContainerNode<?>> implements Modification<S, N> {
36
37     @Override
38     public final Optional<N> modify(S schema, Optional<N> actual, Optional<N> modification,
39             OperationStack operationStack) throws DataModificationException {
40
41         operationStack.enteringNode(modification);
42
43         Optional<N> result;
44         QName nodeQName = getQName(schema);
45
46         switch (operationStack.getCurrentOperation()) {
47         case DELETE: {
48             DataModificationException.DataMissingException.check(nodeQName, actual);
49         }
50         case REMOVE: {
51             result = Optional.absent();
52             break;
53         }
54         case CREATE: {
55             DataModificationException.DataExistsException.check(nodeQName, actual, null);
56         }
57         case REPLACE: {
58             result = modification;
59             break;
60         }
61         case NONE: {
62             DataModificationException.DataMissingException.check(nodeQName, actual);
63         }
64         case MERGE: {
65             // Recursively modify all child nodes
66             result = modifyContainer(schema, actual, modification, operationStack);
67             break;
68         }
69         default:
70             throw new UnsupportedOperationException(String.format("Unable to perform operation: %s on: %s, unknown",
71                     operationStack.getCurrentOperation(), schema));
72         }
73
74         operationStack.exitingNode(modification);
75         return result;
76     }
77
78     protected abstract QName getQName(S schema);
79
80     private Optional<N> modifyContainer(S schema, Optional<N> actual, Optional<N> modification,
81             OperationStack operationStack) throws DataModificationException {
82
83         if (actual.isPresent() == false) {
84             return modification;
85         }
86
87         if (modification.isPresent() == false) {
88             return actual;
89         }
90
91         Set<InstanceIdentifier.PathArgument> toProcess = getChildrenToProcess(schema, actual, modification);
92
93         List<? extends DataContainerChild<?, ?>> merged = modifyContainerChildNodes(schema, operationStack,
94                 actual.get(), modification.get(), toProcess);
95         return build(schema, merged);
96     }
97
98     private List<? extends DataContainerChild<?, ?>> modifyContainerChildNodes(S schema, OperationStack operationStack,
99             N actual, N modification, Set<InstanceIdentifier.PathArgument> toProcess) throws DataModificationException {
100         List<DataContainerChild<?, ?>> result = Lists.newArrayList();
101
102         for (InstanceIdentifier.PathArgument childToProcessId : toProcess) {
103             Object schemaOfChildToProcess = findSchema(schema, childToProcessId);
104
105             Optional<? extends DataContainerChild<?, ?>> modifiedValues = modifyContainerNode(operationStack, actual,
106                     modification, childToProcessId, schemaOfChildToProcess);
107
108             if (modifiedValues.isPresent()) {
109                 result.add(modifiedValues.get());
110             }
111         }
112
113         return result;
114     }
115
116     private Optional<? extends DataContainerChild<?, ?>> modifyContainerNode(OperationStack operationStack, N actual,
117             N modification, InstanceIdentifier.PathArgument childToProcess, Object schemaChild)
118             throws DataModificationException {
119
120         Optional<DataContainerChild<?, ?>> storedChildren = actual.getChild(childToProcess);
121         Optional<DataContainerChild<?, ?>> modifiedChildren = modification.getChild(childToProcess);
122
123         return NodeDispatcher.dispatchChildModification(schemaChild, storedChildren, modifiedChildren, operationStack);
124     }
125
126     private Object findSchema(S schema, InstanceIdentifier.PathArgument childToProcessId) {
127         if (childToProcessId instanceof InstanceIdentifier.AugmentationIdentifier) {
128             return findSchemaForAugment(schema, (InstanceIdentifier.AugmentationIdentifier) childToProcessId);
129         } else {
130             return findSchemaForChild(schema, childToProcessId.getNodeType());
131         }
132     }
133
134     protected abstract Object findSchemaForChild(S schema, QName nodeType);
135
136     protected abstract Object findSchemaForAugment(S schema, InstanceIdentifier.AugmentationIdentifier childToProcessId);
137
138     private Optional<N> build(S schema, List<? extends DataContainerChild<?, ?>> result) {
139         DataContainerNodeBuilder<?, N> b = getBuilder(schema);
140
141         // TODO skip empty container nodes ? e.g. if container looses all its child nodes
142 //        if(result.isEmpty()) {
143 //            return Optional.absent();
144 //        }
145
146         for (DataContainerChild<?, ?> dataContainerChild : result) {
147             b.withChild(dataContainerChild);
148         }
149         return Optional.of(b.build());
150     }
151
152     protected abstract DataContainerNodeBuilder<?, N> getBuilder(S schema);
153
154     protected Set<InstanceIdentifier.PathArgument> getChildrenToProcess(S schema, Optional<N> actual,
155             Optional<N> modification) throws DataModificationException {
156         Set<InstanceIdentifier.PathArgument> qNames = Sets.newLinkedHashSet();
157
158         qNames.addAll(getChildQNames(actual));
159         qNames.addAll(getChildQNames(modification));
160         return qNames;
161     }
162
163     private Set<? extends InstanceIdentifier.PathArgument> getChildQNames(Optional<N> actual) {
164         Set<InstanceIdentifier.PathArgument> qNames = Sets.newLinkedHashSet();
165
166         for (DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> child : actual.get().getValue()) {
167             qNames.add(child.getIdentifier());
168         }
169
170         return qNames;
171     }
172
173     private static final class NodeDispatcher {
174
175         private static final LeafNodeModification LEAF_NODE_MODIFICATION = new LeafNodeModification();
176         private static final LeafSetNodeModification LEAF_SET_NODE_MODIFICATION = new LeafSetNodeModification();
177         private static final AugmentationNodeModification AUGMENTATION_NODE_MODIFICATION = new AugmentationNodeModification();
178         private static final MapNodeModification MAP_NODE_MODIFICATION = new MapNodeModification();
179         private static final ContainerNodeModification CONTAINER_NODE_MODIFICATION = new ContainerNodeModification();
180         private static final ChoiceNodeModification CHOICE_NODE_MODIFICATION = new ChoiceNodeModification();
181
182         static Optional<? extends DataContainerChild<?, ?>> dispatchChildModification(Object schemaChild,
183                 Optional<DataContainerChild<?, ?>> actual, Optional<DataContainerChild<?, ?>> modification,
184                 OperationStack operations) throws DataModificationException {
185
186             if (schemaChild instanceof LeafSchemaNode) {
187                 return onLeafNode((LeafSchemaNode) schemaChild, actual, modification, operations);
188             } else if (schemaChild instanceof ContainerSchemaNode) {
189                 return onContainerNode((ContainerSchemaNode) schemaChild, actual, modification, operations);
190             } else if (schemaChild instanceof LeafListSchemaNode) {
191                 return onLeafSetNode((LeafListSchemaNode) schemaChild, actual, modification, operations);
192             } else if (schemaChild instanceof AugmentationSchema) {
193                 return onAugmentationNode((AugmentationSchema) schemaChild, actual, modification, operations);
194             } else if (schemaChild instanceof ListSchemaNode) {
195                 return onMapNode((ListSchemaNode) schemaChild, actual, modification, operations);
196             } else if (schemaChild instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
197                 return onChoiceNode((org.opendaylight.yangtools.yang.model.api.ChoiceNode) schemaChild, actual,
198                         modification, operations);
199             }
200
201             throw new IllegalArgumentException("Unknown schema node type " + schemaChild);
202         }
203
204         private static Optional<? extends DataContainerChild<?, ?>> onChoiceNode(
205                 org.opendaylight.yangtools.yang.model.api.ChoiceNode schemaChild,
206                 Optional<? extends DataContainerChild<?, ?>> actual,
207                 Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
208                 throws DataModificationException {
209             checkType(actual, ChoiceNode.class);
210             checkType(modification, ChoiceNode.class);
211             return CHOICE_NODE_MODIFICATION.modify(schemaChild, (Optional<ChoiceNode>) actual,
212                     (Optional<ChoiceNode>) modification, operations);
213         }
214
215         private static Optional<? extends DataContainerChild<?, ?>> onMapNode(ListSchemaNode schemaChild,
216                 Optional<? extends DataContainerChild<?, ?>> actual,
217                 Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
218                 throws DataModificationException {
219             checkType(actual, MapNode.class);
220             checkType(modification, MapNode.class);
221             return MAP_NODE_MODIFICATION.modify(schemaChild, (Optional<MapNode>) actual,
222                     (Optional<MapNode>) modification, operations);
223         }
224
225         private static Optional<? extends DataContainerChild<?, ?>> onAugmentationNode(AugmentationSchema schemaChild,
226                 Optional<? extends DataContainerChild<?, ?>> actual,
227                 Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
228                 throws DataModificationException {
229             checkType(actual, AugmentationNode.class);
230             checkType(modification, AugmentationNode.class);
231             return AUGMENTATION_NODE_MODIFICATION.modify(schemaChild, (Optional<AugmentationNode>) actual,
232                     (Optional<AugmentationNode>) modification, operations);
233         }
234
235         private static Optional<? extends DataContainerChild<?, ?>> onLeafSetNode(LeafListSchemaNode schemaChild,
236                 Optional<? extends DataContainerChild<?, ?>> actual,
237                 Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
238                 throws DataModificationException {
239             checkType(actual, LeafSetNode.class);
240             checkType(modification, LeafSetNode.class);
241             return LEAF_SET_NODE_MODIFICATION.modify(schemaChild, (Optional<LeafSetNode<?>>) actual,
242                     (Optional<LeafSetNode<?>>) modification, operations);
243         }
244
245         private static Optional<? extends DataContainerChild<?, ?>> onContainerNode(ContainerSchemaNode schemaChild,
246                 Optional<? extends DataContainerChild<?, ?>> actual,
247                 Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
248                 throws DataModificationException {
249             checkType(actual, ContainerNode.class);
250             checkType(modification, ContainerNode.class);
251             return CONTAINER_NODE_MODIFICATION.modify(schemaChild, (Optional<ContainerNode>) actual,
252                     (Optional<ContainerNode>) modification, operations);
253         }
254
255         private static Optional<? extends DataContainerChild<?, ?>> onLeafNode(LeafSchemaNode schemaChild,
256                 Optional<? extends DataContainerChild<?, ?>> actual,
257                 Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
258                 throws DataModificationException {
259             checkType(actual, LeafNode.class);
260             checkType(modification, LeafNode.class);
261             return LEAF_NODE_MODIFICATION.modify(schemaChild, (Optional<LeafNode<?>>) actual,
262                     (Optional<LeafNode<?>>) modification, operations);
263         }
264
265         private static void checkType(Optional<? extends DataContainerChild<?, ?>> actual, Class<?> leafNodeClass) {
266             if (actual.isPresent()) {
267                 Preconditions.checkArgument(leafNodeClass.isAssignableFrom(actual.get().getClass()),
268                         "Unexpected node type, should be: %s, but was: %s, for: %s", leafNodeClass, actual.getClass(),
269                         actual);
270             }
271         }
272     }
273 }