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