1 package org.opendaylight.controller.cluster.datastore.node;
3 import com.google.common.base.Preconditions;
4 import com.google.common.collect.FluentIterable;
5 import com.google.common.collect.ImmutableMap;
6 import com.google.common.collect.ImmutableSet;
7 import org.opendaylight.controller.cluster.datastore.node.utils.NodeIdentifierFactory;
8 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node;
9 import org.opendaylight.yangtools.concepts.Identifiable;
10 import org.opendaylight.yangtools.yang.common.QName;
11 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
12 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
13 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
14 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
15 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
16 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
24 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
25 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
26 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
27 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
28 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
29 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 import java.util.Collections;
42 import java.util.HashSet;
43 import java.util.List;
45 import java.util.Map.Entry;
47 import java.util.concurrent.ConcurrentHashMap;
49 import static com.google.common.base.Preconditions.checkArgument;
52 * NormalizedNodeBuilder is a builder that walks through a tree like structure and constructs a
53 * NormalizedNode from it.
55 * A large part of this code has been copied over from a similar class in sal-common-impl which was
56 * originally supposed to convert a CompositeNode to NormalizedNode
60 public abstract class NodeToNormalizedNodeBuilder<T extends PathArgument>
61 implements Identifiable<T> {
63 private final T identifier;
65 protected static final Logger logger = LoggerFactory
66 .getLogger(NodeToNormalizedNodeBuilder.class);
69 public T getIdentifier() {
75 protected NodeToNormalizedNodeBuilder(final T identifier) {
77 this.identifier = identifier;
82 * @return Should return true if the node that this operation corresponds to is a mixin
84 public boolean isMixin() {
90 * @return Should return true if the node that this operation corresponds to has a 'key'
91 * associated with it. This is typically true for a list-item or leaf-list entry in yang
93 public boolean isKeyedEntry() {
97 protected Set<QName> getQNameIdentifiers() {
98 return Collections.singleton(identifier.getNodeType());
101 public abstract NodeToNormalizedNodeBuilder<?> getChild(
102 final PathArgument child);
104 public abstract NodeToNormalizedNodeBuilder<?> getChild(QName child);
106 public abstract NormalizedNode<?, ?> normalize(QName nodeType, Node node);
110 private static abstract class SimpleTypeNormalization<T extends PathArgument>
111 extends NodeToNormalizedNodeBuilder<T> {
113 protected SimpleTypeNormalization(final T identifier) {
118 public NormalizedNode<?, ?> normalize(final QName nodeType,
120 checkArgument(node != null);
121 return normalizeImpl(nodeType, node);
124 protected abstract NormalizedNode<?, ?> normalizeImpl(QName nodeType,
128 public NodeToNormalizedNodeBuilder<?> getChild(
129 final PathArgument child) {
134 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
139 public NormalizedNode<?, ?> createDefault(
140 final PathArgument currentArg) {
141 // TODO Auto-generated method stub
148 private static final class LeafNormalization extends
149 SimpleTypeNormalization<NodeIdentifier> {
151 private final LeafSchemaNode schema;
153 protected LeafNormalization(final LeafSchemaNode schema, final NodeIdentifier identifier) {
155 this.schema = schema;
159 protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
161 Object value = NodeValueCodec.toTypeSafeValue(this.schema, this.schema.getType(), node);
162 return ImmutableNodes.leafNode(nodeType, value);
169 private static final class LeafListEntryNormalization extends
170 SimpleTypeNormalization<NodeWithValue> {
172 private final LeafListSchemaNode schema;
174 public LeafListEntryNormalization(final LeafListSchemaNode potential) {
175 super(new NodeWithValue(potential.getQName(), null));
176 this.schema = potential;
180 protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
182 final Object data = node.getValue();
184 Preconditions.checkArgument(false,
185 "No data available in leaf list entry for " + nodeType);
188 Object value = NodeValueCodec.toTypeSafeValue(this.schema, this.schema.getType(), node);
190 NodeWithValue nodeId = new NodeWithValue(nodeType, value);
191 return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId)
192 .withValue(value).build();
197 public boolean isKeyedEntry() {
203 private static abstract class NodeToNormalizationNodeOperation<T extends PathArgument>
204 extends NodeToNormalizedNodeBuilder<T> {
206 protected NodeToNormalizationNodeOperation(final T identifier) {
210 @SuppressWarnings({"rawtypes", "unchecked"})
212 public final NormalizedNodeContainer<?, ?, ?> normalize(
213 final QName nodeType, final Node node) {
214 checkArgument(node != null);
216 if (!node.getType().equals(AugmentationNode.class.getSimpleName())
217 && !node.getType().equals(ContainerNode.class.getSimpleName())
218 && !node.getType().equals(MapNode.class.getSimpleName())) {
219 checkArgument(nodeType != null);
222 NormalizedNodeContainerBuilder builder = createBuilder(node);
224 Set<NodeToNormalizedNodeBuilder<?>> usedMixins = new HashSet<>();
228 if (node.getChildCount() == 0 && (
229 node.getType().equals(LeafSetEntryNode.class.getSimpleName())
230 || node.getType().equals(LeafNode.class.getSimpleName()))) {
231 PathArgument childPathArgument =
232 NodeIdentifierFactory.getArgument(node.getPath());
234 final NormalizedNode child;
235 if (childPathArgument instanceof NodeWithValue) {
236 final NodeWithValue nodeWithValue =
237 new NodeWithValue(childPathArgument.getNodeType(),
240 Builders.leafSetEntryBuilder()
241 .withNodeIdentifier(nodeWithValue)
242 .withValue(node.getValue()).build();
245 ImmutableNodes.leafNode(childPathArgument.getNodeType(),
248 builder.addChild(child);
251 final List<Node> children = node.getChildList();
252 for (Node nodeChild : children) {
254 PathArgument childPathArgument =
255 NodeIdentifierFactory.getArgument(nodeChild.getPath());
257 QName childNodeType = null;
258 NodeToNormalizedNodeBuilder childOp = null;
260 if (childPathArgument instanceof AugmentationIdentifier) {
261 childOp = getChild(childPathArgument);
262 checkArgument(childOp instanceof AugmentationNormalization, childPathArgument);
264 childNodeType = childPathArgument.getNodeType();
265 childOp = getChild(childNodeType);
267 // We skip unknown nodes if this node is mixin since
268 // it's nodes and parent nodes are interleaved
269 if (childOp == null && isMixin()) {
271 } else if (childOp == null) {
273 "childOp is null and this operation is not a mixin : this = {}",
277 checkArgument(childOp != null,
278 "Node %s is not allowed inside %s",
279 childNodeType, getIdentifier());
281 if (childOp.isMixin()) {
282 if (usedMixins.contains(childOp)) {
283 // We already run / processed that mixin, so to avoid
285 // skipping next nodes.
288 // builder.addChild(childOp.normalize(nodeType, treeCacheNode));
289 final NormalizedNode childNode =
290 childOp.normalize(childNodeType, nodeChild);
291 if (childNode != null)
292 builder.addChild(childNode);
293 usedMixins.add(childOp);
295 final NormalizedNode childNode =
296 childOp.normalize(childNodeType, nodeChild);
297 if (childNode != null)
298 builder.addChild(childNode);
304 return (NormalizedNodeContainer<?, ?, ?>) builder.build();
305 } catch (Exception e) {
311 private void logNode(Node node) {
312 //let us find out the type of the node
313 logger.debug("We got a {} , with identifier {} with {} children",
314 node.getType(), node.getPath(),
315 node.getChildList());
318 @SuppressWarnings("rawtypes")
319 protected abstract NormalizedNodeContainerBuilder createBuilder(
325 private static abstract class DataContainerNormalizationOperation<T extends PathArgument>
326 extends NodeToNormalizationNodeOperation<T> {
328 private final DataNodeContainer schema;
329 private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
330 private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
332 protected DataContainerNormalizationOperation(final T identifier,
333 final DataNodeContainer schema) {
335 this.schema = schema;
336 this.byArg = new ConcurrentHashMap<>();
337 this.byQName = new ConcurrentHashMap<>();
341 public NodeToNormalizedNodeBuilder<?> getChild(
342 final PathArgument child) {
343 NodeToNormalizedNodeBuilder<?> potential = byArg.get(child);
344 if (potential != null) {
347 potential = fromSchema(schema, child);
348 return register(potential);
352 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
357 NodeToNormalizedNodeBuilder<?> potential = byQName.get(child);
358 if (potential != null) {
361 potential = fromSchemaAndPathArgument(schema, child);
362 return register(potential);
365 private NodeToNormalizedNodeBuilder<?> register(
366 final NodeToNormalizedNodeBuilder<?> potential) {
367 if (potential != null) {
368 byArg.put(potential.getIdentifier(), potential);
369 for (QName qName : potential.getQNameIdentifiers()) {
370 byQName.put(qName, potential);
379 private static final class ListItemNormalization extends
380 DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
382 private final List<QName> keyDefinition;
383 private final ListSchemaNode schemaNode;
385 protected ListItemNormalization(
386 final NodeIdentifierWithPredicates identifier,
387 final ListSchemaNode schema) {
388 super(identifier, schema);
389 this.schemaNode = schema;
390 keyDefinition = schema.getKeyDefinition();
394 protected NormalizedNodeContainerBuilder createBuilder(
396 NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
397 (NodeIdentifierWithPredicates) NodeIdentifierFactory
398 .createPathArgument(node
399 .getPath(), schemaNode);
400 return Builders.mapEntryBuilder()
402 nodeIdentifierWithPredicates
407 public NormalizedNode<?, ?> createDefault(
408 final PathArgument currentArg) {
409 DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode>
411 Builders.mapEntryBuilder().withNodeIdentifier(
412 (NodeIdentifierWithPredicates) currentArg);
413 for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg)
414 .getKeyValues().entrySet()) {
415 if (keyValue.getValue() == null) {
416 throw new NullPointerException(
417 "Null value found for path : "
420 builder.addChild(Builders.leafBuilder()
422 .withNodeIdentifier(new NodeIdentifier(keyValue.getKey()))
423 .withValue(keyValue.getValue()).build());
425 return builder.build();
430 public boolean isKeyedEntry() {
436 private static final class ContainerNormalization extends
437 DataContainerNormalizationOperation<NodeIdentifier> {
439 protected ContainerNormalization(final ContainerSchemaNode schema) {
440 super(new NodeIdentifier(schema.getQName()), schema);
444 protected NormalizedNodeContainerBuilder createBuilder(
446 return Builders.containerBuilder()
447 .withNodeIdentifier(getIdentifier());
451 public NormalizedNode<?, ?> createDefault(
452 final PathArgument currentArg) {
453 return Builders.containerBuilder()
454 .withNodeIdentifier((NodeIdentifier) currentArg).build();
460 private static abstract class MixinNormalizationOp<T extends PathArgument>
461 extends NodeToNormalizationNodeOperation<T> {
463 protected MixinNormalizationOp(final T identifier) {
468 public final boolean isMixin() {
475 private static final class LeafListMixinNormalization extends
476 MixinNormalizationOp<NodeIdentifier> {
478 private final NodeToNormalizedNodeBuilder<?> innerOp;
480 public LeafListMixinNormalization(final LeafListSchemaNode potential) {
481 super(new NodeIdentifier(potential.getQName()));
482 innerOp = new LeafListEntryNormalization(potential);
486 protected NormalizedNodeContainerBuilder createBuilder(
488 return Builders.leafSetBuilder()
489 .withNodeIdentifier(getIdentifier());
493 public NormalizedNode<?, ?> createDefault(
494 final PathArgument currentArg) {
495 return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier())
500 public NodeToNormalizedNodeBuilder<?> getChild(
501 final PathArgument child) {
502 if (child instanceof NodeWithValue) {
509 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
510 if (getIdentifier().getNodeType().equals(child)) {
519 private static final class AugmentationNormalization extends
520 MixinNormalizationOp<AugmentationIdentifier> {
522 private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
523 private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
525 public AugmentationNormalization(final AugmentationSchema augmentation,
526 final DataNodeContainer schema) {
527 super(augmentationIdentifierFrom(augmentation));
529 ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>>
531 ImmutableMap.builder();
532 ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>>
534 ImmutableMap.builder();
536 for (DataSchemaNode augNode : augmentation.getChildNodes()) {
537 DataSchemaNode resolvedNode =
538 schema.getDataChildByName(augNode.getQName());
539 NodeToNormalizedNodeBuilder<?> resolvedOp =
540 fromDataSchemaNode(resolvedNode);
541 byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
542 for (QName resQName : resolvedOp.getQNameIdentifiers()) {
543 byQNameBuilder.put(resQName, resolvedOp);
546 byQName = byQNameBuilder.build();
547 byArg = byArgBuilder.build();
552 public NodeToNormalizedNodeBuilder<?> getChild(
553 final PathArgument child) {
554 return byArg.get(child);
558 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
559 return byQName.get(child);
563 protected Set<QName> getQNameIdentifiers() {
564 return getIdentifier().getPossibleChildNames();
567 @SuppressWarnings("rawtypes")
569 protected NormalizedNodeContainerBuilder createBuilder(
571 return Builders.augmentationBuilder()
572 .withNodeIdentifier(getIdentifier());
576 public NormalizedNode<?, ?> createDefault(
577 final PathArgument currentArg) {
578 return Builders.augmentationBuilder()
579 .withNodeIdentifier(getIdentifier())
586 private static final class ListMixinNormalization extends
587 MixinNormalizationOp<NodeIdentifier> {
589 private final ListItemNormalization innerNode;
591 public ListMixinNormalization(final ListSchemaNode list) {
592 super(new NodeIdentifier(list.getQName()));
594 new ListItemNormalization(new NodeIdentifierWithPredicates(
595 list.getQName(), Collections.<QName, Object>emptyMap()),
599 @SuppressWarnings("rawtypes")
601 protected NormalizedNodeContainerBuilder createBuilder(
603 return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
607 public NormalizedNode<?, ?> createDefault(
608 final PathArgument currentArg) {
609 return Builders.mapBuilder().withNodeIdentifier(getIdentifier())
614 public NodeToNormalizedNodeBuilder<?> getChild(
615 final PathArgument child) {
616 if (child.getNodeType().equals(getIdentifier().getNodeType())) {
623 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
624 if (getIdentifier().getNodeType().equals(child)) {
633 private static class ChoiceNodeNormalization extends
634 MixinNormalizationOp<NodeIdentifier> {
636 private final ImmutableMap<QName, NodeToNormalizedNodeBuilder<?>>
638 private final ImmutableMap<PathArgument, NodeToNormalizedNodeBuilder<?>>
641 protected ChoiceNodeNormalization(
642 final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
643 super(new NodeIdentifier(schema.getQName()));
644 ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>>
646 ImmutableMap.builder();
647 ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>>
649 ImmutableMap.builder();
651 for (ChoiceCaseNode caze : schema.getCases()) {
652 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
653 NodeToNormalizedNodeBuilder<?> childOp =
654 fromDataSchemaNode(cazeChild);
655 byArgBuilder.put(childOp.getIdentifier(), childOp);
656 for (QName qname : childOp.getQNameIdentifiers()) {
657 byQNameBuilder.put(qname, childOp);
661 byQName = byQNameBuilder.build();
662 byArg = byArgBuilder.build();
666 public NodeToNormalizedNodeBuilder<?> getChild(
667 final PathArgument child) {
668 return byArg.get(child);
672 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
673 return byQName.get(child);
677 protected NormalizedNodeContainerBuilder createBuilder(
679 return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
683 public NormalizedNode<?, ?> createDefault(
684 final PathArgument currentArg) {
685 return Builders.choiceBuilder().withNodeIdentifier(getIdentifier())
691 * Find an appropriate NormalizedNodeBuilder using both the schema and the
698 public static NodeToNormalizedNodeBuilder<?> fromSchemaAndPathArgument(
699 final DataNodeContainer schema, final QName child) {
700 DataSchemaNode potential = schema.getDataChildByName(child);
701 if (potential == null) {
702 Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode>
704 FluentIterable.from(schema.getChildNodes()).filter(
705 org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
706 potential = findChoice(choices, child);
708 if (potential == null) {
709 if (logger.isTraceEnabled()) {
710 logger.trace("BAD CHILD = {}", child.toString());
714 checkArgument(potential != null,
715 "Supplied QName %s is not valid according to schema %s", child,
718 // If the schema in an instance of DataSchemaNode and the potential
719 // is augmenting something then there is a chance that this may be
720 // and augmentation node
721 if ((schema instanceof DataSchemaNode)
722 && potential.isAugmenting()) {
724 AugmentationNormalization augmentation =
725 fromAugmentation(schema, (AugmentationTarget) schema,
728 // If an augmentation normalization (builder) is not found then
729 // we fall through to the regular processing
730 if(augmentation != null){
734 return fromDataSchemaNode(potential);
738 * Given a bunch of choice nodes and a the name of child find a choice node for that child which
739 * has a non-null value
745 private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
746 final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices,
748 org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
750 for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
751 for (ChoiceCaseNode caze : choice.getCases()) {
752 if (caze.getDataChildByName(child) != null) {
753 foundChoice = choice;
763 * Create an AugmentationIdentifier based on the AugmentationSchema
765 * @param augmentation
768 public static AugmentationIdentifier augmentationIdentifierFrom(
769 final AugmentationSchema augmentation) {
770 ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
771 for (DataSchemaNode child : augmentation.getChildNodes()) {
772 potentialChildren.add(child.getQName());
774 return new AugmentationIdentifier(potentialChildren.build());
778 * Create an AugmentationNormalization based on the schema of the DataContainer, the
779 * AugmentationTarget and the potential schema node
786 private static AugmentationNormalization fromAugmentation(
787 final DataNodeContainer schema, final AugmentationTarget augments,
788 final DataSchemaNode potential) {
789 AugmentationSchema augmentation = null;
790 for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
791 DataSchemaNode child = aug.getDataChildByName(potential.getQName());
798 if (augmentation != null) {
799 return new AugmentationNormalization(augmentation, schema);
810 private static NodeToNormalizedNodeBuilder<?> fromSchema(
811 final DataNodeContainer schema, final PathArgument child) {
812 if (child instanceof AugmentationIdentifier) {
813 QName childQName = ((AugmentationIdentifier) child)
814 .getPossibleChildNames().iterator().next();
816 return fromSchemaAndPathArgument(schema, childQName);
818 return fromSchemaAndPathArgument(schema, child.getNodeType());
821 public static NodeToNormalizedNodeBuilder<?> fromDataSchemaNode(
822 final DataSchemaNode potential) {
823 if (potential instanceof ContainerSchemaNode) {
824 return new ContainerNormalization((ContainerSchemaNode) potential);
825 } else if (potential instanceof ListSchemaNode) {
826 return new ListMixinNormalization((ListSchemaNode) potential);
827 } else if (potential instanceof LeafSchemaNode) {
828 return new LeafNormalization((LeafSchemaNode) potential,
829 new NodeIdentifier(potential.getQName()));
830 } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
831 return new ChoiceNodeNormalization(
832 (org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
833 } else if (potential instanceof LeafListSchemaNode) {
834 return new LeafListMixinNormalization(
835 (LeafListSchemaNode) potential);
840 public static NodeToNormalizedNodeBuilder<?> from(final SchemaContext ctx) {
841 return new ContainerNormalization(ctx);
844 public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);