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