de6836489e6a6b970fb96a409e832876d02e82a7
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / dom / serializer / impl / LazyGeneratedCodecRegistry.java
1 package org.opendaylight.controller.sal.binding.dom.serializer.impl;
2
3 import java.lang.ref.WeakReference;
4 import java.lang.reflect.Field;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Objects;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ConcurrentMap;
17 import java.util.Set;
18 import java.util.WeakHashMap;
19
20 import org.opendaylight.controller.sal.binding.dom.serializer.api.AugmentationCodec;
21 import org.opendaylight.controller.sal.binding.dom.serializer.api.ChoiceCaseCodec;
22 import org.opendaylight.controller.sal.binding.dom.serializer.api.ChoiceCodec;
23 import org.opendaylight.controller.sal.binding.dom.serializer.api.CodecRegistry;
24 import org.opendaylight.controller.sal.binding.dom.serializer.api.DataContainerCodec;
25 import org.opendaylight.controller.sal.binding.dom.serializer.api.DomCodec;
26 import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentifierCodec;
27 import org.opendaylight.controller.sal.binding.dom.serializer.api.InstanceIdentifierCodec;
28 import org.opendaylight.controller.sal.binding.dom.serializer.api.ValueWithQName;
29 import org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils;
30 import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
31 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
32 import org.opendaylight.yangtools.binding.generator.util.Types;
33 import org.opendaylight.yangtools.concepts.Delegator;
34 import org.opendaylight.yangtools.concepts.Identifiable;
35 import org.opendaylight.yangtools.yang.binding.Augmentable;
36 import org.opendaylight.yangtools.yang.binding.Augmentation;
37 import org.opendaylight.yangtools.yang.binding.BindingCodec;
38 import org.opendaylight.yangtools.yang.binding.DataContainer;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.Identifier;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
43 import org.opendaylight.yangtools.yang.data.api.Node;
44 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
45 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
46 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
47 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
48 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
49 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
53 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
54 import org.opendaylight.yangtools.yang.model.api.UsesNode;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 import static com.google.common.base.Preconditions.*;
59 import static org.opendaylight.controller.sal.binding.dom.serializer.impl.IntermediateMapping.*;
60
61 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
62 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
63 import org.opendaylight.yangtools.sal.binding.model.api.Type;
64 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
65 import org.opendaylight.yangtools.yang.model.api.Module;
66 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
67
68 import com.google.common.util.concurrent.CycleDetectingLockFactory.WithExplicitOrdering;
69
70 public class LazyGeneratedCodecRegistry implements //
71         CodecRegistry, //
72         SchemaServiceListener, //
73         GeneratorListener {
74
75     private final static Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
76     private final static LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
77
78     private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
79
80     private TransformerGenerator generator;
81
82     // Concrete class to codecs
83     private Map<Class<?>, DataContainerCodec<?>> containerCodecs = new WeakHashMap<>();
84     private Map<Class<?>, IdentifierCodec<?>> identifierCodecs = new WeakHashMap<>();
85     private Map<Class<?>, ChoiceCodecImpl<?>> choiceCodecs = new WeakHashMap<>();
86     private Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = new WeakHashMap<>();
87     private Map<Class<?>, AugmentableCompositeCodec> augmentableCodecs = new WeakHashMap<>();
88
89     /** Binding type to encountered classes mapping **/
90     @SuppressWarnings("rawtypes")
91     Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
92
93     @SuppressWarnings("rawtypes")
94     private ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseNodes = new ConcurrentHashMap<>();
95
96     private CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
97
98     Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
99     Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
100
101     private SchemaContext currentSchema;
102
103     public TransformerGenerator getGenerator() {
104         return generator;
105     }
106
107     public void setGenerator(TransformerGenerator generator) {
108         this.generator = generator;
109     }
110
111     @Override
112     public InstanceIdentifierCodec getInstanceIdentifierCodec() {
113         return instanceIdentifierCodec;
114     }
115
116     @Override
117     public <T extends Augmentation<?>> AugmentationCodec<T> getCodecForAugmentation(Class<T> object) {
118         // TODO Auto-generated method stub
119         return null;
120     }
121
122     @Override
123     public Class<?> getClassForPath(List<QName> names) {
124         DataSchemaNode node = getSchemaNode(names);
125         SchemaPath path = node.getPath();
126         Type type = pathToType.get(path);
127         if (type != null) {
128             type = new ReferencedTypeImpl(type.getPackageName(), type.getName());
129         } else {
130             type = pathToInstantiatedType.get(names);
131         }
132         @SuppressWarnings("rawtypes")
133         WeakReference<Class> weakRef = typeToClass.get(type);
134         if (weakRef == null) {
135             LOG.error("Could not find loaded class for path: {} and type: {}", path, type.getFullyQualifiedName());
136         }
137         return weakRef.get();
138     }
139     
140     @Override
141     public void putPathToClass(List<QName> names,Class<?> cls) {
142         Type reference = Types.typeForClass(cls);
143         pathToInstantiatedType.put(names, reference );
144         bindingClassEncountered(cls);
145     }
146
147
148     @Override
149     public IdentifierCodec<?> getKeyCodecForPath(List<QName> names) {
150         @SuppressWarnings("unchecked")
151         Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
152         return getIdentifierCodecForIdentifiable(cls);
153     }
154
155     @Override
156     public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(Class<T> type) {
157         @SuppressWarnings("unchecked")
158         DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
159         if (ret != null) {
160             return ret;
161         }
162         Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
163         BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
164         DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
165         containerCodecs.put(type, newWrapper);
166         return newWrapper;
167     }
168
169     @Override
170     @SuppressWarnings("rawtypes")
171     public void bindingClassEncountered(Class cls) {
172
173         ConcreteType typeRef = Types.typeForClass(cls);
174         if (typeToClass.containsKey(typeRef)) {
175             return;
176         }
177         LOG.info("Binding Class {} encountered.", cls);
178         WeakReference<Class> weakRef = new WeakReference<>(cls);
179         typeToClass.put(typeRef, weakRef);
180         if (Augmentation.class.isAssignableFrom(cls)) {
181
182         } else if (DataObject.class.isAssignableFrom(cls)) {
183             @SuppressWarnings({ "unchecked", "unused" })
184             Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
185         }
186     }
187
188     @Override
189     public void onClassProcessed(Class<?> cls) {
190         ConcreteType typeRef = Types.typeForClass(cls);
191         if (typeToClass.containsKey(typeRef)) {
192             return;
193         }
194         LOG.info("Binding Class {} encountered.", cls);
195         WeakReference<Class> weakRef = new WeakReference<>((Class) cls);
196         typeToClass.put(typeRef, weakRef);
197     }
198
199     private DataSchemaNode getSchemaNode(List<QName> path) {
200         QName firstNode = path.get(0);
201         DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
202                 firstNode.getRevision());
203         Iterator<QName> iterator = path.iterator();
204         while (iterator.hasNext()) {
205             QName arg = iterator.next();
206             DataSchemaNode currentNode = previous.getDataChildByName(arg);
207             if (currentNode == null && previous instanceof DataNodeContainer) {
208                 currentNode = searchInChoices(previous, arg);
209             }
210             if (currentNode instanceof DataNodeContainer) {
211                 previous = (DataNodeContainer) currentNode;
212             } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
213                 checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
214                 return currentNode;
215             }
216         }
217         return (DataSchemaNode) previous;
218     }
219
220     private DataSchemaNode searchInChoices(DataNodeContainer node, QName arg) {
221         Set<DataSchemaNode> children = node.getChildNodes();
222         for (DataSchemaNode child : children) {
223             if (child instanceof ChoiceNode) {
224                 ChoiceNode choiceNode = (ChoiceNode) child;
225                 DataSchemaNode potential = searchInCases(choiceNode, arg);
226                 if (potential != null) {
227                     return potential;
228                 }
229             }
230         }
231         return null;
232     }
233
234     private DataSchemaNode searchInCases(ChoiceNode choiceNode, QName arg) {
235         Set<ChoiceCaseNode> cases = choiceNode.getCases();
236         for (ChoiceCaseNode caseNode : cases) {
237             DataSchemaNode node = caseNode.getDataChildByName(arg);
238             if (node != null) {
239                 return node;
240             }
241         }
242         return null;
243     }
244
245     private <T> T newInstanceOf(Class<?> newType) {
246         try {
247             @SuppressWarnings("unchecked")
248             T ret = (T) newType.newInstance();
249             return ret;
250         } catch (InstantiationException e) {
251             throw new IllegalStateException(e);
252         } catch (IllegalAccessException e) {
253             throw new IllegalStateException(e);
254         }
255     }
256
257     @Override
258     public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(Class<T> type) {
259         IdentifierCodec<?> obj = identifierCodecs.get(type);
260         if (obj != null) {
261             return obj;
262         }
263         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
264                 .keyTransformerForIdentifiable(type);
265         BindingCodec<Map<QName, Object>, Object> newInstance;
266         newInstance = newInstanceOf(newCodec);
267         IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
268         identifierCodecs.put(type, newWrapper);
269         return newWrapper;
270     }
271
272     @Override
273     public void onCodecCreated(Class<?> cls) {
274         CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
275     }
276
277     @Override
278     public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(Class<T> object) {
279         @SuppressWarnings("unchecked")
280         IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
281         if (obj != null) {
282             return obj;
283         }
284         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
285                 .keyTransformerForIdentifier(object);
286         BindingCodec<Map<QName, Object>, Object> newInstance;
287         newInstance = newInstanceOf(newCodec);
288         IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
289         identifierCodecs.put(object, newWrapper);
290         return newWrapper;
291     }
292
293     @SuppressWarnings("rawtypes")
294     public ChoiceCaseCodecImpl getCaseCodecFor(Class caseClass) {
295         ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
296         if (potential != null) {
297             return potential;
298         }
299         ConcreteType typeref = Types.typeForClass(caseClass);
300         ChoiceCaseCodecImpl caseCodec = typeToCaseNodes.get(typeref);
301
302         @SuppressWarnings("unchecked")
303         Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.schema);
304         BindingCodec newInstance = newInstanceOf(newCodec);
305         caseCodec.setDelegate(newInstance);
306         caseCodecs.put(caseClass, caseCodec);
307
308         for (Entry<Class<?>, ChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
309             if (choice.getKey().isAssignableFrom(caseClass)) {
310                 choice.getValue().cases.put(caseClass, caseCodec);
311             }
312         }
313         return caseCodec;
314     }
315
316     public void onModuleContextAdded(SchemaContext schemaContext, Module module, ModuleContext context) {
317         pathToType.putAll(context.getChildNodes());
318         captureCases(context.getCases(), schemaContext);
319     }
320
321     private void captureCases(Map<SchemaPath, GeneratedTypeBuilder> cases, SchemaContext module) {
322         for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
323             ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
324                     .getValue().getName());
325             ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
326             if (node == null) {
327                 LOG.error("YANGTools Bug: SchemaNode for {}, with path {} was not found in context.",
328                         typeref.getFullyQualifiedName(), caseNode.getKey());
329                 continue;
330             }
331
332             pathToType.put(caseNode.getKey(), caseNode.getValue());
333             @SuppressWarnings("rawtypes")
334             ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
335             typeToCaseNodes.putIfAbsent(typeref, value);
336         }
337     }
338
339     @Override
340     public void onGlobalContextUpdated(SchemaContext context) {
341         currentSchema = context;
342     }
343
344     @SuppressWarnings({ "unchecked", "rawtypes" })
345     @Override
346     public void onChoiceCodecCreated(Class<?> choiceClass,
347             Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec, ChoiceNode schema) {
348         ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
349         checkState(oldCodec == null);
350         BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
351         ChoiceCodecImpl<?> newCodec = new ChoiceCodecImpl(delegate);
352         choiceCodecs.put(choiceClass, newCodec);
353         CodecMapping.setClassToCaseMap(choiceCodec, (Map<Class<?>, BindingCodec<?, ?>>) classToCaseRawCodec);
354         CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
355         
356         tryToCreateCasesCodecs(schema);
357
358     }
359
360     private void tryToCreateCasesCodecs(ChoiceNode schema) {
361         for(ChoiceCaseNode caseNode : schema.getCases()) {
362             SchemaPath path = caseNode.getPath();
363             GeneratedTypeBuilder type;
364             if(path != null && (type = pathToType.get(path)) != null) {
365                 Class<?> caseClass = ClassLoaderUtils.tryToLoadClassWithTCCL(type.getFullyQualifiedName());
366                 if(caseClass != null) {
367                     getCaseCodecFor(caseClass);
368                 }
369             }
370         }
371
372     }
373
374     @Override
375     public void onValueCodecCreated(Class<?> valueClass, Class<?> valueCodec) {
376     }
377
378     @Override
379     public void onCaseCodecCreated(Class<?> choiceClass,
380             Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
381     }
382
383     @Override
384     public void onDataContainerCodecCreated(Class<?> dataClass,
385             Class<? extends BindingCodec<?,?>> dataCodec) {
386         if (Augmentable.class.isAssignableFrom(dataClass)) {
387             AugmentableCompositeCodec augmentableCodec = getAugmentableCodec(dataClass);
388             CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
389         }
390
391     }
392
393     private AugmentableCompositeCodec getAugmentableCodec(Class<?> dataClass) {
394         AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass);
395         if (ret != null) {
396             return ret;
397         }
398         ret = new AugmentableCompositeCodec(dataClass);
399         augmentableCodecs.put(dataClass, ret);
400         return ret;
401     }
402
403     private static abstract class IntermediateCodec<T> implements //
404             DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
405
406         private final BindingCodec<Map<QName, Object>, Object> delegate;
407
408         @Override
409         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
410             return delegate;
411         }
412
413         public IntermediateCodec(BindingCodec<Map<QName, Object>, Object> delegate) {
414             this.delegate = delegate;
415         }
416
417         @Override
418         public Node<?> serialize(ValueWithQName<T> input) {
419             Map<QName, Object> intermediateOutput = delegate.serialize(input);
420             return toNode(intermediateOutput);
421         }
422     }
423
424     private static class IdentifierCodecImpl<T extends Identifier<?>> //
425             extends IntermediateCodec<T> //
426             implements IdentifierCodec<T> {
427
428         public IdentifierCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
429             super(delegate);
430         }
431
432         @Override
433         public ValueWithQName<T> deserialize(Node<?> input) {
434             QName qname = input.getNodeType();
435             @SuppressWarnings("unchecked")
436             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
437             return new ValueWithQName<T>(qname, value);
438         }
439
440         @Override
441         public CompositeNode serialize(ValueWithQName<T> input) {
442             return (CompositeNode) super.serialize(input);
443         }
444     }
445
446     private static class DataContainerCodecImpl<T extends DataContainer> //
447             extends IntermediateCodec<T> //
448             implements DataContainerCodec<T> {
449
450         public DataContainerCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
451             super(delegate);
452         }
453
454         @Override
455         public ValueWithQName<T> deserialize(Node<?> input) {
456             if (input == null) {
457                 return null;
458             }
459             QName qname = input.getNodeType();
460             @SuppressWarnings("unchecked")
461             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
462             return new ValueWithQName<T>(qname, value);
463         }
464
465         @Override
466         public CompositeNode serialize(ValueWithQName<T> input) {
467             return (CompositeNode) super.serialize(input);
468         }
469     }
470
471     @SuppressWarnings("rawtypes")
472     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
473             Delegator<BindingCodec> {
474         private final boolean augmenting;
475         private BindingCodec delegate;
476
477         private final Set<String> validNames;
478         private final Set<QName> validQNames;
479         private ChoiceCaseNode schema;
480
481         public ChoiceCaseCodecImpl(ChoiceCaseNode caseNode) {
482             this.delegate = NOT_READY_CODEC;
483             this.schema = caseNode;
484             validNames = new HashSet<>();
485             validQNames = new HashSet<>();
486             for (DataSchemaNode node : caseNode.getChildNodes()) {
487                 QName qname = node.getQName();
488                 validQNames.add(qname);
489                 validNames.add(qname.getLocalName());
490             }
491             augmenting = caseNode.isAugmenting();
492         }
493
494         @Override
495         public ValueWithQName<T> deserialize(Node<?> input) {
496             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
497         }
498
499         @Override
500         public CompositeNode serialize(ValueWithQName<T> input) {
501             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
502         }
503
504         public BindingCodec getDelegate() {
505             return delegate;
506         }
507
508         public void setDelegate(BindingCodec delegate) {
509             this.delegate = delegate;
510         }
511
512         public ChoiceCaseNode getSchema() {
513             return schema;
514         }
515
516         @Override
517         public boolean isAcceptable(Node<?> input) {
518             if (input instanceof CompositeNode) {
519                 if (augmenting) {
520                     return checkAugmenting((CompositeNode) input);
521                 } else {
522                     return checkLocal((CompositeNode) input);
523                 }
524             }
525             return false;
526         }
527
528         private boolean checkLocal(CompositeNode input) {
529             QName parent = input.getNodeType();
530             for (Node<?> childNode : input.getChildren()) {
531                 QName child = childNode.getNodeType();
532                 if (!Objects.equals(parent.getNamespace(), child.getNamespace())
533                         || !Objects.equals(parent.getRevision(), child.getRevision())) {
534                     continue;
535                 }
536                 if (validNames.contains(child.getLocalName())) {
537                     return true;
538                 }
539             }
540             return false;
541         }
542
543         private boolean checkAugmenting(CompositeNode input) {
544             for (Node<?> child : input.getChildren()) {
545                 if (validQNames.contains(child.getNodeType())) {
546                     return true;
547                 }
548             }
549             return false;
550         }
551     }
552
553     private static class ChoiceCodecImpl<T> implements ChoiceCodec<T> {
554
555         private final BindingCodec<Map<QName, Object>, Object> delegate;
556
557         @SuppressWarnings("rawtypes")
558         private final Map<Class, ChoiceCaseCodecImpl<?>> cases = new WeakHashMap<>();
559
560         private final CaseCompositeNodeMapFacade CompositeToCase;
561
562         public ChoiceCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
563             this.delegate = delegate;
564             this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
565         }
566
567         @Override
568         public ValueWithQName<T> deserialize(Node<?> input) {
569             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
570         }
571
572         @Override
573         public Node<?> serialize(ValueWithQName<T> input) {
574             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
575         }
576
577         public CaseCompositeNodeMapFacade getCompositeToCase() {
578             return CompositeToCase;
579         }
580
581         public Map<Class, ChoiceCaseCodecImpl<?>> getCases() {
582             return cases;
583         }
584
585         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
586             return delegate;
587         }
588
589     }
590
591     @SuppressWarnings("rawtypes")
592     private class CaseClassMapFacade extends MapFacadeBase {
593
594         @Override
595         public Set<java.util.Map.Entry<Class, BindingCodec<Object, Object>>> entrySet() {
596             return Collections.emptySet();
597         }
598
599         @Override
600         public BindingCodec get(Object key) {
601             if (key instanceof Class) {
602                 Class cls = (Class) key;
603                 // bindingClassEncountered(cls);
604                 ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
605                 return caseCodec.getDelegate();
606             }
607             return null;
608         }
609     }
610
611     @SuppressWarnings("rawtypes")
612     private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
613
614         final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
615
616         public CaseCompositeNodeMapFacade(Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
617             this.choiceCases = choiceCases;
618         }
619
620         @Override
621         public BindingCodec get(Object key) {
622             if (!(key instanceof CompositeNode)) {
623                 return null;
624             }
625             for (java.util.Map.Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
626                 ChoiceCaseCodecImpl<?> codec = entry.getValue();
627                 if (codec.isAcceptable((CompositeNode) key)) {
628                     return codec.getDelegate();
629                 }
630             }
631             return null;
632         }
633
634     }
635
636     /**
637      * This map is used as only facade for {@link BindingCodec} in different
638      * classloaders to retrieve codec dynamicly based on provided key.
639      * 
640      * @param <T>
641      *            Key type
642      */
643     @SuppressWarnings("rawtypes")
644     private static abstract class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
645
646         @Override
647         public boolean containsKey(Object key) {
648             return get(key) != null;
649         }
650
651         @Override
652         public void clear() {
653             throw notModifiable();
654         }
655
656         @Override
657         public boolean equals(Object obj) {
658             return super.equals(obj);
659         }
660
661         @Override
662         public BindingCodec remove(Object key) {
663             return null;
664         }
665
666         @Override
667         public int size() {
668             return 0;
669         }
670
671         @Override
672         public Collection<BindingCodec<?, ?>> values() {
673             return Collections.emptySet();
674         }
675
676         private UnsupportedOperationException notModifiable() {
677             return new UnsupportedOperationException("Not externally modifiable.");
678         }
679
680         @Override
681         public BindingCodec<Map<QName, Object>, Object> put(T key, BindingCodec<?, ?> value) {
682             throw notModifiable();
683         }
684
685         @Override
686         public void putAll(Map<? extends T, ? extends BindingCodec<?, ?>> m) {
687             throw notModifiable();
688         }
689
690         @Override
691         public int hashCode() {
692             return super.hashCode();
693         }
694
695         @Override
696         public boolean isEmpty() {
697             return true;
698         }
699
700         @Override
701         public Set<T> keySet() {
702             return Collections.emptySet();
703         }
704
705         @Override
706         public Set<java.util.Map.Entry<T, BindingCodec<?, ?>>> entrySet() {
707             return Collections.emptySet();
708         }
709
710         @Override
711         public boolean containsValue(Object value) {
712             return false;
713         }
714     }
715
716     @SuppressWarnings({ "rawtypes", "unchecked" })
717     private class AugmentableCompositeCodec implements BindingCodec {
718
719         private final Class augmentableType;
720
721         Map<Class, BindingCodec> rawAugmentationCodecs = new WeakHashMap<>();
722
723         public AugmentableCompositeCodec(Class type) {
724             checkArgument(Augmentable.class.isAssignableFrom(type));
725             augmentableType = type;
726         }
727
728         @Override
729         public Object serialize(Object input) {
730             if (input instanceof Augmentable<?>) {
731
732                 Map<Class, Augmentation> augmentations = getAugmentations(input);
733                 return serializeImpl(augmentations);
734             }
735             return null;
736         }
737
738         private Map<Class, Augmentation> getAugmentations(Object input) {
739             Field augmentationField;
740             try {
741                 augmentationField = input.getClass().getDeclaredField("augmentation");
742                 augmentationField.setAccessible(true);
743                 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
744                 return augMap;
745             } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
746                 LOG.debug("Could not read augmentations for {}", input, e);
747             }
748             return Collections.emptyMap();
749         }
750
751         private List serializeImpl(Map<Class, Augmentation> input) {
752             List ret = new ArrayList<>();
753             for (Entry<Class, Augmentation> entry : input.entrySet()) {
754                 BindingCodec codec = getRawCodecForAugmentation(entry.getKey());
755                 List output = (List) codec.serialize(new ValueWithQName(null, entry.getValue()));
756                 ret.addAll(output);
757             }
758             return ret;
759         }
760
761         private BindingCodec getRawCodecForAugmentation(Class key) {
762             BindingCodec ret = rawAugmentationCodecs.get(key);
763             if (ret != null) {
764                 return ret;
765             }
766             try {
767                 Class<? extends BindingCodec> retClass = generator.augmentationTransformerFor(key);
768                 ret = retClass.newInstance();
769                 rawAugmentationCodecs.put(key, ret);
770                 return ret;
771             } catch (InstantiationException e) {
772                 LOG.error("Can not instantiate raw augmentation codec {}", key.getSimpleName(), e);
773             } catch (IllegalAccessException e) {
774                 LOG.debug("BUG: Constructor for {} is not accessible.", key.getSimpleName(), e);
775             }
776             return null;
777         }
778
779         @Override
780         public Map<Class, Augmentation> deserialize(Object input) {
781             Map<Class, Augmentation> ret = new HashMap<>();
782             if (input instanceof CompositeNode) {
783                 for (Entry<Class, BindingCodec> codec : rawAugmentationCodecs.entrySet()) {
784                     Augmentation value = (Augmentation) codec.getValue().deserialize(input);
785                     if (value != null) {
786                         ret.put(codec.getKey(), value);
787                     }
788                 }
789             }
790             return ret;
791         }
792
793         public Map<Class, BindingCodec> getRawAugmentationCodecs() {
794             return rawAugmentationCodecs;
795         }
796
797         public void setRawAugmentationCodecs(Map<Class, BindingCodec> rawAugmentationCodecs) {
798             this.rawAugmentationCodecs = rawAugmentationCodecs;
799         }
800
801         public Class getAugmentableType() {
802             return augmentableType;
803         }
804     }
805
806     @SuppressWarnings({ "rawtypes", "unchecked" })
807     private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
808
809         private BindingCodec delegate;
810
811         @Override
812         public BindingCodec getDelegate() {
813             if (delegate == null) {
814                 throw new IllegalStateException("Codec not initialized yet.");
815             }
816             return delegate;
817         }
818
819         @Override
820         public Object deserialize(Object input) {
821             return getDelegate().deserialize(input);
822         }
823
824         @Override
825         public Object serialize(Object input) {
826             return getDelegate().serialize(input);
827         }
828     }
829 }