Merge "BUG-1413: fixed bug in antlr grammar."
[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 com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import com.google.common.collect.BiMap;
16 import com.google.common.collect.HashBiMap;
17 import com.google.common.collect.HashMultimap;
18 import com.google.common.collect.Iterables;
19 import com.google.common.collect.Multimap;
20 import com.google.common.collect.Multimaps;
21
22 import java.lang.ref.WeakReference;
23 import java.lang.reflect.Field;
24 import java.util.AbstractMap.SimpleEntry;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Set;
35 import java.util.WeakHashMap;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ConcurrentMap;
38
39 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
40 import org.opendaylight.yangtools.binding.generator.util.Types;
41 import org.opendaylight.yangtools.concepts.Delegator;
42 import org.opendaylight.yangtools.concepts.Identifiable;
43 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
44 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException;
45 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
46 import org.opendaylight.yangtools.sal.binding.model.api.Type;
47 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
48 import org.opendaylight.yangtools.yang.binding.Augmentable;
49 import org.opendaylight.yangtools.yang.binding.Augmentation;
50 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
51 import org.opendaylight.yangtools.yang.binding.BindingCodec;
52 import org.opendaylight.yangtools.yang.binding.BindingMapping;
53 import org.opendaylight.yangtools.yang.binding.DataContainer;
54 import org.opendaylight.yangtools.yang.binding.DataObject;
55 import org.opendaylight.yangtools.yang.binding.Identifier;
56 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
57 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
58 import org.opendaylight.yangtools.yang.common.QName;
59 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
60 import org.opendaylight.yangtools.yang.data.api.Node;
61 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
62 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec;
63 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCaseCodec;
64 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCodec;
65 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
66 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec;
67 import org.opendaylight.yangtools.yang.data.impl.codec.DomCodec;
68 import org.opendaylight.yangtools.yang.data.impl.codec.IdentifierCodec;
69 import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
70 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
71 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
72 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
73 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
74 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
75 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
76 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
77 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
78 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
79 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
80 import org.opendaylight.yangtools.yang.model.api.Module;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
82 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
83 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
85 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88
89 class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener, GeneratorListener {
90
91     private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
92
93     // Concrete class to codecs
94     private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
95             .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
96     private static final Map<Class<?>, IdentifierCodec<?>> identifierCodecs = Collections
97             .synchronizedMap(new WeakHashMap<Class<?>, IdentifierCodec<?>>());
98     private static final Map<Class<?>, PublicChoiceCodecImpl<?>> choiceCodecs = Collections
99             .synchronizedMap(new WeakHashMap<Class<?>, PublicChoiceCodecImpl<?>>());
100     private static final Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = Collections
101             .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCaseCodecImpl<?>>());
102     private static final Map<Class<?>, AugmentableDispatchCodec> augmentableCodecs = Collections
103             .synchronizedMap(new WeakHashMap<Class<?>, AugmentableDispatchCodec>());
104     private static final Map<Class<?>, AugmentationCodecWrapper<?>> augmentationCodecs = Collections
105             .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodecWrapper<?>>());
106
107     private static final Map<Class<?>, LocationAwareDispatchCodec<?>> dispatchCodecs = Collections
108             .synchronizedMap(new WeakHashMap<Class<?>, LocationAwareDispatchCodec<?>>());
109
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     private static final ConcurrentMap<Type, ChoiceCaseNode> caseTypeToCaseSchema = new ConcurrentHashMap<>();
118
119     private static final Map<SchemaPath, Type> pathToType = new ConcurrentHashMap<>();
120     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
121     private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
122     private static final BiMap<Type, AugmentationSchema> typeToAugment = HashBiMap
123             .create(new ConcurrentHashMap<Type, AugmentationSchema>());
124
125     private static final Multimap<Type, Type> augmentableToAugmentations = Multimaps.synchronizedMultimap(HashMultimap
126             .<Type, Type> create());
127     private static final Multimap<Type, Type> choiceToCases = Multimaps.synchronizedMultimap(HashMultimap
128             .<Type, Type> create());
129
130     private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
131     private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
132     private final ClassLoadingStrategy classLoadingStrategy;
133     private final AbstractTransformerGenerator generator;
134     private final SchemaLock lock;
135
136     private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
137             CacheBuilder.newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader());
138
139     // FIXME: how is this protected?
140     private SchemaContext currentSchema;
141
142     LazyGeneratedCodecRegistry(final SchemaLock lock, final AbstractTransformerGenerator generator,
143             final ClassLoadingStrategy classLoadingStrategy) {
144         this.lock = Preconditions.checkNotNull(lock);
145         this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy);
146         this.generator = Preconditions.checkNotNull(generator);
147     }
148
149     public SchemaLock getLock() {
150         return lock;
151     }
152
153     @Override
154     public InstanceIdentifierCodec getInstanceIdentifierCodec() {
155         return instanceIdentifierCodec;
156     }
157
158     @SuppressWarnings("unchecked")
159     @Override
160     public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> augClass) {
161         AugmentationCodecWrapper<T> codec = null;
162         @SuppressWarnings("rawtypes")
163         AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(augClass);
164         if (potentialCodec != null) {
165             codec = potentialCodec;
166         } else {
167             lock.waitForSchema(augClass);
168             Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
169                     .augmentationTransformerFor(augClass);
170
171             BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(augmentRawCodec);
172             codec = new AugmentationCodecWrapper<T>(rawCodec, augClass);
173             augmentationCodecs.put(augClass, codec);
174         }
175
176         final Class<? extends Augmentable<?>> objectSupertype;
177         try {
178             objectSupertype = BindingReflections.findAugmentationTarget(augClass);
179         } catch (Exception e) {
180             LOG.warn("Failed to find target for augmentation {}, ignoring it", augClass, e);
181             return codec;
182         }
183
184         if (objectSupertype == null) {
185             LOG.warn("Augmentation target for {} not found, ignoring it", augClass);
186             return codec;
187         }
188
189         getAugmentableCodec(objectSupertype).addImplementation(codec);
190         return codec;
191     }
192
193     @SuppressWarnings("unchecked")
194     @Override
195     public QName getQNameForAugmentation(final Class<?> cls) {
196         Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
197         return getCodecForAugmentation((Class<? extends Augmentation<?>>) cls).getAugmentationQName();
198     }
199
200     @Override
201     public Class<?> getClassForPath(final List<QName> names) {
202         DataSchemaNode node = getSchemaNode(names);
203         Preconditions.checkArgument(node != null, "Path %s points to invalid schema location",names);
204         SchemaNode originalDefinition = SchemaNodeUtils.getRootOriginalIfPossible(node);
205         if(originalDefinition instanceof DataSchemaNode) {
206             node =(DataSchemaNode) originalDefinition;
207         }
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         if(weakRef != null) {
222             return weakRef.get();
223         }
224         try {
225             return classLoadingStrategy.loadClass(type);
226         } catch (ClassNotFoundException e) {
227             throw new IllegalStateException(String.format("Could not find loaded class for path: %s and type: %s", path,type.getFullyQualifiedName()));
228         }
229     }
230
231     @Override
232     public void putPathToClass(final List<QName> names, final Class<?> cls) {
233         final Type reference = Types.typeForClass(cls);
234         pathToInstantiatedType.put(names, reference);
235         LOG.trace("Path {} attached to class {} reference {}", names, cls, reference);
236         bindingClassEncountered(cls);
237     }
238
239     @Override
240     public IdentifierCodec<?> getKeyCodecForPath(final List<QName> names) {
241         @SuppressWarnings("unchecked")
242         Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
243         return getIdentifierCodecForIdentifiable(cls);
244     }
245
246     @Override
247     public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(final Class<T> type) {
248         @SuppressWarnings("unchecked")
249         DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
250         if (ret != null) {
251             return ret;
252         }
253         Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
254         BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
255         DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
256         containerCodecs.put(type, newWrapper);
257         return newWrapper;
258     }
259
260     @Override
261     @SuppressWarnings("rawtypes")
262     public void bindingClassEncountered(final Class cls) {
263
264         ConcreteType typeRef = Types.typeForClass(cls);
265         if (typeToClass.containsKey(typeRef)) {
266             return;
267         }
268         LOG.trace("Binding Class {} encountered.", cls);
269         WeakReference<Class> weakRef = new WeakReference<>(cls);
270         typeToClass.put(typeRef, weakRef);
271         if (Augmentation.class.isAssignableFrom(cls)) {
272             // Intentionally NOOP
273         } else if (DataObject.class.isAssignableFrom(cls)) {
274             getCodecForDataObject((Class<? extends DataObject>) cls);
275         }
276     }
277
278     @Override
279     public void onClassProcessed(final Class<?> cls) {
280         ConcreteType typeRef = Types.typeForClass(cls);
281         if (typeToClass.containsKey(typeRef)) {
282             return;
283         }
284         LOG.trace("Binding Class {} encountered.", cls);
285         @SuppressWarnings("rawtypes")
286         WeakReference<Class> weakRef = new WeakReference<Class>(cls);
287         typeToClass.put(typeRef, weakRef);
288     }
289
290     private DataSchemaNode getSchemaNode(final List<QName> path) {
291         QName firstNode = path.get(0);
292         DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
293                 firstNode.getRevision());
294         Preconditions.checkArgument(previous != null, "Failed to find module %s for path %s", firstNode, path);
295
296         Iterator<QName> iterator = path.iterator();
297         while (iterator.hasNext()) {
298             QName arg = iterator.next();
299             DataSchemaNode currentNode = previous.getDataChildByName(arg);
300             if (currentNode == null && previous instanceof DataNodeContainer) {
301                 currentNode = searchInChoices(previous, arg);
302             }
303             if (currentNode instanceof DataNodeContainer) {
304                 previous = (DataNodeContainer) currentNode;
305             } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
306                 Preconditions.checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
307                 return currentNode;
308             }
309         }
310         return (DataSchemaNode) previous;
311     }
312
313     private DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
314         for (DataSchemaNode child : node.getChildNodes()) {
315             if (child instanceof ChoiceNode) {
316                 ChoiceNode choiceNode = (ChoiceNode) child;
317                 DataSchemaNode potential = searchInCases(choiceNode, arg);
318                 if (potential != null) {
319                     return potential;
320                 }
321             }
322         }
323         return null;
324     }
325
326     private DataSchemaNode searchInCases(final ChoiceNode choiceNode, final QName arg) {
327         Set<ChoiceCaseNode> cases = choiceNode.getCases();
328         for (ChoiceCaseNode caseNode : cases) {
329             DataSchemaNode node = caseNode.getDataChildByName(arg);
330             if (node != null) {
331                 return node;
332             }
333         }
334         return null;
335     }
336
337     private static <T> T newInstanceOf(final Class<?> cls) {
338         try {
339             @SuppressWarnings("unchecked")
340             T ret = (T) cls.newInstance();
341             return ret;
342         } catch (InstantiationException e) {
343             LOG.error("Failed to instantiate codec {}", cls.getSimpleName(), e);
344             throw new IllegalStateException(String.format("Failed to instantiate codec %s", cls), e);
345         } catch (IllegalAccessException e) {
346             LOG.debug(
347                     "Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
348                     cls.getSimpleName(), e);
349             throw new IllegalStateException(String.format("Cannot access contructor of %s", cls), e);
350         }
351     }
352
353     @Override
354     public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class<T> type) {
355         IdentifierCodec<?> obj = identifierCodecs.get(type);
356         if (obj != null) {
357             return obj;
358         }
359         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
360                 .keyTransformerForIdentifiable(type);
361         BindingCodec<Map<QName, Object>, Object> newInstance;
362         newInstance = newInstanceOf(newCodec);
363         IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
364         identifierCodecs.put(type, newWrapper);
365         return newWrapper;
366     }
367
368     @Override
369     public IdentityCodec<?> getIdentityCodec() {
370         return identityRefCodec;
371     }
372
373     @SuppressWarnings("unchecked")
374     @Override
375     public <T extends BaseIdentity> IdentityCodec<T> getCodecForIdentity(final Class<T> codec) {
376         bindingClassEncountered(codec);
377         return identityRefCodec;
378     }
379
380     @Override
381     public void onCodecCreated(final Class<?> cls) {
382         CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
383         CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
384     }
385
386     @Override
387     public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> object) {
388         @SuppressWarnings("unchecked")
389         IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
390         if (obj != null) {
391             return obj;
392         }
393         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
394                 .keyTransformerForIdentifier(object);
395         BindingCodec<Map<QName, Object>, Object> newInstance;
396         newInstance = newInstanceOf(newCodec);
397         IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
398         identifierCodecs.put(object, newWrapper);
399         return newWrapper;
400     }
401
402     @SuppressWarnings("rawtypes")
403     public ChoiceCaseCodecImpl getCaseCodecFor(final Class caseClass) {
404         ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
405         if (potential != null) {
406             return potential;
407         }
408         ConcreteType typeref = Types.typeForClass(caseClass);
409         ChoiceCaseNode caseSchema = caseTypeToCaseSchema.get(typeref);
410
411         Preconditions.checkState(caseSchema != null, "Case schema is not available for %s", caseClass.getName());
412         Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseSchema);
413         BindingCodec newInstance = newInstanceOf(newCodec);
414         @SuppressWarnings("unchecked")
415         ChoiceCaseCodecImpl caseCodec = new ChoiceCaseCodecImpl(caseClass, caseSchema, newInstance);
416         caseCodecs.put(caseClass, caseCodec);
417         return caseCodec;
418     }
419
420     public void onModuleContextAdded(final SchemaContext schemaContext, final Module module, final ModuleContext context) {
421         pathToType.putAll(context.getChildNodes());
422
423         BiMap<Type, AugmentationSchema> bimap = context.getTypeToAugmentation();
424         for (Map.Entry<Type, AugmentationSchema> entry : bimap.entrySet()) {
425             Type key = entry.getKey();
426             AugmentationSchema value = entry.getValue();
427             Collection<DataSchemaNode> augmentedNodes = value.getChildNodes();
428             if (augmentedNodes != null && !augmentedNodes.isEmpty()) {
429                 typeToAugment.put(key, value);
430             }
431         }
432
433         qnamesToIdentityMap.putAll(context.getIdentities());
434         for (Entry<QName, GeneratedTOBuilder> identity : context.getIdentities().entrySet()) {
435             typeToQname.put(
436                     new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),
437                     identity.getKey());
438         }
439
440         synchronized (augmentableToAugmentations) {
441             augmentableToAugmentations.putAll(context.getAugmentableToAugmentations());
442         }
443         synchronized (choiceToCases) {
444             choiceToCases.putAll(context.getChoiceToCases());
445         }
446         synchronized (caseTypeToCaseSchema) {
447             caseTypeToCaseSchema.putAll(context.getCaseTypeToSchemas());
448         }
449     }
450
451     @Override
452     public void onGlobalContextUpdated(final SchemaContext context) {
453         currentSchema = context;
454         resetDispatchCodecsAdaptation();
455
456     }
457
458     /**
459      * Resets / clears adaptation for all schema context sensitive codecs in
460      * order for them to adapt to new schema context and maybe newly discovered
461      * augmentations This ensure correct behaviour for augmentations and
462      * augmented cases for preexisting codecs, which augmentations were
463      * introduced at later point in time.
464      *
465      * This also makes removed augmentations unavailable.
466      */
467     private void resetDispatchCodecsAdaptation() {
468         synchronized (dispatchCodecs) {
469             for (LocationAwareDispatchCodec<?> codec : dispatchCodecs.values()) {
470                 codec.resetCodec(this);
471             }
472         }
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         DispatchChoiceCodecImpl dispatchCodec = new DispatchChoiceCodecImpl(choiceClass,this);
484         choiceCodecs.put(choiceClass, newCodec);
485         synchronized (dispatchCodecs) {
486             dispatchCodecs.put(choiceClass, dispatchCodec);
487         }
488         CodecMapping.setDispatchCodec(choiceCodec, dispatchCodec);
489     }
490
491     @Override
492     public void onValueCodecCreated(final Class<?> valueClass, final Class<?> valueCodec) {
493     }
494
495     @Override
496     public void onCaseCodecCreated(final Class<?> choiceClass,
497             final Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
498     }
499
500     @Override
501     public void onDataContainerCodecCreated(final Class<?> dataClass,
502             final Class<? extends BindingCodec<?, ?>> dataCodec) {
503         if (Augmentable.class.isAssignableFrom(dataClass)) {
504             AugmentableDispatchCodec augmentableCodec = getAugmentableCodec(dataClass);
505             CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
506         }
507     }
508
509     public synchronized AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
510         AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass);
511         if (ret != null) {
512             return ret;
513         }
514         ret = new AugmentableDispatchCodec(dataClass,this);
515         augmentableCodecs.put(dataClass, ret);
516         synchronized (dispatchCodecs) {
517             dispatchCodecs.put(dataClass, ret);
518         }
519         ret.tryToLoadImplementations();
520         return ret;
521     }
522
523     private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
524         private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
525             @Override
526             Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
527                 return Collections.emptyMap();
528             }
529         };
530
531         @Override
532         public AugmentationFieldGetter load(final Class<?> key) throws Exception {
533             Field field;
534             try {
535                 field = key.getDeclaredField("augmentation");
536             } catch (NoSuchFieldException | SecurityException e) {
537                 LOG.debug("Failed to acquire augmentation field", e);
538                 return DUMMY;
539             }
540             field.setAccessible(true);
541
542             return new ReflectionAugmentationFieldGetter(field);
543         }
544     }
545
546     private static abstract class AugmentationFieldGetter {
547         abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input);
548     }
549
550     private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter {
551         private final Field augmentationField;
552
553         ReflectionAugmentationFieldGetter(final Field augmentationField) {
554             this.augmentationField = Preconditions.checkNotNull(augmentationField);
555         }
556
557         @Override
558         @SuppressWarnings("unchecked")
559         Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
560             try {
561                 return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) augmentationField.get(input);
562             } catch (IllegalArgumentException | IllegalAccessException e) {
563                 throw new IllegalStateException("Failed to access augmentation field", e);
564             }
565         }
566     }
567
568     private static abstract class IntermediateCodec<T> implements DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
569
570         private final BindingCodec<Map<QName, Object>, Object> delegate;
571
572         @Override
573         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
574             return delegate;
575         }
576
577         public IntermediateCodec(final BindingCodec<Map<QName, Object>, Object> delegate) {
578             this.delegate = delegate;
579         }
580
581         @Override
582         public Node<?> serialize(final ValueWithQName<T> input) {
583             Map<QName, Object> intermediateOutput = delegate.serialize(input);
584             return IntermediateMapping.toNode(intermediateOutput);
585         }
586
587     }
588
589     private static class IdentifierCodecImpl<T extends Identifier<?>> extends IntermediateCodec<T> implements IdentifierCodec<T> {
590
591         public IdentifierCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
592             super(delegate);
593         }
594
595         @Override
596         public ValueWithQName<T> deserialize(final Node<?> input) {
597             QName qname = input.getNodeType();
598             @SuppressWarnings("unchecked")
599             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
600             return new ValueWithQName<T>(qname, value);
601         }
602
603         @Override
604         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
605             QName qname = input.getNodeType();
606             @SuppressWarnings("unchecked")
607             T value = (T) getDelegate().deserialize((Map<QName, Object>) input, bindingIdentifier);
608             return new ValueWithQName<T>(qname, value);
609         }
610
611         @Override
612         public CompositeNode serialize(final ValueWithQName<T> input) {
613             return (CompositeNode) super.serialize(input);
614         }
615     }
616
617     private static class DataContainerCodecImpl<T extends DataContainer> extends IntermediateCodec<T> implements DataContainerCodec<T> {
618
619         public DataContainerCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
620             super(delegate);
621         }
622
623         @Override
624         public ValueWithQName<T> deserialize(final Node<?> input) {
625             if (input == null) {
626                 return null;
627             }
628             QName qname = input.getNodeType();
629             @SuppressWarnings("unchecked")
630             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
631             return new ValueWithQName<T>(qname, value);
632         }
633
634         @Override
635         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
636             if (input == null) {
637                 return null;
638             }
639             QName qname = input.getNodeType();
640             @SuppressWarnings("unchecked")
641             T value = (T) getDelegate().deserialize((Map<QName, Object>) input, bindingIdentifier);
642             return new ValueWithQName<T>(qname, value);
643         }
644
645         @Override
646         public CompositeNode serialize(final ValueWithQName<T> input) {
647             return (CompositeNode) super.serialize(input);
648         }
649     }
650
651     private interface LocationAwareBindingCodec<P, I> extends BindingCodec<P, I> {
652
653         boolean isApplicable(InstanceIdentifier<?> parentPath, CompositeNode data);
654
655         public Class<?> getDataType();
656
657     }
658
659     @SuppressWarnings("rawtypes")
660     private static abstract class LocationAwareDispatchCodec<T extends LocationAwareBindingCodec> implements BindingCodec {
661
662         private final Map<Class, T> implementations = Collections.synchronizedMap(new WeakHashMap<Class, T>());
663         private final Set<InstanceIdentifier<?>> adaptedForPaths = new HashSet<>();
664         private LazyGeneratedCodecRegistry registry;
665
666
667         protected LocationAwareDispatchCodec(final LazyGeneratedCodecRegistry registry) {
668             this.registry = registry;
669         }
670
671         protected Map<Class, T> getImplementations() {
672             return implementations;
673         }
674
675         /**
676          * Resets codec adaptation based on location and schema context.
677          *
678          * This is required if new cases / augmentations were introduced or
679          * removed and first use of codec is triggered by invocation from DOM to
680          * Java, so the implementations may change and this may require loading
681          * of new codecs and/or removal of existing ones.
682          *
683          */
684         public synchronized void resetCodec(final LazyGeneratedCodecRegistry currentRegistry) {
685             registry = currentRegistry;
686             adaptedForPaths.clear();
687             resetAdaptationImpl();
688         }
689
690         protected void resetAdaptationImpl() {
691             // Intentionally NOOP, subclasses may specify their custom
692             // behaviour.
693         }
694
695         protected final LazyGeneratedCodecRegistry getRegistry() {
696             return registry;
697         }
698         protected void addImplementation(final T implementation) {
699             implementations.put(implementation.getDataType(), implementation);
700         }
701
702         @Override
703         public final Object deserialize(final Object input) {
704             throw new UnsupportedOperationException("Invocation of deserialize without Tree location is unsupported");
705         }
706
707         @Override
708         public final Object deserialize(final Object parent, final InstanceIdentifier parentPath) {
709             adaptForPath(parentPath);
710             Preconditions.checkArgument(parent instanceof CompositeNode, "node must be of CompositeNode type.");
711             CompositeNode parentData = (CompositeNode) parent;
712             ArrayList<T> applicable = new ArrayList<>(implementations.size());
713
714             /*
715              * Codecs are filtered to only ones, which
716              * are applicable in supplied parent context.
717              *
718              */
719             for (T impl : getImplementations().values()) {
720                 @SuppressWarnings("unchecked")
721                 boolean codecApplicable = impl.isApplicable(parentPath, parentData);
722                 if (codecApplicable) {
723                     applicable.add(impl);
724                 }
725             }
726             LOG.trace("{}: Deserializing mixins from {}, Schema Location {}, Applicable Codecs: {}, All Codecs: {}",this,parent,parentPath,applicable,getImplementations().values());
727
728             /* In case of none is applicable, we return
729              * null. Since there is no mixin which
730              * is applicable in this location.
731              */
732             if(applicable.isEmpty()) {
733                 return null;
734             }
735             return deserializeImpl(parentData, parentPath, applicable);
736         }
737
738         protected abstract Object deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> parentPath,
739                 Iterable<T> applicableCodecs);
740
741         @Override
742         public Object serialize(final Object input) {
743             Preconditions.checkArgument(input instanceof DataContainer);
744             Class<? extends DataContainer> inputType = ((DataContainer) input).getImplementedInterface();
745             T implementation = implementations.get(inputType);
746             if (implementation == null) {
747                 implementation = tryToLoadImplementationImpl(inputType);
748             }
749
750             return null;
751         }
752
753         private T tryToLoadImplementationImpl(final Class<? extends DataContainer> inputType) {
754             T implementation = tryToLoadImplementation(inputType);
755             Preconditions.checkArgument(implementation != null, "Data type %s is not supported.", inputType);
756             addImplementation(implementation);
757             return implementation;
758         }
759
760         protected final synchronized void adaptForPath(final InstanceIdentifier<?> path) {
761             if (adaptedForPaths.contains(path)) {
762                 return;
763             }
764             LOG.debug("Adapting mixin codec {} for path {}",this,path);
765             /**
766              * We search in schema context if the use of this location aware
767              * codec (augmentable codec, case codec) makes sense on provided
768              * location (path)
769              *
770              */
771             Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(getRegistry().currentSchema,
772                     path);
773             /**
774              * If context node is present, this codec makes sense on provided
775              * location.
776              *
777              */
778             if (contextNode.isPresent()) {
779                 synchronized (this) {
780                     /**
781                      *
782                      * We adapt (turn on / off) possible implementations of
783                      * child codecs (augmentations, cases) based on this
784                      * location.
785                      *
786                      *
787                      */
788
789                     adaptForPathImpl(path, contextNode.get());
790                     try {
791                         /**
792                          * We trigger serialization of instance identifier, to
793                          * make sure instance identifier codec is aware of
794                          * combination of this path / augmentation / case
795                          */
796                         getRegistry().getInstanceIdentifierCodec().serialize(path);
797                     } catch (Exception e) {
798                         LOG.warn("Exception during preparation of instance identifier codec for  path {}.", path, e);
799                     }
800                     adaptedForPaths.add(path);
801                 }
802             } else {
803                 LOG.debug("Context node (parent node) not found for {}", path);
804             }
805         }
806
807         protected abstract T tryToLoadImplementation(Class<? extends DataContainer> inputType);
808
809         protected abstract void tryToLoadImplementations();
810
811         protected abstract void adaptForPathImpl(InstanceIdentifier<?> path, DataNodeContainer ctx);
812     }
813
814     @SuppressWarnings("rawtypes")
815     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
816     Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
817         private final BindingCodec delegate;
818         private final ChoiceCaseNode schema;
819         private final Map<InstanceIdentifier<?>, ChoiceCaseNode> instantiatedLocations;
820         private final Class<?> dataType;
821
822         public ChoiceCaseCodecImpl(final Class<?> caseClass, final ChoiceCaseNode caseNode,
823                 final BindingCodec newInstance) {
824             this.delegate = newInstance;
825             this.dataType = caseClass;
826             this.schema = caseNode;
827             instantiatedLocations = new HashMap<>();
828         }
829
830         @Override
831         public ValueWithQName<T> deserialize(final Node<?> input) {
832             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
833         }
834
835         @Override
836         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
837             if (input == null) {
838                 return null;
839             }
840             QName qname = input.getNodeType();
841             synchronized (instantiatedLocations) {
842                 ChoiceCaseNode instantiation = instantiatedLocations.get(bindingIdentifier);
843                 if (instantiation != null) {
844                     qname = instantiatedLocations.get(bindingIdentifier).getQName();
845                 }
846             }
847             @SuppressWarnings("unchecked")
848             T value = (T) getDelegate().deserialize(new SimpleEntry(qname, input), bindingIdentifier);
849             return new ValueWithQName<T>(qname, value);
850         }
851
852         @Override
853         public CompositeNode serialize(final ValueWithQName<T> input) {
854             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
855         }
856
857         @Override
858         public BindingCodec getDelegate() {
859             return delegate;
860         }
861
862         public ChoiceCaseNode getSchema() {
863             return schema;
864         }
865
866         @Override
867         @Deprecated
868         public boolean isAcceptable(final Node<?> input) {
869             return checkAgainstSchema(schema, input);
870         }
871
872         private static boolean checkAgainstSchema(final ChoiceCaseNode schema, final Node<?> node) {
873             if (node instanceof CompositeNode) {
874                 CompositeNode input = (CompositeNode) node;
875                 for (Node<?> childNode : input.getValue()) {
876                     QName child = childNode.getNodeType();
877                     if (schema.getDataChildByName(child) != null) {
878                         return true;
879                     }
880                 }
881             }
882             return false;
883         }
884
885         @Override
886         public Class<?> getDataType() {
887             return dataType;
888         }
889
890         public void adaptForPath(final InstanceIdentifier<?> augTarget, final ChoiceCaseNode choiceCaseNode) {
891             synchronized (instantiatedLocations) {
892                 instantiatedLocations.put(augTarget, choiceCaseNode);
893             }
894         }
895
896         @Override
897         public boolean isApplicable(final InstanceIdentifier path, final CompositeNode input) {
898             ChoiceCaseNode instantiatedSchema = null;
899             synchronized (instantiatedLocations) {
900                 instantiatedSchema = instantiatedLocations.get(path);
901             }
902             if (instantiatedSchema == null) {
903                 return false;
904             }
905             return checkAgainstSchema(instantiatedSchema, input);
906         }
907
908         protected boolean isAugmenting(final QName choiceName, final QName proposedQName) {
909             if (schema.isAugmenting()) {
910                 return true;
911             }
912             // Choice QName
913             QName parentQName = Iterables.get(schema.getPath().getPathTowardsRoot(), 1);
914             if (!parentQName.getNamespace().equals(schema.getQName().getNamespace())) {
915                 return true;
916             }
917             if (!parentQName.equals(choiceName)) {
918                 // This item is instantiation of choice via uses in other YANG
919                 // module
920                 if (choiceName.getNamespace().equals(schema.getQName())) {
921                     // Original definition of grouping is in same namespace
922                     // as original definition of case
923                     // so for sure case is introduced via instantiation of
924                     // grouping
925                     return false;
926                 }
927                 // Since we are still here, that means case has same namespace
928                 // as its parent, which is instantiation of grouping
929                 // but case namespace is different from parent node
930                 // so it is augmentation.
931                 return true;
932             }
933             return false;
934         }
935
936         @Override
937         public String toString() {
938             return "ChoiceCaseCodec [case=" + dataType
939                     + ", knownLocations=" + instantiatedLocations.keySet() + "]";
940         }
941     }
942
943     private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
944
945         private final BindingCodec<Map<QName, Object>, Object> delegate;
946
947         public PublicChoiceCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
948             this.delegate = delegate;
949         }
950
951         @Override
952         public ValueWithQName<T> deserialize(final Node<?> input) {
953             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
954         }
955
956         @Override
957         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
958             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
959         }
960
961         @Override
962         public Node<?> serialize(final ValueWithQName<T> input) {
963             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
964         }
965
966         @Override
967         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
968             return delegate;
969         }
970
971     }
972
973     class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
974         private final Class<?> choiceType;
975         private final QName choiceName;
976
977         private DispatchChoiceCodecImpl(final Class<?> type, final LazyGeneratedCodecRegistry registry) {
978             super(registry);
979             choiceType = type;
980             choiceName = BindingReflections.findQName(type);
981         }
982
983         @Override
984         public Object deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> path,
985                 final Iterable<ChoiceCaseCodecImpl<?>> codecs) {
986             ChoiceCaseCodecImpl<?> caseCodec = Iterables.getOnlyElement(codecs);
987             ValueWithQName<?> value = caseCodec.deserialize(input, path);
988             if (value != null) {
989                 return value.getValue();
990             }
991             return null;
992         }
993
994         @SuppressWarnings("unchecked")
995         @Override
996         public Object serialize(final Object input) {
997             Preconditions.checkArgument(input instanceof Map.Entry<?, ?>, "Input must be QName, Value");
998             @SuppressWarnings("rawtypes")
999             QName derivedQName = (QName) ((Map.Entry) input).getKey();
1000             @SuppressWarnings("rawtypes")
1001             Object inputValue = ((Map.Entry) input).getValue();
1002             Preconditions.checkArgument(inputValue instanceof DataObject);
1003             Class<? extends DataContainer> inputType = ((DataObject) inputValue).getImplementedInterface();
1004             ChoiceCaseCodecImpl<?> codec = tryToLoadImplementation(inputType);
1005             Preconditions.checkState(codec != null, "Unable to get codec for %s", inputType);
1006             if (codec.isAugmenting(choiceName, derivedQName)) {
1007                 // If choice is augmenting we use QName which defined this
1008                 // augmentation
1009                 return codec.getDelegate().serialize(new ValueWithQName<>(codec.getSchema().getQName(), inputValue));
1010             }
1011             return codec.getDelegate().serialize(input);
1012         }
1013
1014         @SuppressWarnings("rawtypes")
1015         protected Optional<ChoiceCaseCodecImpl> tryToLoadImplementation(final Type potential) {
1016             try {
1017                 @SuppressWarnings("unchecked")
1018                 Class<? extends DataContainer> clazz = (Class<? extends DataContainer>) classLoadingStrategy
1019                 .loadClass(potential);
1020                 ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz);
1021                 addImplementation(codec);
1022                 return Optional.of(codec);
1023             } catch (ClassNotFoundException e) {
1024                 LOG.warn("Failed to find class for choice {}", potential, e);
1025             }
1026             return Optional.absent();
1027         }
1028
1029         @Override
1030         protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
1031             ChoiceCaseCodecImpl<?> codec = getCaseCodecFor(inputType);
1032             addImplementation(codec);
1033             return codec;
1034         }
1035
1036         @Override
1037         protected void tryToLoadImplementations() {
1038             Type type = referencedType(choiceType);
1039             Collection<Type> potentialCases;
1040             synchronized (choiceToCases) {
1041                 potentialCases = choiceToCases.get(type);
1042             }
1043             for (Type potential : potentialCases) {
1044                 try {
1045                     tryToLoadImplementation(potential);
1046                 } catch (CodeGenerationException e) {
1047                     LOG.warn("Failed to proactively generate choice code for {}", type, e);
1048                 }
1049             }
1050         }
1051
1052         @Override
1053         protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
1054             Optional<ChoiceNode> newChoice = BindingSchemaContextUtils.findInstantiatedChoice(ctxNode, choiceType);
1055             tryToLoadImplementations();
1056             Preconditions.checkState(newChoice.isPresent(), "BUG: Unable to find instantiated choice node in schema.");
1057             for (@SuppressWarnings("rawtypes")
1058             Entry<Class, ChoiceCaseCodecImpl<?>> codec : getImplementations().entrySet()) {
1059                 ChoiceCaseCodecImpl<?> caseCodec = codec.getValue();
1060                 Optional<ChoiceCaseNode> instantiatedSchema = BindingSchemaContextUtils.findInstantiatedCase(newChoice.get(),
1061                         caseCodec.getSchema());
1062                 if (instantiatedSchema.isPresent()) {
1063                     caseCodec.adaptForPath(augTarget, instantiatedSchema.get());
1064                 }
1065             }
1066         }
1067
1068
1069
1070         @Override
1071         public String toString() {
1072             return "DispatchChoiceCodecImpl [choiceType=" + choiceType + "]";
1073         }
1074     }
1075
1076     /**
1077      *
1078      * Dispatch codec for augmented object, which processes augmentations
1079      * <p>
1080      * This codec is used from DataObject codec generated using
1081      * {@link TransformerGenerator#transformerFor(Class)} and is wired
1082      * during {@link LazyGeneratedCodecRegistry#onDataContainerCodecCreated(Class, Class)}.
1083      * <p>
1084      * Instance of this codec is associated with class of Binding DTO which
1085      * represents target for augmentations.
1086      *
1087      */
1088     @SuppressWarnings({ "rawtypes", "unchecked" })
1089     static class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
1090
1091         private final Class augmentableType;
1092
1093         /**
1094          * Construct augmetable dispatch codec.
1095          *
1096          * @param type Class representing augmentation target
1097          * @param registry Registry with which this codec is associated.
1098          */
1099         public AugmentableDispatchCodec(final Class type, final LazyGeneratedCodecRegistry registry) {
1100             super(registry);
1101             Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
1102             augmentableType = type;
1103         }
1104
1105
1106
1107         /**
1108          * Serializes object to list of values which needs to be injected
1109          * into resulting DOM Node. Injection of data to parent DOM Node
1110          * is handled by caller (in this case generated codec).
1111          *
1112          * TODO: Deprecate use of augmentation codec without instance
1113          *       instance identifier
1114          *
1115          * @return list of nodes, which needs to be added to parent node.
1116          *
1117          */
1118         @Override
1119         public Object serialize(final Object input) {
1120             Preconditions.checkArgument(augmentableType.isInstance(input), "Object %s is not instance of %s ",input,augmentableType);
1121             if (input instanceof Augmentable<?>) {
1122                 Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations = getAugmentations(input);
1123                 return serializeImpl(augmentations);
1124             }
1125             return null;
1126         }
1127
1128         /**
1129          *
1130          * Extracts augmentation from Binding DTO field using reflection
1131          *
1132          * @param input Instance of DataObject which is augmentable and
1133          *      may contain augmentation
1134          * @return Map of augmentations if read was successful, otherwise
1135          *      empty map.
1136          */
1137         private Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
1138             return AUGMENTATION_GETTERS.getUnchecked(input.getClass()).getAugmentations(input);
1139         }
1140
1141         /**
1142          *
1143          * Serialization of augmentations, returns list of composite nodes,
1144          * which needs to be injected to parent node.
1145          *
1146          * @param input Map of classes to augmentations
1147          * @return List of nodes, which should be added to parent node.
1148          */
1149         private List serializeImpl(final Map<Class<? extends Augmentation<?>>, Augmentation<?>> input) {
1150             List ret = new ArrayList<>();
1151             for (Entry<Class<? extends Augmentation<?>>, Augmentation<?>> entry : input.entrySet()) {
1152                 AugmentationCodec codec = getRegistry().getCodecForAugmentation(entry.getKey());
1153                 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
1154                 ret.addAll(node.getValue());
1155             }
1156             return ret;
1157         }
1158
1159         /**
1160          *
1161          * Deserialization of augmentation which is location aware.
1162          *
1163          * Note: In case of composite nodes as an input, each codec
1164          * is invoked since there is no augmentation identifier
1165          * and we need to look for concrete classes.
1166          * FIXME: Maybe faster variation will be by extending
1167          * {@link AugmentationCodecWrapper} to look for particular QNames,
1168          * which will filter incoming set of codecs.
1169          *
1170          *
1171          * @param input Input representation of data
1172          * @param path Wildcarded instance identifier representing location of augmentation parent
1173          *       in conceptual schema tree
1174          * @param codecs Set of codecs which are applicable for supplied <code>path</code>,
1175          *       selected by caller to be used by deserialization
1176          *
1177          *
1178          */
1179         @Override
1180         public Map<Class, Augmentation> deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> path,
1181                 final Iterable<AugmentationCodecWrapper> codecs) {
1182             LOG.trace("{}: Going to deserialize augmentations from {} in location {}. Available codecs {}",this,input,path,codecs);
1183             Map<Class, Augmentation> ret = new HashMap<>();
1184             for (AugmentationCodecWrapper codec : codecs) {
1185                 // We add Augmentation Identifier to path, in order to
1186                 // correctly identify children.
1187                 Class type = codec.getDataType();
1188                 final InstanceIdentifier augmentPath = path.augmentation(type);
1189                 ValueWithQName<?> value = codec.deserialize(input, augmentPath);
1190                 if (value != null && value.getValue() != null) {
1191                     ret.put(type, (Augmentation) value.getValue());
1192                 }
1193             }
1194             return ret;
1195         }
1196
1197         /**
1198          *
1199          * Tries to load implementation of concrete augmentation codec for supplied type
1200          *
1201          * Loading of codec may fail, because of supplied type may not be visible
1202          * by classloaders known by registry. If class was not found returns {@link Optional#absent()}.
1203          *
1204          * @param potential Augmentation class identifier for which codecs should be loaded.
1205          * @return Optional with codec for supplied type
1206          *
1207          */
1208         protected Optional<AugmentationCodecWrapper> tryToLoadImplementation(final Type potential) {
1209             try {
1210                 Class<? extends Augmentation<?>> clazz = (Class<? extends Augmentation<?>>) getRegistry().classLoadingStrategy
1211                         .loadClass(potential);
1212                 return Optional.of(tryToLoadImplementation(clazz));
1213             } catch (ClassNotFoundException e) {
1214                 LOG.warn("Failed to find class for augmentation of {}", potential, e);
1215             }
1216             return Optional.absent();
1217         }
1218
1219         @Override
1220         protected AugmentationCodecWrapper tryToLoadImplementation(final Class inputType) {
1221             AugmentationCodecWrapper<? extends Augmentation<?>> potentialImpl = getRegistry().getCodecForAugmentation(inputType);
1222             addImplementation(potentialImpl);
1223             return potentialImpl;
1224         }
1225
1226         @Override
1227         protected void tryToLoadImplementations() {
1228             Type type = referencedType(augmentableType);
1229             Collection<Type> potentialAugmentations;
1230             synchronized (augmentableToAugmentations) {
1231                 potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type));
1232             }
1233             for (Type potential : potentialAugmentations) {
1234                 try {
1235                     tryToLoadImplementation(potential);
1236                 } catch (CodeGenerationException e) {
1237                     LOG.warn("Failed to proactively generate augment code for {}", type, e);
1238                 }
1239             }
1240         }
1241
1242         @Override
1243         protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
1244             if (ctxNode instanceof AugmentationTarget) {
1245                 Set<AugmentationSchema> availableAugmentations = ((AugmentationTarget) ctxNode)
1246                         .getAvailableAugmentations();
1247                 if (!availableAugmentations.isEmpty()) {
1248                     updateAugmentationMapping(augTarget, availableAugmentations);
1249                 }
1250             }
1251         }
1252
1253         /**
1254          *
1255          * Adapts augmentation codec for specific provider location (target)
1256          *
1257          * Since augmentation are not forward-referencing and may be discovered
1258          * during runtime, we need to adapt {@link AugmentableDispatchCodec},
1259          * {@link AugmentationCodecWrapper} and {@link InstanceIdentifierCodec}
1260          * for this newly discovered location where augmentation may be used.
1261          *
1262          * Adaptation consists of:
1263          * <ol>
1264          * <li>scan of available (valid) augmentations for current location
1265          * <li>lookup for Java classes derived from this augmentations
1266          * <li>generation of missing codecs
1267          * <li>updating Augmentation codecs to work with new location
1268          * <li>updating Instance Identifier to work with new location
1269          *
1270          */
1271         private void updateAugmentationMapping(final InstanceIdentifier<?> augTarget,
1272                 final Set<AugmentationSchema> availableAugmentations) {
1273             for (AugmentationSchema aug : availableAugmentations) {
1274
1275                 Type potentialType = getTypeForAugmentation(aug);
1276                 if (potentialType != null) {
1277                     Optional<AugmentationCodecWrapper> potentialImpl = tryToLoadImplementation(potentialType);
1278                     if (potentialImpl.isPresent()) {
1279                         potentialImpl.get().addApplicableFor(augTarget, aug);
1280                         Class augType = potentialImpl.get().getDataType();
1281                         InstanceIdentifier augPath = augTarget.augmentation(augType);
1282                         try {
1283
1284                             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = getRegistry().getInstanceIdentifierCodec()
1285                                     .serialize(augPath);
1286                             if (domPath == null) {
1287                                 LOG.error("Unable to serialize instance identifier for {}", augPath);
1288                             }
1289                         } catch (Exception e) {
1290                             LOG.error("Unable to serialize instance identifiers for {}", augPath, e);
1291                         }
1292
1293                     }
1294                 } else {
1295                     // Omits warning for empty augmentations since they are not
1296                     // represented in data
1297                     if (!aug.getChildNodes().isEmpty()) {
1298                         LOG.warn("Could not find generated type for augmentation {} with children {}", aug,
1299                                 aug.getChildNodes());
1300                     }
1301                 }
1302             }
1303         }
1304
1305         private Type getTypeForAugmentation(final AugmentationSchema aug) {
1306             Optional<AugmentationSchema> currentAug = Optional.of(aug);
1307             while (currentAug.isPresent()) {
1308                 Type potentialType = typeToAugment.inverse().get(currentAug.get());
1309                 if (potentialType != null) {
1310                     return potentialType;
1311                 }
1312                 currentAug = currentAug.get().getOriginalDefinition();
1313             }
1314             return null;
1315         }
1316
1317         @Override
1318         public String toString() {
1319             return "AugmentableDispatchCodec [augmentable=" + augmentableType + "]";
1320         }
1321
1322     }
1323
1324     @SuppressWarnings("rawtypes")
1325     private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>, Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
1326
1327         private final BindingCodec delegate;
1328         private final QName augmentationQName;
1329         private final Multimap<InstanceIdentifier<?>, QName> validAugmentationTargets;
1330         private final Class<?> augmentationType;
1331
1332         public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec, final Class<?> dataType) {
1333             this.delegate = rawCodec;
1334             this.augmentationType = dataType;
1335             this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
1336             this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap
1337                     .<InstanceIdentifier<?>, QName> create());
1338         }
1339
1340         public void addApplicableFor(final InstanceIdentifier<?> path, final AugmentationSchema aug) {
1341             for (DataSchemaNode child : aug.getChildNodes()) {
1342                 validAugmentationTargets.put(path, child.getQName());
1343             }
1344         }
1345
1346         @Override
1347         public BindingCodec getDelegate() {
1348             return delegate;
1349         }
1350
1351         @Override
1352         public CompositeNode serialize(final ValueWithQName<T> input) {
1353             @SuppressWarnings("unchecked")
1354             List<Map<QName, Object>> rawValues = (List<Map<QName, Object>>) getDelegate().serialize(input);
1355             List<Node<?>> serialized = new ArrayList<>(rawValues.size());
1356             for (Map<QName, Object> val : rawValues) {
1357                 serialized.add(IntermediateMapping.toNode(val));
1358             }
1359             return new CompositeNodeTOImpl(input.getQname(), null, serialized);
1360         }
1361
1362         @Override
1363         @SuppressWarnings("unchecked")
1364         public ValueWithQName<T> deserialize(final Node<?> input) {
1365             Object rawCodecValue = getDelegate().deserialize(input);
1366             return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1367         }
1368
1369         @Override
1370         @SuppressWarnings("unchecked")
1371         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
1372             Object rawCodecValue = getDelegate().deserialize(input, bindingIdentifier);
1373             return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1374         }
1375
1376         @Override
1377         public QName getAugmentationQName() {
1378             return augmentationQName;
1379         }
1380
1381         @Override
1382         public boolean isAcceptable(final InstanceIdentifier<?> path) {
1383             if (path == null) {
1384                 return false;
1385             }
1386             return validAugmentationTargets.containsKey(path);
1387         }
1388
1389         @Override
1390         public boolean isApplicable(final InstanceIdentifier parentPath,final CompositeNode parentData) {
1391             return isAcceptable(parentPath);
1392         }
1393
1394         @Override
1395         public Class<?> getDataType() {
1396             return augmentationType;
1397         }
1398
1399         @Override
1400         public String toString() {
1401             return "AugmentationCodecWrapper [augmentation=" + augmentationType
1402                     + ", knownLocations=" + validAugmentationTargets.keySet() + "]";
1403         }
1404     }
1405
1406     @SuppressWarnings("rawtypes")
1407     private class IdentityCompositeCodec implements IdentityCodec {
1408
1409         @Override
1410         public Object deserialize(final Object input) {
1411             Preconditions.checkArgument(input instanceof QName);
1412             return deserialize((QName) input);
1413         }
1414
1415         @Override
1416         public Class<?> deserialize(final QName input) {
1417             if(input == null) {
1418                 return null;
1419             }
1420             Type type = qnamesToIdentityMap.get(input);
1421             if (type == null) {
1422                 String packageName = BindingMapping.getRootPackageName(input);
1423                 String className = BindingMapping.getClassName(input);
1424                 type = new ReferencedTypeImpl(packageName, className);
1425             }
1426             ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1427             WeakReference<Class> softref = typeToClass.get(typeref);
1428             if (softref == null) {
1429
1430                 try {
1431                     Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1432                     if (cls != null) {
1433                         serialize(cls);
1434                         return cls;
1435                     }
1436                 } catch (Exception e) {
1437                     LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1438                             typeref.getFullyQualifiedName());
1439                 }
1440                 return null;
1441             }
1442             return softref.get();
1443         }
1444
1445         @Override
1446         public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
1447             Type type = qnamesToIdentityMap.get(input);
1448             if (type == null) {
1449                 throw new IllegalArgumentException( "Invalid for \"" + input + "\"." );
1450             }
1451             ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1452             WeakReference<Class> softref = typeToClass.get(typeref);
1453             if (softref == null) {
1454
1455                 try {
1456                     Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1457                     if (cls != null) {
1458                         serialize(cls);
1459                         return cls;
1460                     }
1461                 } catch (Exception e) {
1462                     LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1463                             typeref.getFullyQualifiedName());
1464                 }
1465                 return null;
1466             }
1467             return softref.get();
1468         }
1469
1470         @Override
1471         public QName serialize(final Class input) {
1472             Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
1473             bindingClassEncountered(input);
1474             QName qname = identityQNames.get(input);
1475             if (qname != null) {
1476                 return qname;
1477             }
1478             qname = BindingReflections.findQName(input);
1479             if (qname != null) {
1480                 identityQNames.put(input, qname);
1481             }
1482             return qname;
1483         }
1484
1485         @Override
1486         public Object serialize(final Object input) {
1487             Preconditions.checkArgument(input instanceof Class);
1488             return serialize((Class) input);
1489         }
1490
1491     }
1492
1493     private static final Type referencedType(final Class<?> augmentableType) {
1494         return new ReferencedTypeImpl(augmentableType.getPackage().getName(), augmentableType.getSimpleName());
1495     }
1496 }