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