baa36d8699e0c6ea99e89fcd0a1ba501568a0b85
[controller.git] / opendaylight / md-sal / sal-netconf-connector / src / main / java / org / opendaylight / controller / sal / connect / netconf / util / InstanceIdToNodes.java
1 /*
2  * Copyright (c) 2015 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.controller.sal.connect.netconf.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.FluentIterable;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.collect.Iterables;
19 import com.google.common.collect.Lists;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import javax.xml.transform.dom.DOMSource;
30 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
31 import org.opendaylight.yangtools.concepts.Identifiable;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
40 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
50 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
52 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
53 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
54 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
56 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
57 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
58 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
61
62 /**
63  * Transforms an instance of yang instance identifier to a filter like structure in normalized node format. Can be also used to nest the edit-config rpc content.
64  * For each argument of the id, a specific normalized node is created to ensure schema context conformance.
65  */
66 public abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable<T> {
67
68     private final T identifier;
69
70     @Override
71     public T getIdentifier() {
72         return identifier;
73     }
74
75     protected InstanceIdToNodes(final T identifier) {
76         this.identifier = identifier;
77     }
78
79     abstract InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException;
80
81     public abstract NormalizedNode<?, ?> create(YangInstanceIdentifier legacyData, Optional<NormalizedNode<?, ?>> deepestChild, Optional<ModifyAction> operation);
82
83     private static abstract class SimpleTypeNormalization<T extends PathArgument> extends InstanceIdToNodes<T> {
84
85         protected SimpleTypeNormalization(final T identifier) {
86             super(identifier);
87         }
88
89         @Override
90         public NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
91             checkNotNull(id);
92             final PathArgument pathArgument = Iterables.get(id.getPathArguments(), 0);
93             final NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> builder = getBuilder(pathArgument);
94
95             if(deepestChild.isPresent()) {
96                 builder.withValue(deepestChild.get().getValue());
97             }
98
99             addModifyOpIfPresent(operation, builder);
100             return builder.build();
101         }
102
103         protected abstract NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> getBuilder(PathArgument node);
104
105         @Override
106         public InstanceIdToNodes<?> getChild(final PathArgument child) {
107             return null;
108         }
109     }
110
111
112     public void addModifyOpIfPresent(final Optional<ModifyAction> operation, final AttributesBuilder<?> builder) {
113         if(operation.isPresent()) {
114             builder.withAttributes(Collections.singletonMap(NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME, NetconfMessageTransformUtil.modifyOperationToXmlString(operation.get())));
115         }
116     }
117
118     private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
119
120         protected LeafNormalization(final LeafSchemaNode potential) {
121             super(new NodeIdentifier(potential.getQName()));
122         }
123
124         @Override
125         protected NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> getBuilder(final PathArgument node) {
126             return Builders.leafBuilder().withNodeIdentifier(getIdentifier());
127         }
128     }
129
130     private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
131
132         public LeafListEntryNormalization(final LeafListSchemaNode potential) {
133             super(new NodeWithValue(potential.getQName(), null));
134         }
135
136         @Override
137         protected NormalizedNodeAttrBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> getBuilder(final PathArgument node) {
138             Preconditions.checkArgument(node instanceof NodeWithValue);
139             return Builders.leafSetEntryBuilder().withNodeIdentifier((NodeWithValue) node).withValue(((NodeWithValue) node).getValue());
140         }
141
142     }
143
144     private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
145             InstanceIdToNodes<T> {
146
147         protected CompositeNodeNormalizationOperation(final T identifier) {
148             super(identifier);
149         }
150
151         @Override
152         @SuppressWarnings("unchecked")
153         public final NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> lastChild, final Optional<ModifyAction> operation) {
154             checkNotNull(id);
155             final Iterator<PathArgument> iterator = id.getPathArguments().iterator();
156             final PathArgument legacyData = iterator.next();
157
158             if (!isMixin(this) && getIdentifier().getNodeType() != null) {
159                 checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
160                         "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
161             }
162             final NormalizedNodeContainerBuilder builder = createBuilder(legacyData);
163
164             if (iterator.hasNext()) {
165                 final PathArgument childPath = iterator.next();
166                 final InstanceIdToNodes childOp = getChildOperation(childPath);
167
168                 final YangInstanceIdentifier childId = YangInstanceIdentifier.create(Iterables.skip(id.getPathArguments(), 1));
169                 builder.addChild(childOp.create(childId, lastChild, operation));
170             } else if(lastChild.isPresent()) {
171                 builder.withValue(Lists.newArrayList((Collection<?>) lastChild.get().getValue()));
172                 if(operation.isPresent()) {
173                     Preconditions.checkArgument(builder instanceof AttributesBuilder<?>);
174                     addModifyOpIfPresent(operation, ((AttributesBuilder<?>) builder));
175                 }
176             }
177
178             return builder.build();
179         }
180
181         private InstanceIdToNodes getChildOperation(final PathArgument childPath) {
182             final InstanceIdToNodes childOp;
183             try {
184                 childOp = getChild(childPath);
185             } catch (final DataNormalizationException e) {
186                 throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
187             }
188             checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
189             return childOp;
190         }
191
192         @SuppressWarnings("rawtypes")
193         protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode);
194     }
195
196     static boolean isMixin(final InstanceIdToNodes<?> op) {
197         return op instanceof MixinNormalizationOp;
198     }
199
200     private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
201             CompositeNodeNormalizationOperation<T> {
202
203         private final DataNodeContainer schema;
204         private final Map<PathArgument, InstanceIdToNodes<?>> byArg;
205
206         protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
207             super(identifier);
208             this.schema = schema;
209             this.byArg = new ConcurrentHashMap<>();
210         }
211
212         @Override
213         public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
214             InstanceIdToNodes<?> potential = byArg.get(child);
215             if (potential != null) {
216                 return potential;
217             }
218             potential = fromLocalSchema(child);
219             return register(potential);
220         }
221
222         private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
223             if (child instanceof AugmentationIdentifier) {
224                 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
225                         .iterator().next());
226             }
227             return fromSchemaAndQNameChecked(schema, child.getNodeType());
228         }
229
230         private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
231             if (potential != null) {
232                 byArg.put(potential.getIdentifier(), potential);
233             }
234             return potential;
235         }
236     }
237
238     private static final class ListItemNormalization extends
239             DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
240
241         protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
242             super(identifier, schema);
243         }
244
245         @Override
246         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument currentArg) {
247             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
248                     .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
249             for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
250                 builder.addChild(Builders.leafBuilder()
251                         //
252                         .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
253                         .build());
254             }
255             return builder;
256         }
257
258     }
259
260     private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
261
262         protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
263             super(new NodeIdentifier(schema.getQName()), schema);
264         }
265
266         @Override
267         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
268             return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
269         }
270
271     }
272
273     private static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
274
275         protected ContainerTransformation(final ContainerSchemaNode schema) {
276             super(new NodeIdentifier(schema.getQName()), schema);
277         }
278
279         @Override
280         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
281             return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
282         }
283     }
284
285     /**
286      * Marker interface for Mixin nodes normalization operations
287      */
288     private interface MixinNormalizationOp {}
289
290
291     private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
292
293
294         public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
295             super(potential);
296         }
297
298         @Override
299         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
300             return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
301         }
302     }
303
304     private static class UnorderedLeafListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
305
306         private final InstanceIdToNodes<?> innerOp;
307
308         public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
309             super(new NodeIdentifier(potential.getQName()));
310             innerOp = new LeafListEntryNormalization(potential);
311         }
312
313         @Override
314         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
315             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
316         }
317
318         @Override
319         public InstanceIdToNodes<?> getChild(final PathArgument child) {
320             if (child instanceof NodeWithValue) {
321                 return innerOp;
322             }
323             return null;
324         }
325     }
326
327     private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> implements MixinNormalizationOp {
328
329         public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
330             //super();
331             super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema));
332         }
333
334         @Override
335         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
336             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
337         }
338     }
339
340     private static class UnorderedMapMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
341
342         private final ListItemNormalization innerNode;
343
344         public UnorderedMapMixinNormalization(final ListSchemaNode list) {
345             super(new NodeIdentifier(list.getQName()));
346             this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
347                     Collections.<QName, Object>emptyMap()), list);
348         }
349
350         @Override
351         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
352             return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
353         }
354
355         @Override
356         public InstanceIdToNodes<?> getChild(final PathArgument child) {
357             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
358                 return innerNode;
359             }
360             return null;
361         }
362     }
363
364     private static class UnkeyedListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
365
366         private final UnkeyedListItemNormalization innerNode;
367
368         public UnkeyedListMixinNormalization(final ListSchemaNode list) {
369             super(new NodeIdentifier(list.getQName()));
370             this.innerNode = new UnkeyedListItemNormalization(list);
371         }
372
373         @Override
374         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
375             return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
376         }
377
378         @Override
379         public InstanceIdToNodes<?> getChild(final PathArgument child) {
380             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
381                 return innerNode;
382             }
383             return null;
384         }
385
386     }
387
388     private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
389
390         public OrderedMapMixinNormalization(final ListSchemaNode list) {
391             super(list);
392         }
393
394         @Override
395         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
396             return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
397         }
398
399     }
400
401     private static class ChoiceNodeNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
402
403         private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
404
405         protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
406             super(new NodeIdentifier(schema.getQName()));
407             final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
408
409             for (final ChoiceCaseNode caze : schema.getCases()) {
410                 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
411                     final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
412                     byArgBuilder.put(childOp.getIdentifier(), childOp);
413                 }
414             }
415             byArg = byArgBuilder.build();
416         }
417
418         @Override
419         public InstanceIdToNodes<?> getChild(final PathArgument child) {
420             return byArg.get(child);
421         }
422
423         @Override
424         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
425             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
426         }
427     }
428
429     private static class AnyXmlNormalization extends InstanceIdToNodes<NodeIdentifier> {
430
431         protected AnyXmlNormalization(final AnyXmlSchemaNode schema) {
432             super(new NodeIdentifier(schema.getQName()));
433         }
434
435         @Override
436         public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
437             return null;
438         }
439
440         @Override
441         public NormalizedNode<?, ?> create(final YangInstanceIdentifier legacyData, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
442             if(deepestChild.isPresent()) {
443                 Preconditions.checkState(deepestChild instanceof AnyXmlNode);
444                 final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> anyXmlBuilder =
445                         Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
446                 addModifyOpIfPresent(operation, anyXmlBuilder);
447                 return anyXmlBuilder.build();
448             }
449
450             final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder =
451                     Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
452             return builder.build();
453         }
454
455     }
456
457     private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
458         DataSchemaNode potential = parent.getDataChildByName(child);
459         if (potential == null) {
460             final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
461                     parent.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
462             potential = findChoice(choices, child);
463         }
464         return Optional.fromNullable(potential);
465     }
466
467     private static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) throws DataNormalizationException {
468         final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
469         if (!potential.isPresent()) {
470             throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes()));
471         }
472
473         final DataSchemaNode result = potential.get();
474         // We try to look up if this node was added by augmentation
475         if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
476             return fromAugmentation(schema, (AugmentationTarget) schema, result);
477         }
478         return fromDataSchemaNode(result);
479     }
480
481     private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
482             final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
483         org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
484         choiceLoop:
485         for (final org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
486             for (final ChoiceCaseNode caze : choice.getCases()) {
487                 if (findChildSchemaNode(caze, child).isPresent()) {
488                     foundChoice = choice;
489                     break choiceLoop;
490                 }
491             }
492         }
493         return foundChoice;
494     }
495
496     private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
497         final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
498         for (final DataSchemaNode child : augmentation.getChildNodes()) {
499             potentialChildren.add(child.getQName());
500         }
501         return new AugmentationIdentifier(potentialChildren.build());
502     }
503
504     private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
505         final Set<DataSchemaNode> children = new HashSet<>();
506         for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
507             children.add(schema.getDataChildByName(augNode.getQName()));
508         }
509         return new NodeContainerProxy(null, children);
510     }
511
512     /**
513      * Returns a SchemaPathUtil for provided child node
514      * <p/>
515      * If supplied child is added by Augmentation this operation returns
516      * a SchemaPathUtil for augmentation,
517      * otherwise returns a SchemaPathUtil for child as
518      * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
519      */
520     private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
521                                                           final AugmentationTarget parentAug, final DataSchemaNode child) {
522         AugmentationSchema augmentation = null;
523         for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
524             final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
525             if (potential != null) {
526                 augmentation = aug;
527                 break;
528             }
529
530         }
531         if (augmentation != null) {
532             return new AugmentationNormalization(augmentation, parent);
533         } else {
534             return fromDataSchemaNode(child);
535         }
536     }
537
538     private static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
539         if (potential instanceof ContainerSchemaNode) {
540             return new ContainerTransformation((ContainerSchemaNode) potential);
541         } else if (potential instanceof ListSchemaNode) {
542             return fromListSchemaNode((ListSchemaNode) potential);
543         } else if (potential instanceof LeafSchemaNode) {
544             return new LeafNormalization((LeafSchemaNode) potential);
545         } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
546             return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
547         } else if (potential instanceof LeafListSchemaNode) {
548             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
549         } else if (potential instanceof AnyXmlSchemaNode) {
550             return new AnyXmlNormalization((AnyXmlSchemaNode) potential);
551         }
552         return null;
553     }
554
555     private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
556         final List<QName> keyDefinition = potential.getKeyDefinition();
557         if (keyDefinition == null || keyDefinition.isEmpty()) {
558             return new UnkeyedListMixinNormalization(potential);
559         }
560         if (potential.isUserOrdered()) {
561             return new OrderedMapMixinNormalization(potential);
562         }
563         return new UnorderedMapMixinNormalization(potential);
564     }
565
566     private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
567         if (potential.isUserOrdered()) {
568             return new OrderedLeafListMixinNormalization(potential);
569         }
570         return new UnorderedLeafListMixinNormalization(potential);
571     }
572
573     public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id) {
574         return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>absent(), Optional.<ModifyAction>absent());
575     }
576
577     public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode<?, ?> deepestElement) {
578         return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>of(deepestElement), Optional.<ModifyAction>absent());
579     }
580
581     public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestElement, final Optional<ModifyAction> operation) {
582         Preconditions.checkNotNull(ctx);
583         Preconditions.checkNotNull(id);
584         final PathArgument topLevelElement = id.getPathArguments().iterator().next();
585         final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType());
586         Preconditions.checkNotNull(dataChildByName, "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement);
587         try {
588             final InstanceIdToNodes<?> instanceIdToNodes = fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType());
589             return instanceIdToNodes.create(id, deepestElement, operation);
590         } catch (final DataNormalizationException e) {
591             throw new IllegalArgumentException("Unable to serialize: " + id, e);
592         }
593     }
594 }