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