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