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