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