Merge "Common yang and xml loader for tests"
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / dom / serializer / impl / TransformerGenerator.xtend
1 package org.opendaylight.controller.sal.binding.dom.serializer.impl
2
3 import javassist.ClassPool
4 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
5 import org.opendaylight.yangtools.yang.model.api.SchemaNode
6 import org.opendaylight.controller.sal.binding.codegen.util.JavassistUtils
7 import javassist.CtClass
8 import java.util.Map
9 import org.opendaylight.yangtools.yang.common.QName
10 import javassist.CtField
11 import static javassist.Modifier.*
12 import static org.opendaylight.controller.sal.binding.dom.serializer.impl.CodecMapping.*
13 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
14 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
15 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
16 import org.opendaylight.yangtools.sal.binding.model.api.Type
17 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
18 import org.opendaylight.yangtools.binding.generator.util.Types
19 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType
20 import java.util.HashMap
21 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
22 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
23 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
24 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
25 import java.util.List
26 import java.util.TreeSet
27 import com.google.common.base.Joiner
28 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
29 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
30 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
31 import static org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils.*;
32 import org.opendaylight.yangtools.yang.binding.BindingDeserializer
33 import org.opendaylight.yangtools.yang.binding.BindingCodec
34 import org.slf4j.LoggerFactory
35 import org.opendaylight.controller.sal.binding.codegen.CodeGenerationException
36 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
37 import java.security.ProtectionDomain
38 import java.io.File
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
40 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
41 import java.util.Map.Entry
42 import java.util.AbstractMap.SimpleEntry
43 import org.opendaylight.yangtools.yang.binding.DataObject
44 import org.opendaylight.yangtools.yang.binding.Augmentation
45 import java.util.Iterator
46 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
47 import java.util.concurrent.ConcurrentHashMap
48 import static extension org.opendaylight.controller.sal.binding.impl.util.YangSchemaUtils.*;
49 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
50 import org.opendaylight.yangtools.yang.model.util.ExtendedType
51 import org.opendaylight.yangtools.yang.model.util.EnumerationType
52 import static com.google.common.base.Preconditions.*
53 import org.opendaylight.yangtools.yang.model.api.SchemaPath
54
55 class TransformerGenerator {
56
57     private static val log = LoggerFactory.getLogger(TransformerGenerator)
58
59     public static val STRING = Types.typeForClass(String);
60     public static val BOOLEAN = Types.typeForClass(Boolean);
61     public static val INTEGER = Types.typeForClass(Integer);
62     public static val INSTANCE_IDENTIFIER = Types.typeForClass(InstanceIdentifier)
63
64     //public static val DECIMAL = Types.typeForClass(Decimal);
65     public static val LONG = Types.typeForClass(Long);
66
67     val ClassPool classPool
68     val extension JavassistUtils utils;
69
70     CtClass BINDING_CODEC
71
72     CtClass ctQName
73
74     @Property
75     var File classFileCapturePath;
76
77     @Property
78     var Map<Type, Type> typeDefinitions = new ConcurrentHashMap();
79
80     @Property
81     var Map<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
82
83     @Property
84     var Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap();
85
86     @Property
87     var Map<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
88
89     @Property
90     var Map<Type, AugmentationSchema> typeToAugmentation = new ConcurrentHashMap();
91
92     @Property
93     var GeneratorListener listener;
94
95     public new(ClassPool pool) {
96         classPool = pool;
97         utils = new JavassistUtils(pool)
98
99         BINDING_CODEC = BindingCodec.asCtClass;
100         ctQName = QName.asCtClass
101     }
102
103     def Class<? extends BindingCodec<Map<QName, Object>, Object>> transformerFor(Class<?> inputType) {
104         return withClassLoaderAndLock(inputType.classLoader, lock) [ |
105             val ret = getGeneratedClass(inputType)
106             if (ret !== null) {
107                 listener.onClassProcessed(inputType);
108                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
109             }
110             val ref = Types.typeForClass(inputType)
111             val node = typeToSchemaNode.get(ref)
112             val typeSpecBuilder = typeToDefinition.get(ref)
113             checkState(typeSpecBuilder !== null, "Could not find typedefinition for %s", inputType.name);
114             val typeSpec = typeSpecBuilder.toInstance();
115             val newret = generateTransformerFor(inputType, typeSpec, node);
116             listener.onClassProcessed(inputType);
117             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
118         ]
119     }
120
121     def Class<? extends BindingCodec<Map<QName, Object>, Object>> transformerFor(Class<?> inputType, DataSchemaNode node) {
122         return withClassLoaderAndLock(inputType.classLoader, lock) [ |
123             val ret = getGeneratedClass(inputType)
124             if (ret !== null) {
125                 listener.onClassProcessed(inputType);
126                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
127             }
128             val ref = Types.typeForClass(inputType)
129             var typeSpecBuilder = typeToDefinition.get(ref)
130             if (typeSpecBuilder == null) {
131                 typeSpecBuilder = pathToType.get(node.path);
132             }
133             checkState(typeSpecBuilder !== null, "Could not find TypeDefinition for %s, $s", inputType.name, node);
134             val typeSpec = typeSpecBuilder.toInstance();
135             val newret = generateTransformerFor(inputType, typeSpec, node);
136             listener.onClassProcessed(inputType);
137             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
138         ]
139     }
140
141     def Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentationTransformerFor(Class<?> inputType) {
142         return withClassLoaderAndLock(inputType.classLoader, lock) [ |
143             val ret = getGeneratedClass(inputType)
144             if (ret !== null) {
145                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
146             }
147             val ref = Types.typeForClass(inputType)
148             val node = typeToAugmentation.get(ref)
149             val typeSpecBuilder = typeToDefinition.get(ref)
150             val typeSpec = typeSpecBuilder.toInstance();
151             val newret = generateAugmentationTransformerFor(inputType, typeSpec, node);
152             listener.onClassProcessed(inputType);
153             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
154         ]
155     }
156
157     def Class<? extends BindingCodec<Object, Object>> caseCodecFor(Class<?> inputType, ChoiceCaseNode node) {
158         return withClassLoaderAndLock(inputType.classLoader, lock) [ |
159             val ret = getGeneratedClass(inputType)
160             if (ret !== null) {
161                 return ret as Class<? extends BindingCodec<Object, Object>>;
162             }
163             val ref = Types.typeForClass(inputType)
164             val typeSpecBuilder = typeToDefinition.get(ref)
165             val typeSpec = typeSpecBuilder.toInstance();
166             val newret = generateCaseCodec(inputType, typeSpec, node);
167             return newret as Class<? extends BindingCodec<Object, Object>>;
168         ]
169     }
170
171     def Class<? extends BindingCodec<Map<QName, Object>, Object>> keyTransformerForIdentifiable(Class<?> parentType) {
172         return withClassLoaderAndLock(parentType.classLoader, lock) [ |
173             val inputName = parentType.name + "Key";
174             val inputType = loadClassWithTCCL(inputName);
175             val ret = getGeneratedClass(inputType)
176             if (ret !== null) {
177                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
178             }
179             val ref = Types.typeForClass(parentType)
180             val node = typeToSchemaNode.get(ref) as ListSchemaNode
181             val typeSpecBuilder = typeToDefinition.get(ref)
182             val typeSpec = typeSpecBuilder.identifierDefinition;
183             val newret = generateKeyTransformerFor(inputType, typeSpec, node);
184             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
185         ]
186     }
187
188     def getIdentifierDefinition(GeneratedTypeBuilder builder) {
189         val inst = builder.toInstance
190         val keyMethod = inst.methodDefinitions.findFirst[name == "getKey"]
191         return keyMethod.returnType as GeneratedTransferObject
192     }
193
194     def Class<? extends BindingCodec<Map<QName, Object>, Object>> keyTransformerForIdentifier(Class<?> inputType) {
195         return withClassLoaderAndLock(inputType.classLoader, lock) [ |
196             val ret = getGeneratedClass(inputType)
197             if (ret !== null) {
198                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
199             }
200             val ref = Types.typeForClass(inputType)
201             val node = typeToSchemaNode.get(ref) as ListSchemaNode
202             val typeSpecBuilder = typeToDefinition.get(ref)
203             val typeSpec = typeSpecBuilder.toInstance();
204             val newret = generateKeyTransformerFor(inputType, typeSpec, node);
205             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
206         ]
207     }
208
209     private def Class<?> keyTransformerFor(Class<?> inputType, GeneratedType type, ListSchemaNode schema) {
210         return withClassLoaderAndLock(inputType.classLoader, lock) [ |
211             val transformer = getGeneratedClass(inputType)
212             if (transformer != null) {
213                 return transformer;
214             }
215             val newret = generateKeyTransformerFor(inputType, type, schema);
216             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
217         ]
218     }
219
220     private def Class<?> getGeneratedClass(Class<? extends Object> cls) {
221
222         try {
223             return loadClassWithTCCL(cls.codecClassName)
224         } catch (ClassNotFoundException e) {
225             return null;
226         }
227     }
228
229     private def Class<?> keyTransformer(GeneratedType type, ListSchemaNode node) {
230         val cls = loadClassWithTCCL(type.resolvedName + "Key");
231         keyTransformerFor(cls, type, node);
232     }
233
234     private def serializer(Type type, DataSchemaNode node) {
235         val cls = loadClassWithTCCL(type.resolvedName);
236         transformerFor(cls, node);
237     }
238
239     private def Class<?> getValueSerializer(GeneratedTransferObject type) {
240         val cls = loadClassWithTCCL(type.resolvedName);
241         val transformer = cls.generatedClass;
242         if (transformer !== null) {
243             return transformer;
244         }
245         return withClassLoaderAndLock(cls.classLoader, lock) [ |
246             val valueTransformer = generateValueTransformer(cls, type);
247             return valueTransformer;
248         ]
249     }
250
251     private def Class<?> getValueSerializer(Enumeration type) {
252         val cls = loadClassWithTCCL(type.resolvedName);
253         val transformer = cls.generatedClass;
254         if (transformer !== null) {
255             return transformer;
256         }
257
258         return withClassLoaderAndLock(cls.classLoader, lock) [ |
259             val valueTransformer = generateValueTransformer(cls, type);
260             return valueTransformer;
261         ]
262     }
263
264     private def generateKeyTransformerFor(Class<? extends Object> inputType, GeneratedType typeSpec, ListSchemaNode node) {
265         try {
266
267             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
268             val properties = typeSpec.allProperties;
269             val ctCls = createClass(inputType.codecClassName) [
270                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
271                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
272                 staticQNameField(node.QName);
273                 implementsType(BINDING_CODEC)
274                 method(Object, "toDomStatic", QName, Object) [
275                     modifiers = PUBLIC + FINAL + STATIC
276                     body = '''
277                         {
278                             «QName.name» _resultName;
279                             if($1 != null) {
280                                 _resultName = «QName.name».create($1,QNAME.getLocalName());
281                             } else {
282                                 _resultName = QNAME;
283                             }
284                             java.util.List _childNodes = new java.util.ArrayList();
285                             «inputType.resolvedName» value = («inputType.name») $2;
286                             «FOR key : node.keyDefinition»
287                                 «val propertyName = key.getterName»
288                                 «val keyDef = node.getDataChildByName(key)»
289                                 «val property = properties.get(propertyName)»
290                                 «serializeProperty(keyDef, property, propertyName)»;
291                             «ENDFOR»
292                             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
293                         }
294                     '''
295                 ]
296                 method(Object, "fromDomStatic", QName, Object) [
297                     modifiers = PUBLIC + FINAL + STATIC
298                     body = '''
299                         {
300                             if($2 == null){
301                                 return  null;
302                             }
303                             «QName.name» _localQName = $1;
304                             java.util.Map _compositeNode = (java.util.Map) $2;
305                             boolean _is_empty = true;
306                             «FOR key : node.keyDefinition»
307                                 «val propertyName = key.getterName»
308                                 «val keyDef = node.getDataChildByName(key)»
309                                 «val property = properties.get(propertyName)»
310                                 «deserializeProperty(keyDef, property, propertyName)»;
311                             «ENDFOR»
312                             «inputType.resolvedName» _value = new «inputType.name»(«node.keyDefinition.
313                             keyConstructorList»);
314                             return _value;
315                         }
316                     '''
317                 ]
318                 method(Object, "serialize", Object) [
319                     body = '''
320                         {
321                             java.util.Map.Entry _input =  (java.util.Map.Entry) $1;
322                             «QName.name» _localQName = («QName.name») _input.getKey();
323                             «inputType.name» _keyValue = («inputType.name») _input.getValue();
324                             return toDomStatic(_localQName,_keyValue);
325                         }
326                     '''
327                 ]
328                 method(Object, "deserialize", Object) [
329                     body = '''
330                         return fromDomStatic(QNAME,$1);
331                     '''
332                 ]
333             ]
334             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
335             log.info("DOM Codec for {} was generated {}", inputType, ret)
336             return ret as Class<? extends BindingCodec<Map<QName,Object>, ?>>;
337         } catch (Exception e) {
338             processException(inputType, e);
339             return null;
340         }
341     }
342
343     private def Class<? extends BindingCodec<Object, Object>> generateCaseCodec(Class<?> inputType, GeneratedType type,
344         ChoiceCaseNode node) {
345         try {
346
347             //log.info("Generating DOM Codec for {} with {}, TCCL is: {}", inputType, inputType.classLoader,Thread.currentThread.contextClassLoader)
348             val ctCls = createClass(type.codecClassName) [
349                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
350                 implementsType(BINDING_CODEC)
351                 staticQNameField(node.QName);
352                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
353                 staticField(it, AUGMENTATION_CODEC, BindingCodec)
354                 method(Object, "toDomStatic", QName, Object) [
355                     modifiers = PUBLIC + FINAL + STATIC
356                     body = '''
357                         {
358                             «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
359                             java.util.List _childNodes = new java.util.ArrayList();
360                             «type.resolvedName» value = («type.resolvedName») $2;
361                             «transformDataContainerBody(type, type.allProperties, node)»
362                             return ($r) _childNodes;
363                         }
364                     '''
365                 ]
366                 method(Object, "serialize", Object) [
367                     body = '''
368                         {
369                             java.util.Map.Entry _input = (java.util.Map.Entry) $1;
370                             «QName.name» _localName = QNAME;
371                             if(_input.getKey() != null) {
372                                 _localName = («QName.name») _input.getKey();
373                             }
374                             return toDomStatic(_localName,_input.getValue());
375                         }
376                     '''
377                 ]
378                 method(Object, "fromDomStatic", QName, Object) [
379                     modifiers = PUBLIC + FINAL + STATIC
380                     body = deserializeBody(type, node)
381                 ]
382                 method(Object, "deserialize", Object) [
383                     body = '''
384                         {
385                             //System.out.println("«type.name»#deserialize: " +$1);
386                             java.util.Map.Entry _input = (java.util.Map.Entry) $1;
387                             return fromDomStatic((«QName.name»)_input.getKey(),_input.getValue());
388                         }
389                     '''
390                 ]
391             ]
392
393             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)  as Class<? extends BindingCodec<Object, Object>>
394             listener?.onDataContainerCodecCreated(inputType, ret);
395             log.info("DOM Codec for {} was generated {}", inputType, ret)
396             return ret;
397         } catch (Exception e) {
398             processException(inputType, e);
399             return null;
400         }
401     }
402
403     private def dispatch  Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(
404         Class<?> inputType, GeneratedType typeSpec, SchemaNode node) {
405         try {
406
407             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
408             val ctCls = createClass(typeSpec.codecClassName) [
409                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
410                 staticQNameField(node.QName);
411                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
412                 staticField(it, AUGMENTATION_CODEC, BindingCodec)
413                 implementsType(BINDING_CODEC)
414                 method(Object, "toDomStatic", QName, Object) [
415                     modifiers = PUBLIC + FINAL + STATIC
416                     body = serializeBodyFacade(typeSpec, node)
417                 ]
418                 method(Object, "serialize", Object) [
419                     body = '''
420                         {
421                             java.util.Map.Entry _input = (java.util.Map.Entry) $1;
422                             «QName.name» _localName = QNAME;
423                             if(_input.getKey() != null) {
424                                 _localName = («QName.name») _input.getKey();
425                             }
426                             return toDomStatic(_localName,_input.getValue());
427                         }
428                     '''
429                 ]
430                 method(Object, "fromDomStatic", QName, Object) [
431                     modifiers = PUBLIC + FINAL + STATIC
432                     body = deserializeBody(typeSpec, node)
433                 ]
434                 method(Object, "deserialize", Object) [
435                     body = '''
436                         return fromDomStatic(QNAME,$1);
437                     '''
438                 ]
439             ]
440
441             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain) as Class<? extends BindingCodec<Map<QName,Object>, Object>>
442             listener?.onDataContainerCodecCreated(inputType, ret);
443             log.info("DOM Codec for {} was generated {}", inputType, ret)
444             return ret;
445         } catch (Exception e) {
446             processException(inputType, e);
447             return null;
448         }
449     }
450
451     private def Class<? extends BindingCodec<Map<QName, Object>, Object>> generateAugmentationTransformerFor(
452         Class<?> inputType, GeneratedType type, AugmentationSchema node) {
453         try {
454
455             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
456             val properties = type.allProperties
457             val ctCls = createClass(type.codecClassName) [
458                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
459                 staticQNameField(node.augmentationQName);
460                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
461                 staticField(it, AUGMENTATION_CODEC, BindingCodec)
462                 implementsType(BINDING_CODEC)
463                 method(Object, "toDomStatic", QName, Object) [
464                     modifiers = PUBLIC + FINAL + STATIC
465                     body = '''
466                         {
467                             //System.out.println("Qname " + $1);
468                             //System.out.println("Value " + $2);
469                             «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
470                             java.util.List _childNodes = new java.util.ArrayList();
471                             «type.resolvedName» value = («type.resolvedName») $2;
472                             «FOR child : node.childNodes»
473                                 «var signature = properties.getFor(child)»
474                                 //System.out.println("«signature.key»" + value.«signature.key»());
475                                 «serializeProperty(child, signature.value, signature.key)»
476                             «ENDFOR»
477                             return ($r) _childNodes;
478                         }
479                     '''
480                 ]
481                 method(Object, "serialize", Object) [
482                     body = '''
483                         {
484                         java.util.Map.Entry _input = (java.util.Map.Entry) $1;
485                         «QName.name» _localName = QNAME;
486                         if(_input.getKey() != null) {
487                             _localName = («QName.name») _input.getKey();
488                         }
489                         return toDomStatic(_localName,_input.getValue());
490                         }
491                     '''
492                 ]
493                 method(Object, "fromDomStatic", QName, Object) [
494                     modifiers = PUBLIC + FINAL + STATIC
495                     body = '''
496                         {
497                             «QName.name» _localQName = QNAME;
498                             
499                             if($2 == null) {
500                             return null;
501                             }
502                             java.util.Map _compositeNode = (java.util.Map) $2;
503                             //System.out.println(_localQName + " " + _compositeNode);
504                             «type.builderName» _builder = new «type.builderName»();
505                             boolean _is_empty = true;
506                             «FOR child : node.childNodes»
507                                 «val signature = properties.getFor(child)»
508                                 «deserializeProperty(child, signature.value, signature.key)»
509                                 _builder.«signature.key.toSetter»(«signature.key»);
510                             «ENDFOR»
511                             if(_is_empty) {
512                                 return null;
513                             }
514                             return _builder.build();
515                         }
516                     '''
517                 ]
518                 method(Object, "deserialize", Object) [
519                     body = '''
520                         return fromDomStatic(QNAME,$1);
521                     '''
522                 ]
523             ]
524
525             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain) as Class<? extends BindingCodec<Map<QName,Object>, Object>>
526             listener?.onDataContainerCodecCreated(inputType, ret);
527             return ret;
528         } catch (Exception e) {
529             processException(inputType, e);
530             return null;
531         }
532     }
533
534     private def dispatch  Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(
535         Class<?> inputType, GeneratedType typeSpec, ChoiceNode node) {
536         try {
537
538             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
539             val ctCls = createClass(typeSpec.codecClassName) [
540                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
541                 //staticQNameField(inputType);
542                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
543                 staticField(it, CLASS_TO_CASE_MAP, Map)
544                 staticField(it, COMPOSITE_TO_CASE, Map)
545                 //staticField(it,QNAME_TO_CASE_MAP,BindingCodec)
546                 implementsType(BINDING_CODEC)
547                 method(List, "toDomStatic", QName, Object) [
548                     modifiers = PUBLIC + FINAL + STATIC
549                     body = '''
550                         {
551                             if($2 == null) {
552                                 return null;
553                             }
554                             «DataObject.name» _baValue = («DataObject.name») $2;
555                             Class _baClass = _baValue.getImplementedInterface();
556                             «BINDING_CODEC.name» _codec =  «CLASS_TO_CASE_MAP».get(_baClass);
557                             if(_codec == null) {
558                                 return null;
559                             }
560                             java.util.Map.Entry _input = new «SimpleEntry.name»($1,_baValue);
561                             Object _ret =  _codec.serialize(_input);
562                             //System.out.println("«typeSpec.name»#toDomStatic: " + _ret);
563                             return («List.name») _ret;
564                         }
565                     '''
566                 ]
567                 method(Object, "serialize", Object) [
568                     body = '''
569                         throw new «UnsupportedOperationException.name»("Direct invocation not supported.");
570                     '''
571                 ]
572                 method(Object, "fromDomStatic", QName, Map) [
573                     modifiers = PUBLIC + FINAL + STATIC
574                     body = '''
575                         {
576                             «BINDING_CODEC.name» _codec = («BINDING_CODEC.name») «COMPOSITE_TO_CASE».get($2);
577                             if(_codec != null) {
578                                 return _codec.deserialize(new «SimpleEntry.name»($1,$2));
579                             }
580                             return null;
581                         }
582                     '''
583                 ]
584                 method(Object, "deserialize", Object) [
585                     body = '''
586                         throw new «UnsupportedOperationException.name»("Direct invocation not supported.");
587                     '''
588                 ]
589             ]
590
591             val rawRet = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
592             val ret = rawRet as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
593             listener?.onChoiceCodecCreated(inputType, ret, node);
594             log.info("DOM Codec for {} was generated {}", inputType, ret)
595             return ret;
596         } catch (Exception e) {
597             processException(inputType, e);
598             return null;
599         }
600     }
601
602     private def keyConstructorList(List<QName> qnames) {
603         val names = new TreeSet<String>()
604         for (name : qnames) {
605             val fieldName = name.getterName;
606             names.add(fieldName);
607         }
608         return Joiner.on(",").join(names);
609     }
610
611     private def serializeBodyFacade(GeneratedType type, SchemaNode node) {
612         val ret = serializeBody(type, node);
613         return ret;
614     }
615
616     private def String deserializeBody(GeneratedType type, SchemaNode node) {
617         val ret = deserializeBodyImpl(type, node);
618         return ret;
619     }
620
621     private def deserializeKey(GeneratedType type, ListSchemaNode node) {
622         if (node.keyDefinition != null && !node.keyDefinition.empty) {
623             return '''
624                 «type.resolvedName»Key getKey = («type.resolvedName»Key) «keyTransformer(type, node).canonicalName».fromDomStatic(_localQName,_compositeNode);
625                 _builder.setKey(getKey);
626             ''';
627         }
628     }
629
630     private def dispatch String deserializeBodyImpl(GeneratedType type, SchemaNode node) '''
631         {
632             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
633             
634             if($2 == null) {
635                 return null;
636             }
637             java.util.Map _compositeNode = (java.util.Map) $2;
638             «type.builderName» _builder = new «type.builderName»();
639             return _builder.build();
640         }
641     '''
642
643     private def dispatch String deserializeBodyImpl(GeneratedType type, ListSchemaNode node) '''
644         {
645             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
646             if($2 == null) {
647                 return null;
648             }
649             java.util.Map _compositeNode = (java.util.Map) $2;
650             «type.builderName» _builder = new «type.builderName»();
651             «deserializeKey(type, node)»
652             «deserializeDataNodeContainerBody(type, node)»
653             «deserializeAugmentations»
654             return _builder.build();
655         }
656     '''
657
658     private def dispatch String deserializeBodyImpl(GeneratedType type, ContainerSchemaNode node) '''
659         {
660             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
661             if($2 == null) {
662                 return null;
663             }
664             java.util.Map _compositeNode = (java.util.Map) $2;
665             «type.builderName» _builder = new «type.builderName»();
666             «deserializeDataNodeContainerBody(type, node)»
667             «deserializeAugmentations»
668             return _builder.build();
669         }
670     '''
671
672     private def dispatch String deserializeBodyImpl(GeneratedType type, ChoiceCaseNode node) '''
673         {
674             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
675             
676             if($2 == null) {
677                 return null;
678             }
679             java.util.Map _compositeNode = (java.util.Map) $2;
680             //System.out.println(_localQName + " " + _compositeNode);
681             «type.builderName» _builder = new «type.builderName»();
682             «deserializeDataNodeContainerBody(type, node)»
683             «deserializeAugmentations»
684             return _builder.build();
685         }
686     '''
687
688     private def deserializeDataNodeContainerBody(GeneratedType type, DataNodeContainer node) {
689         deserializeNodeContainerBodyImpl(type, type.allProperties, node);
690     }
691
692     private def deserializeNodeContainerBodyImpl(GeneratedType type, HashMap<String, Type> properties,
693         DataNodeContainer node) {
694         val ret = '''
695             boolean _is_empty = true;
696             «FOR child : node.childNodes»
697                 «val signature = properties.getFor(child)»
698                 «IF signature !== null»
699                     «deserializeProperty(child, signature.value, signature.key)»
700                     _builder.«signature.key.toSetter»(«signature.key»);
701                 «ENDIF»
702             «ENDFOR»
703         '''
704         return ret;
705     }
706
707     def deserializeAugmentations() '''
708         java.util.Map _augmentation = (java.util.Map) «AUGMENTATION_CODEC».deserialize(_compositeNode);
709         if(_augmentation != null) {
710             «Iterator.name» _entries = _augmentation.entrySet().iterator();
711             while(_entries.hasNext()) {
712                 java.util.Map.Entry _entry = (java.util.Map.Entry) _entries.next();
713                 //System.out.println("Aug. key:" + _entry.getKey());
714                 Class _type = (Class) _entry.getKey();
715                 «Augmentation.resolvedName» _value = («Augmentation.name») _entry.getValue();
716                 if(_value != null) {
717                     _builder.addAugmentation(_type,_value);
718                 }
719             }
720         }
721     '''
722
723     private def dispatch CharSequence deserializeProperty(ListSchemaNode schema, ParameterizedType type,
724         String propertyName) '''
725         java.util.List _dom_«propertyName» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
726             localName»"));
727         //System.out.println("«propertyName»#deCode"+_dom_«propertyName»);
728         java.util.List «propertyName» = new java.util.ArrayList();
729         if(_dom_«propertyName» != null) {
730             java.util.List _serialized = new java.util.ArrayList();
731             java.util.Iterator _iterator = _dom_«propertyName».iterator();
732             boolean _hasNext = _iterator.hasNext();
733             while(_hasNext) {
734                 Object _listItem = _iterator.next();
735                 _is_empty = false;
736                 //System.out.println("  item" + _listItem);
737                 Object _value = «type.actualTypeArguments.get(0).serializer(schema).resolvedName».fromDomStatic(_localQName,_listItem);
738                 //System.out.println("  value" + _value);
739                 «propertyName».add(_value);
740                 _hasNext = _iterator.hasNext();
741             }
742         }
743         
744         //System.out.println(" list" + «propertyName»);
745     '''
746
747     private def dispatch CharSequence deserializeProperty(LeafListSchemaNode schema, ParameterizedType type,
748         String propertyName) '''
749         java.util.List _dom_«propertyName» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
750             localName»"));
751         java.util.List «propertyName» = new java.util.ArrayList();
752         if(_dom_«propertyName» != null) {
753             java.util.List _serialized = new java.util.ArrayList();
754             java.util.Iterator _iterator = _dom_«propertyName».iterator();
755             boolean _hasNext = _iterator.hasNext();
756             while(_hasNext) {
757                 _is_empty = false;
758                 Object _listItem = _iterator.next();
759                 if(_listItem instanceof java.util.Map.Entry) {
760                     Object _innerValue = ((java.util.Map.Entry) _listItem).getValue();
761                     Object _value = «deserializeValue(type.actualTypeArguments.get(0), "_innerValue")»;
762                     «propertyName».add(_value);
763                 }
764                 _hasNext = _iterator.hasNext();
765             }
766         }
767     '''
768
769     private def dispatch CharSequence deserializeProperty(LeafSchemaNode schema, Type type, String propertyName) '''
770         java.util.List _dom_«propertyName»_list = 
771             _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.localName»"));
772         «type.resolvedName» «propertyName» = null;
773         if(_dom_«propertyName»_list != null && _dom_«propertyName»_list.size() > 0) {
774             _is_empty = false;
775             java.util.Map.Entry _dom_«propertyName» = (java.util.Map.Entry) _dom_«propertyName»_list.get(0);
776             Object _inner_value = _dom_«propertyName».getValue();
777             «propertyName» = «deserializeValue(type, "_inner_value")»;
778         }
779     '''
780
781     private def dispatch CharSequence deserializeProperty(ContainerSchemaNode schema, Type type,
782         String propertyName) '''
783         java.util.List _dom_«propertyName»_list = 
784             _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.localName»"));
785         «type.resolvedName» «propertyName» = null;
786         if(_dom_«propertyName»_list != null && _dom_«propertyName»_list.size() > 0) {
787             _is_empty = false;
788             java.util.Map _dom_«propertyName» = (java.util.Map) _dom_«propertyName»_list.get(0);
789             «propertyName» =  «type.serializer(schema).resolvedName».fromDomStatic(_localQName,_dom_«propertyName»);
790         }
791     '''
792
793     private def dispatch CharSequence deserializeProperty(ChoiceNode schema, Type type, String propertyName) '''
794         «type.resolvedName» «propertyName» = «type.serializer(schema).resolvedName».fromDomStatic(_localQName,_compositeNode);
795         if(«propertyName» != null) {
796             _is_empty = false;
797         }
798     '''
799
800     private def dispatch String deserializeValue(GeneratedTransferObject type, String domParameter) '''
801         («type.resolvedName») «type.valueSerializer.resolvedName».fromDomValue(«domParameter»)
802     '''
803
804     private def dispatch String deserializeValue(Enumeration type, String domParameter) '''
805         («type.resolvedName») «type.valueSerializer.resolvedName».fromDomValue(«domParameter»)
806     '''
807
808     private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateValueTransformer(
809         Class<?> inputType, GeneratedTransferObject typeSpec) {
810         try {
811
812             val returnType = typeSpec.valueReturnType;
813             if (returnType == null) {
814                 val ctCls = createDummyImplementation(inputType, typeSpec);
815                 val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
816                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
817             }
818             var hasBinding = false;
819             try {
820                 val bindingCodecClass = loadClassWithTCCL(BINDING_CODEC.name);
821                 hasBinding = bindingCodecClass !== null;
822             } catch (ClassNotFoundException e) {
823                 hasBinding = false;
824             }
825             val hasYangBinding = hasBinding
826             val ctCls = createClass(typeSpec.codecClassName) [
827                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
828                 if (hasYangBinding) {
829                     implementsType(BINDING_CODEC)
830                     staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
831                     implementsType(BindingDeserializer.asCtClass)
832                 }
833                 method(Object, "toDomValue", Object) [
834                     modifiers = PUBLIC + FINAL + STATIC
835                     body = '''
836                         {
837                             //System.out.println("«inputType.simpleName»#toDomValue: "+$1);
838                             
839                             if($1 == null) {
840                                 return null;
841                             }
842                             «typeSpec.resolvedName» _encapsulatedValue = («typeSpec.resolvedName») $1;
843                             //System.out.println("«inputType.simpleName»#toDomValue:Enc: "+_encapsulatedValue);
844                             «returnType.resolvedName» _value =  _encapsulatedValue.getValue();
845                             //System.out.println("«inputType.simpleName»#toDomValue:DeEnc: "+_value);
846                             Object _domValue = «serializeValue(returnType, "_value")»;
847                             return _domValue;
848                         }
849                     '''
850                 ]
851                 method(Object, "serialize", Object) [
852                     body = '''
853                         {
854                             return toDomValue($1);
855                         }
856                     '''
857                 ]
858                 method(Object, "fromDomValue", Object) [
859                     modifiers = PUBLIC + FINAL + STATIC
860                     body = '''
861                         {
862                             //System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
863                             
864                             if($1 == null) {
865                                 return null;
866                             }
867                             «returnType.resolvedName» _simpleValue = «deserializeValue(returnType, "$1")»;
868                             «typeSpec.resolvedName» _value = new «typeSpec.resolvedName»(_simpleValue);
869                             return _value;
870                         }
871                     '''
872                 ]
873                 method(Object, "deserialize", Object) [
874                     body = '''{
875                             return fromDomValue($1);
876                     }
877                     '''
878                 ]
879             ]
880
881             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
882             log.info("DOM Codec for {} was generated {}", inputType, ret)
883             return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
884         } catch (Exception e) {
885             log.error("Cannot compile DOM Codec for {}", inputType, e);
886             val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
887             exception.addSuppressed(e);
888             throw exception;
889         }
890
891     }
892
893     private def createDummyImplementation(Class<?> object, GeneratedTransferObject typeSpec) {
894         log.info("Generating Dummy DOM Codec for {} with {}", object, object.classLoader)
895         return createClass(typeSpec.codecClassName) [
896             //staticField(Map,"AUGMENTATION_SERIALIZERS");
897             implementsType(BINDING_CODEC)
898             implementsType(BindingDeserializer.asCtClass)
899             method(Object, "toDomValue", Object) [
900                 modifiers = PUBLIC + FINAL + STATIC
901                 body = '''return null;'''
902             ]
903             method(Object, "serialize", Object) [
904                 body = '''
905                     {
906                         return toDomValue($1);
907                     }
908                 '''
909             ]
910             method(Object, "fromDomValue", Object) [
911                 modifiers = PUBLIC + FINAL + STATIC
912                 body = '''return null;'''
913             ]
914             method(Object, "deserialize", Object) [
915                 body = '''{
916                         return fromDomValue($1);
917                     }
918                     '''
919             ]
920         ]
921     }
922
923     private def Type getValueReturnType(GeneratedTransferObject object) {
924         for (prop : object.properties) {
925             if (prop.name == "value") {
926                 return prop.returnType;
927             }
928         }
929         if (object.superType != null) {
930             return getValueReturnType(object.superType);
931         }
932         return null;
933     }
934
935     private def dispatch Class<?> generateValueTransformer(Class<?> inputType, Enumeration typeSpec) {
936         try {
937             val typeRef = new ReferencedTypeImpl(typeSpec.packageName, typeSpec.name);
938             val schema = typeToSchemaNode.get(typeRef) as ExtendedType;
939             val enumSchema = schema.baseType as EnumerationType;
940
941             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
942             val ctCls = createClass(typeSpec.codecClassName) [
943                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
944                 //implementsType(BINDING_CODEC)
945                 method(Object, "toDomValue", Object) [
946                     modifiers = PUBLIC + FINAL + STATIC
947                     body = '''{
948                             if($1 == null) {
949                                 return null;
950                             }
951                             «typeSpec.resolvedName» _value = («typeSpec.resolvedName») $1;
952                             «FOR en : enumSchema.values»
953                             if(«typeSpec.resolvedName».«BindingGeneratorUtil.parseToClassName(en.name)».equals(_value)) {
954                                 return "«en.name»";
955                             }
956                             «ENDFOR»
957                             return null;
958                         }
959                     '''
960                 ]
961                 method(Object, "serialize", Object) [
962                     body = '''
963                         return toDomValue($1);
964                     '''
965                 ]
966                 method(Object, "fromDomValue", Object) [
967                     modifiers = PUBLIC + FINAL + STATIC
968                     body = '''
969                         {
970                             if($1 == null) {
971                                 return null;
972                             }
973                             String _value = (String) $1;
974                             «FOR en : enumSchema.values»
975                                 if("«en.name»".equals(_value)) {
976                                     return «typeSpec.resolvedName».«BindingGeneratorUtil.parseToClassName(en.name)»;
977                                 }
978                             «ENDFOR»
979                             return null;
980                         }
981                     '''
982                 ]
983                 method(Object, "deserialize", Object) [
984                     body = '''
985                         return fromDomValue($1);
986                     '''
987                 ]
988             ]
989
990             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
991             log.info("DOM Codec for {} was generated {}", inputType, ret)
992             return ret;
993         } catch (CodeGenerationException e) {
994             throw new CodeGenerationException("Cannot compile Transformator for " + inputType, e);
995         } catch (Exception e) {
996             log.error("Cannot compile DOM Codec for {}", inputType, e);
997             val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
998             exception.addSuppressed(e);
999             throw exception;
1000         }
1001
1002     }
1003
1004     def Class<?> toClassImpl(CtClass newClass, ClassLoader loader, ProtectionDomain domain) {
1005         val cls = newClass.toClass(loader, domain);
1006         if (classFileCapturePath !== null) {
1007             newClass.writeFile(classFileCapturePath.absolutePath);
1008         }
1009         listener?.onCodecCreated(cls);
1010         return cls;
1011     }
1012
1013     def debugWriteClass(CtClass class1) {
1014         val path = class1.name.replace(".", "/") + ".class"
1015
1016         val captureFile = new File(classFileCapturePath, path);
1017         captureFile.createNewFile
1018
1019     }
1020
1021     private def dispatch String deserializeValue(Type type, String domParameter) {
1022         if (INSTANCE_IDENTIFIER.equals(type)) {
1023
1024             return '''(«InstanceIdentifier.name») «INSTANCE_IDENTIFIER_CODEC».deserialize(«domParameter»)'''
1025         }
1026
1027         return '''(«type.resolvedName») «domParameter»'''
1028
1029     }
1030
1031     /** 
1032      * Default catch all
1033      * 
1034      **/
1035     private def dispatch CharSequence deserializeProperty(DataSchemaNode container, Type type, String propertyName) '''
1036         «type.resolvedName» «propertyName» = null;
1037     '''
1038
1039     private def dispatch CharSequence deserializeProperty(DataSchemaNode container, GeneratedTypeBuilder type,
1040         String propertyName) {
1041         _deserializeProperty(container, type.toInstance, propertyName)
1042     }
1043
1044     public static def toSetter(String it) {
1045
1046         if (startsWith("is")) {
1047             return "set" + substring(2);
1048         } else if (startsWith("get")) {
1049             return "set" + substring(3);
1050         }
1051         return "set" + it;
1052     }
1053
1054     /* 
1055     private def dispatch CharSequence deserializeProperty(DataSchemaNode container,GeneratedType type, String propertyName) '''
1056         «type.resolvedName» «propertyName» = value.«propertyName»();
1057         if(«propertyName» != null) {
1058             Object domValue = «type.serializer».toDomStatic(QNAME,«propertyName»);
1059             _childNodes.add(domValue);
1060         }
1061     '''
1062     */
1063     private def getBuilderName(GeneratedType type) '''«type.resolvedName»Builder'''
1064
1065     private def staticQNameField(CtClass it, QName node) {
1066         val field = new CtField(ctQName, "QNAME", it);
1067         field.modifiers = PUBLIC + FINAL + STATIC;
1068         addField(field,
1069             '''«QName.asCtClass.name».create("«node.namespace»","«node.formattedRevision»","«node.localName»")''')
1070     }
1071
1072     private def dispatch String serializeBody(GeneratedType type, ListSchemaNode node) '''
1073         {
1074             «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
1075             java.util.List _childNodes = new java.util.ArrayList();
1076             «type.resolvedName» value = («type.resolvedName») $2;
1077             «transformDataContainerBody(type, type.allProperties, node)»
1078             «serializeAugmentations»
1079             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
1080         }
1081     '''
1082
1083     private def dispatch String serializeBody(GeneratedType type, ContainerSchemaNode node) '''
1084         {
1085             «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
1086             java.util.List _childNodes = new java.util.ArrayList();
1087             «type.resolvedName» value = («type.resolvedName») $2;
1088             «transformDataContainerBody(type, type.allProperties, node)»
1089             «serializeAugmentations»
1090             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
1091         }
1092     '''
1093
1094     private def dispatch String serializeBody(GeneratedType type, ChoiceCaseNode node) '''
1095         {
1096         «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
1097             java.util.List _childNodes = new java.util.ArrayList();
1098             «type.resolvedName» value = («type.resolvedName») $2;
1099             «transformDataContainerBody(type, type.allProperties, node)»
1100             «serializeAugmentations»
1101             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
1102         }
1103     '''
1104
1105     private def dispatch String serializeBody(GeneratedType type, SchemaNode node) '''
1106         {
1107         «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
1108             java.util.List _childNodes = new java.util.ArrayList();
1109             «type.resolvedName» value = («type.resolvedName») $2;
1110             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
1111         }
1112     '''
1113
1114     private def transformDataContainerBody(Type type, Map<String, Type> properties, DataNodeContainer node) {
1115         val ret = '''
1116             «FOR child : node.childNodes»
1117                 «val signature = properties.getFor(child)»
1118                 «IF signature !== null»
1119                     //System.out.println("«type.name»#«signature.key»" + value.«signature.key»());
1120                     «serializeProperty(child, signature.value, signature.key)»
1121                 «ENDIF»
1122             «ENDFOR»
1123         '''
1124         return ret;
1125     }
1126
1127     private def serializeAugmentations() '''
1128         java.util.List _augmentations = (java.util.List) «AUGMENTATION_CODEC».serialize(value);
1129         if(_augmentations != null) {
1130             _childNodes.addAll(_augmentations);
1131         }
1132     '''
1133
1134     def Entry<String, Type> getFor(Map<String, Type> map, DataSchemaNode node) {
1135         var sig = map.get(node.getterName);
1136         if (sig != null) {
1137             return new SimpleEntry(node.getterName, sig);
1138         }
1139         sig = map.get(node.booleanGetterName);
1140         if (sig != null) {
1141             return new SimpleEntry(node.booleanGetterName, map.get(node.booleanGetterName));
1142         }
1143         return null;
1144     }
1145
1146     private static def String getBooleanGetterName(DataSchemaNode node) {
1147         return "is" + BindingGeneratorUtil.parseToClassName(node.QName.localName);
1148     }
1149
1150     private static def String getGetterName(DataSchemaNode node) {
1151         return "get" + BindingGeneratorUtil.parseToClassName(node.QName.localName);
1152     }
1153
1154     private static def String getGetterName(QName node) {
1155         return "get" + BindingGeneratorUtil.parseToClassName(node.localName);
1156     }
1157
1158     private def dispatch CharSequence serializeProperty(ListSchemaNode schema, ParameterizedType type,
1159         String propertyName) '''
1160         «type.resolvedName» «propertyName» = value.«propertyName»();
1161         //System.out.println("«propertyName»:" + «propertyName»);
1162         if(«propertyName» != null) {
1163             java.util.Iterator _iterator = «propertyName».iterator();
1164             boolean _hasNext = _iterator.hasNext();
1165             while(_hasNext) {
1166                 Object _listItem = _iterator.next();
1167                 Object _domValue = «type.actualTypeArguments.get(0).serializer(schema).resolvedName».toDomStatic(_resultName,_listItem);
1168                 _childNodes.add(_domValue);
1169                 _hasNext = _iterator.hasNext();
1170             }
1171         }
1172     '''
1173
1174     private def dispatch CharSequence serializeProperty(LeafSchemaNode schema, Type type, String propertyName) '''
1175         «type.resolvedName» «propertyName» = value.«propertyName»();
1176         
1177         if(«propertyName» != null) {
1178             «QName.name» _qname = «QName.name».create(_resultName,"«schema.QName.localName»");
1179             Object _propValue = «serializeValue(type, propertyName)»;
1180             if(_propValue != null) {
1181                 Object _domValue = java.util.Collections.singletonMap(_qname,_propValue);
1182                 _childNodes.add(_domValue);
1183             }
1184         }
1185     '''
1186
1187     private def dispatch serializeValue(GeneratedTransferObject type, String parameter) '''«type.valueSerializer.
1188         resolvedName».toDomValue(«parameter»)'''
1189
1190     private def dispatch serializeValue(Enumeration type, String parameter) '''«type.valueSerializer.resolvedName».toDomValue(«parameter»)'''
1191
1192     private def dispatch serializeValue(Type signature, String property) {
1193         if (INSTANCE_IDENTIFIER == signature) {
1194             return '''«INSTANCE_IDENTIFIER_CODEC».serialize(«property»)'''
1195         }
1196         return '''«property»''';
1197     }
1198
1199     private def dispatch CharSequence serializeProperty(LeafListSchemaNode schema, ParameterizedType type,
1200         String propertyName) '''
1201         «type.resolvedName» «propertyName» = value.«propertyName»();
1202         if(«propertyName» != null) {
1203             «QName.name» _qname = «QName.name».create(_resultName,"«schema.QName.localName»");
1204             java.util.Iterator _iterator = «propertyName».iterator();
1205             boolean _hasNext = _iterator.hasNext();
1206             while(_hasNext) {
1207                 Object _listItem = _iterator.next();
1208                 Object _propValue = «serializeValue(type.actualTypeArguments.get(0), "_listItem")»;
1209                 Object _domValue = java.util.Collections.singletonMap(_qname,_propValue);
1210                 _childNodes.add(_domValue);
1211                 _hasNext = _iterator.hasNext();
1212             }
1213         }
1214     '''
1215
1216     private def dispatch CharSequence serializeProperty(ChoiceNode container, GeneratedType type,
1217         String propertyName) '''
1218         «type.resolvedName» «propertyName» = value.«propertyName»();
1219         if(«propertyName» != null) {
1220             java.util.List domValue = «type.serializer(container).resolvedName».toDomStatic(_resultName,«propertyName»);
1221             _childNodes.addAll(domValue);
1222         }
1223     '''
1224
1225     /** 
1226      * Default catch all
1227      * 
1228      **/
1229     private def dispatch CharSequence serializeProperty(DataSchemaNode container, Type type, String propertyName) '''
1230         «type.resolvedName» «propertyName» = value.«propertyName»();
1231         if(«propertyName» != null) {
1232             Object domValue = «propertyName»;
1233             _childNodes.add(domValue);
1234         }
1235     '''
1236
1237     private def dispatch CharSequence serializeProperty(DataSchemaNode container, GeneratedTypeBuilder type,
1238         String propertyName) {
1239         serializeProperty(container, type.toInstance, propertyName)
1240     }
1241
1242     private def dispatch CharSequence serializeProperty(DataSchemaNode container, GeneratedType type,
1243         String propertyName) '''
1244         «type.resolvedName» «propertyName» = value.«propertyName»();
1245         if(«propertyName» != null) {
1246             Object domValue = «type.serializer(container).resolvedName».toDomStatic(_resultName,«propertyName»);
1247             _childNodes.add(domValue);
1248         }
1249     '''
1250
1251     private def codecClassName(GeneratedType typeSpec) {
1252         return '''«typeSpec.resolvedName»$Broker$Codec$DOM'''
1253     }
1254
1255     private def codecClassName(Class<?> typeSpec) {
1256         return '''«typeSpec.name»$Broker$Codec$DOM'''
1257     }
1258
1259     private def HashMap<String, Type> getAllProperties(GeneratedType type) {
1260         val ret = new HashMap<String, Type>();
1261         type.collectAllProperties(ret);
1262         return ret;
1263     }
1264
1265     private def dispatch void collectAllProperties(GeneratedType type, Map<String, Type> set) {
1266         for (definition : type.methodDefinitions) {
1267             set.put(definition.name, definition.returnType);
1268         }
1269         for (property : type.properties) {
1270             set.put(property.getterName, property.returnType);
1271         }
1272         for (parent : type.implements) {
1273             parent.collectAllProperties(set);
1274         }
1275     }
1276
1277     def String getGetterName(GeneratedProperty property) {
1278         return "get" + property.name.toFirstUpper
1279     }
1280
1281     private def dispatch void collectAllProperties(Type type, Map<String, Type> set) {
1282         // NOOP for generic type.
1283     }
1284
1285     def String getResolvedName(Type type) {
1286         return type.asCtClass.name;
1287     }
1288
1289     def String getResolvedName(Class<?> type) {
1290         return type.asCtClass.name;
1291     }
1292
1293     def CtClass asCtClass(Type type) {
1294         val cls = loadClassWithTCCL(type.fullyQualifiedName)
1295         return cls.asCtClass;
1296     }
1297
1298     private def dispatch processException(Class<?> inputType, CodeGenerationException e) {
1299         log.error("Cannot compile DOM Codec for {}. One of it's prerequisites was not generated.", inputType);
1300         throw e;
1301     }
1302
1303     private def dispatch processException(Class<?> inputType, Exception e) {
1304         log.error("Cannot compile DOM Codec for {}", inputType, e);
1305         val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType, e);
1306         throw exception;
1307     }
1308
1309 }
1310
1311 @Data
1312 class PropertyPair {
1313
1314     String getterName;
1315
1316     Type type;
1317
1318     @Property
1319     Type returnType;
1320     @Property
1321     SchemaNode schemaNode;
1322 }