Merge "Added tests for yang.model.util"
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / BindingGeneratorImpl.java
index 60120759fabeb7227c770cc9fd540f647306dc60..b98c48c9844f0b5067aeae72cd1894b9efd43b9c 100644 (file)
@@ -105,9 +105,28 @@ import org.slf4j.LoggerFactory;
 
 public class BindingGeneratorImpl implements BindingGenerator {
     private static final Logger LOG = LoggerFactory.getLogger(BindingGeneratorImpl.class);
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final Splitter BSDOT_SPLITTER = Splitter.on("\\.");
+    private static final char NEW_LINE = '\n';
+
+    /**
+     * Constant with the concrete name of identifier.
+     */
+    private static final String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
+
+    /**
+     * Constant with the concrete name of namespace.
+     */
+    private static final String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
 
     private final Map<Module, ModuleContext> genCtx = new HashMap<>();
 
+    /**
+     * When set to true, generated classes will include javadoc comments which
+     * are useful for users.
+     */
+    private final boolean verboseClassComments;
+
     /**
      * Outer key represents the package name. Outer value represents map of all
      * builders in the same package. Inner key represents the schema node name
@@ -128,18 +147,23 @@ public class BindingGeneratorImpl implements BindingGenerator {
     private SchemaContext schemaContext;
 
     /**
-     * Constant with the concrete name of namespace.
+     * Create a new binding generator with verboe comments.
+     *
+     * @deprecated Use {@link #BindingGeneratorImpl(boolean)} instead.
      */
-    private final static String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
+    @Deprecated
+    public BindingGeneratorImpl() {
+        this(true);
+    }
 
     /**
-     * Constant with the concrete name of identifier.
+     * Create a new binding generator.
+     *
+     * @param verboseClassComments generate verbose comments
      */
-    private final static String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
-
-    private final char NEW_LINE = '\n';
-
-    private final char TAB = '\t';
+    public BindingGeneratorImpl(final boolean verboseClassComments) {
+        this.verboseClassComments = verboseClassComments;
+    }
 
     /**
      * Resolves generated types from <code>context</code> schema nodes of all
@@ -284,7 +308,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
             return null;
         }
         final String packageName = packageNameForGeneratedType(basePackageName, node.getPath());
-        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf);
+        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf, module);
         genType.addComment(node.getDescription());
         genType.setDescription(createDescription(node, genType.getFullyQualifiedName()));
         genType.setModuleName(module.getName());
@@ -294,23 +318,61 @@ public class BindingGeneratorImpl implements BindingGenerator {
             genCtx.get(module).addChildNodeType(node, genType);
             groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings());
             processUsesAugments((DataNodeContainer) node, module);
+            if (node.isAddedByUses() || node.isAugmenting())
+                genType.setSuitableForBoxing(false);
         }
         return genType;
     }
 
+    private boolean hasWhenOrMustConstraints(final SchemaNode node) {
+        boolean hasWhenCondition;
+        boolean hasMustConstraints;
+
+        if (node instanceof ContainerSchemaNode) {
+            ContainerSchemaNode contNode = (ContainerSchemaNode)node;
+            hasWhenCondition = contNode.getConstraints().getWhenCondition() != null;
+            hasMustConstraints = !isNullOrEmpty(contNode.getConstraints().getMustConstraints());
+
+            if (hasWhenCondition || hasMustConstraints)
+                return true;
+        }
+        return false;
+    }
+
     private void containerToGenType(final Module module, final String basePackageName,
             final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ContainerSchemaNode node) {
         final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node);
+
         if (genType != null) {
             constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), genType);
             resolveDataSchemaNodes(module, basePackageName, genType, genType, node.getChildNodes());
+
+            final String parentName = parent.getName();
+            final String childOfName = childOf.getName();
+
+            if (parent != null && !parent.getName().contains("Data"))
+                genType.setParentType(parent);
+            genType.setSuitableForBoxing(hasOnlyOneChild(node) && !hasWhenOrMustConstraints(node));
+
+            if (parentName.equals(childOfName))
+                genType.setSuitableForBoxing(false);
         }
     }
 
+    private boolean hasOnlyOneChild(final ContainerSchemaNode contNode) {
+        if (!isNullOrEmpty(contNode.getChildNodes()) && contNode.getChildNodes().size() == 1)
+            return true;
+        return false;
+    }
+
     private void listToGenType(final Module module, final String basePackageName, final GeneratedTypeBuilder parent,
             final GeneratedTypeBuilder childOf, final ListSchemaNode node) {
         final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node);
+
         if (genType != null) {
+            if (!parent.getName().equals(childOf) && !parent.getName().contains("Data")) {
+                genType.setParentType(parent);
+            }
             constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), Types.listTypeFor(genType));
 
             final List<String> listKeys = listKeys(node);
@@ -530,7 +592,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 processUsesAugments(notification, module);
 
                 final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(basePackageName,
-                        notification, BindingTypes.DATA_OBJECT);
+                        notification, BindingTypes.DATA_OBJECT, module);
                 notificationInterface.addImplementsType(NOTIFICATION);
                 genCtx.get(module).addChildNodeType(notification, notificationInterface);
 
@@ -617,7 +679,6 @@ public class BindingGeneratorImpl implements BindingGenerator {
         newType.setDescription(createDescription(identity, newType.getFullyQualifiedName()));
         newType.setReference(identity.getReference());
         newType.setModuleName(module.getName());
-        SchemaPath path = identity.getPath();
         newType.setSchemaPath(identity.getPath().getPathFromRoot());
 
         final QName qname = identity.getQName();
@@ -679,7 +740,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
      */
     private void groupingToGenType(final String basePackageName, final GroupingDefinition grouping, final Module module) {
         final String packageName = packageNameForGeneratedType(basePackageName, grouping.getPath());
-        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping);
+        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping, module);
         genCtx.get(module).addGroupingType(grouping.getPath(), genType);
         resolveDataSchemaNodes(module, basePackageName, genType, genType, grouping.getChildNodes());
         groupingsToGenTypes(module, grouping.getGroupings());
@@ -833,6 +894,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         if (targetTypeBuilder == null) {
             throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
         }
+        targetTypeBuilder.setSuitableForBoxing(false);
 
         if (!(targetSchemaNode instanceof ChoiceNode)) {
             String packageName = augmentPackageName;
@@ -942,9 +1004,12 @@ public class BindingGeneratorImpl implements BindingGenerator {
         augSchemaNodeToMethods(module, basePackageName, augTypeBuilder, augTypeBuilder, augSchema.getChildNodes());
         augmentBuilders.put(augTypeName, augTypeBuilder);
 
-        genCtx.get(module).addTargetToAugmentation(targetTypeRef, augTypeBuilder);
+        if(!augSchema.getChildNodes().isEmpty()) {
+            genCtx.get(module).addTargetToAugmentation(targetTypeRef, augTypeBuilder);
+            genCtx.get(module).addTypeToAugmentation(augTypeBuilder, augSchema);
+
+        }
         genCtx.get(module).addAugmentType(augTypeBuilder);
-        genCtx.get(module).addTypeToAugmentation(augTypeBuilder, augSchema);
         return augTypeBuilder;
     }
 
@@ -1130,6 +1195,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
             constructGetter(parent, choiceNode.getQName().getLocalName(), choiceNode.getDescription(),
                     choiceTypeBuilder);
             choiceTypeBuilder.addImplementsType(typeForClass(DataContainer.class));
+            choiceTypeBuilder.setParentType(parent);
             genCtx.get(module).addChildNodeType(choiceNode, choiceTypeBuilder);
             generateTypesFromChoiceCases(module, basePackageName, choiceTypeBuilder.toInstance(), choiceNode);
         }
@@ -1176,7 +1242,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         for (ChoiceCaseNode caseNode : caseNodes) {
             if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
                 final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath());
-                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode);
+                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode, module);
                 caseTypeBuilder.addImplementsType(refChoiceType);
                 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
                 genCtx.get(module).addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode);
@@ -1255,7 +1321,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         for (DataSchemaNode caseNode : augmentedNodes) {
             if (caseNode != null) {
                 final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath());
-                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode);
+                final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode, module);
                 caseTypeBuilder.addImplementsType(targetType);
 
                 SchemaNode parent = null;
@@ -1342,7 +1408,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 } else if (typeDef instanceof BitsTypeDefinition) {
                     genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule);
                     if (genTOBuilder != null) {
-                        returnType = new ReferencedTypeImpl(genTOBuilder.getPackageName(), genTOBuilder.getName());
+                        returnType = genTOBuilder.toInstance();
                     }
                 } else {
                     final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
@@ -1365,7 +1431,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 final String nodeParam = node.getNodeParameter();
                 IdentitySchemaNode identity = null;
                 String basePackageName = null;
-                final Iterable<String> splittedElement = Splitter.on(':').split(nodeParam);
+                final Iterable<String> splittedElement = COLON_SPLITTER.split(nodeParam);
                 final Iterator<String> iterator = splittedElement.iterator();
                 final int length = Iterables.size(splittedElement);
                 if (length == 1) {
@@ -1433,13 +1499,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     // GeneratedType for this type definition should be already
                     // created
                     QName qname = typeDef.getQName();
-                    Module unionModule = null;
-                    String prefix = qname.getPrefix();
-                    if (prefix == null || prefix.isEmpty() || prefix.equals(module.getPrefix())) {
-                        unionModule = module;
-                    } else {
-                        unionModule = findModuleFromImports(module.getImports(), qname.getPrefix());
-                    }
+                    Module unionModule = schemaContext.findModuleByNamespaceAndRevision(qname.getNamespace(),
+                            qname.getRevision());
                     final ModuleContext mc = genCtx.get(unionModule);
                     returnType = mc.getTypedefs().get(typeDef.getPath());
                 } else {
@@ -1529,7 +1590,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     }
                 } else if (typeDef instanceof BitsTypeDefinition) {
                     final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node, parentModule);
-                    returnType = new ReferencedTypeImpl(genTOBuilder.getPackageName(), genTOBuilder.getName());
+                    returnType = genTOBuilder.toInstance();
                 } else {
                     final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
@@ -1577,8 +1638,9 @@ public class BindingGeneratorImpl implements BindingGenerator {
         return returnType.toInstance();
     }
 
-    private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode) {
-        return addDefaultInterfaceDefinition(packageName, schemaNode, null);
+    private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
+            final Module module) {
+        return addDefaultInterfaceDefinition(packageName, schemaNode, null, module);
     }
 
     /**
@@ -1605,7 +1667,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
      * @return generated type builder <code>schemaNode</code>
      */
     private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
-            final Type parent) {
+            final Type parent, final Module module) {
         final GeneratedTypeBuilder it = addRawInterfaceDefinition(packageName, schemaNode, "");
         if (parent == null) {
             it.addImplementsType(DATA_OBJECT);
@@ -1617,6 +1679,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         }
 
         if (schemaNode instanceof DataNodeContainer) {
+            groupingsToGenTypes(module, ((DataNodeContainer) schemaNode).getGroupings());
             addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it);
         }
 
@@ -1895,11 +1958,12 @@ public class BindingGeneratorImpl implements BindingGenerator {
             genTOBuilders.addAll(types);
 
             GeneratedTOBuilder resultTOBuilder = null;
-            if (!types.isEmpty()) {
-                resultTOBuilder = types.remove(0);
-                for (GeneratedTOBuilder genTOBuilder : types) {
-                    resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
-                }
+            if (types.isEmpty()) {
+                throw new IllegalStateException("No GeneratedTOBuilder objects generated from union " + typeDef);
+            }
+            resultTOBuilder = types.remove(0);
+            for (GeneratedTOBuilder genTOBuilder : types) {
+                resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
             }
 
             final GeneratedPropertyBuilder genPropBuilder = resultTOBuilder.addProperty("value");
@@ -1967,15 +2031,13 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
     private String createDescription(final Set<? extends SchemaNode> schemaNodes, final String moduleName, final String moduleSourcePath) {
         final StringBuilder sb = new StringBuilder();
-        final String yangSnipet = YangTemplate.generateYangSnipet(schemaNodes);
 
         if (!isNullOrEmpty(schemaNodes)) {
             final SchemaNode node = schemaNodes.iterator().next();
 
             if (node instanceof RpcDefinition) {
                 sb.append("Interface for implementing the following YANG RPCs defined in module <b>" + moduleName + "</b>");
-            }
-            else if (node instanceof NotificationDefinition) {
+            } else if (node instanceof NotificationDefinition) {
                 sb.append("Interface for receiving the following YANG notifications defined in module <b>" + moduleName + "</b>");
             }
         }
@@ -1984,72 +2046,77 @@ public class BindingGeneratorImpl implements BindingGenerator {
         sb.append(moduleSourcePath);
         sb.append("</i>):");
         sb.append(NEW_LINE);
-        sb.append("<pre>");
-        sb.append(NEW_LINE);
-        sb.append(yangSnipet);
-        sb.append("</pre>");
-        sb.append(NEW_LINE);
+
+        if (verboseClassComments) {
+            sb.append("<pre>");
+            sb.append(NEW_LINE);
+            sb.append(YangTemplate.generateYangSnipet(schemaNodes));
+            sb.append("</pre>");
+            sb.append(NEW_LINE);
+        }
 
         return sb.toString();
     }
 
     private String createDescription(final SchemaNode schemaNode, final String fullyQualifiedName) {
         final StringBuilder sb = new StringBuilder();
-        final Module module = findParentModule(schemaContext, schemaNode);
-        final String yangSnipet = YangTemplate.generateYangSnipet(schemaNode);
         final String formattedDescription = YangTemplate.formatToParagraph(schemaNode.getDescription(), 0);
-        final StringBuilder linkToBuilderClass = new StringBuilder();
-        final StringBuilder linkToKeyClass = new StringBuilder();
-        final Splitter splitter = Splitter.on("\\.");
-        final String[] namespace = Iterables.toArray(splitter.split(fullyQualifiedName), String.class);
-        String className = namespace[namespace.length - 1];
-
-        if (hasBuilderClass(schemaNode)) {
-            linkToBuilderClass.append(className);
-            linkToBuilderClass.append("Builder");
-
-            if (schemaNode instanceof ListSchemaNode) {
-                linkToKeyClass.append(className);
-                linkToKeyClass.append("Key");
-            }
-        }
 
         if (!isNullOrEmpty(formattedDescription)) {
             sb.append(formattedDescription);
             sb.append(NEW_LINE);
         }
-        sb.append("<p>");
-        sb.append("This class represents the following YANG schema fragment defined in module <b>");
-        sb.append(module.getName());
-        sb.append("</b>");
-        sb.append(NEW_LINE);
-        sb.append("<br />(Source path: <i>");
-        sb.append(module.getModuleSourcePath());
-        sb.append("</i>):");
-        sb.append(NEW_LINE);
-        sb.append("<pre>");
-        sb.append(NEW_LINE);
-        sb.append(yangSnipet);
-        sb.append("</pre>");
-        sb.append(NEW_LINE);
-        sb.append("The schema path to identify an instance is");
-        sb.append(NEW_LINE);
-        sb.append("<i>");
-        sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
-        sb.append("</i>");
-        sb.append(NEW_LINE);
 
-        if (hasBuilderClass(schemaNode)) {
+        if (verboseClassComments) {
+            final Module module = findParentModule(schemaContext, schemaNode);
+            final StringBuilder linkToBuilderClass = new StringBuilder();
+            final StringBuilder linkToKeyClass = new StringBuilder();
+            final String[] namespace = Iterables.toArray(BSDOT_SPLITTER.split(fullyQualifiedName), String.class);
+            String className = namespace[namespace.length - 1];
+
+            if (hasBuilderClass(schemaNode)) {
+                linkToBuilderClass.append(className);
+                linkToBuilderClass.append("Builder");
+
+                if (schemaNode instanceof ListSchemaNode) {
+                    linkToKeyClass.append(className);
+                    linkToKeyClass.append("Key");
+                }
+            }
+
+            sb.append("<p>");
+            sb.append("This class represents the following YANG schema fragment defined in module <b>");
+            sb.append(module.getName());
+            sb.append("</b>");
+            sb.append(NEW_LINE);
+            sb.append("<br />(Source path: <i>");
+            sb.append(module.getModuleSourcePath());
+            sb.append("</i>):");
+            sb.append(NEW_LINE);
+            sb.append("<pre>");
             sb.append(NEW_LINE);
-            sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+            sb.append(YangTemplate.generateYangSnipet(schemaNode));
+            sb.append("</pre>");
             sb.append(NEW_LINE);
-            sb.append("@see ");
-            sb.append(linkToBuilderClass);
-            if (schemaNode instanceof ListSchemaNode) {
+            sb.append("The schema path to identify an instance is");
+            sb.append(NEW_LINE);
+            sb.append("<i>");
+            sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
+            sb.append("</i>");
+            sb.append(NEW_LINE);
+
+            if (hasBuilderClass(schemaNode)) {
+                sb.append(NEW_LINE);
+                sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+                sb.append(NEW_LINE);
                 sb.append("@see ");
-                sb.append(linkToKeyClass);
+                sb.append(linkToBuilderClass);
+                if (schemaNode instanceof ListSchemaNode) {
+                    sb.append("@see ");
+                    sb.append(linkToKeyClass);
+                }
+                sb.append(NEW_LINE);
             }
-            sb.append(NEW_LINE);
         }
 
         return sb.toString();
@@ -2057,8 +2124,9 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
     private boolean hasBuilderClass(final SchemaNode schemaNode) {
         if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode ||
-                schemaNode instanceof RpcDefinition || schemaNode instanceof NotificationDefinition)
+                schemaNode instanceof RpcDefinition || schemaNode instanceof NotificationDefinition) {
             return true;
+        }
         return false;
     }
 
@@ -2068,26 +2136,28 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
     private String createDescription(final Module module) {
         final StringBuilder sb = new StringBuilder();
-        final String yangSnipet = YangTemplate.generateYangSnipet(module);
         final String formattedDescription = YangTemplate.formatToParagraph(module.getDescription(), 0);
 
         if (!isNullOrEmpty(formattedDescription)) {
             sb.append(formattedDescription);
             sb.append(NEW_LINE);
         }
-        sb.append("<p>");
-        sb.append("This class represents the following YANG schema fragment defined in module <b>");
-        sb.append(module.getName());
-        sb.append("</b>");
-        sb.append(NEW_LINE);
-        sb.append("<br />Source path: <i>");
-        sb.append(module.getModuleSourcePath());
-        sb.append("</i>):");
-        sb.append(NEW_LINE);
-        sb.append("<pre>");
-        sb.append(NEW_LINE);
-        sb.append(yangSnipet);
-        sb.append("</pre>");
+
+        if (verboseClassComments) {
+            sb.append("<p>");
+            sb.append("This class represents the following YANG schema fragment defined in module <b>");
+            sb.append(module.getName());
+            sb.append("</b>");
+            sb.append(NEW_LINE);
+            sb.append("<br />Source path: <i>");
+            sb.append(module.getModuleSourcePath());
+            sb.append("</i>):");
+            sb.append(NEW_LINE);
+            sb.append("<pre>");
+            sb.append(NEW_LINE);
+            sb.append(YangTemplate.generateYangSnipet(module));
+            sb.append("</pre>");
+        }
 
         return sb.toString();
     }