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