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