Fix netconf operation serialization in nc
[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 {
171                 if(lastChild.isPresent()) {
172                     builder.withValue(Lists.newArrayList((Collection<?>) lastChild.get().getValue()));
173                 }
174                 if(operation.isPresent()) {
175                     Preconditions.checkArgument(builder instanceof AttributesBuilder<?>);
176                     addModifyOpIfPresent(operation, ((AttributesBuilder<?>) builder));
177                 }
178             }
179
180             return builder.build();
181         }
182
183         private InstanceIdToNodes getChildOperation(final PathArgument childPath) {
184             final InstanceIdToNodes childOp;
185             try {
186                 childOp = getChild(childPath);
187             } catch (final DataNormalizationException e) {
188                 throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
189             }
190             checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
191             return childOp;
192         }
193
194         @SuppressWarnings("rawtypes")
195         protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode);
196     }
197
198     static boolean isMixin(final InstanceIdToNodes<?> op) {
199         return op instanceof MixinNormalizationOp;
200     }
201
202     private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
203             CompositeNodeNormalizationOperation<T> {
204
205         private final DataNodeContainer schema;
206         private final Map<PathArgument, InstanceIdToNodes<?>> byArg;
207
208         protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
209             super(identifier);
210             this.schema = schema;
211             this.byArg = new ConcurrentHashMap<>();
212         }
213
214         @Override
215         public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
216             InstanceIdToNodes<?> potential = byArg.get(child);
217             if (potential != null) {
218                 return potential;
219             }
220             potential = fromLocalSchema(child);
221             return register(potential);
222         }
223
224         private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
225             if (child instanceof AugmentationIdentifier) {
226                 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
227                         .iterator().next());
228             }
229             return fromSchemaAndQNameChecked(schema, child.getNodeType());
230         }
231
232         private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
233             if (potential != null) {
234                 byArg.put(potential.getIdentifier(), potential);
235             }
236             return potential;
237         }
238     }
239
240     private static final class ListItemNormalization extends
241             DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
242
243         protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
244             super(identifier, schema);
245         }
246
247         @Override
248         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument currentArg) {
249             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
250                     .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
251             for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
252                 builder.addChild(Builders.leafBuilder()
253                         //
254                         .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
255                         .build());
256             }
257             return builder;
258         }
259
260     }
261
262     private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
263
264         protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
265             super(new NodeIdentifier(schema.getQName()), schema);
266         }
267
268         @Override
269         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
270             return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
271         }
272
273     }
274
275     private static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
276
277         protected ContainerTransformation(final ContainerSchemaNode schema) {
278             super(new NodeIdentifier(schema.getQName()), schema);
279         }
280
281         @Override
282         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
283             return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
284         }
285     }
286
287     /**
288      * Marker interface for Mixin nodes normalization operations
289      */
290     private interface MixinNormalizationOp {}
291
292
293     private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
294
295
296         public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
297             super(potential);
298         }
299
300         @Override
301         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
302             return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
303         }
304     }
305
306     private static class UnorderedLeafListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
307
308         private final InstanceIdToNodes<?> innerOp;
309
310         public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
311             super(new NodeIdentifier(potential.getQName()));
312             innerOp = new LeafListEntryNormalization(potential);
313         }
314
315         @Override
316         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
317             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
318         }
319
320         @Override
321         public InstanceIdToNodes<?> getChild(final PathArgument child) {
322             if (child instanceof NodeWithValue) {
323                 return innerOp;
324             }
325             return null;
326         }
327     }
328
329     private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> implements MixinNormalizationOp {
330
331         public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
332             //super();
333             super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema));
334         }
335
336         @Override
337         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
338             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
339         }
340     }
341
342     private static class UnorderedMapMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
343
344         private final ListItemNormalization innerNode;
345
346         public UnorderedMapMixinNormalization(final ListSchemaNode list) {
347             super(new NodeIdentifier(list.getQName()));
348             this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
349                     Collections.<QName, Object>emptyMap()), list);
350         }
351
352         @Override
353         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
354             return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
355         }
356
357         @Override
358         public InstanceIdToNodes<?> getChild(final PathArgument child) {
359             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
360                 return innerNode;
361             }
362             return null;
363         }
364     }
365
366     private static class UnkeyedListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
367
368         private final UnkeyedListItemNormalization innerNode;
369
370         public UnkeyedListMixinNormalization(final ListSchemaNode list) {
371             super(new NodeIdentifier(list.getQName()));
372             this.innerNode = new UnkeyedListItemNormalization(list);
373         }
374
375         @Override
376         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
377             return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
378         }
379
380         @Override
381         public InstanceIdToNodes<?> getChild(final PathArgument child) {
382             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
383                 return innerNode;
384             }
385             return null;
386         }
387
388     }
389
390     private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
391
392         public OrderedMapMixinNormalization(final ListSchemaNode list) {
393             super(list);
394         }
395
396         @Override
397         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
398             return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
399         }
400
401     }
402
403     private static class ChoiceNodeNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
404
405         private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
406
407         protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
408             super(new NodeIdentifier(schema.getQName()));
409             final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
410
411             for (final ChoiceCaseNode caze : schema.getCases()) {
412                 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
413                     final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
414                     byArgBuilder.put(childOp.getIdentifier(), childOp);
415                 }
416             }
417             byArg = byArgBuilder.build();
418         }
419
420         @Override
421         public InstanceIdToNodes<?> getChild(final PathArgument child) {
422             return byArg.get(child);
423         }
424
425         @Override
426         protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
427             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
428         }
429     }
430
431     private static class AnyXmlNormalization extends InstanceIdToNodes<NodeIdentifier> {
432
433         protected AnyXmlNormalization(final AnyXmlSchemaNode schema) {
434             super(new NodeIdentifier(schema.getQName()));
435         }
436
437         @Override
438         public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
439             return null;
440         }
441
442         @Override
443         public NormalizedNode<?, ?> create(final YangInstanceIdentifier legacyData, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
444             if(deepestChild.isPresent()) {
445                 Preconditions.checkState(deepestChild instanceof AnyXmlNode);
446                 final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> anyXmlBuilder =
447                         Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
448                 addModifyOpIfPresent(operation, anyXmlBuilder);
449                 return anyXmlBuilder.build();
450             }
451
452             final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder =
453                     Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
454             addModifyOpIfPresent(operation, builder);
455             return builder.build();
456         }
457
458     }
459
460     private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
461         DataSchemaNode potential = parent.getDataChildByName(child);
462         if (potential == null) {
463             final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
464                     parent.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
465             potential = findChoice(choices, child);
466         }
467         return Optional.fromNullable(potential);
468     }
469
470     private static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) throws DataNormalizationException {
471         final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
472         if (!potential.isPresent()) {
473             throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes()));
474         }
475
476         final DataSchemaNode result = potential.get();
477         // We try to look up if this node was added by augmentation
478         if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
479             return fromAugmentation(schema, (AugmentationTarget) schema, result);
480         }
481         return fromDataSchemaNode(result);
482     }
483
484     private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
485             final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
486         org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
487         choiceLoop:
488         for (final org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
489             for (final ChoiceCaseNode caze : choice.getCases()) {
490                 if (findChildSchemaNode(caze, child).isPresent()) {
491                     foundChoice = choice;
492                     break choiceLoop;
493                 }
494             }
495         }
496         return foundChoice;
497     }
498
499     private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
500         final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
501         for (final DataSchemaNode child : augmentation.getChildNodes()) {
502             potentialChildren.add(child.getQName());
503         }
504         return new AugmentationIdentifier(potentialChildren.build());
505     }
506
507     private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
508         final Set<DataSchemaNode> children = new HashSet<>();
509         for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
510             children.add(schema.getDataChildByName(augNode.getQName()));
511         }
512         return new NodeContainerProxy(null, children);
513     }
514
515     /**
516      * Returns a SchemaPathUtil for provided child node
517      * <p/>
518      * If supplied child is added by Augmentation this operation returns
519      * a SchemaPathUtil for augmentation,
520      * otherwise returns a SchemaPathUtil for child as
521      * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
522      */
523     private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
524                                                           final AugmentationTarget parentAug, final DataSchemaNode child) {
525         AugmentationSchema augmentation = null;
526         for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
527             final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
528             if (potential != null) {
529                 augmentation = aug;
530                 break;
531             }
532
533         }
534         if (augmentation != null) {
535             return new AugmentationNormalization(augmentation, parent);
536         } else {
537             return fromDataSchemaNode(child);
538         }
539     }
540
541     private static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
542         if (potential instanceof ContainerSchemaNode) {
543             return new ContainerTransformation((ContainerSchemaNode) potential);
544         } else if (potential instanceof ListSchemaNode) {
545             return fromListSchemaNode((ListSchemaNode) potential);
546         } else if (potential instanceof LeafSchemaNode) {
547             return new LeafNormalization((LeafSchemaNode) potential);
548         } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
549             return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
550         } else if (potential instanceof LeafListSchemaNode) {
551             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
552         } else if (potential instanceof AnyXmlSchemaNode) {
553             return new AnyXmlNormalization((AnyXmlSchemaNode) potential);
554         }
555         return null;
556     }
557
558     private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
559         final List<QName> keyDefinition = potential.getKeyDefinition();
560         if (keyDefinition == null || keyDefinition.isEmpty()) {
561             return new UnkeyedListMixinNormalization(potential);
562         }
563         if (potential.isUserOrdered()) {
564             return new OrderedMapMixinNormalization(potential);
565         }
566         return new UnorderedMapMixinNormalization(potential);
567     }
568
569     private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
570         if (potential.isUserOrdered()) {
571             return new OrderedLeafListMixinNormalization(potential);
572         }
573         return new UnorderedLeafListMixinNormalization(potential);
574     }
575
576     public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id) {
577         return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>absent(), Optional.<ModifyAction>absent());
578     }
579
580     public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode<?, ?> deepestElement) {
581         return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>of(deepestElement), Optional.<ModifyAction>absent());
582     }
583
584     public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestElement, final Optional<ModifyAction> operation) {
585         Preconditions.checkNotNull(ctx);
586         Preconditions.checkNotNull(id);
587         final PathArgument topLevelElement = id.getPathArguments().iterator().next();
588         final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType());
589         Preconditions.checkNotNull(dataChildByName, "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement);
590         try {
591             final InstanceIdToNodes<?> instanceIdToNodes = fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType());
592             return instanceIdToNodes.create(id, deepestElement, operation);
593         } catch (final DataNormalizationException e) {
594             throw new IllegalArgumentException("Unable to serialize: " + id, e);
595         }
596     }
597 }