Tolerate unresolvable leafrefs in groupings 78/81078/2
authorVratko Polak <vrpolak@cisco.com>
Fri, 1 Jul 2016 11:21:15 +0000 (13:21 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 21 Mar 2019 13:10:14 +0000 (14:10 +0100)
In case a grouping contains a relative leafref pointing outside of
the grouping subtree we cannot safely determine the target type, which
lead to an IllegalArgumentException.

In this case we need to resolve the return type to an Object, which
loses type safety, but really is the best we can do as we just do not
know in what contexts that grouping is used.

Unfortunately this requires API changes to mdsal-binding-generator-api
as we need to communicate the fact we are resolving a type definition
in the context of a grouping to TypeProvider implementations.

JIRA: MDSAL-182
Change-Id: Iaf96d133c08eb47f4bb27b6c4f3db1463b78f05e
Signed-off-by: Vratko Polak <vrpolak@cisco.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 1e1d8a29875fe39cc41cc430d6d96ccebd5eb5f0)

binding/mdsal-binding-generator-api/src/main/java/org/opendaylight/mdsal/binding/generator/spi/TypeProvider.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/AbstractTypeGenerator.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/yang/types/AbstractTypeProvider.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/yang/types/BaseYangTypes.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal182Test.java [new file with mode: 0644]
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/yang/types/TypeProviderTest.java
binding/mdsal-binding-generator-impl/src/test/resources/mdsal-182/good-leafref.yang [new file with mode: 0644]
binding/mdsal-binding-generator-impl/src/test/resources/mdsal-182/grouping-leafref.yang [new file with mode: 0644]
binding/mdsal-binding-test-model/src/main/yang/mdsal-182.yang [new file with mode: 0644]

index daa5e6fb15965f8bff2058877983963efe87b2f4..2f0cc09d5caa086722bc03750431ef2aa094913e 100644 (file)
@@ -19,24 +19,39 @@ public interface TypeProvider {
     Type javaTypeForYangType(String type);
 
     /**
-     * Resolve of yang Type Definition to it's java counter part.
-     * If the Type Definition contains one of yang primitive types the method
-     * will return java.lang. counterpart. (For example if yang type is int32
-     * the java counterpart is java.lang.Integer). In case that Type
-     * Definition contains extended type defined via yang typedef statement
-     * the method SHOULD return Generated Type or Generated Transfer Object
-     * if that Type is correctly referenced to resolved imported yang module.
-     * The method will return <code>null</code> value in situations that
-     * TypeDefinition can't be resolved (either due missing yang import or
-     * incorrectly specified type).
+     * Resolve of YANG Type Definition to it's java counter part. If the Type Definition contains one of YANG primitive
+     * types the method will return {@code java.lang.} counterpart. (For example if YANG type is int32 the Java
+     * counterpart is {@link Integer}). In case that Type Definition contains extended type defined via YANG typedef
+     * statement the method SHOULD return Generated Type or Generated Transfer Object if that Type is correctly
+     * referenced to resolved imported YANG module.
      *
+     * <p>
+     * The method will return <code>null</code> value in situations that TypeDefinition can't be resolved (either due
+     * to missing YANG import or incorrectly specified type).
+     *
+     * <p>
+     * {@code leafref} resolution for relative paths has two models of operation: lenient and strict. This is needed to
+     * handle the case where a grouping leaf's path points outside of the grouping tree. In such a case we cannot
+     * completely determine the correct type and need to fallback to {@link Object}.
      *
      * @param type Type Definition to resolve from
+     * @param lenientRelativeLeafrefs treat relative leafrefs leniently
      * @return Resolved Type
      */
-    Type javaTypeForSchemaDefinitionType(TypeDefinition<?> type, SchemaNode parentNode);
+    Type javaTypeForSchemaDefinitionType(TypeDefinition<?> type, SchemaNode parentNode,
+            boolean lenientRelativeLeafrefs);
+
+    default Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> type, final SchemaNode parentNode) {
+        return javaTypeForSchemaDefinitionType(type, parentNode, false);
+    }
+
+    Type javaTypeForSchemaDefinitionType(TypeDefinition<?> type, SchemaNode parentNode, Restrictions restrictions,
+            boolean lenient);
 
-    Type javaTypeForSchemaDefinitionType(TypeDefinition<?> type, SchemaNode parentNode, Restrictions restrictions);
+    default Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> type, final SchemaNode parentNode,
+            final Restrictions restrictions) {
+        return javaTypeForSchemaDefinitionType(type, parentNode, restrictions, false);
+    }
 
     /**
      * Returns string containing code for creation of new type instance.
index fd242571c98be016cd93b538cd86cf9ce8be6c7c..d8a583533489f5a4a14e78c2eec2c65f77f256c5 100644 (file)
@@ -230,7 +230,7 @@ abstract class AbstractTypeGenerator {
         if (!module.getChildNodes().isEmpty()) {
             final GeneratedTypeBuilder moduleType = moduleToDataType(context);
             context.addModuleNode(moduleType);
-            resolveDataSchemaNodes(context, moduleType, moduleType, module.getChildNodes());
+            resolveDataSchemaNodes(context, moduleType, moduleType, module.getChildNodes(), false);
         }
         return context;
     }
@@ -268,7 +268,7 @@ abstract class AbstractTypeGenerator {
     }
 
     private GeneratedTypeBuilder processDataSchemaNode(final ModuleContext context, final Type baseInterface,
-            final DataSchemaNode node) {
+            final DataSchemaNode node, final boolean inGrouping) {
         if (node.isAugmenting() || node.isAddedByUses()) {
             return null;
         }
@@ -282,24 +282,24 @@ abstract class AbstractTypeGenerator {
         if (node instanceof DataNodeContainer) {
             context.addChildNodeType(node, genType);
             groupingsToGenTypes(context, ((DataNodeContainer) node).getGroupings());
-            processUsesAugments((DataNodeContainer) node, context);
+            processUsesAugments((DataNodeContainer) node, context, inGrouping);
         }
         return genType;
     }
 
     private void containerToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final Type baseInterface, final ContainerSchemaNode node) {
-        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node);
+            final Type baseInterface, final ContainerSchemaNode node, final boolean inGrouping) {
+        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping);
         if (genType != null) {
             constructGetter(parent, genType, node);
-            resolveDataSchemaNodes(context, genType, genType, node.getChildNodes());
-            actionsToGenType(context, genType, node, null);
+            resolveDataSchemaNodes(context, genType, genType, node.getChildNodes(), inGrouping);
+            actionsToGenType(context, genType, node, null, inGrouping);
         }
     }
 
     private void listToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final Type baseInterface, final ListSchemaNode node) {
-        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node);
+            final Type baseInterface, final ListSchemaNode node, final boolean inGrouping) {
+        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping);
         if (genType != null) {
             constructGetter(parent, listTypeFor(genType), node);
 
@@ -312,11 +312,11 @@ abstract class AbstractTypeGenerator {
                 genType.addImplementsType(identifiableMarker);
 
             }
-            actionsToGenType(context, genType, node, genTOBuilder);
+            actionsToGenType(context, genType, node, genTOBuilder, inGrouping);
 
             for (final DataSchemaNode schemaNode : node.getChildNodes()) {
                 if (!schemaNode.isAugmenting()) {
-                    addSchemaNodeToListBuilders(context, schemaNode, genType, genTOBuilder, listKeys);
+                    addSchemaNodeToListBuilders(context, schemaNode, genType, genTOBuilder, listKeys, inGrouping);
                 }
             }
 
@@ -331,11 +331,12 @@ abstract class AbstractTypeGenerator {
         }
     }
 
-    private void processUsesAugments(final DataNodeContainer node, final ModuleContext context) {
+    private void processUsesAugments(final DataNodeContainer node, final ModuleContext context,
+            final boolean inGrouping) {
         for (final UsesNode usesNode : node.getUses()) {
             for (final AugmentationSchemaNode augment : usesNode.getAugmentations()) {
-                usesAugmentationToGenTypes(context, augment, usesNode, node);
-                processUsesAugments(augment, context);
+                usesAugmentationToGenTypes(context, augment, usesNode, node, inGrouping);
+                processUsesAugments(augment, context, inGrouping);
             }
         }
     }
@@ -412,7 +413,7 @@ abstract class AbstractTypeGenerator {
     }
 
     private <T extends DataNodeContainer & ActionNodeContainer> void actionsToGenType(final ModuleContext context,
-            final Type parent, final T parentSchema, final Type keyType) {
+            final Type parent, final T parentSchema, final Type keyType, final boolean inGrouping) {
         for (final ActionDefinition action : parentSchema.getActions()) {
             final GeneratedType input;
             final GeneratedType output;
@@ -421,8 +422,8 @@ abstract class AbstractTypeGenerator {
                 input = context.getChildNode(orig.getInput().getPath()).build();
                 output = context.getChildNode(orig.getOutput().getPath()).build();
             } else {
-                input = actionContainer(context, RPC_INPUT, action.getInput());
-                output = actionContainer(context, RPC_OUTPUT, action.getOutput());
+                input = actionContainer(context, RPC_INPUT, action.getInput(), inGrouping);
+                output = actionContainer(context, RPC_OUTPUT, action.getOutput(), inGrouping);
             }
 
             if (!(parentSchema instanceof GroupingDefinition)) {
@@ -461,9 +462,9 @@ abstract class AbstractTypeGenerator {
     }
 
     private GeneratedType actionContainer(final ModuleContext context, final Type baseInterface,
-            final ContainerSchemaNode schema) {
-        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, schema);
-        resolveDataSchemaNodes(context, genType, genType, schema.getChildNodes());
+            final ContainerSchemaNode schema, final boolean inGrouping) {
+        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, schema, inGrouping);
+        resolveDataSchemaNodes(context, genType, genType, schema.getChildNodes(), inGrouping);
         return genType.build();
     }
 
@@ -518,7 +519,7 @@ abstract class AbstractTypeGenerator {
 
     private Type createRpcContainer(final ModuleContext context, final String rpcName, final RpcDefinition rpc,
             final ContainerSchemaNode schema, final Type type) {
-        processUsesAugments(schema, context);
+        processUsesAugments(schema, context, false);
         final GeneratedTypeBuilder outType = addRawInterfaceDefinition(
             JavaTypeName.create(context.modulePackageName(), rpcName + BindingMapping.getClassName(schema.getQName())),
             schema);
@@ -526,7 +527,7 @@ abstract class AbstractTypeGenerator {
         outType.addImplementsType(type);
         outType.addImplementsType(augmentable(outType));
         annotateDeprecatedIfNecessary(rpc.getStatus(), outType);
-        resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes());
+        resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes(), false);
         context.addChildNodeType(schema, outType);
         return outType.build();
     }
@@ -560,7 +561,7 @@ abstract class AbstractTypeGenerator {
 
         for (final NotificationDefinition notification : notifications) {
             if (notification != null) {
-                processUsesAugments(notification, context);
+                processUsesAugments(notification, context, false);
 
                 final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(
                     context.modulePackageName(), notification, DATA_OBJECT, context);
@@ -570,7 +571,7 @@ abstract class AbstractTypeGenerator {
 
                 // Notification object
                 resolveDataSchemaNodes(context, notificationInterface, notificationInterface,
-                    notification.getChildNodes());
+                    notification.getChildNodes(), false);
 
                 addComment(listenerInterface.addMethod("on" + notificationInterface.getName())
                     .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification")
@@ -685,10 +686,10 @@ abstract class AbstractTypeGenerator {
             final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, grouping);
             annotateDeprecatedIfNecessary(grouping.getStatus(), genType);
             context.addGroupingType(grouping, genType);
-            resolveDataSchemaNodes(context, genType, genType, grouping.getChildNodes());
+            resolveDataSchemaNodes(context, genType, genType, grouping.getChildNodes(), true);
             groupingsToGenTypes(context, grouping.getGroupings());
-            processUsesAugments(grouping, context);
-            actionsToGenType(context, genType, grouping, null);
+            processUsesAugments(grouping, context, true);
+            actionsToGenType(context, genType, grouping, null, true);
         }
     }
 
@@ -776,7 +777,7 @@ abstract class AbstractTypeGenerator {
         checkState(augSchema.getTargetPath() != null,
                 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
 
-        processUsesAugments(augSchema, context);
+        processUsesAugments(augSchema, context, false);
         final SchemaPath targetPath = augSchema.getTargetPath();
         SchemaNode targetSchemaNode = null;
 
@@ -804,21 +805,21 @@ abstract class AbstractTypeGenerator {
 
         if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
             final Type targetType = new ReferencedTypeImpl(targetTypeBuilder.getIdentifier());
-            addRawAugmentGenTypeDefinition(context, targetType, augSchema);
+            addRawAugmentGenTypeDefinition(context, targetType, augSchema, false);
 
         } else {
             generateTypesFromAugmentedChoiceCases(context, targetTypeBuilder.build(),
-                    (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), null);
+                    (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), null, false);
         }
     }
 
     private void usesAugmentationToGenTypes(final ModuleContext context, final AugmentationSchemaNode augSchema,
-            final UsesNode usesNode, final DataNodeContainer usesNodeParent) {
+            final UsesNode usesNode, final DataNodeContainer usesNodeParent, final boolean inGrouping) {
         checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
         checkState(augSchema.getTargetPath() != null,
                 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
 
-        processUsesAugments(augSchema, context);
+        processUsesAugments(augSchema, context, inGrouping);
         final SchemaPath targetPath = augSchema.getTargetPath();
         final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(targetPath, usesNode);
         if (targetSchemaNode == null) {
@@ -838,13 +839,13 @@ abstract class AbstractTypeGenerator {
                 addRawAugmentGenTypeDefinition(context,
                     packageNameForAugmentedGeneratedType(context.modulePackageName(),
                         ((SchemaNode) usesNodeParent).getPath()),
-                    targetTypeBuilder.build(), augSchema);
+                    targetTypeBuilder.build(), augSchema, inGrouping);
             } else {
-                addRawAugmentGenTypeDefinition(context, targetTypeBuilder.build(), augSchema);
+                addRawAugmentGenTypeDefinition(context, targetTypeBuilder.build(), augSchema, inGrouping);
             }
         } else {
             generateTypesFromAugmentedChoiceCases(context, targetTypeBuilder.build(),
-                (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), usesNodeParent);
+                (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), usesNodeParent, inGrouping);
         }
     }
 
@@ -929,7 +930,7 @@ abstract class AbstractTypeGenerator {
      */
     private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(final ModuleContext context,
             final String augmentPackageName, final Type targetTypeRef,
-            final AugmentationSchemaNode augSchema) {
+            final AugmentationSchemaNode augSchema, final boolean inGrouping) {
         Map<String, GeneratedTypeBuilder> augmentBuilders =
             genTypeBuilders.computeIfAbsent(augmentPackageName, k -> new HashMap<>());
         final String augIdentifier = getAugmentIdentifier(augSchema.getUnknownSchemaNodes());
@@ -949,7 +950,7 @@ abstract class AbstractTypeGenerator {
         annotateDeprecatedIfNecessary(augSchema.getStatus(), augTypeBuilder);
         addImplementedInterfaceFromUses(augSchema, augTypeBuilder);
 
-        augSchemaNodeToMethods(context, augTypeBuilder, augSchema.getChildNodes());
+        augSchemaNodeToMethods(context, augTypeBuilder, augSchema.getChildNodes(), inGrouping);
         augmentBuilders.put(augTypeName, augTypeBuilder);
 
         if (!augSchema.getChildNodes().isEmpty()) {
@@ -961,8 +962,9 @@ abstract class AbstractTypeGenerator {
     }
 
     private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(final ModuleContext context, final Type targetTypeRef,
-            final AugmentationSchemaNode augSchema) {
-        return addRawAugmentGenTypeDefinition(context, context.modulePackageName(), targetTypeRef, augSchema);
+            final AugmentationSchemaNode augSchema, final boolean inGrouping) {
+        return addRawAugmentGenTypeDefinition(context, context.modulePackageName(), targetTypeRef, augSchema,
+            inGrouping);
     }
 
     /**
@@ -1027,12 +1029,12 @@ abstract class AbstractTypeGenerator {
      *         added to it.
      */
     private GeneratedTypeBuilder resolveDataSchemaNodes(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final @Nullable Type childOf, final Iterable<DataSchemaNode> schemaNodes) {
+            final @Nullable Type childOf, final Iterable<DataSchemaNode> schemaNodes, final boolean inGrouping) {
         if (schemaNodes != null && parent != null) {
             final Type baseInterface = childOf == null ? DATA_OBJECT : childOf(childOf);
             for (final DataSchemaNode schemaNode : schemaNodes) {
                 if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) {
-                    addSchemaNodeToBuilderAsMethod(context, schemaNode, parent, baseInterface);
+                    addSchemaNodeToBuilderAsMethod(context, schemaNode, parent, baseInterface, inGrouping);
                 }
             }
         }
@@ -1060,12 +1062,13 @@ abstract class AbstractTypeGenerator {
      *         added to it.
      */
     private GeneratedTypeBuilder augSchemaNodeToMethods(final ModuleContext context,
-            final GeneratedTypeBuilder typeBuilder, final Iterable<DataSchemaNode> schemaNodes) {
+            final GeneratedTypeBuilder typeBuilder, final Iterable<DataSchemaNode> schemaNodes,
+            final boolean inGrouping) {
         if (schemaNodes != null && typeBuilder != null) {
             final Type baseInterface = childOf(typeBuilder);
             for (final DataSchemaNode schemaNode : schemaNodes) {
                 if (!schemaNode.isAugmenting()) {
-                    addSchemaNodeToBuilderAsMethod(context, schemaNode, typeBuilder, baseInterface);
+                    addSchemaNodeToBuilderAsMethod(context, schemaNode, typeBuilder, baseInterface, inGrouping);
                 }
             }
         }
@@ -1088,18 +1091,18 @@ abstract class AbstractTypeGenerator {
      *            current module
      */
     private void addSchemaNodeToBuilderAsMethod(final ModuleContext context, final DataSchemaNode node,
-            final GeneratedTypeBuilder typeBuilder, final Type baseInterface) {
+            final GeneratedTypeBuilder typeBuilder, final Type baseInterface, final boolean inGrouping) {
         if (node != null && typeBuilder != null) {
             if (node instanceof LeafSchemaNode) {
-                resolveLeafSchemaNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context);
+                resolveLeafSchemaNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context, inGrouping);
             } else if (node instanceof LeafListSchemaNode) {
-                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) node, context);
+                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) node, context, inGrouping);
             } else if (node instanceof ContainerSchemaNode) {
-                containerToGenType(context, typeBuilder, baseInterface, (ContainerSchemaNode) node);
+                containerToGenType(context, typeBuilder, baseInterface, (ContainerSchemaNode) node, inGrouping);
             } else if (node instanceof ListSchemaNode) {
-                listToGenType(context, typeBuilder, baseInterface, (ListSchemaNode) node);
+                listToGenType(context, typeBuilder, baseInterface, (ListSchemaNode) node, inGrouping);
             } else if (node instanceof ChoiceSchemaNode) {
-                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node);
+                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node, inGrouping);
             } else {
                 // TODO: anyxml not yet supported
                 LOG.debug("Unable to add schema node {} as method in {}: unsupported type of node.", node.getClass(),
@@ -1132,7 +1135,7 @@ abstract class AbstractTypeGenerator {
      *             </ul>
      */
     private void choiceToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final ChoiceSchemaNode choiceNode) {
+            final ChoiceSchemaNode choiceNode, final boolean inGrouping) {
         checkArgument(choiceNode != null, "Choice Schema Node cannot be NULL.");
 
         if (!choiceNode.isAddedByUses()) {
@@ -1144,7 +1147,7 @@ abstract class AbstractTypeGenerator {
             context.addChildNodeType(choiceNode, choiceTypeBuilder);
 
             final GeneratedType choiceType = choiceTypeBuilder.build();
-            generateTypesFromChoiceCases(context, choiceType, choiceNode);
+            generateTypesFromChoiceCases(context, choiceType, choiceNode, inGrouping);
 
             constructGetter(parent, choiceType, choiceNode);
         }
@@ -1171,7 +1174,7 @@ abstract class AbstractTypeGenerator {
      *             </ul>
      */
     private void generateTypesFromChoiceCases(final ModuleContext context, final Type refChoiceType,
-            final ChoiceSchemaNode choiceNode) {
+            final ChoiceSchemaNode choiceNode, final boolean inGrouping) {
         checkArgument(refChoiceType != null, "Referenced Choice Type cannot be NULL.");
         checkArgument(choiceNode != null, "ChoiceNode cannot be NULL.");
 
@@ -1213,13 +1216,14 @@ abstract class AbstractTypeGenerator {
                         if (childOfType == null) {
                             childOfType = findGroupingByPath(parent.getPath());
                         }
-                        resolveDataSchemaNodes(context, caseTypeBuilder, childOfType, caseChildNodes);
+                        resolveDataSchemaNodes(context, caseTypeBuilder, childOfType, caseChildNodes, inGrouping);
                     } else {
-                        resolveDataSchemaNodes(context, caseTypeBuilder, moduleToDataType(context), caseChildNodes);
+                        resolveDataSchemaNodes(context, caseTypeBuilder, moduleToDataType(context), caseChildNodes,
+                            inGrouping);
                     }
                }
             }
-            processUsesAugments(caseNode, context);
+            processUsesAugments(caseNode, context, inGrouping);
         }
     }
 
@@ -1250,7 +1254,7 @@ abstract class AbstractTypeGenerator {
      */
     private void generateTypesFromAugmentedChoiceCases(final ModuleContext context,
             final Type targetType, final ChoiceSchemaNode targetNode, final Iterable<DataSchemaNode> augmentedNodes,
-            final DataNodeContainer usesNodeParent) {
+            final DataNodeContainer usesNodeParent, final boolean inGrouping) {
         checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
         checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL.");
 
@@ -1277,7 +1281,8 @@ abstract class AbstractTypeGenerator {
                 }
                 final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
                 if (childNodes != null) {
-                    resolveDataSchemaNodes(context, caseTypeBuilder, findChildOfType(targetNode), childNodes);
+                    resolveDataSchemaNodes(context, caseTypeBuilder, findChildOfType(targetNode), childNodes,
+                        inGrouping);
                 }
                 context.addCaseType(caseNode.getPath(), caseTypeBuilder);
                 context.addChoiceToCaseMapping(targetType, caseTypeBuilder, node);
@@ -1358,7 +1363,7 @@ abstract class AbstractTypeGenerator {
      *         </ul>
      */
     private Type resolveLeafSchemaNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf,
-            final ModuleContext context) {
+            final ModuleContext context, final boolean inGrouping) {
         if (leaf == null || typeBuilder == null || leaf.isAddedByUses()) {
             return null;
         }
@@ -1369,7 +1374,7 @@ abstract class AbstractTypeGenerator {
         final TypeDefinition<?> typeDef = CompatUtils.compatLeafType(leaf);
         if (isInnerType(leaf, typeDef)) {
             if (typeDef instanceof EnumTypeDefinition) {
-                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf);
+                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, inGrouping);
                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
                 final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.getQName(),
                     typeBuilder, context);
@@ -1395,12 +1400,12 @@ abstract class AbstractTypeGenerator {
                 // and apply restrictions from leaf type
                 final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
                 returnType = typeProvider.javaTypeForSchemaDefinitionType(getBaseOrDeclaredType(typeDef), leaf,
-                        restrictions);
+                        restrictions, inGrouping);
                 addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
             }
         } else {
             final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
-            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions);
+            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions, inGrouping);
             addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
         }
 
@@ -1564,7 +1569,7 @@ abstract class AbstractTypeGenerator {
      *         </ul>
      */
     private boolean resolveLeafListSchemaNode(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node,
-            final ModuleContext context) {
+            final ModuleContext context, final boolean inGrouping) {
         if (node == null || typeBuilder == null || node.isAddedByUses()) {
             return false;
         }
@@ -1577,7 +1582,7 @@ abstract class AbstractTypeGenerator {
         Type returnType = null;
         if (typeDef.getBaseType() == null) {
             if (typeDef instanceof EnumTypeDefinition) {
-                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node);
+                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, inGrouping);
                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
                 final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, nodeName,
                     typeBuilder, context);
@@ -1592,12 +1597,12 @@ abstract class AbstractTypeGenerator {
                 returnType = genTOBuilder.build();
             } else {
                 final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
-                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
+                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping);
                 addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
             }
         } else {
             final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
-            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
+            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping);
             addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
         }
 
@@ -1853,14 +1858,14 @@ abstract class AbstractTypeGenerator {
      */
     private void addSchemaNodeToListBuilders(final ModuleContext context, final DataSchemaNode schemaNode,
             final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder,
-            final List<String> listKeys) {
+            final List<String> listKeys, final boolean inGrouping) {
         checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
         checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL.");
 
         if (schemaNode instanceof LeafSchemaNode) {
             final LeafSchemaNode leaf = (LeafSchemaNode) schemaNode;
             final String leafName = leaf.getQName().getLocalName();
-            Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf, context);
+            Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf, context, inGrouping);
             if (listKeys.contains(leafName)) {
                 if (type == null) {
                     resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
@@ -1870,14 +1875,14 @@ abstract class AbstractTypeGenerator {
             }
         } else if (!schemaNode.isAddedByUses()) {
             if (schemaNode instanceof LeafListSchemaNode) {
-                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) schemaNode, context);
+                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) schemaNode, context, inGrouping);
             } else if (schemaNode instanceof ContainerSchemaNode) {
                 containerToGenType(context, typeBuilder, childOf(typeBuilder),
-                    (ContainerSchemaNode) schemaNode);
+                    (ContainerSchemaNode) schemaNode, inGrouping);
             } else if (schemaNode instanceof ChoiceSchemaNode) {
-                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) schemaNode);
+                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) schemaNode, inGrouping);
             } else if (schemaNode instanceof ListSchemaNode) {
-                listToGenType(context, typeBuilder, childOf(typeBuilder), (ListSchemaNode) schemaNode);
+                listToGenType(context, typeBuilder, childOf(typeBuilder), (ListSchemaNode) schemaNode, inGrouping);
             }
         }
     }
index 86ef3df0863adcdb41e0355052384119f1070eeb..8868683795b70a1c9dec91c83b692492eb3fac95 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.mdsal.binding.yang.types;
 
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
@@ -174,8 +175,9 @@ public abstract class AbstractTypeProvider implements TypeProvider {
     }
 
     @Override
-    public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode) {
-        return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null);
+    public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
+            final boolean lenientRelativeLeafrefs) {
+        return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null, lenientRelativeLeafrefs);
     }
 
     /**
@@ -193,7 +195,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      */
     @Override
     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
-            final Restrictions r) {
+            final Restrictions r, final boolean lenientRelativeLeafrefs) {
         Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
         Preconditions.checkArgument(typeDefinition.getQName() != null,
                 "Type Definition cannot have non specified QName (QName cannot be NULL!)");
@@ -207,14 +209,14 @@ public abstract class AbstractTypeProvider implements TypeProvider {
             // a base type which holds these constraints.
             if (typeDefinition instanceof DecimalTypeDefinition) {
                 final Type ret = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(typeDefinition,
-                    parentNode, r);
+                    parentNode, r, lenientRelativeLeafrefs);
                 if (ret != null) {
                     return ret;
                 }
             }
 
             // Deal with leafrefs/identityrefs
-            Type ret = javaTypeForLeafrefOrIdentityRef(typeDefinition, parentNode);
+            Type ret = javaTypeForLeafrefOrIdentityRef(typeDefinition, parentNode, lenientRelativeLeafrefs);
             if (ret != null) {
                 return ret;
             }
@@ -228,7 +230,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
             return ret;
         }
 
-        Type returnType = javaTypeForExtendedType(typeDefinition);
+        Type returnType = javaTypeForExtendedType(typeDefinition, lenientRelativeLeafrefs);
         if (r != null && returnType instanceof GeneratedTransferObject) {
             final GeneratedTransferObject gto = (GeneratedTransferObject) returnType;
             final Module module = findParentModule(schemaContext, parentNode);
@@ -315,12 +317,13 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      *            type definition which is converted to JAVA <code>Type</code>
      * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
      */
-    private Type javaTypeForLeafrefOrIdentityRef(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode) {
+    private Type javaTypeForLeafrefOrIdentityRef(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
+            final boolean inGrouping) {
         if (typeDefinition instanceof LeafrefTypeDefinition) {
             final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
             Preconditions.checkArgument(!isLeafRefSelfReference(leafref, parentNode),
                 "Leafref %s is referencing itself, incoming StackOverFlowError detected.", leafref);
-            return provideTypeForLeafref(leafref, parentNode);
+            return provideTypeForLeafref(leafref, parentNode, inGrouping);
         } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
             return provideTypeForIdentityref((IdentityrefTypeDefinition) typeDefinition);
         }
@@ -336,10 +339,10 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      *            type definition which is converted to JAVA <code>Type</code>
      * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
      */
-    private Type javaTypeForExtendedType(final TypeDefinition<?> typeDefinition) {
+    private Type javaTypeForExtendedType(final TypeDefinition<?> typeDefinition, final boolean lenient) {
         final String typedefName = typeDefinition.getQName().getLocalName();
         final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
-        Type returnType = javaTypeForLeafrefOrIdentityRef(baseTypeDef, typeDefinition);
+        Type returnType = javaTypeForLeafrefOrIdentityRef(baseTypeDef, typeDefinition, lenient);
         if (returnType == null) {
             if (baseTypeDef instanceof EnumTypeDefinition) {
                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
@@ -356,7 +359,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
                     }
                     if (returnType == null) {
                         returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
-                                baseTypeDef, typeDefinition, r);
+                                baseTypeDef, typeDefinition, r, lenient);
                     }
                 }
             }
@@ -518,8 +521,9 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      * The path of <code>leafrefType</code> is followed to find referenced node
      * and its <code>Type</code> is returned.
      *
-     * @param leafrefType
-     *            leafref type definition for which is the type sought
+     * @param leafrefType leafref type definition for which is the type sought
+     * @param parentNode parent node of the leaf being resolved
+     * @param inGrouping true if we are resolving the type within a grouping.
      * @return JAVA <code>Type</code> of data schema node which is referenced in
      *         <code>leafrefType</code>
      * @throws IllegalArgumentException
@@ -527,45 +531,72 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      *             <li>if <code>leafrefType</code> equal null</li>
      *             <li>if path statement of <code>leafrefType</code> equal null</li>
      *             </ul>
-     *
      */
-    public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType, final SchemaNode parentNode) {
-        Type returnType = null;
+    public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType, final SchemaNode parentNode,
+            final boolean inGrouping) {
         Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
 
         Preconditions.checkArgument(leafrefType.getPathStatement() != null,
                 "The Path Statement for Leafref Type Definition cannot be NULL!");
 
         final RevisionAwareXPath xpath = leafrefType.getPathStatement();
-        final String strXPath = xpath.toString();
+        final String strXPath = verifyNotNull(xpath.toString());
+        if (strXPath.indexOf('[') != -1) {
+            // XXX: why are we special-casing this?
+            return Types.objectType();
+        }
 
-        if (strXPath != null) {
-            if (strXPath.indexOf('[') == -1) {
-                final Module module = findParentModule(schemaContext, parentNode);
-                Preconditions.checkArgument(module != null, "Failed to find module for parent %s", parentNode);
+        final Module module = findParentModule(schemaContext, parentNode);
+        Preconditions.checkArgument(module != null, "Failed to find module for parent %s", parentNode);
 
-                final SchemaNode dataNode;
-                if (xpath.isAbsolute()) {
-                    dataNode = findDataSchemaNode(schemaContext, module, xpath);
-                } else {
-                    dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
-                }
-                Preconditions.checkArgument(dataNode != null, "Failed to find leafref target: %s in module %s (%s)",
-                        strXPath, this.getParentModule(parentNode).getName(), parentNode.getQName().getModule());
-
-                // FIXME: this block seems to be some weird magic hack. Analyze and refactor it.
-                if (leafContainsEnumDefinition(dataNode)) {
-                    returnType = referencedTypes.get(dataNode.getPath());
-                } else if (leafListContainsEnumDefinition(dataNode)) {
-                    returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
-                }
-                if (returnType == null) {
-                    returnType = resolveTypeFromDataSchemaNode(dataNode);
-                }
-            } else {
-                returnType = Types.objectType();
+        final SchemaNode dataNode;
+        if (xpath.isAbsolute()) {
+            dataNode = findDataSchemaNode(schemaContext, module, xpath);
+        } else {
+            dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
+            if (dataNode == null && inGrouping) {
+                // Relative path within a grouping may end up being unresolvable because it may refer outside
+                // the grouping, in which case it is polymorphic based on instantiation, for example:
+                //
+                // grouping foo {
+                //     leaf foo {
+                //         type leafref {
+                //             path "../../bar";
+                //         }
+                //     }
+                // }
+                //
+                // container one {
+                //     leaf bar {
+                //         type string;
+                //     }
+                //     uses foo;
+                // }
+                //
+                // container two {
+                //     leaf bar {
+                //         type uint16;
+                //     }
+                //     uses foo;
+                // }
+                LOG.debug("Leafref type {} not found in parent {}, assuming polymorphic object", leafrefType,
+                    parentNode);
+                return Types.objectType();
             }
         }
+        Preconditions.checkArgument(dataNode != null, "Failed to find leafref target: %s in module %s (%s)",
+                strXPath, this.getParentModule(parentNode).getName(), parentNode.getQName().getModule());
+
+        // FIXME: this block seems to be some weird magic hack. Analyze and refactor it.
+        Type returnType = null;
+        if (leafContainsEnumDefinition(dataNode)) {
+            returnType = referencedTypes.get(dataNode.getPath());
+        } else if (leafListContainsEnumDefinition(dataNode)) {
+            returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
+        }
+        if (returnType == null) {
+            returnType = resolveTypeFromDataSchemaNode(dataNode);
+        }
         Preconditions.checkArgument(returnType != null, "Failed to find leafref target: %s in module %s (%s)",
                 strXPath, this.getParentModule(parentNode).getName(), parentNode.getQName().getModule(), this);
         return returnType;
index 7cb8141096b0dbbd4e8fd9d48e9c8c9fa076cca2..0df7afe9e6168b0bc5e2cb6a3943202a8678e59e 100644 (file)
@@ -159,7 +159,8 @@ public final class BaseYangTypes {
          *         returned.
          */
         @Override
-        public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> type, final SchemaNode parentNode) {
+        public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> type, final SchemaNode parentNode,
+                final boolean lenientRelativeLeafrefs) {
             if (type != null) {
                 return TYPE_MAP.get(type.getQName().getLocalName());
             }
@@ -169,7 +170,7 @@ public final class BaseYangTypes {
 
         @Override
         public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> type, final SchemaNode parentNode,
-                final Restrictions restrictions) {
+                final Restrictions restrictions, final boolean lenientRelativeLeafrefs) {
             String typeName = type.getQName().getLocalName();
             switch (typeName) {
             case "binary":
@@ -199,7 +200,7 @@ public final class BaseYangTypes {
             case "union" :
                 return UNION_TYPE;
             default:
-                return javaTypeForSchemaDefinitionType(type, parentNode);
+                return javaTypeForSchemaDefinitionType(type, parentNode, lenientRelativeLeafrefs);
             }
         }
 
diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal182Test.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal182Test.java
new file mode 100644 (file)
index 0000000..bf297c2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.generator.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+/**
+ * Test leafref resolution when the leaf is from a grouping.
+ */
+public class Mdsal182Test {
+
+    @Test
+    public void testOneUpLeafref() {
+        final SchemaContext context = YangParserTestUtils.parseYangResource("/mdsal-182/good-leafref.yang");
+        final Collection<Type> types = new BindingGeneratorImpl().generateTypes(context);
+        assertEquals(6, types.size());
+    }
+
+    @Test
+    public void testTwoUpLeafref() {
+        final SchemaContext context = YangParserTestUtils.parseYangResource("/mdsal-182/grouping-leafref.yang");
+        final Collection<Type> types = new BindingGeneratorImpl().generateTypes(context);
+        assertNotNull(types);
+        assertEquals(4, types.size());
+    }
+}
index e138a62aeb58d0ba06a9baabb69e6b70a6a1feb2..003d490348a99944518f4e938ac10e18d82faa7a 100644 (file)
@@ -398,7 +398,7 @@ public class TypeProviderTest {
     public void provideTypeForLeafrefWithNullLeafrefTypeTest() {
         final AbstractTypeProvider provider = new RuntimeTypeProvider(this.schemaContext);
 
-        provider.provideTypeForLeafref(null, null);
+        provider.provideTypeForLeafref(null, null, false);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -406,7 +406,7 @@ public class TypeProviderTest {
         final AbstractTypeProvider provider = new RuntimeTypeProvider(this.schemaContext);
 
         final LeafrefTypeWithNullXpath leafrePath = new LeafrefTypeWithNullXpath();
-        provider.provideTypeForLeafref(leafrePath, this.schemaNode);
+        provider.provideTypeForLeafref(leafrePath, this.schemaNode, false);
     }
 
     @Test(expected = IllegalStateException.class)
@@ -417,7 +417,7 @@ public class TypeProviderTest {
         final TypeDefinition<?> leafType = leaf.getType();
         assertTrue(leafType instanceof LeafrefTypeDefinition);
         doReturn(null).when(this.schemaNode).getPath();
-        provider.provideTypeForLeafref((LeafrefTypeDefinition) leafType, this.schemaNode);
+        provider.provideTypeForLeafref((LeafrefTypeDefinition) leafType, this.schemaNode, false);
     }
 
     @Test
diff --git a/binding/mdsal-binding-generator-impl/src/test/resources/mdsal-182/good-leafref.yang b/binding/mdsal-binding-generator-impl/src/test/resources/mdsal-182/good-leafref.yang
new file mode 100644 (file)
index 0000000..ef2342b
--- /dev/null
@@ -0,0 +1,44 @@
+module good-leafref {
+
+  namespace "odl:test:leafref:good";
+  prefix "gl";
+  revision 2016-07-01;
+
+  leaf target-leaf {
+    type string;
+  }
+
+  /* one level without uses */
+  container direct-container {
+    leaf direct-leafref {
+      type leafref {
+        path "../../target-leaf";
+      }
+    }
+  }
+
+  /* one level with uses */
+  grouping leafref-grouping {
+    leaf grouping-leafref {
+      type leafref {
+        path "../../target-leaf";
+      }
+    }
+  }
+
+  container grouping-container {
+    uses leafref-grouping;
+  }
+
+  /* two levels without uses */
+  container outer-container {
+    container inner-container {
+      leaf my-leafref {
+        type leafref {
+          path "../../../target-leaf";
+        }
+      }
+    }
+  }
+
+}
diff --git a/binding/mdsal-binding-generator-impl/src/test/resources/mdsal-182/grouping-leafref.yang b/binding/mdsal-binding-generator-impl/src/test/resources/mdsal-182/grouping-leafref.yang
new file mode 100644 (file)
index 0000000..1bed584
--- /dev/null
@@ -0,0 +1,26 @@
+module grouping-leafref {
+
+  namespace "odl:test:leafref:grouping";
+  prefix "gl";
+  revision 2016-07-01;
+
+  leaf target-leaf {
+    type string;
+  }
+
+  /* two levels with uses */
+  grouping leafref-grouping {
+    leaf my-leafref {
+      type leafref {
+        path "../../../target-leaf";
+      }
+    }
+  }
+
+  container outer-container {
+    container inner-container {
+      uses leafref-grouping;
+    }
+  }
+
+}
diff --git a/binding/mdsal-binding-test-model/src/main/yang/mdsal-182.yang b/binding/mdsal-binding-test-model/src/main/yang/mdsal-182.yang
new file mode 100644 (file)
index 0000000..36894e2
--- /dev/null
@@ -0,0 +1,27 @@
+module mdsal-182 {
+       namespace "mdsal-182";
+       prefix "mdsal182";
+
+    grouping foo {
+        leaf foo {
+            type leafref {
+                path "../../bar";
+            }
+        }
+    }
+
+    container one {
+        leaf bar {
+            type string;
+        }
+        uses foo;
+    }
+
+    container two {
+        leaf bar {
+            type uint16;
+        }
+        uses foo;
+    }
+}
+