Added configuration options to MD-SAL
[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             if(node == null) {
284                 LOG.error("YANGTools Bug: SchemaNode for {}, with path {} was not found in context.",typeref.getFullyQualifiedName(), caseNode.getKey());
285                 continue;
286             }
287             
288             @SuppressWarnings("rawtypes")
289             ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
290             typeToCaseNodes.putIfAbsent(typeref, value);
291         }
292     }
293
294     @Override
295     public void onGlobalContextUpdated(SchemaContext context) {
296         currentSchema = context;
297     }
298
299     @SuppressWarnings({ "unchecked", "rawtypes" })
300     @Override
301     public void onChoiceCodecCreated(Class<?> choiceClass,
302             Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
303         ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
304         checkState(oldCodec == null);
305         BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
306         ChoiceCodecImpl<?> newCodec = new ChoiceCodecImpl(delegate);
307         choiceCodecs.put(choiceClass, newCodec);
308         CodecMapping.setClassToCaseMap(choiceCodec, (Map<Class, BindingCodec>) classToCaseRawCodec);
309         CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
310
311     }
312
313     @Override
314     public void onValueCodecCreated(Class<?> valueClass, Class<?> valueCodec) {
315         // TODO Auto-generated method stub
316
317     }
318
319     @Override
320     public void onCaseCodecCreated(Class<?> choiceClass,
321             Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
322         // TODO Auto-generated method stub
323
324     }
325
326     @Override
327     public void onDataContainerCodecCreated(Class<?> dataClass,
328             Class<? extends BindingCodec<Map<QName, Object>, Object>> dataCodec) {
329         if (Augmentable.class.isAssignableFrom(dataClass)) {
330             AugmentableCompositeCodec augmentableCodec = getAugmentableCodec(dataClass);
331             CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
332         }
333
334     }
335
336     private AugmentableCompositeCodec getAugmentableCodec(Class<?> dataClass) {
337         AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass);
338         if (ret != null) {
339             return ret;
340         }
341         ret = new AugmentableCompositeCodec(dataClass);
342         augmentableCodecs.put(dataClass, ret);
343         return ret;
344     }
345
346     private static abstract class IntermediateCodec<T> implements //
347             DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
348
349         private final BindingCodec<Map<QName, Object>, Object> delegate;
350
351         @Override
352         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
353             return delegate;
354         }
355
356         public IntermediateCodec(BindingCodec<Map<QName, Object>, Object> delegate) {
357             this.delegate = delegate;
358         }
359
360         @Override
361         public Node<?> serialize(ValueWithQName<T> input) {
362             Map<QName, Object> intermediateOutput = delegate.serialize(input);
363             return toNode(intermediateOutput);
364         }
365     }
366
367     private static class IdentifierCodecImpl<T extends Identifier<?>> //
368             extends IntermediateCodec<T> //
369             implements IdentifierCodec<T> {
370
371         public IdentifierCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
372             super(delegate);
373         }
374
375         @Override
376         public ValueWithQName<T> deserialize(Node<?> input) {
377             QName qname = input.getNodeType();
378             @SuppressWarnings("unchecked")
379             T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
380             return new ValueWithQName<T>(qname, value);
381         }
382
383         @Override
384         public CompositeNode serialize(ValueWithQName<T> input) {
385             return (CompositeNode) super.serialize(input);
386         }
387     }
388
389     private static class DataContainerCodecImpl<T extends DataContainer> //
390             extends IntermediateCodec<T> //
391             implements DataContainerCodec<T> {
392
393         public DataContainerCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
394             super(delegate);
395         }
396
397         @Override
398         public ValueWithQName<T> deserialize(Node<?> input) {
399             if (input == null) {
400                 return null;
401             }
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     @SuppressWarnings("rawtypes")
415     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
416             Delegator<BindingCodec> {
417         private final boolean augmenting;
418         private BindingCodec delegate;
419
420         private final Set<String> validNames;
421         private final Set<QName> validQNames;
422         private ChoiceCaseNode schema;
423
424         public ChoiceCaseCodecImpl(ChoiceCaseNode caseNode) {
425             this.delegate = NOT_READY_CODEC;
426             this.schema = caseNode;
427             validNames = new HashSet<>();
428             validQNames = new HashSet<>();
429             for (DataSchemaNode node : caseNode.getChildNodes()) {
430                 QName qname = node.getQName();
431                 validQNames.add(qname);
432                 validNames.add(qname.getLocalName());
433             }
434             augmenting = caseNode.isAugmenting();
435         }
436
437         @Override
438         public ValueWithQName<T> deserialize(Node<?> input) {
439             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
440         }
441
442         @Override
443         public CompositeNode serialize(ValueWithQName<T> input) {
444             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
445         }
446
447         public BindingCodec getDelegate() {
448             return delegate;
449         }
450
451         public void setDelegate(BindingCodec delegate) {
452             this.delegate = delegate;
453         }
454
455         public ChoiceCaseNode getSchema() {
456             return schema;
457         }
458
459         @Override
460         public boolean isAcceptable(Node<?> input) {
461             if (false == (input instanceof CompositeNode)) {
462                 if (augmenting) {
463                     return checkAugmenting((CompositeNode) input);
464                 } else {
465                     return checkLocal((CompositeNode) input);
466                 }
467             }
468             return false;
469         }
470
471         private boolean checkLocal(CompositeNode input) {
472             QName parent = input.getNodeType();
473             for (Node<?> childNode : input.getChildren()) {
474                 QName child = childNode.getNodeType();
475                 if (false == Objects.equals(parent.getNamespace(), child.getNamespace())) {
476                     continue;
477                 }
478                 if (false == Objects.equals(parent.getRevision(), child.getRevision())) {
479                     continue;
480                 }
481                 if (validNames.contains(child.getLocalName())) {
482                     return true;
483                 }
484             }
485             return false;
486         }
487
488         private boolean checkAugmenting(CompositeNode input) {
489             for (Node<?> child : input.getChildren()) {
490                 if (validQNames.contains(child.getNodeType())) {
491                     return true;
492                 }
493             }
494             return false;
495         }
496     }
497
498     private static class ChoiceCodecImpl<T> implements ChoiceCodec<T> {
499
500         private final BindingCodec<Map<QName, Object>, Object> delegate;
501
502         @SuppressWarnings("rawtypes")
503         private final Map<Class, ChoiceCaseCodecImpl<?>> cases = new WeakHashMap<>();
504
505         private final CaseCompositeNodeMapFacade CompositeToCase;
506
507         public ChoiceCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
508             this.delegate = delegate;
509             this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
510         }
511
512         @Override
513         public ValueWithQName<T> deserialize(Node<?> input) {
514             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
515         }
516
517         @Override
518         public Node<?> serialize(ValueWithQName<T> input) {
519             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
520         }
521
522         public CaseCompositeNodeMapFacade getCompositeToCase() {
523             return CompositeToCase;
524         }
525
526         public Map<Class, ChoiceCaseCodecImpl<?>> getCases() {
527             return cases;
528         }
529
530         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
531             return delegate;
532         }
533
534     }
535
536     @SuppressWarnings("rawtypes")
537     private class CaseClassMapFacade extends MapFacadeBase {
538
539         @Override
540         public Set<java.util.Map.Entry<Class, BindingCodec<Object, Object>>> entrySet() {
541             return null;
542         }
543
544         @Override
545         public BindingCodec get(Object key) {
546             if (key instanceof Class) {
547                 Class cls = (Class) key;
548                 bindingClassEncountered(cls);
549                 ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
550                 return caseCodec.getDelegate();
551             }
552             return null;
553         }
554     }
555
556     @SuppressWarnings("rawtypes")
557     private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
558
559         final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
560
561         public CaseCompositeNodeMapFacade(Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
562             this.choiceCases = choiceCases;
563         }
564
565         @Override
566         public Set<java.util.Map.Entry<CompositeNode, BindingCodec>> entrySet() {
567             return null;
568         }
569
570         @Override
571         public BindingCodec get(Object key) {
572             if (false == (key instanceof CompositeNode)) {
573                 return null;
574             }
575             for (java.util.Map.Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
576                 ChoiceCaseCodecImpl<?> codec = entry.getValue();
577                 if (codec.isAcceptable((CompositeNode) key)) {
578                     return codec.getDelegate();
579                 }
580             }
581             return null;
582         }
583     }
584
585     /**
586      * This map is used as only facade for {@link BindingCodec} in different
587      * classloaders to retrieve codec dynamicly based on provided key.
588      * 
589      * @param <T>
590      *            Key type
591      */
592     @SuppressWarnings("rawtypes")
593     private static abstract class MapFacadeBase<T> implements Map<T, BindingCodec> {
594
595         @Override
596         public boolean containsKey(Object key) {
597             return get(key) != null;
598         }
599
600         @Override
601         public void clear() {
602             throw notModifiable();
603         }
604
605         @Override
606         public boolean equals(Object obj) {
607             return super.equals(obj);
608         }
609
610         @Override
611         public BindingCodec remove(Object key) {
612             return null;
613         }
614
615         @Override
616         public int size() {
617             return 0;
618         }
619
620         @Override
621         public Collection<BindingCodec> values() {
622             return null;
623         }
624
625         private UnsupportedOperationException notModifiable() {
626             return new UnsupportedOperationException("Not externally modifiable.");
627         }
628
629         @Override
630         public BindingCodec<Map<QName, Object>, Object> put(T key, BindingCodec value) {
631             throw notModifiable();
632         }
633
634         @Override
635         public void putAll(Map<? extends T, ? extends BindingCodec> m) {
636             throw notModifiable();
637         }
638
639         @Override
640         public int hashCode() {
641             return super.hashCode();
642         }
643
644         @Override
645         public boolean isEmpty() {
646             return false;
647         }
648
649         @Override
650         public Set<T> keySet() {
651             return null;
652         }
653
654         @Override
655         public Set<java.util.Map.Entry<T, BindingCodec>> entrySet() {
656             return null;
657         }
658
659         @Override
660         public boolean containsValue(Object value) {
661             return false;
662         }
663     }
664
665     @SuppressWarnings({ "rawtypes", "unchecked" })
666     private class AugmentableCompositeCodec implements BindingCodec {
667
668         private final Class augmentableType;
669
670         Map<Class, BindingCodec> rawAugmentationCodecs = new WeakHashMap<>();
671
672         public AugmentableCompositeCodec(Class type) {
673             checkArgument(Augmentable.class.isAssignableFrom(type));
674             augmentableType = type;
675         }
676
677         @Override
678         public Object serialize(Object input) {
679             if (input instanceof Augmentable<?>) {
680
681                 Map<Class, Augmentation> augmentations = getAugmentations(input);
682                 return serializeImpl(augmentations);
683             }
684             return null;
685         }
686
687         private Map<Class, Augmentation> getAugmentations(Object input) {
688             Field augmentationField;
689             try {
690                 augmentationField = input.getClass().getDeclaredField("augmentation");
691                 augmentationField.setAccessible(true);
692                 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
693                 return augMap;
694             } catch (NoSuchFieldException e) {
695
696             } catch (SecurityException e) {
697
698             } catch (IllegalArgumentException e) {
699
700             } catch (IllegalAccessException e) {
701
702             }
703             return Collections.emptyMap();
704         }
705
706         private List serializeImpl(Map<Class, Augmentation> input) {
707             List ret = new ArrayList<>();
708             for (Entry<Class, Augmentation> entry : input.entrySet()) {
709                 BindingCodec codec = getRawCodecForAugmentation(entry.getKey());
710                 List output = (List) codec.serialize(new ValueWithQName(null, entry.getValue()));
711                 ret.addAll(output);
712             }
713             return ret;
714         }
715
716         private BindingCodec getRawCodecForAugmentation(Class key) {
717             BindingCodec ret = rawAugmentationCodecs.get(key);
718             if (ret != null) {
719                 return ret;
720             }
721             try {
722                 Class<? extends BindingCodec> retClass = generator.augmentationTransformerFor(key);
723                 ret = retClass.newInstance();
724                 rawAugmentationCodecs.put(key, ret);
725                 return ret;
726             } catch (InstantiationException e) {
727
728             } catch (IllegalAccessException e) {
729
730             }
731             return null;
732         }
733
734         @Override
735         public Map<Class, Augmentation> deserialize(Object input) {
736             Map<Class, Augmentation> ret = new HashMap<>();
737             if (input instanceof CompositeNode) {
738                 for (Entry<Class, BindingCodec> codec : rawAugmentationCodecs.entrySet()) {
739                     Augmentation value = (Augmentation) codec.getValue().deserialize(input);
740                     if (value != null) {
741                         ret.put(codec.getKey(), value);
742                     }
743                 }
744             }
745             return ret;
746         }
747
748         public Map<Class, BindingCodec> getRawAugmentationCodecs() {
749             return rawAugmentationCodecs;
750         }
751
752         public void setRawAugmentationCodecs(Map<Class, BindingCodec> rawAugmentationCodecs) {
753             this.rawAugmentationCodecs = rawAugmentationCodecs;
754         }
755
756         public Class getAugmentableType() {
757             return augmentableType;
758         }
759     }
760
761     @SuppressWarnings({ "rawtypes", "unchecked" })
762     private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
763
764         private BindingCodec delegate;
765
766         @Override
767         public BindingCodec getDelegate() {
768             if (delegate == null) {
769                 throw new IllegalStateException("Codec not initialized yet.");
770             }
771             return delegate;
772         }
773
774         @Override
775         public Object deserialize(Object input) {
776             return getDelegate().deserialize(input);
777         }
778
779         @Override
780         public Object serialize(Object input) {
781             return getDelegate().serialize(input);
782         }
783     }
784 }