Add BindingTypes.augmentation()
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / impl / AbstractTypeGenerator.java
index 047716a10f96d6fef14aff548a322357f7c4a2ed..05a1fedc3d8f2717c5c2694563b762fd361f90b5 100644 (file)
@@ -9,36 +9,60 @@ package org.opendaylight.mdsal.binding.generator.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.computeDefaultSUID;
 import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.packageNameForAugmentedGeneratedType;
 import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.packageNameForGeneratedType;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.BASE_IDENTITY;
 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_OBJECT;
 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_ROOT;
-import static org.opendaylight.mdsal.binding.model.util.BindingTypes.IDENTIFIABLE;
-import static org.opendaylight.mdsal.binding.model.util.BindingTypes.IDENTIFIER;
 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.NOTIFICATION;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.NOTIFICATION_LISTENER;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.QNAME;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.ROUTING_CONTEXT;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.RPC_INPUT;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.RPC_OUTPUT;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.RPC_SERVICE;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.action;
 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.augmentable;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.augmentation;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.childOf;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.choiceIn;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.identifiable;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.identifier;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.instanceNotification;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.keyedListAction;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.keyedListNotification;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.opaqueObject;
+import static org.opendaylight.mdsal.binding.model.util.BindingTypes.rpcResult;
 import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN;
-import static org.opendaylight.mdsal.binding.model.util.Types.FUTURE;
-import static org.opendaylight.mdsal.binding.model.util.Types.VOID;
-import static org.opendaylight.mdsal.binding.model.util.Types.typeForClass;
+import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
+import static org.opendaylight.mdsal.binding.model.util.Types.classType;
+import static org.opendaylight.mdsal.binding.model.util.Types.listTypeFor;
+import static org.opendaylight.mdsal.binding.model.util.Types.listenableFutureTypeFor;
+import static org.opendaylight.mdsal.binding.model.util.Types.primitiveVoidType;
+import static org.opendaylight.mdsal.binding.model.util.Types.wildcardTypeFor;
 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.findParentModule;
 
 import com.google.common.base.Splitter;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
 import org.opendaylight.mdsal.binding.model.api.Constant;
 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
@@ -47,6 +71,7 @@ import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
 import org.opendaylight.mdsal.binding.model.api.Restrictions;
 import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotationTypeBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.EnumBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
@@ -56,22 +81,19 @@ import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilde
 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.TypeMemberBuilder;
 import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil;
-import org.opendaylight.mdsal.binding.model.util.BindingTypes;
 import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl;
 import org.opendaylight.mdsal.binding.model.util.TypeConstants;
-import org.opendaylight.mdsal.binding.model.util.Types;
 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.mdsal.binding.yang.types.AbstractTypeProvider;
 import org.opendaylight.mdsal.binding.yang.types.BaseYangTypes;
 import org.opendaylight.mdsal.binding.yang.types.GroupingDefinitionDependencySort;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.BindingMapping;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
@@ -80,6 +102,7 @@ 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.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -88,16 +111,14 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.api.Status;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
-import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
@@ -110,8 +131,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 abstract class AbstractTypeGenerator {
-    private static final Logger LOG = LoggerFactory.getLogger(BindingGeneratorImpl.class);
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeGenerator.class);
     private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final JavaTypeName DEPRECATED_ANNOTATION = JavaTypeName.create(Deprecated.class);
+    private static final JavaTypeName OVERRIDE_ANNOTATION = JavaTypeName.create(Override.class);
+    private static final Type LIST_STRING_TYPE = listTypeFor(BaseYangTypes.STRING_TYPE);
 
     /**
      * Comparator based on augment target path.
@@ -147,10 +171,9 @@ abstract class AbstractTypeGenerator {
     private final Map<QNameModule, ModuleContext> genCtx = new HashMap<>();
 
     /**
-     * Outer key represents the package name. Outer value represents map of all
-     * builders in the same package. Inner key represents the schema node name
-     * (in JAVA class/interface name format). Inner value represents instance of
-     * builder for schema node specified in key part.
+     * Outer key represents the package name. Outer value represents map of all builders in the same package. Inner key
+     * represents the schema node name (in JAVA class/interface name format). Inner value represents instance of builder
+     * for schema node specified in key part.
      */
     private final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders = new HashMap<>();
 
@@ -160,14 +183,20 @@ abstract class AbstractTypeGenerator {
     private final AbstractTypeProvider typeProvider;
 
     /**
-     * Holds reference to schema context to resolve data of augmented element
-     * when creating augmentation builder
+     * Holds reference to schema context to resolve data of augmented element when creating augmentation builder.
      */
     private final SchemaContext schemaContext;
 
-    AbstractTypeGenerator(final SchemaContext context, final AbstractTypeProvider typeProvider) {
+    /**
+     * Holds renamed elements.
+     */
+    private final Map<SchemaNode, JavaTypeName> renames;
+
+    AbstractTypeGenerator(final SchemaContext context, final AbstractTypeProvider typeProvider,
+            final Map<SchemaNode, JavaTypeName> renames) {
         this.schemaContext = requireNonNull(context);
         this.typeProvider = requireNonNull(typeProvider);
+        this.renames = requireNonNull(renames);
 
         final List<Module> contextModules = ModuleDependencySort.sort(schemaContext.getModules());
         final List<ModuleContext> contexts = new ArrayList<>(contextModules.size());
@@ -211,7 +240,7 @@ abstract class AbstractTypeGenerator {
         if (!module.getChildNodes().isEmpty()) {
             final GeneratedTypeBuilder moduleType = moduleToDataType(context);
             context.addModuleNode(moduleType);
-            resolveDataSchemaNodes(context, moduleType, moduleType, module.getChildNodes());
+            resolveDataSchemaNodes(context, moduleType, moduleType, module.getChildNodes(), false);
         }
         return context;
     }
@@ -241,20 +270,21 @@ abstract class AbstractTypeGenerator {
             if (typedef != null) {
                 final Type type = typeProvider.generatedTypeForExtendedDefinitionType(typedef,  typedef);
                 if (type != null) {
-                    context.addTypedefType(typedef.getPath(), type);
+                    context.addTypedefType(typedef, type);
                     context.addTypeToSchema(type,typedef);
                 }
             }
         }
     }
 
-    private GeneratedTypeBuilder processDataSchemaNode(final ModuleContext context, final GeneratedTypeBuilder childOf,
-            final DataSchemaNode node) {
+    private GeneratedTypeBuilder processDataSchemaNode(final ModuleContext context, final Type baseInterface,
+            final DataSchemaNode node, final boolean inGrouping) {
         if (node.isAugmenting() || node.isAddedByUses()) {
             return null;
         }
-        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, node, childOf);
-        annotateDeprecatedIfNecessary(node.getStatus(), genType);
+        final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, node, baseInterface);
+        defaultImplementedInterace(genType);
+        annotateDeprecatedIfNecessary(node, genType);
 
         final Module module = context.module();
         genType.setModuleName(module.getName());
@@ -263,57 +293,68 @@ abstract class AbstractTypeGenerator {
         if (node instanceof DataNodeContainer) {
             context.addChildNodeType(node, genType);
             groupingsToGenTypes(context, ((DataNodeContainer) node).getGroupings());
-            processUsesAugments((DataNodeContainer) node, context);
+            processUsesAugments((DataNodeContainer) node, context, inGrouping);
         }
         return genType;
     }
 
     private void containerToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final GeneratedTypeBuilder childOf, final ContainerSchemaNode node) {
-        final GeneratedTypeBuilder genType = processDataSchemaNode(context, childOf, node);
+            final Type baseInterface, final ContainerSchemaNode node, final boolean inGrouping) {
+        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping);
         if (genType != null) {
             constructGetter(parent, genType, node);
-            resolveDataSchemaNodes(context, genType, genType, node.getChildNodes());
+            resolveDataSchemaNodes(context, genType, genType, node.getChildNodes(), inGrouping);
+            actionsToGenType(context, genType, node, null, inGrouping);
+            notificationsToGenType(context, genType, node, null, inGrouping);
         }
     }
 
-    private void listToGenType(final ModuleContext context,
-            final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ListSchemaNode node) {
-        final GeneratedTypeBuilder genType = processDataSchemaNode(context, childOf, node);
+    private void listToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
+            final Type baseInterface, final ListSchemaNode node, final boolean inGrouping) {
+        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping);
         if (genType != null) {
-            constructGetter(parent, Types.listTypeFor(genType), node);
-
             final List<String> listKeys = listKeys(node);
-            final GeneratedTOBuilder genTOBuilder = resolveListKeyTOBuilder(context, node);
-            if (genTOBuilder != null) {
-                final Type identifierMarker = Types.parameterizedTypeFor(IDENTIFIER, genType);
-                final Type identifiableMarker = Types.parameterizedTypeFor(IDENTIFIABLE, genTOBuilder);
-                genTOBuilder.addImplementsType(identifierMarker);
-                genType.addImplementsType(identifiableMarker);
+            final GeneratedTOBuilder keyTypeBuilder;
+            if (!listKeys.isEmpty()) {
+                keyTypeBuilder = typeProvider.newGeneratedTOBuilder(JavaTypeName.create(
+                    packageNameForGeneratedType(context.modulePackageName(), node.getPath()),
+                    BindingMapping.getClassName(node.getQName().getLocalName() + "Key")))
+                        .addImplementsType(identifier(genType));
+                genType.addImplementsType(identifiable(keyTypeBuilder));
+            } else {
+                keyTypeBuilder = null;
             }
 
+            final ParameterizedType listType = listTypeFor(genType);
+            constructGetter(parent, listType, node);
+            constructNonnull(parent, listType, node);
+
+            actionsToGenType(context, genType, node, keyTypeBuilder, inGrouping);
+            notificationsToGenType(context, genType, node, keyTypeBuilder, inGrouping);
+
             for (final DataSchemaNode schemaNode : node.getChildNodes()) {
                 if (!schemaNode.isAugmenting()) {
-                    addSchemaNodeToListBuilders(context, schemaNode, genType, genTOBuilder, listKeys);
+                    addSchemaNodeToListBuilders(context, schemaNode, genType, keyTypeBuilder, listKeys, inGrouping);
                 }
             }
 
             // serialVersionUID
-            if (genTOBuilder != null) {
+            if (keyTypeBuilder != null) {
                 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
-                prop.setValue(Long.toString(computeDefaultSUID(genTOBuilder)));
-                genTOBuilder.setSUID(prop);
+                prop.setValue(Long.toString(computeDefaultSUID(keyTypeBuilder)));
+                keyTypeBuilder.setSUID(prop);
             }
 
-            typeBuildersToGenTypes(context, genType, genTOBuilder);
+            typeBuildersToGenTypes(context, genType, keyTypeBuilder);
         }
     }
 
-    private void processUsesAugments(final DataNodeContainer node, final ModuleContext context) {
+    private void processUsesAugments(final DataNodeContainer node, final ModuleContext context,
+            final boolean inGrouping) {
         for (final UsesNode usesNode : node.getUses()) {
             for (final AugmentationSchemaNode augment : usesNode.getAugmentations()) {
-                usesAugmentationToGenTypes(context, augment, usesNode, node);
-                processUsesAugments(augment, context);
+                usesAugmentationToGenTypes(context, augment, usesNode, node, inGrouping);
+                processUsesAugments(augment, context, inGrouping);
             }
         }
     }
@@ -389,6 +430,65 @@ abstract class AbstractTypeGenerator {
         return moduleDataTypeBuilder;
     }
 
+    private <T extends DataNodeContainer & ActionNodeContainer> void actionsToGenType(final ModuleContext context,
+            final Type parent, final T parentSchema, final Type keyType, final boolean inGrouping) {
+        for (final ActionDefinition action : parentSchema.getActions()) {
+            if (action.isAugmenting()) {
+                continue;
+            }
+
+            final GeneratedType input;
+            final GeneratedType output;
+            if (action.isAddedByUses()) {
+                final ActionDefinition orig = findOrigAction(parentSchema, action).get();
+                input = context.getChildNode(orig.getInput().getPath()).build();
+                output = context.getChildNode(orig.getOutput().getPath()).build();
+            } else {
+                input = actionContainer(context, RPC_INPUT, action.getInput(), inGrouping);
+                output = actionContainer(context, RPC_OUTPUT, action.getOutput(), inGrouping);
+            }
+
+            if (!(parentSchema instanceof GroupingDefinition)) {
+                // Parent is a non-grouping, hence we need to establish an Action instance, which can be completely
+                // identified by an InstanceIdentifier. We do not generate Actions for groupings as they are inexact,
+                // and do not capture an actual instantiation.
+                final QName qname = action.getQName();
+                final GeneratedTypeBuilder builder = typeProvider.newGeneratedTypeBuilder(JavaTypeName.create(
+                    packageNameForGeneratedType(context.modulePackageName(), action.getPath()),
+                    BindingMapping.getClassName(qname)));
+                qnameConstant(builder, JavaTypeName.create(context.modulePackageName(),
+                    BindingMapping.MODULE_INFO_CLASS_NAME), qname.getLocalName());
+
+                annotateDeprecatedIfNecessary(action, builder);
+                builder.addImplementsType(keyType != null ? keyedListAction(parent, keyType, input, output)
+                        : action(parent, input, output));
+
+                addCodegenInformation(builder, context.module(), action);
+                context.addChildNodeType(action, builder);
+            }
+        }
+    }
+
+    private Optional<ActionDefinition> findOrigAction(final DataNodeContainer parent, final ActionDefinition action) {
+        for (UsesNode uses : parent.getUses()) {
+            final GroupingDefinition grp = findUsedGrouping(uses);
+            final Optional<ActionDefinition> found = grp.findAction(action.getQName());
+            if (found.isPresent()) {
+                final ActionDefinition result = found.get();
+                return result.isAddedByUses() ? findOrigAction(grp, result) : found;
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    private GeneratedType actionContainer(final ModuleContext context, final Type baseInterface,
+            final ContainerSchemaNode schema, final boolean inGrouping) {
+        final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, schema, inGrouping);
+        resolveDataSchemaNodes(context, genType, genType, schema.getChildNodes(), inGrouping);
+        return genType.build();
+    }
+
     /**
      * Converts all <b>RPCs</b> input and output substatements of the module
      * to the list of <code>Type</code> objects. In addition are to containers
@@ -415,62 +515,41 @@ abstract class AbstractTypeGenerator {
         }
 
         final GeneratedTypeBuilder interfaceBuilder = moduleTypeBuilder(context, "Service");
-        interfaceBuilder.addImplementsType(Types.typeForClass(RpcService.class));
+        interfaceBuilder.addImplementsType(RPC_SERVICE);
 
         addCodegenInformation(interfaceBuilder, module, "RPCs", rpcDefinitions);
 
         for (final RpcDefinition rpc : rpcDefinitions) {
             if (rpc != null) {
                 final String rpcName = BindingMapping.getClassName(rpc.getQName());
-                final String rpcMethodName = BindingMapping.getPropertyName(rpcName);
+                final String rpcMethodName = BindingMapping.getRpcMethodName(rpc.getQName());
                 final MethodSignatureBuilder method = interfaceBuilder.addMethod(rpcMethodName);
-                final ContainerSchemaNode input = rpc.getInput();
-                final ContainerSchemaNode output = rpc.getOutput();
 
                 // Do not refer to annotation class, as it may not be available at runtime
-                method.addAnnotation("javax.annotation", "CheckReturnValue");
-
-                //in case of implicit RPC input (StatementSource.CONTEXT),
-                // stay compatible (no input argument generated)
-                if (input != null && isExplicitStatement(input)) {
-                    final Type inTypeInstance = createRpcContainer(context, rpcName, rpc, input);
-                    method.addParameter(inTypeInstance, "input");
-                }
-
-                final Type outTypeInstance;
-                //in case of implicit RPC output (StatementSource.CONTEXT),
-                //stay compatible (Future<RpcResult<Void>> return type generated)
-                if (output != null && isExplicitStatement(output)) {
-                    outTypeInstance = createRpcContainer(context, rpcName, rpc, output);
-                } else {
-                    outTypeInstance = VOID;
-                }
-
-                final Type rpcRes = Types.parameterizedTypeFor(Types.typeForClass(RpcResult.class), outTypeInstance);
+                method.addAnnotation("edu.umd.cs.findbugs.annotations", "CheckReturnValue");
                 addComment(method, rpc);
-                method.setReturnType(Types.parameterizedTypeFor(FUTURE, rpcRes));
+                method.addParameter(
+                    createRpcContainer(context, rpcName, rpc, verifyNotNull(rpc.getInput()), RPC_INPUT), "input");
+                method.setReturnType(listenableFutureTypeFor(
+                    rpcResult(createRpcContainer(context, rpcName, rpc, verifyNotNull(rpc.getOutput()), RPC_OUTPUT))));
             }
         }
 
         context.addTopLevelNodeType(interfaceBuilder);
     }
 
-    private static boolean isExplicitStatement(final ContainerSchemaNode node) {
-        return node instanceof EffectiveStatement
-                && ((EffectiveStatement<?, ?>) node).getDeclared().getStatementSource() == StatementSource.DECLARATION;
-    }
-
     private Type createRpcContainer(final ModuleContext context, final String rpcName, final RpcDefinition rpc,
-            final ContainerSchemaNode schema) {
-        processUsesAugments(schema, context);
+            final ContainerSchemaNode schema, final Type type) {
+        processUsesAugments(schema, context, false);
         final GeneratedTypeBuilder outType = addRawInterfaceDefinition(
             JavaTypeName.create(context.modulePackageName(), rpcName + BindingMapping.getClassName(schema.getQName())),
             schema);
         addImplementedInterfaceFromUses(schema, outType);
-        outType.addImplementsType(DATA_OBJECT);
+        outType.addImplementsType(type);
         outType.addImplementsType(augmentable(outType));
-        annotateDeprecatedIfNecessary(rpc.getStatus(), outType);
-        resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes());
+        defaultImplementedInterace(outType);
+        annotateDeprecatedIfNecessary(rpc, outType);
+        resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes(), false);
         context.addChildNodeType(schema, outType);
         return outType.build();
     }
@@ -500,25 +579,26 @@ abstract class AbstractTypeGenerator {
         }
 
         final GeneratedTypeBuilder listenerInterface = moduleTypeBuilder(context, "Listener");
-        listenerInterface.addImplementsType(BindingTypes.NOTIFICATION_LISTENER);
+        listenerInterface.addImplementsType(NOTIFICATION_LISTENER);
 
         for (final NotificationDefinition notification : notifications) {
             if (notification != null) {
-                processUsesAugments(notification, context);
+                processUsesAugments(notification, context, false);
 
                 final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(
-                    context.modulePackageName(), notification, null, context);
-                annotateDeprecatedIfNecessary(notification.getStatus(), notificationInterface);
+                    context.modulePackageName(), notification, DATA_OBJECT, context);
+                defaultImplementedInterace(notificationInterface);
+                annotateDeprecatedIfNecessary(notification, notificationInterface);
                 notificationInterface.addImplementsType(NOTIFICATION);
                 context.addChildNodeType(notification, notificationInterface);
 
                 // Notification object
                 resolveDataSchemaNodes(context, notificationInterface, notificationInterface,
-                    notification.getChildNodes());
+                    notification.getChildNodes(), false);
 
                 addComment(listenerInterface.addMethod("on" + notificationInterface.getName())
                     .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification")
-                    .setReturnType(Types.primitiveVoidType()), notification);
+                    .setReturnType(primitiveVoidType()), notification);
             }
         }
 
@@ -526,6 +606,40 @@ abstract class AbstractTypeGenerator {
         context.addTopLevelNodeType(listenerInterface);
     }
 
+    private <T extends DataNodeContainer & NotificationNodeContainer> void notificationsToGenType(
+            final ModuleContext context, final Type parent, final T parentSchema, final Type keyType,
+            final boolean inGrouping) {
+        final Set<NotificationDefinition> notifications = parentSchema.getNotifications();
+        if (notifications.isEmpty()) {
+            return;
+        }
+
+        for (NotificationDefinition notif : notifications) {
+            if (notif.isAugmenting()) {
+                continue;
+            }
+            if (parentSchema instanceof GroupingDefinition) {
+                // Notifications cannot be really established, as they lack instantiation context, which would be
+                // completely described by an InstanceIdentifier -- hence we cannot create a binding class
+                continue;
+            }
+
+            processUsesAugments(notif, context, false);
+
+            final GeneratedTypeBuilder notifInterface = addDefaultInterfaceDefinition(
+                packageNameForGeneratedType(context.modulePackageName(), notif.getPath()), notif, DATA_OBJECT, context);
+            defaultImplementedInterace(notifInterface);
+            annotateDeprecatedIfNecessary(notif, notifInterface);
+
+            notifInterface.addImplementsType(keyType != null ? keyedListNotification(notifInterface, parent, keyType)
+                    : instanceNotification(notifInterface, parent));
+            context.addChildNodeType(notif, notifInterface);
+
+            // Notification object
+            resolveDataSchemaNodes(context, notifInterface, notifInterface, notif.getChildNodes(), false);
+        }
+    }
+
     /**
      * Converts all <b>identities</b> of the module to the list of
      * <code>Type</code> objects.
@@ -567,20 +681,29 @@ abstract class AbstractTypeGenerator {
         if (identity == null) {
             return;
         }
-        final GeneratedTypeBuilder newType = typeProvider.newGeneratedTypeBuilder(JavaTypeName.create(
-            packageNameForGeneratedType(context.modulePackageName(), identity.getPath()),
-            BindingMapping.getClassName(identity.getQName())));
+
+        JavaTypeName name = renames.get(identity);
+        if (name == null) {
+            name = JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), identity.getPath()),
+                BindingMapping.getClassName(identity.getQName()));
+        }
+
+        final GeneratedTypeBuilder newType = typeProvider.newGeneratedTypeBuilder(name);
         final Set<IdentitySchemaNode> baseIdentities = identity.getBaseIdentities();
-        if (baseIdentities.isEmpty()) {
-            final GeneratedTOBuilder gto = typeProvider.newGeneratedTOBuilder(JavaTypeName.create(BaseIdentity.class));
-            newType.addImplementsType(gto.build());
-        } else {
+        if (!baseIdentities.isEmpty()) {
             for (IdentitySchemaNode baseIdentity : baseIdentities) {
-                final QName qname = baseIdentity.getQName();
-                final GeneratedTransferObject gto = typeProvider.newGeneratedTOBuilder(JavaTypeName.create(
-                    BindingMapping.getRootPackageName(qname.getModule()), BindingMapping.getClassName(qname))).build();
+                JavaTypeName base = renames.get(baseIdentity);
+                if (base == null) {
+                    final QName qname = baseIdentity.getQName();
+                    base = JavaTypeName.create(BindingMapping.getRootPackageName(qname.getModule()),
+                        BindingMapping.getClassName(qname));
+                }
+
+                final GeneratedTransferObject gto = typeProvider.newGeneratedTOBuilder(base).build();
                 newType.addImplementsType(gto);
             }
+        } else {
+            newType.addImplementsType(BASE_IDENTITY);
         }
 
         final Module module = context.module();
@@ -588,15 +711,16 @@ abstract class AbstractTypeGenerator {
         newType.setModuleName(module.getName());
         newType.setSchemaPath(identity.getPath());
 
-        qnameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, identity.getQName());
+        qnameConstant(newType, JavaTypeName.create(context.modulePackageName(), BindingMapping.MODULE_INFO_CLASS_NAME),
+            identity.getQName().getLocalName());
 
-        context.addIdentityType(identity.getQName(), newType);
+        context.addIdentityType(identity, newType);
     }
 
-
-    private static Constant qnameConstant(final GeneratedTypeBuilderBase<?> toBuilder, final String constantName,
-            final QName name) {
-        return toBuilder.addConstant(typeForClass(QName.class), constantName, name);
+    private static Constant qnameConstant(final GeneratedTypeBuilderBase<?> toBuilder,
+            final JavaTypeName yangModuleInfo, final String localName) {
+        return toBuilder.addConstant(QNAME, BindingMapping.QNAME_STATIC_FIELD_NAME,
+            new SimpleImmutableEntry<>(yangModuleInfo, localName));
     }
 
     /**
@@ -617,32 +741,26 @@ abstract class AbstractTypeGenerator {
             // Converts individual grouping to GeneratedType. Firstly generated type builder is created and every child
             // node of grouping is resolved to the method.
             final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, grouping);
-            annotateDeprecatedIfNecessary(grouping.getStatus(), genType);
-            context.addGroupingType(grouping.getPath(), genType);
-            resolveDataSchemaNodes(context, genType, genType, grouping.getChildNodes());
+            narrowImplementedInterface(genType);
+            annotateDeprecatedIfNecessary(grouping, genType);
+            context.addGroupingType(grouping, genType);
+            resolveDataSchemaNodes(context, genType, genType, grouping.getChildNodes(), true);
             groupingsToGenTypes(context, grouping.getGroupings());
-            processUsesAugments(grouping, context);
+            processUsesAugments(grouping, context, true);
+            actionsToGenType(context, genType, grouping, null, true);
+            notificationsToGenType(context, genType, grouping, null, true);
         }
     }
 
     /**
-     * Adds enumeration builder created from <code>enumTypeDef</code> to
-     * <code>typeBuilder</code>.
-     *
-     * Each <code>enumTypeDef</code> item is added to builder with its name and
-     * value.
+     * Adds enumeration builder created from <code>enumTypeDef</code> to <code>typeBuilder</code>. Each
+     * <code>enumTypeDef</code> item is added to builder with its name and value.
      *
-     * @param enumTypeDef
-     *            EnumTypeDefinition contains enum data
-     * @param enumName
-     *            string contains name which will be assigned to enumeration
-     *            builder
-     * @param typeBuilder
-     *            GeneratedTypeBuilder to which will be enum builder assigned
-     * @param module
-     *            Module in which type should be generated
-     * @return enumeration builder which contains data from
-     *         <code>enumTypeDef</code>
+     * @param enumTypeDef EnumTypeDefinition contains enum data
+     * @param enumName string contains name which will be assigned to enumeration builder
+     * @param typeBuilder GeneratedTypeBuilder to which will be enum builder assigned
+     * @param module Module in which type should be generated
+     * @return enumeration builder which contains data from <code>enumTypeDef</code>
      */
     private EnumBuilder resolveInnerEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final QName enumName,
             final GeneratedTypeBuilder typeBuilder, final ModuleContext context) {
@@ -659,16 +777,10 @@ abstract class AbstractTypeGenerator {
     /**
      * Generates type builder for <code>module</code>.
      *
-     * @param module
-     *            Module which is source of package name for generated type
-     *            builder
-     * @param postfix
-     *            string which is added to the module class name representation
-     *            as suffix
-     * @return instance of GeneratedTypeBuilder which represents
-     *         <code>module</code>.
-     * @throws IllegalArgumentException
-     *             if <code>module</code> is null
+     * @param module Module which is source of package name for generated type builder
+     * @param postfix string which is added to the module class name representation as suffix
+     * @return instance of GeneratedTypeBuilder which represents <code>module</code>.
+     * @throws IllegalArgumentException if <code>module</code> is null
      */
     private GeneratedTypeBuilder moduleTypeBuilder(final ModuleContext context, final String postfix) {
         final Module module = context.module();
@@ -682,20 +794,13 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Converts <code>augSchema</code> to list of <code>Type</code> which
-     * contains generated type for augmentation. In addition there are also
-     * generated types for all containers, list and choices which are child of
-     * <code>augSchema</code> node or a generated types for cases are added if
-     * augmented node is choice.
+     * Converts <code>augSchema</code> to list of <code>Type</code> which contains generated type for augmentation.
+     * In addition there are also generated types for all containers, list and choices which are child of
+     * <code>augSchema</code> node or a generated types for cases are added if augmented node is choice.
      *
-     * @param augmentPackageName
-     *            string with the name of the package to which the augmentation
-     *            belongs
-     * @param augSchema
-     *            AugmentationSchema which is contains data about augmentation
-     *            (target path, childs...)
-     * @param module
-     *            current module
+     * @param augmentPackageName string with the name of the package to which the augmentation belongs
+     * @param augSchema AugmentationSchema which is contains data about augmentation (target path, childs...)
+     * @param module current module
      * @throws IllegalArgumentException
      *             <ul>
      *             <li>if <code>augmentPackageName</code> equals null</li>
@@ -709,7 +814,7 @@ abstract class AbstractTypeGenerator {
         checkState(augSchema.getTargetPath() != null,
                 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
 
-        processUsesAugments(augSchema, context);
+        processUsesAugments(augSchema, context, false);
         final SchemaPath targetPath = augSchema.getTargetPath();
         SchemaNode targetSchemaNode = null;
 
@@ -737,21 +842,21 @@ abstract class AbstractTypeGenerator {
 
         if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
             final Type targetType = new ReferencedTypeImpl(targetTypeBuilder.getIdentifier());
-            addRawAugmentGenTypeDefinition(context, targetType, augSchema);
+            addRawAugmentGenTypeDefinition(context, targetType, augSchema, false);
 
         } else {
             generateTypesFromAugmentedChoiceCases(context, targetTypeBuilder.build(),
-                    (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), null);
+                    (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), null, false);
         }
     }
 
     private void usesAugmentationToGenTypes(final ModuleContext context, final AugmentationSchemaNode augSchema,
-            final UsesNode usesNode, final DataNodeContainer usesNodeParent) {
+            final UsesNode usesNode, final DataNodeContainer usesNodeParent, final boolean inGrouping) {
         checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
         checkState(augSchema.getTargetPath() != null,
                 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
 
-        processUsesAugments(augSchema, context);
+        processUsesAugments(augSchema, context, inGrouping);
         final SchemaPath targetPath = augSchema.getTargetPath();
         final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(targetPath, usesNode);
         if (targetSchemaNode == null) {
@@ -771,33 +876,35 @@ abstract class AbstractTypeGenerator {
                 addRawAugmentGenTypeDefinition(context,
                     packageNameForAugmentedGeneratedType(context.modulePackageName(),
                         ((SchemaNode) usesNodeParent).getPath()),
-                    targetTypeBuilder.build(), augSchema);
+                    targetTypeBuilder.build(), augSchema, inGrouping);
             } else {
-                addRawAugmentGenTypeDefinition(context, targetTypeBuilder.build(), augSchema);
+                addRawAugmentGenTypeDefinition(context, targetTypeBuilder.build(), augSchema, inGrouping);
             }
         } else {
             generateTypesFromAugmentedChoiceCases(context, targetTypeBuilder.build(),
-                (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), usesNodeParent);
+                (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), usesNodeParent, inGrouping);
+        }
+    }
+
+    private GroupingDefinition findUsedGrouping(final UsesNode uses) {
+        final SchemaNode targetGrouping = findNodeInSchemaContext(schemaContext, uses.getGroupingPath()
+            .getPathFromRoot());
+        if (targetGrouping instanceof GroupingDefinition) {
+            return (GroupingDefinition) targetGrouping;
         }
+
+        throw new IllegalArgumentException("Failed to resolve used grouping for " + uses);
     }
 
     /**
      * Convenient method to find node added by uses statement.
      *
-     * @param targetPath
-     *            node path
-     * @param parentUsesNode
-     *            parent of uses node
+     * @param targetPath node path
+     * @param parentUsesNode parent of uses node
      * @return node from its original location in grouping
      */
     private DataSchemaNode findOriginalTargetFromGrouping(final SchemaPath targetPath, final UsesNode parentUsesNode) {
-        final SchemaNode targetGrouping = findNodeInSchemaContext(schemaContext, parentUsesNode.getGroupingPath()
-                .getPathFromRoot());
-        if (!(targetGrouping instanceof GroupingDefinition)) {
-            throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
-        }
-
-        SchemaNode result = targetGrouping;
+        SchemaNode result = findUsedGrouping(parentUsesNode);
         for (final QName node : targetPath.getPathFromRoot()) {
             if (result instanceof DataNodeContainer) {
                 final QName resultNode = node.withModule(result.getQName().getModule());
@@ -836,34 +943,21 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Returns a generated type builder for an augmentation.
-     *
-     * The name of the type builder is equal to the name of augmented node with
-     * serial number as suffix.
+     * Returns a generated type builder for an augmentation. The name of the type builder is equal to the name
+     * of augmented node with serial number as suffix.
      *
-     * @param context
-     *            current module
-     * @param augmentPackageName
-     *            string with contains the package name to which the augment
-     *            belongs
-     * @param basePackageName
-     *            string with the package name to which the augmented node
-     *            belongs
-     * @param targetTypeRef
-     *            target type
-     * @param augSchema
-     *            augmentation schema which contains data about the child nodes
-     *            and uses of augment
+     * @param context current module
+     * @param augmentPackageName string with contains the package name to which the augment belongs
+     * @param basePackageName string with the package name to which the augmented node belongs
+     * @param targetTypeRef target type
+     * @param augSchema augmentation schema which contains data about the child nodes and uses of augment
      * @return generated type builder for augment
      */
     private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(final ModuleContext context,
             final String augmentPackageName, final Type targetTypeRef,
-            final AugmentationSchemaNode augSchema) {
-        Map<String, GeneratedTypeBuilder> augmentBuilders = genTypeBuilders.get(augmentPackageName);
-        if (augmentBuilders == null) {
-            augmentBuilders = new HashMap<>();
-            genTypeBuilders.put(augmentPackageName, augmentBuilders);
-        }
+            final AugmentationSchemaNode augSchema, final boolean inGrouping) {
+        Map<String, GeneratedTypeBuilder> augmentBuilders =
+            genTypeBuilders.computeIfAbsent(augmentPackageName, k -> new HashMap<>());
         final String augIdentifier = getAugmentIdentifier(augSchema.getUnknownSchemaNodes());
 
         String augTypeName;
@@ -877,31 +971,32 @@ abstract class AbstractTypeGenerator {
             JavaTypeName.create(augmentPackageName, augTypeName));
 
         augTypeBuilder.addImplementsType(DATA_OBJECT);
-        augTypeBuilder.addImplementsType(Types.augmentationTypeFor(targetTypeRef));
-        annotateDeprecatedIfNecessary(augSchema.getStatus(), augTypeBuilder);
+        defaultImplementedInterace(augTypeBuilder);
+
+        augTypeBuilder.addImplementsType(augmentation(targetTypeRef));
+        annotateDeprecatedIfNecessary(augSchema, augTypeBuilder);
         addImplementedInterfaceFromUses(augSchema, augTypeBuilder);
 
-        augSchemaNodeToMethods(context,augTypeBuilder, augTypeBuilder, augSchema.getChildNodes());
+        augSchemaNodeToMethods(context, augTypeBuilder, augSchema.getChildNodes(), inGrouping);
+        actionsToGenType(context, augTypeBuilder, augSchema, null, inGrouping);
+        notificationsToGenType(context, augTypeBuilder, augSchema, null, inGrouping);
+
         augmentBuilders.put(augTypeName, augTypeBuilder);
 
         if (!augSchema.getChildNodes().isEmpty()) {
             context.addTypeToAugmentation(augTypeBuilder, augSchema);
-
         }
+
         context.addAugmentType(augTypeBuilder);
         return augTypeBuilder;
     }
 
     private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(final ModuleContext context, final Type targetTypeRef,
-            final AugmentationSchemaNode augSchema) {
-        return addRawAugmentGenTypeDefinition(context, context.modulePackageName(), targetTypeRef, augSchema);
+            final AugmentationSchemaNode augSchema, final boolean inGrouping) {
+        return addRawAugmentGenTypeDefinition(context, context.modulePackageName(), targetTypeRef, augSchema,
+            inGrouping);
     }
 
-    /**
-     *
-     * @param unknownSchemaNodes
-     * @return nodeParameter of UnknownSchemaNode
-     */
     private static String getAugmentIdentifier(final List<UnknownSchemaNode> unknownSchemaNodes) {
         for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) {
             final QName nodeType = unknownSchemaNode.getNodeType();
@@ -914,15 +1009,11 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Returns first unique name for the augment generated type builder. The
-     * generated type builder name for augment consists from name of augmented
-     * node and serial number of its augmentation.
+     * Returns first unique name for the augment generated type builder. The generated type builder name for augment
+     * consists from name of augmented node and serial number of its augmentation.
      *
-     * @param builders
-     *            map of builders which were created in the package to which the
-     *            augmentation belongs
-     * @param genTypeName
-     *            string with name of augmented node
+     * @param builders map of builders which were created in the package to which the augmentation belongs
+     * @param genTypeName string with name of augmented node
      * @return string with unique name for augmentation builder
      */
     private static String augGenTypeName(final Map<String, GeneratedTypeBuilder> builders, final String genTypeName) {
@@ -936,34 +1027,27 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Adds the methods to <code>typeBuilder</code> which represent subnodes of
-     * node for which <code>typeBuilder</code> was created.
+     * Adds the methods to <code>typeBuilder</code> which represent subnodes of node for which <code>typeBuilder</code>
+     * was created. The subnodes aren't mapped to the methods if they are part of grouping or augment (in this case are
+     * already part of them).
      *
-     * The subnodes aren't mapped to the methods if they are part of grouping or
-     * augment (in this case are already part of them).
-     *
-     * @param module
-     *            current module
-     * @param parent
-     *            generated type builder which represents any node. The subnodes
-     *            of this node are added to the <code>typeBuilder</code> as
-     *            methods. The subnode can be of type leaf, leaf-list, list,
-     *            container, choice.
-     * @param childOf
-     *            parent type
-     * @param schemaNodes
-     *            set of data schema nodes which are the children of the node
-     *            for which <code>typeBuilder</code> was created
-     * @return generated type builder which is the same builder as input
-     *         parameter. The getter methods (representing child nodes) could be
-     *         added to it.
+     * @param module current module
+     * @param parent generated type builder which represents any node. The subnodes of this node are added
+     *               to the <code>typeBuilder</code> as methods. The subnode can be of type leaf, leaf-list, list,
+     *               container, choice.
+     * @param childOf parent type
+     * @param schemaNodes set of data schema nodes which are the children of the node for which
+     *                    <code>typeBuilder</code> was created
+     * @return generated type builder which is the same builder as input parameter. The getter methods (representing
+     *         child nodes) could be added to it.
      */
     private GeneratedTypeBuilder resolveDataSchemaNodes(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final GeneratedTypeBuilder childOf, final Iterable<DataSchemaNode> schemaNodes) {
+            final @Nullable Type childOf, final Iterable<DataSchemaNode> schemaNodes, final boolean inGrouping) {
         if (schemaNodes != null && parent != null) {
+            final Type baseInterface = childOf == null ? DATA_OBJECT : childOf(childOf);
             for (final DataSchemaNode schemaNode : schemaNodes) {
                 if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) {
-                    addSchemaNodeToBuilderAsMethod(context, schemaNode, parent, childOf);
+                    addSchemaNodeToBuilderAsMethod(context, schemaNode, parent, baseInterface, inGrouping);
                 }
             }
         }
@@ -971,32 +1055,27 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Adds the methods to <code>typeBuilder</code> what represents subnodes of
-     * node for which <code>typeBuilder</code> was created.
+     * Adds the methods to <code>typeBuilder</code> what represents subnodes of node for which <code>typeBuilder</code>
+     * was created.
      *
-     * @param module
-     *            current module
-     * @param typeBuilder
-     *            generated type builder which represents any node. The subnodes
-     *            of this node are added to the <code>typeBuilder</code> as
-     *            methods. The subnode can be of type leaf, leaf-list, list,
-     *            container, choice.
-     * @param childOf
-     *            parent type
-     * @param schemaNodes
-     *            set of data schema nodes which are the children of the node
-     *            for which <code>typeBuilder</code> was created
-     * @return generated type builder which is the same object as the input
-     *         parameter <code>typeBuilder</code>. The getter method could be
-     *         added to it.
+     * @param module current module
+     * @param typeBuilder generated type builder which represents any node. The subnodes of this node are added
+     *                    to the <code>typeBuilder</code> as methods. The subnode can be of type leaf, leaf-list, list,
+     *                    container, choice.
+     * @param childOf parent type
+     * @param schemaNodes set of data schema nodes which are the children of the node for which <code>typeBuilder</code>
+     *                    was created
+     * @return generated type builder which is the same object as the input parameter <code>typeBuilder</code>.
+     *         The getter method could be added to it.
      */
     private GeneratedTypeBuilder augSchemaNodeToMethods(final ModuleContext context,
-            final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf,
-            final Iterable<DataSchemaNode> schemaNodes) {
+            final GeneratedTypeBuilder typeBuilder, final Iterable<DataSchemaNode> schemaNodes,
+            final boolean inGrouping) {
         if (schemaNodes != null && typeBuilder != null) {
+            final Type baseInterface = childOf(typeBuilder);
             for (final DataSchemaNode schemaNode : schemaNodes) {
                 if (!schemaNode.isAugmenting()) {
-                    addSchemaNodeToBuilderAsMethod(context, schemaNode, typeBuilder, childOf);
+                    addSchemaNodeToBuilderAsMethod(context, schemaNode, typeBuilder, baseInterface, inGrouping);
                 }
             }
         }
@@ -1004,35 +1083,29 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Adds to <code>typeBuilder</code> a method which is derived from
-     * <code>schemaNode</code>.
+     * Adds to <code>typeBuilder</code> a method which is derived from <code>schemaNode</code>.
      *
-     * @param node
-     *            data schema node which is added to <code>typeBuilder</code> as
-     *            a method
-     * @param typeBuilder
-     *            generated type builder to which is <code>schemaNode</code>
-     *            added as a method.
-     * @param childOf
-     *            parent type
-     * @param module
-     *            current module
+     * @param node data schema node which is added to <code>typeBuilder</code> as a method
+     * @param typeBuilder generated type builder to which is <code>schemaNode</code> added as a method.
+     * @param childOf parent type
+     * @param module current module
      */
     private void addSchemaNodeToBuilderAsMethod(final ModuleContext context, final DataSchemaNode node,
-            final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf) {
+            final GeneratedTypeBuilder typeBuilder, final Type baseInterface, final boolean inGrouping) {
         if (node != null && typeBuilder != null) {
             if (node instanceof LeafSchemaNode) {
-                resolveLeafSchemaNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context);
+                resolveLeafSchemaNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context, inGrouping);
             } else if (node instanceof LeafListSchemaNode) {
-                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) node, context);
+                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) node, context, inGrouping);
             } else if (node instanceof ContainerSchemaNode) {
-                containerToGenType(context, typeBuilder, childOf, (ContainerSchemaNode) node);
+                containerToGenType(context, typeBuilder, baseInterface, (ContainerSchemaNode) node, inGrouping);
             } else if (node instanceof ListSchemaNode) {
-                listToGenType(context, typeBuilder, childOf, (ListSchemaNode) node);
+                listToGenType(context, typeBuilder, baseInterface, (ListSchemaNode) node, inGrouping);
             } else if (node instanceof ChoiceSchemaNode) {
-                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node);
+                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node, inGrouping);
+            } else if (node instanceof AnyXmlSchemaNode || node instanceof AnyDataSchemaNode) {
+                opaqueToGeneratedType(context, typeBuilder, node);
             } else {
-                // TODO: anyxml not yet supported
                 LOG.debug("Unable to add schema node {} as method in {}: unsupported type of node.", node.getClass(),
                         typeBuilder.getFullyQualifiedName());
             }
@@ -1040,22 +1113,15 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * 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.
+     * 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 context
-     *            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.
+     * @param context 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>
@@ -1063,35 +1129,46 @@ abstract class AbstractTypeGenerator {
      *             </ul>
      */
     private void choiceToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent,
-            final ChoiceSchemaNode choiceNode) {
-        checkArgument(choiceNode != null, "Choice Schema Node cannot be NULL.");
-
+            final ChoiceSchemaNode choiceNode, final boolean inGrouping) {
         if (!choiceNode.isAddedByUses()) {
             final GeneratedTypeBuilder choiceTypeBuilder = addRawInterfaceDefinition(
                 JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), choiceNode.getPath()),
                 BindingMapping.getClassName(choiceNode.getQName())), choiceNode);
-            constructGetter(parent, choiceTypeBuilder, choiceNode);
-            choiceTypeBuilder.addImplementsType(typeForClass(DataContainer.class));
-            annotateDeprecatedIfNecessary(choiceNode.getStatus(), choiceTypeBuilder);
+            choiceTypeBuilder.addImplementsType(choiceIn(parent));
+            annotateDeprecatedIfNecessary(choiceNode, choiceTypeBuilder);
             context.addChildNodeType(choiceNode, choiceTypeBuilder);
-            generateTypesFromChoiceCases(context, choiceTypeBuilder.build(), choiceNode);
+
+            final GeneratedType choiceType = choiceTypeBuilder.build();
+            generateTypesFromChoiceCases(context, choiceType, choiceNode, inGrouping);
+
+            constructGetter(parent, choiceType, choiceNode);
+        }
+    }
+
+    private void opaqueToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent,
+            final DataSchemaNode anyNode) {
+        if (!anyNode.isAddedByUses()) {
+            final GeneratedTypeBuilder anyxmlTypeBuilder = addRawInterfaceDefinition(
+                JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), anyNode.getPath()),
+                BindingMapping.getClassName(anyNode.getQName())), anyNode);
+            anyxmlTypeBuilder.addImplementsType(opaqueObject(anyxmlTypeBuilder)).addImplementsType(childOf(parent));
+            defaultImplementedInterace(anyxmlTypeBuilder);
+            annotateDeprecatedIfNecessary(anyNode, anyxmlTypeBuilder);
+            context.addChildNodeType(anyNode, anyxmlTypeBuilder);
+
+            constructGetter(parent, anyxmlTypeBuilder.build(), anyNode);
         }
     }
 
     /**
-     * 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 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
+     * Converts <code>caseNodes</code> set to list of corresponding generated types. For every <i>case</i> which is not
+     * 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 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 context
-     *            current module context
-     * @param refChoiceType
-     *            type which represents superior <i>case</i>
-     * @param choiceNode
-     *            choice case node which is mapped to generated type
+     * @param context current module context
+     * @param refChoiceType type which represents superior <i>case</i>
+     * @param choiceNode choice case node which is mapped to generated type
      * @throws IllegalArgumentException
      *             <ul>
      *             <li>if <code>refChoiceType</code> equals null</li>
@@ -1099,7 +1176,7 @@ abstract class AbstractTypeGenerator {
      *             </ul>
      */
     private void generateTypesFromChoiceCases(final ModuleContext context, final Type refChoiceType,
-            final ChoiceSchemaNode choiceNode) {
+            final ChoiceSchemaNode choiceNode, final boolean inGrouping) {
         checkArgument(refChoiceType != null, "Referenced Choice Type cannot be NULL.");
         checkArgument(choiceNode != null, "ChoiceNode cannot be NULL.");
 
@@ -1107,7 +1184,8 @@ abstract class AbstractTypeGenerator {
             if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
                 final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode);
                 caseTypeBuilder.addImplementsType(refChoiceType);
-                annotateDeprecatedIfNecessary(caseNode.getStatus(), caseTypeBuilder);
+                defaultImplementedInterace(caseTypeBuilder);
+                annotateDeprecatedIfNecessary(caseNode, caseTypeBuilder);
                 context.addCaseType(caseNode.getPath(), caseTypeBuilder);
                 context.addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode);
                 final Iterable<DataSchemaNode> caseChildNodes = caseNode.getChildNodes();
@@ -1137,38 +1215,33 @@ abstract class AbstractTypeGenerator {
                         }
 
                         checkState(parent != null, "Could not find Choice node parent %s", choiceNodeParentPath);
-                        GeneratedTypeBuilder childOfType = findChildNodeByPath(parent.getPath());
+                        Type childOfType = findChildNodeByPath(parent.getPath());
                         if (childOfType == null) {
                             childOfType = findGroupingByPath(parent.getPath());
                         }
-                        resolveDataSchemaNodes(context, caseTypeBuilder, childOfType, caseChildNodes);
+                        resolveDataSchemaNodes(context, caseTypeBuilder, childOfType, caseChildNodes, inGrouping);
                     } else {
-                        resolveDataSchemaNodes(context, caseTypeBuilder, moduleToDataType(context), caseChildNodes);
+                        resolveDataSchemaNodes(context, caseTypeBuilder, moduleToDataType(context), caseChildNodes,
+                            inGrouping);
                     }
-               }
+                }
             }
-            processUsesAugments(caseNode, context);
+            processUsesAugments(caseNode, context, inGrouping);
         }
     }
 
     /**
-     * Generates list of generated types for all the cases of a choice which are
-     * added to the choice through the augment.
+     * Generates list of generated types for all the cases of a choice which are added to the choice through
+     * the augment.
      *
-     * @param module
-     *            current module
-     * @param basePackageName
-     *            string contains name of package to which augment belongs. If
-     *            an augmented choice is from an other package (pcg1) than an
-     *            augmenting choice (pcg2) then case's of the augmenting choice
-     *            will belong to pcg2.
-     * @param targetType
-     *            Type which represents target choice
-     * @param targetNode
-     *            node which represents target choice
-     * @param augmentedNodes
-     *            set of choice case nodes for which is checked if are/aren't
-     *            added to choice through augmentation
+     * @param module current module
+     * @param basePackageName string contains name of package to which augment belongs. If an augmented choice is
+     *                        from an other package (pcg1) than an augmenting choice (pcg2) then case's
+     *                        of the augmenting choice will belong to pcg2.
+     * @param targetType Type which represents target choice
+     * @param targetNode node which represents target choice
+     * @param augmentedNodes set of choice case nodes for which is checked if are/are not added to choice through
+     *                       augmentation
      * @throws IllegalArgumentException
      *             <ul>
      *             <li>if <code>basePackageName</code> is null</li>
@@ -1176,9 +1249,11 @@ abstract class AbstractTypeGenerator {
      *             <li>if <code>augmentedNodes</code> is null</li>
      *             </ul>
      */
+    // FIXME: nullness rules need to untangled in this method
+    @SuppressFBWarnings("NP_NULL_ON_SOME_PATH")
     private void generateTypesFromAugmentedChoiceCases(final ModuleContext context,
             final Type targetType, final ChoiceSchemaNode targetNode, final Iterable<DataSchemaNode> augmentedNodes,
-            final DataNodeContainer usesNodeParent) {
+            final DataNodeContainer usesNodeParent, final boolean inGrouping) {
         checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
         checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL.");
 
@@ -1186,25 +1261,7 @@ abstract class AbstractTypeGenerator {
             if (caseNode != null) {
                 final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode);
                 caseTypeBuilder.addImplementsType(targetType);
-
-                SchemaNode parent;
-                final SchemaPath nodeSp = targetNode.getPath();
-                parent = findDataSchemaNode(schemaContext, nodeSp.getParent());
-
-                GeneratedTypeBuilder childOfType = null;
-                if (parent instanceof Module) {
-                    childOfType = moduleContext(((Module) parent).getQNameModule()).getModuleNode();
-                } else if (parent instanceof CaseSchemaNode) {
-                    childOfType = findCaseByPath(parent.getPath());
-                } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
-                    childOfType = findChildNodeByPath(parent.getPath());
-                } else if (parent instanceof GroupingDefinition) {
-                    childOfType = findGroupingByPath(parent.getPath());
-                }
-
-                if (childOfType == null) {
-                    throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
-                }
+                defaultImplementedInterace(caseTypeBuilder);
 
                 CaseSchemaNode node = null;
                 final String caseLocalName = caseNode.getQName().getLocalName();
@@ -1224,7 +1281,8 @@ abstract class AbstractTypeGenerator {
                 }
                 final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
                 if (childNodes != null) {
-                    resolveDataSchemaNodes(context, caseTypeBuilder, childOfType, childNodes);
+                    resolveDataSchemaNodes(context, caseTypeBuilder, findChildOfType(targetNode), childNodes,
+                        inGrouping);
                 }
                 context.addCaseType(caseNode.getPath(), caseTypeBuilder);
                 context.addChoiceToCaseMapping(targetType, caseTypeBuilder, node);
@@ -1232,6 +1290,30 @@ abstract class AbstractTypeGenerator {
         }
     }
 
+    private GeneratedTypeBuilder findChildOfType(final ChoiceSchemaNode targetNode) {
+        final SchemaPath nodePath = targetNode.getPath();
+        final SchemaPath parentSp = nodePath.getParent();
+        if (parentSp.getParent() == null) {
+            return moduleContext(nodePath.getLastComponent().getModule()).getModuleNode();
+        }
+
+        final SchemaNode parent = findDataSchemaNode(schemaContext, parentSp);
+        GeneratedTypeBuilder childOfType = null;
+        if (parent instanceof CaseSchemaNode) {
+            childOfType = findCaseByPath(parent.getPath());
+        } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
+            childOfType = findChildNodeByPath(parent.getPath());
+        } else if (parent instanceof GroupingDefinition) {
+            childOfType = findGroupingByPath(parent.getPath());
+        }
+
+        if (childOfType == null) {
+            throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
+        }
+
+        return childOfType;
+    }
+
     private static CaseSchemaNode findNamedCase(final ChoiceSchemaNode choice, final String caseName) {
         final List<CaseSchemaNode> cases = choice.findCaseNodes(caseName);
         return cases.isEmpty() ? null : cases.get(0);
@@ -1256,23 +1338,17 @@ abstract class AbstractTypeGenerator {
         if (!patternConstraints.isEmpty()) {
             final StringBuilder field = new StringBuilder().append(TypeConstants.PATTERN_CONSTANT_NAME).append("_")
                 .append(BindingMapping.getPropertyName(leafName));
-            typeBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), field.toString(),
+            typeBuilder.addConstant(LIST_STRING_TYPE, field.toString(),
                 typeProvider.resolveRegExpressions(patternConstraints));
         }
     }
 
     /**
-     * Converts <code>leaf</code> to the getter method which is added to
-     * <code>typeBuilder</code>.
+     * Converts <code>leaf</code> to the getter method which is added to <code>typeBuilder</code>.
      *
-     * @param typeBuilder
-     *            generated type builder to which is added getter method as
-     *            <code>leaf</code> mapping
-     * @param leaf
-     *            leaf schema node which is mapped as getter method which is
-     *            added to <code>typeBuilder</code>
-     * @param module
-     *            Module in which type was defined
+     * @param typeBuilder generated type builder to which is added getter method as <code>leaf</code> mapping
+     * @param leaf leaf schema node which is mapped as getter method which is added to <code>typeBuilder</code>
+     * @param module Module in which type was defined
      * @return boolean value
      *         <ul>
      *         <li>false - if <code>leaf</code> or <code>typeBuilder</code> are
@@ -1281,7 +1357,7 @@ abstract class AbstractTypeGenerator {
      *         </ul>
      */
     private Type resolveLeafSchemaNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf,
-            final ModuleContext context) {
+            final ModuleContext context, final boolean inGrouping) {
         if (leaf == null || typeBuilder == null || leaf.isAddedByUses()) {
             return null;
         }
@@ -1289,10 +1365,10 @@ abstract class AbstractTypeGenerator {
         final Module parentModule = findParentModule(schemaContext, leaf);
         Type returnType = null;
 
-        final TypeDefinition<?> typeDef = CompatUtils.compatLeafType(leaf);
+        final TypeDefinition<?> typeDef = CompatUtils.compatType(leaf);
         if (isInnerType(leaf, typeDef)) {
             if (typeDef instanceof EnumTypeDefinition) {
-                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf);
+                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, inGrouping);
                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
                 final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.getQName(),
                     typeBuilder, context);
@@ -1301,13 +1377,10 @@ abstract class AbstractTypeGenerator {
                 }
                 typeProvider.putReferencedType(leaf.getPath(), returnType);
             } else if (typeDef instanceof UnionTypeDefinition) {
-                GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((UnionTypeDefinition) typeDef, typeBuilder, leaf,
-                    parentModule);
-                if (genTOBuilder != null) {
-                    returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule);
-                    // Store the inner type within the union so that we can find the reference for it
-                    context.addInnerTypedefType(typeDef.getPath(), returnType);
-                }
+                final UnionTypeDefinition unionDef = (UnionTypeDefinition)typeDef;
+                returnType = addTOToTypeBuilder(unionDef, typeBuilder, leaf, parentModule);
+                // Store the inner type within the union so that we can find the reference for it
+                context.addInnerTypedefType(typeDef.getPath(), returnType);
             } else if (typeDef instanceof BitsTypeDefinition) {
                 GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((BitsTypeDefinition) typeDef, typeBuilder, leaf,
                     parentModule);
@@ -1321,12 +1394,12 @@ abstract class AbstractTypeGenerator {
                 // and apply restrictions from leaf type
                 final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
                 returnType = typeProvider.javaTypeForSchemaDefinitionType(getBaseOrDeclaredType(typeDef), leaf,
-                        restrictions);
+                        restrictions, inGrouping);
                 addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
             }
         } else {
             final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
-            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions);
+            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions, inGrouping);
             addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
         }
 
@@ -1381,7 +1454,7 @@ abstract class AbstractTypeGenerator {
                             + nodeParam);
                 }
 
-                final AnnotationTypeBuilder rc = getter.addAnnotation(JavaTypeName.create(RoutingContext.class));
+                final AnnotationTypeBuilder rc = getter.addAnnotation(ROUTING_CONTEXT);
                 final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath());
                 final String genTypeName = BindingMapping.getClassName(identity.getQName().getLocalName());
                 rc.addParameter("value", packageName + "." + genTypeName + ".class");
@@ -1411,7 +1484,7 @@ abstract class AbstractTypeGenerator {
             final boolean isReadOnly) {
         if (leaf != null && toBuilder != null) {
             Type returnType;
-            final TypeDefinition<?> typeDef = CompatUtils.compatLeafType(leaf);
+            final TypeDefinition<?> typeDef = CompatUtils.compatType(leaf);
             if (typeDef instanceof UnionTypeDefinition) {
                 // GeneratedType for this type definition should have be already created
                 final ModuleContext mc = moduleContext(typeDef.getQName().getModule());
@@ -1434,19 +1507,12 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Converts <code>leaf</code> schema node to property of generated TO
-     * builder.
+     * Converts <code>leaf</code> schema node to property of generated TO builder.
      *
-     * @param toBuilder
-     *            generated TO builder to which is <code>leaf</code> added as
-     *            property
-     * @param leaf
-     *            leaf schema node which is added to <code>toBuilder</code> as
-     *            property
-     * @param returnType
-     *            property type
-     * @param isReadOnly
-     *            boolean value which says if leaf property is|isn't read only
+     * @param toBuilder generated TO builder to which is <code>leaf</code> added as property
+     * @param leaf leaf schema node which is added to <code>toBuilder</code> as property
+     * @param returnType property type
+     * @param isReadOnly boolean value which says if leaf property is|isn't read only
      * @return boolean value
      *         <ul>
      *         <li>false - if <code>leaf</code>, <code>toBuilder</code> or leaf
@@ -1472,15 +1538,10 @@ abstract class AbstractTypeGenerator {
     }
 
     /**
-     * Converts <code>node</code> leaf list schema node to getter method of
-     * <code>typeBuilder</code>.
+     * Converts <code>node</code> leaf list schema node to getter method of <code>typeBuilder</code>.
      *
-     * @param typeBuilder
-     *            generated type builder to which is <code>node</code> added as
-     *            getter method
-     * @param node
-     *            leaf list schema node which is added to
-     *            <code>typeBuilder</code> as getter method
+     * @param typeBuilder generated type builder to which is <code>node</code> added as getter method
+     * @param node leaf list schema node which is added to <code>typeBuilder</code> as getter method
      * @param module module
      * @return boolean value
      *         <ul>
@@ -1490,7 +1551,7 @@ abstract class AbstractTypeGenerator {
      *         </ul>
      */
     private boolean resolveLeafListSchemaNode(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node,
-            final ModuleContext context) {
+            final ModuleContext context, final boolean inGrouping) {
         if (node == null || typeBuilder == null || node.isAddedByUses()) {
             return false;
         }
@@ -1503,170 +1564,124 @@ abstract class AbstractTypeGenerator {
         Type returnType = null;
         if (typeDef.getBaseType() == null) {
             if (typeDef instanceof EnumTypeDefinition) {
-                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node);
+                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, inGrouping);
                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
                 final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, nodeName,
                     typeBuilder, context);
                 returnType = new ReferencedTypeImpl(enumBuilder.getIdentifier());
                 typeProvider.putReferencedType(node.getPath(), returnType);
             } else if (typeDef instanceof UnionTypeDefinition) {
-                final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((UnionTypeDefinition)typeDef, typeBuilder,
-                    node, parentModule);
-                if (genTOBuilder != null) {
-                    returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule);
-                }
+                final UnionTypeDefinition unionDef = (UnionTypeDefinition)typeDef;
+                returnType = addTOToTypeBuilder(unionDef, typeBuilder, node, parentModule);
             } else if (typeDef instanceof BitsTypeDefinition) {
                 final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((BitsTypeDefinition)typeDef, typeBuilder,
                     node, parentModule);
                 returnType = genTOBuilder.build();
             } else {
                 final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
-                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
+                returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping);
                 addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
             }
         } else {
             final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
-            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
+            returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping);
             addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
         }
 
-        final ParameterizedType listType = Types.listTypeFor(returnType);
-        constructGetter(typeBuilder, listType, node);
+        constructGetter(typeBuilder, listTypeFor(returnType), node);
         return true;
     }
 
-    private Type createReturnTypeForUnion(final GeneratedTOBuilder genTOBuilder, final TypeDefinition<?> typeDef,
+    private Type createReturnTypeForUnion(final GeneratedTOBuilder genTOBuilder, final UnionTypeDefinition typeDef,
             final GeneratedTypeBuilder typeBuilder, final Module parentModule) {
-        final GeneratedTOBuilder returnType = typeProvider.newGeneratedTOBuilder(genTOBuilder.getIdentifier());
-
-        addCodegenInformation(returnType, parentModule, typeDef);
-        returnType.setSchemaPath(typeDef.getPath());
-        returnType.setModuleName(parentModule.getName());
+        final GeneratedTOBuilder returnTypeBuilder = typeProvider.newGeneratedTOBuilder(genTOBuilder.getIdentifier());
+        returnTypeBuilder.setIsUnion(true);
+        addCodegenInformation(returnTypeBuilder, parentModule, typeDef);
+        returnTypeBuilder.setSchemaPath(typeDef.getPath());
+        returnTypeBuilder.setModuleName(parentModule.getName());
+        final GeneratedTransferObject returnType = returnTypeBuilder.build();
 
         genTOBuilder.setTypedef(true);
         genTOBuilder.setIsUnion(true);
         AbstractTypeProvider.addUnitsToGenTO(genTOBuilder, typeDef.getUnits().orElse(null));
 
-
-
-        final GeneratedTOBuilder unionBuilder = createUnionBuilder(genTOBuilder, typeBuilder);
-
-
-        final MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance");
-        method.setReturnType(returnType);
-        method.addParameter(Types.STRING, "defaultValue");
-        method.setAccessModifier(AccessModifier.PUBLIC);
-        method.setStatic(true);
-
-        final Set<Type> types = typeProvider.getAdditionalTypes().get(parentModule);
-        if (types == null) {
-            typeProvider.getAdditionalTypes().put(parentModule,
-                    Sets.newHashSet(unionBuilder.build()));
-        } else {
-            types.add(unionBuilder.build());
-        }
-        return returnType.build();
+        createUnionBuilder(genTOBuilder, typeBuilder, returnType, parentModule);
+        return returnType;
     }
 
-    private GeneratedTOBuilder createUnionBuilder(final GeneratedTOBuilder genTOBuilder,
-            final GeneratedTypeBuilder typeBuilder) {
+    private void createUnionBuilder(final GeneratedTOBuilder genTOBuilder, final GeneratedTypeBuilder typeBuilder,
+            final GeneratedTransferObject returnType, final Module parentModule) {
         // Append enclosing path hierarchy without dots
         final StringBuilder sb = new StringBuilder();
         genTOBuilder.getIdentifier().localNameComponents().forEach(sb::append);
         final GeneratedTOBuilder unionBuilder = typeProvider.newGeneratedTOBuilder(
             JavaTypeName.create(typeBuilder.getPackageName(), sb.append("Builder").toString()));
         unionBuilder.setIsUnionBuilder(true);
-        return unionBuilder;
+
+        final MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance");
+        method.setReturnType(returnType);
+        method.addParameter(STRING, "defaultValue");
+        method.setAccessModifier(AccessModifier.PUBLIC);
+        method.setStatic(true);
+
+        final GeneratedTransferObject unionBuilderType = unionBuilder.build();
+        typeProvider.getAdditionalTypes().computeIfAbsent(parentModule, key -> new HashSet<>()).add(unionBuilderType);
     }
 
     private GeneratedTypeBuilder addDefaultInterfaceDefinition(final ModuleContext context,
             final SchemaNode schemaNode) {
-        return addDefaultInterfaceDefinition(context, schemaNode, null);
+        return addDefaultInterfaceDefinition(context, schemaNode, DATA_OBJECT);
     }
 
     private GeneratedTypeBuilder addDefaultInterfaceDefinition(final ModuleContext context,
-            final SchemaNode schemaNode, final GeneratedTypeBuilder childOf) {
+            final SchemaNode schemaNode, final Type baseInterface) {
         final String packageName = packageNameForGeneratedType(context.modulePackageName(), schemaNode.getPath());
-        return addDefaultInterfaceDefinition(packageName, schemaNode, childOf, context);
+        return addDefaultInterfaceDefinition(packageName, schemaNode, baseInterface, context);
     }
 
-
     /**
-     * Instantiates generated type builder with <code>packageName</code> and
-     * <code>schemaNode</code>.
-     *
-     * The new builder always implements
-     * {@link org.opendaylight.yangtools.yang.binding.DataObject DataObject}.<br>
-     * If <code>schemaNode</code> is instance of GroupingDefinition it also
-     * implements {@link org.opendaylight.yangtools.yang.binding.Augmentable
-     * Augmentable}.<br>
+     * Instantiates generated type builder with <code>packageName</code> and <code>schemaNode</code>. The new builder
+     * always implements {@link org.opendaylight.yangtools.yang.binding.DataObject DataObject}.<br>
+     * If <code>schemaNode</code> is instance of GroupingDefinition it also implements
+     * {@link org.opendaylight.yangtools.yang.binding.Augmentable Augmentable}.<br>
      * If <code>schemaNode</code> is instance of
-     * {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer
-     * DataNodeContainer} it can also implement nodes which are specified in
-     * <i>uses</i>.
+     * {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer DataNodeContainer} it can also implement nodes
+     * which are specified in <i>uses</i>.
      *
-     * @param packageName
-     *            string with the name of the package to which
-     *            <code>schemaNode</code> belongs.
-     * @param schemaNode
-     *            schema node for which is created generated type builder
-     * @param parent
-     *            parent type (can be null)
+     * @param packageName string with the name of the package to which <code>schemaNode</code> belongs.
+     * @param schemaNode schema node for which is created generated type builder
+     * @param parent parent type (can be null)
      * @return generated type builder <code>schemaNode</code>
      */
     private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
-            final Type parent, final ModuleContext context) {
-        final GeneratedTypeBuilder it = addRawInterfaceDefinition(
-            JavaTypeName.create(packageName, BindingMapping.getClassName(schemaNode.getQName())), schemaNode);
-        if (parent == null) {
-            it.addImplementsType(DATA_OBJECT);
-        } else {
-            it.addImplementsType(BindingTypes.childOf(parent));
+            final Type baseInterface, final ModuleContext context) {
+        JavaTypeName name = renames.get(schemaNode);
+        if (name == null) {
+            name = JavaTypeName.create(packageName, BindingMapping.getClassName(schemaNode.getQName()));
         }
+
+        final GeneratedTypeBuilder it = addRawInterfaceDefinition(name, schemaNode);
+        it.addImplementsType(baseInterface);
         if (!(schemaNode instanceof GroupingDefinition)) {
             it.addImplementsType(augmentable(it));
         }
-
         if (schemaNode instanceof DataNodeContainer) {
-            groupingsToGenTypes(context, ((DataNodeContainer) schemaNode).getGroupings());
-            addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it);
+            final DataNodeContainer containerSchema = (DataNodeContainer) schemaNode;
+            groupingsToGenTypes(context, containerSchema.getGroupings());
+            addImplementedInterfaceFromUses(containerSchema, it);
         }
 
         return it;
     }
 
     /**
-     * Wraps the calling of the same overloaded method.
-     *
-     * @param packageName
-     *            string with the package name to which returning generated type
-     *            builder belongs
-     * @param schemaNode
-     *            schema node which provide data about the schema node name
-     * @return generated type builder for <code>schemaNode</code>
-     */
-    private GeneratedTypeBuilder addRawInterfaceDefinition(final ModuleContext context, final SchemaNode schemaNode,
-            final String prefix) {
-        return addRawInterfaceDefinition(
-            JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), schemaNode.getPath()),
-                prefix + BindingMapping.getClassName(schemaNode.getQName())), schemaNode);
-    }
-
-    /**
-     * Returns reference to generated type builder for specified
-     * <code>schemaNode</code> with <code>packageName</code>.
-     *
-     * Firstly the generated type builder is searched in
-     * {@link BindingGeneratorImpl#genTypeBuilders genTypeBuilders}. If it isn't
-     * found it is created and added to <code>genTypeBuilders</code>.
+     * Returns reference to generated type builder for specified <code>schemaNode</code> with <code>packageName</code>.
+     * Firstly the generated type builder is searched in {@link BindingGeneratorImpl#genTypeBuilders genTypeBuilders}.
+     * If it is not found it is created and added to <code>genTypeBuilders</code>.
      *
-     * @param packageName
-     *            string with the package name to which returning generated type
-     *            builder belongs
-     * @param schemaNode
-     *            schema node which provide data about the schema node name
-     * @param prefix
-     *            return type name prefix
+     * @param packageName string with the package name to which returning generated type builder belongs
+     * @param schemaNode schema node which provide data about the schema node name
+     * @param prefix return type name prefix
      * @return generated type builder for <code>schemaNode</code>
      * @throws IllegalArgumentException
      *             <ul>
@@ -1675,7 +1690,6 @@ abstract class AbstractTypeGenerator {
      *             <li>if QName of schema node is null</li>
      *             <li>if schemaNode name is null</li>
      *             </ul>
-     *
      */
     private GeneratedTypeBuilder addRawInterfaceDefinition(final JavaTypeName identifier, final SchemaNode schemaNode) {
         checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
@@ -1686,7 +1700,8 @@ abstract class AbstractTypeGenerator {
         // FIXME: Validation of name conflict
         final GeneratedTypeBuilder newType = typeProvider.newGeneratedTypeBuilder(identifier);
         final Module module = findParentModule(schemaContext, schemaNode);
-        qnameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, schemaNode.getQName());
+        qnameConstant(newType, JavaTypeName.create(BindingMapping.getRootPackageName(module.getQNameModule()),
+            BindingMapping.MODULE_INFO_CLASS_NAME), schemaNode.getQName().getLocalName());
 
         addCodegenInformation(newType, module, schemaNode);
         newType.setSchemaPath(schemaNode.getPath());
@@ -1710,30 +1725,18 @@ abstract class AbstractTypeGenerator {
     /**
      * Creates the name of the getter method name from <code>localName</code>.
      *
-     * @param localName
-     *            string with the name of the getter method
-     * @param returnType
-     *            return type
-     * @return string with the name of the getter method for
-     *         <code>methodName</code> in JAVA method format
+     * @param localName string with the name of the getter method
+     * @param returnType return type
+     * @return string with the name of the getter method for <code>methodName</code> in JAVA method format
      */
     public static String getterMethodName(final String localName, final Type returnType) {
-        final StringBuilder method = new StringBuilder();
-        if (BOOLEAN.equals(returnType)) {
-            method.append("is");
-        } else {
-            method.append("get");
-        }
-        final String name = BindingMapping.toFirstUpper(BindingMapping.getPropertyName(localName));
-        method.append(name);
-        return method.toString();
+        return BindingMapping.getGetterMethodName(localName, BOOLEAN.equals(returnType));
     }
 
     /**
-     * Created a method signature builder as part of <code>interfaceBuilder</code>.
-     *
-     * The method signature builder is created for the getter method of <code>schemaNodeName</code>.
-     * Also <code>comment</code> and <code>returnType</code> information are added to the builder.
+     * Created a method signature builder as part of <code>interfaceBuilder</code>. The method signature builder is
+     * created for the getter method of <code>schemaNodeName</code>. Also <code>comment</code>
+     * and <code>returnType</code> information are added to the builder.
      *
      * @param interfaceBuilder generated type builder for which the getter method should be created
      * @param returnType type which represents the return type of the getter method
@@ -1748,32 +1751,31 @@ abstract class AbstractTypeGenerator {
             getterMethodName(node.getQName().getLocalName(), returnType));
         getMethod.setReturnType(returnType);
 
-        if (node.getStatus() == Status.DEPRECATED) {
-            getMethod.addAnnotation("java.lang", "Deprecated");
-        }
+        annotateDeprecatedIfNecessary(node, getMethod);
         addComment(getMethod, node);
 
         return getMethod;
     }
 
+    private static void constructNonnull(final GeneratedTypeBuilder interfaceBuilder, final Type returnType,
+            final ListSchemaNode node) {
+        final MethodSignatureBuilder getMethod = interfaceBuilder.addMethod(
+            BindingMapping.getNonnullMethodName(node.getQName().getLocalName()));
+        getMethod.setReturnType(returnType).setDefault(true);
+        annotateDeprecatedIfNecessary(node, getMethod);
+    }
+
     /**
-     * Adds <code>schemaNode</code> to <code>typeBuilder</code> as getter method
-     * or to <code>genTOBuilder</code> as property.
+     * Adds <code>schemaNode</code> to <code>typeBuilder</code> as getter method or to <code>genTOBuilder</code>
+     * as a property.
      *
-     * @param basePackageName
-     *            string contains the module package name
-     * @param schemaNode
-     *            data schema node which should be added as getter method to
-     *            <code>typeBuilder</code> or as a property to
-     *            <code>genTOBuilder</code> if is part of the list key
-     * @param typeBuilder
-     *            generated type builder for the list schema node
-     * @param genTOBuilder
-     *            generated TO builder for the list keys
-     * @param listKeys
-     *            list of string which contains names of the list keys
-     * @param module
-     *            current module
+     * @param basePackageName string contains the module package name
+     * @param schemaNode data schema node which should be added as getter method to <code>typeBuilder</code>
+     *                   or as a property to <code>genTOBuilder</code> if is part of the list key
+     * @param typeBuilder generated type builder for the list schema node
+     * @param genTOBuilder generated TO builder for the list keys
+     * @param listKeys list of string which contains names of the list keys
+     * @param module current module
      * @throws IllegalArgumentException
      *             <ul>
      *             <li>if <code>schemaNode</code> equals null</li>
@@ -1782,14 +1784,14 @@ abstract class AbstractTypeGenerator {
      */
     private void addSchemaNodeToListBuilders(final ModuleContext context, final DataSchemaNode schemaNode,
             final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder,
-            final List<String> listKeys) {
+            final List<String> listKeys, final boolean inGrouping) {
         checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
         checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL.");
 
         if (schemaNode instanceof LeafSchemaNode) {
             final LeafSchemaNode leaf = (LeafSchemaNode) schemaNode;
             final String leafName = leaf.getQName().getLocalName();
-            Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf, context);
+            Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf, context, inGrouping);
             if (listKeys.contains(leafName)) {
                 if (type == null) {
                     resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
@@ -1799,13 +1801,14 @@ abstract class AbstractTypeGenerator {
             }
         } else if (!schemaNode.isAddedByUses()) {
             if (schemaNode instanceof LeafListSchemaNode) {
-                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) schemaNode, context);
+                resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) schemaNode, context, inGrouping);
             } else if (schemaNode instanceof ContainerSchemaNode) {
-                containerToGenType(context, typeBuilder, typeBuilder, (ContainerSchemaNode) schemaNode);
+                containerToGenType(context, typeBuilder, childOf(typeBuilder),
+                    (ContainerSchemaNode) schemaNode, inGrouping);
             } else if (schemaNode instanceof ChoiceSchemaNode) {
-                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) schemaNode);
+                choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) schemaNode, inGrouping);
             } else if (schemaNode instanceof ListSchemaNode) {
-                listToGenType(context, typeBuilder, typeBuilder, (ListSchemaNode) schemaNode);
+                listToGenType(context, typeBuilder, childOf(typeBuilder), (ListSchemaNode) schemaNode, inGrouping);
             }
         }
     }
@@ -1816,163 +1819,108 @@ abstract class AbstractTypeGenerator {
 
         if (genTOBuilder != null) {
             final GeneratedTransferObject genTO = genTOBuilder.build();
-
-            // Fake the 'getKey()' for items, this is equivalent to constructGetter()
-            final MethodSignatureBuilder getMethod = typeBuilder.addMethod(getterMethodName("key", genTO));
-            getMethod.setReturnType(genTO);
-            getMethod.setComment("Returns Primary Key of Yang List Type");
+            // Add Identifiable.getKey() for items
+            typeBuilder.addMethod(BindingMapping.IDENTIFIABLE_KEY_NAME).setReturnType(genTO)
+                .addAnnotation(OVERRIDE_ANNOTATION);
             context.addGeneratedTOBuilder(genTOBuilder);
         }
     }
 
     /**
-     * Selects the names of the list keys from <code>list</code> and returns
-     * them as the list of the strings
+     * Selects the names of the list keys from <code>list</code> and returns them as the list of the strings.
      *
-     * @param list
-     *            of string with names of the list keys
-     * @return list of string which represents names of the list keys. If the
-     *         <code>list</code> contains no keys then the empty list is
-     *         returned.
+     * @param list of string with names of the list keys
+     * @return list of string which represents names of the list keys. If the <code>list</code> contains no keys then
+     *         an empty list is returned.
      */
     private static List<String> listKeys(final ListSchemaNode list) {
-        final List<String> listKeys = new ArrayList<>();
-
         final List<QName> keyDefinition = list.getKeyDefinition();
-        if (keyDefinition != null) {
-            for (final QName keyDef : keyDefinition) {
-                listKeys.add(keyDef.getLocalName());
-            }
-        }
-        return listKeys;
-    }
-
-    /**
-     * Generates for the <code>list</code> which contains any list keys special
-     * generated TO builder.
-     *
-     * @param packageName
-     *            string with package name to which the list belongs
-     * @param list
-     *            list schema node which is source of data about the list name
-     * @return generated TO builder which represents the keys of the
-     *         <code>list</code> or null if <code>list</code> is null or list of
-     *         key definitions is null or empty.
-     */
-    private GeneratedTOBuilder resolveListKeyTOBuilder(final ModuleContext context, final ListSchemaNode list) {
-        if (list.getKeyDefinition() != null && !list.getKeyDefinition().isEmpty()) {
-            return typeProvider.newGeneratedTOBuilder(JavaTypeName.create(
-                packageNameForGeneratedType(context.modulePackageName(), list.getPath()),
-                BindingMapping.getClassName(list.getQName().getLocalName() + "Key")));
+        switch (keyDefinition.size()) {
+            case 0:
+                return Collections.emptyList();
+            case 1:
+                return Collections.singletonList(keyDefinition.get(0).getLocalName());
+            default:
+                final List<String> listKeys = new ArrayList<>(keyDefinition.size());
+                for (final QName keyDef : keyDefinition) {
+                    listKeys.add(keyDef.getLocalName());
+                }
+                return listKeys;
         }
-        return null;
     }
 
     /**
-     * Builds a GeneratedTOBuilder for a UnionType {@link UnionTypeDefinition}.
-     *
-     * If more then one generated TO builder is created for enclosing then all
-     * of the generated TO builders are added to <code>typeBuilder</code> as
+     * Builds a GeneratedTOBuilder for a UnionType {@link UnionTypeDefinition}. If more then one generated TO builder
+     * is created for enclosing then all of the generated TO builders are added to <code>typeBuilder</code> as
      * enclosing transfer objects.
      *
-     * @param typeDef
-     *            type definition which can be of type <code>UnionType</code> or
-     *            <code>BitsTypeDefinition</code>
-     * @param typeBuilder
-     *            generated type builder to which is added generated TO created
-     *            from <code>typeDef</code>
-     * @param leaf
-     *            string with name for generated TO builder
-     * @param parentModule
-     *            parent module
+     * @param typeDef type definition which can be of type <code>UnionType</code> or <code>BitsTypeDefinition</code>
+     * @param typeBuilder generated type builder to which is added generated TO created from <code>typeDef</code>
+     * @param leaf string with name for generated TO builder
+     * @param parentModule parent module
      * @return generated TO builder for <code>typeDef</code>
      */
-    private GeneratedTOBuilder addTOToTypeBuilder(final UnionTypeDefinition typeDef,
+    private Type addTOToTypeBuilder(final UnionTypeDefinition typeDef,
             final GeneratedTypeBuilder typeBuilder, final DataSchemaNode leaf, final Module parentModule) {
         final List<GeneratedTOBuilder> types = typeProvider.provideGeneratedTOBuildersForUnionTypeDef(
-            typeBuilder.getIdentifier().createEnclosed(BindingMapping.getClassName(leaf.getQName())),
-            typeDef, leaf);
+            allocateNestedType(typeBuilder.getIdentifier(), leaf.getQName()), typeDef, leaf);
 
         checkState(!types.isEmpty(), "No GeneratedTOBuilder objects generated from union %s", typeDef);
         final List<GeneratedTOBuilder> genTOBuilders = new ArrayList<>(types);
         final GeneratedTOBuilder resultTOBuilder = types.remove(0);
-        for (final GeneratedTOBuilder genTOBuilder : types) {
-            resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
+        types.forEach(resultTOBuilder::addEnclosingTransferObject);
+        genTOBuilders.forEach(typeBuilder::addEnclosingTransferObject);
+
+        for (GeneratedTOBuilder builder : types) {
+            if (builder.isUnion()) {
+                final GeneratedTransferObject type = builder.build();
+                createUnionBuilder(builder, typeBuilder, type, parentModule);
+            }
         }
 
-        final GeneratedPropertyBuilder genPropBuilder = resultTOBuilder.addProperty("value");
-        genPropBuilder.setReturnType(Types.CHAR_ARRAY);
-        resultTOBuilder.addEqualsIdentity(genPropBuilder);
-        resultTOBuilder.addHashIdentity(genPropBuilder);
-        resultTOBuilder.addToStringProperty(genPropBuilder);
-        processEnclosedTOBuilderes(typeBuilder, genTOBuilders);
-        return resultTOBuilder;
+        return createReturnTypeForUnion(resultTOBuilder, typeDef, typeBuilder, parentModule);
     }
 
     /**
-     * Builds generated TO builders for <code>typeDef</code> of type {@link BitsTypeDefinition} which are
-     * also added to <code>typeBuilder</code> as enclosing transfer object.
-     *
-     * If more then one generated TO builder is created for enclosing then all
-     * of the generated TO builders are added to <code>typeBuilder</code> as
-     * enclosing transfer objects.
+     * Builds generated TO builders for <code>typeDef</code> of type {@link BitsTypeDefinition} which are also added
+     * to <code>typeBuilder</code> as enclosing transfer object. If more then one generated TO builder is created
+     * for enclosing then all of the generated TO builders are added to <code>typeBuilder</code> as enclosing transfer
+     * objects.
      *
-     * @param typeDef
-     *            type definition which can be of type <code>UnionType</code> or
-     *            <code>BitsTypeDefinition</code>
-     * @param typeBuilder
-     *            generated type builder to which is added generated TO created
-     *            from <code>typeDef</code>
-     * @param leaf
-     *            string with name for generated TO builder
-     * @param parentModule
-     *            parent module
+     * @param typeDef type definition which can be of type <code>UnionType</code> or <code>BitsTypeDefinition</code>
+     * @param typeBuilder generated type builder to which is added generated TO created from <code>typeDef</code>
+     * @param leaf string with name for generated TO builder
+     * @param parentModule parent module
      * @return generated TO builder for <code>typeDef</code>
      */
     private GeneratedTOBuilder addTOToTypeBuilder(final BitsTypeDefinition typeDef,
             final GeneratedTypeBuilder typeBuilder, final DataSchemaNode leaf, final Module parentModule) {
         final GeneratedTOBuilder genTOBuilder = typeProvider.provideGeneratedTOBuilderForBitsTypeDefinition(
-            typeBuilder.getIdentifier().createEnclosed(BindingMapping.getClassName(leaf.getQName())),
-            typeDef, parentModule.getName());
+            allocateNestedType(typeBuilder.getIdentifier(), leaf.getQName()), typeDef, parentModule.getName());
         typeBuilder.addEnclosingTransferObject(genTOBuilder);
         return genTOBuilder;
-
-    }
-
-    private static GeneratedTOBuilder processEnclosedTOBuilderes(final GeneratedTypeBuilder typeBuilder,
-            final List<GeneratedTOBuilder> genTOBuilders) {
-        for (final GeneratedTOBuilder genTOBuilder : genTOBuilders) {
-            typeBuilder.addEnclosingTransferObject(genTOBuilder);
-        }
-        return genTOBuilders.get(0);
     }
 
     /**
-     * Adds the implemented types to type builder.
-     *
-     * The method passes through the list of <i>uses</i> in
-     * {@code dataNodeContainer}. For every <i>use</i> is obtained corresponding
-     * generated type from {@link ModuleContext#groupings
-     * allGroupings} which is added as <i>implements type</i> to
-     * <code>builder</code>
+     * Adds the implemented types to type builder. The method passes through the list of <i>uses</i> in
+     * {@code dataNodeContainer}. For every <i>use</i> is obtained corresponding generated type
+     * from {@link ModuleContext#groupings allGroupings} which is added as <i>implements type</i>
+     * to <code>builder</code>
      *
-     * @param dataNodeContainer
-     *            element which contains the list of used YANG groupings
-     * @param builder
-     *            builder to which are added implemented types according to
-     *            <code>dataNodeContainer</code>
+     * @param dataNodeContainer element which contains the list of used YANG groupings
+     * @param builder builder to which are added implemented types according to <code>dataNodeContainer</code>
      * @return generated type builder with all implemented types
      */
     private GeneratedTypeBuilder addImplementedInterfaceFromUses(final DataNodeContainer dataNodeContainer,
             final GeneratedTypeBuilder builder) {
         for (final UsesNode usesNode : dataNodeContainer.getUses()) {
-            final GeneratedType genType = findGroupingByPath(usesNode.getGroupingPath()).build();
+            final GeneratedTypeBuilder genType = findGroupingByPath(usesNode.getGroupingPath());
             if (genType == null) {
                 throw new IllegalStateException("Grouping " + usesNode.getGroupingPath() + "is not resolved for "
                         + builder.getName());
             }
 
-            builder.addImplementsType(genType);
+            builder.addImplementsType(genType.build());
         }
         return builder;
     }
@@ -2007,9 +1955,42 @@ abstract class AbstractTypeGenerator {
         return null;
     }
 
-    private static void annotateDeprecatedIfNecessary(final Status status, final GeneratedTypeBuilder builder) {
-        if (status == Status.DEPRECATED) {
-            builder.addAnnotation("java.lang", "Deprecated");
+    private static JavaTypeName allocateNestedType(final JavaTypeName parent, final QName child) {
+        // Single '$' suffix cannot come from user, this mirrors AbstractGeneratedTypeBuilder.addEnumeration()
+        return parent.createEnclosed(BindingMapping.getClassName(child), "$");
+    }
+
+    private static void annotateDeprecatedIfNecessary(final WithStatus node, final AnnotableTypeBuilder builder) {
+        switch (node.getStatus()) {
+            case DEPRECATED:
+            case OBSOLETE:
+                // FIXME: we really want to use a pre-made annotation
+                builder.addAnnotation(DEPRECATED_ANNOTATION);
+                break;
+            case CURRENT:
+                // No-op
+                break;
+            default:
+                throw new IllegalStateException("Unhandled status in " + node);
         }
     }
+
+    private static void narrowImplementedInterface(final GeneratedTypeBuilder typeBuilder) {
+        defineImplementedInterfaceMethod(typeBuilder, wildcardTypeFor(typeBuilder.getIdentifier()));
+    }
+
+    private static void defaultImplementedInterace(final GeneratedTypeBuilder typeBuilder) {
+        defineImplementedInterfaceMethod(typeBuilder, new ReferencedTypeImpl(typeBuilder.getIdentifier()))
+            .setDefault(true);
+    }
+
+    private static MethodSignatureBuilder defineImplementedInterfaceMethod(final GeneratedTypeBuilder typeBuilder,
+            final Type classType) {
+        final MethodSignatureBuilder ret = typeBuilder
+                .addMethod(BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME)
+                .setAccessModifier(AccessModifier.PUBLIC)
+                .setReturnType(classType(classType));
+        ret.addAnnotation(OVERRIDE_ANNOTATION);
+        return ret;
+    }
 }