Rework NormalizedNode type hierarchy
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / InstanceIdToNodes.java
index 0185f57cd4b7fe2a332b99ac6c4d7dd1cda0aa52..8590557411cde7e07f639c1199650a61837c1df6 100644 (file)
@@ -7,30 +7,34 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.FluentIterable;
-import java.util.Collections;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.Iterables;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Map.Entry;
+import java.util.Optional;
 import javax.xml.transform.dom.DOMSource;
-import org.opendaylight.yangtools.concepts.Identifiable;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.ModifyAction;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerLike;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -41,145 +45,142 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
  * Base strategy for converting an instance identifier into a normalized node structure.
  * Use provided static methods for generic YangInstanceIdentifier -> NormalizedNode translation in ImmutableNodes.
  */
-abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable<T> {
-
-    private final T identifier;
-
-    @Override
-    public T getIdentifier() {
-        return identifier;
-    }
-
-    protected InstanceIdToNodes(final T identifier) {
-        this.identifier = identifier;
+abstract class InstanceIdToNodes<T extends PathArgument> extends AbstractSimpleIdentifiable<T> {
+    InstanceIdToNodes(final T identifier) {
+        super(identifier);
     }
 
     /**
-     * Build a strategy for the next path argument
+     * Build a strategy for the next path argument.
      *
      * @param child child identifier
      * @return transformation strategy for a specific child
      */
-    abstract InstanceIdToNodes<?> getChild(final PathArgument child);
+    abstract @Nullable InstanceIdToNodes<?> getChild(PathArgument child);
 
     /**
-     *
-     * Convert instance identifier into a NormalizedNode structure
+     * Convert instance identifier into a NormalizedNode structure.
      *
      * @param instanceId Instance identifier to transform into NormalizedNodes
      * @param deepestChild Optional normalized node to be inserted as the last child
      * @param operation Optional modify operation to be set on the last child
      * @return NormalizedNode structure corresponding to submitted instance ID
      */
-    abstract NormalizedNode<?, ?> create(YangInstanceIdentifier instanceId, Optional<NormalizedNode<?, ?>> deepestChild, Optional<Entry<QName,ModifyAction>> operation);
-
-
-    public void addModifyOpIfPresent(final Optional<Entry<QName,ModifyAction>> operation, final AttributesBuilder<?> builder) {
-        if(operation.isPresent()) {
-            builder.withAttributes(Collections.singletonMap(operation.get().getKey(), modifyOperationToXmlString(operation.get().getValue())));
-        }
-    }
-
-    public static String modifyOperationToXmlString(final ModifyAction operation) {
-        return operation.name().toLowerCase();
-    }
-
-    static boolean isMixin(final InstanceIdToNodes<?> op) {
-        return op instanceof MixinNormalizationOp;
-    }
-
-    /**
-     * Marker interface for Mixin nodes normalization operations
-     */
-    interface MixinNormalizationOp {}
-
+    abstract @NonNull NormalizedNode create(PathArgument first, Iterator<PathArgument> others,
+            Optional<NormalizedNode> deepestChild);
 
-    private static class UnkeyedListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> implements MixinNormalizationOp {
+    abstract boolean isMixin();
 
+    private static final class UnkeyedListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
         private final UnkeyedListItemNormalization innerNode;
 
-        public UnkeyedListMixinNormalization(final ListSchemaNode list) {
-            super(new NodeIdentifier(list.getQName()));
+        UnkeyedListMixinNormalization(final ListSchemaNode list) {
+            super(NodeIdentifier.create(list.getQName()));
             this.innerNode = new UnkeyedListItemNormalization(list);
         }
 
         @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+        CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> createBuilder(final PathArgument compositeNode) {
             return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
         }
 
         @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
-            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
-                return innerNode;
-            }
-            return null;
+        InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
         }
-    }
 
-    private static class AnyXmlNormalization extends InstanceIdToNodes<NodeIdentifier> {
+        @Override
+        boolean isMixin() {
+            return true;
+        }
+    }
 
-        protected AnyXmlNormalization(final AnyXmlSchemaNode schema) {
-            super(new NodeIdentifier(schema.getQName()));
+    private abstract static class AbstractOpaqueNormalization extends InstanceIdToNodes<NodeIdentifier> {
+        AbstractOpaqueNormalization(final DataSchemaNode schema) {
+            super(NodeIdentifier.create(schema.getQName()));
         }
 
         @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+        final InstanceIdToNodes<?> getChild(final PathArgument child) {
             return null;
         }
 
         @Override
-        public NormalizedNode<?, ?> create(final YangInstanceIdentifier instanceId, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<Entry<QName,ModifyAction>> operation) {
-            if(deepestChild.isPresent()) {
-                Preconditions.checkState(deepestChild instanceof AnyXmlNode);
-                final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> anyXmlBuilder =
-                        Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
-                addModifyOpIfPresent(operation, anyXmlBuilder);
-                return anyXmlBuilder.build();
+        final boolean isMixin() {
+            return false;
+        }
+    }
+
+    private static final class AnydataNormalization extends AbstractOpaqueNormalization {
+        AnydataNormalization(final AnydataSchemaNode schema) {
+            super(schema);
+        }
+
+        @Override
+        NormalizedNode create(final PathArgument first, final Iterator<PathArgument> others,
+                final Optional<NormalizedNode> deepestChild) {
+            checkState(deepestChild.isPresent(), "Cannot instantiate anydata node without a value");
+            final NormalizedNode child = deepestChild.get();
+            checkState(child instanceof AnydataNode, "Invalid child %s", child);
+            return createAnydata((AnydataNode<?>) child);
+        }
+
+        private <T> AnydataNode<T> createAnydata(final AnydataNode<T> child) {
+            return Builders.anydataBuilder(child.bodyObjectModel()).withValue(child.body())
+            .withNodeIdentifier(getIdentifier()).build();
+        }
+    }
+
+    private static final class AnyXmlNormalization extends AbstractOpaqueNormalization {
+        AnyXmlNormalization(final AnyxmlSchemaNode schema) {
+            super(schema);
+        }
+
+        @Override
+        NormalizedNode create(final PathArgument first, final Iterator<PathArgument> others,
+                final Optional<NormalizedNode> deepestChild) {
+            final NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> builder =
+                    Builders.anyXmlBuilder()
+                    .withNodeIdentifier(getIdentifier());
+            if (deepestChild.isPresent()) {
+                final NormalizedNode child = deepestChild.get();
+                checkState(child instanceof DOMSourceAnyxmlNode, "Invalid child %s", child);
+                builder.withValue(((DOMSourceAnyxmlNode) child).body());
             }
 
-            final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder =
-                    Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
-            addModifyOpIfPresent(operation, builder);
             return builder.build();
         }
-
     }
 
     private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
-        DataSchemaNode potential = parent.getDataChildByName(child);
-        if (potential == null) {
-            final Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
-            potential = findChoice(choices, child);
-        }
-        return Optional.fromNullable(potential);
+        final Optional<DataSchemaNode> potential = parent.findDataChildByName(child);
+        return potential.isPresent() ? potential : Optional.ofNullable(
+            findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child));
     }
 
-    static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
+    static @Nullable InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
         final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
-        Preconditions.checkArgument(potential.isPresent(),
-                "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes());
+        checkArgument(potential.isPresent(),
+                "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,
+                schema.getChildNodes());
 
         final DataSchemaNode result = potential.get();
         // We try to look up if this node was added by augmentation
-        if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+        if (schema instanceof DataSchemaNode && result.isAugmenting()) {
             return fromAugmentation(schema, (AugmentationTarget) schema, result);
         }
         return fromDataSchemaNode(result);
     }
 
-    private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
-        ChoiceSchemaNode foundChoice = null;
-        choiceLoop:
+    private static @Nullable ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
         for (final ChoiceSchemaNode choice : choices) {
-            for (final ChoiceCaseNode caze : choice.getCases()) {
+            for (final CaseSchemaNode caze : choice.getCases()) {
                 if (findChildSchemaNode(caze, child).isPresent()) {
-                    foundChoice = choice;
-                    break choiceLoop;
+                    return choice;
                 }
             }
         }
-        return foundChoice;
+        return null;
     }
 
     /**
@@ -190,27 +191,20 @@ abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable
      * otherwise returns a SchemaPathUtil for child as
      * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
      */
-    private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
-                                                          final AugmentationTarget parentAug, final DataSchemaNode child) {
-        AugmentationSchema augmentation = null;
-        for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
-            final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
-            if (potential != null) {
-                augmentation = aug;
-                break;
+    private static @Nullable InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
+            final AugmentationTarget parentAug, final DataSchemaNode child) {
+        for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
+            final Optional<DataSchemaNode> potential = aug.findDataChildByName(child.getQName());
+            if (potential.isPresent()) {
+                return new InstanceIdToCompositeNodes.AugmentationNormalization(aug, parent);
             }
-
-        }
-        if (augmentation != null) {
-            return new InstanceIdToCompositeNodes.AugmentationNormalization(augmentation, parent);
-        } else {
-            return fromDataSchemaNode(child);
         }
+        return fromDataSchemaNode(child);
     }
 
-    static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
-        if (potential instanceof ContainerSchemaNode) {
-            return new InstanceIdToCompositeNodes.ContainerTransformation((ContainerSchemaNode) potential);
+    static @Nullable InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerLike) {
+            return new InstanceIdToCompositeNodes.ContainerTransformation((ContainerLike) potential);
         } else if (potential instanceof ListSchemaNode) {
             return fromListSchemaNode((ListSchemaNode) potential);
         } else if (potential instanceof LeafSchemaNode) {
@@ -219,8 +213,10 @@ abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable
             return new InstanceIdToCompositeNodes.ChoiceNodeNormalization((ChoiceSchemaNode) potential);
         } else if (potential instanceof LeafListSchemaNode) {
             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
-        } else if (potential instanceof AnyXmlSchemaNode) {
-            return new AnyXmlNormalization((AnyXmlSchemaNode) potential);
+        } else if (potential instanceof AnydataSchemaNode) {
+            return new AnydataNormalization((AnydataSchemaNode) potential);
+        } else if (potential instanceof AnyxmlSchemaNode) {
+            return new AnyXmlNormalization((AnyxmlSchemaNode) potential);
         }
         return null;
     }
@@ -230,18 +226,12 @@ abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable
         if (keyDefinition == null || keyDefinition.isEmpty()) {
             return new UnkeyedListMixinNormalization(potential);
         }
-        if (potential.isUserOrdered()) {
-            return new InstanceIdToCompositeNodes.OrderedMapMixinNormalization(potential);
-        }
-        return new InstanceIdToCompositeNodes.UnorderedMapMixinNormalization(potential);
+        return potential.isUserOrdered() ? new InstanceIdToCompositeNodes.OrderedMapMixinNormalization(potential)
+                : new InstanceIdToCompositeNodes.UnorderedMapMixinNormalization(potential);
     }
 
     private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
-        if (potential.isUserOrdered()) {
-            return new InstanceIdToCompositeNodes.OrderedLeafListMixinNormalization(potential);
-        }
-        return new InstanceIdToCompositeNodes.UnorderedLeafListMixinNormalization(potential);
+        return potential.isUserOrdered() ? new InstanceIdToCompositeNodes.OrderedLeafListMixinNormalization(potential)
+                : new InstanceIdToCompositeNodes.UnorderedLeafListMixinNormalization(potential);
     }
-
-
 }