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