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.protobuff.messages.common.NormalizedNodeMessages.Node;
8 import org.opendaylight.controller.cluster.datastore.node.utils.NodeIdentifierFactory;
9 import org.opendaylight.yangtools.concepts.Identifiable;
10 import org.opendaylight.yangtools.yang.common.QName;
11 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
12 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
13 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
14 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
15 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
16 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
20 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
21 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
22 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
23 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
26 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
27 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import java.util.Collections;
38 import java.util.HashSet;
39 import java.util.List;
41 import java.util.Map.Entry;
43 import java.util.concurrent.ConcurrentHashMap;
45 import static com.google.common.base.Preconditions.checkArgument;
48 * NormalizedNodeBuilder is a builder that walks through a tree like structure and constructs a
49 * NormalizedNode from it.
51 * A large part of this code has been copied over from a similar class in sal-common-impl which was
52 * originally supposed to convert a CompositeNode to NormalizedNode
56 public abstract class NodeToNormalizedNodeBuilder<T extends PathArgument>
57 implements Identifiable<T> {
59 private final T identifier;
61 protected static final Logger logger = LoggerFactory
62 .getLogger(NodeToNormalizedNodeBuilder.class);
65 public T getIdentifier() {
69 protected NodeToNormalizedNodeBuilder(final T identifier) {
71 this.identifier = identifier;
77 * @return Should return true if the node that this operation corresponds to is a mixin
79 public boolean isMixin() {
86 * @return Should return true if the node that this operation corresponds to has a 'key'
87 * associated with it. This is typically true for a list-item or leaf-list entry in yang
89 public boolean isKeyedEntry() {
93 protected Set<QName> getQNameIdentifiers() {
94 return Collections.singleton(identifier.getNodeType());
97 public abstract NodeToNormalizedNodeBuilder<?> getChild(
98 final PathArgument child);
100 public abstract NodeToNormalizedNodeBuilder<?> getChild(QName child);
102 public abstract NormalizedNode<?, ?> normalize(QName nodeType, Node node);
106 private static abstract class SimpleTypeNormalization<T extends PathArgument>
107 extends NodeToNormalizedNodeBuilder<T> {
109 protected SimpleTypeNormalization(final T identifier) {
114 public NormalizedNode<?, ?> normalize(final QName nodeType, final Node node) {
115 checkArgument(node != null);
116 return normalizeImpl(nodeType, node);
119 protected abstract NormalizedNode<?, ?> normalizeImpl(QName nodeType,
123 public NodeToNormalizedNodeBuilder<?> getChild(final PathArgument child) {
128 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
133 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
134 // TODO Auto-generated method stub
140 private static final class LeafNormalization extends
141 SimpleTypeNormalization<NodeIdentifier> {
143 protected LeafNormalization(final NodeIdentifier identifier) {
148 protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
150 return ImmutableNodes.leafNode(nodeType, node.getValue());
156 private static final class LeafListEntryNormalization extends
157 SimpleTypeNormalization<NodeWithValue> {
159 public LeafListEntryNormalization(final LeafListSchemaNode potential) {
160 super(new NodeWithValue(potential.getQName(), null));
164 protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
166 final Object data = node.getValue();
168 Preconditions.checkArgument(false,
169 "No data available in leaf list entry for " + nodeType);
171 NodeWithValue nodeId = new NodeWithValue(nodeType, data);
172 return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId)
173 .withValue(data).build();
178 public boolean isKeyedEntry() {
183 private static abstract class NodeToNormalizationNodeOperation<T extends PathArgument>
184 extends NodeToNormalizedNodeBuilder<T> {
186 protected NodeToNormalizationNodeOperation(final T identifier) {
190 @SuppressWarnings({"rawtypes", "unchecked"})
192 public final NormalizedNodeContainer<?, ?, ?> normalize(
193 final QName nodeType, final Node node) {
194 checkArgument(node != null);
195 if (!node.getType().equals(AugmentationNode.class.getSimpleName())) {
196 checkArgument(nodeType != null);
199 NormalizedNodeContainerBuilder builder = createBuilder(node);
201 Set<NodeToNormalizedNodeBuilder<?>> usedMixins = new HashSet<>();
205 if (node.getChildCount() == 0) {
206 PathArgument childPathArgument =
207 NodeIdentifierFactory.getArgument(node.getPath());
208 NormalizedNode child = null;
209 if (childPathArgument instanceof NodeWithValue) {
210 final NodeWithValue nodeWithValue =
211 new NodeWithValue(childPathArgument.getNodeType(),
214 Builders.leafSetEntryBuilder().withNodeIdentifier(nodeWithValue)
215 .withValue(node.getValue()).build();
218 ImmutableNodes.leafNode(childPathArgument.getNodeType(),
221 builder.addChild(child);
224 final List<Node> children = node.getChildList();
225 for (Node nodeChild : children) {
227 PathArgument childPathArgument =
228 NodeIdentifierFactory.getArgument(nodeChild.getPath());
230 QName childNodeType = null;
231 NodeToNormalizedNodeBuilder childOp = null;
234 if (childPathArgument instanceof AugmentationIdentifier) {
235 childOp = getChild(childPathArgument);
237 childNodeType = childPathArgument.getNodeType();
238 childOp = getChild(childNodeType);
240 // We skip unknown nodes if this node is mixin since
241 // it's nodes and parent nodes are interleaved
242 if (childOp == null && isMixin()) {
244 } else if (childOp == null) {
246 "childOp is null and this operation is not a mixin : this = {}",
250 checkArgument(childOp != null, "Node %s is not allowed inside %s",
251 childNodeType, getIdentifier());
252 if (childOp.isMixin()) {
253 if (usedMixins.contains(childOp)) {
254 // We already run / processed that mixin, so to avoid
256 // skiping next nodes.
259 // builder.addChild(childOp.normalize(nodeType, treeCacheNode));
260 final NormalizedNode childNode =
261 childOp.normalize(childNodeType, nodeChild);
262 if (childNode != null)
263 builder.addChild(childNode);
264 usedMixins.add(childOp);
266 final NormalizedNode childNode =
267 childOp.normalize(childNodeType, nodeChild);
268 if (childNode != null)
269 builder.addChild(childNode);
275 return (NormalizedNodeContainer<?, ?, ?>) builder.build();
276 } catch (Exception e) {
282 private void logNode(Node node) {
283 //let us find out the type of the node
284 logger.debug("We got a {} , with identifier {} with {} children", node.getType(),node.getPath(),
285 node.getChildList());
288 @SuppressWarnings("rawtypes")
289 protected abstract NormalizedNodeContainerBuilder createBuilder(
294 private static abstract class DataContainerNormalizationOperation<T extends PathArgument>
295 extends NodeToNormalizationNodeOperation<T> {
297 private final DataNodeContainer schema;
298 private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
299 private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
301 protected DataContainerNormalizationOperation(final T identifier,
302 final DataNodeContainer schema) {
304 this.schema = schema;
305 this.byArg = new ConcurrentHashMap<>();
306 this.byQName = new ConcurrentHashMap<>();
310 public NodeToNormalizedNodeBuilder<?> getChild(final PathArgument child) {
311 NodeToNormalizedNodeBuilder<?> potential = byArg.get(child);
312 if (potential != null) {
315 potential = fromSchema(schema, child);
316 return register(potential);
320 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
325 NodeToNormalizedNodeBuilder<?> potential = byQName.get(child);
326 if (potential != null) {
329 potential = fromSchemaAndPathArgument(schema, child);
330 return register(potential);
333 private NodeToNormalizedNodeBuilder<?> register(
334 final NodeToNormalizedNodeBuilder<?> potential) {
335 if (potential != null) {
336 byArg.put(potential.getIdentifier(), potential);
337 for (QName qName : potential.getQNameIdentifiers()) {
338 byQName.put(qName, potential);
346 private static final class ListItemNormalization extends
347 DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
349 private final List<QName> keyDefinition;
350 private final ListSchemaNode schemaNode;
352 protected ListItemNormalization(
353 final NodeIdentifierWithPredicates identifier,
354 final ListSchemaNode schema) {
355 super(identifier, schema);
356 this.schemaNode = schema;
357 keyDefinition = schema.getKeyDefinition();
361 protected NormalizedNodeContainerBuilder createBuilder(final Node node) {
362 return Builders.mapEntryBuilder().withNodeIdentifier(
363 (NodeIdentifierWithPredicates) NodeIdentifierFactory.getArgument(node
368 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
369 DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
370 Builders.mapEntryBuilder().withNodeIdentifier(
371 (NodeIdentifierWithPredicates) currentArg);
372 for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg)
373 .getKeyValues().entrySet()) {
374 if (keyValue.getValue() == null) {
375 throw new NullPointerException("Null value found for path : "
378 builder.addChild(Builders.leafBuilder()
380 .withNodeIdentifier(new NodeIdentifier(keyValue.getKey()))
381 .withValue(keyValue.getValue()).build());
383 return builder.build();
388 public boolean isKeyedEntry() {
393 private static final class ContainerNormalization extends
394 DataContainerNormalizationOperation<NodeIdentifier> {
396 protected ContainerNormalization(final ContainerSchemaNode schema) {
397 super(new NodeIdentifier(schema.getQName()), schema);
401 protected NormalizedNodeContainerBuilder createBuilder(final Node node) {
402 return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
406 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
407 return Builders.containerBuilder()
408 .withNodeIdentifier((NodeIdentifier) currentArg).build();
413 private static abstract class MixinNormalizationOp<T extends PathArgument>
414 extends NodeToNormalizationNodeOperation<T> {
416 protected MixinNormalizationOp(final T identifier) {
421 public final boolean isMixin() {
427 private static final class LeafListMixinNormalization extends
428 MixinNormalizationOp<NodeIdentifier> {
430 private final NodeToNormalizedNodeBuilder<?> innerOp;
432 public LeafListMixinNormalization(final LeafListSchemaNode potential) {
433 super(new NodeIdentifier(potential.getQName()));
434 innerOp = new LeafListEntryNormalization(potential);
438 protected NormalizedNodeContainerBuilder createBuilder(
440 return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
444 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
445 return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier())
450 public NodeToNormalizedNodeBuilder<?> getChild(final PathArgument child) {
451 if (child instanceof NodeWithValue) {
458 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
459 if (getIdentifier().getNodeType().equals(child)) {
467 private static final class AugmentationNormalization extends
468 MixinNormalizationOp<AugmentationIdentifier> {
470 private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
471 private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
473 public AugmentationNormalization(final AugmentationSchema augmentation,
474 final DataNodeContainer schema) {
475 super(augmentationIdentifierFrom(augmentation));
477 ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>> byQNameBuilder =
478 ImmutableMap.builder();
479 ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>> byArgBuilder =
480 ImmutableMap.builder();
482 for (DataSchemaNode augNode : augmentation.getChildNodes()) {
483 DataSchemaNode resolvedNode =
484 schema.getDataChildByName(augNode.getQName());
485 NodeToNormalizedNodeBuilder<?> resolvedOp =
486 fromDataSchemaNode(resolvedNode);
487 byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
488 for (QName resQName : resolvedOp.getQNameIdentifiers()) {
489 byQNameBuilder.put(resQName, resolvedOp);
492 byQName = byQNameBuilder.build();
493 byArg = byArgBuilder.build();
498 public NodeToNormalizedNodeBuilder<?> getChild(final PathArgument child) {
499 return byArg.get(child);
503 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
504 return byQName.get(child);
508 protected Set<QName> getQNameIdentifiers() {
509 return getIdentifier().getPossibleChildNames();
512 @SuppressWarnings("rawtypes")
514 protected NormalizedNodeContainerBuilder createBuilder(final Node node) {
515 return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
519 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
520 return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier())
526 private static final class ListMixinNormalization extends
527 MixinNormalizationOp<NodeIdentifier> {
529 private final ListItemNormalization innerNode;
531 public ListMixinNormalization(final ListSchemaNode list) {
532 super(new NodeIdentifier(list.getQName()));
534 new ListItemNormalization(new NodeIdentifierWithPredicates(
535 list.getQName(), Collections.<QName, Object>emptyMap()), list);
538 @SuppressWarnings("rawtypes")
540 protected NormalizedNodeContainerBuilder createBuilder(
542 return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
546 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
547 return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
551 public NodeToNormalizedNodeBuilder<?> getChild(final PathArgument child) {
552 if (child.getNodeType().equals(getIdentifier().getNodeType())) {
559 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
560 if (getIdentifier().getNodeType().equals(child)) {
568 private static class ChoiceNodeNormalization extends
569 MixinNormalizationOp<NodeIdentifier> {
571 private final ImmutableMap<QName, NodeToNormalizedNodeBuilder<?>> byQName;
572 private final ImmutableMap<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
574 protected ChoiceNodeNormalization(
575 final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
576 super(new NodeIdentifier(schema.getQName()));
577 ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>> byQNameBuilder =
578 ImmutableMap.builder();
579 ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>> byArgBuilder =
580 ImmutableMap.builder();
582 for (ChoiceCaseNode caze : schema.getCases()) {
583 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
584 NodeToNormalizedNodeBuilder<?> childOp =
585 fromDataSchemaNode(cazeChild);
586 byArgBuilder.put(childOp.getIdentifier(), childOp);
587 for (QName qname : childOp.getQNameIdentifiers()) {
588 byQNameBuilder.put(qname, childOp);
592 byQName = byQNameBuilder.build();
593 byArg = byArgBuilder.build();
597 public NodeToNormalizedNodeBuilder<?> getChild(final PathArgument child) {
598 return byArg.get(child);
602 public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
603 return byQName.get(child);
607 protected NormalizedNodeContainerBuilder createBuilder(final Node node) {
608 return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
612 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
613 return Builders.choiceBuilder().withNodeIdentifier(getIdentifier())
618 public static NodeToNormalizedNodeBuilder<?> fromSchemaAndPathArgument(
619 final DataNodeContainer schema, final QName child) {
620 DataSchemaNode potential = schema.getDataChildByName(child);
621 if (potential == null) {
622 Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices =
623 FluentIterable.from(schema.getChildNodes()).filter(
624 org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
625 potential = findChoice(choices, child);
627 if (potential == null) {
628 if (logger.isTraceEnabled()) {
629 logger.trace("BAD CHILD = {}", child.toString());
633 checkArgument(potential != null,
634 "Supplied QName %s is not valid according to schema %s", child, schema);
635 if ((schema instanceof DataSchemaNode)
636 && !((DataSchemaNode) schema).isAugmenting()
637 && potential.isAugmenting()) {
638 return fromAugmentation(schema, (AugmentationTarget) schema, potential);
640 return fromDataSchemaNode(potential);
644 * Given a bunch of choice nodes and a the name of child find a choice node for that child which
645 * has a non-null value
651 private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
652 final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices,
654 org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
655 choiceLoop: for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
656 for (ChoiceCaseNode caze : choice.getCases()) {
657 if (caze.getDataChildByName(child) != null) {
658 foundChoice = choice;
668 * Create an AugmentationIdentifier based on the AugmentationSchema
670 * @param augmentation
673 public static AugmentationIdentifier augmentationIdentifierFrom(
674 final AugmentationSchema augmentation) {
675 ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
676 for (DataSchemaNode child : augmentation.getChildNodes()) {
677 potentialChildren.add(child.getQName());
679 return new AugmentationIdentifier(null, potentialChildren.build());
683 * Create an AugmentationNormalization based on the schema of the DataContainer, the
684 * AugmentationTarget and the potential schema node
691 private static AugmentationNormalization fromAugmentation(
692 final DataNodeContainer schema, final AugmentationTarget augments,
693 final DataSchemaNode potential) {
694 AugmentationSchema augmentation = null;
695 for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
696 DataSchemaNode child = aug.getDataChildByName(potential.getQName());
703 if (augmentation != null) {
704 return new AugmentationNormalization(augmentation, schema);
716 private static NodeToNormalizedNodeBuilder<?> fromSchema(
717 final DataNodeContainer schema, final PathArgument child) {
718 if (child instanceof AugmentationIdentifier) {
719 return fromSchemaAndPathArgument(schema, ((AugmentationIdentifier) child)
720 .getPossibleChildNames().iterator().next());
722 return fromSchemaAndPathArgument(schema, child.getNodeType());
725 public static NodeToNormalizedNodeBuilder<?> fromDataSchemaNode(
726 final DataSchemaNode potential) {
727 if (potential instanceof ContainerSchemaNode) {
728 return new ContainerNormalization((ContainerSchemaNode) potential);
729 } else if (potential instanceof ListSchemaNode) {
730 return new ListMixinNormalization((ListSchemaNode) potential);
731 } else if (potential instanceof LeafSchemaNode) {
732 return new LeafNormalization(new NodeIdentifier(potential.getQName()));
733 } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
734 return new ChoiceNodeNormalization(
735 (org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
736 } else if (potential instanceof LeafListSchemaNode) {
737 return new LeafListMixinNormalization((LeafListSchemaNode) potential);
742 public static NodeToNormalizedNodeBuilder<?> from(final SchemaContext ctx) {
743 return new ContainerNormalization(ctx);
746 public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);