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