HONEYCOMB-334 - List entry injection for yang data 45/5045/3
authorJan Srnicek <jsrnicek@cisco.com>
Fri, 10 Feb 2017 07:55:55 +0000 (08:55 +0100)
committerMarek Gradzki <mgradzki@cisco.com>
Fri, 10 Feb 2017 08:53:27 +0000 (08:53 +0000)
Major changes
 - mechanism to inject list entries by key
 - provided processor registry to hide explicit implementations

 Minor changes
 - general refactoring

 Test cases
 - list in root of model
 - list under container
 - list under nested container
 - list in augmentation

Change-Id: I9abe1ce5f9176c132ad88627b135516574e40e06
Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
22 files changed:
infra/test-utils/test-api/src/main/yang/hc-data.yang
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java [new file with mode: 0644]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java [new file with mode: 0644]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java [new file with mode: 0644]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java [new file with mode: 0644]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java [new file with mode: 0644]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java [deleted file]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java [deleted file]
infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java [deleted file]
infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/AbstractYangDataProcessorTest.java [new file with mode: 0644]
infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessorTest.java [new file with mode: 0644]
infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerContainerTest.java [moved from infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java with 85% similarity]
infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/InjectionTestData.java [new file with mode: 0644]
infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ListNodeDataProcessorTest.java [new file with mode: 0644]
infra/test-utils/test-tools/src/test/resources/augmentListEntry.json [new file with mode: 0644]
infra/test-utils/test-tools/src/test/resources/nestedListEntry.json [new file with mode: 0644]
infra/test-utils/test-tools/src/test/resources/rootListEntry.json [new file with mode: 0644]
infra/test-utils/test-tools/src/test/resources/simpleListEntry.json [new file with mode: 0644]
infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java

index a817a24..6cd5245 100644 (file)
@@ -12,6 +12,13 @@ module hc-data{
         prefix "ext";
       }
 
+      list root-list {
+        key root-name;
+        leaf root-name{
+          type string;
+        }
+      }
+
       container simple-container{
          list simple-list{
             key name;
@@ -23,6 +30,13 @@ module hc-data{
                     type string;
                 }
             }
+
+            list nested-list {
+                key nested-name;
+                leaf nested-name {
+                     type string;
+                }
+            }
         }
 
         container nested-container{
@@ -67,5 +81,12 @@ module hc-data{
         leaf name-in-augment{
             type string;
         }
+
+        list list-in-augment{
+            key key-in-augment;
+            leaf key-in-augment{
+                type string;
+            }
+        }
       }
 }
\ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java
new file mode 100644 (file)
index 0000000..93360d2
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import javax.annotation.Nonnull;
+
+abstract class AbstractYangContextHolder {
+
+    private final SchemaContext schemaContext;
+    private final BindingToNormalizedNodeCodec serializer;
+
+    AbstractYangContextHolder(@Nonnull final SchemaContext schemaContext,
+                              @Nonnull final BindingToNormalizedNodeCodec serializer){
+        this.schemaContext=schemaContext;
+        this.serializer=serializer;
+    }
+
+    SchemaContext schemaContext() {
+        return schemaContext;
+    }
+
+    BindingToNormalizedNodeCodec serializer() {
+        return serializer;
+    }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java
new file mode 100644 (file)
index 0000000..089bb43
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import java.io.InputStream;
+
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.honeycomb.translate.util.JsonUtils.readContainerEntryJson;
+import static io.fd.honeycomb.translate.util.JsonUtils.readJson;
+
+final class ContainerNodeDataProcessor extends AbstractYangContextHolder implements YangDataProcessor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ContainerNodeDataProcessor.class);
+
+    ContainerNodeDataProcessor(@Nonnull SchemaContext schemaContext, @Nonnull BindingToNormalizedNodeCodec serializer) {
+        super(schemaContext, serializer);
+    }
+
+    @Nonnull
+    @Override
+    public DataObject getNodeData(@Nonnull YangInstanceIdentifier yangInstanceIdentifier, @Nonnull String resourcePath) {
+
+        final InputStream resourceStream = this.getClass().getResourceAsStream(resourcePath);
+        final YangInstanceIdentifier nodeParent = getNodeParent(yangInstanceIdentifier).orElse(null);
+        final SchemaNode parentSchema = parentSchema(schemaContext(), serializer(), nodeParent, () -> LOG);
+
+        // to be able to process containers in root of model
+        if (isRoot(yangInstanceIdentifier)) {
+            // if root ,read as root
+            final ContainerNode data = readJson(schemaContext(), resourceStream, parentSchema);
+            checkState(data.getValue().size() == 1, "Single root expected in %s", resourcePath);
+            //then extracts first child
+            final QName rootNodeType = data.getValue().iterator().next().getNodeType();
+            final YangInstanceIdentifier realIdentifier = YangInstanceIdentifier.of(rootNodeType);
+            return nodeBinding(serializer(), realIdentifier, data).getValue();
+        } else {
+            // reads just container
+            final YangInstanceIdentifier.NodeIdentifier nodeIdentifier = containerNodeIdentifier(yangInstanceIdentifier);
+            final ContainerNode data = readContainerEntryJson(schemaContext(), resourceStream, parentSchema, nodeIdentifier);
+            return nodeBinding(serializer(), yangInstanceIdentifier, data.getValue().iterator().next()).getValue();
+        }
+    }
+
+    @Override
+    public boolean canProcess(@Nonnull YangInstanceIdentifier identifier) {
+        return isRoot(identifier) ||
+                isContainer(identifierBinding(serializer(), identifier).getTargetType());
+    }
+
+    private static boolean isContainer(final Class targetType) {
+        return !Identifiable.class.isAssignableFrom(targetType)
+                && !Augmentation.class.isAssignableFrom(targetType);
+    }
+
+    private static YangInstanceIdentifier.NodeIdentifier containerNodeIdentifier(@Nonnull final YangInstanceIdentifier nodeIdentifier) {
+        return java.util.Optional.ofNullable(nodeIdentifier.getLastPathArgument())
+                .filter(pathArgument -> pathArgument instanceof YangInstanceIdentifier.NodeIdentifier)
+                .map(YangInstanceIdentifier.NodeIdentifier.class::cast)
+                .orElseThrow(() -> new IllegalArgumentException(
+                        String.format("Unable to create container node identifier from %s", nodeIdentifier)));
+    }
+}
index 906a5fc..0f8b1b9 100644 (file)
 package io.fd.honeycomb.test.tools;
 
 import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor;
-import io.fd.honeycomb.test.tools.factories.ChildNodeDataFactory;
-import io.fd.honeycomb.test.tools.factories.RootNodeDataFactory;
-import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Parameter;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-import org.apache.commons.lang3.StringUtils;
 import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
 import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
 public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangContextProducer, InjectablesProcessor {
 
     private static final Logger LOG = LoggerFactory.getLogger(HoneycombTestRunner.class);
@@ -48,8 +42,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC
     private BindingToNormalizedNodeCodec serializer;
     private AbstractModuleStringInstanceIdentifierCodec iidParser;
 
-    private ChildNodeDataFactory childNodeDataFactory;
-    private RootNodeDataFactory rootNodeDataFactory;
+    private YangDataProcessorRegistry processorRegistry;
 
     public HoneycombTestRunner(final Class<?> klass) throws InitializationError {
         super(klass);
@@ -64,12 +57,11 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC
         final ModuleInfoBackedContext ctx = getCheckedModuleInfoContext(test);
         schemaContext = ctx.getSchemaContext();
         // Create serializer from it in order to later transform NormalizedNodes into BA
-        serializer = createSerializer(ctx, schemaContext);
+        serializer = createSerializer(ctx);
         // Create InstanceIdentifier Codec in order to later transform string represented IID into YangInstanceIdentifier
         iidParser = getIIDCodec(ctx);
 
-        childNodeDataFactory = new ChildNodeDataFactory(schemaContext, serializer, iidParser);
-        rootNodeDataFactory = new RootNodeDataFactory(schemaContext, serializer, iidParser);
+        processorRegistry = YangDataProcessorRegistry.create(schemaContext, serializer);
 
         injectFields(test);
         return test;
@@ -83,7 +75,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC
 
     /**
      * Allows method parameters injection
-     * */
+     */
     @Override
     protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
         return new InjectableTestMethodInvoker(method, test, Arrays.stream(method.getMethod().getParameters())
@@ -94,7 +86,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC
 
     private Object injectValueOrNull(final Parameter parameter) {
         return isInjectable(parameter)
-                ? getData(resourcePath(parameter), instanceIdentifier(parameter).orNull(), parameter.getType())
+                ? processorRegistry.getNodeData(instanceIdentifier(iidParser, parameter), resourcePath(parameter))
                 : null;
     }
 
@@ -107,22 +99,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC
         injectableFields(testInstance.getClass()).forEach(field -> {
             LOG.debug("Processing field {}", field);
             injectField(field, testInstance,
-                    getData(resourcePath(field), instanceIdentifier(field).orNull(), field.getType()));
+                    processorRegistry.getNodeData(instanceIdentifier(iidParser, field), resourcePath(field)));
         });
     }
-
-    private DataObject getData(final String resourcePath, @Nullable final String identifier,
-                               final Class<?> injectedType) {
-        try {
-            if (StringUtils.isNotEmpty(identifier)) {
-                LOG.debug("Processing {}  as child node {}", injectedType, identifier);
-                return childNodeDataFactory.getChildNodeData(identifier, resourcePath);
-            } else {
-                LOG.debug("Processing {} as root node", injectedType);
-                return rootNodeDataFactory.getRootNodeData(getRootInstanceIdentifier(injectedType), resourcePath);
-            }
-        } catch (DeserializationException | IOException e) {
-            throw new IllegalStateException(e);
-        }
-    }
 }
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java
new file mode 100644 (file)
index 0000000..375f553
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import java.io.InputStream;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static io.fd.honeycomb.translate.util.JsonUtils.readListEntryFromJson;
+
+/**
+ * json --> BA processor for list entry data
+ */
+final class ListNodeDataProcessor extends AbstractYangContextHolder implements YangDataProcessor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ListNodeDataProcessor.class);
+
+    ListNodeDataProcessor(@Nonnull final SchemaContext schemaContext,
+                          @Nonnull final BindingToNormalizedNodeCodec serializer) {
+        super(schemaContext, serializer);
+    }
+
+    @Nonnull
+    @Override
+    public DataObject getNodeData(@Nonnull final YangInstanceIdentifier nodeIdentifier,
+                                  @Nonnull final String resourcePath) {
+        checkArgument(canProcess(nodeIdentifier), "Cannot process identifier %s", nodeIdentifier);
+        final YangInstanceIdentifier listParent = listNodeParent(nodeIdentifier);
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates keyedNodeIdentifier = listNodeIdentifier(nodeIdentifier);
+        final InputStream resourceStream = this.getClass().getResourceAsStream(resourcePath);
+        final SchemaNode parentSchemaNode = parentSchema(schemaContext(), serializer(), listParent, () -> LOG);
+        final MapEntryNode data = readListEntryFromJson(schemaContext(), resourceStream, parentSchemaNode, keyedNodeIdentifier);
+
+        return nodeBinding(serializer(), nodeIdentifier, data).getValue();
+    }
+
+    @Override
+    public boolean canProcess(@Nonnull final YangInstanceIdentifier identifier) {
+        return !isRoot(identifier) &&
+                Identifiable.class.isAssignableFrom(identifierBinding(serializer(), identifier).getTargetType());
+    }
+
+    private YangInstanceIdentifier listNodeParent(@Nonnull final YangInstanceIdentifier nodeIdentifier) {
+        // if it is list, real parent is second to last
+        return getNodeParent(nodeIdentifier).map(parent -> getNodeParent(parent).orElse(null))
+                .orElseThrow(() -> new IllegalArgumentException(
+                        String.format("Unable to get parent for list node from %s", nodeIdentifier)));
+    }
+
+    private static YangInstanceIdentifier.NodeIdentifierWithPredicates listNodeIdentifier(@Nonnull final YangInstanceIdentifier nodeIdentifier) {
+        return java.util.Optional.ofNullable(nodeIdentifier.getLastPathArgument())
+                .filter(pathArgument -> pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates)
+                .map(YangInstanceIdentifier.NodeIdentifierWithPredicates.class::cast)
+                .orElseThrow(() -> new IllegalArgumentException(
+                        String.format("Unable to create list node identifier from %s", nodeIdentifier)));
+    }
+}
+
+
+
index 23af973..017a68c 100644 (file)
 
 package io.fd.honeycomb.test.tools;
 
-import static com.google.common.base.Preconditions.checkState;
-
 import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import javassist.ClassPool;
 import org.apache.commons.lang3.reflect.MethodUtils;
 import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
@@ -34,6 +29,12 @@ import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
 import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static com.google.common.base.Preconditions.checkState;
+
 /**
  * Common logic to initialize serializers/deserializers/etc while working with yang based data
  */
@@ -67,14 +68,13 @@ interface YangContextProducer {
         return (AbstractModuleStringInstanceIdentifierCodec) cstr.newInstance(ctx.getSchemaContext(), jsonCodecFactory);
     }
 
-    default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext,
-                                                          final SchemaContext schemaContexts) {
+    default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext) {
 
         final BindingNormalizedNodeCodecRegistry codecRegistry =
                 new BindingNormalizedNodeCodecRegistry(
                         StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault())));
         codecRegistry
-                .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, schemaContexts));
+                .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, moduleInfoBackedContext.getSchemaContext()));
         return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry);
     }
 }
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java
new file mode 100644 (file)
index 0000000..a353e4b
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.slf4j.Logger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+interface YangDataProcessor {
+
+    /**
+     * Attempts to find data in file specified by <b>resourcePath</b>,<br>
+     * and translate it to BA object
+     *
+     * @param yangInstanceIdentifier identifier of path to read
+     * @param resourcePath           path of resource file to load
+     */
+    @Nonnull
+    DataObject getNodeData(@Nonnull final YangInstanceIdentifier yangInstanceIdentifier,
+                           @Nonnull final String resourcePath);
+
+    /**
+     * Verifies if provided identifier is identifying node processed by this processor
+     *
+     * @param identifier node identifier
+     */
+    boolean canProcess(@Nonnull final YangInstanceIdentifier identifier);
+
+    default boolean isRoot(@Nonnull final YangInstanceIdentifier identifier) {
+        return identifier.getPathArguments().isEmpty();
+    }
+
+    @Nonnull
+    default Optional<YangInstanceIdentifier> getNodeParent(@Nonnull final YangInstanceIdentifier identifier) {
+        return Optional.ofNullable(identifier.getParent());
+    }
+
+    @Nonnull
+    default SchemaNode parentSchema(@Nonnull final SchemaContext schemaContext,
+                                    @Nonnull final BindingToNormalizedNodeCodec serializer,
+                                    @Nullable final YangInstanceIdentifier parentYangId,
+                                    @Nonnull final Supplier<Logger> logProvider) {
+        // null or root
+        if (parentYangId == null || parentYangId.getPathArguments().size() == 0) {
+            // no parent == use schema context as root context
+            logProvider.get().info("Parent is null, providing schema context as parent node");
+            return schemaContext;
+        }
+
+        final com.google.common.base.Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId;
+        try {
+            parentInstanceId = serializer.toBinding(parentYangId);
+        } catch (DeserializationException e) {
+            throw new IllegalArgumentException(String.format("Unable to deserialize %s", parentYangId));
+        }
+
+        if (!parentInstanceId.isPresent()) {
+            throw new IllegalStateException(String.format("Unable to resolve %s to instance identifier", parentYangId));
+        }
+
+        final com.google.common.base.Optional<DataNodeContainer> dataNodeContainerOptional =
+                BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get());
+
+
+        if (!dataNodeContainerOptional.isPresent()) {
+            throw new IllegalArgumentException(String.format("Error finding DataNodeContainer for %s", parentInstanceId.get()));
+        }
+
+        final DataNodeContainer parentNode = dataNodeContainerOptional.get();
+        logProvider.get().info("Parent schema node resolved as {}", parentNode);
+        return (SchemaNode) parentNode;
+    }
+
+    @Nonnull
+    default Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> nodeBinding(@Nonnull final BindingToNormalizedNodeCodec serializer,
+                                                                                        @Nonnull final YangInstanceIdentifier identifier,
+                                                                                        @Nonnull final NormalizedNode<?, ?> data) {
+        try {
+            return serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(identifier, data))
+                    .or(() -> {
+                        throw new IllegalArgumentException(String.format("Unable to create node binding  for %s|%s", identifier, data));
+                    });
+        } catch (DeserializationException e) {
+            throw new IllegalArgumentException(String.format("Unable to deserialize node %s|%s", identifier, data));
+        }
+    }
+
+    @Nonnull
+    default InstanceIdentifier<? extends DataObject> identifierBinding(@Nonnull final BindingToNormalizedNodeCodec serializer,
+                                                                       @Nonnull final YangInstanceIdentifier identifier) {
+        try {
+            return serializer.toBinding(identifier)
+                    .or(() -> {
+                        throw new IllegalArgumentException(String.format("Unable convert %s to binding", identifier));
+                    });
+        } catch (DeserializationException e) {
+            throw new IllegalArgumentException(String.format("Unable to deserialize %s", identifier));
+        }
+    }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java
new file mode 100644 (file)
index 0000000..d23cd59
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import javax.annotation.Nonnull;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Process yang data from json to BA Objects
+ */
+final class YangDataProcessorRegistry {
+
+    private final List<YangDataProcessor> processors;
+
+    private YangDataProcessorRegistry(@Nonnull final SchemaContext context,
+                                      @Nonnull final BindingToNormalizedNodeCodec codec) {
+        // linked should be faster for iteration
+        processors = new LinkedList<>();
+        processors.add(new ListNodeDataProcessor(context, codec));
+        processors.add(new ContainerNodeDataProcessor(context, codec));
+    }
+
+    static YangDataProcessorRegistry create(@Nonnull final SchemaContext context,
+                                                   @Nonnull final BindingToNormalizedNodeCodec codec) {
+        return new YangDataProcessorRegistry(context, codec);
+    }
+
+    @Nonnull
+    DataObject getNodeData(@Nonnull final YangInstanceIdentifier yangInstanceIdentifier,
+                                  @Nonnull final String resourcePath) {
+        return pickProcessor(yangInstanceIdentifier).getNodeData(yangInstanceIdentifier, resourcePath);
+    }
+
+    private YangDataProcessor pickProcessor(final YangInstanceIdentifier yangInstanceIdentifier) {
+        final List<YangDataProcessor> eligibleProcessors = processors.stream()
+                .filter(processors -> processors.canProcess(yangInstanceIdentifier))
+                .collect(Collectors.toList());
+
+        // canProcess should be exclusive for node type, but just in case
+        checkState(eligibleProcessors.size() == 1,
+                "No single eligible processor found, matches=[%s]", eligibleProcessors);
+
+        return eligibleProcessors.get(0);
+    }
+}
index c364938..216d835 100644 (file)
 
 package io.fd.honeycomb.test.tools.annotations;
 
-import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID;
-
-import com.google.common.base.Optional;
-import java.lang.reflect.Field;
-import java.lang.reflect.Parameter;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
 import org.apache.commons.lang3.reflect.FieldUtils;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl;
 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+
+import javax.annotation.Nonnull;
+import java.lang.reflect.Field;
+import java.lang.reflect.Parameter;
+import java.util.List;
+import java.util.Set;
+
+import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID;
 
 /**
  * Common logic for @InjectTestData
@@ -54,23 +53,23 @@ public interface InjectablesProcessor {
         return parameter.getAnnotation(InjectTestData.class).resourcePath();
     }
 
-    default Optional<String> instanceIdentifier(final Field field) {
+    default YangInstanceIdentifier instanceIdentifier(@Nonnull final AbstractModuleStringInstanceIdentifierCodec parser, @Nonnull final Field field) {
         final String identifier = field.getAnnotation(InjectTestData.class).id();
         // == used instead of equals to ensure constant was used
         if (NO_ID.equals(identifier)) {
-            return Optional.absent();
+            return getRootInstanceIdentifier(field.getType());
         } else {
-            return Optional.of(identifier);
+            return parser.deserialize(identifier);
         }
     }
 
-    default Optional<String> instanceIdentifier(final Parameter parameter) {
+    default YangInstanceIdentifier instanceIdentifier(@Nonnull final AbstractModuleStringInstanceIdentifierCodec parser, @Nonnull final Parameter parameter) {
         final String identifier = parameter.getAnnotation(InjectTestData.class).id();
         // == used instead of equals to ensure constant was used
         if (NO_ID.equals(identifier)) {
-            return Optional.absent();
+            return getRootInstanceIdentifier(parameter.getType());
         } else {
-            return Optional.of(identifier);
+            return parser.deserialize(identifier);
         }
     }
 
@@ -83,7 +82,7 @@ public interface InjectablesProcessor {
         }
     }
 
-    default YangInstanceIdentifier getRootInstanceIdentifier(final Class type) {
+    static YangInstanceIdentifier getRootInstanceIdentifier(final Class type) {
         try {
             return YangInstanceIdentifier.of(QName.class.cast(type.getField("QNAME").get(null)));
         } catch (IllegalAccessException e) {
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java
deleted file mode 100644 (file)
index 0581212..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.test.tools.factories;
-
-
-import com.google.common.base.Optional;
-import java.io.IOException;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
-import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ChildNodeDataFactory extends YangDataFactory {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ChildNodeDataFactory.class);
-
-    public ChildNodeDataFactory(@Nonnull final SchemaContext schemaContext,
-                                @Nonnull final BindingToNormalizedNodeCodec serializer,
-                                @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) {
-        super(schemaContext, serializer, iidParser);
-    }
-
-    public DataObject getChildNodeData(final String instanceIdentifier,
-                                       final String resourcePath) throws DeserializationException, IOException {
-        // Parse string ID into YangId
-        final YangInstanceIdentifier nodeYid = iidParser.deserialize(instanceIdentifier);
-        // Look for parent YangId
-        final YangInstanceIdentifier parentYid = nodeYid.getParent();
-
-        if (parentYid.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Attempt to process root node as children has been detected,to process root nodes just don't use id in @InjectTestData");
-        }
-        // Find Schema node for parent of data that's currently being parsed (needed when parsing the data into NormalizedNodes)
-        return getDataForNode(nodeYid, resourcePath, getNonRootParentSchema(parentYid));
-    }
-
-    private DataNodeContainer getNonRootParentSchema(final YangInstanceIdentifier parentYangId)
-            throws DeserializationException {
-        LOG.debug("Processing parent identifier {}", parentYangId);
-        final Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId = serializer.toBinding(parentYangId);
-        if (!parentInstanceId.isPresent()) {
-            throw new IllegalStateException("Unable to resolve " + parentYangId + " to instance identifier");
-        }
-
-        final Optional<DataNodeContainer> dataNodeContainerOptional =
-                BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get());
-
-        if (!dataNodeContainerOptional.isPresent()) {
-            throw new IllegalArgumentException("Error finding DataNodeContainer for " + parentInstanceId.get());
-        }
-
-        return dataNodeContainerOptional.get();
-    }
-}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java
deleted file mode 100644 (file)
index 3aacea0..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.test.tools.factories;
-
-
-import java.io.IOException;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-public class RootNodeDataFactory extends YangDataFactory {
-
-    public RootNodeDataFactory(@Nonnull final SchemaContext schemaContext,
-                               @Nonnull final BindingToNormalizedNodeCodec serializer,
-                               @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) {
-        super(schemaContext, serializer, iidParser);
-    }
-
-    public DataObject getRootNodeData(final YangInstanceIdentifier rootInstanceIdentifier,
-                                       final String resourcePath) throws DeserializationException, IOException {
-        //entire schema context is parent schema in this case
-        return getDataForNode(rootInstanceIdentifier, resourcePath, schemaContext);
-    }
-}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java
deleted file mode 100644 (file)
index ecf556c..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.test.tools.factories;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import io.fd.honeycomb.translate.util.JsonUtils;
-import java.io.IOException;
-import java.util.AbstractMap;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-
-/**
- * Common logic for reading of yang data
- */
-abstract class YangDataFactory {
-
-    final SchemaContext schemaContext;
-    final BindingToNormalizedNodeCodec serializer;
-    final AbstractModuleStringInstanceIdentifierCodec iidParser;
-
-    YangDataFactory(@Nonnull final SchemaContext schemaContext,
-                    @Nonnull final BindingToNormalizedNodeCodec serializer,
-                    @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) {
-        this.schemaContext = checkNotNull(schemaContext, "SchemaContext cannot be null");
-        this.serializer = checkNotNull(serializer, "Serializer cannot be null");
-        this.iidParser = checkNotNull(iidParser, "Instance identifier parser cannot be null");
-    }
-
-    DataObject getDataForNode(final YangInstanceIdentifier nodeYangIdentifier,
-                              final String resourcePath,
-                              final DataNodeContainer parentSchema)
-            throws DeserializationException, IOException {
-
-        // Reads resources from provided resource path
-        final ContainerNode rootData = getCheckedRootData(resourcePath, parentSchema);
-
-        // Now transform the single child from JSON into BA format
-        final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData =
-                extractCheckedSingleChild(rootData);
-
-        return getCheckedBinding(nodeYangIdentifier, actualData).getValue();
-    }
-
-    private ContainerNode getCheckedRootData(final String resourcePath, final DataNodeContainer parentSchema)
-            throws IOException {
-        // TODO the cast to SchemaNode is dangerous and would not work for Augments, Choices and some other nodes maybe. At least check
-        // TODO not sure if this is true, while testing this code was working fine event while processing choices/cases,
-        // TODO only problem is to find suitable codec that can process cases,etc
-        // Transform JSON into NormalizedNode
-
-        final ContainerNode rootData = JsonUtils.readJson(schemaContext,
-                checkNotNull(this.getClass().getResource(resourcePath), "Unable to find resource %s", resourcePath)
-                        .openStream(), ((SchemaNode) parentSchema));
-
-        checkArgument(rootData.getValue().size() == 1, "Only a single data node is expected in %s, but there were: %s",
-                resourcePath, rootData.getValue());
-        return rootData;
-    }
-
-    private DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> extractCheckedSingleChild(
-            final ContainerNode rootData) {
-        // Now transform the single child from JSON into BA format
-        final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData =
-                Iterables.getFirst(rootData.getValue(), null);
-
-        checkNotNull(actualData, "Unable to extract single child from %s", rootData);
-        return actualData;
-    }
-
-    private Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> getCheckedBinding(
-            final YangInstanceIdentifier nodeYangIdentifier,
-            final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData)
-            throws DeserializationException {
-        final Optional<Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject>> ba =
-                serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(nodeYangIdentifier, actualData));
-
-        checkArgument(ba.isPresent(), "Unable to convert to binding %s", nodeYangIdentifier);
-        return ba.get();
-    }
-}
diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/AbstractYangDataProcessorTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/AbstractYangDataProcessorTest.java
new file mode 100644 (file)
index 0000000..2d5f025
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+
+import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor;
+import org.junit.Before;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+
+abstract class AbstractYangDataProcessorTest implements InjectablesProcessor, YangContextProducer {
+
+    ModuleInfoBackedContext moduleInfoBackedContext;
+    AbstractModuleStringInstanceIdentifierCodec codec;
+    BindingToNormalizedNodeCodec serializer;
+
+    @Before
+    public void init() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        moduleInfoBackedContext = provideSchemaContextFor(Collections.singleton($YangModuleInfoImpl.getInstance()));
+        codec = getIIDCodec(moduleInfoBackedContext);
+        serializer = createSerializer(moduleInfoBackedContext);
+
+        // to init children
+        setUp();
+    }
+
+    // for children init
+    abstract void setUp();
+}
diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessorTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessorTest.java
new file mode 100644 (file)
index 0000000..e1dbc18
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import static org.junit.Assert.*;
+
+public class ContainerNodeDataProcessorTest extends AbstractYangDataProcessorTest {
+
+    private ContainerNodeDataProcessor processor;
+
+    @Override
+    void setUp() {
+        processor = new ContainerNodeDataProcessor(moduleInfoBackedContext.getSchemaContext(), serializer);
+    }
+
+    @Test
+    public void testGetNodeDataNestedContainer() {
+        final DataObject nodeData = processor.getNodeData(codec.deserialize(InjectionTestData.CONTAINER_UNDER_LIST_DATA_PATH),
+                InjectionTestData.CONTAINER_UNDER_LIST_RESOURCE);
+        assertNotNull(nodeData);
+    }
+
+    @Test
+    public void testGetNodeDataRootContainer() {
+        final DataObject nodeData = processor.getNodeData(YangInstanceIdentifier.EMPTY, InjectionTestData.CONTAINER_IN_ROOT_RESOURCE);
+        assertNotNull(nodeData);
+    }
+
+
+    @Test
+    public void testCanProcessNegative() {
+        assertFalse(processor.canProcess(codec.deserialize(InjectionTestData.SIMPLE_LIST_DATA_PATH)));
+        assertFalse(processor.canProcess(codec.deserialize(InjectionTestData.NESTED_LIST_DATA_PATH)));
+    }
+
+    @Test
+    public void testCanProcessPositive() {
+        assertTrue(processor.canProcess(YangInstanceIdentifier.EMPTY));
+        assertTrue(processor.canProcess(codec.deserialize(InjectionTestData.CONTAINER_UNDER_LIST_DATA_PATH)));
+    }
+}
 
 package io.fd.honeycomb.test.tools;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
 import io.fd.honeycomb.test.tools.annotations.InjectTestData;
 import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor;
 import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider;
-import java.util.Collections;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.AugContainerAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.SimpleContainer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.SimpleContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.*;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.NestedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.SimpleList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.augmented.container.ListInAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.ContUnderList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.ContUnderListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.NestedList;
 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 
+import java.util.Collections;
+
+import static io.fd.honeycomb.test.tools.InjectionTestData.*;
+import static org.junit.Assert.*;
+
 @RunWith(HoneycombTestRunner.class)
-public class HoneycombTestRunnerTest implements InjectablesProcessor {
+public class HoneycombTestRunnerContainerTest implements InjectablesProcessor {
 
     @InjectTestData(resourcePath = "/simpleContainerEmpty.json")
     private SimpleContainer simpleContainer;
@@ -52,6 +52,19 @@ public class HoneycombTestRunnerTest implements InjectablesProcessor {
             "/hc-data:cont-under-list")
     private ContUnderList containerUnderList;
 
+    @InjectTestData(resourcePath = AUGMENT_LIST_RESOURCE, id = AUGMENT_LIST_DATA_PATH)
+    private ListInAugment listInAugment;
+
+    @InjectTestData(resourcePath = NESTED_LIST_RESOURCE, id = NESTED_LIST_DATA_PATH)
+    private NestedList nestedList;
+
+    @InjectTestData(resourcePath = ROOT_LIST_RESOURCE, id = ROOT_LIST_DATA_PATH)
+    private RootList rootList;
+
+    @InjectTestData(resourcePath = SIMPLES_LIST_RESOURCE, id = SIMPLE_LIST_DATA_PATH)
+    private SimpleList simpleList;
+
+
     @SchemaContextProvider
     public ModuleInfoBackedContext getSchemaContext() {
         return provideSchemaContextFor(Collections.singleton($YangModuleInfoImpl.getInstance()));
diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/InjectionTestData.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/InjectionTestData.java
new file mode 100644 (file)
index 0000000..4d4ab54
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+final class InjectionTestData {
+
+    static final String CONTAINER_UNDER_LIST_DATA_PATH = "/hc-data:simple-container" +
+            "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']" +
+            "/hc-data:cont-under-list";
+
+    static final String SIMPLE_LIST_DATA_PATH = "/hc-data:simple-container" +
+            "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']";
+
+    static final String NESTED_LIST_DATA_PATH = "/hc-data:simple-container" +
+            "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']" +
+            "/hc-data:nested-list[hc-data:nested-name='nameUnderNestedList']";
+
+    static final String ROOT_LIST_DATA_PATH = "/hc-data:root-list[hc-data:root-name='rootName']";
+    static final String AUGMENT_LIST_DATA_PATH = "/hc-data:simple-container" +
+            "/hc-data:augmented-container" +
+            "/hc-data:list-in-augment[hc-data:key-in-augment='keyInAugment']";
+
+    static final String CONTAINER_IN_ROOT_RESOURCE = "/simpleContainerEmpty.json";
+    static final String CONTAINER_UNDER_LIST_RESOURCE = "/containerInList.json";
+    static final String SIMPLES_LIST_RESOURCE = "/simpleListEntry.json";
+    static final String NESTED_LIST_RESOURCE = "/nestedListEntry.json";
+    static final String ROOT_LIST_RESOURCE = "/rootListEntry.json";
+    static final String AUGMENT_LIST_RESOURCE = "/augmentListEntry.json";
+
+    private InjectionTestData() {
+    }
+}
diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ListNodeDataProcessorTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ListNodeDataProcessorTest.java
new file mode 100644 (file)
index 0000000..1118d33
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.RootList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.SimpleList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.augmented.container.ListInAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.NestedList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import static io.fd.honeycomb.test.tools.InjectionTestData.*;
+import static org.junit.Assert.*;
+
+public class ListNodeDataProcessorTest extends AbstractYangDataProcessorTest {
+
+    private ListNodeDataProcessor processor;
+
+    @Override
+    void setUp() {
+        processor = new ListNodeDataProcessor(moduleInfoBackedContext.getSchemaContext(), serializer);
+    }
+
+    @Test
+    public void testCanProcessPositive() {
+        assertTrue(processor.canProcess(codec.deserialize(SIMPLE_LIST_DATA_PATH)));
+        assertTrue(processor.canProcess(codec.deserialize(NESTED_LIST_DATA_PATH)));
+    }
+
+    @Test
+    public void testCanProcessNegative() {
+        assertFalse(processor.canProcess(codec.deserialize(CONTAINER_UNDER_LIST_DATA_PATH)));
+        assertFalse(processor.canProcess(YangInstanceIdentifier.EMPTY));
+    }
+
+    @Test
+    public void testGetNodeDataSimpleList() {
+        final DataObject nodeData = processor.getNodeData(codec.deserialize(SIMPLE_LIST_DATA_PATH), SIMPLES_LIST_RESOURCE);
+        assertNotNull(nodeData);
+        assertEquals("nameUnderSimpleList", ((SimpleList) nodeData).getName());
+    }
+
+    @Test
+    public void testGetNodeDataNestedList() {
+        final DataObject nodeData = processor.getNodeData(codec.deserialize(NESTED_LIST_DATA_PATH), NESTED_LIST_RESOURCE);
+        assertNotNull(nodeData);
+        assertEquals("nameUnderNestedList", ((NestedList) nodeData).getNestedName());
+    }
+
+    @Test
+    public void testGetNodeDataRootList() {
+        final DataObject nodeData = processor.getNodeData(codec.deserialize(ROOT_LIST_DATA_PATH), ROOT_LIST_RESOURCE);
+        assertNotNull(nodeData);
+        assertEquals("rootName", ((RootList) nodeData).getRootName());
+    }
+
+    @Test
+    public void testGetNodeDataAugmentList() {
+        final DataObject nodeData = processor.getNodeData(codec.deserialize(AUGMENT_LIST_DATA_PATH), AUGMENT_LIST_RESOURCE);
+        assertNotNull(nodeData);
+        assertEquals("keyInAugment", ((ListInAugment) nodeData).getKeyInAugment());
+    }
+}
diff --git a/infra/test-utils/test-tools/src/test/resources/augmentListEntry.json b/infra/test-utils/test-tools/src/test/resources/augmentListEntry.json
new file mode 100644 (file)
index 0000000..1bdc3e6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "list-in-augment":[
+    {
+      "key-in-augment":"keyInAugment"
+    },
+    {
+      "key-in-augment":"otherKeyInAugment"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/test/resources/nestedListEntry.json b/infra/test-utils/test-tools/src/test/resources/nestedListEntry.json
new file mode 100644 (file)
index 0000000..c874f1c
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "nested-list": [
+    {
+      "nested-name": "nameUnderNestedList"
+    },
+    {
+      "nested-name": "otherName"
+    }
+  ]
+}
diff --git a/infra/test-utils/test-tools/src/test/resources/rootListEntry.json b/infra/test-utils/test-tools/src/test/resources/rootListEntry.json
new file mode 100644 (file)
index 0000000..cd09c68
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "root-list":[
+    {
+      "root-name":"rootName"
+    },
+    {
+      "root-name":"otherRootName"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/test/resources/simpleListEntry.json b/infra/test-utils/test-tools/src/test/resources/simpleListEntry.json
new file mode 100644 (file)
index 0000000..64ad3e6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "simple-list": [
+    {
+      "name": "nameUnderSimpleList"
+    },
+    {
+      "name": "otherName"
+    }
+  ]
+}
index 3216cac..332bf67 100644 (file)
@@ -19,15 +19,10 @@ package io.fd.honeycomb.translate.util;
 import com.google.common.base.Charsets;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
@@ -44,6 +39,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nonnull;
+import java.io.*;
+
 public final class JsonUtils {
 
     private static final Logger LOG = LoggerFactory.getLogger(JsonUtils.class);
@@ -101,6 +99,43 @@ public final class JsonUtils {
         return builder.build();
     }
 
+    public static ContainerNode readContainerEntryJson(@Nonnull final SchemaContext schemaContext,
+                                                       @Nonnull final InputStream stream,
+                                                       @Nonnull final SchemaNode parentSchema,
+                                                       @Nonnull final YangInstanceIdentifier.NodeIdentifier nodeIdentifier) {
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder =
+                Builders.containerBuilder().withNodeIdentifier(nodeIdentifier);
+        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder);
+
+        try (final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext, parentSchema)) {
+            final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8));
+            jsonParser.parse(reader);
+        } catch (IOException e) {
+            LOG.warn("Unable to close json parser. Ignoring exception", e);
+        }
+
+        return builder.build();
+    }
+
+    public static MapEntryNode readListEntryFromJson(@Nonnull final SchemaContext schemaContext,
+                                                     @Nonnull final InputStream stream,
+                                                     @Nonnull final SchemaNode parentSchema,
+                                                     @Nonnull final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifier) {
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = Builders.mapEntryBuilder()
+                .withNodeIdentifier(nodeIdentifier);
+
+
+        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(mapEntryBuilder);
+        try (final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext, parentSchema)) {
+            final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8));
+            jsonParser.parse(reader);
+        } catch (IOException e) {
+            LOG.warn("Unable to close json parser. Ignoring exception", e);
+        }
+
+        return mapEntryBuilder.build();
+    }
+
     private static void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException {
         for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> child : data.getValue()) {
             nnWriter.write(child);