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 0e6a8e473cb217764e6d4ef7a712e69cbbc390ee..b98c48c9844f0b5067aeae72cd1894b9efd43b9c 100644 (file)
@@ -25,9 +25,11 @@ import static org.opendaylight.yangtools.binding.generator.util.Types.VOID;
 import static org.opendaylight.yangtools.binding.generator.util.Types.typeForClass;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findNodeInSchemaContext;
-import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findOriginal;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
 
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -36,7 +38,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
 import org.opendaylight.yangtools.binding.generator.util.BindingTypes;
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
@@ -75,6 +76,7 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 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.LeafListSchemaNode;
@@ -101,15 +103,30 @@ import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
 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
@@ -130,14 +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";
+    public BindingGeneratorImpl(final boolean verboseClassComments) {
+        this.verboseClassComments = verboseClassComments;
+    }
 
     /**
      * Resolves generated types from <code>context</code> schema nodes of all
@@ -282,29 +308,71 @@ 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());
+        genType.setReference(node.getReference());
+        genType.setSchemaPath(node.getPath().getPathFromRoot());
         if (node instanceof DataNodeContainer) {
-            genCtx.get(module).addChildNodeType(node.getPath(), genType);
+            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 void containerToGenType(final Module module, final String basePackageName, final GeneratedTypeBuilder parent,
-            final GeneratedTypeBuilder childOf, final ContainerSchemaNode node) {
+    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);
@@ -413,6 +481,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         addImplementedInterfaceFromUses(module, moduleDataTypeBuilder);
         moduleDataTypeBuilder.addImplementsType(DATA_ROOT);
         moduleDataTypeBuilder.addComment(module.getDescription());
+        moduleDataTypeBuilder.setDescription(createDescription(module));
+        moduleDataTypeBuilder.setReference(module.getReference());
         return moduleDataTypeBuilder;
     }
 
@@ -444,6 +514,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
         final GeneratedTypeBuilder interfaceBuilder = moduleTypeBuilder(module, "Service");
         interfaceBuilder.addImplementsType(Types.typeForClass(RpcService.class));
+        interfaceBuilder.setDescription(createDescription(rpcDefinitions, module.getName(), module.getModuleSourcePath()));
+
         for (RpcDefinition rpc : rpcDefinitions) {
             if (rpc != null) {
                 final String rpcName = BindingMapping.getClassName(rpc.getQName());
@@ -459,7 +531,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     inType.addImplementsType(DATA_OBJECT);
                     inType.addImplementsType(augmentable(inType));
                     resolveDataSchemaNodes(module, basePackageName, inType, inType, input.getChildNodes());
-                    genCtx.get(module).addChildNodeType(input.getPath(), inType);
+                    genCtx.get(module).addChildNodeType(input, inType);
                     final GeneratedType inTypeInstance = inType.toInstance();
                     method.addParameter(inTypeInstance, "input");
                 }
@@ -471,7 +543,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     outType.addImplementsType(DATA_OBJECT);
                     outType.addImplementsType(augmentable(outType));
                     resolveDataSchemaNodes(module, basePackageName, outType, outType, output.getChildNodes());
-                    genCtx.get(module).addChildNodeType(output.getPath(), outType);
+                    genCtx.get(module).addChildNodeType(output, outType);
                     outTypeInstance = outType.toInstance();
                 }
 
@@ -513,24 +585,27 @@ public class BindingGeneratorImpl implements BindingGenerator {
         listenerInterface.addImplementsType(BindingTypes.NOTIFICATION_LISTENER);
         final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
 
+
+
         for (NotificationDefinition notification : notifications) {
             if (notification != null) {
                 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.getPath(), notificationInterface);
+                genCtx.get(module).addChildNodeType(notification, notificationInterface);
 
                 // Notification object
                 resolveDataSchemaNodes(module, basePackageName, notificationInterface, notificationInterface,
                         notification.getChildNodes());
 
                 listenerInterface.addMethod("on" + notificationInterface.getName())
-                        .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification")
-                        .setComment(notification.getDescription()).setReturnType(Types.VOID);
+                .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification")
+                .setComment(notification.getDescription()).setReturnType(Types.VOID);
             }
         }
+        listenerInterface.setDescription(createDescription(notifications, module.getName(), module.getModuleSourcePath()));
 
         genCtx.get(module).addTopLevelNodeType(listenerInterface);
     }
@@ -577,14 +652,14 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *            information about base of identity
      *
      */
-    private void identityToGenType(final Module module, final String basePackageName, final IdentitySchemaNode identity,
-            final SchemaContext context) {
+    private void identityToGenType(final Module module, final String basePackageName,
+            final IdentitySchemaNode identity, final SchemaContext context) {
         if (identity == null) {
             return;
         }
         final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath());
         final String genTypeName = BindingMapping.getClassName(identity.getQName());
-        final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(packageName, genTypeName);
+        final GeneratedTOBuilderImpl newType = new GeneratedTOBuilderImpl(packageName, genTypeName);
         final IdentitySchemaNode baseIdentity = identity.getBaseIdentity();
         if (baseIdentity == null) {
             final GeneratedTOBuilderImpl gto = new GeneratedTOBuilderImpl(BaseIdentity.class.getPackage().getName(),
@@ -592,21 +667,28 @@ public class BindingGeneratorImpl implements BindingGenerator {
             newType.setExtendsType(gto.toInstance());
         } else {
             final Module baseIdentityParentModule = SchemaContextUtil.findParentModule(context, baseIdentity);
-            final String returnTypePkgName = BindingMapping.getRootPackageName(baseIdentityParentModule.getQNameModule());
+            final String returnTypePkgName = BindingMapping.getRootPackageName(baseIdentityParentModule
+                    .getQNameModule());
             final String returnTypeName = BindingMapping.getClassName(baseIdentity.getQName());
             final GeneratedTransferObject gto = new GeneratedTOBuilderImpl(returnTypePkgName, returnTypeName)
-                    .toInstance();
+            .toInstance();
             newType.setExtendsType(gto);
         }
         newType.setAbstract(true);
         newType.addComment(identity.getDescription());
+        newType.setDescription(createDescription(identity, newType.getFullyQualifiedName()));
+        newType.setReference(identity.getReference());
+        newType.setModuleName(module.getName());
+        newType.setSchemaPath(identity.getPath().getPathFromRoot());
+
         final QName qname = identity.getQName();
         qnameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, qname);
 
         genCtx.get(module).addIdentityType(identity.getQName(), newType);
     }
 
-    private static Constant qnameConstant(final GeneratedTypeBuilderBase<?> toBuilder, final String constantName, final QName name) {
+    private static Constant qnameConstant(final GeneratedTypeBuilderBase<?> toBuilder, final String constantName,
+            final QName name) {
         StringBuilder sb = new StringBuilder("org.opendaylight.yangtools.yang.common.QName");
         sb.append(".create(");
         sb.append('"');
@@ -636,7 +718,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
     private void groupingsToGenTypes(final Module module, final Collection<GroupingDefinition> groupings) {
         final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
         final List<GroupingDefinition> groupingsSortedByDependencies = new GroupingDefinitionDependencySort()
-                .sort(groupings);
+        .sort(groupings);
         for (GroupingDefinition grouping : groupingsSortedByDependencies) {
             groupingToGenType(basePackageName, grouping, module);
         }
@@ -658,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());
@@ -688,6 +770,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 && (enumTypeDef.getQName().getLocalName() != null)) {
             final String enumerationName = BindingMapping.getClassName(enumName);
             final EnumBuilder enumBuilder = typeBuilder.addEnumeration(enumerationName);
+            enumBuilder.setDescription(enumTypeDef.getDescription());
             enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
             return enumBuilder;
         }
@@ -712,7 +795,13 @@ public class BindingGeneratorImpl implements BindingGenerator {
         checkArgument(module != null, "Module reference cannot be NULL.");
         final String packageName = BindingMapping.getRootPackageName(module.getQNameModule());
         final String moduleName = BindingMapping.getClassName(module.getName()) + postfix;
-        return new GeneratedTypeBuilderImpl(packageName, moduleName);
+
+        final GeneratedTypeBuilderImpl moduleBuilder = new GeneratedTypeBuilderImpl(packageName, moduleName);
+        moduleBuilder.setDescription(createDescription(module));
+        moduleBuilder.setReference(module.getReference());
+        moduleBuilder.setModuleName(moduleName);
+
+        return moduleBuilder;
     }
 
     /**
@@ -739,7 +828,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      * @throws IllegalStateException
      *             if augment target path is null
      */
-    private void augmentationToGenTypes(final String augmentPackageName, final AugmentationSchema augSchema, final Module module) {
+    private void augmentationToGenTypes(final String augmentPackageName, final AugmentationSchema augSchema,
+            final Module module) {
         checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
         checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
         checkState(augSchema.getTargetPath() != null,
@@ -751,9 +841,11 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
         targetSchemaNode = findDataSchemaNode(schemaContext, targetPath);
         if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
-            targetSchemaNode = findOriginal((DataSchemaNode) targetSchemaNode, schemaContext);
+            if (targetSchemaNode instanceof DerivableSchemaNode) {
+                targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
+            }
             if (targetSchemaNode == null) {
-                throw new NullPointerException("Failed to find target node from grouping in augmentation " + augSchema
+                throw new IllegalStateException("Failed to find target node from grouping in augmentation " + augSchema
                         + " in module " + module.getName());
             }
         }
@@ -781,8 +873,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         }
     }
 
-    private void usesAugmentationToGenTypes(final String augmentPackageName, final AugmentationSchema augSchema, final Module module,
-            final UsesNode usesNode, final DataNodeContainer usesNodeParent) {
+    private void usesAugmentationToGenTypes(final String augmentPackageName, final AugmentationSchema augSchema,
+            final Module module, final UsesNode usesNode, final DataNodeContainer usesNodeParent) {
         checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
         checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
         checkState(augSchema.getTargetPath() != null,
@@ -802,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;
@@ -911,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;
     }
 
@@ -981,8 +1077,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *         added to it.
      */
     private GeneratedTypeBuilder resolveDataSchemaNodes(final Module module, final String basePackageName,
-            final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final Set<DataSchemaNode> schemaNodes) {
-        if ((schemaNodes != null) && (parent != null)) {
+            final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final Iterable<DataSchemaNode> schemaNodes) {
+        if (schemaNodes != null && parent != null) {
             for (DataSchemaNode schemaNode : schemaNodes) {
                 if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) {
                     addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, parent, childOf, module);
@@ -1015,7 +1111,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *         added to it.
      */
     private GeneratedTypeBuilder augSchemaNodeToMethods(final Module module, final String basePackageName,
-            final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf, final Set<DataSchemaNode> schemaNodes) {
+            final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf,
+            final Iterable<DataSchemaNode> schemaNodes) {
         if ((schemaNodes != null) && (typeBuilder != null)) {
             for (DataSchemaNode schemaNode : schemaNodes) {
                 if (!schemaNode.isAugmenting()) {
@@ -1087,8 +1184,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *             <li>if <code>choiceNode</code> is null</li>
      *             </ul>
      */
-    private void choiceToGeneratedType(final Module module, final String basePackageName, final GeneratedTypeBuilder parent,
-            final ChoiceNode choiceNode) {
+    private void choiceToGeneratedType(final Module module, final String basePackageName,
+            final GeneratedTypeBuilder parent, final ChoiceNode choiceNode) {
         checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
         checkArgument(choiceNode != null, "Choice Schema Node cannot be NULL.");
 
@@ -1098,7 +1195,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
             constructGetter(parent, choiceNode.getQName().getLocalName(), choiceNode.getDescription(),
                     choiceTypeBuilder);
             choiceTypeBuilder.addImplementsType(typeForClass(DataContainer.class));
-            genCtx.get(module).addChildNodeType(choiceNode.getPath(), choiceTypeBuilder);
+            choiceTypeBuilder.setParentType(parent);
+            genCtx.get(module).addChildNodeType(choiceNode, choiceTypeBuilder);
             generateTypesFromChoiceCases(module, basePackageName, choiceTypeBuilder.toInstance(), choiceNode);
         }
     }
@@ -1130,8 +1228,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *             <li>if <code>caseNodes</code> equals null</li>
      *             </ul>
      */
-    private void generateTypesFromChoiceCases(final Module module, final String basePackageName, final Type refChoiceType,
-            final ChoiceNode choiceNode) {
+    private void generateTypesFromChoiceCases(final Module module, final String basePackageName,
+            final Type refChoiceType, final ChoiceNode choiceNode) {
         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.");
@@ -1144,11 +1242,11 @@ 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);
-                final Set<DataSchemaNode> caseChildNodes = caseNode.getChildNodes();
+                final Iterable<DataSchemaNode> caseChildNodes = caseNode.getChildNodes();
                 if (caseChildNodes != null) {
                     Object parentNode = null;
                     final SchemaPath nodeSp = choiceNode.getPath();
@@ -1161,11 +1259,13 @@ public class BindingGeneratorImpl implements BindingGenerator {
                         SchemaNode targetSchemaNode = findDataSchemaNode(schemaContext, targetPath);
                         if (targetSchemaNode instanceof DataSchemaNode
                                 && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
-                            targetSchemaNode = findOriginal((DataSchemaNode) targetSchemaNode, schemaContext);
+                            if (targetSchemaNode instanceof DerivableSchemaNode) {
+                                targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
+                            }
                             if (targetSchemaNode == null) {
-                                throw new NullPointerException(
+                                throw new IllegalStateException(
                                         "Failed to find target node from grouping for augmentation " + augSchema
-                                                + " in module " + module.getName());
+                                        + " in module " + module.getName());
                             }
                         }
                         parent = targetSchemaNode;
@@ -1174,6 +1274,9 @@ public class BindingGeneratorImpl implements BindingGenerator {
                         parent = findDataSchemaNode(schemaContext, sp.getParent());
                     }
                     GeneratedTypeBuilder childOfType = findChildNodeByPath(parent.getPath());
+                    if (childOfType == null) {
+                        childOfType = findGroupingByPath(parent.getPath());
+                    }
                     resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, caseChildNodes);
                 }
             }
@@ -1209,8 +1312,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *             <li>if <code>augmentedNodes</code> is null</li>
      *             </ul>
      */
-    private void generateTypesFromAugmentedChoiceCases(final Module module, final String basePackageName, final Type targetType,
-            final ChoiceNode targetNode, final Set<DataSchemaNode> augmentedNodes) {
+    private void generateTypesFromAugmentedChoiceCases(final Module module, final String basePackageName,
+            final Type targetType, final ChoiceNode targetNode, final Iterable<DataSchemaNode> augmentedNodes) {
         checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
         checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
         checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL.");
@@ -1218,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;
@@ -1246,7 +1349,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 } else {
                     node = targetNode.getCaseNodeByName(caseNode.getQName().getLocalName());
                 }
-                final Set<DataSchemaNode> childNodes = node.getChildNodes();
+                final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
                 if (childNodes != null) {
                     resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
                 }
@@ -1298,14 +1401,14 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     }
                     ((TypeProviderImpl) typeProvider).putReferencedType(leaf.getPath(), returnType);
                 } else if (typeDef instanceof UnionType) {
-                    genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf);
+                    genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule);
                     if (genTOBuilder != null) {
                         returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule);
                     }
                 } else if (typeDef instanceof BitsTypeDefinition) {
-                    genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf);
+                    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);
@@ -1320,14 +1423,15 @@ public class BindingGeneratorImpl implements BindingGenerator {
         return returnType;
     }
 
-    private void processContextRefExtension(final LeafSchemaNode leaf, final MethodSignatureBuilder getter, final Module module) {
+    private void processContextRefExtension(final LeafSchemaNode leaf, final MethodSignatureBuilder getter,
+            final Module module) {
         for (UnknownSchemaNode node : leaf.getUnknownSchemaNodes()) {
             final QName nodeType = node.getNodeType();
             if ("context-reference".equals(nodeType.getLocalName())) {
                 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) {
@@ -1395,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 {
@@ -1434,8 +1533,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *         <li>true - other cases</li>
      *         </ul>
      */
-    private boolean resolveLeafSchemaNodeAsProperty(final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf, final Type returnType,
-            final boolean isReadOnly) {
+    private boolean resolveLeafSchemaNodeAsProperty(final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf,
+            final Type returnType, final boolean isReadOnly) {
         if (returnType == null) {
             return false;
         }
@@ -1485,13 +1584,13 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     returnType = new ReferencedTypeImpl(enumBuilder.getPackageName(), enumBuilder.getName());
                     ((TypeProviderImpl) typeProvider).putReferencedType(node.getPath(), returnType);
                 } else if (typeDef instanceof UnionType) {
-                    final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node);
+                    final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node, parentModule);
                     if (genTOBuilder != null) {
                         returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule);
                     }
                 } else if (typeDef instanceof BitsTypeDefinition) {
-                    final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node);
-                    returnType = new ReferencedTypeImpl(genTOBuilder.getPackageName(), genTOBuilder.getName());
+                    final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node, parentModule);
+                    returnType = genTOBuilder.toInstance();
                 } else {
                     final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
@@ -1509,6 +1608,12 @@ public class BindingGeneratorImpl implements BindingGenerator {
             final GeneratedTypeBuilder typeBuilder, final Module parentModule) {
         final GeneratedTOBuilderImpl returnType = new GeneratedTOBuilderImpl(genTOBuilder.getPackageName(),
                 genTOBuilder.getName());
+
+        returnType.setDescription(typeDef.getDescription());
+        returnType.setReference(typeDef.getReference());
+        returnType.setSchemaPath(typeDef.getPath().getPathFromRoot());
+        returnType.setModuleName(parentModule.getName());
+
         genTOBuilder.setTypedef(true);
         genTOBuilder.setIsUnion(true);
         ((TypeProviderImpl) typeProvider).addUnitsToGenTO(genTOBuilder, typeDef.getUnits());
@@ -1533,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);
     }
 
     /**
@@ -1560,7 +1666,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *            parent type (can be null)
      * @return generated type builder <code>schemaNode</code>
      */
-    private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode, final Type parent) {
+    private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
+            final Type parent, final Module module) {
         final GeneratedTypeBuilder it = addRawInterfaceDefinition(packageName, schemaNode, "");
         if (parent == null) {
             it.addImplementsType(DATA_OBJECT);
@@ -1572,6 +1679,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         }
 
         if (schemaNode instanceof DataNodeContainer) {
+            groupingsToGenTypes(module, ((DataNodeContainer) schemaNode).getGroupings());
             addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it);
         }
 
@@ -1617,7 +1725,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *             </ul>
      *
      */
-    private GeneratedTypeBuilder addRawInterfaceDefinition(final String packageName, final SchemaNode schemaNode, final String prefix) {
+    private GeneratedTypeBuilder addRawInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
+            final String prefix) {
         checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
         checkArgument(packageName != null, "Package Name for Generated Type cannot be NULL.");
         checkArgument(schemaNode.getQName() != null, "QName for Data Schema Node cannot be NULL.");
@@ -1633,8 +1742,14 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
         // FIXME: Validation of name conflict
         final GeneratedTypeBuilderImpl newType = new GeneratedTypeBuilderImpl(packageName, genTypeName);
+        final Module module = findParentModule(schemaContext, schemaNode);
         qnameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, schemaNode.getQName());
         newType.addComment(schemaNode.getDescription());
+        newType.setDescription(createDescription(schemaNode, newType.getFullyQualifiedName()));
+        newType.setReference(schemaNode.getReference());
+        newType.setSchemaPath(schemaNode.getPath().getPathFromRoot());
+        newType.setModuleName(module.getName());
+
         if (!genTypeBuilders.containsKey(packageName)) {
             final Map<String, GeneratedTypeBuilder> builders = new HashMap<>();
             builders.put(genTypeName, newType);
@@ -1691,8 +1806,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      * @return method signature builder which represents the getter method of
      *         <code>interfaceBuilder</code>
      */
-    private MethodSignatureBuilder constructGetter(final GeneratedTypeBuilder interfaceBuilder, final String schemaNodeName,
-            final String comment, final Type returnType) {
+    private MethodSignatureBuilder constructGetter(final GeneratedTypeBuilder interfaceBuilder,
+            final String schemaNodeName, final String comment, final Type returnType) {
         final MethodSignatureBuilder getMethod = interfaceBuilder
                 .addMethod(getterMethodName(schemaNodeName, returnType));
         getMethod.setComment(comment);
@@ -1725,7 +1840,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      *             </ul>
      */
     private void addSchemaNodeToListBuilders(final String basePackageName, final DataSchemaNode schemaNode,
-            final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder, final List<String> listKeys, final Module module) {
+            final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder, final List<String> listKeys,
+            final Module module) {
         checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
         checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL.");
 
@@ -1753,7 +1869,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
         }
     }
 
-    private void typeBuildersToGenTypes(final Module module, final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder) {
+    private void typeBuildersToGenTypes(final Module module, final GeneratedTypeBuilder typeBuilder,
+            final GeneratedTOBuilder genTOBuilder) {
         checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL.");
 
         if (genTOBuilder != null) {
@@ -1829,8 +1946,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
      * @param leaf
      * @return generated TO builder for <code>typeDef</code>
      */
-    private GeneratedTOBuilder addTOToTypeBuilder(final TypeDefinition<?> typeDef, final GeneratedTypeBuilder typeBuilder,
-            final DataSchemaNode leaf) {
+    private GeneratedTOBuilder addTOToTypeBuilder(final TypeDefinition<?> typeDef,
+            final GeneratedTypeBuilder typeBuilder, final DataSchemaNode leaf, final Module parentModule) {
         final String classNameFromLeaf = BindingMapping.getClassName(leaf.getQName());
         final List<GeneratedTOBuilder> genTOBuilders = new ArrayList<>();
         final String packageName = typeBuilder.getFullyQualifiedName();
@@ -1841,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");
@@ -1856,7 +1974,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
 
         } else if (typeDef instanceof BitsTypeDefinition) {
             genTOBuilders.add((((TypeProviderImpl) typeProvider)).provideGeneratedTOBuilderForBitsTypeDefinition(
-                    packageName, typeDef, classNameFromLeaf));
+                    packageName, typeDef, classNameFromLeaf, parentModule.getName()));
         }
         if (genTOBuilders != null && !genTOBuilders.isEmpty()) {
             for (GeneratedTOBuilder genTOBuilder : genTOBuilders) {
@@ -1893,13 +2011,157 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     throw new IllegalStateException("Grouping " + usesNode.getGroupingPath() + "is not resolved for "
                             + builder.getName());
                 }
+
                 builder.addImplementsType(genType);
-                builder.addComment(genType.getComment());
+                /*
+                builder.addComment(genType.getDescription());
+                builder.setDescription(genType.getDescription());
+                builder.setModuleName(genType.getModuleName());
+                builder.setReference(genType.getReference());
+                builder.setSchemaPath(genType.getSchemaPath());
+                */
             }
         }
         return builder;
     }
 
+    private boolean isNullOrEmpty(final Collection<?> list) {
+        return (list == null || list.isEmpty() ? true : false);
+    }
+
+    private String createDescription(final Set<? extends SchemaNode> schemaNodes, final String moduleName, final String moduleSourcePath) {
+        final StringBuilder sb = new StringBuilder();
+
+        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) {
+                sb.append("Interface for receiving the following YANG notifications defined in module <b>" + moduleName + "</b>");
+            }
+        }
+        sb.append(NEW_LINE);
+        sb.append("<br />(Source path: <i>");
+        sb.append(moduleSourcePath);
+        sb.append("</i>):");
+        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 String formattedDescription = YangTemplate.formatToParagraph(schemaNode.getDescription(), 0);
+
+        if (!isNullOrEmpty(formattedDescription)) {
+            sb.append(formattedDescription);
+            sb.append(NEW_LINE);
+        }
+
+        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(YangTemplate.generateYangSnipet(schemaNode));
+            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)) {
+                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(linkToBuilderClass);
+                if (schemaNode instanceof ListSchemaNode) {
+                    sb.append("@see ");
+                    sb.append(linkToKeyClass);
+                }
+                sb.append(NEW_LINE);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private boolean hasBuilderClass(final SchemaNode schemaNode) {
+        if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode ||
+                schemaNode instanceof RpcDefinition || schemaNode instanceof NotificationDefinition) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isNullOrEmpty(final String string) {
+        return (string == null || string.isEmpty() ? true : false);
+    }
+
+    private String createDescription(final Module module) {
+        final StringBuilder sb = new StringBuilder();
+        final String formattedDescription = YangTemplate.formatToParagraph(module.getDescription(), 0);
+
+        if (!isNullOrEmpty(formattedDescription)) {
+            sb.append(formattedDescription);
+            sb.append(NEW_LINE);
+        }
+
+        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();
+    }
+
     private GeneratedTypeBuilder findChildNodeByPath(final SchemaPath path) {
         for (ModuleContext ctx : genCtx.values()) {
             GeneratedTypeBuilder result = ctx.getChildNode(path);