BUG-981: defensive checks
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / LazyGeneratedCodecRegistry.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.sal.binding.generator.impl;
9
10 import java.lang.ref.WeakReference;
11 import java.lang.reflect.Field;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Objects;
22 import java.util.Set;
23 import java.util.WeakHashMap;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26
27 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
28 import org.opendaylight.yangtools.binding.generator.util.Types;
29 import org.opendaylight.yangtools.concepts.Delegator;
30 import org.opendaylight.yangtools.concepts.Identifiable;
31 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
32 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException;
33 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
34 import org.opendaylight.yangtools.sal.binding.model.api.Type;
35 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
36 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
37 import org.opendaylight.yangtools.yang.binding.Augmentable;
38 import org.opendaylight.yangtools.yang.binding.Augmentation;
39 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
40 import org.opendaylight.yangtools.yang.binding.BindingCodec;
41 import org.opendaylight.yangtools.yang.binding.DataContainer;
42 import org.opendaylight.yangtools.yang.binding.DataObject;
43 import org.opendaylight.yangtools.yang.binding.Identifier;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
48 import org.opendaylight.yangtools.yang.data.api.Node;
49 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
50 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec;
51 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCaseCodec;
52 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCodec;
53 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
54 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec;
55 import org.opendaylight.yangtools.yang.data.impl.codec.DomCodec;
56 import org.opendaylight.yangtools.yang.data.impl.codec.IdentifierCodec;
57 import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
58 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
59 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
60 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
61 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
62 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
63 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
64 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
65 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
66 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
67 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
68 import org.opendaylight.yangtools.yang.model.api.Module;
69 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
70 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
71 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
72 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 import com.google.common.base.Optional;
77 import com.google.common.base.Preconditions;
78 import com.google.common.collect.BiMap;
79 import com.google.common.collect.HashBiMap;
80 import com.google.common.collect.HashMultimap;
81 import com.google.common.collect.Multimap;
82 import com.google.common.collect.Multimaps;
83
84 public class LazyGeneratedCodecRegistry implements //
85         CodecRegistry, //
86         SchemaContextListener, //
87         GeneratorListener {
88
89     private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
90     private static final LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
91
92     private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
93     private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
94
95     private TransformerGenerator generator;
96
97     // Concrete class to codecs
98     private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
99             .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
100     private static final Map<Class<?>, IdentifierCodec<?>> identifierCodecs = Collections
101             .synchronizedMap(new WeakHashMap<Class<?>, IdentifierCodec<?>>());
102     private static final Map<Class<?>, PublicChoiceCodecImpl<?>> choiceCodecs = Collections
103             .synchronizedMap(new WeakHashMap<Class<?>, PublicChoiceCodecImpl<?>>());
104     private static final Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = Collections
105             .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCaseCodecImpl<?>>());
106     private static final Map<Class<?>, AugmentableDispatchCodec> augmentableCodecs = Collections
107             .synchronizedMap(new WeakHashMap<Class<?>, AugmentableDispatchCodec>());
108     private static final Map<Class<?>, AugmentationCodecWrapper<?>> augmentationCodecs = Collections
109             .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodecWrapper<?>>());
110     private static final Map<Class<?>, QName> identityQNames = Collections
111             .synchronizedMap(new WeakHashMap<Class<?>, QName>());
112     private static final Map<QName, Type> qnamesToIdentityMap = new ConcurrentHashMap<>();
113     /** Binding type to encountered classes mapping **/
114     @SuppressWarnings("rawtypes")
115     private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
116
117     @SuppressWarnings("rawtypes")
118     private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
119
120     private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
121
122     private static final Map<SchemaPath, InstanceIdentifier<?>> pathToBindingIdentifier = new ConcurrentHashMap<>();
123     private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
124     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
125     private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
126     private static final BiMap<Type, AugmentationSchema> typeToAugment = HashBiMap
127             .create(new ConcurrentHashMap<Type, AugmentationSchema>());
128
129     private static final Multimap<Type, Type> augmentableToAugmentations = Multimaps.synchronizedMultimap(HashMultimap
130             .<Type, Type> create());
131     private static final Multimap<Type, Type> choiceToCases = Multimaps.synchronizedMultimap(HashMultimap
132             .<Type, Type> create());
133
134     private final SchemaLock lock;
135
136     private SchemaContext currentSchema;
137
138     private final ClassLoadingStrategy classLoadingStrategy;
139
140     LazyGeneratedCodecRegistry(final SchemaLock lock, final ClassLoadingStrategy identityClassLoadingStrategy) {
141         this.lock = Preconditions.checkNotNull(lock);
142         this.classLoadingStrategy = identityClassLoadingStrategy;
143     }
144
145     public SchemaLock getLock() {
146         return lock;
147     }
148
149     public TransformerGenerator getGenerator() {
150         return generator;
151     }
152
153     public void setGenerator(final TransformerGenerator generator) {
154         this.generator = generator;
155     }
156
157     @Override
158     public InstanceIdentifierCodec getInstanceIdentifierCodec() {
159         return instanceIdentifierCodec;
160     }
161
162     @SuppressWarnings("unchecked")
163     @Override
164     public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> object) {
165         AugmentationCodecWrapper<T> codec = null;
166         @SuppressWarnings("rawtypes")
167         AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(object);
168         if (potentialCodec != null) {
169             codec = potentialCodec;
170         } else {
171             try {
172                 lock.waitForSchema(object);
173                 Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
174                         .augmentationTransformerFor(object);
175                 BindingCodec<Map<QName, Object>, Object> rawCodec = augmentRawCodec.newInstance();
176
177                 codec = new AugmentationCodecWrapper<T>(rawCodec, null, object);
178                 augmentationCodecs.put(object, codec);
179             } catch (InstantiationException e) {
180                 LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e);
181             } catch (IllegalAccessException e) {
182                 LOG.debug(
183                         "Run-time consistency issue: constructor {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
184                         object.getSimpleName(), e);
185             }
186         }
187         Class<? extends Augmentable<?>> objectSupertype = getAugmentableArgumentFrom(object);
188         if (objectSupertype != null) {
189             getAugmentableCodec(objectSupertype).addImplementation(codec);
190         } else {
191             LOG.warn("Could not find augmentation target for augmentation {}", object);
192         }
193         return codec;
194     }
195
196     @SuppressWarnings("unchecked")
197     @Override
198     public QName getQNameForAugmentation(final Class<?> cls) {
199         Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
200         return getCodecForAugmentation((Class<? extends Augmentation<?>>) cls).getAugmentationQName();
201     }
202
203     private static Class<? extends Augmentable<?>> getAugmentableArgumentFrom(
204             final Class<? extends Augmentation<?>> augmentation) {
205         try {
206             Class<? extends Augmentable<?>> ret = BindingReflections.findAugmentationTarget(augmentation);
207             return ret;
208
209         } catch (Exception e) {
210             LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
211             return null;
212         }
213     }
214
215     @Override
216     public Class<?> getClassForPath(final List<QName> names) {
217         final DataSchemaNode node = getSchemaNode(names);
218         final SchemaPath path = node.getPath();
219         final Type t = pathToType.get(path);
220
221         final Type type;
222         if (t != null) {
223             type = new ReferencedTypeImpl(t.getPackageName(), t.getName());
224         } else {
225             type = pathToInstantiatedType.get(names);
226             Preconditions.checkState(type != null, "Failed to lookup instantiated type for path %s", path);
227         }
228
229         @SuppressWarnings("rawtypes")
230         final WeakReference<Class> weakRef = typeToClass.get(type);
231         Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path, type.getFullyQualifiedName());
232         return weakRef.get();
233     }
234
235     @Override
236     public void putPathToClass(final List<QName> names, final Class<?> cls) {
237         final Type reference = Types.typeForClass(cls);
238         pathToInstantiatedType.put(names, reference);
239         LOG.trace("Path {} attached to class {} reference {}", names, cls, reference);
240         bindingClassEncountered(cls);
241     }
242
243     public InstanceIdentifier<?> getBindingIdentifierByPath(final SchemaPath path) {
244         return pathToBindingIdentifier.get(path);
245     }
246
247     public void putPathToBindingIdentifier(final SchemaPath path, final InstanceIdentifier<?> bindingIdentifier) {
248         pathToBindingIdentifier.put(path, bindingIdentifier);
249     }
250
251     public InstanceIdentifier<?> putPathToBindingIdentifier(final SchemaPath path,
252             final InstanceIdentifier<?> bindingIdentifier, final Class<?> childClass) {
253         @SuppressWarnings({ "unchecked", "rawtypes" })
254         InstanceIdentifier<?> newId = bindingIdentifier.builder().child((Class) childClass).build();
255         pathToBindingIdentifier.put(path, newId);
256         return newId;
257     }
258
259     @Override
260     public IdentifierCodec<?> getKeyCodecForPath(final List<QName> names) {
261         @SuppressWarnings("unchecked")
262         Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
263         return getIdentifierCodecForIdentifiable(cls);
264     }
265
266     @Override
267     public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(final Class<T> type) {
268         @SuppressWarnings("unchecked")
269         DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
270         if (ret != null) {
271             return ret;
272         }
273         Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
274         BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
275         DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
276         containerCodecs.put(type, newWrapper);
277         return newWrapper;
278     }
279
280     @Override
281     @SuppressWarnings("rawtypes")
282     public void bindingClassEncountered(final Class cls) {
283
284         ConcreteType typeRef = Types.typeForClass(cls);
285         if (typeToClass.containsKey(typeRef)) {
286             return;
287         }
288         LOG.trace("Binding Class {} encountered.", cls);
289         WeakReference<Class> weakRef = new WeakReference<>(cls);
290         typeToClass.put(typeRef, weakRef);
291         if (Augmentation.class.isAssignableFrom(cls)) {
292
293         } else if (DataObject.class.isAssignableFrom(cls)) {
294             @SuppressWarnings({ "unchecked", "unused" })
295             Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
296         }
297     }
298
299     @Override
300     public void onClassProcessed(final Class<?> cls) {
301         ConcreteType typeRef = Types.typeForClass(cls);
302         if (typeToClass.containsKey(typeRef)) {
303             return;
304         }
305         LOG.trace("Binding Class {} encountered.", cls);
306         @SuppressWarnings("rawtypes")
307         WeakReference<Class> weakRef = new WeakReference<Class>(cls);
308         typeToClass.put(typeRef, weakRef);
309     }
310
311     private DataSchemaNode getSchemaNode(final List<QName> path) {
312         QName firstNode = path.get(0);
313         DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
314                 firstNode.getRevision());
315         Iterator<QName> iterator = path.iterator();
316         while (iterator.hasNext()) {
317             QName arg = iterator.next();
318             DataSchemaNode currentNode = previous.getDataChildByName(arg);
319             if (currentNode == null && previous instanceof DataNodeContainer) {
320                 currentNode = searchInChoices(previous, arg);
321             }
322             if (currentNode instanceof DataNodeContainer) {
323                 previous = (DataNodeContainer) currentNode;
324             } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
325                 Preconditions.checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
326                 return currentNode;
327             }
328         }
329         return (DataSchemaNode) previous;
330     }
331
332     private DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
333         Set<DataSchemaNode> children = node.getChildNodes();
334         for (DataSchemaNode child : children) {
335             if (child instanceof ChoiceNode) {
336                 ChoiceNode choiceNode = (ChoiceNode) child;
337                 DataSchemaNode potential = searchInCases(choiceNode, arg);
338                 if (potential != null) {
339                     return potential;
340                 }
341             }
342         }
343         return null;
344     }
345
346     private DataSchemaNode searchInCases(final ChoiceNode choiceNode, final QName arg) {
347         Set<ChoiceCaseNode> cases = choiceNode.getCases();
348         for (ChoiceCaseNode caseNode : cases) {
349             DataSchemaNode node = caseNode.getDataChildByName(arg);
350             if (node != null) {
351                 return node;
352             }
353         }
354         return null;
355     }
356
357     private <T> T newInstanceOf(final Class<?> newType) {
358         try {
359             @SuppressWarnings("unchecked")
360             T ret = (T) newType.newInstance();
361             return ret;
362         } catch (InstantiationException e) {
363             throw new IllegalStateException(e);
364         } catch (IllegalAccessException e) {
365             throw new IllegalStateException(e);
366         }
367     }
368
369     @Override
370     public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class<T> type) {
371         IdentifierCodec<?> obj = identifierCodecs.get(type);
372         if (obj != null) {
373             return obj;
374         }
375         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
376                 .keyTransformerForIdentifiable(type);
377         BindingCodec<Map<QName, Object>, Object> newInstance;
378         newInstance = newInstanceOf(newCodec);
379         IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
380         identifierCodecs.put(type, newWrapper);
381         return newWrapper;
382     }
383
384     @Override
385     public IdentityCodec<?> getIdentityCodec() {
386         return identityRefCodec;
387     }
388
389     @SuppressWarnings("unchecked")
390     @Override
391     public <T extends BaseIdentity> IdentityCodec<T> getCodecForIdentity(final Class<T> codec) {
392         bindingClassEncountered(codec);
393         return identityRefCodec;
394     }
395
396     @Override
397     public void onCodecCreated(final Class<?> cls) {
398         CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
399         CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
400     }
401
402     @Override
403     public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> object) {
404         @SuppressWarnings("unchecked")
405         IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
406         if (obj != null) {
407             return obj;
408         }
409         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
410                 .keyTransformerForIdentifier(object);
411         BindingCodec<Map<QName, Object>, Object> newInstance;
412         newInstance = newInstanceOf(newCodec);
413         IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
414         identifierCodecs.put(object, newWrapper);
415         return newWrapper;
416     }
417
418     @SuppressWarnings("rawtypes")
419     public ChoiceCaseCodecImpl getCaseCodecFor(final Class caseClass) {
420         ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
421         if (potential != null) {
422             return potential;
423         }
424         ConcreteType typeref = Types.typeForClass(caseClass);
425         ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref);
426
427         Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s",
428                 caseClass.getName());
429         Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
430                 caseClass.getName());
431         @SuppressWarnings("unchecked")
432         Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
433         BindingCodec newInstance = newInstanceOf(newCodec);
434         caseCodec.setDelegate(newInstance);
435         caseCodecs.put(caseClass, caseCodec);
436
437         for (Entry<Class<?>, PublicChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
438             if (choice.getKey().isAssignableFrom(caseClass)) {
439                 choice.getValue().cases.put(caseClass, caseCodec);
440             }
441         }
442         return caseCodec;
443     }
444
445     public void onModuleContextAdded(final SchemaContext schemaContext, final Module module, final ModuleContext context) {
446         pathToType.putAll(context.getChildNodes());
447
448         BiMap<Type, AugmentationSchema> bimap = context.getTypeToAugmentation();
449         for (Map.Entry<Type, AugmentationSchema> entry : bimap.entrySet()) {
450             Type key = entry.getKey();
451             AugmentationSchema value = entry.getValue();
452             Set<DataSchemaNode> augmentedNodes = value.getChildNodes();
453             if (augmentedNodes != null && !(augmentedNodes.isEmpty())) {
454                 typeToAugment.put(key, value);
455             }
456         }
457
458         qnamesToIdentityMap.putAll(context.getIdentities());
459         for (Entry<QName, GeneratedTOBuilder> identity : context.getIdentities().entrySet()) {
460             typeToQname.put(
461                     new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),
462                     identity.getKey());
463         }
464
465         synchronized(augmentableToAugmentations) {
466             augmentableToAugmentations.putAll(context.getAugmentableToAugmentations());
467         }
468         synchronized(choiceToCases)  {
469             choiceToCases.putAll(context.getChoiceToCases());
470         }
471         captureCases(context.getCases(), schemaContext);
472     }
473
474     private void captureCases(final Map<SchemaPath, GeneratedTypeBuilder> cases, final SchemaContext module) {
475         for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
476             ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
477                     .getValue().getName());
478
479             pathToType.put(caseNode.getKey(), caseNode.getValue());
480
481             ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
482
483             if (node == null) {
484                 LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.",
485                         typeref.getFullyQualifiedName(), caseNode.getKey());
486                 @SuppressWarnings("rawtypes")
487                 ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl();
488                 typeToCaseCodecs.putIfAbsent(typeref, value);
489                 continue;
490             }
491             @SuppressWarnings("rawtypes")
492             ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
493             typeToCaseCodecs.putIfAbsent(typeref, value);
494         }
495     }
496
497     @Override
498     public void onGlobalContextUpdated(final SchemaContext context) {
499         currentSchema = context;
500     }
501
502     @SuppressWarnings({ "unchecked", "rawtypes" })
503     @Override
504     public void onChoiceCodecCreated(final Class<?> choiceClass,
505             final Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec, final ChoiceNode schema) {
506         ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
507         Preconditions.checkState(oldCodec == null);
508         BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
509         PublicChoiceCodecImpl<?> newCodec = new PublicChoiceCodecImpl(delegate);
510         choiceCodecs.put(choiceClass, newCodec);
511         CodecMapping.setClassToCaseMap(choiceCodec, classToCaseRawCodec);
512         CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
513
514         tryToCreateCasesCodecs(schema);
515
516     }
517
518     @Deprecated
519     private void tryToCreateCasesCodecs(final ChoiceNode schema) {
520         for (ChoiceCaseNode choiceCase : schema.getCases()) {
521             ChoiceCaseNode caseNode = choiceCase;
522             if (caseNode.isAddedByUses()) {
523                 DataSchemaNode origCaseNode = SchemaContextUtil.findOriginal(caseNode, currentSchema);
524                 if (origCaseNode instanceof ChoiceCaseNode) {
525                     caseNode = (ChoiceCaseNode) origCaseNode;
526                 }
527             }
528             SchemaPath path = caseNode.getPath();
529
530             GeneratedTypeBuilder type;
531             if (path != null && (type = pathToType.get(path)) != null) {
532                 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
533                 @SuppressWarnings("rawtypes")
534                 ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref);
535                 if (partialCodec.getSchema() == null) {
536                     partialCodec.setSchema(caseNode);
537                 }
538                 try {
539                     Class<?> caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName());
540                     getCaseCodecFor(caseClass);
541                 } catch (ClassNotFoundException e) {
542                     LOG.trace("Could not proactivelly create case codec for {}", type, e);
543                 }
544             }
545         }
546
547     }
548
549     @Override
550     public void onValueCodecCreated(final Class<?> valueClass, final Class<?> valueCodec) {
551     }
552
553     @Override
554     public void onCaseCodecCreated(final Class<?> choiceClass,
555             final Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
556     }
557
558     @Override
559     public void onDataContainerCodecCreated(final Class<?> dataClass,
560             final Class<? extends BindingCodec<?, ?>> dataCodec) {
561         if (Augmentable.class.isAssignableFrom(dataClass)) {
562             AugmentableDispatchCodec augmentableCodec = getAugmentableCodec(dataClass);
563             CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
564         }
565     }
566
567     public AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
568         AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass);
569         if (ret != null) {
570             return ret;
571         }
572         ret = new AugmentableDispatchCodec(dataClass);
573         augmentableCodecs.put(dataClass, ret);
574         ret.tryToLoadImplementations();
575         return ret;
576     }
577
578     private static abstract class IntermediateCodec<T> implements //
579             DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
580
581         private final BindingCodec<Map<QName, Object>, Object> delegate;
582
583         @Override
584         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
585             return delegate;
586         }
587
588         public IntermediateCodec(final BindingCodec<Map<QName, Object>, Object> delegate) {
589             this.delegate = delegate;
590         }
591
592         @Override
593         public Node<?> serialize(final ValueWithQName<T> input) {
594             Map<QName, Object> intermediateOutput = delegate.serialize(input);
595             return IntermediateMapping.toNode(intermediateOutput);
596         }
597
598     }
599
600     private static class IdentifierCodecImpl<T extends Identifier<?>> //
601             extends IntermediateCodec<T> //
602             implements IdentifierCodec<T> {
603
604         public IdentifierCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
605             super(delegate);
606         }
607
608         @Override
609         public ValueWithQName<T> deserialize(final Node<?> input) {
610             QName qname = input.getNodeType();
611             @SuppressWarnings("unchecked")
612             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
613             return new ValueWithQName<T>(qname, value);
614         }
615
616         @Override
617         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
618             QName qname = input.getNodeType();
619             @SuppressWarnings("unchecked")
620             T value = (T) getDelegate().deserialize((Map<QName, Object>) input, bindingIdentifier);
621             return new ValueWithQName<T>(qname, value);
622         }
623
624         @Override
625         public CompositeNode serialize(final ValueWithQName<T> input) {
626             return (CompositeNode) super.serialize(input);
627         }
628     }
629
630     private static class DataContainerCodecImpl<T extends DataContainer> //
631             extends IntermediateCodec<T> //
632             implements DataContainerCodec<T> {
633
634         public DataContainerCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
635             super(delegate);
636         }
637
638         @Override
639         public ValueWithQName<T> deserialize(final Node<?> input) {
640             if (input == null) {
641                 return null;
642             }
643             QName qname = input.getNodeType();
644             @SuppressWarnings("unchecked")
645             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
646             return new ValueWithQName<T>(qname, value);
647         }
648
649         @Override
650         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
651             if (input == null) {
652                 return null;
653             }
654             QName qname = input.getNodeType();
655             @SuppressWarnings("unchecked")
656             T value = (T) getDelegate().deserialize((Map<QName, Object>) input, bindingIdentifier);
657             return new ValueWithQName<T>(qname, value);
658         }
659
660         @Override
661         public CompositeNode serialize(final ValueWithQName<T> input) {
662             return (CompositeNode) super.serialize(input);
663         }
664     }
665
666     private interface LocationAwareBindingCodec<P, I> extends BindingCodec<P, I> {
667
668         boolean isApplicable(InstanceIdentifier<?> location);
669
670         public Class<?> getDataType();
671
672     }
673
674     @SuppressWarnings("rawtypes")
675     private abstract class LocationAwareDispatchCodec<T extends LocationAwareBindingCodec> implements BindingCodec {
676
677         private final Map<Class, T> implementations = Collections.synchronizedMap(new WeakHashMap<Class, T>());
678         private final Set<InstanceIdentifier<?>> adaptedForPaths = new HashSet<>();
679
680         protected Map<Class, T> getImplementations() {
681             return implementations;
682         }
683
684         protected void addImplementation(final T implementation) {
685             implementations.put(implementation.getDataType(), implementation);
686         }
687
688         @Override
689         public final Object deserialize(final Object input) {
690             throw new UnsupportedOperationException("Invocation of deserialize without Tree location is unsupported");
691         }
692
693         @Override
694         public Object serialize(final Object input) {
695             Preconditions.checkArgument(input instanceof DataContainer);
696             Class<? extends DataContainer> inputType = ((DataContainer) input).getImplementedInterface();
697             T implementation = implementations.get(inputType);
698             if (implementation == null) {
699                 implementation = tryToLoadImplementationImpl(inputType);
700             }
701
702             return null;
703         }
704
705         private T tryToLoadImplementationImpl(final Class<? extends DataContainer> inputType) {
706             T implementation = tryToLoadImplementation(inputType);
707             Preconditions.checkArgument(implementation != null, "Data type %s is not supported.", inputType);
708             addImplementation(implementation);
709             return implementation;
710         }
711
712         protected final void adaptForPath(final InstanceIdentifier<?> path) {
713             if (adaptedForPaths.contains(path)) {
714                 return;
715             }
716             Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema, path);
717             if (contextNode.isPresent()) {
718                 synchronized (this) {
719                     adaptForPathImpl(path, contextNode.get());
720                     adaptedForPaths.add(path);
721                 }
722             } else {
723                 LOG.debug("Context node (parent node) not found for {}",path);
724             }
725         }
726
727         protected abstract T tryToLoadImplementation(Class<? extends DataContainer> inputType);
728
729         protected abstract void tryToLoadImplementations();
730
731         protected abstract void adaptForPathImpl(InstanceIdentifier<?> path, DataNodeContainer ctx);
732     }
733
734     @SuppressWarnings("rawtypes")
735     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
736             Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
737         private boolean augmenting;
738         private boolean uses;
739         private BindingCodec delegate;
740
741         private Set<String> validNames;
742         private Set<QName> validQNames;
743         private ChoiceCaseNode schema;
744         private Set<InstanceIdentifier<?>> applicableLocations;
745
746         @Override
747         public boolean isApplicable(final InstanceIdentifier location) {
748             return applicableLocations.contains(location);
749         }
750
751         public void setSchema(final ChoiceCaseNode caseNode) {
752             this.schema = caseNode;
753             validNames = new HashSet<>();
754             validQNames = new HashSet<>();
755             for (DataSchemaNode node : caseNode.getChildNodes()) {
756                 QName qname = node.getQName();
757                 validQNames.add(qname);
758                 validNames.add(qname.getLocalName());
759             }
760             augmenting = caseNode.isAugmenting();
761             uses = caseNode.isAddedByUses();
762             applicableLocations = new HashSet<>();
763         }
764
765         public ChoiceCaseCodecImpl() {
766             this.delegate = NOT_READY_CODEC;
767         }
768
769         public ChoiceCaseCodecImpl(final ChoiceCaseNode caseNode) {
770             this.delegate = NOT_READY_CODEC;
771             setSchema(caseNode);
772         }
773
774         @Override
775         public ValueWithQName<T> deserialize(final Node<?> input) {
776             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
777         }
778
779         @Override
780         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
781             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
782         }
783
784         @Override
785         public CompositeNode serialize(final ValueWithQName<T> input) {
786             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
787         }
788
789         @Override
790         public BindingCodec getDelegate() {
791             return delegate;
792         }
793
794         public void setDelegate(final BindingCodec delegate) {
795             this.delegate = delegate;
796         }
797
798         public ChoiceCaseNode getSchema() {
799             return schema;
800         }
801
802         @Override
803         public boolean isAcceptable(final Node<?> input) {
804             if (input instanceof CompositeNode) {
805                 if (augmenting && !uses) {
806                     return checkAugmenting((CompositeNode) input);
807                 } else {
808                     return checkLocal((CompositeNode) input);
809                 }
810             }
811             return false;
812         }
813
814         @SuppressWarnings("deprecation")
815         private boolean checkLocal(final CompositeNode input) {
816             QName parent = input.getNodeType();
817             for (Node<?> childNode : input.getChildren()) {
818                 QName child = childNode.getNodeType();
819                 if (!Objects.equals(parent.getNamespace(), child.getNamespace())
820                         || !Objects.equals(parent.getRevision(), child.getRevision())) {
821                     continue;
822                 }
823                 if (validNames.contains(child.getLocalName())) {
824                     return true;
825                 }
826             }
827             return false;
828         }
829
830         @SuppressWarnings("deprecation")
831         private boolean checkAugmenting(final CompositeNode input) {
832             for (Node<?> child : input.getChildren()) {
833                 if (validQNames.contains(child.getNodeType())) {
834                     return true;
835                 }
836             }
837             return false;
838         }
839
840         @Override
841         public Class<?> getDataType() {
842             // TODO Auto-generated method stub
843             throw new UnsupportedOperationException("Not implemented Yet.");
844         }
845     }
846
847     private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
848
849         private final BindingCodec<Map<QName, Object>, Object> delegate;
850
851         @SuppressWarnings("rawtypes")
852         private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
853                 .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
854
855         private final CaseCompositeNodeMapFacade CompositeToCase;
856
857         public PublicChoiceCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
858             this.delegate = delegate;
859             this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
860         }
861
862         @Override
863         public ValueWithQName<T> deserialize(final Node<?> input) {
864             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
865         }
866
867         @Override
868         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
869             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
870         }
871
872         @Override
873         public Node<?> serialize(final ValueWithQName<T> input) {
874             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
875         }
876
877         public CaseCompositeNodeMapFacade getCompositeToCase() {
878             return CompositeToCase;
879         }
880
881         @Override
882         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
883             return delegate;
884         }
885
886     }
887
888     @SuppressWarnings("unused")
889     private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
890
891         @Override
892         public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
893             // TODO Auto-generated method stub
894             return null;
895         }
896
897         @Override
898         public Object serialize(final Object input) {
899             // TODO Auto-generated method stub
900             return null;
901         }
902
903         @Override
904         protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
905             return getCaseCodecFor(inputType);
906         }
907
908         @Override
909         protected void tryToLoadImplementations() {
910             // TODO Auto-generated method stub
911
912         }
913
914         @Override
915         protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctx) {
916             // TODO Auto-generated method stub
917
918         }
919     }
920
921     @SuppressWarnings("rawtypes")
922     private class CaseClassMapFacade extends MapFacadeBase {
923
924         @Override
925         public Set<Entry<Class, BindingCodec<Object, Object>>> entrySet() {
926             return Collections.emptySet();
927         }
928
929         @Override
930         public BindingCodec get(final Object key) {
931             if (key instanceof Class) {
932                 Class cls = (Class) key;
933                 // bindingClassEncountered(cls);
934                 ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
935                 return caseCodec.getDelegate();
936             }
937             return null;
938         }
939     }
940
941     @SuppressWarnings("rawtypes")
942     private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
943
944         final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
945
946         public CaseCompositeNodeMapFacade(final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
947             this.choiceCases = choiceCases;
948         }
949
950         @Override
951         public BindingCodec get(final Object key) {
952             if (!(key instanceof CompositeNode)) {
953                 return null;
954             }
955             for (Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
956                 ChoiceCaseCodecImpl<?> codec = entry.getValue();
957                 if (codec.isAcceptable((CompositeNode) key)) {
958                     return codec.getDelegate();
959                 }
960             }
961             return null;
962         }
963
964     }
965
966     /**
967      * This map is used as only facade for
968      * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different
969      * classloaders to retrieve codec dynamicly based on provided key.
970      *
971      * @param <T>
972      *            Key type
973      */
974     @SuppressWarnings("rawtypes")
975     private abstract static class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
976
977         @Override
978         public boolean containsKey(final Object key) {
979             return get(key) != null;
980         }
981
982         @Override
983         public void clear() {
984             throw notModifiable();
985         }
986
987         @Override
988         public boolean equals(final Object obj) {
989             return super.equals(obj);
990         }
991
992         @Override
993         public BindingCodec remove(final Object key) {
994             return null;
995         }
996
997         @Override
998         public int size() {
999             return 0;
1000         }
1001
1002         @Override
1003         public Collection<BindingCodec<?, ?>> values() {
1004             return Collections.emptySet();
1005         }
1006
1007         private UnsupportedOperationException notModifiable() {
1008             return new UnsupportedOperationException("Not externally modifiable.");
1009         }
1010
1011         @Override
1012         public BindingCodec<Map<QName, Object>, Object> put(final T key, final BindingCodec<?, ?> value) {
1013             throw notModifiable();
1014         }
1015
1016         @Override
1017         public void putAll(final Map<? extends T, ? extends BindingCodec<?, ?>> m) {
1018             throw notModifiable();
1019         }
1020
1021         @Override
1022         public int hashCode() {
1023             return super.hashCode();
1024         }
1025
1026         @Override
1027         public boolean isEmpty() {
1028             return true;
1029         }
1030
1031         @Override
1032         public Set<T> keySet() {
1033             return Collections.emptySet();
1034         }
1035
1036         @Override
1037         public Set<Entry<T, BindingCodec<?, ?>>> entrySet() {
1038             return Collections.emptySet();
1039         }
1040
1041         @Override
1042         public boolean containsValue(final Object value) {
1043             return false;
1044         }
1045     }
1046
1047     @SuppressWarnings({ "rawtypes", "unchecked" })
1048     private class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
1049
1050         private final Class augmentableType;
1051
1052         public AugmentableDispatchCodec(final Class type) {
1053             Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
1054             augmentableType = type;
1055         }
1056
1057         @Override
1058         // TODO deprecate use without iid
1059         public Object serialize(final Object input) {
1060             if (input instanceof Augmentable<?>) {
1061                 Map<Class, Augmentation> augmentations = getAugmentations(input);
1062                 return serializeImpl(augmentations);
1063             }
1064             return null;
1065         }
1066
1067         private Map<Class, Augmentation> getAugmentations(final Object input) {
1068             Field augmentationField;
1069             try {
1070                 augmentationField = input.getClass().getDeclaredField("augmentation");
1071                 augmentationField.setAccessible(true);
1072                 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
1073                 return augMap;
1074             } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
1075                 LOG.debug("Could not read augmentations for {}", input, e);
1076             }
1077             return Collections.emptyMap();
1078         }
1079
1080         @SuppressWarnings("deprecation")
1081         private List serializeImpl(final Map<Class, Augmentation> input) {
1082             List ret = new ArrayList<>();
1083             for (Entry<Class, Augmentation> entry : input.entrySet()) {
1084                 AugmentationCodec codec = getCodecForAugmentation(entry.getKey());
1085                 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
1086                 ret.addAll(node.getChildren());
1087             }
1088             return ret;
1089         }
1090
1091         @Override
1092         public Map<Class, Augmentation> deserialize(final Object input, final InstanceIdentifier path) {
1093             adaptForPath(path);
1094             Map<Class, Augmentation> ret = new HashMap<>();
1095
1096             if (input instanceof CompositeNode) {
1097                 List<Entry<Class, AugmentationCodecWrapper>> codecs = new ArrayList<>(getImplementations().entrySet());
1098                 for (Entry<Class, AugmentationCodecWrapper> codec : codecs) {
1099                     AugmentationCodec<?> ac = codec.getValue();
1100                     if (ac.isAcceptable(path)) {
1101                         // We add Augmentation Identifier to path, in order to
1102                         // correctly identify children.
1103                         InstanceIdentifier augmentPath = path.builder().augmentation(codec.getKey()).build();
1104                         ValueWithQName<?> value = codec.getValue().deserialize((CompositeNode) input, augmentPath);
1105                         if (value != null && value.getValue() != null) {
1106                             ret.put(codec.getKey(), (Augmentation) value.getValue());
1107                         }
1108                     }
1109                 }
1110             }
1111             return ret;
1112         }
1113
1114         protected Optional<AugmentationCodecWrapper> tryToLoadImplementation(final Type potential) {
1115             try {
1116                 Class<? extends Augmentation<?>> clazz = (Class<? extends Augmentation<?>>) classLoadingStrategy
1117                         .loadClass(potential);
1118                 return Optional.of(tryToLoadImplementation(clazz));
1119             } catch (ClassNotFoundException e) {
1120                 LOG.warn("Failed to find class for augmentation of {}, reason: {}", potential, e.toString());
1121             }
1122             return Optional.absent();
1123         }
1124
1125         @Override
1126         protected AugmentationCodecWrapper tryToLoadImplementation(final Class inputType) {
1127             AugmentationCodecWrapper<? extends Augmentation<?>> potentialImpl = getCodecForAugmentation(inputType);
1128             addImplementation(potentialImpl);
1129             return potentialImpl;
1130
1131         }
1132
1133         @Override
1134         protected void tryToLoadImplementations() {
1135             Type type = referencedType(augmentableType);
1136             Collection<Type> potentialAugmentations;
1137             synchronized(augmentableToAugmentations) {
1138                 potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type));
1139             }
1140             for (Type potential : potentialAugmentations) {
1141                 try {
1142                     tryToLoadImplementation(potential);
1143                 } catch (CodeGenerationException e) {
1144                     LOG.warn("Failed to proactively generate augment coded for {}, reason: {}", type, e.toString());
1145                 }
1146             }
1147
1148         }
1149
1150         @Override
1151         protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctxNode) {
1152             if (ctxNode instanceof AugmentationTarget) {
1153                 Set<AugmentationSchema> availableAugmentations = ((AugmentationTarget) ctxNode)
1154                         .getAvailableAugmentations();
1155                 if (!availableAugmentations.isEmpty()) {
1156                     updateAugmentationMapping(path,availableAugmentations);
1157                 }
1158
1159             }
1160         }
1161
1162         private void updateAugmentationMapping(final InstanceIdentifier<?> path, final Set<AugmentationSchema> availableAugmentations) {
1163             for (AugmentationSchema aug : availableAugmentations) {
1164
1165                 Type potentialType = getTypeForAugmentation(aug);
1166                 if (potentialType != null) {
1167                     Optional<AugmentationCodecWrapper> potentialImpl = tryToLoadImplementation(potentialType);
1168                     if (potentialImpl.isPresent()) {
1169                         potentialImpl.get().addApplicableFor(path,aug);
1170                     }
1171                 } else {
1172                     LOG.warn("Could not find generated type for augmentation {} with childs {}.",aug,aug.getChildNodes());
1173                 }
1174             }
1175             availableAugmentations.toString();
1176         }
1177
1178         private Type getTypeForAugmentation(final AugmentationSchema aug) {
1179             Optional<AugmentationSchema> currentAug = Optional.of(aug);
1180             while(currentAug.isPresent()) {
1181                 Type potentialType = typeToAugment.inverse().get(currentAug.get());
1182                 if(potentialType != null) {
1183                     return potentialType;
1184                 }
1185                 currentAug = currentAug.get().getOriginalDefinition();
1186             }
1187             return null;
1188         }
1189     }
1190
1191     @SuppressWarnings({ "rawtypes", "unchecked" })
1192     private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
1193
1194         private BindingCodec delegate;
1195
1196         @Override
1197         public BindingCodec getDelegate() {
1198             if (delegate == null) {
1199                 throw new IllegalStateException("Codec not initialized yet.");
1200             }
1201             return delegate;
1202         }
1203
1204         @Override
1205         public Object deserialize(final Object input) {
1206             return getDelegate().deserialize(input);
1207         }
1208
1209         @Override
1210         public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
1211             return getDelegate().deserialize(input, bindingIdentifier);
1212         }
1213
1214         @Override
1215         public Object serialize(final Object input) {
1216             return getDelegate().serialize(input);
1217         }
1218
1219     }
1220
1221     @SuppressWarnings("rawtypes")
1222     private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
1223             Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
1224
1225         private final BindingCodec delegate;
1226         private final QName augmentationQName;
1227         private final Multimap<InstanceIdentifier<?>,QName> validAugmentationTargets;
1228         private final Class<?> augmentationType;
1229
1230         public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec,
1231                 final InstanceIdentifier<?> targetId, final Class<?> dataType) {
1232             this.delegate = rawCodec;
1233             this.augmentationType = dataType;
1234             this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
1235             this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap.<InstanceIdentifier<?>,QName>create());
1236         }
1237
1238         public void addApplicableFor(final InstanceIdentifier<?> path, final AugmentationSchema aug) {
1239             for(DataSchemaNode child : aug.getChildNodes()) {
1240                 validAugmentationTargets.put(path,child.getQName());
1241             }
1242         }
1243
1244         @Override
1245         public BindingCodec getDelegate() {
1246             return delegate;
1247         }
1248
1249         @Override
1250         public CompositeNode serialize(final ValueWithQName<T> input) {
1251             @SuppressWarnings("unchecked")
1252             List<Map<QName, Object>> rawValues = (List<Map<QName, Object>>) getDelegate().serialize(input);
1253             List<Node<?>> serialized = new ArrayList<>(rawValues.size());
1254             for (Map<QName, Object> val : rawValues) {
1255                 serialized.add(IntermediateMapping.toNode(val));
1256             }
1257             return new CompositeNodeTOImpl(input.getQname(), null, serialized);
1258         }
1259
1260         @Override
1261         @SuppressWarnings("unchecked")
1262         public ValueWithQName<T> deserialize(final Node<?> input) {
1263             Object rawCodecValue = getDelegate().deserialize(input);
1264             return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1265         }
1266
1267         @Override
1268         @SuppressWarnings("unchecked")
1269         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
1270             // if (!isAcceptable(bindingIdentifier)) {
1271             // return null;
1272             // }
1273             Object rawCodecValue = getDelegate().deserialize(input, bindingIdentifier);
1274             return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1275         }
1276
1277         @Override
1278         public QName getAugmentationQName() {
1279             return augmentationQName;
1280         }
1281
1282         @Override
1283         public boolean isAcceptable(final InstanceIdentifier<?> path) {
1284             if (path == null) {
1285                 return false;
1286             }
1287             return validAugmentationTargets.containsKey(path);
1288         }
1289
1290         @Override
1291         public boolean isApplicable(final InstanceIdentifier location) {
1292             return isAcceptable(location);
1293         }
1294
1295         @Override
1296         public Class<?> getDataType() {
1297             return augmentationType;
1298         }
1299     }
1300
1301     @SuppressWarnings("rawtypes")
1302     private class IdentityCompositeCodec implements IdentityCodec {
1303
1304         @Override
1305         public Object deserialize(final Object input) {
1306             Preconditions.checkArgument(input instanceof QName);
1307             return deserialize((QName) input);
1308         }
1309
1310         @Override
1311         public Class<?> deserialize(final QName input) {
1312             Type type = qnamesToIdentityMap.get(input);
1313             if (type == null) {
1314                 return null;
1315             }
1316             ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1317             WeakReference<Class> softref = typeToClass.get(typeref);
1318             if (softref == null) {
1319
1320                 try {
1321                     Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1322                     if (cls != null) {
1323                         serialize(cls);
1324                         return cls;
1325                     }
1326                 } catch (Exception e) {
1327                     LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1328                             typeref.getFullyQualifiedName());
1329                 }
1330                 return null;
1331             }
1332             return softref.get();
1333         }
1334
1335         @Override
1336         public Object deserialize(final Object input,final InstanceIdentifier bindingIdentifier) {
1337             Type type = qnamesToIdentityMap.get(input);
1338             if (type == null) {
1339                 return null;
1340             }
1341             ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1342             WeakReference<Class> softref = typeToClass.get(typeref);
1343             if (softref == null) {
1344
1345                 try {
1346                     Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1347                     if (cls != null) {
1348                         serialize(cls);
1349                         return cls;
1350                     }
1351                 } catch (Exception e) {
1352                     LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1353                             typeref.getFullyQualifiedName());
1354                 }
1355                 return null;
1356             }
1357             return softref.get();
1358         }
1359
1360         @Override
1361         public QName serialize(final Class input) {
1362             Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
1363             bindingClassEncountered(input);
1364             QName qname = identityQNames.get(input);
1365             if (qname != null) {
1366                 return qname;
1367             }
1368             ConcreteType typeref = Types.typeForClass(input);
1369             qname = typeToQname.get(typeref);
1370             if (qname != null) {
1371                 identityQNames.put(input, qname);
1372             }
1373             return qname;
1374         }
1375
1376         @Override
1377         public Object serialize(final Object input) {
1378             Preconditions.checkArgument(input instanceof Class);
1379             return serialize((Class) input);
1380         }
1381
1382     }
1383
1384     public boolean isCodecAvailable(final Class<? extends DataContainer> cls) {
1385         if (containerCodecs.containsKey(cls)) {
1386             return true;
1387         }
1388         if (identifierCodecs.containsKey(cls)) {
1389             return true;
1390         }
1391         if (choiceCodecs.containsKey(cls)) {
1392             return true;
1393         }
1394         if (caseCodecs.containsKey(cls)) {
1395             return true;
1396         }
1397         if (augmentableCodecs.containsKey(cls)) {
1398             return true;
1399         }
1400         if (augmentationCodecs.containsKey(cls)) {
1401             return true;
1402         }
1403         return false;
1404     }
1405
1406     public static final Type referencedType(final Class<?> augmentableType) {
1407         return new ReferencedTypeImpl(augmentableType.getPackage().getName(), augmentableType.getSimpleName());
1408     }
1409 }