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