Binding generator v2 - Choice/Cases support 83/58683/1
authorMartin Ciglan <martin.ciglan@pantheon.tech>
Fri, 9 Jun 2017 09:09:53 +0000 (11:09 +0200)
committerMartin Ciglan <martin.ciglan@pantheon.tech>
Sun, 11 Jun 2017 18:45:46 +0000 (18:45 +0000)
- generate types & Java code
- builder template fix
- test yang file

Change-Id: Ia001b5e02a3b2e870ae0b46f59a7a9546395b48b
Signed-off-by: Martin Ciglan <martin.ciglan@pantheon.tech>
(cherry picked from commit d235138fce6cc0bd83c72e7b6f8eeefaf260b428)

binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/GenHelperUtil.java
binding2/mdsal-binding2-generator-impl/src/test/resources/choice/test-choice.yang [new file with mode: 0644]
binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRenderer.java
binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding/javav2/java/api/generator/builderTemplate.scala.txt

index 0c72ed3bf8fa2884ea5d96cd2557c991cb663084..e050053455b8d3ba0b5d4bcb8f6bfbc7ceb52237 100644 (file)
@@ -23,12 +23,15 @@ import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenU
 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.computeDefaultSUID;
 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.encodeAngleBrackets;
 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.packageNameForGeneratedType;
+import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.INSTANTIABLE;
 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.NOTIFICATION;
 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.parameterizedTypeFor;
+import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -62,9 +65,12 @@ import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
@@ -536,11 +542,59 @@ final class GenHelperUtil {
             } else if (node instanceof AnyXmlSchemaNode) {
                 resolveAnyxmlNodeAsMethod(schemaContext, typeBuilder, genCtx, (AnyXmlSchemaNode) node, module,
                         typeProvider);
+            } else if (node instanceof ChoiceSchemaNode) {
+                choiceToGenType(module, schemaContext, verboseClassComments, basePackageName, childOf,
+                        (ChoiceSchemaNode) node, genTypeBuilders, genCtx, typeProvider);
             }
         }
 
     }
 
+    /**
+     * Converts <code>choiceNode</code> to the list of generated types for
+     * choice and its cases.
+     *
+     * The package names for choice and for its cases are created as
+     * concatenation of the module package (<code>basePackageName</code>) and
+     * names of all parents node.
+     *
+     * @param module
+     *            current module
+     * @param basePackageName
+     *            string with the module package name
+     * @param parent
+     *            parent type
+     * @param choiceNode
+     *            choice node which is mapped to generated type. Also child
+     *            nodes - cases are mapped to generated types.
+     * @throws IllegalArgumentException
+     *             <ul>
+     *             <li>if <code>basePackageName</code> is null</li>
+     *             <li>if <code>choiceNode</code> is null</li>
+     *             </ul>
+     */
+    private static void choiceToGenType(final Module module, final SchemaContext schemaContext, final boolean
+            verboseClasssComments, final String basePackageName, final GeneratedTypeBuilder parent, final
+            ChoiceSchemaNode choiceNode, final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
+            final Map<Module, ModuleContext> genCtx, final TypeProvider typeProvider) {
+        checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
+        checkArgument(choiceNode != null, "Choice Schema Node cannot be NULL.");
+
+        if (!choiceNode.isAddedByUses()) {
+            final String packageName = packageNameForGeneratedType(basePackageName, choiceNode.getPath(),
+                    BindingNamespaceType.Data);
+            final GeneratedTypeBuilder choiceTypeBuilder = addRawInterfaceDefinition(packageName, choiceNode,
+                    schemaContext, "", verboseClasssComments, genTypeBuilders);
+            constructGetter(parent, choiceNode.getQName().getLocalName(),
+                    choiceNode.getDescription(), choiceTypeBuilder, choiceNode.getStatus());
+            choiceTypeBuilder.addImplementsType(INSTANTIABLE);
+            annotateDeprecatedIfNecessary(choiceNode.getStatus(), choiceTypeBuilder);
+            genCtx.get(module).addChildNodeType(choiceNode, choiceTypeBuilder);
+            generateTypesFromChoiceCases(module, schemaContext, genCtx, basePackageName, choiceTypeBuilder.toInstance(),
+                choiceNode, verboseClasssComments, typeProvider, genTypeBuilders);
+        }
+    }
+
     private static void containerToGenType(final Module module, final String basePackageName,
         final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ContainerSchemaNode node,
         final SchemaContext schemaContext, final boolean verboseClassComments, final Map<Module, ModuleContext> genCtx,
@@ -688,6 +742,107 @@ final class GenHelperUtil {
         return returnType;
     }
 
+    /**
+     * Converts <code>caseNodes</code> set to list of corresponding generated
+     * types.
+     *
+     * For every <i>case</i> which isn't added through augment or <i>uses</i> is
+     * created generated type builder. The package names for the builder is
+     * created as concatenation of the module package (
+     * <code>basePackageName</code>) and names of all parents nodes of the
+     * concrete <i>case</i>. There is also relation "<i>implements type</i>"
+     * between every case builder and <i>choice</i> type
+     *
+     * @param module
+     *            current module
+     * @param schemaContext
+     *            current schema context
+     * @param genCtx
+     *            actual generated context
+     * @param basePackageName
+     *            string with the module package name
+     * @param refChoiceType
+     *            type which represents superior <i>case</i>
+     * @param choiceNode
+     *            choice case node which is mapped to generated type
+     * @param verboseClassComments
+     *            Javadoc verbosity switch
+     * @throws IllegalArgumentException
+     *             <ul>
+     *             <li>if <code>basePackageName</code> equals null</li>
+     *             <li>if <code>refChoiceType</code> equals null</li>
+     *             <li>if <code>caseNodes</code> equals null</li>
+     *             </ul>
+     */
+    private static void generateTypesFromChoiceCases(final Module module, final SchemaContext schemaContext,
+            final Map<Module, ModuleContext> genCtx, final String basePackageName, final Type refChoiceType,
+            final ChoiceSchemaNode choiceNode, final boolean verboseClassComments, final TypeProvider typeProvider,
+            final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
+        checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
+        checkArgument(refChoiceType != null, "Referenced Choice Type cannot be NULL.");
+        checkArgument(choiceNode != null, "ChoiceNode cannot be NULL.");
+
+        final Set<ChoiceCaseNode> caseNodes = choiceNode.getCases();
+        if (caseNodes == null) {
+            return;
+        }
+
+        for (final ChoiceCaseNode caseNode : caseNodes) {
+            if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
+                final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath(),
+                    BindingNamespaceType.Data);
+                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode,
+                    module, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
+                caseTypeBuilder.addImplementsType(refChoiceType);
+                caseTypeBuilder.setParentTypeForBuilder(refChoiceType);
+                annotateDeprecatedIfNecessary(caseNode.getStatus(), caseTypeBuilder);
+                genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
+                genCtx.get(module).addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode);
+                final Iterable<DataSchemaNode> caseChildNodes = caseNode.getChildNodes();
+                if (caseChildNodes != null) {
+                    final SchemaPath choiceNodeParentPath = choiceNode.getPath().getParent();
+
+                    if (!Iterables.isEmpty(choiceNodeParentPath.getPathFromRoot())) {
+                        SchemaNode parent = findDataSchemaNode(schemaContext, choiceNodeParentPath);
+
+                        if (parent instanceof AugmentationSchema) {
+                            final AugmentationSchema augSchema = (AugmentationSchema) parent;
+                            final SchemaPath targetPath = augSchema.getTargetPath();
+                            SchemaNode targetSchemaNode = findDataSchemaNode(schemaContext, targetPath);
+                            if (targetSchemaNode instanceof DataSchemaNode
+                                    && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
+                                if (targetSchemaNode instanceof DerivableSchemaNode) {
+                                    targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
+                                }
+                                if (targetSchemaNode == null) {
+                                    throw new IllegalStateException(
+                                            "Failed to find target node from grouping for augmentation " + augSchema
+                                                    + " in module " + module.getName());
+                                }
+                            }
+                            parent = targetSchemaNode;
+                        }
+
+                        Preconditions.checkState(parent != null, "Could not find Choice node parent %s",
+                                choiceNodeParentPath);
+                        GeneratedTypeBuilder childOfType = findChildNodeByPath(parent.getPath(), genCtx);
+                        if (childOfType == null) {
+                            childOfType = findGroupingByPath(parent.getPath(), genCtx);
+                        }
+                        resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, caseChildNodes,
+                                genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
+                    } else {
+                        resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, moduleToDataType(module,
+                                genCtx, verboseClassComments), caseChildNodes, genCtx, schemaContext,
+                                verboseClassComments, genTypeBuilders, typeProvider);
+                    }
+                }
+            }
+            processUsesAugments(schemaContext, caseNode, module, genCtx, genTypeBuilders, verboseClassComments,
+                    typeProvider);
+        }
+    }
+
     private static Type resolveAnyxmlNodeAsMethod(final SchemaContext schemaContext, final GeneratedTypeBuilder
             typeBuilder, final Map<Module, ModuleContext> genCtx, final AnyXmlSchemaNode anyxml, final Module module,
             final TypeProvider typeProvider) {
@@ -780,13 +935,15 @@ final class GenHelperUtil {
             }
         } else if (!schemaNode.isAddedByUses()) {
             //TODO: implement leaf list to generated type
-            //TODO: implement choice to generated type
             if (schemaNode instanceof ContainerSchemaNode) {
                 containerToGenType(module, basePackageName, typeBuilder, typeBuilder, (ContainerSchemaNode) schemaNode,
                         schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
             } else if (schemaNode instanceof ListSchemaNode) {
                 listToGenType(module, basePackageName, typeBuilder, typeBuilder, (ListSchemaNode) schemaNode,
                         schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
+            } else if (schemaNode instanceof ChoiceSchemaNode) {
+                choiceToGenType(module, schemaContext, verboseClassComments, basePackageName, typeBuilder,
+                        (ChoiceSchemaNode) schemaNode, genTypeBuilders, genCtx, typeProvider);
             }
         }
     }
diff --git a/binding2/mdsal-binding2-generator-impl/src/test/resources/choice/test-choice.yang b/binding2/mdsal-binding2-generator-impl/src/test/resources/choice/test-choice.yang
new file mode 100644 (file)
index 0000000..e2e7e80
--- /dev/null
@@ -0,0 +1,29 @@
+module test-choice {
+    yang-version 1;
+    namespace "org.test.choice";
+    prefix "tstch";
+
+    container food {
+        choice snack {
+            case sports-arena {
+                leaf pretzel {
+                    type empty;
+                }
+
+                leaf beer {
+                    type empty;
+                }
+            }
+
+            case late-night {
+                leaf chocolate {
+                    type enumeration {
+                        enum dark;
+                        enum milk;
+                        enum first-available;
+                    }
+                }
+            }
+        }
+    }
+}
index 8fae7f7fa7dfef24d458a229384710a10253d197..d81280d44e80943d28a999b8845762758747a9b5 100644 (file)
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
 import org.opendaylight.mdsal.binding.javav2.generator.util.ReferencedTypeImpl;
 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
@@ -258,6 +259,12 @@ public class BuilderRenderer extends BaseRenderer {
         } else {
             parentTypeForBuilderName = null;
         }
+
+        boolean childTreeNode = false;
+        if (getType().getImplements().contains(BindingTypes.TREE_CHILD_NODE)) {
+            childTreeNode = true;
+        }
+
         importedNames.put("augmentation", importedName(Augmentation.class));
         importedNames.put("classInstMap", importedName(ClassToInstanceMap.class));
 
@@ -266,7 +273,7 @@ public class BuilderRenderer extends BaseRenderer {
         List<String> getterMethods = new ArrayList<>(Collections2.transform(properties, this::getterMethod));
 
         return builderTemplate.render(getType(), properties, importedNames, importedNamesForProperties, augmentField,
-                copyConstructorHelper, getterMethods, parentTypeForBuilderName)
+            copyConstructorHelper, getterMethods, parentTypeForBuilderName, childTreeNode)
                 .body();
     }
 
index 6113e16ac2ad7ddce985abaa70e1872648aaa770..2f7485c088c5d2c9ad1b4f1b79a77a5a29f94c40 100644 (file)
@@ -29,7 +29,7 @@
 
 @(genType: GeneratedType, properties: Set[GeneratedProperty], importedNames: Map[String, String],
 ImportedNamesWithProperties: Map[GeneratedProperty, String], augmentField: GeneratedProperty, copyConstructorHelper: String,
-getterMethods: List[String], parentTypeForBuilderName: String)
+getterMethods: List[String], parentTypeForBuilderName: String, childTreeNode: Boolean)
 @if(genType != null) {
 @{wrapToDocumentation(formatDataForJavaDocBuilder(importedNames.get("genType")))}
 public class @{genType.getName}Builder implements @{getSimpleNameForBuilder} <@{importedNames.get("genType")}> {
@@ -400,7 +400,7 @@ public class @{genType.getName}Builder implements @{getSimpleNameForBuilder} <@{
 }
 
 @generateImplementedMethods() = {
-    @if(parentTypeForBuilderName != null) {
+    @if(childTreeNode) {
         @@Override
         public @{importedNames.get("item")}<@{parentTypeForBuilderName}> treeIdentifier() {
             //TODO implement