Merge "Added tests for yang.model.util"
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / LazyGeneratedCodecRegistry.java
index edc186e0ebe0dbe5aabd5305502508f0a2abae6d..f72856e1c11b540c2c6d855ef26b0ab7455a209e 100644 (file)
@@ -7,8 +7,15 @@
  */
 package org.opendaylight.yangtools.sal.binding.generator.impl;
 
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -18,12 +25,10 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
 import org.opendaylight.yangtools.binding.generator.util.Types;
 import org.opendaylight.yangtools.concepts.Delegator;
@@ -33,16 +38,17 @@ import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationExcep
 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
-import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
@@ -68,26 +74,15 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
-
-class LazyGeneratedCodecRegistry implements //
-        CodecRegistry, //
-        SchemaContextListener, //
-        GeneratorListener {
+class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener, GeneratorListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
-    private static final LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
 
     // Concrete class to codecs
     private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
@@ -102,17 +97,17 @@ class LazyGeneratedCodecRegistry implements //
             .synchronizedMap(new WeakHashMap<Class<?>, AugmentableDispatchCodec>());
     private static final Map<Class<?>, AugmentationCodecWrapper<?>> augmentationCodecs = Collections
             .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodecWrapper<?>>());
+
+    private static final Map<Class<?>, LocationAwareDispatchCodec<?>> dispatchCodecs = Collections
+            .synchronizedMap(new WeakHashMap<Class<?>, LocationAwareDispatchCodec<?>>());
+
     private static final Map<Class<?>, QName> identityQNames = Collections
             .synchronizedMap(new WeakHashMap<Class<?>, QName>());
     private static final Map<QName, Type> qnamesToIdentityMap = new ConcurrentHashMap<>();
-    /** Binding type to encountered classes mapping **/
-    @SuppressWarnings("rawtypes")
-    private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
 
-    @SuppressWarnings("rawtypes")
-    private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
+    private static final ConcurrentMap<Type, ChoiceCaseNode> caseTypeToCaseSchema = new ConcurrentHashMap<>();
 
-    private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
+    private static final Map<SchemaPath, Type> pathToType = new ConcurrentHashMap<>();
     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
     private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
     private static final BiMap<Type, AugmentationSchema> typeToAugment = HashBiMap
@@ -124,7 +119,6 @@ class LazyGeneratedCodecRegistry implements //
             .<Type, Type> create());
 
     private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
-    private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
     private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
     private final ClassLoadingStrategy classLoadingStrategy;
     private final AbstractTransformerGenerator generator;
@@ -151,35 +145,36 @@ class LazyGeneratedCodecRegistry implements //
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> object) {
+    public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> augClass) {
         AugmentationCodecWrapper<T> codec = null;
         @SuppressWarnings("rawtypes")
-        AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(object);
+        AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(augClass);
         if (potentialCodec != null) {
             codec = potentialCodec;
         } else {
-            try {
-                lock.waitForSchema(object);
-                Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
-                        .augmentationTransformerFor(object);
-                BindingCodec<Map<QName, Object>, Object> rawCodec = augmentRawCodec.newInstance();
-
-                codec = new AugmentationCodecWrapper<T>(rawCodec, null, object);
-                augmentationCodecs.put(object, codec);
-            } catch (InstantiationException e) {
-                LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e);
-            } catch (IllegalAccessException e) {
-                LOG.debug(
-                        "Run-time consistency issue: constructor {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
-                        object.getSimpleName(), e);
-            }
+            lock.waitForSchema(augClass);
+            Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
+                    .augmentationTransformerFor(augClass);
+
+            BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(augmentRawCodec);
+            codec = new AugmentationCodecWrapper<T>(rawCodec, augClass);
+            augmentationCodecs.put(augClass, codec);
         }
-        Class<? extends Augmentable<?>> objectSupertype = getAugmentableArgumentFrom(object);
-        if (objectSupertype != null) {
-            getAugmentableCodec(objectSupertype).addImplementation(codec);
-        } else {
-            LOG.warn("Could not find augmentation target for augmentation {}", object);
+
+        final Class<? extends Augmentable<?>> objectSupertype;
+        try {
+            objectSupertype = BindingReflections.findAugmentationTarget(augClass);
+        } catch (Exception e) {
+            LOG.warn("Failed to find target for augmentation {}, ignoring it", augClass, e);
+            return codec;
         }
+
+        if (objectSupertype == null) {
+            LOG.warn("Augmentation target for {} not found, ignoring it", augClass);
+            return codec;
+        }
+
+        getAugmentableCodec(objectSupertype).addImplementation(codec);
         return codec;
     }
 
@@ -190,21 +185,14 @@ class LazyGeneratedCodecRegistry implements //
         return getCodecForAugmentation((Class<? extends Augmentation<?>>) cls).getAugmentationQName();
     }
 
-    private static Class<? extends Augmentable<?>> getAugmentableArgumentFrom(
-            final Class<? extends Augmentation<?>> augmentation) {
-        try {
-            Class<? extends Augmentable<?>> ret = BindingReflections.findAugmentationTarget(augmentation);
-            return ret;
-
-        } catch (Exception e) {
-            LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
-            return null;
-        }
-    }
-
     @Override
     public Class<?> getClassForPath(final List<QName> names) {
-        final DataSchemaNode node = getSchemaNode(names);
+        DataSchemaNode node = getSchemaNode(names);
+        Preconditions.checkArgument(node != null, "Path %s points to invalid schema location", names);
+        SchemaNode originalDefinition = SchemaNodeUtils.getRootOriginalIfPossible(node);
+        if (originalDefinition instanceof DataSchemaNode) {
+            node = (DataSchemaNode) originalDefinition;
+        }
         final SchemaPath path = node.getPath();
         final Type t = pathToType.get(path);
 
@@ -216,10 +204,12 @@ class LazyGeneratedCodecRegistry implements //
             Preconditions.checkState(type != null, "Failed to lookup instantiated type for path %s", path);
         }
 
-        @SuppressWarnings("rawtypes")
-        final WeakReference<Class> weakRef = typeToClass.get(type);
-        Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path, type.getFullyQualifiedName());
-        return weakRef.get();
+        try {
+            return classLoadingStrategy.loadClass(type);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException(String.format("Could not find loaded class for path: %s and type: %s",
+                    path, type.getFullyQualifiedName()));
+        }
     }
 
     @Override
@@ -254,43 +244,29 @@ class LazyGeneratedCodecRegistry implements //
     @Override
     @SuppressWarnings("rawtypes")
     public void bindingClassEncountered(final Class cls) {
-
-        ConcreteType typeRef = Types.typeForClass(cls);
-        if (typeToClass.containsKey(typeRef)) {
-            return;
-        }
-        LOG.trace("Binding Class {} encountered.", cls);
-        WeakReference<Class> weakRef = new WeakReference<>(cls);
-        typeToClass.put(typeRef, weakRef);
         if (Augmentation.class.isAssignableFrom(cls)) {
-
+            // Intentionally NOOP
         } else if (DataObject.class.isAssignableFrom(cls)) {
-            @SuppressWarnings({ "unchecked", "unused" })
-            Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
+            getCodecForDataObject((Class<? extends DataObject>) cls);
         }
     }
 
     @Override
     public void onClassProcessed(final Class<?> cls) {
-        ConcreteType typeRef = Types.typeForClass(cls);
-        if (typeToClass.containsKey(typeRef)) {
-            return;
-        }
-        LOG.trace("Binding Class {} encountered.", cls);
-        @SuppressWarnings("rawtypes")
-        WeakReference<Class> weakRef = new WeakReference<Class>(cls);
-        typeToClass.put(typeRef, weakRef);
+
     }
 
     private DataSchemaNode getSchemaNode(final List<QName> path) {
         QName firstNode = path.get(0);
         DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
                 firstNode.getRevision());
+        Preconditions.checkArgument(previous != null, "Failed to find module %s for path %s", firstNode, path);
+
         Iterator<QName> iterator = path.iterator();
         while (iterator.hasNext()) {
             QName arg = iterator.next();
             DataSchemaNode currentNode = previous.getDataChildByName(arg);
-            if (currentNode == null && previous instanceof DataNodeContainer) {
+            if (currentNode == null) {
                 currentNode = searchInChoices(previous, arg);
             }
             if (currentNode instanceof DataNodeContainer) {
@@ -304,8 +280,7 @@ class LazyGeneratedCodecRegistry implements //
     }
 
     private DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
-        Set<DataSchemaNode> children = node.getChildNodes();
-        for (DataSchemaNode child : children) {
+        for (DataSchemaNode child : node.getChildNodes()) {
             if (child instanceof ChoiceNode) {
                 ChoiceNode choiceNode = (ChoiceNode) child;
                 DataSchemaNode potential = searchInCases(choiceNode, arg);
@@ -328,30 +303,55 @@ class LazyGeneratedCodecRegistry implements //
         return null;
     }
 
-    private <T> T newInstanceOf(final Class<?> newType) {
+    private static <T> T newInstanceOf(final Class<?> cls) {
         try {
             @SuppressWarnings("unchecked")
-            T ret = (T) newType.newInstance();
+            T ret = (T) cls.newInstance();
             return ret;
         } catch (InstantiationException e) {
-            throw new IllegalStateException(e);
+            LOG.error("Failed to instantiate codec {}", cls.getSimpleName(), e);
+            throw new IllegalStateException(String.format("Failed to instantiate codec %s", cls), e);
         } catch (IllegalAccessException e) {
-            throw new IllegalStateException(e);
+            LOG.debug(
+                    "Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
+                    cls.getSimpleName(), e);
+            throw new IllegalStateException(String.format("Cannot access contructor of %s", cls), e);
         }
     }
 
+    @SuppressWarnings("unchecked")
     @Override
-    public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class<T> type) {
-        IdentifierCodec<?> obj = identifierCodecs.get(type);
+    public IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class identifiable) {
+
+        Class identifier = ClassLoaderUtils.findFirstGenericArgument(identifiable,
+                org.opendaylight.yangtools.yang.binding.Identifiable.class);
+        IdentifierCodec<?> obj = identifierCodecs.get(identifier);
         if (obj != null) {
             return obj;
         }
+        return createIdentifierCodec(identifier, identifiable);
+    }
+
+    @Override
+    public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> identifier) {
+        @SuppressWarnings("unchecked")
+        IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(identifier);
+        if (obj != null) {
+            return obj;
+        }
+        Class<? extends Identifiable<T>> identifiable = ClassLoaderUtils.findFirstGenericArgument(identifier,
+                Identifier.class);
+        return createIdentifierCodec(identifier, identifiable);
+    }
+
+    private <T extends Identifier<?>> IdentifierCodec<T> createIdentifierCodec(final Class<T> identifier,
+            final Class<? extends Identifiable<T>> identifiable) {
         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
-                .keyTransformerForIdentifiable(type);
+                .keyTransformerForIdentifiable(identifiable);
         BindingCodec<Map<QName, Object>, Object> newInstance;
         newInstance = newInstanceOf(newCodec);
-        IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
-        identifierCodecs.put(type, newWrapper);
+        IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
+        identifierCodecs.put(identifier, newWrapper);
         return newWrapper;
     }
 
@@ -373,22 +373,6 @@ class LazyGeneratedCodecRegistry implements //
         CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
     }
 
-    @Override
-    public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> object) {
-        @SuppressWarnings("unchecked")
-        IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
-        if (obj != null) {
-            return obj;
-        }
-        Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
-                .keyTransformerForIdentifier(object);
-        BindingCodec<Map<QName, Object>, Object> newInstance;
-        newInstance = newInstanceOf(newCodec);
-        IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
-        identifierCodecs.put(object, newWrapper);
-        return newWrapper;
-    }
-
     @SuppressWarnings("rawtypes")
     public ChoiceCaseCodecImpl getCaseCodecFor(final Class caseClass) {
         ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
@@ -396,22 +380,14 @@ class LazyGeneratedCodecRegistry implements //
             return potential;
         }
         ConcreteType typeref = Types.typeForClass(caseClass);
-        ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref);
+        ChoiceCaseNode caseSchema = caseTypeToCaseSchema.get(typeref);
 
-        Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s",
-                caseClass.getName());
-        Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
-                caseClass.getName());
-        Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
+        Preconditions.checkState(caseSchema != null, "Case schema is not available for %s", caseClass.getName());
+        Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseSchema);
         BindingCodec newInstance = newInstanceOf(newCodec);
-        caseCodec.setDelegate(newInstance);
+        @SuppressWarnings("unchecked")
+        ChoiceCaseCodecImpl caseCodec = new ChoiceCaseCodecImpl(caseClass, caseSchema, newInstance);
         caseCodecs.put(caseClass, caseCodec);
-
-        for (Entry<Class<?>, PublicChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
-            if (choice.getKey().isAssignableFrom(caseClass)) {
-                choice.getValue().cases.put(caseClass, caseCodec);
-            }
-        }
         return caseCodec;
     }
 
@@ -422,8 +398,8 @@ class LazyGeneratedCodecRegistry implements //
         for (Map.Entry<Type, AugmentationSchema> entry : bimap.entrySet()) {
             Type key = entry.getKey();
             AugmentationSchema value = entry.getValue();
-            Set<DataSchemaNode> augmentedNodes = value.getChildNodes();
-            if (augmentedNodes != null && !(augmentedNodes.isEmpty())) {
+            Collection<DataSchemaNode> augmentedNodes = value.getChildNodes();
+            if (augmentedNodes != null && !augmentedNodes.isEmpty()) {
                 typeToAugment.put(key, value);
             }
         }
@@ -435,41 +411,39 @@ class LazyGeneratedCodecRegistry implements //
                     identity.getKey());
         }
 
-        synchronized(augmentableToAugmentations) {
+        synchronized (augmentableToAugmentations) {
             augmentableToAugmentations.putAll(context.getAugmentableToAugmentations());
         }
-        synchronized(choiceToCases)  {
+        synchronized (choiceToCases) {
             choiceToCases.putAll(context.getChoiceToCases());
         }
-        captureCases(context.getCases(), schemaContext);
-    }
-
-    private void captureCases(final Map<SchemaPath, GeneratedTypeBuilder> cases, final SchemaContext module) {
-        for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
-            ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
-                    .getValue().getName());
-
-            pathToType.put(caseNode.getKey(), caseNode.getValue());
-
-            ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
-
-            if (node == null) {
-                LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.",
-                        typeref.getFullyQualifiedName(), caseNode.getKey());
-                @SuppressWarnings("rawtypes")
-                ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl();
-                typeToCaseCodecs.putIfAbsent(typeref, value);
-                continue;
-            }
-            @SuppressWarnings("rawtypes")
-            ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
-            typeToCaseCodecs.putIfAbsent(typeref, value);
+        synchronized (caseTypeToCaseSchema) {
+            caseTypeToCaseSchema.putAll(context.getCaseTypeToSchemas());
         }
     }
 
     @Override
     public void onGlobalContextUpdated(final SchemaContext context) {
         currentSchema = context;
+        resetDispatchCodecsAdaptation();
+
+    }
+
+    /**
+     * Resets / clears adaptation for all schema context sensitive codecs in
+     * order for them to adapt to new schema context and maybe newly discovered
+     * augmentations This ensure correct behaviour for augmentations and
+     * augmented cases for preexisting codecs, which augmentations were
+     * introduced at later point in time.
+     *
+     * This also makes removed augmentations unavailable.
+     */
+    private void resetDispatchCodecsAdaptation() {
+        synchronized (dispatchCodecs) {
+            for (LocationAwareDispatchCodec<?> codec : dispatchCodecs.values()) {
+                codec.resetCodec(this);
+            }
+        }
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -480,43 +454,12 @@ class LazyGeneratedCodecRegistry implements //
         Preconditions.checkState(oldCodec == null);
         BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
         PublicChoiceCodecImpl<?> newCodec = new PublicChoiceCodecImpl(delegate);
+        DispatchChoiceCodecImpl dispatchCodec = new DispatchChoiceCodecImpl(choiceClass, this);
         choiceCodecs.put(choiceClass, newCodec);
-        CodecMapping.setClassToCaseMap(choiceCodec, classToCaseRawCodec);
-        CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
-
-        tryToCreateCasesCodecs(schema);
-
-    }
-
-    @Deprecated
-    private void tryToCreateCasesCodecs(final ChoiceNode schema) {
-        for (ChoiceCaseNode choiceCase : schema.getCases()) {
-            ChoiceCaseNode caseNode = choiceCase;
-            if (caseNode.isAddedByUses()) {
-                DataSchemaNode origCaseNode = SchemaContextUtil.findOriginal(caseNode, currentSchema);
-                if (origCaseNode instanceof ChoiceCaseNode) {
-                    caseNode = (ChoiceCaseNode) origCaseNode;
-                }
-            }
-            SchemaPath path = caseNode.getPath();
-
-            GeneratedTypeBuilder type;
-            if (path != null && (type = pathToType.get(path)) != null) {
-                ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
-                @SuppressWarnings("rawtypes")
-                ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref);
-                if (partialCodec.getSchema() == null) {
-                    partialCodec.setSchema(caseNode);
-                }
-                try {
-                    Class<?> caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName());
-                    getCaseCodecFor(caseClass);
-                } catch (ClassNotFoundException e) {
-                    LOG.trace("Could not proactivelly create case codec for {}", type, e);
-                }
-            }
+        synchronized (dispatchCodecs) {
+            dispatchCodecs.put(choiceClass, dispatchCodec);
         }
-
+        CodecMapping.setDispatchCodec(choiceCodec, dispatchCodec);
     }
 
     @Override
@@ -537,19 +480,22 @@ class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    public AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
+    public synchronized AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
         AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass);
         if (ret != null) {
             return ret;
         }
-        ret = new AugmentableDispatchCodec(dataClass);
+        ret = new AugmentableDispatchCodec(dataClass, this);
         augmentableCodecs.put(dataClass, ret);
+        synchronized (dispatchCodecs) {
+            dispatchCodecs.put(dataClass, ret);
+        }
         ret.tryToLoadImplementations();
         return ret;
     }
 
-    private static abstract class IntermediateCodec<T> implements //
-            DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
+    private static abstract class IntermediateCodec<T> implements DomCodec<T>,
+            Delegator<BindingCodec<Map<QName, Object>, Object>> {
 
         private final BindingCodec<Map<QName, Object>, Object> delegate;
 
@@ -570,9 +516,8 @@ class LazyGeneratedCodecRegistry implements //
 
     }
 
-    private static class IdentifierCodecImpl<T extends Identifier<?>> //
-            extends IntermediateCodec<T> //
-            implements IdentifierCodec<T> {
+    private static class IdentifierCodecImpl<T extends Identifier<?>> extends IntermediateCodec<T> implements
+            IdentifierCodec<T> {
 
         public IdentifierCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
             super(delegate);
@@ -600,9 +545,8 @@ class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    private static class DataContainerCodecImpl<T extends DataContainer> //
-            extends IntermediateCodec<T> //
-            implements DataContainerCodec<T> {
+    private static class DataContainerCodecImpl<T extends DataContainer> extends IntermediateCodec<T> implements
+            DataContainerCodec<T> {
 
         public DataContainerCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
             super(delegate);
@@ -638,22 +582,52 @@ class LazyGeneratedCodecRegistry implements //
 
     private interface LocationAwareBindingCodec<P, I> extends BindingCodec<P, I> {
 
-        boolean isApplicable(InstanceIdentifier<?> location);
+        boolean isApplicable(InstanceIdentifier<?> parentPath, CompositeNode data);
 
         public Class<?> getDataType();
 
     }
 
     @SuppressWarnings("rawtypes")
-    private abstract class LocationAwareDispatchCodec<T extends LocationAwareBindingCodec> implements BindingCodec {
+    private static abstract class LocationAwareDispatchCodec<T extends LocationAwareBindingCodec> implements
+            BindingCodec {
 
         private final Map<Class, T> implementations = Collections.synchronizedMap(new WeakHashMap<Class, T>());
         private final Set<InstanceIdentifier<?>> adaptedForPaths = new HashSet<>();
+        private LazyGeneratedCodecRegistry registry;
+
+        protected LocationAwareDispatchCodec(final LazyGeneratedCodecRegistry registry) {
+            this.registry = registry;
+        }
 
         protected Map<Class, T> getImplementations() {
             return implementations;
         }
 
+        /**
+         * Resets codec adaptation based on location and schema context.
+         *
+         * This is required if new cases / augmentations were introduced or
+         * removed and first use of codec is triggered by invocation from DOM to
+         * Java, so the implementations may change and this may require loading
+         * of new codecs and/or removal of existing ones.
+         *
+         */
+        public synchronized void resetCodec(final LazyGeneratedCodecRegistry currentRegistry) {
+            registry = currentRegistry;
+            adaptedForPaths.clear();
+            resetAdaptationImpl();
+        }
+
+        protected void resetAdaptationImpl() {
+            // Intentionally NOOP, subclasses may specify their custom
+            // behaviour.
+        }
+
+        protected final LazyGeneratedCodecRegistry getRegistry() {
+            return registry;
+        }
+
         protected void addImplementation(final T implementation) {
             implementations.put(implementation.getDataType(), implementation);
         }
@@ -663,6 +637,40 @@ class LazyGeneratedCodecRegistry implements //
             throw new UnsupportedOperationException("Invocation of deserialize without Tree location is unsupported");
         }
 
+        @Override
+        public final Object deserialize(final Object parent, final InstanceIdentifier parentPath) {
+            adaptForPath(parentPath);
+            Preconditions.checkArgument(parent instanceof CompositeNode, "node must be of CompositeNode type.");
+            CompositeNode parentData = (CompositeNode) parent;
+            ArrayList<T> applicable = new ArrayList<>(implementations.size());
+
+            /*
+             * Codecs are filtered to only ones, which are applicable in
+             * supplied parent context.
+             */
+            for (T impl : getImplementations().values()) {
+                @SuppressWarnings("unchecked")
+                boolean codecApplicable = impl.isApplicable(parentPath, parentData);
+                if (codecApplicable) {
+                    applicable.add(impl);
+                }
+            }
+            LOG.trace("{}: Deserializing mixins from {}, Schema Location {}, Applicable Codecs: {}, All Codecs: {}",
+                    this, parent, parentPath, applicable, getImplementations().values());
+
+            /*
+             * In case of none is applicable, we return null. Since there is no
+             * mixin which is applicable in this location.
+             */
+            if (applicable.isEmpty()) {
+                return null;
+            }
+            return deserializeImpl(parentData, parentPath, applicable);
+        }
+
+        protected abstract Object deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> parentPath,
+                Iterable<T> applicableCodecs);
+
         @Override
         public Object serialize(final Object input) {
             Preconditions.checkArgument(input instanceof DataContainer);
@@ -682,43 +690,50 @@ class LazyGeneratedCodecRegistry implements //
             return implementation;
         }
 
-        protected final void adaptForPath(final InstanceIdentifier<?> path) {
+        protected final synchronized void adaptForPath(final InstanceIdentifier<?> path) {
             if (adaptedForPaths.contains(path)) {
                 return;
             }
+            LOG.debug("Adapting mixin codec {} for path {}", this, path);
             /**
-             * We search in schema context if the use of this location aware codec (augmentable codec, case codec)
-             * makes sense on provided location (path)
+             * We search in schema context if the use of this location aware
+             * codec (augmentable codec, case codec) makes sense on provided
+             * location (path)
              *
              */
-            Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema, path);
+            Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(
+                    getRegistry().currentSchema, path);
             /**
-             * If context node is present, this codec makes sense on provided location.
+             * If context node is present, this codec makes sense on provided
+             * location.
              *
              */
             if (contextNode.isPresent()) {
                 synchronized (this) {
                     /**
                      *
-                     * We adapt (turn on / off) possible implementations of child codecs (augmentations, cases)
-                     * based on this location.
+                     * We adapt (turn on / off) possible implementations of
+                     * child codecs (augmentations, cases) based on this
+                     * location.
                      *
                      *
                      */
+
                     adaptForPathImpl(path, contextNode.get());
-                    try  {
+                    try {
                         /**
-                         * We trigger serialization of instance identifier, to make sure instance identifier
-                         * codec is aware of combination of this path / augmentation / case
+                         * We trigger serialization of instance identifier, to
+                         * make sure instance identifier codec is aware of
+                         * combination of this path / augmentation / case
                          */
-                        instanceIdentifierCodec.serialize(path);
+                        getRegistry().getInstanceIdentifierCodec().serialize(path);
                     } catch (Exception e) {
-                        LOG.warn("Exception during preparation of instance identifier codec for  path {}.",path,e);
+                        LOG.warn("Exception during preparation of instance identifier codec for  path {}.", path, e);
                     }
                     adaptedForPaths.add(path);
                 }
             } else {
-                LOG.debug("Context node (parent node) not found for {}",path);
+                LOG.debug("Context node (parent node) not found for {}", path);
             }
         }
 
@@ -732,41 +747,17 @@ class LazyGeneratedCodecRegistry implements //
     @SuppressWarnings("rawtypes")
     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
             Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
-        private boolean augmenting;
-        private boolean uses;
-        private BindingCodec delegate;
-
-        private Set<String> validNames;
-        private Set<QName> validQNames;
-        private ChoiceCaseNode schema;
-        private Set<InstanceIdentifier<?>> applicableLocations;
-
-        @Override
-        public boolean isApplicable(final InstanceIdentifier location) {
-            return applicableLocations.contains(location);
-        }
-
-        public void setSchema(final ChoiceCaseNode caseNode) {
+        private final BindingCodec delegate;
+        private final ChoiceCaseNode schema;
+        private final Map<InstanceIdentifier<?>, ChoiceCaseNode> instantiatedLocations;
+        private final Class<?> dataType;
+
+        public ChoiceCaseCodecImpl(final Class<?> caseClass, final ChoiceCaseNode caseNode,
+                final BindingCodec newInstance) {
+            this.delegate = newInstance;
+            this.dataType = caseClass;
             this.schema = caseNode;
-            validNames = new HashSet<>();
-            validQNames = new HashSet<>();
-            for (DataSchemaNode node : caseNode.getChildNodes()) {
-                QName qname = node.getQName();
-                validQNames.add(qname);
-                validNames.add(qname.getLocalName());
-            }
-            augmenting = caseNode.isAugmenting();
-            uses = caseNode.isAddedByUses();
-            applicableLocations = new HashSet<>();
-        }
-
-        public ChoiceCaseCodecImpl() {
-            this.delegate = NOT_READY_CODEC;
-        }
-
-        public ChoiceCaseCodecImpl(final ChoiceCaseNode caseNode) {
-            this.delegate = NOT_READY_CODEC;
-            setSchema(caseNode);
+            instantiatedLocations = new HashMap<>();
         }
 
         @Override
@@ -776,7 +767,19 @@ class LazyGeneratedCodecRegistry implements //
 
         @Override
         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
-            throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
+            if (input == null) {
+                return null;
+            }
+            QName qname = input.getNodeType();
+            synchronized (instantiatedLocations) {
+                ChoiceCaseNode instantiation = instantiatedLocations.get(bindingIdentifier);
+                if (instantiation != null) {
+                    qname = instantiatedLocations.get(bindingIdentifier).getQName();
+                }
+            }
+            @SuppressWarnings("unchecked")
+            T value = (T) getDelegate().deserialize(new SimpleEntry(qname, input), bindingIdentifier);
+            return new ValueWithQName<T>(qname, value);
         }
 
         @Override
@@ -789,72 +792,93 @@ class LazyGeneratedCodecRegistry implements //
             return delegate;
         }
 
-        public void setDelegate(final BindingCodec delegate) {
-            this.delegate = delegate;
-        }
-
         public ChoiceCaseNode getSchema() {
             return schema;
         }
 
         @Override
+        @Deprecated
         public boolean isAcceptable(final Node<?> input) {
-            if (input instanceof CompositeNode) {
-                if (augmenting && !uses) {
-                    return checkAugmenting((CompositeNode) input);
-                } else {
-                    return checkLocal((CompositeNode) input);
+            return checkAgainstSchema(schema, input);
+        }
+
+        private static boolean checkAgainstSchema(final ChoiceCaseNode schema, final Node<?> node) {
+            if (node instanceof CompositeNode) {
+                CompositeNode input = (CompositeNode) node;
+                for (Node<?> childNode : input.getValue()) {
+                    QName child = childNode.getNodeType();
+                    if (schema.getDataChildByName(child) != null) {
+                        return true;
+                    }
                 }
             }
             return false;
         }
 
-        @SuppressWarnings("deprecation")
-        private boolean checkLocal(final CompositeNode input) {
-            QName parent = input.getNodeType();
-            for (Node<?> childNode : input.getChildren()) {
-                QName child = childNode.getNodeType();
-                if (!Objects.equals(parent.getNamespace(), child.getNamespace())
-                        || !Objects.equals(parent.getRevision(), child.getRevision())) {
-                    continue;
-                }
-                if (validNames.contains(child.getLocalName())) {
-                    return true;
-                }
+        @Override
+        public Class<?> getDataType() {
+            return dataType;
+        }
+
+        public void adaptForPath(final InstanceIdentifier<?> augTarget, final ChoiceCaseNode choiceCaseNode) {
+            synchronized (instantiatedLocations) {
+                instantiatedLocations.put(augTarget, choiceCaseNode);
             }
-            return false;
         }
 
-        @SuppressWarnings("deprecation")
-        private boolean checkAugmenting(final CompositeNode input) {
-            for (Node<?> child : input.getChildren()) {
-                if (validQNames.contains(child.getNodeType())) {
-                    return true;
+        @Override
+        public boolean isApplicable(final InstanceIdentifier path, final CompositeNode input) {
+            ChoiceCaseNode instantiatedSchema = null;
+            synchronized (instantiatedLocations) {
+                instantiatedSchema = instantiatedLocations.get(path);
+            }
+            if (instantiatedSchema == null) {
+                return false;
+            }
+            return checkAgainstSchema(instantiatedSchema, input);
+        }
+
+        protected boolean isAugmenting(final QName choiceName, final QName proposedQName) {
+            if (schema.isAugmenting()) {
+                return true;
+            }
+            // Choice QName
+            QName parentQName = Iterables.get(schema.getPath().getPathTowardsRoot(), 1);
+            if (!parentQName.getNamespace().equals(schema.getQName().getNamespace())) {
+                return true;
+            }
+            if (!parentQName.equals(choiceName)) {
+                // This item is instantiation of choice via uses in other YANG
+                // module
+                if (choiceName.getNamespace().equals(schema.getQName().getNamespace())) {
+                    // Original definition of grouping is in same namespace
+                    // as original definition of case
+                    // so for sure case is introduced via instantiation of
+                    // grouping
+                    return false;
                 }
+                // Since we are still here, that means case has same namespace
+                // as its parent, which is instantiation of grouping
+                // but case namespace is different from parent node
+                // so it is augmentation.
+                return true;
             }
             return false;
         }
 
         @Override
-        public Class<?> getDataType() {
-            // TODO Auto-generated method stub
-            throw new UnsupportedOperationException("Not implemented Yet.");
+        public String toString() {
+            return "ChoiceCaseCodec [case=" + dataType + ", knownLocations=" + instantiatedLocations.keySet() + "]";
         }
     }
 
-    private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
+    private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>,
+            Delegator<BindingCodec<Map<QName, Object>, Object>> {
 
         private final BindingCodec<Map<QName, Object>, Object> delegate;
 
-        @SuppressWarnings("rawtypes")
-        private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
-                .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
-
-        private final CaseCompositeNodeMapFacade CompositeToCase;
-
         public PublicChoiceCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
             this.delegate = delegate;
-            this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
         }
 
         @Override
@@ -872,10 +896,6 @@ class LazyGeneratedCodecRegistry implements //
             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
         }
 
-        public CaseCompositeNodeMapFacade getCompositeToCase() {
-            return CompositeToCase;
-        }
-
         @Override
         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
             return delegate;
@@ -883,335 +903,350 @@ class LazyGeneratedCodecRegistry implements //
 
     }
 
-    @SuppressWarnings("unused")
-    private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
+    class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
+        private final Class<?> choiceType;
+        private final QName choiceName;
 
-        @Override
-        public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
-            // TODO Auto-generated method stub
-            return null;
+        private DispatchChoiceCodecImpl(final Class<?> type, final LazyGeneratedCodecRegistry registry) {
+            super(registry);
+            choiceType = type;
+            choiceName = BindingReflections.findQName(type);
         }
 
         @Override
-        public Object serialize(final Object input) {
-            // TODO Auto-generated method stub
+        public Object deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> path,
+                final Iterable<ChoiceCaseCodecImpl<?>> codecs) {
+            ChoiceCaseCodecImpl<?> caseCodec = Iterables.getOnlyElement(codecs);
+            ValueWithQName<?> value = caseCodec.deserialize(input, path);
+            if (value != null) {
+                return value.getValue();
+            }
             return null;
         }
 
+        @SuppressWarnings("unchecked")
         @Override
-        protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
-            return getCaseCodecFor(inputType);
-        }
-
-        @Override
-        protected void tryToLoadImplementations() {
-            // TODO Auto-generated method stub
-
+        public Object serialize(final Object input) {
+            Preconditions.checkArgument(input instanceof Map.Entry<?, ?>, "Input must be QName, Value");
+            @SuppressWarnings("rawtypes")
+            QName derivedQName = (QName) ((Map.Entry) input).getKey();
+            @SuppressWarnings("rawtypes")
+            Object inputValue = ((Map.Entry) input).getValue();
+            Preconditions.checkArgument(inputValue instanceof DataObject);
+            Class<? extends DataContainer> inputType = ((DataObject) inputValue).getImplementedInterface();
+            ChoiceCaseCodecImpl<?> codec = tryToLoadImplementation(inputType);
+            Preconditions.checkState(codec != null, "Unable to get codec for %s", inputType);
+            if (codec.isAugmenting(choiceName, derivedQName)) {
+                // If choice is augmenting we use QName which defined this
+                // augmentation
+                return codec.getDelegate().serialize(new ValueWithQName<>(codec.getSchema().getQName(), inputValue));
+            }
+            return codec.getDelegate().serialize(input);
         }
 
-        @Override
-        protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctx) {
-            // TODO Auto-generated method stub
-
+        @SuppressWarnings("rawtypes")
+        protected Optional<ChoiceCaseCodecImpl> tryToLoadImplementation(final Type potential) {
+            try {
+                @SuppressWarnings("unchecked")
+                Class<? extends DataContainer> clazz = (Class<? extends DataContainer>) classLoadingStrategy
+                        .loadClass(potential);
+                ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz);
+                addImplementation(codec);
+                return Optional.of(codec);
+            } catch (ClassNotFoundException e) {
+                LOG.warn("Failed to find class for choice {}", potential, e);
+            }
+            return Optional.absent();
         }
-    }
-
-    @SuppressWarnings("rawtypes")
-    private class CaseClassMapFacade extends MapFacadeBase {
 
         @Override
-        public Set<Entry<Class, BindingCodec<Object, Object>>> entrySet() {
-            return Collections.emptySet();
+        protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
+            ChoiceCaseCodecImpl<?> codec = getCaseCodecFor(inputType);
+            addImplementation(codec);
+            return codec;
         }
 
         @Override
-        public BindingCodec get(final Object key) {
-            if (key instanceof Class) {
-                Class cls = (Class) key;
-                // bindingClassEncountered(cls);
-                ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
-                return caseCodec.getDelegate();
+        protected void tryToLoadImplementations() {
+            Type type = referencedType(choiceType);
+            Collection<Type> potentialCases;
+            synchronized (choiceToCases) {
+                potentialCases = choiceToCases.get(type);
+            }
+            for (Type potential : potentialCases) {
+                try {
+                    tryToLoadImplementation(potential);
+                } catch (CodeGenerationException e) {
+                    LOG.warn("Failed to proactively generate choice code for {}", type, e);
+                }
             }
-            return null;
-        }
-    }
-
-    @SuppressWarnings("rawtypes")
-    private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
-
-        final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
-
-        public CaseCompositeNodeMapFacade(final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
-            this.choiceCases = choiceCases;
         }
 
         @Override
-        public BindingCodec get(final Object key) {
-            if (!(key instanceof CompositeNode)) {
-                return null;
-            }
-            for (Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
-                ChoiceCaseCodecImpl<?> codec = entry.getValue();
-                if (codec.isAcceptable((CompositeNode) key)) {
-                    return codec.getDelegate();
+        protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
+            Optional<ChoiceNode> newChoice = BindingSchemaContextUtils.findInstantiatedChoice(ctxNode, choiceType);
+            tryToLoadImplementations();
+            Preconditions.checkState(newChoice.isPresent(), "BUG: Unable to find instantiated choice node in schema.");
+            for (@SuppressWarnings("rawtypes")
+            Entry<Class, ChoiceCaseCodecImpl<?>> codec : getImplementations().entrySet()) {
+                ChoiceCaseCodecImpl<?> caseCodec = codec.getValue();
+                Optional<ChoiceCaseNode> instantiatedSchema = BindingSchemaContextUtils.findInstantiatedCase(
+                        newChoice.get(), caseCodec.getSchema());
+                if (instantiatedSchema.isPresent()) {
+                    caseCodec.adaptForPath(augTarget, instantiatedSchema.get());
                 }
             }
-            return null;
         }
 
+        @Override
+        public String toString() {
+            return "DispatchChoiceCodecImpl [choiceType=" + choiceType + "]";
+        }
     }
 
     /**
-     * This map is used as only facade for
-     * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different
-     * classloaders to retrieve codec dynamicly based on provided key.
      *
-     * @param <T>
-     *            Key type
+     * Dispatch codec for augmented object, which processes augmentations
+     * <p>
+     * This codec is used from DataObject codec generated using
+     * {@link TransformerGenerator#transformerFor(Class)} and is wired during
+     * {@link LazyGeneratedCodecRegistry#onDataContainerCodecCreated(Class, Class)}.
+     * <p>
+     * Instance of this codec is associated with class of Binding DTO which
+     * represents target for augmentations.
+     *
      */
-    @SuppressWarnings("rawtypes")
-    private abstract static class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
-
-        @Override
-        public boolean containsKey(final Object key) {
-            return get(key) != null;
-        }
-
-        @Override
-        public void clear() {
-            throw notModifiable();
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            return super.equals(obj);
-        }
-
-        @Override
-        public BindingCodec remove(final Object key) {
-            return null;
-        }
-
-        @Override
-        public int size() {
-            return 0;
-        }
-
-        @Override
-        public Collection<BindingCodec<?, ?>> values() {
-            return Collections.emptySet();
-        }
-
-        private UnsupportedOperationException notModifiable() {
-            return new UnsupportedOperationException("Not externally modifiable.");
-        }
-
-        @Override
-        public BindingCodec<Map<QName, Object>, Object> put(final T key, final BindingCodec<?, ?> value) {
-            throw notModifiable();
-        }
-
-        @Override
-        public void putAll(final Map<? extends T, ? extends BindingCodec<?, ?>> m) {
-            throw notModifiable();
-        }
-
-        @Override
-        public int hashCode() {
-            return super.hashCode();
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return true;
-        }
-
-        @Override
-        public Set<T> keySet() {
-            return Collections.emptySet();
-        }
-
-        @Override
-        public Set<Entry<T, BindingCodec<?, ?>>> entrySet() {
-            return Collections.emptySet();
-        }
-
-        @Override
-        public boolean containsValue(final Object value) {
-            return false;
-        }
-    }
-
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
+    static class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
 
         private final Class augmentableType;
 
-        public AugmentableDispatchCodec(final Class type) {
+        /**
+         * Construct augmetable dispatch codec.
+         *
+         * @param type
+         *            Class representing augmentation target
+         * @param registry
+         *            Registry with which this codec is associated.
+         */
+        public AugmentableDispatchCodec(final Class type, final LazyGeneratedCodecRegistry registry) {
+            super(registry);
             Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
             augmentableType = type;
         }
 
+        /**
+         * Serializes object to list of values which needs to be injected into
+         * resulting DOM Node. Injection of data to parent DOM Node is handled
+         * by caller (in this case generated codec).
+         *
+         * TODO: Deprecate use of augmentation codec without instance instance
+         * identifier
+         *
+         * @return list of nodes, which needs to be added to parent node.
+         *
+         */
         @Override
-        // TODO deprecate use without iid
         public Object serialize(final Object input) {
+            Preconditions.checkArgument(augmentableType.isInstance(input), "Object %s is not instance of %s ", input,
+                    augmentableType);
             if (input instanceof Augmentable<?>) {
-                Map<Class, Augmentation> augmentations = getAugmentations(input);
+                Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations = BindingReflections
+                        .getAugmentations((Augmentable<?>) input);
                 return serializeImpl(augmentations);
             }
             return null;
         }
 
-        private Map<Class, Augmentation> getAugmentations(final Object input) {
-            Field augmentationField;
-            try {
-                augmentationField = input.getClass().getDeclaredField("augmentation");
-                augmentationField.setAccessible(true);
-                Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
-                return augMap;
-            } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
-                LOG.debug("Could not read augmentations for {}", input, e);
-            }
-            return Collections.emptyMap();
-        }
-
-        @SuppressWarnings("deprecation")
-        private List serializeImpl(final Map<Class, Augmentation> input) {
+        /**
+         *
+         * Serialization of augmentations, returns list of composite nodes,
+         * which needs to be injected to parent node.
+         *
+         * @param input
+         *            Map of classes to augmentations
+         * @return List of nodes, which should be added to parent node.
+         */
+        private List serializeImpl(final Map<Class<? extends Augmentation<?>>, Augmentation<?>> input) {
             List ret = new ArrayList<>();
-            for (Entry<Class, Augmentation> entry : input.entrySet()) {
-                AugmentationCodec codec = getCodecForAugmentation(entry.getKey());
+            for (Entry<Class<? extends Augmentation<?>>, Augmentation<?>> entry : input.entrySet()) {
+                AugmentationCodec codec = getRegistry().getCodecForAugmentation(entry.getKey());
                 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
-                ret.addAll(node.getChildren());
+                ret.addAll(node.getValue());
             }
             return ret;
         }
 
-        @Override
-        public Map<Class, Augmentation> deserialize(final Object input, final InstanceIdentifier path) {
-            adaptForPath(path);
+        /**
+         *
+         * Deserialization of augmentation which is location aware.
+         *
+         * Note: In case of composite nodes as an input, each codec is invoked
+         * since there is no augmentation identifier and we need to look for
+         * concrete classes. FIXME: Maybe faster variation will be by extending
+         * {@link AugmentationCodecWrapper} to look for particular QNames, which
+         * will filter incoming set of codecs.
+         *
+         *
+         * @param input
+         *            Input representation of data
+         * @param path
+         *            Wildcarded instance identifier representing location of
+         *            augmentation parent in conceptual schema tree
+         * @param codecs
+         *            Set of codecs which are applicable for supplied
+         *            <code>path</code>, selected by caller to be used by
+         *            deserialization
+         *
+         *
+         */
+        @Override
+        public Map<Class, Augmentation> deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> path,
+                final Iterable<AugmentationCodecWrapper> codecs) {
+            LOG.trace("{}: Going to deserialize augmentations from {} in location {}. Available codecs {}", this,
+                    input, path, codecs);
             Map<Class, Augmentation> ret = new HashMap<>();
-
-            if (input instanceof CompositeNode) {
-                List<Entry<Class, AugmentationCodecWrapper>> codecs = new ArrayList<>(getImplementations().entrySet());
-                for (Entry<Class, AugmentationCodecWrapper> codec : codecs) {
-                    AugmentationCodec<?> ac = codec.getValue();
-                    if (ac.isAcceptable(path)) {
-                        // We add Augmentation Identifier to path, in order to
-                        // correctly identify children.
-                        InstanceIdentifier augmentPath = path.builder().augmentation(codec.getKey()).build();
-                        ValueWithQName<?> value = codec.getValue().deserialize((CompositeNode) input, augmentPath);
-                        if (value != null && value.getValue() != null) {
-                            ret.put(codec.getKey(), (Augmentation) value.getValue());
-                        }
-                    }
+            for (AugmentationCodecWrapper codec : codecs) {
+                // We add Augmentation Identifier to path, in order to
+                // correctly identify children.
+                Class type = codec.getDataType();
+                final InstanceIdentifier augmentPath = path.augmentation(type);
+                ValueWithQName<?> value = codec.deserialize(input, augmentPath);
+                if (value != null && value.getValue() != null) {
+                    ret.put(type, (Augmentation) value.getValue());
                 }
             }
             return ret;
         }
 
+        /**
+         *
+         * Tries to load implementation of concrete augmentation codec for
+         * supplied type
+         *
+         * Loading of codec may fail, because of supplied type may not be
+         * visible by classloaders known by registry. If class was not found
+         * returns {@link Optional#absent()}.
+         *
+         * @param potential
+         *            Augmentation class identifier for which codecs should be
+         *            loaded.
+         * @return Optional with codec for supplied type
+         *
+         */
         protected Optional<AugmentationCodecWrapper> tryToLoadImplementation(final Type potential) {
             try {
-                Class<? extends Augmentation<?>> clazz = (Class<? extends Augmentation<?>>) classLoadingStrategy
+                Class<? extends Augmentation<?>> clazz = (Class<? extends Augmentation<?>>) getRegistry().classLoadingStrategy
                         .loadClass(potential);
                 return Optional.of(tryToLoadImplementation(clazz));
             } catch (ClassNotFoundException e) {
-                LOG.warn("Failed to find class for augmentation of {}, reason: {}", potential, e.toString());
+                LOG.warn("Failed to find class for augmentation of {}", potential, e);
             }
             return Optional.absent();
         }
 
         @Override
         protected AugmentationCodecWrapper tryToLoadImplementation(final Class inputType) {
-            AugmentationCodecWrapper<? extends Augmentation<?>> potentialImpl = getCodecForAugmentation(inputType);
+            AugmentationCodecWrapper<? extends Augmentation<?>> potentialImpl = getRegistry().getCodecForAugmentation(
+                    inputType);
             addImplementation(potentialImpl);
             return potentialImpl;
-
         }
 
         @Override
         protected void tryToLoadImplementations() {
             Type type = referencedType(augmentableType);
             Collection<Type> potentialAugmentations;
-            synchronized(augmentableToAugmentations) {
+            synchronized (augmentableToAugmentations) {
                 potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type));
             }
             for (Type potential : potentialAugmentations) {
                 try {
                     tryToLoadImplementation(potential);
                 } catch (CodeGenerationException e) {
-                    LOG.warn("Failed to proactively generate augment coded for {}, reason: {}", type, e.toString());
+                    LOG.warn("Failed to proactively generate augment code for {}", type, e);
                 }
             }
-
         }
 
         @Override
-        protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctxNode) {
+        protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
             if (ctxNode instanceof AugmentationTarget) {
                 Set<AugmentationSchema> availableAugmentations = ((AugmentationTarget) ctxNode)
                         .getAvailableAugmentations();
                 if (!availableAugmentations.isEmpty()) {
-                    updateAugmentationMapping(path,availableAugmentations);
+                    updateAugmentationMapping(augTarget, availableAugmentations);
                 }
-
             }
         }
 
-        private void updateAugmentationMapping(final InstanceIdentifier<?> path, final Set<AugmentationSchema> availableAugmentations) {
+        /**
+         *
+         * Adapts augmentation codec for specific provider location (target)
+         *
+         * Since augmentation are not forward-referencing and may be discovered
+         * during runtime, we need to adapt {@link AugmentableDispatchCodec},
+         * {@link AugmentationCodecWrapper} and {@link InstanceIdentifierCodec}
+         * for this newly discovered location where augmentation may be used.
+         *
+         * Adaptation consists of:
+         * <ol>
+         * <li>scan of available (valid) augmentations for current location
+         * <li>lookup for Java classes derived from this augmentations
+         * <li>generation of missing codecs
+         * <li>updating Augmentation codecs to work with new location
+         * <li>updating Instance Identifier to work with new location
+         *
+         */
+        private void updateAugmentationMapping(final InstanceIdentifier<?> augTarget,
+                final Set<AugmentationSchema> availableAugmentations) {
             for (AugmentationSchema aug : availableAugmentations) {
 
                 Type potentialType = getTypeForAugmentation(aug);
                 if (potentialType != null) {
                     Optional<AugmentationCodecWrapper> potentialImpl = tryToLoadImplementation(potentialType);
                     if (potentialImpl.isPresent()) {
-                        potentialImpl.get().addApplicableFor(path,aug);
+                        potentialImpl.get().addApplicableFor(augTarget, aug);
+                        Class augType = potentialImpl.get().getDataType();
+                        InstanceIdentifier augPath = augTarget.augmentation(augType);
+                        try {
+
+                            org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier domPath = getRegistry()
+                                    .getInstanceIdentifierCodec().serialize(augPath);
+                            if (domPath == null) {
+                                LOG.error("Unable to serialize instance identifier for {}", augPath);
+                            }
+                        } catch (Exception e) {
+                            LOG.error("Unable to serialize instance identifiers for {}", augPath, e);
+                        }
+
                     }
                 } else {
-                    LOG.warn("Could not find generated type for augmentation {} with childs {}.",aug,aug.getChildNodes());
+                    // Omits warning for empty augmentations since they are not
+                    // represented in data
+                    if (!aug.getChildNodes().isEmpty()) {
+                        LOG.warn("Could not find generated type for augmentation {} with children {}", aug,
+                                aug.getChildNodes());
+                    }
                 }
             }
-            availableAugmentations.toString();
         }
 
         private Type getTypeForAugmentation(final AugmentationSchema aug) {
             Optional<AugmentationSchema> currentAug = Optional.of(aug);
-            while(currentAug.isPresent()) {
+            while (currentAug.isPresent()) {
                 Type potentialType = typeToAugment.inverse().get(currentAug.get());
-                if(potentialType != null) {
+                if (potentialType != null) {
                     return potentialType;
                 }
                 currentAug = currentAug.get().getOriginalDefinition();
             }
             return null;
         }
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
-
-        private BindingCodec delegate;
-
-        @Override
-        public BindingCodec getDelegate() {
-            if (delegate == null) {
-                throw new IllegalStateException("Codec not initialized yet.");
-            }
-            return delegate;
-        }
 
         @Override
-        public Object deserialize(final Object input) {
-            return getDelegate().deserialize(input);
-        }
-
-        @Override
-        public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
-            return getDelegate().deserialize(input, bindingIdentifier);
-        }
-
-        @Override
-        public Object serialize(final Object input) {
-            return getDelegate().serialize(input);
+        public String toString() {
+            return "AugmentableDispatchCodec [augmentable=" + augmentableType + "]";
         }
 
     }
@@ -1222,20 +1257,20 @@ class LazyGeneratedCodecRegistry implements //
 
         private final BindingCodec delegate;
         private final QName augmentationQName;
-        private final Multimap<InstanceIdentifier<?>,QName> validAugmentationTargets;
+        private final Multimap<InstanceIdentifier<?>, QName> validAugmentationTargets;
         private final Class<?> augmentationType;
 
-        public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec,
-                final InstanceIdentifier<?> targetId, final Class<?> dataType) {
+        public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec, final Class<?> dataType) {
             this.delegate = rawCodec;
             this.augmentationType = dataType;
             this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
-            this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap.<InstanceIdentifier<?>,QName>create());
+            this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap
+                    .<InstanceIdentifier<?>, QName> create());
         }
 
         public void addApplicableFor(final InstanceIdentifier<?> path, final AugmentationSchema aug) {
-            for(DataSchemaNode child : aug.getChildNodes()) {
-                validAugmentationTargets.put(path,child.getQName());
+            for (DataSchemaNode child : aug.getChildNodes()) {
+                validAugmentationTargets.put(path, child.getQName());
             }
         }
 
@@ -1265,9 +1300,6 @@ class LazyGeneratedCodecRegistry implements //
         @Override
         @SuppressWarnings("unchecked")
         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
-            // if (!isAcceptable(bindingIdentifier)) {
-            // return null;
-            // }
             Object rawCodecValue = getDelegate().deserialize(input, bindingIdentifier);
             return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
         }
@@ -1286,14 +1318,20 @@ class LazyGeneratedCodecRegistry implements //
         }
 
         @Override
-        public boolean isApplicable(final InstanceIdentifier location) {
-            return isAcceptable(location);
+        public boolean isApplicable(final InstanceIdentifier parentPath, final CompositeNode parentData) {
+            return isAcceptable(parentPath);
         }
 
         @Override
         public Class<?> getDataType() {
             return augmentationType;
         }
+
+        @Override
+        public String toString() {
+            return "AugmentationCodecWrapper [augmentation=" + augmentationType + ", knownLocations="
+                    + validAugmentationTargets.keySet() + "]";
+        }
     }
 
     @SuppressWarnings("rawtypes")
@@ -1307,52 +1345,32 @@ class LazyGeneratedCodecRegistry implements //
 
         @Override
         public Class<?> deserialize(final QName input) {
+            if (input == null) {
+                return null;
+            }
             Type type = qnamesToIdentityMap.get(input);
             if (type == null) {
-                return null;
+                String packageName = BindingMapping.getRootPackageName(input);
+                String className = BindingMapping.getClassName(input);
+                type = new ReferencedTypeImpl(packageName, className);
             }
-            ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
-            WeakReference<Class> softref = typeToClass.get(typeref);
-            if (softref == null) {
-
-                try {
-                    Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
-                    if (cls != null) {
-                        serialize(cls);
-                        return cls;
-                    }
-                } catch (Exception e) {
-                    LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
-                            typeref.getFullyQualifiedName());
+            try {
+                final Class<?> cls = classLoadingStrategy.loadClass(type);
+                if (cls != null) {
+                    serialize(cls);
+                    return cls;
                 }
-                return null;
+            } catch (Exception e) {
+                LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
+                        type.getFullyQualifiedName(), e);
             }
-            return softref.get();
+            return null;
+
         }
 
         @Override
-        public Object deserialize(final Object input,final InstanceIdentifier bindingIdentifier) {
-            Type type = qnamesToIdentityMap.get(input);
-            if (type == null) {
-                return null;
-            }
-            ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
-            WeakReference<Class> softref = typeToClass.get(typeref);
-            if (softref == null) {
-
-                try {
-                    Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
-                    if (cls != null) {
-                        serialize(cls);
-                        return cls;
-                    }
-                } catch (Exception e) {
-                    LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
-                            typeref.getFullyQualifiedName());
-                }
-                return null;
-            }
-            return softref.get();
+        public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
+            return deserialize(input);
         }
 
         @Override
@@ -1363,8 +1381,7 @@ class LazyGeneratedCodecRegistry implements //
             if (qname != null) {
                 return qname;
             }
-            ConcreteType typeref = Types.typeForClass(input);
-            qname = typeToQname.get(typeref);
+            qname = BindingReflections.findQName(input);
             if (qname != null) {
                 identityQNames.put(input, qname);
             }
@@ -1379,32 +1396,8 @@ class LazyGeneratedCodecRegistry implements //
 
     }
 
-    public boolean isCodecAvailable(final Class<?> cls) {
-        // FIXME: enforce type?
-        // Preconditions.checkArgument(DataContainer.class.isAssignableFrom(cls));
-
-        if (containerCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (identifierCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (choiceCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (caseCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (augmentableCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (augmentationCodecs.containsKey(cls)) {
-            return true;
-        }
-        return false;
-    }
-
-    public static final Type referencedType(final Class<?> augmentableType) {
+    private static final Type referencedType(final Class<?> augmentableType) {
         return new ReferencedTypeImpl(augmentableType.getPackage().getName(), augmentableType.getSimpleName());
     }
+
 }