Merge "Add injectable LoadingStrategy to RuntimeGeneratedMappingServiceImpl."
[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.lang.reflect.ParameterizedType;
13 import java.util.*;
14 import java.util.Map.Entry;
15 import java.util.concurrent.Callable;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18
19 import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils;
20 import org.opendaylight.yangtools.yang.data.impl.codec.*;
21 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
22 import org.opendaylight.yangtools.binding.generator.util.Types;
23 import org.opendaylight.yangtools.concepts.Delegator;
24 import org.opendaylight.yangtools.concepts.Identifiable;
25 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
26 import org.opendaylight.yangtools.sal.binding.model.api.Type;
27 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
28 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
29 import org.opendaylight.yangtools.yang.binding.*;
30 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
33 import org.opendaylight.yangtools.yang.data.api.Node;
34 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
35 import org.opendaylight.yangtools.yang.model.api.*;
36 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.common.base.Preconditions;
41
42 public class LazyGeneratedCodecRegistry implements //
43         CodecRegistry, //
44         SchemaServiceListener, //
45         GeneratorListener {
46
47     private final static Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
48     private final static LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
49
50     private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
51     private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
52
53     private TransformerGenerator generator;
54
55     // Concrete class to codecs
56     private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
57             .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
58     private static final Map<Class<?>, IdentifierCodec<?>> identifierCodecs = Collections
59             .synchronizedMap(new WeakHashMap<Class<?>, IdentifierCodec<?>>());
60     private static final Map<Class<?>, ChoiceCodecImpl<?>> choiceCodecs = Collections
61             .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCodecImpl<?>>());
62     private static final Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = Collections
63             .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCaseCodecImpl<?>>());
64     private static final Map<Class<?>, AugmentableCompositeCodec> augmentableCodecs = Collections
65             .synchronizedMap(new WeakHashMap<Class<?>, AugmentableCompositeCodec>());
66     private static final Map<Class<?>, AugmentationCodec<?>> augmentationCodecs = Collections
67             .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodec<?>>());
68     private static final Map<Class<?>, QName> identityQNames = Collections
69             .synchronizedMap(new WeakHashMap<Class<?>, QName>());
70     private static final Map<QName, Type> qnamesToIdentityMap = new ConcurrentHashMap<>();
71     /** Binding type to encountered classes mapping **/
72     @SuppressWarnings("rawtypes")
73     private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
74
75     @SuppressWarnings("rawtypes")
76     private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
77
78     private CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
79
80     private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
81     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
82     private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
83
84     private final SchemaLock lock;
85
86     private SchemaContext currentSchema;
87
88     private final GeneratedClassLoadingStrategy classLoadingStrategy;
89
90     LazyGeneratedCodecRegistry(SchemaLock lock, GeneratedClassLoadingStrategy identityClassLoadingStrategy) {
91         this.lock = Preconditions.checkNotNull(lock);
92         this.classLoadingStrategy = identityClassLoadingStrategy;
93     }
94
95     public SchemaLock getLock() {
96         return lock;
97     }
98
99     public TransformerGenerator getGenerator() {
100         return generator;
101     }
102
103     public void setGenerator(TransformerGenerator generator) {
104         this.generator = generator;
105     }
106
107     @Override
108     public InstanceIdentifierCodec getInstanceIdentifierCodec() {
109         return instanceIdentifierCodec;
110     }
111
112     @Override
113     public <T extends Augmentation<?>> AugmentationCodec<T> getCodecForAugmentation(Class<T> object) {
114         AugmentationCodec<T> codec = null;
115         @SuppressWarnings("rawtypes")
116         AugmentationCodec potentialCodec = augmentationCodecs.get(object);
117         if (potentialCodec != null) {
118             codec = potentialCodec;
119         } else
120             try {
121                 lock.waitForSchema(object);
122                 Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
123                         .augmentationTransformerFor(object);
124                 BindingCodec<Map<QName, Object>, Object> rawCodec = augmentRawCodec.newInstance();
125                 codec = new AugmentationCodecWrapper<T>(rawCodec);
126                 augmentationCodecs.put(object, codec);
127             } catch (InstantiationException e) {
128                 LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e);
129             } catch (IllegalAccessException e) {
130                 LOG.debug(
131                         "Run-time consistency issue: constructor {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
132                         object.getSimpleName(), e);
133             }
134         Class<? extends Augmentable<?>> objectSupertype = getAugmentableArgumentFrom(object);
135         if (objectSupertype != null) {
136             getAugmentableCodec(objectSupertype).addAugmentationCodec(object, codec);
137         } else {
138             LOG.warn("Could not find augmentation target for augmentation {}", object);
139         }
140         return codec;
141     }
142
143     @Override
144     public QName getQNameForAugmentation(Class<?> cls) {
145         Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
146         return getCodecForAugmentation((Class<? extends Augmentation>) cls).getAugmentationQName();
147     }
148
149     private static Class<? extends Augmentable<?>> getAugmentableArgumentFrom(
150             final Class<? extends Augmentation<?>> augmentation) {
151         try {
152             Class<? extends Augmentable<?>> ret = ClassLoaderUtils.withClassLoader(augmentation.getClassLoader(),
153                     new Callable<Class<? extends Augmentable<?>>>() {
154                         @Override
155                         @SuppressWarnings("unchecked")
156                         public Class<? extends Augmentable<?>> call() throws Exception {
157                             for (java.lang.reflect.Type supertype : augmentation.getGenericInterfaces()) {
158                                 if (supertype instanceof ParameterizedType
159                                         && Augmentation.class.equals(((ParameterizedType) supertype).getRawType())) {
160                                     ParameterizedType augmentationGeneric = (ParameterizedType) supertype;
161                                     return (Class<? extends Augmentable<?>>) augmentationGeneric
162                                             .getActualTypeArguments()[0];
163                                 }
164                             }
165                             return null;
166                         }
167                     });
168             return ret;
169         } catch (Exception e) {
170             LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
171             return null;
172         }
173     }
174
175     @Override
176     public Class<?> getClassForPath(List<QName> names) {
177         DataSchemaNode node = getSchemaNode(names);
178         SchemaPath path = node.getPath();
179         Type type = pathToType.get(path);
180         if (type != null) {
181             type = new ReferencedTypeImpl(type.getPackageName(), type.getName());
182         } else {
183             type = pathToInstantiatedType.get(names);
184         }
185         @SuppressWarnings("rawtypes")
186         WeakReference<Class> weakRef = typeToClass.get(type);
187         if (weakRef == null) {
188             LOG.error("Could not find loaded class for path: {} and type: {}", path, type.getFullyQualifiedName());
189         }
190         return weakRef.get();
191     }
192
193     @Override
194     public void putPathToClass(List<QName> names, Class<?> cls) {
195         Type reference = Types.typeForClass(cls);
196         pathToInstantiatedType.put(names, reference);
197         bindingClassEncountered(cls);
198     }
199
200     @Override
201     public IdentifierCodec<?> getKeyCodecForPath(List<QName> names) {
202         @SuppressWarnings("unchecked")
203         Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
204         return getIdentifierCodecForIdentifiable(cls);
205     }
206
207     @Override
208     public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(Class<T> type) {
209         @SuppressWarnings("unchecked")
210         DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
211         if (ret != null) {
212             return ret;
213         }
214         Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
215         BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
216         DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
217         containerCodecs.put(type, newWrapper);
218         return newWrapper;
219     }
220
221     @SuppressWarnings("rawtypes")
222     public void bindingClassEncountered(Class cls) {
223
224         ConcreteType typeRef = Types.typeForClass(cls);
225         if (typeToClass.containsKey(typeRef)) {
226             return;
227         }
228         LOG.trace("Binding Class {} encountered.", cls);
229         WeakReference<Class> weakRef = new WeakReference<>(cls);
230         typeToClass.put(typeRef, weakRef);
231         if (Augmentation.class.isAssignableFrom(cls)) {
232
233         } else if (DataObject.class.isAssignableFrom(cls)) {
234             @SuppressWarnings({ "unchecked", "unused" })
235             Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
236         }
237     }
238
239     @Override
240     public void onClassProcessed(Class<?> cls) {
241         ConcreteType typeRef = Types.typeForClass(cls);
242         if (typeToClass.containsKey(typeRef)) {
243             return;
244         }
245         LOG.trace("Binding Class {} encountered.", cls);
246         WeakReference<Class> weakRef = new WeakReference<>((Class) cls);
247         typeToClass.put(typeRef, weakRef);
248     }
249
250     private DataSchemaNode getSchemaNode(List<QName> path) {
251         QName firstNode = path.get(0);
252         DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
253                 firstNode.getRevision());
254         Iterator<QName> iterator = path.iterator();
255         while (iterator.hasNext()) {
256             QName arg = iterator.next();
257             DataSchemaNode currentNode = previous.getDataChildByName(arg);
258             if (currentNode == null && previous instanceof DataNodeContainer) {
259                 currentNode = searchInChoices(previous, arg);
260             }
261             if (currentNode instanceof DataNodeContainer) {
262                 previous = (DataNodeContainer) currentNode;
263             } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
264                 Preconditions.checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
265                 return currentNode;
266             }
267         }
268         return (DataSchemaNode) previous;
269     }
270
271     private DataSchemaNode searchInChoices(DataNodeContainer node, QName arg) {
272         Set<DataSchemaNode> children = node.getChildNodes();
273         for (DataSchemaNode child : children) {
274             if (child instanceof ChoiceNode) {
275                 ChoiceNode choiceNode = (ChoiceNode) child;
276                 DataSchemaNode potential = searchInCases(choiceNode, arg);
277                 if (potential != null) {
278                     return potential;
279                 }
280             }
281         }
282         return null;
283     }
284
285     private DataSchemaNode searchInCases(ChoiceNode choiceNode, QName arg) {
286         Set<ChoiceCaseNode> cases = choiceNode.getCases();
287         for (ChoiceCaseNode caseNode : cases) {
288             DataSchemaNode node = caseNode.getDataChildByName(arg);
289             if (node != null) {
290                 return node;
291             }
292         }
293         return null;
294     }
295
296     private <T> T newInstanceOf(Class<?> newType) {
297         try {
298             @SuppressWarnings("unchecked")
299             T ret = (T) newType.newInstance();
300             return ret;
301         } catch (InstantiationException e) {
302             throw new IllegalStateException(e);
303         } catch (IllegalAccessException e) {
304             throw new IllegalStateException(e);
305         }
306     }
307
308     @Override
309     public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(Class<T> type) {
310         IdentifierCodec<?> obj = identifierCodecs.get(type);
311         if (obj != null) {
312             return obj;
313         }
314         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
315                 .keyTransformerForIdentifiable(type);
316         BindingCodec<Map<QName, Object>, Object> newInstance;
317         newInstance = newInstanceOf(newCodec);
318         IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
319         identifierCodecs.put(type, newWrapper);
320         return newWrapper;
321     }
322
323     @Override
324     public IdentityCodec<?> getIdentityCodec() {
325         return identityRefCodec;
326     }
327
328     @Override
329     public <T extends BaseIdentity> IdentityCodec<T> getCodecForIdentity(Class<T> codec) {
330         bindingClassEncountered(codec);
331         return identityRefCodec;
332     }
333
334     @Override
335     public void onCodecCreated(Class<?> cls) {
336         CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
337         CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
338     }
339
340     @Override
341     public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(Class<T> object) {
342         @SuppressWarnings("unchecked")
343         IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
344         if (obj != null) {
345             return obj;
346         }
347         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
348                 .keyTransformerForIdentifier(object);
349         BindingCodec<Map<QName, Object>, Object> newInstance;
350         newInstance = newInstanceOf(newCodec);
351         IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
352         identifierCodecs.put(object, newWrapper);
353         return newWrapper;
354     }
355
356     @SuppressWarnings("rawtypes")
357     public ChoiceCaseCodecImpl getCaseCodecFor(Class caseClass) {
358         ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
359         if (potential != null) {
360             return potential;
361         }
362         ConcreteType typeref = Types.typeForClass(caseClass);
363         ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref);
364
365         Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s",
366                 caseClass.getName());
367         Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
368                 caseClass.getName());
369         @SuppressWarnings("unchecked")
370         Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
371         BindingCodec newInstance = newInstanceOf(newCodec);
372         caseCodec.setDelegate(newInstance);
373         caseCodecs.put(caseClass, caseCodec);
374
375         for (Entry<Class<?>, ChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
376             if (choice.getKey().isAssignableFrom(caseClass)) {
377                 choice.getValue().cases.put(caseClass, caseCodec);
378             }
379         }
380         return caseCodec;
381     }
382
383     public void onModuleContextAdded(SchemaContext schemaContext, Module module, ModuleContext context) {
384         pathToType.putAll(context.getChildNodes());
385         qnamesToIdentityMap.putAll(context.getIdentities());
386         for (Entry<QName, GeneratedTOBuilder> identity : context.getIdentities().entrySet()) {
387             typeToQname.put(
388                     new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),
389                     identity.getKey());
390         }
391         captureCases(context.getCases(), schemaContext);
392     }
393
394     private void captureCases(Map<SchemaPath, GeneratedTypeBuilder> cases, SchemaContext module) {
395         for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
396             ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
397                     .getValue().getName());
398
399             pathToType.put(caseNode.getKey(), caseNode.getValue());
400
401             ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
402
403             if (node == null) {
404                 LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.",
405                         typeref.getFullyQualifiedName(), caseNode.getKey());
406                 @SuppressWarnings("rawtypes")
407                 ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl();
408                 typeToCaseCodecs.putIfAbsent(typeref, value);
409                 continue;
410             }
411             @SuppressWarnings("rawtypes")
412             ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
413             typeToCaseCodecs.putIfAbsent(typeref, value);
414         }
415     }
416
417     @Override
418     public void onGlobalContextUpdated(SchemaContext context) {
419         currentSchema = context;
420     }
421
422     @SuppressWarnings({ "unchecked", "rawtypes" })
423     @Override
424     public void onChoiceCodecCreated(Class<?> choiceClass,
425             Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec, ChoiceNode schema) {
426         ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
427         Preconditions.checkState(oldCodec == null);
428         BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
429         ChoiceCodecImpl<?> newCodec = new ChoiceCodecImpl(delegate);
430         choiceCodecs.put(choiceClass, newCodec);
431         CodecMapping.setClassToCaseMap(choiceCodec, (Map<Class<?>, BindingCodec<?, ?>>) classToCaseRawCodec);
432         CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
433
434         tryToCreateCasesCodecs(schema);
435
436     }
437
438     private void tryToCreateCasesCodecs(ChoiceNode schema) {
439         for (ChoiceCaseNode caseNode : schema.getCases()) {
440             SchemaPath path = caseNode.getPath();
441             GeneratedTypeBuilder type;
442             if (path != null && (type = pathToType.get(path)) != null) {
443                 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
444                 ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref);
445                 if (partialCodec.getSchema() == null) {
446                     partialCodec.setSchema(caseNode);
447                 }
448                 try {
449                     Class<?> caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName());
450                     getCaseCodecFor(caseClass);
451                 } catch (ClassNotFoundException e) {
452                     LOG.trace("Could not proactivelly create case codec for {}", type, e);
453                 }
454             }
455         }
456
457     }
458
459     @Override
460     public void onValueCodecCreated(Class<?> valueClass, Class<?> valueCodec) {
461     }
462
463     @Override
464     public void onCaseCodecCreated(Class<?> choiceClass,
465             Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
466     }
467
468     @Override
469     public void onDataContainerCodecCreated(Class<?> dataClass, Class<? extends BindingCodec<?, ?>> dataCodec) {
470         if (Augmentable.class.isAssignableFrom(dataClass)) {
471             AugmentableCompositeCodec augmentableCodec = getAugmentableCodec(dataClass);
472             CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
473         }
474
475     }
476
477     public AugmentableCompositeCodec getAugmentableCodec(Class<?> dataClass) {
478         AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass);
479         if (ret != null) {
480             return ret;
481         }
482         ret = new AugmentableCompositeCodec(dataClass);
483         augmentableCodecs.put(dataClass, ret);
484         return ret;
485     }
486
487     private static abstract class IntermediateCodec<T> implements //
488             DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
489
490         private final BindingCodec<Map<QName, Object>, Object> delegate;
491
492         @Override
493         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
494             return delegate;
495         }
496
497         public IntermediateCodec(BindingCodec<Map<QName, Object>, Object> delegate) {
498             this.delegate = delegate;
499         }
500
501         @Override
502         public Node<?> serialize(ValueWithQName<T> input) {
503             Map<QName, Object> intermediateOutput = delegate.serialize(input);
504             return IntermediateMapping.toNode(intermediateOutput);
505         }
506     }
507
508     private static class IdentifierCodecImpl<T extends Identifier<?>> //
509             extends IntermediateCodec<T> //
510             implements IdentifierCodec<T> {
511
512         public IdentifierCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
513             super(delegate);
514         }
515
516         @Override
517         public ValueWithQName<T> deserialize(Node<?> input) {
518             QName qname = input.getNodeType();
519             @SuppressWarnings("unchecked")
520             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
521             return new ValueWithQName<T>(qname, value);
522         }
523
524         @Override
525         public CompositeNode serialize(ValueWithQName<T> input) {
526             return (CompositeNode) super.serialize(input);
527         }
528     }
529
530     private static class DataContainerCodecImpl<T extends DataContainer> //
531             extends IntermediateCodec<T> //
532             implements DataContainerCodec<T> {
533
534         public DataContainerCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
535             super(delegate);
536         }
537
538         @Override
539         public ValueWithQName<T> deserialize(Node<?> input) {
540             if (input == null) {
541                 return null;
542             }
543             QName qname = input.getNodeType();
544             @SuppressWarnings("unchecked")
545             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
546             return new ValueWithQName<T>(qname, value);
547         }
548
549         @Override
550         public CompositeNode serialize(ValueWithQName<T> input) {
551             return (CompositeNode) super.serialize(input);
552         }
553     }
554
555     @SuppressWarnings("rawtypes")
556     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
557             Delegator<BindingCodec> {
558         private boolean augmenting;
559         private BindingCodec delegate;
560
561         private Set<String> validNames;
562         private Set<QName> validQNames;
563         private ChoiceCaseNode schema;
564
565         public void setSchema(ChoiceCaseNode caseNode) {
566             this.schema = schema;
567             this.schema = caseNode;
568             validNames = new HashSet<>();
569             validQNames = new HashSet<>();
570             for (DataSchemaNode node : caseNode.getChildNodes()) {
571                 QName qname = node.getQName();
572                 validQNames.add(qname);
573                 validNames.add(qname.getLocalName());
574             }
575             augmenting = caseNode.isAugmenting();
576         }
577
578         public ChoiceCaseCodecImpl() {
579             this.delegate = NOT_READY_CODEC;
580         }
581
582         public ChoiceCaseCodecImpl(ChoiceCaseNode caseNode) {
583             this.delegate = NOT_READY_CODEC;
584             setSchema(caseNode);
585         }
586
587         @Override
588         public ValueWithQName<T> deserialize(Node<?> input) {
589             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
590         }
591
592         @Override
593         public CompositeNode serialize(ValueWithQName<T> input) {
594             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
595         }
596
597         public BindingCodec getDelegate() {
598             return delegate;
599         }
600
601         public void setDelegate(BindingCodec delegate) {
602             this.delegate = delegate;
603         }
604
605         public ChoiceCaseNode getSchema() {
606             return schema;
607         }
608
609         @Override
610         public boolean isAcceptable(Node<?> input) {
611             if (input instanceof CompositeNode) {
612                 if (augmenting) {
613                     return checkAugmenting((CompositeNode) input);
614                 } else {
615                     return checkLocal((CompositeNode) input);
616                 }
617             }
618             return false;
619         }
620
621         private boolean checkLocal(CompositeNode input) {
622             QName parent = input.getNodeType();
623             for (Node<?> childNode : input.getChildren()) {
624                 QName child = childNode.getNodeType();
625                 if (!Objects.equals(parent.getNamespace(), child.getNamespace())
626                         || !Objects.equals(parent.getRevision(), child.getRevision())) {
627                     continue;
628                 }
629                 if (validNames.contains(child.getLocalName())) {
630                     return true;
631                 }
632             }
633             return false;
634         }
635
636         private boolean checkAugmenting(CompositeNode input) {
637             for (Node<?> child : input.getChildren()) {
638                 if (validQNames.contains(child.getNodeType())) {
639                     return true;
640                 }
641             }
642             return false;
643         }
644     }
645
646     private static class ChoiceCodecImpl<T> implements ChoiceCodec<T> {
647
648         private final BindingCodec<Map<QName, Object>, Object> delegate;
649
650         @SuppressWarnings("rawtypes")
651         private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
652                 .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
653
654         private final CaseCompositeNodeMapFacade CompositeToCase;
655
656         public ChoiceCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
657             this.delegate = delegate;
658             this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
659         }
660
661         @Override
662         public ValueWithQName<T> deserialize(Node<?> input) {
663             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
664         }
665
666         @Override
667         public Node<?> serialize(ValueWithQName<T> input) {
668             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
669         }
670
671         public CaseCompositeNodeMapFacade getCompositeToCase() {
672             return CompositeToCase;
673         }
674
675         public Map<Class, ChoiceCaseCodecImpl<?>> getCases() {
676             return cases;
677         }
678
679         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
680             return delegate;
681         }
682
683     }
684
685     @SuppressWarnings("rawtypes")
686     private class CaseClassMapFacade extends MapFacadeBase {
687
688         @Override
689         public Set<Entry<Class, BindingCodec<Object, Object>>> entrySet() {
690             return Collections.emptySet();
691         }
692
693         @Override
694         public BindingCodec get(Object key) {
695             if (key instanceof Class) {
696                 Class cls = (Class) key;
697                 // bindingClassEncountered(cls);
698                 ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
699                 return caseCodec.getDelegate();
700             }
701             return null;
702         }
703     }
704
705     @SuppressWarnings("rawtypes")
706     private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
707
708         final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
709
710         public CaseCompositeNodeMapFacade(Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
711             this.choiceCases = choiceCases;
712         }
713
714         @Override
715         public BindingCodec get(Object key) {
716             if (!(key instanceof CompositeNode)) {
717                 return null;
718             }
719             for (Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
720                 ChoiceCaseCodecImpl<?> codec = entry.getValue();
721                 if (codec.isAcceptable((CompositeNode) key)) {
722                     return codec.getDelegate();
723                 }
724             }
725             return null;
726         }
727
728     }
729
730     /**
731      * This map is used as only facade for
732      * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different
733      * classloaders to retrieve codec dynamicly based on provided key.
734      *
735      * @param <T>
736      *            Key type
737      */
738     @SuppressWarnings("rawtypes")
739     private static abstract class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
740
741         @Override
742         public boolean containsKey(Object key) {
743             return get(key) != null;
744         }
745
746         @Override
747         public void clear() {
748             throw notModifiable();
749         }
750
751         @Override
752         public boolean equals(Object obj) {
753             return super.equals(obj);
754         }
755
756         @Override
757         public BindingCodec remove(Object key) {
758             return null;
759         }
760
761         @Override
762         public int size() {
763             return 0;
764         }
765
766         @Override
767         public Collection<BindingCodec<?, ?>> values() {
768             return Collections.emptySet();
769         }
770
771         private UnsupportedOperationException notModifiable() {
772             return new UnsupportedOperationException("Not externally modifiable.");
773         }
774
775         @Override
776         public BindingCodec<Map<QName, Object>, Object> put(T key, BindingCodec<?, ?> value) {
777             throw notModifiable();
778         }
779
780         @Override
781         public void putAll(Map<? extends T, ? extends BindingCodec<?, ?>> m) {
782             throw notModifiable();
783         }
784
785         @Override
786         public int hashCode() {
787             return super.hashCode();
788         }
789
790         @Override
791         public boolean isEmpty() {
792             return true;
793         }
794
795         @Override
796         public Set<T> keySet() {
797             return Collections.emptySet();
798         }
799
800         @Override
801         public Set<Entry<T, BindingCodec<?, ?>>> entrySet() {
802             return Collections.emptySet();
803         }
804
805         @Override
806         public boolean containsValue(Object value) {
807             return false;
808         }
809     }
810
811     @SuppressWarnings({ "rawtypes", "unchecked" })
812     private class AugmentableCompositeCodec implements BindingCodec {
813
814         private final Class augmentableType;
815
816         Map<Class, AugmentationCodec<?>> localAugmentationCodecs = Collections
817                 .synchronizedMap(new WeakHashMap<Class, AugmentationCodec<?>>());
818
819         public AugmentableCompositeCodec(Class type) {
820             Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
821             augmentableType = type;
822         }
823
824         @Override
825         public Object serialize(Object input) {
826             if (input instanceof Augmentable<?>) {
827
828                 Map<Class, Augmentation> augmentations = getAugmentations(input);
829                 return serializeImpl(augmentations);
830             }
831             return null;
832         }
833
834         private Map<Class, Augmentation> getAugmentations(Object input) {
835             Field augmentationField;
836             try {
837                 augmentationField = input.getClass().getDeclaredField("augmentation");
838                 augmentationField.setAccessible(true);
839                 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
840                 return augMap;
841             } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
842                 LOG.debug("Could not read augmentations for {}", input, e);
843             }
844             return Collections.emptyMap();
845         }
846
847         private List serializeImpl(Map<Class, Augmentation> input) {
848             List ret = new ArrayList<>();
849             for (Entry<Class, Augmentation> entry : input.entrySet()) {
850                 AugmentationCodec codec = getCodecForAugmentation(entry.getKey());
851                 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
852                 ret.addAll(node.getChildren());
853             }
854             return ret;
855         }
856
857         public synchronized <T extends Augmentation<?>> void addAugmentationCodec(Class<T> augmentationClass,
858                 AugmentationCodec<T> value) {
859             localAugmentationCodecs.put(augmentationClass, value);
860         }
861
862         @Override
863         public Map<Class, Augmentation> deserialize(Object input) {
864             Map<Class, Augmentation> ret = new HashMap<>();
865             if (input instanceof CompositeNode) {
866                 List<Entry<Class, AugmentationCodec<?>>> codecs = new ArrayList<>(localAugmentationCodecs.entrySet());
867                 for (Entry<Class, AugmentationCodec<?>> codec : codecs) {
868                     ValueWithQName<?> value = codec.getValue().deserialize((CompositeNode) input);
869                     if (value != null && value.getValue() != null) {
870                         ret.put(codec.getKey(), (Augmentation) value.getValue());
871                     }
872                 }
873             }
874             return ret;
875         }
876
877         public Class getAugmentableType() {
878             return augmentableType;
879         }
880     }
881
882     @SuppressWarnings({ "rawtypes", "unchecked" })
883     private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
884
885         private BindingCodec delegate;
886
887         @Override
888         public BindingCodec getDelegate() {
889             if (delegate == null) {
890                 throw new IllegalStateException("Codec not initialized yet.");
891             }
892             return delegate;
893         }
894
895         @Override
896         public Object deserialize(Object input) {
897             return getDelegate().deserialize(input);
898         }
899
900         @Override
901         public Object serialize(Object input) {
902             return getDelegate().serialize(input);
903         }
904     }
905
906     private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
907             Delegator<BindingCodec> {
908
909         private BindingCodec delegate;
910         private QName augmentationQName;
911
912         public AugmentationCodecWrapper(BindingCodec<Map<QName, Object>, Object> rawCodec) {
913             this.delegate = rawCodec;
914             this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
915         }
916
917         @Override
918         public BindingCodec getDelegate() {
919             return delegate;
920         }
921
922         @Override
923         public CompositeNode serialize(ValueWithQName<T> input) {
924             @SuppressWarnings("unchecked")
925             List<Map<QName, Object>> rawValues = (List<Map<QName, Object>>) getDelegate().serialize(input);
926             List<Node<?>> serialized = new ArrayList<>(rawValues.size());
927             for (Map<QName, Object> val : rawValues) {
928                 serialized.add(IntermediateMapping.toNode(val));
929             }
930             return new CompositeNodeTOImpl(input.getQname(), null, serialized);
931         }
932
933         @Override
934         @SuppressWarnings("unchecked")
935         public ValueWithQName<T> deserialize(Node<?> input) {
936             Object rawCodecValue = getDelegate().deserialize((Map<QName, Object>) input);
937             return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
938         }
939
940         @Override
941         public QName getAugmentationQName() {
942             return augmentationQName;
943         }
944     }
945
946     private class IdentityCompositeCodec implements IdentityCodec {
947
948         @Override
949         public Object deserialize(Object input) {
950             Preconditions.checkArgument(input instanceof QName);
951             return deserialize((QName) input);
952         }
953
954         @Override
955         public Class<?> deserialize(QName input) {
956             Type type = qnamesToIdentityMap.get(input);
957             if (type == null) {
958                 return null;
959             }
960             ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
961             WeakReference<Class> softref = typeToClass.get(typeref);
962             if (softref == null) {
963
964                 try {
965                     Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
966                     if (cls != null) {
967                         serialize(cls);
968                         return cls;
969                     }
970                 } catch (Exception e) {
971                     LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
972                             typeref.getFullyQualifiedName());
973                 }
974                 return null;
975             }
976             return softref.get();
977         }
978
979         @Override
980         public QName serialize(Class input) {
981             Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
982             bindingClassEncountered(input);
983             QName qname = identityQNames.get(input);
984             if (qname != null) {
985                 return qname;
986             }
987             ConcreteType typeref = Types.typeForClass(input);
988             qname = typeToQname.get(typeref);
989             if (qname != null) {
990                 identityQNames.put(input, qname);
991             }
992             return qname;
993         }
994
995         @Override
996         public Object serialize(Object input) {
997             Preconditions.checkArgument(input instanceof Class);
998             return serialize((Class) input);
999         }
1000     }
1001
1002     public boolean isCodecAvailable(Class<? extends DataContainer> cls) {
1003         if (containerCodecs.containsKey(cls)) {
1004             return true;
1005         }
1006         if (identifierCodecs.containsKey(cls)) {
1007             return true;
1008         }
1009         if (choiceCodecs.containsKey(cls)) {
1010             return true;
1011         }
1012         if (caseCodecs.containsKey(cls)) {
1013             return true;
1014         }
1015         if (augmentableCodecs.containsKey(cls)) {
1016             return true;
1017         }
1018         if (augmentationCodecs.containsKey(cls)) {
1019             return true;
1020         }
1021         return false;
1022     }
1023 }