Clean YangStatementParserListenerImpl up
[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.api.schema.UnkeyedListNode;
27 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
28 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
34
35 abstract class AbstractContainerNodeModification<S, N extends DataContainerNode<?>> implements Modification<S, N> {
36
37     @Override
38     public final Optional<N> modify(final S schema, final Optional<N> actual, final Optional<N> modification,
39             final 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(final S schema, final Optional<N> actual, final Optional<N> modification,
81             final OperationStack operationStack) throws DataModificationException {
82
83         if (!actual.isPresent()) {
84             return modification;
85         }
86
87         if (!modification.isPresent()) {
88             return actual;
89         }
90
91         Set<YangInstanceIdentifier.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(final S schema, final OperationStack operationStack,
99             final N actual, final N modification, final Set<YangInstanceIdentifier.PathArgument> toProcess) throws DataModificationException {
100         List<DataContainerChild<?, ?>> result = Lists.newArrayList();
101
102         for (YangInstanceIdentifier.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(final OperationStack operationStack, final N actual,
117             final N modification, final YangInstanceIdentifier.PathArgument childToProcess, final 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(final S schema, final YangInstanceIdentifier.PathArgument childToProcessId) {
127         if (childToProcessId instanceof YangInstanceIdentifier.AugmentationIdentifier) {
128             return findSchemaForAugment(schema, (YangInstanceIdentifier.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, YangInstanceIdentifier.AugmentationIdentifier childToProcessId);
137
138     private Optional<N> build(final S schema, final 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<YangInstanceIdentifier.PathArgument> getChildrenToProcess(final S schema, final Optional<N> actual,
155             final Optional<N> modification) throws DataModificationException {
156         Set<YangInstanceIdentifier.PathArgument> qNames = Sets.newLinkedHashSet();
157
158         qNames.addAll(getChildQNames(actual));
159         qNames.addAll(getChildQNames(modification));
160         return qNames;
161     }
162
163     private Set<? extends YangInstanceIdentifier.PathArgument> getChildQNames(final Optional<N> actual) {
164         Set<YangInstanceIdentifier.PathArgument> qNames = Sets.newLinkedHashSet();
165
166         for (DataContainerChild<? extends YangInstanceIdentifier.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 UnkeyedListNodeModification UNKEYED_LIST_NODE_MODIFICATION = new UnkeyedListNodeModification();
180         private static final ContainerNodeModification CONTAINER_NODE_MODIFICATION = new ContainerNodeModification();
181         private static final ChoiceNodeModification CHOICE_NODE_MODIFICATION = new ChoiceNodeModification();
182
183         private NodeDispatcher() {
184             throw new UnsupportedOperationException("Utility class should not be instantiated!");
185         }
186
187         static Optional<? extends DataContainerChild<?, ?>> dispatchChildModification(final Object schemaChild,
188                 final Optional<DataContainerChild<?, ?>> actual, final Optional<DataContainerChild<?, ?>> modification,
189                 final OperationStack operations) throws DataModificationException {
190
191             if (schemaChild instanceof LeafSchemaNode) {
192                 return onLeafNode((LeafSchemaNode) schemaChild, actual, modification, operations);
193             } else if (schemaChild instanceof ContainerSchemaNode) {
194                 return onContainerNode((ContainerSchemaNode) schemaChild, actual, modification, operations);
195             } else if (schemaChild instanceof LeafListSchemaNode) {
196                 return onLeafSetNode((LeafListSchemaNode) schemaChild, actual, modification, operations);
197             } else if (schemaChild instanceof AugmentationSchema) {
198                 return onAugmentationNode((AugmentationSchema) schemaChild, actual, modification, operations);
199             } else if (schemaChild instanceof ListSchemaNode) {
200                 if (((ListSchemaNode)schemaChild).getKeyDefinition().isEmpty()) {
201                     return onUnkeyedNode((ListSchemaNode) schemaChild, actual, modification, operations);
202                 } else {
203                     return onMapNode((ListSchemaNode) schemaChild, actual, modification, operations);
204                 }
205             } else if (schemaChild instanceof ChoiceSchemaNode) {
206                 return onChoiceNode((ChoiceSchemaNode) schemaChild, actual,
207                         modification, operations);
208             }
209
210             throw new IllegalArgumentException("Unknown schema node type " + schemaChild);
211         }
212
213         private static Optional<? extends DataContainerChild<?, ?>> onChoiceNode(
214             final ChoiceSchemaNode schemaChild,
215                 final Optional<? extends DataContainerChild<?, ?>> actual,
216                 final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
217                 throws DataModificationException {
218             checkType(actual, ChoiceNode.class);
219             checkType(modification, ChoiceNode.class);
220             return CHOICE_NODE_MODIFICATION.modify(schemaChild, (Optional<ChoiceNode>) actual,
221                     (Optional<ChoiceNode>) modification, operations);
222         }
223
224         private static Optional<? extends DataContainerChild<?, ?>> onMapNode(final ListSchemaNode schemaChild,
225                 final Optional<? extends DataContainerChild<?, ?>> actual,
226                 final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
227                 throws DataModificationException {
228             checkType(actual, MapNode.class);
229             checkType(modification, MapNode.class);
230             return MAP_NODE_MODIFICATION.modify(schemaChild, (Optional<MapNode>) actual,
231                     (Optional<MapNode>) modification, operations);
232         }
233
234         private static Optional<? extends DataContainerChild<?, ?>> onUnkeyedNode(final ListSchemaNode schemaChild,
235                 final Optional<? extends DataContainerChild<?, ?>> actual,
236                         final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
237                                 throws DataModificationException {
238             checkType(actual, UnkeyedListNode.class);
239             checkType(modification, UnkeyedListNode.class);
240             return UNKEYED_LIST_NODE_MODIFICATION.modify(schemaChild, (Optional<UnkeyedListNode>) actual,
241                     (Optional<UnkeyedListNode>) modification, operations);
242         }
243
244         private static Optional<? extends DataContainerChild<?, ?>> onAugmentationNode(final AugmentationSchema schemaChild,
245                 final Optional<? extends DataContainerChild<?, ?>> actual,
246                 final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
247                 throws DataModificationException {
248             checkType(actual, AugmentationNode.class);
249             checkType(modification, AugmentationNode.class);
250             return AUGMENTATION_NODE_MODIFICATION.modify(schemaChild, (Optional<AugmentationNode>) actual,
251                     (Optional<AugmentationNode>) modification, operations);
252         }
253
254         private static Optional<? extends DataContainerChild<?, ?>> onLeafSetNode(final LeafListSchemaNode schemaChild,
255                 final Optional<? extends DataContainerChild<?, ?>> actual,
256                 final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
257                 throws DataModificationException {
258             checkType(actual, LeafSetNode.class);
259             checkType(modification, LeafSetNode.class);
260             return LEAF_SET_NODE_MODIFICATION.modify(schemaChild, (Optional<LeafSetNode<?>>) actual,
261                     (Optional<LeafSetNode<?>>) modification, operations);
262         }
263
264         private static Optional<? extends DataContainerChild<?, ?>> onContainerNode(final ContainerSchemaNode schemaChild,
265                 final Optional<? extends DataContainerChild<?, ?>> actual,
266                 final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
267                 throws DataModificationException {
268             checkType(actual, ContainerNode.class);
269             checkType(modification, ContainerNode.class);
270             return CONTAINER_NODE_MODIFICATION.modify(schemaChild, (Optional<ContainerNode>) actual,
271                     (Optional<ContainerNode>) modification, operations);
272         }
273
274         private static Optional<? extends DataContainerChild<?, ?>> onLeafNode(final LeafSchemaNode schemaChild,
275                 final Optional<? extends DataContainerChild<?, ?>> actual,
276                 final Optional<? extends DataContainerChild<?, ?>> modification, final OperationStack operations)
277                 throws DataModificationException {
278             checkType(actual, LeafNode.class);
279             checkType(modification, LeafNode.class);
280             return LEAF_NODE_MODIFICATION.modify(schemaChild, (Optional<LeafNode<?>>) actual,
281                     (Optional<LeafNode<?>>) modification, operations);
282         }
283
284         private static void checkType(final Optional<? extends DataContainerChild<?, ?>> actual, final Class<?> leafNodeClass) {
285             if (actual.isPresent()) {
286                 Preconditions.checkArgument(leafNodeClass.isAssignableFrom(actual.get().getClass()),
287                         "Unexpected node type, should be: %s, but was: %s, for: %s", leafNodeClass, actual.getClass(),
288                         actual);
289             }
290         }
291     }
292 }