Tolerate unresolvable leafrefs in groupings 42/41142/7
authorVratko Polak <vrpolak@cisco.com>
Fri, 1 Jul 2016 11:21:15 +0000 (13:21 +0200)
committerRobert Varga <nite@hq.sk>
Wed, 20 Mar 2019 09:28:16 +0000 (09:28 +0000)
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>
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 2df061031fc9587bed2897296eb4c8b16570fef8..2f4c7e7a0004e52a4196b06bb4973faa48949646 100644 (file)
@@ -15,24 +15,39 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 
 public interface TypeProvider {
     /**
-     * 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 620f5532e17cd1c5c70b7ff09629ca25e4a7cc3e..2e2b1a499611d6cfa1e1f734fec83f1eaa7cb29f 100644 (file)
@@ -233,7 +233,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;
     }
@@ -271,7 +271,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;
         }
@@ -286,24 +286,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) {
             final ParameterizedType listType = listTypeFor(genType);
             constructGetter(parent, listType, node);
@@ -318,11 +318,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);
                 }
             }
 
@@ -337,11 +337,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);
             }
         }
     }
@@ -418,7 +419,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;
@@ -427,8 +428,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)) {
@@ -467,9 +468,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();
     }
 
@@ -526,7 +527,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);
@@ -535,7 +536,7 @@ abstract class AbstractTypeGenerator {
         outType.addImplementsType(augmentable(outType));
         defaultImplementedInterace(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();
     }
@@ -569,7 +570,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);
@@ -580,7 +581,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")
@@ -696,10 +697,10 @@ abstract class AbstractTypeGenerator {
             narrowImplementedInterface(genType);
             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);
         }
     }
 
@@ -765,7 +766,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;
 
@@ -793,21 +794,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) {
@@ -827,13 +828,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);
         }
     }
 
@@ -906,7 +907,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());
@@ -928,7 +929,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()) {
@@ -940,8 +941,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);
     }
 
     private static String getAugmentIdentifier(final List<UnknownSchemaNode> unknownSchemaNodes) {
@@ -989,12 +991,12 @@ abstract class AbstractTypeGenerator {
      *         child nodes) could be 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);
                 }
             }
         }
@@ -1016,12 +1018,13 @@ abstract class AbstractTypeGenerator {
      *         The getter method could be 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);
                 }
             }
         }
@@ -1037,18 +1040,18 @@ abstract class AbstractTypeGenerator {
      * @param module 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(),
@@ -1074,7 +1077,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()) {
@@ -1086,7 +1089,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);
         }
@@ -1108,7 +1111,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.");
 
@@ -1151,13 +1154,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);
         }
     }
 
@@ -1184,7 +1188,7 @@ abstract class AbstractTypeGenerator {
     @SuppressFBWarnings("NP_NULL_ON_SOME_PATH")
     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.");
 
@@ -1212,7 +1216,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);
@@ -1287,7 +1292,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;
         }
@@ -1298,7 +1303,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);
@@ -1324,12 +1329,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());
         }
 
@@ -1481,7 +1486,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;
         }
@@ -1494,7 +1499,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);
@@ -1509,12 +1514,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());
         }
 
@@ -1714,14 +1719,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);
@@ -1731,14 +1736,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 39f5931d9ae8876a19bcb579157bc7231759cebb..8f14010b44525d26204b76fde6cdcce2eee41100 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.mdsal.binding.model.util.BindingTypes.TYPE_OBJECT;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
@@ -151,8 +152,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);
     }
 
     /**
@@ -168,7 +170,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      */
     @Override
     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
-            final Restrictions restrictions) {
+            final Restrictions restrictions, 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!)");
@@ -182,14 +184,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, restrictions);
+                    parentNode, restrictions, 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;
             }
@@ -203,7 +205,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
             return ret;
         }
 
-        Type returnType = javaTypeForExtendedType(typeDefinition);
+        Type returnType = javaTypeForExtendedType(typeDefinition, lenientRelativeLeafrefs);
         if (restrictions != null && returnType instanceof GeneratedTransferObject) {
             final GeneratedTransferObject gto = (GeneratedTransferObject) returnType;
             final Module module = findParentModule(schemaContext, parentNode);
@@ -289,12 +291,13 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      * @param typeDefinition 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);
         }
@@ -308,10 +311,10 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      * @param typeDefinition 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;
@@ -328,7 +331,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
                     }
                     if (returnType == null) {
                         returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
-                                baseTypeDef, typeDefinition, r);
+                                baseTypeDef, typeDefinition, r, lenient);
                     }
                 }
             }
@@ -485,50 +488,79 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      * to find referenced node and its <code>Type</code> is returned.
      *
      * @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
      *             <ul>
      *             <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) {
+    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();
-        Type returnType = null;
+        Preconditions.checkArgument(xpath != null, "The Path Statement for Leafref Type Definition cannot be NULL!");
 
-        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 String strXPath = verifyNotNull(xpath.toString());
+        if (strXPath.indexOf('[') != -1) {
+            // XXX: why are we special-casing this?
+            return Types.objectType();
+        }
 
-                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 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);
+            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 2f3d7a23e544ab7ee8673d01d21464f524d2504a..d299aadcce1dee5f583a7dd6118f4c8fdd743ca0 100644 (file)
@@ -153,7 +153,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());
             }
@@ -163,7 +164,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":
@@ -193,7 +194,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 1a0c80993b3c852bb4330834c6dee5a7da767f40..72642e64900e525f5f7f737cc49e358887c2aa09 100644 (file)
@@ -384,7 +384,7 @@ public class TypeProviderTest {
     public void provideTypeForLeafrefWithNullLeafrefTypeTest() {
         final AbstractTypeProvider provider = new RuntimeTypeProvider(SCHEMA_CONTEXT);
 
-        provider.provideTypeForLeafref(null, null);
+        provider.provideTypeForLeafref(null, null, false);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -392,7 +392,7 @@ public class TypeProviderTest {
         final AbstractTypeProvider provider = new RuntimeTypeProvider(SCHEMA_CONTEXT);
 
         final LeafrefTypeWithNullXpath leafrePath = new LeafrefTypeWithNullXpath();
-        provider.provideTypeForLeafref(leafrePath, this.schemaNode);
+        provider.provideTypeForLeafref(leafrePath, this.schemaNode, false);
     }
 
     @Test(expected = IllegalStateException.class)
@@ -403,7 +403,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;
+    }
+}
+