Merge "BUG-576: fixed possible null pointer dereference in BindingGeneratorImpl"
authorRobert Varga <rovarga@cisco.com>
Fri, 29 Aug 2014 20:54:16 +0000 (20:54 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 29 Aug 2014 20:54:16 +0000 (20:54 +0000)
28 files changed:
code-generator/binding-data-codec/pom.xml
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/api/BindingNormalizedNodeSerializer.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingNormalizedNodeCodecRegistry.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AbstractBindingRuntimeTest.java [new file with mode: 0644]
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java [new file with mode: 0644]
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/CaseSubstitutionTest.java [new file with mode: 0644]
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierSerializeDeserializeTest.java [new file with mode: 0644]
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierTest.java [new file with mode: 0644]
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/NormalizedNodeSerializeDeserializeTest.java [new file with mode: 0644]
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend
code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/NestedGroupingCompilationTest.java [new file with mode: 0644]
code-generator/binding-java-api-generator/src/test/resources/compilation/nested-grouping/test.yang [new file with mode: 0644]
code-generator/binding-test-model/pom.xml [new file with mode: 0644]
code-generator/binding-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java [new file with mode: 0644]
code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang [new file with mode: 0644]
code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-binding-test.yang [new file with mode: 0644]
code-generator/binding-type-provider/src/main/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImpl.java
code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend
code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java
code-generator/pom.xml
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java

index 80cfe17b79577474ab8d120448007209aca5feef..b8bf7e11ef1f9bcb5e6dc008239c077166c63c83 100644 (file)
             <artifactId>jsr305</artifactId>
             <scope>provided</scope>
         </dependency>
-
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-test-model</artifactId>
+            <scope>test</scope>
         </dependency>
+
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
@@ -65,9 +66,9 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.xtend</groupId>
-            <artifactId>org.eclipse.xtend.lib</artifactId>
+            <dependency><groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
index b75afa8b5d2b593521c8b4b0a77e021b5966a9c7..3e10492e10c75a4d573c5b06fe52797fee54f454 100644 (file)
@@ -10,6 +10,9 @@ package org.opendaylight.yangtools.binding.data.codec.api;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -18,7 +21,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 /**
  * Serialization service, which provides two-way serialization between
  * Java Binding Data representation and NormalizedNode representation.
- *
  */
 public interface BindingNormalizedNodeSerializer {
 
@@ -28,15 +30,16 @@ public interface BindingNormalizedNodeSerializer {
       * @param binding Binding Instance Identifier
       * @return DOM Instance Identifier
       */
-     YangInstanceIdentifier toYangInstanceIdentifier(InstanceIdentifier<?> binding);
+     YangInstanceIdentifier toYangInstanceIdentifier(@Nonnull InstanceIdentifier<?> binding);
 
      /**
       * Translates supplied YANG Instance Identifier into Binding instance identifier.
       *
       * @param dom YANG Instance Identifier
-      * @return Binding Instance Identifier
+      * @return Binding Instance Identifier, or null if the instance identifier is not
+      *         representable.
       */
-     InstanceIdentifier<?> fromYangInstanceIdentifier(YangInstanceIdentifier dom);
+     @Nullable InstanceIdentifier<?> fromYangInstanceIdentifier(@Nonnull YangInstanceIdentifier dom);
 
      /**
       * Translates supplied Binding Instance Identifier and data into NormalizedNode representation.
@@ -45,7 +48,7 @@ public interface BindingNormalizedNodeSerializer {
       * @param data Data object representing data
       * @return NormalizedNode representation
       */
-     <T extends DataObject> Entry<YangInstanceIdentifier,NormalizedNode<?,?>> toNormalizedNode(InstanceIdentifier<T> path, T data);
+     <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?,?>> toNormalizedNode(InstanceIdentifier<T> path, T data);
 
      /**
       * Translates supplied YANG Instance Identifier and NormalizedNode into Binding data.
@@ -54,7 +57,7 @@ public interface BindingNormalizedNodeSerializer {
       * @param data NormalizedNode representing data
       * @return DOM Instance Identifier
       */
-     Entry<InstanceIdentifier<?>,DataObject> fromNormalizedNode(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+     @Nullable Entry<InstanceIdentifier<?>,DataObject> fromNormalizedNode(@Nonnull YangInstanceIdentifier path, NormalizedNode<?, ?> data);
 
      /**
       * Returns map view which contains translated set of entries to normalized nodes.
index 46fd45960f3572affcd4993d08d8b87e6489f9e0..6d1e31c2b47a1a3b7589ee060f89cbcd39156633 100644 (file)
@@ -18,6 +18,7 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -25,6 +26,9 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.Callable;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 import org.opendaylight.yangtools.binding.data.codec.impl.NodeCodecContext.CodecContextFactory;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.concepts.Immutable;
@@ -52,10 +56,13 @@ import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 class BindingCodecContext implements CodecContextFactory, Immutable {
-
+    private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class);
     private static final String GETTER_PREFIX = "get";
+
     private final SchemaRootCodecContext root;
     private final BindingRuntimeContext context;
     private final Codec<YangInstanceIdentifier, InstanceIdentifier<?>> instanceIdentifierCodec =
@@ -72,7 +79,7 @@ class BindingCodecContext implements CodecContextFactory, Immutable {
         return context;
     }
 
-    public Codec<YangInstanceIdentifier, InstanceIdentifier<?>> getInstanceIdentifierCodec() {
+    Codec<YangInstanceIdentifier, InstanceIdentifier<?>> getInstanceIdentifierCodec() {
         return instanceIdentifierCodec;
     }
 
@@ -102,14 +109,27 @@ class BindingCodecContext implements CodecContextFactory, Immutable {
         return currentNode;
     }
 
-    public NodeCodecContext getCodecContextNode(final YangInstanceIdentifier dom,
-            final List<InstanceIdentifier.PathArgument> builder) {
+    /**
+     * Multi-purpose utility function. Traverse the codec tree, looking for
+     * the appropriate codec for the specified {@link YangInstanceIdentifier}.
+     * As a side-effect, gather all traversed binding {@link InstanceIdentifier.PathArgument}s
+     * into the supplied collection.
+     *
+     * @param dom {@link YangInstanceIdentifier} which is to be translated
+     * @param bindingArguments Collection for traversed path arguments
+     * @return Codec for target node, or @null if the node does not have a
+     *         binding representation (choice, case, leaf).
+     */
+    @Nullable NodeCodecContext getCodecContextNode(final @Nonnull YangInstanceIdentifier dom,
+            final @Nonnull Collection<InstanceIdentifier.PathArgument> bindingArguments) {
         NodeCodecContext currentNode = root;
         ListNodeCodecContext currentList = null;
+
         for (YangInstanceIdentifier.PathArgument domArg : dom.getPathArguments()) {
-            Preconditions.checkArgument(currentNode instanceof DataContainerCodecContext<?>);
-            DataContainerCodecContext<?> previous = (DataContainerCodecContext<?>) currentNode;
-            NodeCodecContext nextNode = previous.getYangIdentifierChild(domArg);
+            Preconditions.checkArgument(currentNode instanceof DataContainerCodecContext<?>, "Unexpected child of non-container node %s", currentNode);
+            final DataContainerCodecContext<?> previous = (DataContainerCodecContext<?>) currentNode;
+            final NodeCodecContext nextNode = previous.getYangIdentifierChild(domArg);
+
             /*
              * List representation in YANG Instance Identifier consists of two
              * arguments: first is list as a whole, second is list as an item so
@@ -119,19 +139,13 @@ class BindingCodecContext implements CodecContextFactory, Immutable {
              * Identifier as Item or IdentifiableItem
              */
             if (currentList != null) {
+                Preconditions.checkArgument(currentList == nextNode, "List should be referenced two times in YANG Instance Identifier %s", dom);
 
-                if (currentList == nextNode) {
-
-                    // We entered list, so now we have all information to emit
-                    // list
-                    // path using second list argument.
-                    builder.add(currentList.getBindingPathArgument(domArg));
-                    currentList = null;
-                    currentNode = nextNode;
-                } else {
-                    throw new IllegalArgumentException(
-                            "List should be referenced two times in YANG Instance Identifier");
-                }
+                // We entered list, so now we have all information to emit
+                // list path using second list argument.
+                bindingArguments.add(currentList.getBindingPathArgument(domArg));
+                currentList = null;
+                currentNode = nextNode;
             } else if (nextNode instanceof ListNodeCodecContext) {
                 // We enter list, we do not update current Node yet,
                 // since we need to verify
@@ -141,24 +155,27 @@ class BindingCodecContext implements CodecContextFactory, Immutable {
                 // it is not supported by binding instance identifier.
                 currentNode = nextNode;
             } else if (nextNode instanceof DataContainerCodecContext<?>) {
-                builder.add(((DataContainerCodecContext<?>) nextNode).getBindingPathArgument(domArg));
+                bindingArguments.add(((DataContainerCodecContext<?>) nextNode).getBindingPathArgument(domArg));
                 currentNode = nextNode;
             } else if (nextNode instanceof LeafNodeCodecContext) {
-                Preconditions.checkArgument(builder == null, "Instance Identifier for leaf is not representable.");
-
+                LOG.debug("Instance identifier referencing a leaf is not representable (%s)", dom);
+                return null;
             }
         }
+
         // Algorithm ended in list as whole representation
         // we sill need to emit identifier for list
-
-        if (builder != null) {
-            Preconditions.checkArgument(!(currentNode instanceof ChoiceNodeCodecContext),
-                    "Instance Identifier for choice is not representable.");
-            Preconditions.checkArgument(!(currentNode instanceof CaseNodeCodecContext),
-                    "Instance Identifier for case is not representable.");
+        if (currentNode instanceof ChoiceNodeCodecContext) {
+            LOG.debug("Instance identifier targeting a choice is not representable (%s)", dom);
+            return null;
+        }
+        if (currentNode instanceof CaseNodeCodecContext) {
+            LOG.debug("Instance identifier targeting a case is not representable (%s)", dom);
+            return null;
         }
+
         if (currentList != null) {
-            builder.add(currentList.getBindingPathArgument(null));
+            bindingArguments.add(currentList.getBindingPathArgument(null));
             return currentList;
         }
         return currentNode;
@@ -264,12 +281,11 @@ class BindingCodecContext implements CodecContextFactory, Immutable {
         } else if (rootType instanceof InstanceIdentifierTypeDefinition) {
             return ValueTypeCodec.encapsulatedValueCodecFor(valueType, instanceIdentifierCodec);
         } else if (rootType instanceof UnionTypeDefinition) {
-            Callable<UnionTypeCodec> loader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) rootType,
-                    getInstanceIdentifierCodec(), getIdentityCodec());
+            Callable<UnionTypeCodec> loader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) rootType);
             try {
                 return loader.call();
             } catch (Exception e) {
-                throw new IllegalStateException("Unable to load codec for "+valueType,e);
+                throw new IllegalStateException("Unable to load codec for " + valueType, e);
             }
         }
         return ValueTypeCodec.getCodecFor(valueType, instantiatedType);
@@ -279,16 +295,16 @@ class BindingCodecContext implements CodecContextFactory, Immutable {
 
         @Override
         public YangInstanceIdentifier serialize(final InstanceIdentifier<?> input) {
-            List<YangInstanceIdentifier.PathArgument> domArgs = new LinkedList<>();
+            List<YangInstanceIdentifier.PathArgument> domArgs = new ArrayList<>();
             getCodecContextNode(input, domArgs);
             return YangInstanceIdentifier.create(domArgs);
         }
 
         @Override
         public InstanceIdentifier<?> deserialize(final YangInstanceIdentifier input) {
-            List<InstanceIdentifier.PathArgument> builder = new LinkedList<>();
-            getCodecContextNode(input, builder);
-            return InstanceIdentifier.create(builder);
+            final List<InstanceIdentifier.PathArgument> builder = new ArrayList<>();
+            final NodeCodecContext codec = getCodecContextNode(input, builder);
+            return codec == null ? null : InstanceIdentifier.create(builder);
         }
     }
 
index 9b772bd9dc0f344503d2e3a53c3d98bffef117a6..bfdad3efeb4cb555a22946a035ae460a8592698a 100644 (file)
@@ -16,7 +16,7 @@ import com.google.common.cache.LoadingCache;
 
 import java.io.IOException;
 import java.util.AbstractMap.SimpleEntry;
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -34,7 +34,9 @@ import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -74,23 +76,21 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
 
     @Override
     public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
-        List<YangInstanceIdentifier.PathArgument> builder = new LinkedList<>();
-        codecContext.getCodecContextNode(binding, builder);
         return codecContext.getInstanceIdentifierCodec().serialize(binding);
     }
 
     @Override
     public InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
         return codecContext.getInstanceIdentifierCodec().deserialize(dom);
-   }
+    }
 
     @Override
     public <T extends DataObject> Entry<YangInstanceIdentifier,NormalizedNode<?,?>> toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
         NormalizedNodeResult result = new NormalizedNodeResult();
-        // We create dom stream writer which produces normalized nodes
+        // We create DOM stream writer which produces normalized nodes
         NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
 
-        // We create Binding Stream Writer wchich translates from Binding to Normalized Nodes
+        // We create Binding Stream Writer which translates from Binding to Normalized Nodes
         Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx = codecContext.newWriter(path, domWriter);
 
         // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
@@ -103,26 +103,51 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
         return new SimpleEntry<YangInstanceIdentifier,NormalizedNode<?,?>>(writeCtx.getKey(),result.getResult());
     }
 
-    private boolean isBindingRepresentable(final NormalizedNode<?, ?> data) {
-        return !(data instanceof MapNode) && !(data instanceof UnkeyedListNode) && !(data instanceof LeafSetNode) && !(data instanceof LeafNode<?>);
-    }
+    private static boolean isBindingRepresentable(final NormalizedNode<?, ?> data) {
+        if (data instanceof ChoiceNode) {
+            return false;
+        }
+        if (data instanceof LeafNode<?>) {
+            return false;
+        }
+        if (data instanceof LeafSetNode) {
+            return false;
+        }
+        if( data instanceof LeafSetEntryNode<?>) {
+            return false;
+        }
+        if (data instanceof MapNode) {
+            return false;
+        }
+        if (data instanceof UnkeyedListNode) {
+            return false;
+        }
 
+        return true;
+    }
 
     @Override
-    public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
-            final NormalizedNode<?, ?> data) {
-        List<PathArgument> builder = new LinkedList<>();
-        if(isBindingRepresentable(data)) {
-            DataObject lazyObj = (DataObject) codecContext.getCodecContextNode(path, builder).dataFromNormalizedNode(data);
-            InstanceIdentifier<?> bindingPath = InstanceIdentifier.create(builder);
-            return new SimpleEntry<InstanceIdentifier<?>, DataObject>(bindingPath,lazyObj);
+    public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        if (!isBindingRepresentable(data)) {
+            return null;
         }
-        return null;
+
+        final List<PathArgument> builder = new ArrayList<>();
+        final NodeCodecContext codec = codecContext.getCodecContextNode(path, builder);
+        if (codec == null) {
+            if (data != null) {
+                LOG.warn("Path {} does not have a binding equivalent, should have been caught earlier ({})", path, data.getClass());
+            }
+            return null;
+        }
+
+        final DataObject lazyObj = (DataObject) codec.dataFromNormalizedNode(data);
+        final InstanceIdentifier<?> bindingPath = InstanceIdentifier.create(builder);
+        return new SimpleEntry<InstanceIdentifier<?>, DataObject>(bindingPath, lazyObj);
     }
 
     @Override
-    public Map<InstanceIdentifier<?>, DataObject> fromNormalizedNodes(
-            final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> dom) {
+    public Map<InstanceIdentifier<?>, DataObject> fromNormalizedNodes(final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> dom) {
         throw new UnsupportedOperationException("Not implemented yet");
     }
 
@@ -144,8 +169,8 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
 
 
     private static class DeserializeFunction<T> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
-
         private final DataObjectCodecContext<?> ctx;
+
         public DeserializeFunction(final DataObjectCodecContext<?> ctx) {
             super();
             this.ctx = ctx;
@@ -159,8 +184,6 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
             }
             return Optional.absent();
         }
-
-
     }
 
     private class GeneratorLoader extends CacheLoader<Class<? extends DataObject>, DataObjectSerializer> {
index 700476f86bae3d79c3ed8c8d4d6e4315a7bac807..bac768703c24cab5f00e1b13014143f9b5fcf218 100644 (file)
@@ -11,8 +11,13 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -36,20 +41,50 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
         Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder = new HashMap<>();
         Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
         Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
-
+        Set<Class<?>> potentialSubstitutions = new HashSet<>();
+        // Walks all cases for supplied choice in current runtime context
         for (Class<?> caze : factory().getRuntimeContext().getCases(bindingClass())) {
+            // We try to load case using exact match thus name
+            // and original schema must equals
             DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
+            // If we have case definition, this case is instantiated
+            // at current location and thus is valid in context of parent choice
             if (cazeDef != null) {
                 byClassBuilder.put(cazeDef.getBindingClass(), cazeDef);
+                // Updates collection of case children
                 for (Class<? extends DataObject> cazeChild : BindingReflections.getChildrenClasses((Class) caze)) {
                     byCaseChildClassBuilder.put(cazeChild, cazeDef);
                 }
+                // Updates collection of YANG instance identifier to case
                 for (DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
                     byYangCaseChildBuilder.put(new NodeIdentifier(cazeChild.getQName()), cazeDef);
                 }
+            } else {
+                /*
+                 * If case definition is not available, we store it for
+                 * later check if it could be used as substitution of existing one.
+                 */
+                potentialSubstitutions.add(caze);
             }
         }
 
+        Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
+        /*
+         * Walks all cases which are not directly instantiated and
+         * tries to match them to instantiated cases - represent same data as instantiated case,
+         * only case name or schema path is different. This is required due property of
+         * binding specification, that if choice is in grouping schema path location is lost,
+         * and users may use incorrect case class using copy builders.
+         */
+        for(Class<?> substitution : potentialSubstitutions) {
+            search: for(Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
+                if(BindingReflections.isSubstitutionFor(substitution, real.getKey())) {
+                    bySubstitutionBuilder.put(substitution, real.getValue());
+                    break search;
+                }
+            }
+        }
+        byClassBuilder.putAll(bySubstitutionBuilder);
         byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder);
         byClass = ImmutableMap.copyOf(byClassBuilder);
         byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder);
index 32fdf6d80ea597da1c4ec000bcecb880c9564564..19a9600f62705ee7a3e0878c5386a582192f4266 100644 (file)
@@ -107,6 +107,29 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
     @Override
     protected DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) {
         DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
+        if (childProto != null) {
+            return childProto.get();
+        }
+
+        if (Augmentation.class.isAssignableFrom(childClass))  {
+            /*
+             * It is potentially mismatched valid augmentation - we look up equivalent augmentation
+             * using reflection and walk all stream child and compare augmenations classes
+             * if they are equivalent.
+             *
+             * FIXME: Cache mapping of mismatched augmentation to real one, to speed up lookup.
+             */
+            Class<?> augTarget = BindingReflections.findAugmentationTarget((Class) childClass);
+            if ((bindingClass().equals(augTarget))) {
+                for (DataContainerCodecPrototype<?> realChild : byStreamClass.values()) {
+                    if (Augmentation.class.isAssignableFrom(realChild.getBindingClass())
+                            && BindingReflections.isSubstitutionFor(childClass,realChild.getBindingClass())) {
+                        childProto = realChild;
+                        break;
+                    }
+                }
+            }
+        }
         Preconditions.checkArgument(childProto != null, " Child %s is not valid child.",childClass);
         return childProto.get();
     }
index 6cbcc5c15680f9ef6a9247030aafa14ece6197f1..f94673c28897baa1eb67d79b12ae18efdb14a017 100644 (file)
@@ -8,13 +8,14 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.collect.ImmutableSet;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.Callable;
-import org.opendaylight.yangtools.concepts.Codec;
+
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
@@ -34,12 +35,10 @@ final class UnionTypeCodec extends ReflectionBasedCodec {
         }
     }
 
-    @SuppressWarnings("rawtypes")
-    static final Callable<UnionTypeCodec> loader(final Class<?> unionCls,final UnionTypeDefinition unionType, final Codec instanceIdentifier, final Codec identity) {
+    static final Callable<UnionTypeCodec> loader(final Class<?> unionCls, final UnionTypeDefinition unionType) {
         return new Callable<UnionTypeCodec>() {
-
             @Override
-            public UnionTypeCodec call() throws Exception {
+            public UnionTypeCodec call() throws NoSuchMethodException, SecurityException {
                 Set<UnionValueOptionContext> values = new HashSet<>();
                 for(TypeDefinition<?> subtype : unionType.getTypes()) {
                     String methodName = "get" + BindingMapping.getClassName(subtype.getQName());
diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AbstractBindingRuntimeTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AbstractBindingRuntimeTest.java
new file mode 100644 (file)
index 0000000..a56364c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.binding.data.codec.test;
+
+import org.junit.Before;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public abstract class AbstractBindingRuntimeTest {
+
+    private SchemaContext schemaContext;
+    private BindingRuntimeContext runtimeContext;
+
+    @Before
+    public void setup() {
+        ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create();
+        ctx.addModuleInfos(BindingReflections.loadModuleInfos());
+        schemaContext = ctx.tryToCreateSchemaContext().get();
+        runtimeContext = BindingRuntimeContext.create(ctx, schemaContext);
+
+    }
+
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    public BindingRuntimeContext getRuntimeContext() {
+        return runtimeContext;
+    }
+}
diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java
new file mode 100644 (file)
index 0000000..62d913f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.binding.data.codec.test;
+
+import javassist.ClassPool;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class AugmentationSubstitutionTest extends AbstractBindingRuntimeTest {
+
+    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
+            .child(TopLevelList.class, TOP_FOO_KEY).toInstance();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
+            .augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
+            .augmentation(TreeComplexUsesAugment.class);
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+    private BindingNormalizedNodeCodecRegistry registry;
+
+    @Override
+    public void setup() {
+        super.setup();
+        JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
+        registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
+        registry.onBindingRuntimeContextUpdated(getRuntimeContext());
+    }
+
+    @Test
+    public void augmentationInGroupingSubstituted() {
+        TopLevelList baRpc = new TopLevelListBuilder()
+            .setKey(TOP_FOO_KEY)
+            .addAugmentation(RpcComplexUsesAugment.class, new RpcComplexUsesAugmentBuilder(createComplexData()).build())
+            .build();
+        TopLevelList baTree = new TopLevelListBuilder()
+            .setKey(TOP_FOO_KEY)
+            .addAugmentation(TreeComplexUsesAugment.class, new TreeComplexUsesAugmentBuilder(createComplexData()).build())
+            .build();
+        NormalizedNode<?, ?> domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue();
+        NormalizedNode<?, ?> domRpcEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue();
+        assertEquals(domTreeEntry, domRpcEntry);
+    }
+
+    private RpcComplexUsesAugment createComplexData() {
+        return new RpcComplexUsesAugmentBuilder()
+        .setContainerWithUses(new ContainerWithUsesBuilder()
+            .setLeafFromGrouping("foo")
+        .build())
+        .setListViaUses(Collections.<ListViaUses>emptyList())
+        .build();
+    }
+
+}
diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/CaseSubstitutionTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/CaseSubstitutionTest.java
new file mode 100644 (file)
index 0000000..a01e12d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.binding.data.codec.test;
+
+import javassist.ClassPool;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.put.top.input.top.level.list.choice.in.list.ComplexViaUsesWithDifferentNameBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.top.top.level.list.choice.in.list.ComplexViaUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class CaseSubstitutionTest extends AbstractBindingRuntimeTest {
+
+    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
+            .child(TopLevelList.class, TOP_FOO_KEY).toInstance();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
+            .augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
+            .augmentation(TreeComplexUsesAugment.class);
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+    private BindingNormalizedNodeCodecRegistry registry;
+
+    @Override
+    public void setup() {
+        super.setup();
+        JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
+        registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
+        registry.onBindingRuntimeContextUpdated(getRuntimeContext());
+    }
+
+    @Test
+    public void choiceInGroupingSubstituted() {
+        TopLevelList baRpc = new TopLevelListBuilder()
+            .setKey(TOP_FOO_KEY)
+            .setChoiceInList(new ComplexViaUsesWithDifferentNameBuilder(createComplexData()).build())
+            .build();
+        TopLevelList baTree = new TopLevelListBuilder()
+            .setKey(TOP_FOO_KEY)
+            .setChoiceInList(new ComplexViaUsesBuilder(createComplexData()).build())
+            .build();
+        NormalizedNode<?, ?> domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue();
+        NormalizedNode<?, ?> domRpcEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue();
+        assertEquals(domTreeEntry, domRpcEntry);
+    }
+
+    private RpcComplexUsesAugment createComplexData() {
+        return new RpcComplexUsesAugmentBuilder()
+        .setContainerWithUses(new ContainerWithUsesBuilder()
+            .setLeafFromGrouping("foo")
+        .build())
+        .setListViaUses(Collections.<ListViaUses>emptyList())
+        .build();
+    }
+
+}
diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierSerializeDeserializeTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierSerializeDeserializeTest.java
new file mode 100644 (file)
index 0000000..67b7d1f
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.binding.data.codec.test;
+
+import com.google.common.collect.Iterables;
+import javassist.ClassPool;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class InstanceIdentifierSerializeDeserializeTest extends AbstractBindingRuntimeTest{
+    public static final String TOP_LEVEL_LIST_KEY_VALUE = "foo";
+
+    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
+            .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).toInstance();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY =
+            BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES =
+            BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
+
+    public static final QName TOP_QNAME =
+            QName.create("urn:opendaylight:params:xml:ns:yang:yangtools:test:binding", "2014-07-01", "top");
+    public static final QName TOP_LEVEL_LIST_QNAME = QName.create(TOP_QNAME, "top-level-list");
+    public static final QName TOP_LEVEL_LIST_KEY = QName.create(TOP_QNAME, "name");
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+    public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.builder().node(TOP_QNAME).build();
+    public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME);
+    public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_1_PATH = BI_TOP_LEVEL_LIST_PATH
+            .node(new YangInstanceIdentifier.NodeIdentifierWithPredicates(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY, TOP_LEVEL_LIST_KEY_VALUE));
+
+    private BindingNormalizedNodeCodecRegistry registry;
+
+    @Before
+    public void setup() {
+        super.setup();
+        JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
+        registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
+        registry.onBindingRuntimeContextUpdated(getRuntimeContext());
+    }
+
+    @Test
+    public void testYangIIToBindingAwareII() {
+        InstanceIdentifier<?> instanceIdentifier = registry.fromYangInstanceIdentifier(BI_TOP_PATH);
+        assertEquals(Top.class, instanceIdentifier.getTargetType());
+    }
+
+    @Test
+    public void testYangIIToBindingAwareIIListWildcarded() {
+        InstanceIdentifier<?> instanceIdentifier = registry.fromYangInstanceIdentifier(BI_TOP_LEVEL_LIST_PATH);
+        assertEquals(TopLevelList.class, instanceIdentifier.getTargetType());
+        assertTrue(instanceIdentifier.isWildcarded());
+    }
+
+    @Test
+    public void testYangIIToBindingAwareIIListWithKey() {
+        InstanceIdentifier<?> instanceIdentifier = registry.fromYangInstanceIdentifier(BI_TOP_LEVEL_LIST_1_PATH);
+        InstanceIdentifier.PathArgument last = Iterables.getLast(instanceIdentifier.getPathArguments());
+        assertEquals(TopLevelList.class, instanceIdentifier.getTargetType());
+        assertFalse(instanceIdentifier.isWildcarded());
+        assertTrue(last instanceof InstanceIdentifier.IdentifiableItem);
+        Identifier key = ((InstanceIdentifier.IdentifiableItem) last).getKey();
+        assertEquals(TopLevelListKey.class, key.getClass());
+        assertEquals(TOP_LEVEL_LIST_KEY_VALUE, ((TopLevelListKey)key).getName());
+    }
+
+    @Test
+    public void testBindingAwareIIToYangIContainer() {
+        YangInstanceIdentifier yangInstanceIdentifier = registry.toYangInstanceIdentifier(
+                InstanceIdentifier.create(Top.class).child(TopLevelList.class));
+        YangInstanceIdentifier.PathArgument lastPathArgument = yangInstanceIdentifier.getLastPathArgument();
+        assertTrue(lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifier);
+        assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType());
+    }
+
+    @Test
+    public void testBindingAwareIIToYangIIWildcard() {
+        YangInstanceIdentifier yangInstanceIdentifier = registry.toYangInstanceIdentifier(
+                InstanceIdentifier.create(Top.class).child(TopLevelList.class));
+        YangInstanceIdentifier.PathArgument lastPathArgument = yangInstanceIdentifier.getLastPathArgument();
+        assertTrue(lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifier);
+        assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType());
+    }
+
+    @Test
+    public void testBindingAwareIIToYangIIListWithKey() {
+        YangInstanceIdentifier yangInstanceIdentifier = registry.toYangInstanceIdentifier(
+                InstanceIdentifier.create(Top.class).child(TopLevelList.class, TOP_FOO_KEY));
+        YangInstanceIdentifier.PathArgument lastPathArgument = yangInstanceIdentifier.getLastPathArgument();
+        assertTrue(lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates);
+        assertTrue(((YangInstanceIdentifier.NodeIdentifierWithPredicates) lastPathArgument).getKeyValues().containsValue(TOP_LEVEL_LIST_KEY_VALUE));
+        assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType());
+    }
+
+    @Test
+    public void testBindingAwareIIToYangIIAugmentation() {
+        YangInstanceIdentifier.PathArgument lastArg = registry.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
+        assertTrue(lastArg instanceof YangInstanceIdentifier.AugmentationIdentifier);
+    }
+
+    @Test
+    public void testBindingAwareIIToYangIILeafOnlyAugmentation() {
+        YangInstanceIdentifier.PathArgument leafOnlyLastArg = registry.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument();
+        assertTrue(leafOnlyLastArg instanceof YangInstanceIdentifier.AugmentationIdentifier);
+        assertTrue(((YangInstanceIdentifier.AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+    }
+}
diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierTest.java
new file mode 100644 (file)
index 0000000..e011017
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.binding.data.codec.test;
+
+import javassist.ClassPool;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+public class InstanceIdentifierTest extends AbstractBindingRuntimeTest {
+
+    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
+            .child(TopLevelList.class, TOP_FOO_KEY).toInstance();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
+            .augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
+            .augmentation(TreeComplexUsesAugment.class);
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+    private BindingNormalizedNodeCodecRegistry registry;
+
+    @Override
+    public void setup() {
+        super.setup();
+        JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
+        registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
+        registry.onBindingRuntimeContextUpdated(getRuntimeContext());
+    }
+
+    @Test
+    public void testComplexAugmentationSerialization() {
+        YangInstanceIdentifier yangII = registry.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES);
+        PathArgument lastArg = yangII.getLastPathArgument();
+        assertTrue("Last argument should be AugmentationIdentifier", lastArg instanceof AugmentationIdentifier);
+        InstanceIdentifier<?> bindingII = registry.fromYangInstanceIdentifier(yangII);
+        assertEquals(BA_TREE_COMPLEX_USES, bindingII);
+    }
+
+    @Test
+    public void testLeafOnlyAugmentationSerialization() {
+        PathArgument leafOnlyLastArg = registry.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument();
+        assertTrue("Last argument should be AugmentationIdentifier", leafOnlyLastArg instanceof AugmentationIdentifier);
+        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+    }
+
+}
diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/NormalizedNodeSerializeDeserializeTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/NormalizedNodeSerializeDeserializeTest.java
new file mode 100644 (file)
index 0000000..4b08fbe
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.binding.data.codec.test;
+
+import javassist.ClassPool;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.ChoiceContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.ChoiceContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.choice.identifier.ExtendedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.choice.identifier.extended.ExtendedIdBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedListKey;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingRuntimeTest{
+
+    public static final String TOP_LEVEL_LIST_FOO_KEY_VALUE = "foo";
+    public static final TopLevelListKey TOP_LEVEL_LIST_FOO_KEY = new TopLevelListKey(TOP_LEVEL_LIST_FOO_KEY_VALUE);
+
+    public static final QName TOP_QNAME =
+            QName.create("urn:opendaylight:params:xml:ns:yang:yangtools:test:binding", "2014-07-01", "top");
+    public static final QName TOP_LEVEL_LIST_QNAME = QName.create(TOP_QNAME, "top-level-list");
+    public static final QName TOP_LEVEL_LIST_KEY_QNAME = QName.create(TOP_QNAME, "name");
+    public static final QName TOP_LEVEL_LEAF_LIST_QNAME = QName.create(TOP_QNAME, "top-level-leaf-list");
+    public static final QName NESTED_LIST_QNAME = QName.create(TOP_QNAME, "nested-list");
+    public static final QName NESTED_LIST_KEY_QNAME = QName.create(TOP_QNAME, "name");
+    public static final QName CHOICE_CONTAINER_QNAME =
+            QName.create("urn:opendaylight:params:xml:ns:yang:yangtools:test:binding", "2014-07-01", "choice-container");
+    public static final QName CHOICE_IDENTIFIER_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "identifier");
+    public static final QName CHOICE_IDENTIFIER_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "id");
+    public static final QName SIMPLE_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "simple-id");
+    public static final QName EXTENDED_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "extended-id");
+    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
+            .builder(Top.class).child(TopLevelList.class, TOP_LEVEL_LIST_FOO_KEY).toInstance();
+    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY =
+            BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES =
+            BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
+
+    public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.of(TOP_QNAME);
+    public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME);
+    public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_FOO_PATH = BI_TOP_LEVEL_LIST_PATH
+            .node(new YangInstanceIdentifier.NodeIdentifierWithPredicates(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE));
+    public static final YangInstanceIdentifier BI_CHOICE_CONTAINER_PATH = YangInstanceIdentifier.of(CHOICE_CONTAINER_QNAME);
+
+    private BindingNormalizedNodeCodecRegistry registry;
+
+    @Before
+    public void setup() {
+        super.setup();
+        JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
+        registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
+        registry.onBindingRuntimeContextUpdated(getRuntimeContext());
+    }
+
+    @Test
+    public void containerToNormalized() {
+        Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                registry.toNormalizedNode(InstanceIdentifier.create(Top.class), top());
+        ContainerNode topNormalized = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME))
+                .withChild(mapNodeBuilder(TOP_LEVEL_LIST_QNAME).build()).build();
+        assertEquals(topNormalized, entry.getValue());
+    }
+
+    @Test
+    public void containerFromNormalized() {
+        ContainerNode topNormalized = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME))
+                .withChild(mapNodeBuilder(TOP_LEVEL_LIST_QNAME).build()).build();
+        Map.Entry<InstanceIdentifier<?>, DataObject> entry = registry.fromNormalizedNode(BI_TOP_PATH, topNormalized);
+        assertEquals(top(), entry.getValue());
+    }
+
+    @Test
+    public void listWithKeysToNormalized() {
+        Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                registry.toNormalizedNode(BA_TOP_LEVEL_LIST, topLevelList(TOP_LEVEL_LIST_FOO_KEY));
+        MapEntryNode topLevelListNormalized = ImmutableMapEntryNodeBuilder.create()
+                .withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                                TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .build();
+        assertEquals(topLevelListNormalized, entry.getValue());
+    }
+
+    @Test
+    public void listWithKeysFromNormalized() {
+        MapEntryNode topLevelListNormalized = ImmutableMapEntryNodeBuilder.create()
+                .withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                                TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .build();
+        Map.Entry<InstanceIdentifier<?>, DataObject> entry =
+                registry.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, topLevelListNormalized);
+        assertEquals(topLevelList(TOP_LEVEL_LIST_FOO_KEY), entry.getValue());
+    }
+
+    @Test
+    public void leafOnlyAugmentationToNormalized() {
+        Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                registry.toNormalizedNode(BA_TREE_LEAF_ONLY, new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build());
+        Set<QName> augmentationChildren = new HashSet<>();
+        augmentationChildren.add(SIMPLE_VALUE_QNAME);
+        AugmentationNode augmentationNode = ImmutableAugmentationNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.AugmentationIdentifier(augmentationChildren))
+                .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue"))
+                .build();
+        assertEquals(augmentationNode, entry.getValue());
+    }
+
+    @Test
+    public void leafOnlyAugmentationFromNormalized() {
+        Set<QName> augmentationChildren = new HashSet<>();
+        augmentationChildren.add(SIMPLE_VALUE_QNAME);
+        AugmentationNode augmentationNode = ImmutableAugmentationNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.AugmentationIdentifier(augmentationChildren))
+                .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue"))
+                .build();
+        Map.Entry<InstanceIdentifier<?>, DataObject> entry = registry.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH.node(
+                        new YangInstanceIdentifier.AugmentationIdentifier(augmentationChildren)), augmentationNode);
+        assertEquals(new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build(), entry.getValue());
+    }
+
+    @Test
+    public void leafListToNormalized() {
+        List<String> topLevelLeafList = new ArrayList<>();
+        topLevelLeafList.add("foo");
+        Top top = new TopBuilder().setTopLevelLeafList(topLevelLeafList).build();
+
+        Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                registry.toNormalizedNode(InstanceIdentifier.create(Top.class), top);
+        ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME))
+                .withChild(ImmutableLeafSetNodeBuilder.create()
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME))
+                        .withChild(
+                                ImmutableLeafSetEntryNodeBuilder.create()
+                                        .withNodeIdentifier(new YangInstanceIdentifier.NodeWithValue(TOP_LEVEL_LEAF_LIST_QNAME, "foo"))
+                                        .withValue("foo")
+                                        .build())
+                        .build())
+                .build();
+        assertEquals(containerNode, entry.getValue());
+    }
+
+    @Test
+    public void leafListFromNormalized() {
+        ContainerNode topWithLeafList = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME))
+                .withChild(ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME))
+                        .withChild(ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
+                                new YangInstanceIdentifier.NodeWithValue(TOP_LEVEL_LEAF_LIST_QNAME, "foo")).withValue("foo").build()).build())
+                .build();
+        Map.Entry<InstanceIdentifier<?>, DataObject> entry = registry.fromNormalizedNode(BI_TOP_PATH, topWithLeafList);
+        List<String> topLevelLeafList = new ArrayList<>();
+        topLevelLeafList.add("foo");
+        Top top = new TopBuilder().setTopLevelLeafList(topLevelLeafList).build();
+        assertEquals(top, entry.getValue());
+    }
+
+    @Test
+    public void choiceToNormalized() {
+        ChoiceContainer choiceContainerBA = new ChoiceContainerBuilder().setIdentifier(new ExtendedBuilder().setExtendedId(
+                new ExtendedIdBuilder().setId("identifier_value").build()).build()).build();
+        Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                registry.toNormalizedNode(InstanceIdentifier.create(ChoiceContainer.class), choiceContainerBA);
+        ContainerNode choiceContainer = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_CONTAINER_QNAME))
+                .withChild(ImmutableChoiceNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_IDENTIFIER_QNAME))
+                        .withChild(ImmutableContainerNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(EXTENDED_ID_QNAME))
+                                .withChild(leafNode(CHOICE_IDENTIFIER_ID_QNAME, "identifier_value")).build()).build())
+                .build();
+        assertEquals(choiceContainer, entry.getValue());
+    }
+
+    @Test
+    public void choiceFromNormalized() {
+        ContainerNode choiceContainerBI = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_CONTAINER_QNAME))
+                .withChild(ImmutableChoiceNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_IDENTIFIER_QNAME))
+                        .withChild(ImmutableContainerNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(EXTENDED_ID_QNAME))
+                                .withChild(leafNode(CHOICE_IDENTIFIER_ID_QNAME, "identifier_value")).build()).build())
+                .build();
+        Map.Entry<InstanceIdentifier<?>, DataObject> entry = registry.fromNormalizedNode(BI_CHOICE_CONTAINER_PATH, choiceContainerBI);
+        ChoiceContainer choiceContainerBA = new ChoiceContainerBuilder().setIdentifier(new ExtendedBuilder().setExtendedId(
+                new ExtendedIdBuilder().setId("identifier_value").build()).build()).build();
+        assertEquals(choiceContainerBA, entry.getValue());
+    }
+
+    @Test
+    public void orderedLisToNormalized() {
+        InstanceIdentifier<TopLevelList> ii = BA_TOP_LEVEL_LIST;
+        List<NestedList> nestedLists = new ArrayList<>();
+        nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("foo")).build());
+        nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("bar")).build());
+        TopLevelList topLevelList = new TopLevelListBuilder().setKey(TOP_LEVEL_LIST_FOO_KEY).setNestedList(nestedLists).build();
+        Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry = registry.toNormalizedNode(ii, topLevelList);
+        MapEntryNode foo = mapEntryBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .withChild(
+                        ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
+                                .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
+                                .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar")).build()).build();
+        assertEquals(foo, entry.getValue());
+    }
+
+    @Test
+    public void orderedLisFromNormalized() {
+        MapEntryNode foo = mapEntryBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+                .withChild(
+                        ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
+                                .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
+                                .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar")).build()).build();
+        InstanceIdentifier<TopLevelList> ii = BA_TOP_LEVEL_LIST;
+
+        Map.Entry<InstanceIdentifier<?>, DataObject> entry = registry.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, foo);
+        List<NestedList> nestedLists = new ArrayList<>();
+        nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("foo")).build());
+        nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("bar")).build());
+        TopLevelList topLevelList = new TopLevelListBuilder().setKey(TOP_LEVEL_LIST_FOO_KEY).setNestedList(nestedLists).build();
+        assertEquals(topLevelList, entry.getValue());
+    }
+}
index 1070f4c33c26797dcc82f72e7be0a6a930d92a32..c1fd71df23a3065cebb6206481f52b184f08a986 100644 (file)
@@ -308,7 +308,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
             return null;
         }
         final String packageName = packageNameForGeneratedType(basePackageName, node.getPath());
-        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf);
+        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf, module);
         genType.addComment(node.getDescription());
         genType.setDescription(createDescription(node, genType.getFullyQualifiedName()));
         genType.setModuleName(module.getName());
@@ -554,7 +554,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 processUsesAugments(notification, module);
 
                 final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(basePackageName,
-                        notification, BindingTypes.DATA_OBJECT);
+                        notification, BindingTypes.DATA_OBJECT, module);
                 notificationInterface.addImplementsType(NOTIFICATION);
                 genCtx.get(module).addChildNodeType(notification, notificationInterface);
 
@@ -702,7 +702,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
      */
     private void groupingToGenType(final String basePackageName, final GroupingDefinition grouping, final Module module) {
         final String packageName = packageNameForGeneratedType(basePackageName, grouping.getPath());
-        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping);
+        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping, module);
         genCtx.get(module).addGroupingType(grouping.getPath(), genType);
         resolveDataSchemaNodes(module, basePackageName, genType, genType, grouping.getChildNodes());
         groupingsToGenTypes(module, grouping.getGroupings());
@@ -1202,7 +1202,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         for (ChoiceCaseNode caseNode : caseNodes) {
             if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
                 final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath());
-                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode);
+                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode, module);
                 caseTypeBuilder.addImplementsType(refChoiceType);
                 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
                 genCtx.get(module).addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode);
@@ -1281,7 +1281,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         for (DataSchemaNode caseNode : augmentedNodes) {
             if (caseNode != null) {
                 final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath());
-                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode);
+                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode, module);
                 caseTypeBuilder.addImplementsType(targetType);
 
                 SchemaNode parent = null;
@@ -1598,8 +1598,9 @@ public class BindingGeneratorImpl implements BindingGenerator {
         return returnType.toInstance();
     }
 
-    private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode) {
-        return addDefaultInterfaceDefinition(packageName, schemaNode, null);
+    private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
+            final Module module) {
+        return addDefaultInterfaceDefinition(packageName, schemaNode, null, module);
     }
 
     /**
@@ -1626,7 +1627,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
      * @return generated type builder <code>schemaNode</code>
      */
     private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
-            final Type parent) {
+            final Type parent, final Module module) {
         final GeneratedTypeBuilder it = addRawInterfaceDefinition(packageName, schemaNode, "");
         if (parent == null) {
             it.addImplementsType(DATA_OBJECT);
@@ -1638,6 +1639,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         }
 
         if (schemaNode instanceof DataNodeContainer) {
+            groupingsToGenTypes(module, ((DataNodeContainer) schemaNode).getGroupings());
             addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it);
         }
 
index 7c8586ed8f5af71ceab4b88bda76f082c0f4fd94..e532be5b9e83689280fbb2469de874174647b45b 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.yangtools.sal.binding.generator.impl;
 
 import com.google.common.collect.ImmutableList;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -19,7 +18,6 @@ import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
-
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -29,10 +27,10 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
@@ -60,9 +58,9 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
     public InstanceIdentifier<? extends Object> deserialize(
             final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier input) {
         Class<?> baType = null;
-        List<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument> biArgs = input.getPath();
-        List<QName> scannedPath = new ArrayList<>(biArgs.size());
-        List<InstanceIdentifier.PathArgument> baArgs = new ArrayList<InstanceIdentifier.PathArgument>(biArgs.size());
+        Iterable<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument> biArgs = input.getPathArguments();
+        List<QName> scannedPath = new ArrayList<>();
+        List<InstanceIdentifier.PathArgument> baArgs = new ArrayList<InstanceIdentifier.PathArgument>();
         for (org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument biArg : biArgs) {
 
             scannedPath.add(biArg.getNodeType());
index 92047d1f22c6547d284c8a29453eb5274a9408ab..f72856e1c11b540c2c6d855ef26b0ab7455a209e 100644 (file)
@@ -15,7 +15,6 @@ import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
-
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,7 +29,6 @@ import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
 import org.opendaylight.yangtools.binding.generator.util.Types;
 import org.opendaylight.yangtools.concepts.Delegator;
@@ -268,7 +266,7 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
         while (iterator.hasNext()) {
             QName arg = iterator.next();
             DataSchemaNode currentNode = previous.getDataChildByName(arg);
-            if (currentNode == null && previous instanceof DataNodeContainer) {
+            if (currentNode == null) {
                 currentNode = searchInChoices(previous, arg);
             }
             if (currentNode instanceof DataNodeContainer) {
@@ -852,7 +850,7 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
             if (!parentQName.equals(choiceName)) {
                 // This item is instantiation of choice via uses in other YANG
                 // module
-                if (choiceName.getNamespace().equals(schema.getQName())) {
+                if (choiceName.getNamespace().equals(schema.getQName().getNamespace())) {
                     // Original definition of grouping is in same namespace
                     // as original definition of case
                     // so for sure case is introduced via instantiation of
index 286b5f06b17017a6c01fe9ad678db612a65af16b..d322833ad81ab9c69e8309462779a1f24c1a37fc 100644 (file)
@@ -213,7 +213,7 @@ class BuilderTemplate extends BaseTemplate {
 
             «generateFields(false)»
 
-            «generateAugmentField(true)»
+            «generateAugmentField(false)»
 
             «generateConstructorsFromIfcs(type)»
 
@@ -235,7 +235,7 @@ class BuilderTemplate extends BaseTemplate {
 
                 «generateFields(true)»
 
-                «generateAugmentField(false)»
+                «generateAugmentField(true)»
 
                 «generateCopyConstructor(true)»
 
@@ -421,9 +421,9 @@ class BuilderTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
-    def private generateAugmentField(boolean init) '''
+    def private generateAugmentField(boolean isPrivate) '''
         «IF augmentField != null»
-            private «Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
+            «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
         «ENDIF»
     '''
 
diff --git a/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/NestedGroupingCompilationTest.java b/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/NestedGroupingCompilationTest.java
new file mode 100644 (file)
index 0000000..62190fc
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.sal.java.api.generator.test;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.BASE_PKG;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.COMPILER_OUTPUT_PATH;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.FS;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.GENERATOR_OUTPUT_PATH;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.NS_TEST;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.assertFilesCount;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.assertImplementsIfc;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.cleanUp;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.getSourceFiles;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.testCompilation;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashSet;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Test correct code generation.
+ *
+ */
+public class NestedGroupingCompilationTest extends BaseCompilationTest {
+
+    @Test
+    public void testListGeneration() throws Exception {
+        final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "nested-grouping");
+        assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
+        final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "nested-grouping");
+        assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
+
+        generateTestSources("/compilation/nested-grouping", sourcesOutputDir);
+
+        // Test if all sources are generated
+        File parent = new File(sourcesOutputDir, NS_TEST);
+        File foo = new File(parent, "Foo.java");
+        File fooBuilder = new File(parent, "FooBuilder.java");
+        File testData = new File(parent, "TestData.java");
+        File fooDir = new File(parent, "foo");
+        assertTrue(foo.exists());
+        assertTrue(fooBuilder.exists());
+        assertTrue(testData.exists());
+        assertTrue(fooDir.exists());
+        assertFilesCount(parent, 4);
+
+        parent = new File(parent, "foo");
+        File bar = new File(parent, "Bar.java");
+        assertTrue(bar.exists());
+        assertFilesCount(parent, 1);
+
+        // Test if sources are compilable
+        testCompilation(sourcesOutputDir, compiledOutputDir);
+
+        ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
+        Class<?> fooClass = Class.forName(BASE_PKG + ".urn.opendaylight.test.rev131008.Foo", true, loader);
+        Class<?> barClass = Class.forName(BASE_PKG + ".urn.opendaylight.test.rev131008.foo.Bar", true, loader);
+
+        // Test generated 'foo'
+        assertTrue(fooClass.isInterface());
+        assertImplementsIfc(fooClass, barClass);
+
+        cleanUp(sourcesOutputDir, compiledOutputDir);
+    }
+
+    private void generateTestSources(String resourceDirPath, File sourcesOutputDir) throws Exception {
+        final List<File> sourceFiles = getSourceFiles(resourceDirPath);
+        final SchemaContext context = parser.parseFiles(sourceFiles);
+        final List<Type> types = bindingGenerator.generateTypes(context);
+        final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
+        generator.generateToFile(sourcesOutputDir);
+    }
+
+}
diff --git a/code-generator/binding-java-api-generator/src/test/resources/compilation/nested-grouping/test.yang b/code-generator/binding-java-api-generator/src/test/resources/compilation/nested-grouping/test.yang
new file mode 100644 (file)
index 0000000..94cf5e1
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:test";
+    prefix "t";
+
+    revision "2013-10-08" {
+    }
+
+    container foo {
+        grouping bar {
+            leaf id {
+                type int32;
+            }
+        }
+        uses bar;
+    }
+
+}
diff --git a/code-generator/binding-test-model/pom.xml b/code-generator/binding-test-model/pom.xml
new file mode 100644 (file)
index 0000000..106c904
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>binding-generator</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-binding</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>yang-ext</artifactId>
+        </dependency>
+    </dependencies>
+
+    <artifactId>binding-test-model</artifactId>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        target/generated-sources/sal
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                            <inspectDependencies>true</inspectDependencies>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <scm>
+        <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <tag>HEAD</tag>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+    </scm>
+
+</project>
diff --git a/code-generator/binding-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java b/code-generator/binding-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java
new file mode 100644 (file)
index 0000000..0d3e7e8
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.test.model.util;
+
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedListKey;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.Arrays;
+
+public class ListsBindingUtils {
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+    private ListsBindingUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+    public static final TopLevelListKey TOP_BAR_KEY = new TopLevelListKey("bar");
+    public static final ListViaUsesKey USES_ONE_KEY = new ListViaUsesKey("one");
+    public static final ListViaUsesKey USES_TWO_KEY = new ListViaUsesKey("two");
+
+
+    public static InstanceIdentifier<TopLevelList> path(final TopLevelListKey key) {
+        return TOP_PATH.child(TopLevelList.class, key);
+    }
+
+    public static InstanceIdentifier<NestedList> path(final TopLevelListKey top,final NestedListKey nested) {
+        return path(top).child(NestedList.class, nested);
+    }
+
+    public static InstanceIdentifier<ListViaUses> path(final TopLevelListKey top,final ListViaUsesKey uses) {
+        return path(top).augmentation(TreeComplexUsesAugment.class).child(ListViaUses.class, uses);
+    }
+
+    public static <T extends DataObject & Augmentation<TopLevelList>> InstanceIdentifier<T> path(final TopLevelListKey key, final Class<T> augmentation) {
+        return path(key).augmentation(augmentation);
+    }
+
+    public static Top top(final TopLevelList... listItems) {
+        return new TopBuilder().setTopLevelList(Arrays.asList(listItems)).build();
+    }
+
+    public static TopLevelList topLevelList(final TopLevelListKey key) {
+        return new TopLevelListBuilder().setKey(key).build();
+    }
+
+    public static TopLevelList topLevelList(final TopLevelListKey key, final TreeComplexUsesAugment augment) {
+        TopLevelListBuilder builder = new TopLevelListBuilder().setKey(key);
+        builder.addAugmentation(TreeComplexUsesAugment.class, augment);
+        return builder.build();
+    }
+
+    public static TreeComplexUsesAugment complexUsesAugment(final ListViaUsesKey... keys) {
+        ImmutableList.Builder<ListViaUses> listViaUses = ImmutableList.<ListViaUses> builder();
+        for (ListViaUsesKey key : keys) {
+            listViaUses.add(new ListViaUsesBuilder().setKey(key).build());
+        }
+        return new TreeComplexUsesAugmentBuilder().setListViaUses(listViaUses.build()).build();
+    }
+
+    public static TreeLeafOnlyUsesAugment leafOnlyUsesAugment(String leafFromGroupingValue) {
+
+        return new TreeLeafOnlyUsesAugmentBuilder().setLeafFromGrouping(leafFromGroupingValue).build();
+    }
+
+}
diff --git a/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang
new file mode 100644 (file)
index 0000000..6d155be
--- /dev/null
@@ -0,0 +1,111 @@
+module opendaylight-yangtools-augment-test {
+
+    namespace "urn:opendaylight:params:xml:ns:yang:yangtools:test:augment";
+    prefix aug-test;
+
+    import opendaylight-yangtools-binding-test {
+        prefix test;
+    }
+    import yang-ext {
+        prefix ext;
+    }
+
+    description
+        "This module contains a collection of YANG augmentations used for
+        some test cases.";
+
+    revision 2014-07-09 {
+        description
+        "Test model for testing data broker with nested lists.";
+    }
+
+    grouping leaf-from-grouping {
+        leaf leaf-from-grouping {
+            type string;
+        }
+    }
+
+    grouping complex-from-grouping {
+        container container-with-uses {
+            uses leaf-from-grouping;
+        }
+        list list-via-uses {
+            key "name";
+            leaf name {
+                type string;
+            }
+        }
+    
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-leaf-only-uses-augment;
+        uses leaf-from-grouping;
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-leaf-only-uses-augment;
+        uses leaf-from-grouping;
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-complex-uses-augment;
+        uses complex-from-grouping;
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-complex-uses-augment;
+        uses complex-from-grouping;
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-leaf-only-augment;
+
+        leaf simple-value {
+            type string;
+        }
+    }
+
+    augment "/test:top/test:top-level-list" {
+        ext:augment-identifier tree-second-leaf-only-augment;
+
+        leaf second-simple-value {
+            type string;
+        }
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-leaf-only-augment;
+
+        leaf simple-value {
+            type string;
+        }
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list" {
+        ext:augment-identifier rpc-second-leaf-only-augment;
+
+        leaf second-simple-value {
+            type string;
+        }
+    }
+
+    augment "/test:top/test:top-level-list/test:choice-in-list" {
+        case simple-via-uses {
+            uses leaf-from-grouping;
+        }
+        case complex-via-uses {
+            uses complex-from-grouping;
+        }
+    }
+
+    augment "/test:put-top/test:input/test:top-level-list/test:choice-in-list" {
+        case simple-via-uses {
+            uses leaf-from-grouping;
+        }
+        case complex-via-uses-with-different-name {
+            uses complex-from-grouping;
+        }
+    }
+
+}
diff --git a/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-binding-test.yang b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-binding-test.yang
new file mode 100644 (file)
index 0000000..c5e5ce2
--- /dev/null
@@ -0,0 +1,86 @@
+module opendaylight-yangtools-binding-test {
+
+    namespace "urn:opendaylight:params:xml:ns:yang:yangtools:test:binding";
+    prefix list-test;
+
+
+    description
+        "This module contains a collection of YANG definitions used for
+        some test cases.";
+
+    revision 2014-07-01 {
+        description
+        "Test model for testing data broker with nested lists.";
+    }
+
+    grouping two-level-list {
+        list top-level-list {
+            description
+                "Top Level List";
+            key "name";
+            leaf name {
+                type string;
+            }
+
+            choice choice-in-list {
+                case simple-case {
+                    leaf simple {
+                        type string;
+                    }
+                }
+            }
+
+            list nested-list {
+                key "name";
+                leaf name {
+                    type string;
+                }
+                leaf type {
+                    type string;
+                    mandatory true;
+                    description
+                        "Mandatory type of list.";
+                }
+                ordered-by user;
+                description
+                    "A list of service functions that compose the service chain";
+            }
+        }
+        leaf-list top-level-leaf-list {
+            type string;
+        }
+    }
+
+    grouping choice {
+        choice identifier {
+            case simple {
+                container simple-id {
+                    leaf id {
+                        type int32;
+                    }
+                }
+            }
+            case extended {
+                container extended-id {
+                    leaf id {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    container top {
+        uses two-level-list;
+    }
+
+    container choice-container {
+        uses choice;
+    }
+
+    rpc put-top {
+        input {
+            uses two-level-list;
+        }
+    }
+}
index cf23c844de29cfe5e5ea5d2ab36905f8a7dfb3ce..f1864ab1b7e5af44810b1dd9a07d5d1e81e52d6a 100644 (file)
@@ -14,6 +14,9 @@ import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findD
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import com.google.common.io.BaseEncoding;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -31,7 +34,6 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
 import org.opendaylight.yangtools.binding.generator.util.TypeConstants;
@@ -90,10 +92,6 @@ import org.opendaylight.yangtools.yang.model.util.Uint64;
 import org.opendaylight.yangtools.yang.model.util.Uint8;
 import org.opendaylight.yangtools.yang.model.util.UnionType;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
-import com.google.common.io.BaseEncoding;
-
 public final class TypeProviderImpl implements TypeProvider {
     private static final Pattern NUMBERS_PATTERN = Pattern.compile("[0-9]+\\z");
 
@@ -810,11 +808,13 @@ public final class TypeProviderImpl implements TypeProvider {
         final List<GeneratedTOBuilder> genTOBuilders = provideGeneratedTOBuildersForUnionTypeDef(basePackageName,
                 typedef, typeDefName, parentNode);
         GeneratedTOBuilder resultTOBuilder = null;
-        if (!genTOBuilders.isEmpty()) {
-            resultTOBuilder = genTOBuilders.remove(0);
-            for (GeneratedTOBuilder genTOBuilder : genTOBuilders) {
-                resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
-            }
+        if (genTOBuilders.isEmpty()) {
+            throw new IllegalStateException("No GeneratedTOBuilder objects generated from union " + typedef);
+        }
+
+        resultTOBuilder = genTOBuilders.remove(0);
+        for (GeneratedTOBuilder genTOBuilder : genTOBuilders) {
+            resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
         }
 
         final GeneratedPropertyBuilder genPropBuilder = resultTOBuilder.addProperty("value");
@@ -1062,7 +1062,7 @@ public final class TypeProviderImpl implements TypeProvider {
         final String packageName = packageNameForGeneratedType(basePackageName, typedef.getPath());
         final String typeDefTOName = typedef.getQName().getLocalName();
 
-        if ((packageName != null) && (typedef != null) && (typeDefTOName != null)) {
+        if ((packageName != null) && (typeDefTOName != null)) {
             final String genTOName = BindingMapping.getClassName(typeDefTOName);
             final GeneratedTOBuilderImpl newType = new GeneratedTOBuilderImpl(packageName, genTOName);
 
@@ -1300,9 +1300,8 @@ public final class TypeProviderImpl implements TypeProvider {
             typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
         }
         // keys are in ascending order
-        Set<Integer> depths = typeDefinitionsDepths.keySet();
-        for (Integer depth : depths) {
-            sortedTypeDefinition.addAll(typeDefinitionsDepths.get(depth));
+        for (Map.Entry<Integer, List<TypeDefinition<?>>> entry : typeDefinitionsDepths.entrySet()) {
+            sortedTypeDefinition.addAll(entry.getValue());
         }
 
         return sortedTypeDefinition;
index d8c49592965f0cb1f62654aed4bff87894c0269e..f40cbcaa3dff249ad6cc8443028d380853fa8485 100644 (file)
@@ -975,9 +975,9 @@ class GeneratorImpl {
             </ul>
             «ENDIF»
 
-            «IF !path.path.empty»
+            «IF path.pathArguments.iterator.hasNext»
             <h3>XML example</h3>
-            «nodes.xmlExample(path.path.last.nodeType,path)»
+            «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
             </h3>
             «ENDIF»
             «FOR childNode : containers»
@@ -1045,8 +1045,8 @@ class GeneratorImpl {
 
 
     def header(int level,YangInstanceIdentifier name) '''
-        <h«level» id="«FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
-            «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
+        <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
+            «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
         </h«level»>
     '''
 
@@ -1152,7 +1152,7 @@ class GeneratorImpl {
     }
 
     def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
-        <a href="#«FOR cmp : identifier.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
+        <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
     '''
 
 
@@ -1181,7 +1181,7 @@ class GeneratorImpl {
         append(currentModule.name)
         append(":")
         var previous = false;
-        for(arg : identifier.path) {
+        for(arg : identifier.pathArguments) {
             if(previous) append("/")
             append(arg.nodeType.localName);
             previous = true;
index 1208f190f376519a18f54fd406c469c9b94f3671..1a1052baa14838f604e6d1d6a068d785911ea126 100644 (file)
@@ -82,13 +82,9 @@ public class YangModuleInfoCompilationTest {
         testCompilation(sourcesOutputDir, compiledOutputDir);
 
         // Create URLClassLoader
-        File[] roots = File.listRoots();
-        URL[] urls = new URL[roots.length + 1];
-        for (int i = 0; i < roots.length; i++) {
-            urls[i] = roots[i].toURI().toURL();
-
-        }
-        urls[roots.length] = compiledOutputDir.toURI().toURL();
+        URL[] urls = new URL[2];
+        urls[0] = compiledOutputDir.toURI().toURL();
+        urls[1] = new File(System.getProperty("user.dir")).toURI().toURL();
         ClassLoader loader = new URLClassLoader(urls);
 
         // Load class
@@ -176,13 +172,16 @@ public class YangModuleInfoCompilationTest {
     private static List<File> getSourceFiles(String path) throws Exception {
         final URI resPath = YangModuleInfoCompilationTest.class.getResource(path).toURI();
         final File sourcesDir = new File(resPath);
+        final URI currentDir = new File(System.getProperty("user.dir")).toURI();
         if (sourcesDir.exists()) {
             final List<File> sourceFiles = new ArrayList<>();
             final File[] fileArray = sourcesDir.listFiles();
             if (fileArray == null) {
                 throw new IllegalArgumentException("Unable to locate files in " + sourcesDir);
             }
-            sourceFiles.addAll(Arrays.asList(fileArray));
+            for (File sourceFile : fileArray) {
+                sourceFiles.add(new File(currentDir.relativize(sourceFile.toURI()).toString()));
+            }
             return sourceFiles;
         } else {
             throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")");
index 4cdf7c23c78e0349ef6e162d16a98cf76dd548a1..fc239681b50fbf6de3dfabd9397fd4c634b01481 100644 (file)
@@ -30,6 +30,7 @@
         <module>binding-java-api-generator</module>
         <module>binding-type-provider</module>
         <module>maven-sal-api-gen-plugin</module>
+        <module>binding-test-model</module>
         <module>binding-data-codec</module>
     </modules>
 
             </dependency>
 
             <!-- Local Dependencies -->
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>binding-test-model</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>binding-model-api</artifactId>
index f9f4c5c7b547774e3e4567a326d2192541d7fbc1..bc7474a9f2cb765796063f07e8c9cd65dc2b5e96 100644 (file)
@@ -16,12 +16,15 @@ import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Sets;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -581,4 +584,51 @@ public class BindingReflections {
     public static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Augmentable<?> input) {
         return AugmentationFieldGetter.getGetter(input.getClass()).getAugmentations(input);
     }
+
+    /**
+     *
+     * Determines if two augmentation classes or case classes represents same data.
+     * <p>
+     * Two augmentations or cases could be substituted only if and if:
+     * <ul>
+     * <li>Both implements same interfaces</li>
+     * <li>Both have same children</li>
+     * <li>If augmentations: Both have same augmentation target class. Target class was generated for data node in grouping.</li>
+     * <li>If cases: Both are from same choice. Choice class was generated for data node in grouping.</li>
+     * </ul>
+     * <p>
+     * <b>Explanation:</b> Binding Specification reuses classes generated for groupings as part of normal data tree,
+     * this classes from grouping could be used at various locations and user may not be aware of it
+     * and may use incorrect case or augmentation in particular subtree (via copy constructors, etc).
+     *
+     * @param potential Class which is potential substition
+     * @param target Class which should be used at particular subtree
+     * @return true if and only if classes represents same data.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"})
+    public static boolean isSubstitutionFor(Class potential, Class target) {
+        HashSet<Class> subImplemented = Sets.newHashSet(potential.getInterfaces());
+        HashSet<Class> targetImplemented = Sets.newHashSet(target.getInterfaces());
+        if(!subImplemented.equals(targetImplemented)) {
+            return false;
+        }
+        if(Augmentation.class.isAssignableFrom(potential)
+                && !BindingReflections.findAugmentationTarget(potential).equals(BindingReflections.findAugmentationTarget(target))) {
+                return false;
+        }
+        for(Method potentialMethod : potential.getMethods()) {
+            try {
+                Method targetMethod = target.getMethod(potentialMethod.getName(), potentialMethod.getParameterTypes());
+                if(!potentialMethod.getReturnType().equals(targetMethod.getReturnType())) {
+                    return false;
+                }
+            } catch (NoSuchMethodException e) {
+                // Counterpart method is missing, so classes could not be substituted.
+                return false;
+            } catch (SecurityException e) {
+                throw new IllegalStateException("Could not compare methods",e);
+            }
+        }
+        return true;
+    }
 }