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