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