BUG-1276: fixed generated union constructor
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / TransformerGenerator.xtend
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.sal.binding.generator.impl
9
10 import com.google.common.base.Joiner
11 import java.io.File
12 import java.security.ProtectionDomain
13 import java.util.AbstractMap.SimpleEntry
14 import java.util.Collection
15 import java.util.Collections
16 import java.util.HashMap
17 import java.util.HashSet
18 import java.util.Iterator
19 import java.util.List
20 import java.util.Map
21 import java.util.Map.Entry
22 import java.util.Set
23 import java.util.TreeSet
24 import javassist.CannotCompileException
25 import javassist.ClassPool
26 import javassist.CtClass
27 import javassist.CtField
28 import javassist.CtMethod
29 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
30 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
31 import org.opendaylight.yangtools.binding.generator.util.Types
32 import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
33 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException
34 import org.opendaylight.yangtools.sal.binding.generator.util.XtendHelper
35 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
36 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
37 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
38 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
39 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType
40 import org.opendaylight.yangtools.sal.binding.model.api.Type
41 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
42 import org.opendaylight.yangtools.yang.binding.Augmentation
43 import org.opendaylight.yangtools.yang.binding.BindingCodec
44 import org.opendaylight.yangtools.yang.binding.BindingDeserializer
45 import org.opendaylight.yangtools.yang.binding.BindingMapping
46 import org.opendaylight.yangtools.yang.binding.DataObject
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
48 import org.opendaylight.yangtools.yang.common.QName
49 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
50 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
51 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
52 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
53 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
54 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
55 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
56 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
57 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
58 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
59 import org.opendaylight.yangtools.yang.model.api.SchemaNode
60 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
61 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
62 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit
63 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition
64 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition
65 import org.opendaylight.yangtools.yang.model.util.EnumerationType
66 import org.opendaylight.yangtools.yang.model.util.ExtendedType
67 import org.slf4j.LoggerFactory
68
69 import static com.google.common.base.Preconditions.*
70 import static javassist.Modifier.*
71 import static org.opendaylight.yangtools.sal.binding.generator.impl.CodecMapping.*
72
73 import static extension org.opendaylight.yangtools.sal.binding.generator.util.YangSchemaUtils.*
74 import java.util.ArrayList
75
76 class TransformerGenerator extends AbstractTransformerGenerator {
77     private static val LOG = LoggerFactory.getLogger(TransformerGenerator)
78
79     public static val STRING = Types.typeForClass(String);
80     public static val BOOLEAN = Types.typeForClass(Boolean);
81     public static val INTEGER = Types.typeForClass(Integer);
82     public static val INSTANCE_IDENTIFIER = Types.typeForClass(InstanceIdentifier);
83     //public static val DECIMAL = Types.typeForClass(Decimal);
84     public static val LONG = Types.typeForClass(Long);
85     public static val CLASS_TYPE = Types.typeForClass(Class);
86
87     @Property
88     var File classFileCapturePath;
89
90     val CtClass BINDING_CODEC
91     val CtClass ctQName
92
93     public new(TypeResolver typeResolver, ClassPool pool) {
94         super(typeResolver, pool)
95
96         BINDING_CODEC = BindingCodec.asCtClass;
97         ctQName = QName.asCtClass
98     }
99
100     override transformerForImpl(Class inputType) {
101         return runOnClassLoader(inputType.classLoader) [ |
102             val ret = getGeneratedClass(inputType)
103             if (ret !== null) {
104                 listener.onClassProcessed(inputType);
105                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
106             }
107             val ref = Types.typeForClass(inputType)
108             val node = getSchemaNode(ref)
109             createMapping(inputType, node, null)
110             val typeSpecBuilder = getDefinition(ref)
111             checkState(typeSpecBuilder !== null, "Could not find typedefinition for %s", inputType.name);
112             val typeSpec = typeSpecBuilder.toInstance();
113             val newret = generateTransformerFor(inputType, typeSpec, node);
114             listener.onClassProcessed(inputType);
115             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
116         ]
117     }
118
119     def Class<? extends BindingCodec<Map<QName, Object>, Object>> transformerFor(Class<?> inputType, DataSchemaNode node) {
120         return runOnClassLoader(inputType.classLoader) [ |
121             createMapping(inputType, node, null)
122             val ret = getGeneratedClass(inputType)
123             if (ret !== null) {
124                 listener.onClassProcessed(inputType);
125                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
126             }
127             val ref = Types.typeForClass(inputType)
128             var typeSpecBuilder = getDefinition(ref)
129             if (typeSpecBuilder == null) {
130                 typeSpecBuilder = getTypeBuilder(node.path);
131             }
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     override augmentationTransformerForImpl(Class inputType) {
142         return runOnClassLoader(inputType.classLoader) [ |
143
144             val ret = getGeneratedClass(inputType)
145             if (ret !== null) {
146                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
147             }
148             val ref = Types.typeForClass(inputType)
149             val node = getAugmentation(ref)
150             val typeSpecBuilder = getDefinition(ref)
151             val typeSpec = typeSpecBuilder.toInstance();
152             //mappingForNodes(node.childNodes, typeSpec.allProperties, bindingId)
153             val newret = generateAugmentationTransformerFor(inputType, typeSpec, node);
154             listener.onClassProcessed(inputType);
155             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
156         ]
157     }
158
159     override caseCodecForImpl(Class inputType, ChoiceCaseNode node) {
160         return runOnClassLoader(inputType.classLoader) [ |
161             createMapping(inputType, node, null)
162             val ret = getGeneratedClass(inputType)
163             if (ret !== null) {
164                 return ret as Class<? extends BindingCodec<Object, Object>>;
165             }
166             val ref = Types.typeForClass(inputType)
167             val typeSpecBuilder = getDefinition(ref)
168             val typeSpec = typeSpecBuilder.toInstance();
169             val newret = generateCaseCodec(inputType, typeSpec, node);
170             return newret as Class<? extends BindingCodec<Object, Object>>;
171         ]
172     }
173
174     override keyTransformerForIdentifiableImpl(Class parentType) {
175         return runOnClassLoader(parentType.classLoader) [ |
176             val inputName = parentType.name + "Key";
177             val inputType = loadClass(inputName);
178             val ret = getGeneratedClass(inputType)
179             if (ret !== null) {
180                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
181             }
182             val ref = Types.typeForClass(parentType)
183             val node = getSchemaNode(ref) as ListSchemaNode
184             val typeSpecBuilder = getDefinition(ref)
185             val typeSpec = typeSpecBuilder.identifierDefinition;
186             val newret = generateKeyTransformerFor(inputType, typeSpec, node);
187             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
188         ]
189     }
190
191     private def void createMapping(Class<?> inputType, SchemaNode node, InstanceIdentifier<?> parentId) {
192         var ClassLoader cl = inputType.classLoader
193         if (cl === null) {
194             cl = Thread.currentThread.contextClassLoader
195         }
196         ClassLoaderUtils.withClassLoader(cl,
197             [ |
198                 if (!(node instanceof DataNodeContainer)) {
199                     return null
200                 }
201                 var InstanceIdentifier<?> bindingId = getBindingIdentifierByPath(node.path)
202                 if (bindingId != null) {
203                     return null
204                 }
205                 val ref = Types.typeForClass(inputType)
206                 var typeSpecBuilder = getDefinition(ref)
207                 if (typeSpecBuilder == null) {
208                     typeSpecBuilder = getTypeBuilder(node.path);
209                 }
210                 checkState(typeSpecBuilder !== null, "Could not find type definition for %s, $s", inputType.name, node);
211                 val typeSpec = typeSpecBuilder.toInstance();
212                 var InstanceIdentifier<?> parent
213                 if (parentId == null) {
214                     bindingId = InstanceIdentifier.create(inputType as Class)
215                     parent = bindingId
216                     putPathToBindingIdentifier(node.path, bindingId)
217                 } else {
218                     parent = putPathToBindingIdentifier(node.path, parentId, inputType)
219                 }
220                 val Map<String, Type> properties = typeSpec.allProperties
221                 if (node instanceof DataNodeContainer) {
222                     mappingForNodes((node as DataNodeContainer).childNodes, properties, parent)
223                 } else if (node instanceof ChoiceNode) {
224                     mappingForNodes((node as ChoiceNode).cases, properties, parent)
225                 }
226                 return null;
227             ])
228     }
229
230     private def void mappingForNodes(Collection<? extends DataSchemaNode> childNodes, Map<String, Type> properties,
231         InstanceIdentifier<?> parent) {
232         for (DataSchemaNode child : childNodes) {
233             val signature = properties.getFor(child)
234             if (signature != null) {
235                 val Type childType = signature.value
236                 var Class<?> childTypeClass = null;
237                 if (child instanceof ListSchemaNode && childType instanceof ParameterizedType) {
238                     childTypeClass = loadClass((childType as ParameterizedType).actualTypeArguments.get(0))
239                 } else {
240                     childTypeClass = loadClass(childType)
241                 }
242                 createMapping(childTypeClass, child, parent)
243             }
244         }
245     }
246
247     def getIdentifierDefinition(GeneratedTypeBuilder builder) {
248         val inst = builder.toInstance
249         val keyMethod = inst.methodDefinitions.findFirst[name == "getKey"]
250         return keyMethod.returnType as GeneratedTransferObject
251     }
252
253     override keyTransformerForIdentifierImpl(Class inputType) {
254         return runOnClassLoader(inputType.classLoader) [ |
255             val ret = getGeneratedClass(inputType)
256             if (ret !== null) {
257                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
258             }
259             val ref = Types.typeForClass(inputType)
260             val node = getSchemaNode(ref) as ListSchemaNode
261             val typeSpecBuilder = getDefinition(ref)
262             val typeSpec = typeSpecBuilder.toInstance();
263             val newret = generateKeyTransformerFor(inputType, typeSpec, node);
264             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
265         ]
266     }
267
268     private def Class<?> keyTransformerFor(Class<?> inputType, GeneratedType type, ListSchemaNode schema) {
269         return runOnClassLoader(inputType.classLoader) [ |
270             val transformer = getGeneratedClass(inputType)
271             if (transformer != null) {
272                 return transformer;
273             }
274             val newret = generateKeyTransformerFor(inputType, type, schema);
275             return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
276         ]
277     }
278
279     private def Class<?> getGeneratedClass(Class<? extends Object> cls) {
280
281         try {
282             return loadClass(cls.codecClassName)
283         } catch (ClassNotFoundException e) {
284             return null;
285         }
286     }
287
288     private def Class<?> keyTransformer(GeneratedType type, ListSchemaNode node) {
289         val cls = loadClass(type.resolvedName + "Key");
290         keyTransformerFor(cls, type, node);
291     }
292
293     private def serializer(Type type, DataSchemaNode node) {
294         val cls = loadClass(type.resolvedName);
295         transformerFor(cls, node);
296     }
297
298     private def Class<?> valueSerializer(GeneratedTransferObject type, TypeDefinition<?> typeDefinition) {
299         val cls = loadClass(type.resolvedName);
300         val transformer = cls.generatedClass;
301         if (transformer !== null) {
302             return transformer;
303         }
304         var baseType = typeDefinition;
305         while (baseType.baseType != null) {
306             baseType = baseType.baseType;
307         }
308         val finalType = baseType;
309         return runOnClassLoader(cls.classLoader) [ |
310             val valueTransformer = generateValueTransformer(cls, type, finalType);
311             return valueTransformer;
312         ]
313     }
314
315     private def Class<?> valueSerializer(Enumeration type, TypeDefinition<?> typeDefinition) {
316         val cls = loadClass(type.resolvedName);
317         val transformer = cls.generatedClass;
318         if (transformer !== null) {
319             return transformer;
320         }
321
322         return runOnClassLoader(cls.classLoader) [ |
323             val valueTransformer = generateValueTransformer(cls, type, typeDefinition);
324             return valueTransformer;
325         ]
326     }
327
328     private def generateKeyTransformerFor(Class<? extends Object> inputType, GeneratedType typeSpec, ListSchemaNode node) {
329         try {
330
331             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
332             val properties = typeSpec.allProperties;
333             val ctCls = createClass(inputType.codecClassName) [
334                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
335                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
336                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
337                 staticQNameField(node.QName);
338                 implementsType(BINDING_CODEC)
339                 method(Object, "toDomStatic", #[QName, Object]) [
340                     modifiers = PUBLIC + FINAL + STATIC
341                     bodyChecked = '''
342                         {
343                             «QName.name» _resultName;
344                             if($1 != null) {
345                                 _resultName = «QName.name».create($1,QNAME.getLocalName());
346                             } else {
347                                 _resultName = QNAME;
348                             }
349                             java.util.List _childNodes = new java.util.ArrayList();
350                             «inputType.resolvedName» value = («inputType.name») $2;
351                             «FOR key : node.keyDefinition»
352                                 «val propertyName = key.getterName»
353                                 «val keyDef = node.getDataChildByName(key)»
354                                 «val property = properties.get(propertyName)»
355                                 «serializeProperty(keyDef, property, propertyName)»;
356                             «ENDFOR»
357                             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
358                         }
359                     '''
360                 ]
361                 method(Object, "fromDomStatic", #[QName, Object]) [
362                     modifiers = PUBLIC + FINAL + STATIC
363                     bodyChecked = '''
364                         {
365                             if($2 == null){
366                                 return  null;
367                             }
368                             «QName.name» _localQName = $1;
369                             java.util.Map _compositeNode = (java.util.Map) $2;
370                             boolean _is_empty = true;
371                             «FOR key : node.keyDefinition»
372                                 «val propertyName = key.getterName»
373                                 «val keyDef = node.getDataChildByName(key)»
374                                 «val property = properties.get(propertyName)»
375                                 «deserializeProperty(keyDef, property, propertyName)»;
376                             «ENDFOR»
377                             «inputType.resolvedName» _value = new «inputType.name»(«node.keyDefinition.
378                             keyConstructorList»);
379                             return _value;
380                         }
381                     '''
382                 ]
383                 method(Object, "serialize", Object) [
384                     bodyChecked = '''
385                         {
386                             java.util.Map.Entry _input =  (java.util.Map.Entry) $1;
387                             «QName.name» _localQName = («QName.name») _input.getKey();
388                             «inputType.name» _keyValue = («inputType.name») _input.getValue();
389                             return toDomStatic(_localQName,_keyValue);
390                         }
391                     '''
392                 ]
393                 method(Object, "deserialize", Object) [
394                     bodyChecked = '''
395                         {
396                             «QName.name» _qname = QNAME;
397                             if($1 instanceof java.util.Map.Entry) {
398                                 _qname = («QName.name») ((java.util.Map.Entry) $1).getKey();
399                             }
400                             return fromDomStatic(_qname,$1);
401                         }
402                     '''
403                 ]
404             ]
405             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
406             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
407             return ret as Class<? extends BindingCodec<Map<QName,Object>, ?>>;
408         } catch (Exception e) {
409             processException(inputType, e);
410             return null;
411         }
412     }
413
414     private def Class<? extends BindingCodec<Object, Object>> generateCaseCodec(Class<?> inputType, GeneratedType type,
415         ChoiceCaseNode node) {
416         try {
417             //log.info("Generating DOM Codec for {} with {}, TCCL is: {}", inputType, inputType.classLoader,Thread.currentThread.contextClassLoader)
418             val ctCls = createClass(type.codecClassName) [
419                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
420                 implementsType(BINDING_CODEC)
421                 staticQNameField(node.QName);
422                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
423                 staticField(it, AUGMENTATION_CODEC, BindingCodec)
424                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
425                 method(Object, "toDomStatic", #[QName, Object]) [
426                     modifiers = PUBLIC + FINAL + STATIC
427                     bodyChecked = '''
428                         {
429                             «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
430                             java.util.List _childNodes = new java.util.ArrayList();
431                             «type.resolvedName» value = («type.resolvedName») $2;
432                             «transformDataContainerBody(type, type.allProperties, node)»
433                             return ($r) _childNodes;
434                         }
435                     '''
436                 ]
437                 method(Object, "serialize", Object) [
438                     bodyChecked = '''
439                         {
440                             java.util.Map.Entry _input = (java.util.Map.Entry) $1;
441                             «QName.name» _localName = QNAME;
442                             if(_input.getKey() != null) {
443                                 _localName = («QName.name») _input.getKey();
444                             }
445                             return toDomStatic(_localName,_input.getValue());
446                         }
447                     '''
448                 ]
449                 method(Object, "fromDomStatic", #[QName, Object, InstanceIdentifier]) [
450                     modifiers = PUBLIC + FINAL + STATIC
451                     bodyChecked = deserializeBody(type, node, getBindingIdentifierByPath(node.path))
452                 ]
453                 method(Object, "deserialize", #[Object, InstanceIdentifier]) [
454                     bodyChecked = '''
455                         {
456                             //System.out.println("«type.name»#deserialize: " +$1);
457                             java.util.Map.Entry _input = (java.util.Map.Entry) $1;
458                             return fromDomStatic((«QName.name»)_input.getKey(),_input.getValue(),$2);
459                         }
460                     '''
461                 ]
462             ]
463
464             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)  as Class<? extends BindingCodec<Object, Object>>
465             listener?.onDataContainerCodecCreated(inputType, ret);
466             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
467             return ret;
468         } catch (Exception e) {
469             processException(inputType, e);
470             return null;
471         }
472     }
473
474     private def dispatch  Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(
475         Class<?> inputType, GeneratedType typeSpec, SchemaNode node) {
476         try {
477
478             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
479             val ctCls = createClass(typeSpec.codecClassName) [
480                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
481                 staticQNameField(node.QName);
482                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
483                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
484                 staticField(it, AUGMENTATION_CODEC, BindingCodec)
485                 implementsType(BINDING_CODEC)
486
487                 method(Object, "toDomStatic", #[QName, Object]) [
488                     modifiers = PUBLIC + FINAL + STATIC
489                     bodyChecked = serializeBodyFacade(typeSpec, node)
490                 ]
491                 method(Object, "serialize", Object) [
492                     bodyChecked = '''
493                         {
494                             java.util.Map.Entry _input = (java.util.Map.Entry) $1;
495                             «QName.name» _localName = QNAME;
496                             if(_input.getKey() != null) {
497                                 _localName = («QName.name») _input.getKey();
498                             }
499                             return toDomStatic(_localName,_input.getValue());
500                         }
501                     '''
502                 ]
503
504                 method(Object, "fromDomStatic", #[QName, Object, InstanceIdentifier]) [
505                     modifiers = PUBLIC + FINAL + STATIC
506                     bodyChecked = deserializeBody(typeSpec, node, getBindingIdentifierByPath(node.path))
507                 ]
508
509                 method(Object, "deserialize", #[Object, InstanceIdentifier]) [
510                     bodyChecked = '''
511                         {
512                             «QName.name» _qname = QNAME;
513                             if($1 instanceof java.util.Map.Entry) {
514                                 _qname = («QName.name») ((java.util.Map.Entry) $1).getKey();
515                             }
516                             return fromDomStatic(_qname,$1,$2);
517                         }
518                     '''
519                 ]
520             ]
521
522             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain) as Class<? extends BindingCodec<Map<QName,Object>, Object>>
523             listener?.onDataContainerCodecCreated(inputType, ret);
524             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
525             return ret;
526         } catch (Exception e) {
527             processException(inputType, e);
528             return null;
529         }
530     }
531
532     private def Class<? extends BindingCodec<Map<QName, Object>, Object>> generateAugmentationTransformerFor(
533         Class<?> inputType, GeneratedType type, AugmentationSchema node) {
534         try {
535
536             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
537             val properties = type.allProperties
538             val ctCls = createClass(type.codecClassName) [
539                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
540                 staticQNameField(node.augmentationQName);
541                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
542                 staticField(it, AUGMENTATION_CODEC, BindingCodec)
543                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
544                 implementsType(BINDING_CODEC)
545
546                 method(Object, "toDomStatic", #[QName, Object]) [
547                     modifiers = PUBLIC + FINAL + STATIC
548                     bodyChecked = '''
549                         {
550                             ////System.out.println("Qname " + $1);
551                             ////System.out.println("Value " + $2);
552                             «QName.name» _resultName = «QName.name».create(QNAME,QNAME.getLocalName());
553                             java.util.List _childNodes = new java.util.ArrayList();
554                             «type.resolvedName» value = («type.resolvedName») $2;
555                             «FOR child : node.childNodes»
556                                 «var signature = properties.getFor(child)»
557                                 ////System.out.println("«signature.key»" + value.«signature.key»());
558                                 «serializeProperty(child, signature.value, signature.key)»
559                             «ENDFOR»
560                             return ($r) _childNodes;
561                         }
562                     '''
563                 ]
564                 method(Object, "serialize", Object) [
565                     bodyChecked = '''
566                         {
567                         java.util.Map.Entry _input = (java.util.Map.Entry) $1;
568                         «QName.name» _localName = QNAME;
569                         if(_input.getKey() != null) {
570                             _localName = («QName.name») _input.getKey();
571                         }
572                         return toDomStatic(_localName,_input.getValue());
573                         }
574                     '''
575                 ]
576
577                 method(Object, "fromDomStatic", #[QName, Object, InstanceIdentifier]) [
578                     modifiers = PUBLIC + FINAL + STATIC
579                     bodyChecked = '''
580                         {
581                             «QName.name» _localQName = QNAME;
582
583                             if($2 == null) {
584                             return null;
585                             }
586                             java.util.Map _compositeNode = (java.util.Map) $2;
587                             //System.out.println(_localQName + " " + _compositeNode);
588                             «type.builderName» _builder = new «type.builderName»();
589                             boolean _is_empty = true;
590                             «FOR child : node.childNodes»
591                                 «val signature = properties.getFor(child)»
592                                 «deserializeProperty(child, signature.value, signature.key)»
593                                 _builder.«signature.key.toSetter»(«signature.key»);
594                             «ENDFOR»
595                             if(_is_empty) {
596                             return null;
597                             }
598                             return _builder.build();
599                         }
600                     '''
601                 ]
602
603                 method(Object, "deserialize", #[Object, InstanceIdentifier]) [
604                     bodyChecked = '''
605                         return fromDomStatic(QNAME,$1,$2);
606                     '''
607                 ]
608             ]
609
610             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain) as Class<? extends BindingCodec<Map<QName,Object>, Object>>
611             listener?.onDataContainerCodecCreated(inputType, ret);
612             return ret;
613         } catch (Exception e) {
614             processException(inputType, e);
615             return null;
616         }
617     }
618
619     private def dispatch  Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(
620         Class<?> inputType, GeneratedType typeSpec, ChoiceNode node) {
621         try {
622
623             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
624             val ctCls = createClass(typeSpec.codecClassName) [
625                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
626                 //staticQNameField(inputType);
627                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
628                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
629                 staticField(it, DISPATCH_CODEC, BindingCodec)
630                 //staticField(it,QNAME_TO_CASE_MAP,BindingCodec)
631                 implementsType(BINDING_CODEC)
632                 method(List, "toDomStatic", #[QName, Object]) [
633                     modifiers = PUBLIC + FINAL + STATIC
634                     bodyChecked = '''
635                         {
636                             if($2 == null) {
637                                 return null;
638                             }
639                             if («DISPATCH_CODEC» == null) {
640                                 throw new «IllegalStateException.name»("Implementation of codec was not initialized.");
641                             }
642                             java.util.Map.Entry _input = new «SimpleEntry.name»($1,$2);
643                             Object _ret =  «DISPATCH_CODEC».serialize(_input);
644                             ////System.out.println("«typeSpec.name»#toDomStatic: " + _ret);
645                             return («List.name») _ret;
646                         }
647                     '''
648                 ]
649                 method(Object, "serialize", Object) [
650                     bodyChecked = '''
651                         throw new «UnsupportedOperationException.name»("Direct invocation not supported.");
652                     '''
653                 ]
654                 method(Object, "fromDomStatic", #[QName, Map, InstanceIdentifier]) [
655                     modifiers = PUBLIC + FINAL + STATIC
656                     bodyChecked = '''
657                         {
658                             if («DISPATCH_CODEC» == null) {
659                                 throw new «IllegalStateException.name»("Implementation of codec was not initialized.");
660                             }
661                             return «DISPATCH_CODEC».deserialize($2,$3);
662                         }
663                     '''
664                 ]
665                 method(Object, "deserialize", #[Object, InstanceIdentifier]) [
666                     bodyChecked = '''
667                         throw new «UnsupportedOperationException.name»("Direct invocation not supported.");
668                     '''
669                 ]
670             ]
671
672             val rawRet = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
673             val ret = rawRet as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
674             listener?.onChoiceCodecCreated(inputType, ret, node);
675             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
676             return ret;
677         } catch (Exception e) {
678             processException(inputType, e);
679             return null;
680         }
681     }
682
683     private def keyConstructorList(List<QName> qnames) {
684         val names = new TreeSet<String>()
685         for (name : qnames) {
686             val fieldName = name.getterName;
687             names.add(fieldName);
688         }
689         return Joiner.on(",").join(names);
690     }
691
692     private def serializeBodyFacade(GeneratedType type, SchemaNode node) {
693         val ret = serializeBody(type, node);
694         return ret;
695     }
696
697     private def String deserializeBody(GeneratedType type, SchemaNode node, InstanceIdentifier<?> bindingId) {
698         val ret = deserializeBodyImpl(type, node, bindingId);
699         return ret;
700     }
701
702     private def deserializeKey(GeneratedType type, ListSchemaNode node) {
703         if (node.keyDefinition != null && !node.keyDefinition.empty) {
704             return '''
705                 «type.resolvedName»Key getKey = («type.resolvedName»Key) «keyTransformer(type, node).canonicalName».fromDomStatic(_localQName,_compositeNode);
706                 _builder.setKey(getKey);
707             ''';
708         }
709     }
710
711     private def String deserializeBodyWithAugmentations(GeneratedType type, DataNodeContainer node, InstanceIdentifier<?> bindingId) '''
712         {
713             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
714             if($2 == null) {
715                 return null;
716             }
717             java.util.Map _compositeNode = (java.util.Map) $2;
718             //System.out.println(_localQName + " " + _compositeNode);
719             «type.builderName» _builder = new «type.builderName»();
720             «deserializeDataNodeContainerBody(type, node, bindingId)»
721             «type.deserializeAugmentations»
722             return _builder.build();
723         }
724     '''
725
726     private def dispatch String deserializeBodyImpl(GeneratedType type, SchemaNode node, InstanceIdentifier<?> bindingId) '''
727         {
728             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
729
730             if($2 == null) {
731             return null;
732             }
733             java.util.Map _compositeNode = (java.util.Map) $2;
734             «type.builderName» _builder = new «type.builderName»();
735             return _builder.build();
736         }
737     '''
738
739     private def dispatch String deserializeBodyImpl(GeneratedType type, ListSchemaNode node, InstanceIdentifier<?> bindingId) '''
740         {
741             «QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
742             if($2 == null) {
743                 return null;
744             }
745             java.util.Map _compositeNode = (java.util.Map) $2;
746             //System.out.println(_localQName + " " + _compositeNode);
747             «type.builderName» _builder = new «type.builderName»();
748             «deserializeKey(type, node)»
749             «deserializeDataNodeContainerBody(type, node, bindingId)»
750             «type.deserializeAugmentations»
751             return _builder.build();
752         }
753     '''
754
755     private def dispatch String deserializeBodyImpl(GeneratedType type, ContainerSchemaNode node, InstanceIdentifier<?> bindingId) {
756         return deserializeBodyWithAugmentations(type, node, bindingId);
757     }
758
759     private def dispatch String deserializeBodyImpl(GeneratedType type, NotificationDefinition node, InstanceIdentifier<?> bindingId) {
760         return deserializeBodyWithAugmentations(type, node, bindingId);
761     }
762
763     private def dispatch String deserializeBodyImpl(GeneratedType type, ChoiceCaseNode node, InstanceIdentifier<?> bindingId) {
764         return deserializeBodyWithAugmentations(type, node, bindingId);
765     }
766
767     private def deserializeDataNodeContainerBody(GeneratedType type, DataNodeContainer node, InstanceIdentifier<?> bindingId) {
768         deserializeNodeContainerBodyImpl(type, type.allProperties, node, bindingId);
769     }
770
771     private def deserializeNodeContainerBodyImpl(GeneratedType type, HashMap<String, Type> properties,
772         DataNodeContainer node, InstanceIdentifier<?> bindingId) {
773         val ret = '''
774             boolean _is_empty = true;
775             «FOR child : node.childNodes»
776                 «val signature = properties.getFor(child)»
777                 «IF signature !== null»
778                     «deserializeProperty(child, signature.value, signature.key)»
779                     _builder.«signature.key.toSetter»(«signature.key»);
780                 «ENDIF»
781             «ENDFOR»
782         '''
783         return ret;
784     }
785
786     def deserializeAugmentations(GeneratedType type) '''
787         «InstanceIdentifier.resolvedName» iid = $3.builder().child(«type.resolvedName».class).build();
788         java.util.Map _augmentation = (java.util.Map) «AUGMENTATION_CODEC».deserialize(_compositeNode,$3);
789         if(_augmentation != null) {
790             «Iterator.name» _entries = _augmentation.entrySet().iterator();
791             while(_entries.hasNext()) {
792                 java.util.Map.Entry _entry = (java.util.Map.Entry) _entries.next();
793                 ////System.out.println("Aug. key:" + _entry.getKey());
794                 Class _type = (Class) _entry.getKey();
795                 «Augmentation.resolvedName» _value = («Augmentation.name») _entry.getValue();
796                 if(_value != null) {
797                     _builder.addAugmentation(_type,_value);
798                 }
799             }
800         }
801     '''
802
803     private def dispatch CharSequence deserializeProperty(ListSchemaNode schema, ParameterizedType type,
804         String propertyName) '''
805         java.util.List _dom_«propertyName» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
806             localName»"));
807         ////System.out.println("«propertyName»#deCode"+_dom_«propertyName»);
808         java.util.List «propertyName» = new java.util.ArrayList();
809         if(_dom_«propertyName» != null) {
810             java.util.List _serialized = new java.util.ArrayList();
811             java.util.Iterator _iterator = _dom_«propertyName».iterator();
812             boolean _hasNext = _iterator.hasNext();
813             while(_hasNext) {
814                 Object _listItem = _iterator.next();
815                 _is_empty = false;
816                 ////System.out.println("  item" + _listItem);
817                 «val param = type.actualTypeArguments.get(0)»
818                 «InstanceIdentifier.resolvedName» iid = $3.builder().child(«param.resolvedName».class).build();
819                 Object _value = «type.actualTypeArguments.get(0).serializer(schema).resolvedName».fromDomStatic(_localQName,_listItem,iid);
820                 ////System.out.println("  value" + _value);
821                 «propertyName».add(_value);
822                 _hasNext = _iterator.hasNext();
823             }
824         }
825
826         ////System.out.println(" list" + «propertyName»);
827     '''
828
829     private def dispatch CharSequence deserializeProperty(LeafListSchemaNode schema, ParameterizedType type,
830         String propertyName) '''
831         java.util.List _dom_«propertyName» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
832             localName»"));
833         java.util.List «propertyName» = new java.util.ArrayList();
834         if(_dom_«propertyName» != null) {
835             java.util.List _serialized = new java.util.ArrayList();
836             java.util.Iterator _iterator = _dom_«propertyName».iterator();
837             boolean _hasNext = _iterator.hasNext();
838             while(_hasNext) {
839                 _is_empty = false;
840                 Object _listItem = _iterator.next();
841                 if(_listItem instanceof java.util.Map.Entry) {
842                     Object _innerValue = ((java.util.Map.Entry) _listItem).getValue();
843                     Object _value = «deserializeValue(type.actualTypeArguments.get(0), "_innerValue", schema.type)»;
844                     «propertyName».add(_value);
845                 }
846                 _hasNext = _iterator.hasNext();
847             }
848         }
849     '''
850
851     private def dispatch CharSequence deserializeProperty(LeafSchemaNode schema, Type type, String propertyName) '''
852         java.util.List _dom_«propertyName»_list =
853             _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.localName»"));
854         «type.resolvedName» «propertyName» = null;
855         if(_dom_«propertyName»_list != null && _dom_«propertyName»_list.size() > 0) {
856             _is_empty = false;
857             java.util.Map.Entry _dom_«propertyName» = (java.util.Map.Entry) _dom_«propertyName»_list.get(0);
858             Object _inner_value = _dom_«propertyName».getValue();
859             «propertyName» = «deserializeValue(type, "_inner_value", schema.type)»;
860         }
861     '''
862
863     private def dispatch CharSequence deserializeProperty(ContainerSchemaNode schema, Type type,
864         String propertyName) '''
865         java.util.List _dom_«propertyName»_list =
866             _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.localName»"));
867         «type.resolvedName» «propertyName» = null;
868         if(_dom_«propertyName»_list != null && _dom_«propertyName»_list.size() > 0) {
869             _is_empty = false;
870             java.util.Map _dom_«propertyName» = (java.util.Map) _dom_«propertyName»_list.get(0);
871             «InstanceIdentifier.resolvedName» iid = $3.builder().child(«type.resolvedName».class).build();
872             «propertyName» =  «type.serializer(schema).resolvedName».fromDomStatic(_localQName,_dom_«propertyName»,iid);
873         }
874     '''
875
876     private def dispatch CharSequence deserializeProperty(ChoiceNode schema, Type type, String propertyName) '''
877         «type.resolvedName» «propertyName» = «type.serializer(schema).resolvedName».fromDomStatic(_localQName,_compositeNode,$3);
878         if(«propertyName» != null) {
879             _is_empty = false;
880         }
881     '''
882
883     private def dispatch String deserializeValue(GeneratedTransferObject type, String domParameter,
884         TypeDefinition<?> typeDefinition) '''
885         («type.resolvedName») «type.valueSerializer(typeDefinition).resolvedName».fromDomValue(«domParameter»)
886     '''
887
888     private def dispatch String deserializeValue(Enumeration type, String domParameter, TypeDefinition<?> typeDefinition) '''
889         («type.resolvedName») «type.valueSerializer(typeDefinition).resolvedName».fromDomValue(«domParameter»)
890     '''
891
892     private def dispatch String deserializeValue(Type type, String domParameter, TypeDefinition<?> typeDef) {
893         if (INSTANCE_IDENTIFIER.equals(type)) {
894             return '''(«InstanceIdentifier.name») «INSTANCE_IDENTIFIER_CODEC».deserialize(«domParameter»)'''
895         } else if (CLASS_TYPE.equals(type)) {
896             return '''(«Class.name») «IDENTITYREF_CODEC».deserialize(«domParameter»)'''
897         } else if (typeDef!=null && typeDef instanceof EmptyTypeDefinition) {
898             if(domParameter == null) {
899                 return ''' Boolean.FALSE '''
900             } else {
901                 return ''' Boolean.TRUE '''
902             }
903         }
904         return '''(«type.resolvedName») «domParameter»'''
905     }
906
907     private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateValueTransformer(
908         Class<?> inputType, GeneratedTransferObject typeSpec, TypeDefinition<?> typeDef) {
909         try {
910
911             val returnType = typeSpec.valueReturnType;
912             if (returnType == null) {
913                 val ctCls = createDummyImplementation(inputType, typeSpec);
914                 val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
915                 return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
916             }
917
918             val ctCls = createClass(typeSpec.codecClassName) [
919                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
920                 if (inputType.isYangBindingAvailable) {
921                     implementsType(BINDING_CODEC)
922                     staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
923                     staticField(it, IDENTITYREF_CODEC, BindingCodec)
924                     implementsType(BindingDeserializer.asCtClass)
925                 }
926                 method(Object, "toDomValue", Object) [
927                     modifiers = PUBLIC + FINAL + STATIC
928                     val ctSpec = typeSpec.asCtClass;
929                     bodyChecked = '''
930                         {
931                             ////System.out.println("«inputType.simpleName»#toDomValue: "+$1);
932
933                             if($1 == null) {
934                             return null;
935                             }
936                             «typeSpec.resolvedName» _encapsulatedValue = («typeSpec.resolvedName») $1;
937                             ////System.out.println("«inputType.simpleName»#toDomValue:Enc: "+_encapsulatedValue);
938                             «returnType.resolvedName» _value =  _encapsulatedValue.getValue();
939                             ////System.out.println("«inputType.simpleName»#toDomValue:DeEnc: "+_value);
940                             Object _domValue = «serializeValue(returnType, "_value", null)»;
941                             return _domValue;
942                         }
943                     '''
944                 ]
945                 method(Object, "serialize", Object) [
946                     bodyChecked = '''
947                         {
948                             return toDomValue($1);
949                         }
950                     '''
951                 ]
952                 method(Object, "fromDomValue", Object) [
953                     modifiers = PUBLIC + FINAL + STATIC
954                     bodyChecked = '''
955                         {
956                             ////System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
957
958                             if($1 == null) {
959                             return null;
960                             }
961                             «returnType.resolvedName» _simpleValue = «deserializeValue(returnType, "$1", null)»;
962                             «typeSpec.resolvedName» _value = new «typeSpec.resolvedName»(_simpleValue);
963                             return _value;
964                         }
965                     '''
966                 ]
967                 method(Object, "deserialize", Object) [
968                     bodyChecked = '''{
969                             return fromDomValue($1);
970                     }
971                     '''
972                 ]
973             ]
974
975             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
976             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
977             return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
978         } catch (Exception e) {
979             LOG.error("Cannot compile DOM Codec for {}", inputType, e);
980             val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
981             exception.addSuppressed(e);
982             throw exception;
983         }
984     }
985
986     private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateValueTransformer(
987         Class<?> inputType, GeneratedTransferObject typeSpec, UnionTypeDefinition typeDef) {
988         try {
989             val ctCls = createClass(typeSpec.codecClassName) [
990                 val properties = typeSpec.allProperties;
991                 val getterToTypeDefinition = XtendHelper.getTypes(typeDef).toMap[type|type.QName.getterName];
992                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
993                 if (inputType.isYangBindingAvailable) {
994                     implementsType(BINDING_CODEC)
995                     staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
996                     staticField(it, IDENTITYREF_CODEC, BindingCodec)
997                     implementsType(BindingDeserializer.asCtClass)
998                 }
999                 method(Object, "toDomValue", Object) [
1000                     modifiers = PUBLIC + FINAL + STATIC
1001                     val ctSpec = inputType.asCtClass;
1002                     bodyChecked = '''
1003                         {
1004                             ////System.out.println("«inputType.simpleName»#toDomValue: "+$1);
1005
1006                             if($1 == null) {
1007                             return null;
1008                             }
1009                             «typeSpec.resolvedName» _value = («typeSpec.resolvedName») $1;
1010                             «FOR property : properties.entrySet»
1011                             «IF property.key != "getValue"»
1012                                 «property.value.resolvedName» «property.key» = («property.value.resolvedName») _value.«property.
1013                             key»();
1014                                 if(«property.key» != null) {
1015                                     return «serializeValue(property.value, property.key,
1016                             getterToTypeDefinition.get(property.key))»;
1017                                 }
1018                             «ENDIF»
1019                             «ENDFOR»
1020
1021                             return null;
1022                         }
1023                     '''
1024                 ]
1025                 method(Object, "serialize", Object) [
1026                     bodyChecked = '''
1027                         {
1028                             return toDomValue($1);
1029                         }
1030                     '''
1031                 ]
1032                 method(Object, "fromDomValue", Object) [
1033                     modifiers = PUBLIC + FINAL + STATIC
1034                     bodyChecked = '''
1035                         {
1036                             ////System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
1037
1038                             if($1 == null) {
1039                             return null;
1040                             }
1041                             if($1 instanceof String) {
1042                             String _simpleValue = (String) $1;
1043                             return new «typeSpec.resolvedName»(_simpleValue.toCharArray());
1044                             }
1045                             return null;
1046                         }
1047                     '''
1048                 ]
1049                 method(Object, "deserialize", Object) [
1050                     bodyChecked = '''{
1051                             return fromDomValue($1);
1052                     }
1053                     '''
1054                 ]
1055             ]
1056
1057             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
1058             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
1059             return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
1060         } catch (Exception e) {
1061             LOG.error("Cannot compile DOM Codec for {}", inputType, e);
1062             val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
1063             exception.addSuppressed(e);
1064             throw exception;
1065         }
1066     }
1067
1068     private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateValueTransformer(
1069         Class<?> inputType, GeneratedTransferObject typeSpec, BitsTypeDefinition typeDef) {
1070         try {
1071             val ctCls = createClass(typeSpec.codecClassName) [
1072                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
1073                 if (inputType.isYangBindingAvailable) {
1074                     implementsType(BINDING_CODEC)
1075                     staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
1076                     staticField(it, IDENTITYREF_CODEC, BindingCodec)
1077                     implementsType(BindingDeserializer.asCtClass)
1078                 }
1079                 method(Object, "toDomValue", Object) [
1080                     modifiers = PUBLIC + FINAL + STATIC
1081                     val ctSpec = typeSpec.asCtClass;
1082                     bodyChecked = '''
1083                         {
1084                             ////System.out.println("«inputType.simpleName»#toDomValue: "+$1);
1085
1086                             if($1 == null) {
1087                             return null;
1088                             }
1089                             «typeSpec.resolvedName» _encapsulatedValue = («typeSpec.resolvedName») $1;
1090                             «HashSet.resolvedName» _value = new «HashSet.resolvedName»();
1091                             //System.out.println("«inputType.simpleName»#toDomValue:Enc: "+_encapsulatedValue);
1092
1093                             «FOR bit : typeDef.bits»
1094                             «val getter = bit.getterName()»
1095                             if(Boolean.TRUE.equals(_encapsulatedValue.«getter»())) {
1096                                 _value.add("«bit.name»");
1097                             }
1098                             «ENDFOR»
1099                             «Set.resolvedName» _domValue =  «Collections.resolvedName».unmodifiableSet(_value);
1100                             //System.out.println("«inputType.simpleName»#toDomValue:DeEnc: "+_domValue);
1101
1102                             return _domValue;
1103                         }
1104                     '''
1105                 ]
1106                 method(Object, "serialize", Object) [
1107                     bodyChecked = '''
1108                         {
1109                             return toDomValue($1);
1110                         }
1111                     '''
1112                 ]
1113                 method(Object, "fromDomValue", Object) [
1114                     modifiers = PUBLIC + FINAL + STATIC
1115                     val sortedBits = new ArrayList(typeDef.bits)
1116                     Collections.sort(sortedBits, [o1, o2|
1117                         o1.propertyName.compareTo(o2.propertyName)
1118                     ])
1119                     bodyChecked = '''
1120                         {
1121                             //System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
1122
1123                             if($1 == null) {
1124                             return null;
1125                             }
1126                             «Set.resolvedName» _domValue = («Set.resolvedName») $1;
1127                             «FOR bit : sortedBits»
1128                             Boolean «bit.propertyName» = Boolean.valueOf(_domValue.contains("«bit.name»"));
1129                             «ENDFOR»
1130
1131                             return new «inputType.resolvedName»(«FOR bit : sortedBits SEPARATOR ","»«bit.propertyName»«ENDFOR»);
1132                         }
1133                     '''
1134                 ]
1135                 method(Object, "deserialize", Object) [
1136                     bodyChecked = '''{
1137                             return fromDomValue($1);
1138                     }
1139                     '''
1140                 ]
1141             ]
1142
1143             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
1144             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
1145             return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
1146         } catch (Exception e) {
1147             LOG.error("Cannot compile DOM Codec for {}", inputType, e);
1148             val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
1149             exception.addSuppressed(e);
1150             throw exception;
1151         }
1152     }
1153
1154     def String getPropertyName(Bit bit) {
1155         '''_«BindingGeneratorUtil.parseToValidParamName(bit.name)»'''
1156     }
1157
1158     def String getterName(Bit bit) {
1159
1160         val paramName = BindingGeneratorUtil.parseToValidParamName(bit.name);
1161         return '''is«paramName.toFirstUpper»''';
1162     }
1163
1164     def boolean isYangBindingAvailable(Class<?> class1) {
1165         try {
1166             val bindingCodecClass = class1.classLoader.loadClass(BINDING_CODEC.name);
1167             return bindingCodecClass !== null;
1168         } catch (ClassNotFoundException e) {
1169             return false;
1170         }
1171     }
1172
1173     private def createDummyImplementation(Class<?> object, GeneratedTransferObject typeSpec) {
1174         LOG.trace("Generating Dummy DOM Codec for {} with {}", object, object.classLoader)
1175         return createClass(typeSpec.codecClassName) [
1176             if (object.isYangBindingAvailable) {
1177                 implementsType(BINDING_CODEC)
1178                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
1179                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
1180                 implementsType(BindingDeserializer.asCtClass)
1181             }
1182             //implementsType(BindingDeserializer.asCtClass)
1183             method(Object, "toDomValue", Object) [
1184                 modifiers = PUBLIC + FINAL + STATIC
1185                 bodyChecked = '''{
1186                     if($1 == null) {
1187                         return null;
1188                     }
1189                     return $1.toString();
1190
1191                     }'''
1192             ]
1193             method(Object, "serialize", Object) [
1194                 bodyChecked = '''
1195                     {
1196                         return toDomValue($1);
1197                     }
1198                 '''
1199             ]
1200             method(Object, "fromDomValue", Object) [
1201                 modifiers = PUBLIC + FINAL + STATIC
1202                 bodyChecked = '''return null;'''
1203             ]
1204             method(Object, "deserialize", Object) [
1205                 bodyChecked = '''{
1206                         return fromDomValue($1);
1207                     }
1208                     '''
1209             ]
1210         ]
1211     }
1212
1213     private def Type getValueReturnType(GeneratedTransferObject object) {
1214         for (prop : object.properties) {
1215             if (prop.name == "value") {
1216                 return prop.returnType;
1217             }
1218         }
1219         if (object.superType != null) {
1220             return getValueReturnType(object.superType);
1221         }
1222         return null;
1223     }
1224
1225     private def dispatch Class<?> generateValueTransformer(Class<?> inputType, Enumeration typeSpec, TypeDefinition<?> type) {
1226         var EnumerationType enumSchemaType
1227         if (type instanceof EnumerationType) {
1228             enumSchemaType = type as EnumerationType
1229         } else {
1230             val typeRef = new ReferencedTypeImpl(typeSpec.packageName, typeSpec.name);
1231             val schema = getSchemaNode(typeRef) as ExtendedType;
1232             enumSchemaType = schema.baseType as EnumerationType;
1233         }
1234         val enumSchema = enumSchemaType;
1235         try {
1236             //log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
1237             val ctCls = createClass(typeSpec.codecClassName) [
1238                 //staticField(Map,"AUGMENTATION_SERIALIZERS");
1239                 //implementsType(BINDING_CODEC)
1240                 method(Object, "toDomValue", Object) [
1241                     modifiers = PUBLIC + FINAL + STATIC
1242                     bodyChecked = '''{
1243                             if($1 == null) {
1244                                 return null;
1245                             }
1246                             «typeSpec.resolvedName» _value = («typeSpec.resolvedName») $1;
1247                             «FOR en : enumSchema.values»
1248                             if(«typeSpec.resolvedName».«BindingMapping.getClassName(en.name)».equals(_value)) {
1249                                 return "«en.name»";
1250                             }
1251                             «ENDFOR»
1252                             return null;
1253                         }
1254                     '''
1255                 ]
1256                 method(Object, "serialize", Object) [
1257                     bodyChecked = '''
1258                         return toDomValue($1);
1259                     '''
1260                 ]
1261                 method(Object, "fromDomValue", Object) [
1262                     modifiers = PUBLIC + FINAL + STATIC
1263                     bodyChecked = '''
1264                         {
1265                             if($1 == null) {
1266                                 return null;
1267                             }
1268                             String _value = (String) $1;
1269                             «FOR en : enumSchema.values»
1270                                 if("«en.name»".equals(_value)) {
1271                                     return «typeSpec.resolvedName».«BindingMapping.getClassName(en.name)»;
1272                                 }
1273                             «ENDFOR»
1274                             return null;
1275                         }
1276                     '''
1277                 ]
1278                 method(Object, "deserialize", Object) [
1279                     bodyChecked = '''
1280                         return fromDomValue($1);
1281                     '''
1282                 ]
1283             ]
1284
1285             val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
1286             LOG.debug("DOM Codec for {} was generated {}", inputType, ret)
1287             return ret;
1288         } catch (CodeGenerationException e) {
1289             throw new CodeGenerationException("Cannot compile Transformator for " + inputType, e);
1290         } catch (Exception e) {
1291             LOG.error("Cannot compile DOM Codec for {}", inputType, e);
1292             val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
1293             exception.addSuppressed(e);
1294             throw exception;
1295         }
1296
1297     }
1298
1299     def Class<?> toClassImpl(CtClass newClass, ClassLoader loader, ProtectionDomain domain) {
1300         val cls = newClass.toClass(loader, domain);
1301         if (classFileCapturePath !== null) {
1302             newClass.writeFile(classFileCapturePath.absolutePath);
1303         }
1304         listener?.onCodecCreated(cls);
1305         return cls;
1306     }
1307
1308     def debugWriteClass(CtClass class1) {
1309         val path = class1.name.replace(".", "/") + ".class"
1310
1311         val captureFile = new File(classFileCapturePath, path);
1312         captureFile.createNewFile
1313
1314     }
1315
1316     /**
1317      * Default catch all
1318      *
1319      **/
1320     private def dispatch CharSequence deserializeProperty(DataSchemaNode container, Type type, String propertyName) '''
1321         «type.resolvedName» «propertyName» = null;
1322     '''
1323
1324     private def dispatch CharSequence deserializeProperty(DataSchemaNode container, GeneratedTypeBuilder type,
1325         String propertyName) {
1326         _deserializeProperty(container, type.toInstance, propertyName)
1327     }
1328
1329     static def toSetter(String it) {
1330
1331         if (startsWith("is")) {
1332             return "set" + substring(2);
1333         } else if (startsWith("get")) {
1334             return "set" + substring(3);
1335         }
1336         return "set" + it;
1337     }
1338
1339     /*
1340     private def dispatch CharSequence deserializeProperty(DataSchemaNode container,GeneratedType type, String propertyName) '''
1341         «type.resolvedName» «propertyName» = value.«propertyName»();
1342         if(«propertyName» != null) {
1343             Object domValue = «type.serializer».toDomStatic(QNAME,«propertyName»);
1344             _childNodes.add(domValue);
1345         }
1346     '''
1347     */
1348     private def getBuilderName(GeneratedType type) '''«type.resolvedName»Builder'''
1349
1350     private def staticQNameField(CtClass it, QName node) {
1351         val field = new CtField(ctQName, "QNAME", it);
1352         field.modifiers = PUBLIC + FINAL + STATIC;
1353         addField(field,
1354             '''«QName.asCtClass.name».create("«node.namespace»","«node.formattedRevision»","«node.localName»")''')
1355     }
1356
1357     private def String serializeBodyImpl(GeneratedType type, DataNodeContainer nodeContainer) '''
1358         {
1359             «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
1360             java.util.List _childNodes = new java.util.ArrayList();
1361             «type.resolvedName» value = («type.resolvedName») $2;
1362             «transformDataContainerBody(type, type.allProperties, nodeContainer)»
1363             «serializeAugmentations»
1364             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
1365         }
1366     '''
1367
1368     private def dispatch String serializeBody(GeneratedType type, ListSchemaNode node) {
1369         return serializeBodyImpl(type, node);
1370     }
1371
1372     private def dispatch String serializeBody(GeneratedType type, NotificationDefinition node) {
1373         return serializeBodyImpl(type, node);
1374     }
1375
1376     private def dispatch String serializeBody(GeneratedType type, ContainerSchemaNode node) {
1377         return serializeBodyImpl(type, node);
1378     }
1379
1380     private def dispatch String serializeBody(GeneratedType type, ChoiceCaseNode node) {
1381         return serializeBodyImpl(type, node);
1382     }
1383
1384     private def dispatch String serializeBody(GeneratedType type, SchemaNode node) '''
1385         {
1386         «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
1387             java.util.List _childNodes = new java.util.ArrayList();
1388             «type.resolvedName» value = («type.resolvedName») $2;
1389             return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
1390         }
1391     '''
1392
1393     private def transformDataContainerBody(Type type, Map<String, Type> properties, DataNodeContainer node) {
1394         val ret = '''
1395             «FOR child : node.childNodes»
1396                 «val signature = properties.getFor(child)»
1397                 «IF signature !== null»
1398                     ////System.out.println("«type.name»#«signature.key»" + value.«signature.key»());
1399                     «serializeProperty(child, signature.value, signature.key)»
1400                 «ENDIF»
1401             «ENDFOR»
1402         '''
1403         return ret;
1404     }
1405
1406     private static def serializeAugmentations() '''
1407         java.util.List _augmentations = (java.util.List) «AUGMENTATION_CODEC».serialize(value);
1408         if(_augmentations != null) {
1409             _childNodes.addAll(_augmentations);
1410         }
1411     '''
1412
1413     private static def Entry<String, Type> getFor(Map<String, Type> map, DataSchemaNode node) {
1414         var sig = map.get(node.getterName);
1415         if (sig != null) {
1416             return new SimpleEntry(node.getterName, sig);
1417         }
1418         sig = map.get(node.booleanGetterName);
1419         if (sig != null) {
1420             return new SimpleEntry(node.booleanGetterName, map.get(node.booleanGetterName));
1421         }
1422         return null;
1423     }
1424
1425     private static def String getBooleanGetterName(DataSchemaNode node) {
1426         return "is" + BindingMapping.getPropertyName(node.QName.localName).toFirstUpper;
1427     }
1428
1429     private static def String getGetterName(DataSchemaNode node) {
1430         return "get" + BindingMapping.getPropertyName(node.QName.localName).toFirstUpper;
1431     }
1432
1433     private static def String getGetterName(QName node) {
1434         return "get" + BindingMapping.getPropertyName(node.localName).toFirstUpper;
1435     }
1436
1437     private def dispatch CharSequence serializeProperty(ListSchemaNode schema, ParameterizedType type,
1438         String propertyName) '''
1439         «type.resolvedName» «propertyName» = value.«propertyName»();
1440         ////System.out.println("«propertyName»:" + «propertyName»);
1441         if(«propertyName» != null) {
1442             java.util.Iterator _iterator = «propertyName».iterator();
1443             boolean _hasNext = _iterator.hasNext();
1444             while(_hasNext) {
1445                 Object _listItem = _iterator.next();
1446                 Object _domValue = «type.actualTypeArguments.get(0).serializer(schema).resolvedName».toDomStatic(_resultName,_listItem);
1447                 _childNodes.add(_domValue);
1448                 _hasNext = _iterator.hasNext();
1449             }
1450         }
1451     '''
1452
1453     private def dispatch CharSequence serializeProperty(LeafSchemaNode schema, Type type, String propertyName) '''
1454         «type.resolvedName» «propertyName» = value.«propertyName»();
1455
1456         if(«propertyName» != null) {
1457             «QName.name» _qname = «QName.name».create(_resultName,"«schema.QName.localName»");
1458             Object _propValue = «serializeValue(type, propertyName, schema.type)»;
1459             if(_propValue != null) {
1460                 Object _domValue = java.util.Collections.singletonMap(_qname,_propValue);
1461                 _childNodes.add(_domValue);
1462             }
1463         }
1464     '''
1465
1466     private def dispatch serializeValue(GeneratedTransferObject type, String parameter, TypeDefinition<?> typeDefinition) {
1467         '''«type.valueSerializer(typeDefinition).resolvedName».toDomValue(«parameter»)'''
1468     }
1469
1470     private def dispatch serializeValue(Enumeration type, String parameter, TypeDefinition<?> typeDefinition) {
1471         '''«type.valueSerializer(typeDefinition).resolvedName».toDomValue(«parameter»)'''
1472     }
1473
1474     private def dispatch serializeValue(Type type, String parameter, EmptyTypeDefinition typeDefinition) {
1475         '''(«parameter».booleanValue() ? "" : null)'''
1476     }
1477
1478     private def dispatch serializeValue(Type signature, String property, TypeDefinition<?> typeDefinition) {
1479         serializeValue(signature,property)
1480     }
1481
1482     private def dispatch serializeValue(Type signature, String property, Void typeDefinition) {
1483         serializeValue(signature,property)
1484     }
1485
1486     private def dispatch serializeValue(Type signature, String property) {
1487         if (INSTANCE_IDENTIFIER == signature) {
1488             return '''«INSTANCE_IDENTIFIER_CODEC».serialize(«property»)'''
1489         } else if (CLASS_TYPE.equals(signature)) {
1490             return '''(«QName.resolvedName») «IDENTITYREF_CODEC».serialize(«property»)'''
1491         }
1492         if ("char[]" == signature.name) {
1493             return '''new String(«property»)''';
1494         }
1495         return '''«property»''';
1496     }
1497
1498     private def dispatch CharSequence serializeProperty(LeafListSchemaNode schema, ParameterizedType type,
1499         String propertyName) '''
1500         «type.resolvedName» «propertyName» = value.«propertyName»();
1501         if(«propertyName» != null) {
1502             «QName.name» _qname = «QName.name».create(_resultName,"«schema.QName.localName»");
1503             java.util.Iterator _iterator = «propertyName».iterator();
1504             boolean _hasNext = _iterator.hasNext();
1505             while(_hasNext) {
1506                 Object _listItem = _iterator.next();
1507                 Object _propValue = «serializeValue(type.actualTypeArguments.get(0), "_listItem", schema.type)»;
1508                 Object _domValue = java.util.Collections.singletonMap(_qname,_propValue);
1509                 _childNodes.add(_domValue);
1510                 _hasNext = _iterator.hasNext();
1511             }
1512         }
1513     '''
1514
1515     private def dispatch CharSequence serializeProperty(ChoiceNode container, GeneratedType type,
1516         String propertyName) '''
1517         «type.resolvedName» «propertyName» = value.«propertyName»();
1518         if(«propertyName» != null) {
1519             java.util.List domValue = «type.serializer(container).resolvedName».toDomStatic(_resultName,«propertyName»);
1520             _childNodes.addAll(domValue);
1521         }
1522     '''
1523
1524     /**
1525      * Default catch all
1526      *
1527      **/
1528     private def dispatch CharSequence serializeProperty(DataSchemaNode container, Type type, String propertyName) '''
1529         «type.resolvedName» «propertyName» = value.«propertyName»();
1530         if(«propertyName» != null) {
1531             Object domValue = «propertyName»;
1532             _childNodes.add(domValue);
1533         }
1534     '''
1535
1536     private def dispatch CharSequence serializeProperty(DataSchemaNode container, GeneratedTypeBuilder type,
1537         String propertyName) {
1538         serializeProperty(container, type.toInstance, propertyName)
1539     }
1540
1541     private def dispatch CharSequence serializeProperty(DataSchemaNode container, GeneratedType type,
1542         String propertyName) '''
1543         «type.resolvedName» «propertyName» = value.«propertyName»();
1544         if(«propertyName» != null) {
1545             Object domValue = «type.serializer(container).resolvedName».toDomStatic(_resultName,«propertyName»);
1546             _childNodes.add(domValue);
1547         }
1548     '''
1549
1550     private def codecClassName(GeneratedType typeSpec) {
1551         return '''«typeSpec.resolvedName»$Broker$Codec$DOM'''
1552     }
1553
1554     private def codecClassName(Class<?> typeSpec) {
1555         return '''«typeSpec.name»$Broker$Codec$DOM'''
1556     }
1557
1558     private def HashMap<String, Type> getAllProperties(GeneratedType type) {
1559         val ret = new HashMap<String, Type>();
1560         type.collectAllProperties(ret);
1561         return ret;
1562     }
1563
1564     private def dispatch void collectAllProperties(GeneratedType type, Map<String, Type> set) {
1565         for (definition : type.methodDefinitions) {
1566             set.put(definition.name, definition.returnType);
1567         }
1568         for (property : type.properties) {
1569             set.put(property.getterName, property.returnType);
1570         }
1571         for (parent : type.implements) {
1572             parent.collectAllProperties(set);
1573         }
1574     }
1575
1576     def String getGetterName(GeneratedProperty property) {
1577         return "get" + property.name.toFirstUpper
1578     }
1579
1580     private def dispatch void collectAllProperties(Type type, Map<String, Type> set) {
1581         // NOOP for generic type.
1582     }
1583
1584     def String getResolvedName(Type type) {
1585         return type.asCtClass.name;
1586     }
1587
1588     def String getResolvedName(Class<?> type) {
1589         return type.asCtClass.name;
1590     }
1591
1592     def CtClass asCtClass(Type type) {
1593         val cls = loadClass(type.fullyQualifiedName)
1594         return cls.asCtClass;
1595     }
1596
1597     private def dispatch processException(Class<?> inputType, CodeGenerationException e) {
1598         LOG.error("Cannot compile DOM Codec for {}. One of it's prerequisites was not generated.", inputType);
1599         throw e;
1600     }
1601
1602     private def dispatch processException(Class<?> inputType, Exception e) {
1603         LOG.error("Cannot compile DOM Codec for {}", inputType, e);
1604         val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType, e);
1605         throw exception;
1606     }
1607
1608     private def setBodyChecked(CtMethod method, String body) {
1609         try {
1610             method.setBody(body);
1611         } catch (CannotCompileException e) {
1612             LOG.error("Cannot compile method: {}#{} {}, Reason: {} Body: {}", method.declaringClass, method.name,
1613                 method.signature, e.message, body)
1614             throw e;
1615         }
1616     }
1617 }
1618
1619 @Data
1620 class PropertyPair {
1621
1622     String getterName;
1623
1624     Type type;
1625
1626     @Property
1627     Type returnType;
1628     @Property
1629     SchemaNode schemaNode;
1630 }