Fix for Bug 586.
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / BindingGeneratorImpl.xtend
1 /*
2  * Copyright (c) 2013 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 static com.google.common.base.Preconditions.*;
11 import static extension org.opendaylight.yangtools.binding.generator.util.Types.*;
12 import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.*;
13 import static org.opendaylight.yangtools.binding.generator.util.BindingTypes.*;
14 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.*;
15
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.Iterator
23 import java.util.Collection
24 import org.opendaylight.yangtools.binding.generator.util.BindingTypes;
25 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
26 import org.opendaylight.yangtools.binding.generator.util.Types;
27 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
28 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTypeBuilderImpl;
29 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
30 import org.opendaylight.yangtools.sal.binding.generator.spi.TypeProvider;
31 import org.opendaylight.yangtools.sal.binding.model.api.AccessModifier;
32 import org.opendaylight.yangtools.sal.binding.model.api.Type;
33 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.EnumBuilder;
34 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
35 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
36 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
37 import org.opendaylight.yangtools.sal.binding.yang.types.GroupingDefinitionDependencySort;
38 import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
39 import org.opendaylight.yangtools.yang.binding.RpcService;
40 import org.opendaylight.yangtools.yang.common.RpcResult;
41 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
42 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
43 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
44 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
46 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
48 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.Module;
53 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
54 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
55 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
57 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
58 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
61 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.util.DataNodeIterator;
63 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
64 import org.opendaylight.yangtools.yang.model.util.UnionType;
65 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort
66 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
67 import org.opendaylight.yangtools.yang.model.api.UsesNode
68 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
69 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.AnnotationTypeBuilder
70 import org.opendaylight.yangtools.yang.model.api.ModuleImport
71 import org.opendaylight.yangtools.yang.binding.DataContainer
72 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
73 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
74 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
75 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
76 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedPropertyBuilder
77 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedPropertyBuilderImpl
78 import org.opendaylight.yangtools.yang.common.QName
79 import org.opendaylight.yangtools.yang.binding.BindingMapping
80 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilderBase
81
82 import com.google.common.collect.Sets
83 import java.net.URI
84 import java.util.Date
85
86 public class BindingGeneratorImpl implements BindingGenerator {
87
88     private final Map<Module, ModuleContext> genCtx = new HashMap()
89
90     /**
91      * Outer key represents the package name. Outer value represents map of
92      * all builders in the same package. Inner key represents the schema node
93      * name (in JAVA class/interface name format). Inner value represents
94      * instance of builder for schema node specified in key part.
95      */
96     private Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders;
97
98     /**
99      * Provide methods for converting YANG types to JAVA types.
100      */
101     private var TypeProvider typeProvider;
102
103     /**
104      * Holds reference to schema context to resolve data of augmented element
105      * when creating augmentation builder
106      */
107     private var SchemaContext schemaContext;
108
109     /**
110      * Constant with the concrete name of namespace.
111      */
112     private val static String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
113
114     /**
115      * Constant with the concrete name of identifier.
116      */
117     private val static String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
118
119     /**
120      * Resolves generated types from <code>context</code> schema nodes of all
121      * modules.
122      *
123      * Generated types are created for modules, groupings, types, containers,
124      * lists, choices, augments, rpcs, notification, identities.
125      *
126      * @param context
127      *            schema context which contains data about all schema nodes
128      *            saved in modules
129      * @return list of types (usually <code>GeneratedType</code>
130      *         <code>GeneratedTransferObject</code>which are generated from
131      *         <code>context</code> data.
132      * @throws IllegalArgumentException
133      *             if param <code>context</code> is null
134      * @throws IllegalStateException
135      *             if <code>context</code> contain no modules
136      */
137     override generateTypes(SchemaContext context) {
138         checkArgument(context !== null, "Schema Context reference cannot be NULL.");
139         checkState(context.modules !== null, "Schema Context does not contain defined modules.");
140         schemaContext = context;
141         typeProvider = new TypeProviderImpl(context);
142         val Set<Module> modules = context.modules;
143         return generateTypes(context, modules);
144     }
145
146     /**
147      * Resolves generated types from <code>context</code> schema nodes only for
148      * modules specified in <code>modules</code>
149      *
150      * Generated types are created for modules, groupings, types, containers,
151      * lists, choices, augments, rpcs, notification, identities.
152      *
153      * @param context
154      *            schema context which contains data about all schema nodes
155      *            saved in modules
156      * @param modules
157      *            set of modules for which schema nodes should be generated
158      *            types
159      * @return list of types (usually <code>GeneratedType</code> or
160      *         <code>GeneratedTransferObject</code>) which:
161      *         <ul>
162      *         <li>are generated from <code>context</code> schema nodes and</li>
163      *         <li>are also part of some of the module in <code>modules</code>
164      *         set</li>.
165      *         </ul>
166      * @throws IllegalArgumentException
167      *             <ul>
168      *             <li>if param <code>context</code> is null or</li>
169      *             <li>if param <code>modules</code> is null</li>
170      *             </ul>
171      * @throws IllegalStateException
172      *             if <code>context</code> contain no modules
173      */
174     override generateTypes(SchemaContext context, Set<Module> modules) {
175         checkArgument(context !== null, "Schema Context reference cannot be NULL.");
176         checkState(context.modules !== null, "Schema Context does not contain defined modules.");
177         checkArgument(modules !== null, "Set of Modules cannot be NULL.");
178
179         schemaContext = context;
180         typeProvider = new TypeProviderImpl(context);
181         val contextModules = ModuleDependencySort.sort(context.modules);
182         genTypeBuilders = new HashMap();
183
184         for (contextModule : contextModules) {
185             moduleToGenTypes(contextModule, context);
186         }
187         for (contextModule : contextModules) {
188             allAugmentsToGenTypes(contextModule);
189         }
190
191         val List<Type> filteredGenTypes = new ArrayList();
192         for (Module m : modules) {
193             val ctx = checkNotNull(genCtx.get(m), "Module context not found for module %s", m)
194             filteredGenTypes.addAll(ctx.generatedTypes);
195             val Set<Type> additionalTypes = (typeProvider as TypeProviderImpl).additionalTypes.get(m)
196             if (additionalTypes != null) {
197                 filteredGenTypes.addAll(additionalTypes)
198             }
199         }
200
201         return filteredGenTypes;
202     }
203
204     private def void moduleToGenTypes(Module m, SchemaContext context) {
205         genCtx.put(m, new ModuleContext)
206         allTypeDefinitionsToGenTypes(m)
207         groupingsToGenTypes(m, m.groupings)
208         rpcMethodsToGenType(m)
209         allIdentitiesToGenTypes(m, context)
210         notificationsToGenType(m)
211
212         if (!m.childNodes.isEmpty()) {
213             val moduleType = moduleToDataType(m)
214             genCtx.get(m).addModuleNode(moduleType)
215             val basePackageName = moduleNamespaceToPackageName(m);
216             resolveDataSchemaNodes(m, basePackageName, moduleType, moduleType, m.childNodes)
217         }
218     }
219
220     /**
221      * Converts all extended type definitions of module to the list of
222      * <code>Type</code> objects.
223      *
224      * @param module
225      *            module from which is obtained set of type definitions
226      * @throws IllegalArgumentException
227      *             <ul>
228      *             <li>if module equals null</li>
229      *             <li>if name of module equals null</li>
230      *             <li>if type definitions of module equal null</li>
231      *             </ul>
232      *
233      */
234     private def void allTypeDefinitionsToGenTypes(Module module) {
235         checkArgument(module !== null, "Module reference cannot be NULL.");
236         checkArgument(module.name !== null, "Module name cannot be NULL.");
237         val it = new DataNodeIterator(module);
238         val List<TypeDefinition<?>> typeDefinitions = it.allTypedefs;
239         checkState(typeDefinitions !== null, '''Type Definitions for module Â«module.name» cannot be NULL.''');
240
241         for (TypeDefinition<?> typedef : typeDefinitions) {
242             if (typedef !== null) {
243                 val type = (typeProvider as TypeProviderImpl).generatedTypeForExtendedDefinitionType(typedef, typedef);
244                 if (type !== null) {
245                     genCtx.get(module).addTypedefType(typedef.path, type)
246                 }
247             }
248         }
249     }
250
251     private def GeneratedTypeBuilder processDataSchemaNode(Module module, String basePackageName,
252         GeneratedTypeBuilder parent, GeneratedTypeBuilder childOf, DataSchemaNode node) {
253         if (node.augmenting || node.addedByUses) {
254             return null
255         }
256         val packageName = packageNameForGeneratedType(basePackageName, node.path)
257         val genType = addDefaultInterfaceDefinition(packageName, node, childOf)
258         if (node instanceof DataNodeContainer) {
259             genCtx.get(module).addChildNodeType(node.path, genType)
260             groupingsToGenTypes(module, (node as DataNodeContainer).groupings)
261             processUsesAugments(node as DataNodeContainer, module)
262         }
263         return genType
264     }
265
266     private def void containerToGenType(Module module, String basePackageName, GeneratedTypeBuilder parent,
267         GeneratedTypeBuilder childOf, ContainerSchemaNode node) {
268         val genType = processDataSchemaNode(module, basePackageName, parent, childOf, node)
269         if (genType != null) {
270             constructGetter(parent, node.QName.localName, node.description, genType)
271             resolveDataSchemaNodes(module, basePackageName, genType, genType, node.childNodes)
272         }
273     }
274
275     private def void listToGenType(Module module, String basePackageName, GeneratedTypeBuilder parent,
276         GeneratedTypeBuilder childOf, ListSchemaNode node) {
277         val genType = processDataSchemaNode(module, basePackageName, parent, childOf, node)
278         if (genType != null) {
279             constructGetter(parent, node.QName.localName, node.description, Types.listTypeFor(genType))
280
281             val List<String> listKeys = listKeys(node);
282             val packageName = packageNameForGeneratedType(basePackageName, (node).path)
283             val genTOBuilder = resolveListKeyTOBuilder(packageName, node);
284             if (genTOBuilder !== null) {
285                 val identifierMarker = IDENTIFIER.parameterizedTypeFor(genType);
286                 val identifiableMarker = IDENTIFIABLE.parameterizedTypeFor(genTOBuilder);
287                 genTOBuilder.addImplementsType(identifierMarker);
288                 genType.addImplementsType(identifiableMarker);
289             }
290
291             for (schemaNode : node.childNodes) {
292                 if (!schemaNode.augmenting) {
293                     addSchemaNodeToListBuilders(basePackageName, schemaNode, genType, genTOBuilder, listKeys, module);
294                 }
295             }
296
297             // serialVersionUID
298             if (genTOBuilder !== null) {
299                 val GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
300                 prop.setValue(Long.toString(computeDefaultSUID(genTOBuilder as GeneratedTOBuilderImpl)));
301                 genTOBuilder.setSUID(prop);
302             }
303
304             typeBuildersToGenTypes(module, genType, genTOBuilder);
305         }
306     }
307
308     private def void processUsesAugments(DataNodeContainer node, Module module) {
309         val basePackageName = moduleNamespaceToPackageName(module);
310         for (usesNode : node.uses) {
311             for (augment : usesNode.augmentations) {
312                 usesAugmentationToGenTypes(basePackageName, augment, module, usesNode, node);
313                 processUsesAugments(augment, module);
314             }
315         }
316     }
317
318     /**
319      * Converts all <b>augmentation</b> of the module to the list
320      * <code>Type</code> objects.
321      *
322      * @param module
323      *            module from which is obtained list of all augmentation objects
324      *            to iterate over them
325      * @throws IllegalArgumentException
326      *             <ul>
327      *             <li>if the module equals null</li>
328      *             <li>if the name of module equals null</li>
329      *             <li>if the set of child nodes equals null</li>
330      *             </ul>
331      *
332      */
333     private def void allAugmentsToGenTypes(Module module) {
334         checkArgument(module !== null, "Module reference cannot be NULL.");
335         checkArgument(module.name !== null, "Module name cannot be NULL.");
336         if (module.childNodes === null) {
337             throw new IllegalArgumentException(
338                 "Reference to Set of Augmentation Definitions in module " + module.name + " cannot be NULL.");
339         }
340
341         val basePackageName = moduleNamespaceToPackageName(module);
342         val List<AugmentationSchema> augmentations = resolveAugmentations(module);
343         for (augment : augmentations) {
344             augmentationToGenTypes(basePackageName, augment, module);
345         }
346     }
347
348     /**
349      * Returns list of <code>AugmentationSchema</code> objects. The objects are
350      * sorted according to the length of their target path from the shortest to
351      * the longest.
352      *
353      * @param module
354      *            module from which is obtained list of all augmentation objects
355      * @return list of sorted <code>AugmentationSchema</code> objects obtained
356      *         from <code>module</code>
357      * @throws IllegalArgumentException
358      *             <ul>
359      *             <li>if the module equals null</li>
360      *             <li>if the set of augmentation equals null</li>
361      *             </ul>
362      *
363      */
364     private def List<AugmentationSchema> resolveAugmentations(Module module) {
365         checkArgument(module !== null, "Module reference cannot be NULL.");
366         checkState(module.augmentations !== null, "Augmentations Set cannot be NULL.");
367
368         val Set<AugmentationSchema> augmentations = module.augmentations;
369         val List<AugmentationSchema> sortedAugmentations = new ArrayList(augmentations);
370         Collections.sort(sortedAugmentations,
371             [ augSchema1, augSchema2 |
372                 if (augSchema1.targetPath.path.size() > augSchema2.targetPath.path.size()) {
373                     return 1;
374                 } else if (augSchema1.targetPath.path.size() < augSchema2.targetPath.path.size()) {
375                     return -1;
376                 }
377                 return 0;
378             ]);
379         return sortedAugmentations;
380     }
381
382     /**
383      * Converts whole <b>module</b> to <code>GeneratedType</code> object.
384      * Firstly is created the module builder object from which is vally
385      * obtained reference to <code>GeneratedType</code> object.
386      *
387      * @param module
388      *            module from which are obtained the module name, child nodes,
389      *            uses and is derived package name
390      * @return <code>GeneratedType</code> which is internal representation of
391      *         the module
392      * @throws IllegalArgumentException
393      *             if the module equals null
394      *
395      */
396     private def GeneratedTypeBuilder moduleToDataType(Module module) {
397         checkArgument(module !== null, "Module reference cannot be NULL.");
398
399         val moduleDataTypeBuilder = moduleTypeBuilder(module, "Data");
400         addImplementedInterfaceFromUses(module, moduleDataTypeBuilder);
401         moduleDataTypeBuilder.addImplementsType(DATA_ROOT);
402         return moduleDataTypeBuilder;
403     }
404
405     /**
406      * Converts all <b>rpcs</b> inputs and outputs substatements of the module
407      * to the list of <code>Type</code> objects. In addition are to containers
408      * and lists which belong to input or output also part of returning list.
409      *
410      * @param module
411      *            module from which is obtained set of all rpc objects to
412      *            iterate over them
413      * @throws IllegalArgumentException
414      *             <ul>
415      *             <li>if the module equals null</li>
416      *             <li>if the name of module equals null</li>
417      *             <li>if the set of child nodes equals null</li>
418      *             </ul>
419      *
420      */
421     private def void rpcMethodsToGenType(Module module) {
422         checkArgument(module !== null, "Module reference cannot be NULL.");
423         checkArgument(module.name !== null, "Module name cannot be NULL.");
424         checkArgument(module.childNodes !== null,
425             "Reference to Set of RPC Method Definitions in module " + module.name + " cannot be NULL.");
426
427         val basePackageName = moduleNamespaceToPackageName(module);
428         val Set<RpcDefinition> rpcDefinitions = module.rpcs;
429         if (rpcDefinitions.isEmpty()) {
430             return;
431         }
432
433         val interfaceBuilder = moduleTypeBuilder(module, "Service");
434         interfaceBuilder.addImplementsType(Types.typeForClass(RpcService));
435         for (rpc : rpcDefinitions) {
436             if (rpc !== null) {
437                 val rpcName = BindingMapping.getClassName(rpc.QName);
438                 val rpcMethodName = parseToValidParamName(rpcName);
439                 val method = interfaceBuilder.addMethod(rpcMethodName);
440                 val input = rpc.input;
441                 val output = rpc.output;
442
443                 if (input !== null) {
444                     val inType = addRawInterfaceDefinition(basePackageName, input, rpcName);
445                     addImplementedInterfaceFromUses(input, inType);
446                     inType.addImplementsType(DATA_OBJECT);
447                     inType.addImplementsType(augmentable(inType));
448                     resolveDataSchemaNodes(module, basePackageName, inType, inType, input.childNodes);
449                     genCtx.get(module).addChildNodeType(input.path, inType)
450                     val inTypeInstance = inType.toInstance();
451                     method.addParameter(inTypeInstance, "input");
452                 }
453
454                 var Type outTypeInstance = VOID;
455                 if (output !== null) {
456                     val outType = addRawInterfaceDefinition(basePackageName, output, rpcName);
457                     addImplementedInterfaceFromUses(output, outType);
458                     outType.addImplementsType(DATA_OBJECT);
459                     outType.addImplementsType(augmentable(outType));
460                     resolveDataSchemaNodes(module, basePackageName, outType, outType, output.childNodes);
461                     genCtx.get(module).addChildNodeType(output.path, outType)
462                     outTypeInstance = outType.toInstance();
463                 }
464
465                 val rpcRes = Types.parameterizedTypeFor(Types.typeForClass(RpcResult), outTypeInstance);
466                 method.setReturnType(Types.parameterizedTypeFor(FUTURE, rpcRes));
467             }
468         }
469
470         genCtx.get(module).addTopLevelNodeType(interfaceBuilder)
471     }
472
473     /**
474      * Converts all <b>notifications</b> of the module to the list of
475      * <code>Type</code> objects. In addition are to this list added containers
476      * and lists which are part of this notification.
477      *
478      * @param module
479      *            module from which is obtained set of all notification objects
480      *            to iterate over them
481      * @throws IllegalArgumentException
482      *             <ul>
483      *             <li>if the module equals null</li>
484      *             <li>if the name of module equals null</li>
485      *             <li>if the set of child nodes equals null</li>
486      *             </ul>
487      *
488      */
489     private def void notificationsToGenType(Module module) {
490         checkArgument(module !== null, "Module reference cannot be NULL.");
491         checkArgument(module.name !== null, "Module name cannot be NULL.");
492
493         if (module.childNodes === null) {
494             throw new IllegalArgumentException(
495                 "Reference to Set of Notification Definitions in module " + module.name + " cannot be NULL.");
496         }
497         val notifications = module.notifications;
498         if(notifications.empty) return;
499
500         val listenerInterface = moduleTypeBuilder(module, "Listener");
501         listenerInterface.addImplementsType(BindingTypes.NOTIFICATION_LISTENER);
502         val basePackageName = moduleNamespaceToPackageName(module);
503
504         for (notification : notifications) {
505             if (notification !== null) {
506                 processUsesAugments(notification, module);
507
508                 val notificationInterface = addDefaultInterfaceDefinition(basePackageName, notification,
509                     BindingTypes.DATA_OBJECT);
510                 notificationInterface.addImplementsType(NOTIFICATION);
511                 genCtx.get(module).addChildNodeType(notification.path, notificationInterface)
512
513                 // Notification object
514                 resolveDataSchemaNodes(module, basePackageName, notificationInterface, notificationInterface,
515                     notification.childNodes);
516
517                 listenerInterface.addMethod("on" + notificationInterface.name) //
518                 .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification").
519                     setReturnType(Types.VOID);
520             }
521         }
522
523         genCtx.get(module).addTopLevelNodeType(listenerInterface)
524     }
525
526     /**
527      * Converts all <b>identities</b> of the module to the list of
528      * <code>Type</code> objects.
529      *
530      * @param module
531      *            module from which is obtained set of all identity objects to
532      *            iterate over them
533      * @param context
534      *            schema context only used as input parameter for method
535      *            {@link identityToGenType}
536      *
537      */
538     private def void allIdentitiesToGenTypes(Module module, SchemaContext context) {
539         val Set<IdentitySchemaNode> schemaIdentities = module.identities;
540         val basePackageName = moduleNamespaceToPackageName(module);
541
542         if (schemaIdentities !== null && !schemaIdentities.isEmpty()) {
543             for (identity : schemaIdentities) {
544                 identityToGenType(module, basePackageName, identity, context);
545             }
546         }
547     }
548
549     /**
550      * Converts the <b>identity</b> object to GeneratedType. Firstly it is
551      * created transport object builder. If identity contains base identity then
552      * reference to base identity is added to superior identity as its extend.
553      * If identity doesn't contain base identity then only reference to abstract
554      * class {@link org.opendaylight.yangtools.yang.model.api.BaseIdentity
555      * BaseIdentity} is added
556      *
557      * @param module current module
558      * @param basePackageName
559      *            string contains the module package name
560      * @param identity
561      *            IdentitySchemaNode which contains data about identity
562      * @param context
563      *            SchemaContext which is used to get package and name
564      *            information about base of identity
565      *
566      */
567     private def void identityToGenType(Module module, String basePackageName, IdentitySchemaNode identity,
568         SchemaContext context) {
569         if (identity === null) {
570             return;
571         }
572         val packageName = packageNameForGeneratedType(basePackageName, identity.path);
573         val genTypeName = BindingMapping.getClassName(identity.QName);
574         val newType = new GeneratedTOBuilderImpl(packageName, genTypeName);
575         val baseIdentity = identity.baseIdentity;
576         if (baseIdentity === null) {
577             newType.setExtendsType(Types.baseIdentityTO);
578         } else {
579             val baseIdentityParentModule = SchemaContextUtil.findParentModule(context, baseIdentity);
580             val returnTypePkgName = moduleNamespaceToPackageName(baseIdentityParentModule);
581             val returnTypeName = BindingMapping.getClassName(baseIdentity.QName);
582             val gto = new GeneratedTOBuilderImpl(returnTypePkgName, returnTypeName).toInstance();
583             newType.setExtendsType(gto);
584         }
585         newType.setAbstract(true);
586         val qname = identity.QName;
587         
588         newType.qnameConstant(BindingMapping.QNAME_STATIC_FIELD_NAME,qname);
589         
590         genCtx.get(module).addIdentityType(identity.QName,newType)
591     }
592     
593     private static def qnameConstant(GeneratedTypeBuilderBase<?> toBuilder, String constantName, QName name) {
594         toBuilder.addConstant(QName.typeForClass,constantName,'''
595             org.opendaylight.yangtools.yang.common.QName.create("«name.namespace»","«name.formattedRevision»","«name.localName»")
596         ''');
597     }
598
599     /**
600      * Converts all <b>groupings</b> of the module to the list of
601      * <code>Type</code> objects. Firstly are groupings sorted according mutual
602      * dependencies. At least dependent (independent) groupings are in the list
603      * saved at first positions. For every grouping the record is added to map
604      * {@link BindingGeneratorImpl#allGroupings allGroupings}
605      *
606      * @param module
607      *            current module
608      * @param collection of groupings from which types will be generated
609      *
610      */
611     private def void groupingsToGenTypes(Module module, Collection<GroupingDefinition> groupings) {
612         val basePackageName = moduleNamespaceToPackageName(module);
613         val List<GroupingDefinition> groupingsSortedByDependencies = new GroupingDefinitionDependencySort().sort(
614             groupings);
615         for (grouping : groupingsSortedByDependencies) {
616             groupingToGenType(basePackageName, grouping, module);
617         }
618     }
619
620     /**
621      * Converts individual grouping to GeneratedType. Firstly generated type
622      * builder is created and every child node of grouping is resolved to the
623      * method.
624      *
625      * @param basePackageName
626      *            string contains the module package name
627      * @param grouping
628      *            GroupingDefinition which contains data about grouping
629      * @param module current module
630      * @return GeneratedType which is generated from grouping (object of type
631      *         <code>GroupingDefinition</code>)
632      */
633     private def void groupingToGenType(String basePackageName, GroupingDefinition grouping, Module module) {
634         val packageName = packageNameForGeneratedType(basePackageName, grouping.path);
635         val genType = addDefaultInterfaceDefinition(packageName, grouping);
636         genCtx.get(module).addGroupingType(grouping.path, genType)
637         resolveDataSchemaNodes(module, basePackageName, genType, genType, grouping.childNodes);
638         groupingsToGenTypes(module, grouping.groupings);
639         processUsesAugments(grouping, module);
640     }
641
642     /**
643      * Tries to find EnumTypeDefinition in <code>typeDefinition</code>. If base
644      * type of <code>typeDefinition</code> is of the type ExtendedType then this
645      * method is recursively called with this base type.
646      *
647      * @param typeDefinition
648      *            TypeDefinition in which should be EnumTypeDefinition found as
649      *            base type
650      * @return EnumTypeDefinition if it is found inside
651      *         <code>typeDefinition</code> or <code>null</code> in other case
652      */
653     private def EnumTypeDefinition enumTypeDefFromExtendedType(TypeDefinition<?> typeDefinition) {
654         if (typeDefinition !== null) {
655             if (typeDefinition.baseType instanceof EnumTypeDefinition) {
656                 return typeDefinition.baseType as EnumTypeDefinition;
657             } else if (typeDefinition.baseType instanceof ExtendedType) {
658                 return enumTypeDefFromExtendedType(typeDefinition.baseType);
659             }
660         }
661         return null;
662     }
663
664     /**
665      * Adds enumeration builder created from <code>enumTypeDef</code> to
666      * <code>typeBuilder</code>.
667      *
668      * Each <code>enumTypeDef</code> item is added to builder with its name and
669      * value.
670      *
671      * @param enumTypeDef
672      *            EnumTypeDefinition contains enum data
673      * @param enumName
674      *            string contains name which will be assigned to enumeration
675      *            builder
676      * @param typeBuilder
677      *            GeneratedTypeBuilder to which will be enum builder assigned
678      * @return enumeration builder which contains data from
679      *         <code>enumTypeDef</code>
680      */
681     private def EnumBuilder resolveInnerEnumFromTypeDefinition(EnumTypeDefinition enumTypeDef, QName enumName,
682         GeneratedTypeBuilder typeBuilder) {
683         if ((enumTypeDef !== null) && (typeBuilder !== null) && (enumTypeDef.QName !== null) &&
684             (enumTypeDef.QName.localName !== null)) {
685             val enumerationName = BindingMapping.getClassName(enumName);
686             val enumBuilder = typeBuilder.addEnumeration(enumerationName);
687             enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
688             return enumBuilder;
689         }
690         return null;
691     }
692
693     /**
694      * Generates type builder for <code>module</code>.
695      *
696      * @param module
697      *            Module which is source of package name for generated type
698      *            builder
699      * @param postfix
700      *            string which is added to the module class name representation
701      *            as suffix
702      * @return instance of GeneratedTypeBuilder which represents
703      *         <code>module</code>.
704      * @throws IllegalArgumentException
705      *             if <code>module</code> equals null
706      */
707     private def GeneratedTypeBuilder moduleTypeBuilder(Module module, String postfix) {
708         checkArgument(module !== null, "Module reference cannot be NULL.");
709         val packageName = moduleNamespaceToPackageName(module);
710         val moduleName = BindingMapping.getClassName(module.name) + postfix;
711         return new GeneratedTypeBuilderImpl(packageName, moduleName);
712     }
713
714     /**
715      * Converts <code>augSchema</code> to list of <code>Type</code> which
716      * contains generated type for augmentation. In addition there are also
717      * generated types for all containers, list and choices which are child of
718      * <code>augSchema</code> node or a generated types for cases are added if
719      * augmented node is choice.
720      *
721      * @param augmentPackageName
722      *            string with the name of the package to which the augmentation
723      *            belongs
724      * @param augSchema
725      *            AugmentationSchema which is contains data about augmentation
726      *            (target path, childs...)
727      * @param module current module
728      * @param parentUsesNode parent uses node of this augment (can be null if this augment is not defined under uses statement)
729      * @throws IllegalArgumentException
730      *             <ul>
731      *             <li>if <code>augmentPackageName</code> equals null</li>
732      *             <li>if <code>augSchema</code> equals null</li>
733      *             <li>if target path of <code>augSchema</code> equals null</li>
734      *             </ul>
735      */
736     private def void augmentationToGenTypes(String augmentPackageName, AugmentationSchema augSchema, Module module) {
737         checkArgument(augmentPackageName !== null, "Package Name cannot be NULL.");
738         checkArgument(augSchema !== null, "Augmentation Schema cannot be NULL.");
739         checkState(augSchema.targetPath !== null,
740             "Augmentation Schema does not contain Target Path (Target Path is NULL).");
741
742         processUsesAugments(augSchema, module);
743         val targetPath = augSchema.targetPath;
744         var SchemaNode targetSchemaNode = null
745
746         targetSchemaNode = findDataSchemaNode(schemaContext, targetPath);
747         if (targetSchemaNode instanceof DataSchemaNode && (targetSchemaNode as DataSchemaNode).isAddedByUses()) {
748             targetSchemaNode = findOriginal(targetSchemaNode as DataSchemaNode);
749             if (targetSchemaNode == null) {
750                 throw new NullPointerException(
751                     "Failed to find target node from grouping in augmentation " + augSchema + " in module " +
752                         module.name);
753             }
754         }
755         if (targetSchemaNode == null) {
756             throw new IllegalArgumentException("augment target not found: " + targetPath)
757         }
758
759         var targetTypeBuilder = findChildNodeByPath(targetSchemaNode.path)
760         if (targetTypeBuilder === null) {
761             targetTypeBuilder = findCaseByPath(targetSchemaNode.path)
762         }
763         if (targetTypeBuilder === null) {
764             throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
765         }
766
767         if (!(targetSchemaNode instanceof ChoiceNode)) {
768             var packageName = augmentPackageName;
769             val augTypeBuilder = addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName,
770                 targetTypeBuilder.toInstance, augSchema);
771             genCtx.get(module).addAugmentType(augTypeBuilder)
772             genCtx.get(module).addTypeToAugmentation(augTypeBuilder, augSchema);
773         } else {
774             generateTypesFromAugmentedChoiceCases(module, augmentPackageName, targetTypeBuilder.toInstance,
775                 targetSchemaNode as ChoiceNode, augSchema.childNodes);
776         }
777     }
778
779     private def void usesAugmentationToGenTypes(String augmentPackageName, AugmentationSchema augSchema, Module module,
780         UsesNode usesNode, DataNodeContainer usesNodeParent) {
781         checkArgument(augmentPackageName !== null, "Package Name cannot be NULL.");
782         checkArgument(augSchema !== null, "Augmentation Schema cannot be NULL.");
783         checkState(augSchema.targetPath !== null,
784             "Augmentation Schema does not contain Target Path (Target Path is NULL).");
785
786         processUsesAugments(augSchema, module);
787         val targetPath = augSchema.targetPath;
788         var SchemaNode targetSchemaNode = null
789         targetSchemaNode = findOriginalTargetFromGrouping(targetPath, usesNode);
790         if (targetSchemaNode == null) {
791             throw new IllegalArgumentException("augment target not found: " + targetPath)
792         }
793
794         var targetTypeBuilder = findChildNodeByPath(targetSchemaNode.path)
795         if (targetTypeBuilder === null) {
796             targetTypeBuilder = findCaseByPath(targetSchemaNode.path)
797         }
798         if (targetTypeBuilder === null) {
799             throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
800         }
801
802         if (!(targetSchemaNode instanceof ChoiceNode)) {
803             var packageName = augmentPackageName;
804             if (usesNodeParent instanceof SchemaNode) {
805                 packageName = packageNameForGeneratedType(augmentPackageName, (usesNodeParent as SchemaNode).path, true)
806             }
807             val augTypeBuilder = addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName,
808                 targetTypeBuilder.toInstance, augSchema);
809             genCtx.get(module).addAugmentType(augTypeBuilder)
810             genCtx.get(module).addTypeToAugmentation(augTypeBuilder, augSchema);
811         } else {
812             generateTypesFromAugmentedChoiceCases(module, augmentPackageName, targetTypeBuilder.toInstance,
813                 targetSchemaNode as ChoiceNode, augSchema.childNodes);
814         }
815     }
816
817     /**
818      * Utility method which search for original node defined in grouping.
819      */
820     private def DataSchemaNode findOriginal(DataSchemaNode node) {
821         var DataSchemaNode result = findCorrectTargetFromGrouping(node);
822         if (result == null) {
823             result = findCorrectTargetFromAugment(node);
824             if (result != null) {
825                 if (result.addedByUses) {
826                     result = findOriginal(result);
827                 }
828             }
829         }
830         return result;
831     }
832
833     private def DataSchemaNode findCorrectTargetFromAugment(DataSchemaNode node) {
834         if (!node.augmenting) {
835             return null
836         }
837
838         var QName currentName = node.QName
839         var Object currentNode = node
840         var Object parent = node;
841         val tmpPath = new ArrayList<QName>()
842         val tmpTree = new ArrayList<SchemaNode>()
843
844         var AugmentationSchema augment = null;
845         do {
846             val SchemaPath sp = (parent as SchemaNode).path
847             val List<QName> names = sp.path
848             val List<QName> newNames = new ArrayList(names)
849             newNames.remove(newNames.size - 1)
850             val SchemaPath newSp = new SchemaPath(newNames, sp.absolute)
851             parent = findDataSchemaNode(schemaContext, newSp)
852             if (parent instanceof AugmentationTarget) {
853                 tmpPath.add(currentName);
854                 tmpTree.add(currentNode as SchemaNode)
855                 augment = findNodeInAugment((parent as AugmentationTarget).availableAugmentations, currentName);
856                 if (augment == null) {
857                     currentName = (parent as DataSchemaNode).QName
858                     currentNode = parent
859                 }
860             }
861         } while ((parent as DataSchemaNode).augmenting && augment == null);
862
863         if (augment == null) {
864             return null;
865         } else {
866             Collections.reverse(tmpPath);
867             Collections.reverse(tmpTree);
868             var Object actualParent = augment;
869             var DataSchemaNode result = null;
870             for (name : tmpPath) {
871                 if (actualParent instanceof DataNodeContainer) {
872                     result = (actualParent as DataNodeContainer).getDataChildByName(name.localName);
873                     actualParent = (actualParent as DataNodeContainer).getDataChildByName(name.localName);
874                 } else {
875                     if (actualParent instanceof ChoiceNode) {
876                         result = (actualParent as ChoiceNode).getCaseNodeByName(name.localName);
877                         actualParent = (actualParent as ChoiceNode).getCaseNodeByName(name.localName);
878                     }
879                 }
880             }
881
882             if (result.addedByUses) {
883                 result = findCorrectTargetFromAugmentGrouping(result, augment, tmpTree);
884             }
885
886             return result;
887         }
888     }
889
890     private def AugmentationSchema findNodeInAugment(Collection<AugmentationSchema> augments, QName name) {
891         for (augment : augments) {
892             val DataSchemaNode node = augment.getDataChildByName(name);
893             if (node != null) {
894                 return augment;
895             }
896         }
897         return null;
898     }
899
900     private def DataSchemaNode findCorrectTargetFromGrouping(DataSchemaNode node) {
901         if (node.path.path.size == 1) {
902             // uses is under module statement
903             val Module m = findParentModule(schemaContext, node);
904             var DataSchemaNode result = null;
905             for (u : m.uses) {
906                 var SchemaNode targetGrouping = findNodeInSchemaContext(schemaContext, u.groupingPath.path);
907                 if (!(targetGrouping instanceof GroupingDefinition)) {
908                     throw new IllegalArgumentException("Failed to generate code for augment in " + u);
909                 }
910                 var gr = targetGrouping as GroupingDefinition;
911                 result = gr.getDataChildByName(node.QName.localName);
912             }
913             if (result == null) {
914                 throw new IllegalArgumentException("Failed to generate code for augment")
915             }
916             return result
917         } else {
918             var DataSchemaNode result = null;
919             var QName currentName = node.QName
920             var tmpPath = new ArrayList<QName>()
921             var Object parent = null
922
923             val SchemaPath sp = node.path
924             val List<QName> names = sp.path
925             val List<QName> newNames = new ArrayList(names)
926             newNames.remove(newNames.size - 1)
927             val SchemaPath newSp = new SchemaPath(newNames, sp.absolute)
928             parent = findDataSchemaNode(schemaContext, newSp)
929
930             do {
931                 tmpPath.add(currentName);
932                 if (parent instanceof DataNodeContainer) {
933                     val dataNodeParent = parent as DataNodeContainer;
934                     for (u : dataNodeParent.uses) {
935                         if (result == null) {
936                             result = getResultFromUses(u, currentName.localName)
937                         }
938                     }
939                 }
940                 if (result == null) {
941                     currentName = (parent as SchemaNode).QName
942                     if (parent instanceof SchemaNode) {
943                         val SchemaPath nodeSp = (parent as SchemaNode).path
944                         val List<QName> nodeNames = nodeSp.path
945                         val List<QName> nodeNewNames = new ArrayList(nodeNames)
946                         nodeNewNames.remove(nodeNewNames.size - 1)
947                         if (nodeNewNames.empty) {
948                             parent = getParentModule(parent as SchemaNode)
949                         } else {
950                             val SchemaPath nodeNewSp = new SchemaPath(nodeNewNames, nodeSp.absolute)
951                             parent = findDataSchemaNode(schemaContext, nodeNewSp)
952                         }
953                     } else {
954                         throw new IllegalArgumentException("Failed to generate code for augment")
955                     }
956                 }
957             } while (result == null && !(parent instanceof Module));
958
959             if (result != null) {
960                 result = getTargetNode(tmpPath, result)
961             }
962             return result;
963         }
964     }
965
966     private def DataSchemaNode findCorrectTargetFromAugmentGrouping(DataSchemaNode node, AugmentationSchema parentNode,
967         List<SchemaNode> dataTree) {
968
969         var DataSchemaNode result = null;
970         var QName currentName = node.QName
971         var tmpPath = new ArrayList<QName>()
972         tmpPath.add(currentName)
973         var int i = 1;
974         var Object parent = null
975
976         do {
977             if (dataTree.size < 2 || dataTree.size == i) {
978                 parent = parentNode
979             } else {
980                 parent = dataTree.get(dataTree.size - (i+1))
981                 tmpPath.add((parent as SchemaNode).QName)
982             }
983
984             val dataNodeParent = parent as DataNodeContainer;
985             for (u : dataNodeParent.uses) {
986                 if (result == null) {
987                     result = getResultFromUses(u, currentName.localName)
988                 }
989             }
990             if (result == null) {
991                 i = i + 1
992                 currentName = (parent as SchemaNode).QName
993             }
994         } while (result == null);
995
996         if (result != null) {
997             result = getTargetNode(tmpPath, result)
998         }
999         return result;
1000     }
1001
1002     private def getResultFromUses(UsesNode u, String currentName) {
1003         var SchemaNode targetGrouping = findNodeInSchemaContext(schemaContext, u.groupingPath.path)
1004         if (!(targetGrouping instanceof GroupingDefinition)) {
1005             throw new IllegalArgumentException("Failed to generate code for augment in " + u)
1006         }
1007         var gr = targetGrouping as GroupingDefinition
1008         return gr.getDataChildByName(currentName)
1009     }
1010
1011     private def getTargetNode(List<QName> tmpPath, DataSchemaNode node) {
1012         var DataSchemaNode result = node
1013         if (tmpPath.size == 1) {
1014             if (result != null && result.addedByUses) {
1015                 result = findOriginal(result);
1016             }
1017             return result;
1018         } else {
1019             var DataSchemaNode newParent = result;
1020             Collections.reverse(tmpPath);
1021
1022             tmpPath.remove(0);
1023             for (name : tmpPath) {
1024                 // searching by local name is must, because node has different namespace in its original location
1025                 if (newParent instanceof DataNodeContainer) {
1026                     newParent = (newParent as DataNodeContainer).getDataChildByName(name.localName);
1027                 } else {
1028                     newParent = (newParent as ChoiceNode).getCaseNodeByName(name.localName);
1029                 }
1030             }
1031             if (newParent != null && newParent.addedByUses) {
1032                 newParent = findOriginal(newParent);
1033             }
1034             return newParent;
1035         }
1036     }
1037
1038
1039     /**
1040      * Convenient method to find node added by uses statement.
1041      */
1042     private def DataSchemaNode findOriginalTargetFromGrouping(SchemaPath targetPath, UsesNode parentUsesNode) {
1043         var SchemaNode targetGrouping = findNodeInSchemaContext(schemaContext, parentUsesNode.groupingPath.path);
1044         if (!(targetGrouping instanceof GroupingDefinition)) {
1045             throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
1046         }
1047
1048         var grouping = targetGrouping as GroupingDefinition;
1049         var SchemaNode result = grouping;
1050         val List<QName> path = targetPath.path
1051         for (node : path) {
1052             // finding by local name is valid, grouping cannot contain nodes with same name and different namespace
1053             if (result instanceof DataNodeContainer) {
1054                 result = (result as DataNodeContainer).getDataChildByName(node.localName)
1055             } else if (result instanceof ChoiceNode) {
1056                 result = (result as ChoiceNode).getCaseNodeByName(node.localName)
1057             }
1058         }
1059         if (result == null) {
1060             return null;
1061         }
1062
1063         val String targetSchemaNodeName = result.QName.localName;
1064         var boolean fromUses = (result as DataSchemaNode).addedByUses
1065         var Iterator<UsesNode> groupingUses = grouping.uses.iterator;
1066         while (fromUses) {
1067             if (groupingUses.hasNext()) {
1068                 grouping = findNodeInSchemaContext(schemaContext, groupingUses.next().groupingPath.path) as GroupingDefinition;
1069                 result = grouping.getDataChildByName(targetSchemaNodeName);
1070                 fromUses = (result as DataSchemaNode).addedByUses;
1071             } else {
1072                 throw new NullPointerException("Failed to generate code for augment in " + parentUsesNode);
1073             }
1074         }
1075
1076         return result as DataSchemaNode
1077     }
1078
1079     /**
1080      * Returns a generated type builder for an augmentation.
1081      *
1082      * The name of the type builder is equal to the name of augmented node with
1083      * serial number as suffix.
1084      *
1085      * @param module current module
1086      * @param augmentPackageName
1087      *            string with contains the package name to which the augment
1088      *            belongs
1089      * @param basePackageName
1090      *            string with the package name to which the augmented node
1091      *            belongs
1092      * @param targetTypeRef
1093      *            target type
1094      * @param augSchema
1095      *            augmentation schema which contains data about the child nodes
1096      *            and uses of augment
1097      * @return generated type builder for augment
1098      */
1099     private def GeneratedTypeBuilder addRawAugmentGenTypeDefinition(Module module, String augmentPackageName,
1100         String basePackageName, Type targetTypeRef, AugmentationSchema augSchema) {
1101         var Map<String, GeneratedTypeBuilder> augmentBuilders = genTypeBuilders.get(augmentPackageName);
1102         if (augmentBuilders === null) {
1103             augmentBuilders = new HashMap();
1104             genTypeBuilders.put(augmentPackageName, augmentBuilders);
1105         }
1106         val augIdentifier = getAugmentIdentifier(augSchema.unknownSchemaNodes);
1107
1108         val augTypeName = if (augIdentifier !== null) {
1109                 BindingMapping.getClassName(augIdentifier)
1110             } else {
1111                 augGenTypeName(augmentBuilders, targetTypeRef.name);
1112             }
1113
1114         val augTypeBuilder = new GeneratedTypeBuilderImpl(augmentPackageName, augTypeName);
1115
1116         augTypeBuilder.addImplementsType(DATA_OBJECT);
1117         augTypeBuilder.addImplementsType(Types.augmentationTypeFor(targetTypeRef));
1118         addImplementedInterfaceFromUses(augSchema, augTypeBuilder);
1119
1120         augSchemaNodeToMethods(module, basePackageName, augTypeBuilder, augTypeBuilder, augSchema.childNodes);
1121         augmentBuilders.put(augTypeName, augTypeBuilder);
1122         return augTypeBuilder;
1123     }
1124
1125     /**
1126      *
1127      * @param unknownSchemaNodes
1128      * @return nodeParameter of UnknownSchemaNode
1129      */
1130     private def String getAugmentIdentifier(List<UnknownSchemaNode> unknownSchemaNodes) {
1131         for (unknownSchemaNode : unknownSchemaNodes) {
1132             val nodeType = unknownSchemaNode.nodeType;
1133             if (AUGMENT_IDENTIFIER_NAME.equals(nodeType.localName) &&
1134                 YANG_EXT_NAMESPACE.equals(nodeType.namespace.toString())) {
1135                 return unknownSchemaNode.nodeParameter;
1136             }
1137         }
1138         return null;
1139     }
1140
1141     /**
1142      * Returns first unique name for the augment generated type builder. The
1143      * generated type builder name for augment consists from name of augmented
1144      * node and serial number of its augmentation.
1145      *
1146      * @param builders
1147      *            map of builders which were created in the package to which the
1148      *            augmentation belongs
1149      * @param genTypeName
1150      *            string with name of augmented node
1151      * @return string with unique name for augmentation builder
1152      */
1153     private def String augGenTypeName(Map<String, GeneratedTypeBuilder> builders, String genTypeName) {
1154         var index = 1;
1155         while ((builders !== null) && builders.containsKey(genTypeName + index)) {
1156             index = index + 1;
1157         }
1158         return genTypeName + index;
1159     }
1160
1161     /**
1162      * Adds the methods to <code>typeBuilder</code> which represent subnodes of
1163      * node for which <code>typeBuilder</code> was created.
1164      *
1165      * The subnodes aren't mapped to the methods if they are part of grouping or
1166      * augment (in this case are already part of them).
1167      *
1168      * @param module current module
1169      * @param basePackageName
1170      *            string contains the module package name
1171      * @param parent
1172      *            generated type builder which represents any node. The subnodes
1173      *            of this node are added to the <code>typeBuilder</code> as
1174      *            methods. The subnode can be of type leaf, leaf-list, list,
1175      *            container, choice.
1176      * @param childOf parent type
1177      * @param schemaNodes
1178      *            set of data schema nodes which are the children of the node
1179      *            for which <code>typeBuilder</code> was created
1180      * @return generated type builder which is the same builder as input
1181      *         parameter. The getter methods (representing child nodes) could be
1182      *         added to it.
1183      */
1184     private def GeneratedTypeBuilder resolveDataSchemaNodes(Module module, String basePackageName,
1185         GeneratedTypeBuilder parent, GeneratedTypeBuilder childOf, Set<DataSchemaNode> schemaNodes) {
1186         if ((schemaNodes !== null) && (parent !== null)) {
1187             for (schemaNode : schemaNodes) {
1188                 if (!schemaNode.augmenting && !schemaNode.addedByUses) {
1189                     addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, parent, childOf, module);
1190                 }
1191             }
1192         }
1193         return parent;
1194     }
1195
1196     /**
1197      * Adds the methods to <code>typeBuilder</code> what represents subnodes of
1198      * node for which <code>typeBuilder</code> was created.
1199      *
1200      * @param module current module
1201      * @param basePackageName
1202      *            string contains the module package name
1203      * @param typeBuilder
1204      *            generated type builder which represents any node. The subnodes
1205      *            of this node are added to the <code>typeBuilder</code> as
1206      *            methods. The subnode can be of type leaf, leaf-list, list,
1207      *            container, choice.
1208      * @param childOf parent type
1209      * @param schemaNodes
1210      *            set of data schema nodes which are the children of the node
1211      *            for which <code>typeBuilder</code> was created
1212      * @return generated type builder which is the same object as the input
1213      *         parameter <code>typeBuilder</code>. The getter method could be
1214      *         added to it.
1215      */
1216     private def GeneratedTypeBuilder augSchemaNodeToMethods(Module module, String basePackageName,
1217         GeneratedTypeBuilder typeBuilder, GeneratedTypeBuilder childOf, Set<DataSchemaNode> schemaNodes) {
1218         if ((schemaNodes !== null) && (typeBuilder !== null)) {
1219             for (schemaNode : schemaNodes) {
1220                 if (!schemaNode.isAugmenting()) {
1221                     addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, typeBuilder, childOf, module);
1222                 }
1223             }
1224         }
1225         return typeBuilder;
1226     }
1227
1228     /**
1229      * Adds to <code>typeBuilder</code> a method which is derived from
1230      * <code>schemaNode</code>.
1231      *
1232      * @param basePackageName
1233      *            string with the module package name
1234      * @param node
1235      *            data schema node which is added to <code>typeBuilder</code> as
1236      *            a method
1237      * @param typeBuilder
1238      *            generated type builder to which is <code>schemaNode</code>
1239      *            added as a method.
1240      * @param childOf parent type
1241      * @param module current module
1242      */
1243     private def void addSchemaNodeToBuilderAsMethod(String basePackageName, DataSchemaNode node,
1244         GeneratedTypeBuilder typeBuilder, GeneratedTypeBuilder childOf, Module module) {
1245         if (node !== null && typeBuilder !== null) {
1246             switch (node) {
1247                 case node instanceof LeafSchemaNode:
1248                     resolveLeafSchemaNodeAsMethod(typeBuilder, node as LeafSchemaNode)
1249                 case node instanceof LeafListSchemaNode:
1250                     resolveLeafListSchemaNode(typeBuilder, node as LeafListSchemaNode)
1251                 case node instanceof ContainerSchemaNode:
1252                     containerToGenType(module, basePackageName, typeBuilder, childOf, node as ContainerSchemaNode)
1253                 case node instanceof ListSchemaNode:
1254                     listToGenType(module, basePackageName, typeBuilder, childOf, node as ListSchemaNode)
1255                 case node instanceof ChoiceNode:
1256                     choiceToGeneratedType(module, basePackageName, typeBuilder, node as ChoiceNode)
1257             }
1258         }
1259     }
1260
1261     /**
1262      * Converts <code>choiceNode</code> to the list of generated types for
1263      * choice and its cases.
1264      *
1265      * The package names for choice and for its cases are created as
1266      * concatenation of the module package (<code>basePackageName</code>) and
1267      * names of all parents node.
1268      *
1269      * @param module current module
1270      * @param basePackageName
1271      *            string with the module package name
1272      * @param parent parent type
1273      * @param childOf concrete parent for case child nodes
1274      * @param choiceNode
1275      *            choice node which is mapped to generated type. Also child
1276      *            nodes - cases are mapped to generated types.
1277      * @throws IllegalArgumentException
1278      *             <ul>
1279      *             <li>if <code>basePackageName</code> equals null</li>
1280      *             <li>if <code>choiceNode</code> equals null</li>
1281      *             </ul>
1282      *
1283      */
1284     private def void choiceToGeneratedType(Module module, String basePackageName, GeneratedTypeBuilder parent,
1285         ChoiceNode choiceNode) {
1286         checkArgument(basePackageName !== null, "Base Package Name cannot be NULL.");
1287         checkArgument(choiceNode !== null, "Choice Schema Node cannot be NULL.");
1288
1289         if (!choiceNode.addedByUses) {
1290             val packageName = packageNameForGeneratedType(basePackageName, choiceNode.path);
1291             val choiceTypeBuilder = addRawInterfaceDefinition(packageName, choiceNode);
1292             constructGetter(parent, choiceNode.QName.localName, choiceNode.description, choiceTypeBuilder);
1293             choiceTypeBuilder.addImplementsType(DataContainer.typeForClass);
1294             genCtx.get(module).addChildNodeType(choiceNode.path, choiceTypeBuilder)
1295             generateTypesFromChoiceCases(module, basePackageName, parent, choiceTypeBuilder.toInstance, choiceNode);
1296         }
1297     }
1298
1299     /**
1300      * Converts <code>caseNodes</code> set to list of corresponding generated
1301      * types.
1302      *
1303      * For every <i>case</i> which isn't added through augment or <i>uses</i> is
1304      * created generated type builder. The package names for the builder is
1305      * created as concatenation of the module package (
1306      * <code>basePackageName</code>) and names of all parents nodes of the
1307      * concrete <i>case</i>. There is also relation "<i>implements type</i>"
1308      * between every case builder and <i>choice</i> type
1309      *
1310      * @param basePackageName
1311      *            string with the module package name
1312      * @param refChoiceType
1313      *            type which represents superior <i>case</i>
1314      * @param caseNodes
1315      *            set of choice case nodes which are mapped to generated types
1316      * @return list of generated types for <code>caseNodes</code>.
1317      * @throws IllegalArgumentException
1318      *             <ul>
1319      *             <li>if <code>basePackageName</code> equals null</li>
1320      *             <li>if <code>refChoiceType</code> equals null</li>
1321      *             <li>if <code>caseNodes</code> equals null</li>
1322      *             </ul>
1323      *             *
1324      */
1325     private def void generateTypesFromChoiceCases(Module module, String basePackageName,
1326         GeneratedTypeBuilder choiceParent, Type refChoiceType, ChoiceNode choiceNode) {
1327         checkArgument(basePackageName !== null, "Base Package Name cannot be NULL.");
1328         checkArgument(refChoiceType !== null, "Referenced Choice Type cannot be NULL.");
1329         checkArgument(choiceNode !== null, "ChoiceNode cannot be NULL.");
1330
1331         val Set<ChoiceCaseNode> caseNodes = choiceNode.cases;
1332         if (caseNodes == null) {
1333             return
1334         }
1335
1336         for (caseNode : caseNodes) {
1337             if (caseNode !== null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
1338                 val packageName = packageNameForGeneratedType(basePackageName, caseNode.path)
1339                 val caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode)
1340                 caseTypeBuilder.addImplementsType(refChoiceType)
1341                 genCtx.get(module).addCaseType(caseNode.path, caseTypeBuilder)
1342                 val Set<DataSchemaNode> caseChildNodes = caseNode.childNodes
1343                 if (caseChildNodes !== null) {
1344                     var Object parentNode = null
1345                     val SchemaPath nodeSp = choiceNode.path
1346                     val List<QName> nodeNames = nodeSp.path
1347                     val List<QName> nodeNewNames = new ArrayList(nodeNames)
1348                     nodeNewNames.remove(nodeNewNames.size - 1)
1349                     val SchemaPath nodeNewSp = new SchemaPath(nodeNewNames, nodeSp.absolute)
1350                     parentNode = findDataSchemaNode(schemaContext, nodeNewSp)
1351
1352                     var SchemaNode parent
1353                     if (parentNode instanceof AugmentationSchema) {
1354                         val augSchema = parentNode as AugmentationSchema;
1355                         val targetPath = augSchema.targetPath;
1356                         var targetSchemaNode = findDataSchemaNode(schemaContext, targetPath)
1357                         if (targetSchemaNode instanceof DataSchemaNode &&
1358                             (targetSchemaNode as DataSchemaNode).isAddedByUses()) {
1359                             targetSchemaNode = findOriginal(targetSchemaNode as DataSchemaNode);
1360                             if (targetSchemaNode == null) {
1361                                 throw new NullPointerException(
1362                                     "Failed to find target node from grouping for augmentation " + augSchema +
1363                                         " in module " + module.name);
1364                             }
1365                         }
1366                         parent = targetSchemaNode
1367                     } else {
1368                         val SchemaPath sp = choiceNode.path
1369                         val List<QName> names = sp.path
1370                         val List<QName> newNames = new ArrayList(names)
1371                         newNames.remove(newNames.size - 1)
1372                         val SchemaPath newSp = new SchemaPath(newNames, sp.absolute)
1373                         parent = findDataSchemaNode(schemaContext, newSp)
1374                     }
1375                     var GeneratedTypeBuilder childOfType = findChildNodeByPath(parent.path)
1376                     resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, caseChildNodes)
1377                 }
1378             }
1379
1380             processUsesAugments(caseNode, module);
1381         }
1382     }
1383
1384     /**
1385      * Generates list of generated types for all the cases of a choice which are
1386      * added to the choice through the augment.
1387      *
1388      *
1389      * @param basePackageName
1390      *            string contains name of package to which augment belongs. If
1391      *            an augmented choice is from an other package (pcg1) than an
1392      *            augmenting choice (pcg2) then case's of the augmenting choice
1393      *            will belong to pcg2.
1394      * @param refChoiceType
1395      *            Type which represents the choice to which case belongs. Every
1396      *            case has to contain its choice in extend part.
1397      * @param caseNodes
1398      *            set of choice case nodes for which is checked if are/aren't
1399      *            added to choice through augmentation
1400      * @return list of generated types which represents augmented cases of
1401      *         choice <code>refChoiceType</code>
1402      * @throws IllegalArgumentException
1403      *             <ul>
1404      *             <li>if <code>basePackageName</code> equals null</li>
1405      *             <li>if <code>refChoiceType</code> equals null</li>
1406      *             <li>if <code>caseNodes</code> equals null</li>
1407      *             </ul>
1408      */
1409     private def void generateTypesFromAugmentedChoiceCases(Module module, String basePackageName, Type targetType,
1410         ChoiceNode targetNode, Set<DataSchemaNode> augmentedNodes) {
1411         checkArgument(basePackageName !== null, "Base Package Name cannot be NULL.");
1412         checkArgument(targetType !== null, "Referenced Choice Type cannot be NULL.");
1413         checkArgument(augmentedNodes !== null, "Set of Choice Case Nodes cannot be NULL.");
1414
1415         for (caseNode : augmentedNodes) {
1416             if (caseNode !== null) {
1417                 val packageName = packageNameForGeneratedType(basePackageName, caseNode.path);
1418                 val caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode);
1419                 caseTypeBuilder.addImplementsType(targetType);
1420
1421                 var SchemaNode parent = null
1422                 val SchemaPath nodeSp = targetNode.path
1423                 val List<QName> nodeNames = nodeSp.path
1424                 val List<QName> nodeNewNames = new ArrayList(nodeNames)
1425                 nodeNewNames.remove(nodeNewNames.size - 1)
1426                 val SchemaPath nodeNewSp = new SchemaPath(nodeNewNames, nodeSp.absolute)
1427                 parent = findDataSchemaNode(schemaContext, nodeNewSp)
1428
1429                 var GeneratedTypeBuilder childOfType = null;
1430                 if (parent instanceof Module) {
1431                     childOfType = genCtx.get(parent as Module).moduleNode
1432                 } else if (parent instanceof ChoiceCaseNode) {
1433                     childOfType = findCaseByPath(parent.path)
1434                 } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
1435                     childOfType = findChildNodeByPath(parent.path)
1436                 } else if (parent instanceof GroupingDefinition) {
1437                     childOfType = findGroupingByPath(parent.path);
1438                 }
1439
1440                 if (childOfType == null) {
1441                     throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
1442                 }
1443
1444                 if (caseNode instanceof DataNodeContainer) {
1445                     val DataNodeContainer dataNodeCase = caseNode as DataNodeContainer;
1446                     val Set<DataSchemaNode> childNodes = dataNodeCase.childNodes;
1447                     if (childNodes !== null) {
1448                         resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
1449                     }
1450                 } else {
1451                     val ChoiceCaseNode node = targetNode.getCaseNodeByName(caseNode.getQName().getLocalName());
1452                     val Set<DataSchemaNode> childNodes = node.childNodes;
1453                     if (childNodes !== null) {
1454                         resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
1455                     }
1456                 }
1457
1458                 genCtx.get(module).addCaseType(caseNode.path, caseTypeBuilder)
1459             }
1460         }
1461
1462     }
1463
1464     /**
1465      * Converts <code>leaf</code> to the getter method which is added to
1466      * <code>typeBuilder</code>.
1467      *
1468      * @param typeBuilder
1469      *            generated type builder to which is added getter method as
1470      *            <code>leaf</code> mapping
1471      * @param leaf
1472      *            leaf schema node which is mapped as getter method which is
1473      *            added to <code>typeBuilder</code>
1474      * @return boolean value
1475      *         <ul>
1476      *         <li>false - if <code>leaf</code> or <code>typeBuilder</code> are
1477      *         null</li>
1478      *         <li>true - in other cases</li>
1479      *         </ul>
1480      */
1481     private def Type resolveLeafSchemaNodeAsMethod(GeneratedTypeBuilder typeBuilder, LeafSchemaNode leaf) {
1482         var Type returnType = null;
1483         if ((leaf !== null) && (typeBuilder !== null)) {
1484             val leafName = leaf.QName.localName;
1485             var String leafDesc = leaf.description;
1486             if (leafDesc === null) {
1487                 leafDesc = "";
1488             }
1489
1490             val parentModule = findParentModule(schemaContext, leaf);
1491             if (leafName !== null && !leaf.isAddedByUses()) {
1492                 val TypeDefinition<?> typeDef = leaf.type;
1493
1494                 var GeneratedTOBuilder genTOBuilder;
1495                 if (typeDef instanceof EnumTypeDefinition) {
1496                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf);
1497                     val enumTypeDef = typeDef as EnumTypeDefinition;
1498                     val enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.QName, typeBuilder);
1499
1500                     if (enumBuilder !== null) {
1501                         returnType = new ReferencedTypeImpl(enumBuilder.packageName, enumBuilder.name);
1502                     }
1503                     (typeProvider as TypeProviderImpl).putReferencedType(leaf.path, returnType);
1504                 } else if (typeDef instanceof UnionType) {
1505                     genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule);
1506                     if (genTOBuilder !== null) {
1507                         returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule)
1508                     }
1509                 } else if (typeDef instanceof BitsTypeDefinition) {
1510                     genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule);
1511                     if (genTOBuilder !== null) {
1512                         returnType = new ReferencedTypeImpl(genTOBuilder.packageName, genTOBuilder.name);
1513                     }
1514                 } else {
1515                     val Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
1516                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions);
1517                 }
1518                 if (returnType !== null) {
1519                     val MethodSignatureBuilder getter = constructGetter(typeBuilder, leafName, leafDesc, returnType);
1520                     processContextRefExtension(leaf, getter, parentModule);
1521                 }
1522             }
1523         }
1524         return returnType;
1525     }
1526
1527     private def void processContextRefExtension(LeafSchemaNode leaf, MethodSignatureBuilder getter, Module module) {
1528         for (node : leaf.unknownSchemaNodes) {
1529             val nodeType = node.nodeType;
1530             if ("context-reference".equals(nodeType.localName)) {
1531                 val nodeParam = node.nodeParameter;
1532                 var IdentitySchemaNode identity = null;
1533                 var String basePackageName = null;
1534                 val String[] splittedElement = nodeParam.split(":");
1535                 if (splittedElement.length == 1) {
1536                     identity = findIdentityByName(module.identities, splittedElement.get(0));
1537                     basePackageName = moduleNamespaceToPackageName(module);
1538                 } else if (splittedElement.length == 2) {
1539                     var prefix = splittedElement.get(0);
1540                     val Module dependentModule = findModuleFromImports(module.imports, prefix)
1541                     if (dependentModule == null) {
1542                         throw new IllegalArgumentException(
1543                             "Failed to process context-reference: unknown prefix " + prefix);
1544                     }
1545                     identity = findIdentityByName(dependentModule.identities, splittedElement.get(1));
1546                     basePackageName = moduleNamespaceToPackageName(dependentModule);
1547                 } else {
1548                     throw new IllegalArgumentException(
1549                         "Failed to process context-reference: unknown identity " + nodeParam);
1550                 }
1551                 if (identity == null) {
1552                     throw new IllegalArgumentException(
1553                         "Failed to process context-reference: unknown identity " + nodeParam);
1554                 }
1555
1556                 val Class<RoutingContext> clazz = typeof(RoutingContext);
1557                 val AnnotationTypeBuilder rc = getter.addAnnotation(clazz.package.name, clazz.simpleName);
1558                 val packageName = packageNameForGeneratedType(basePackageName, identity.path);
1559                 val genTypeName = BindingMapping.getClassName(identity.QName.localName);
1560                 rc.addParameter("value", packageName + "." + genTypeName + ".class");
1561             }
1562         }
1563     }
1564
1565     private def IdentitySchemaNode findIdentityByName(Set<IdentitySchemaNode> identities, String name) {
1566         for (id : identities) {
1567             if (id.QName.localName.equals(name)) {
1568                 return id;
1569             }
1570         }
1571         return null;
1572     }
1573
1574     private def Module findModuleFromImports(Set<ModuleImport> imports, String prefix) {
1575         for (imp : imports) {
1576             if (imp.prefix.equals(prefix)) {
1577                 return schemaContext.findModuleByName(imp.moduleName, imp.revision);
1578             }
1579         }
1580         return null;
1581     }
1582
1583     private def boolean resolveLeafSchemaNodeAsProperty(GeneratedTOBuilder toBuilder, LeafSchemaNode leaf,
1584         boolean isReadOnly, Module module) {
1585         if ((leaf !== null) && (toBuilder !== null)) {
1586             val leafName = leaf.QName.localName;
1587             var String leafDesc = leaf.description;
1588             if (leafDesc === null) {
1589                 leafDesc = "";
1590             }
1591
1592             if (leafName !== null) {
1593                 var Type returnType = null;
1594                 val TypeDefinition<?> typeDef = leaf.type;
1595                 if (typeDef instanceof UnionTypeDefinition) {
1596                     // GeneratedType for this type definition should be already created
1597                     var qname = typeDef.QName
1598                     var Module unionModule = null
1599                     if (qname.prefix == null || qname.prefix.empty) {
1600                         unionModule = module
1601                     } else {
1602                         unionModule = findModuleFromImports(module.imports, qname.prefix)
1603                     }
1604                     val ModuleContext mc = genCtx.get(unionModule)
1605                     returnType = mc.typedefs.get(typeDef.path)
1606                 } else {
1607                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf);
1608                 }
1609                 return resolveLeafSchemaNodeAsProperty(toBuilder, leaf, returnType, isReadOnly)
1610             }
1611         }
1612         return false;
1613     }
1614
1615     /**
1616      * Converts <code>leaf</code> schema node to property of generated TO
1617      * builder.
1618      *
1619      * @param toBuilder
1620      *            generated TO builder to which is <code>leaf</code> added as
1621      *            property
1622      * @param leaf
1623      *            leaf schema node which is added to <code>toBuilder</code> as
1624      *            property
1625      * @param returnType property type
1626      * @param isReadOnly
1627      *            boolean value which says if leaf property is|isn't read only
1628      * @return boolean value
1629      *         <ul>
1630      *         <li>false - if <code>leaf</code>, <code>toBuilder</code> or leaf
1631      *         name equals null or if leaf is added by <i>uses</i>.</li>
1632      *         <li>true - other cases</li>
1633      *         </ul>
1634      */
1635     private def resolveLeafSchemaNodeAsProperty(GeneratedTOBuilder toBuilder, LeafSchemaNode leaf, Type returnType,
1636         boolean isReadOnly) {
1637         if (returnType == null) {
1638             return false;
1639         }
1640         val leafName = leaf.QName.localName
1641         val leafDesc = leaf.description
1642         val propBuilder = toBuilder.addProperty(parseToValidParamName(leafName));
1643         propBuilder.setReadOnly(isReadOnly);
1644         propBuilder.setReturnType(returnType);
1645         propBuilder.setComment(leafDesc);
1646         toBuilder.addEqualsIdentity(propBuilder);
1647         toBuilder.addHashIdentity(propBuilder);
1648         toBuilder.addToStringProperty(propBuilder);
1649         return true;
1650     }
1651
1652     /**
1653      * Converts <code>node</code> leaf list schema node to getter method of
1654      * <code>typeBuilder</code>.
1655      *
1656      * @param typeBuilder
1657      *            generated type builder to which is <code>node</code> added as
1658      *            getter method
1659      * @param node
1660      *            leaf list schema node which is added to
1661      *            <code>typeBuilder</code> as getter method
1662      * @return boolean value
1663      *         <ul>
1664      *         <li>true - if <code>node</code>, <code>typeBuilder</code>,
1665      *         nodeName equal null or <code>node</code> is added by <i>uses</i></li>
1666      *         <li>false - other cases</li>
1667      *         </ul>
1668      */
1669     private def boolean resolveLeafListSchemaNode(GeneratedTypeBuilder typeBuilder, LeafListSchemaNode node) {
1670         if ((node !== null) && (typeBuilder !== null)) {
1671             val nodeName = node.QName;
1672             var String nodeDesc = node.description;
1673             if (nodeDesc === null) {
1674                 nodeDesc = "";
1675             }
1676             if (nodeName !== null && !node.isAddedByUses()) {
1677                 val TypeDefinition<?> typeDef = node.type;
1678                 val parentModule = findParentModule(schemaContext, node);
1679
1680                 var Type returnType = null;
1681                 if (typeDef instanceof EnumTypeDefinition) {
1682                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node);
1683                     val enumTypeDef = typeDef as EnumTypeDefinition;
1684                     val enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, nodeName, typeBuilder);
1685                     returnType = new ReferencedTypeImpl(enumBuilder.packageName, enumBuilder.name);
1686                     (typeProvider as TypeProviderImpl).putReferencedType(node.path, returnType);
1687                 } else if (typeDef instanceof UnionType) {
1688                     val genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node, parentModule);
1689                     if (genTOBuilder !== null) {
1690                         returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule)
1691                     }
1692                 } else if (typeDef instanceof BitsTypeDefinition) {
1693                     val genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node, parentModule);
1694                     returnType = new ReferencedTypeImpl(genTOBuilder.packageName, genTOBuilder.name);
1695                 } else {
1696                     val Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
1697                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
1698                 }
1699
1700                 val listType = Types.listTypeFor(returnType);
1701                 constructGetter(typeBuilder, nodeName.localName, nodeDesc, listType);
1702                 return true;
1703             }
1704         }
1705         return false;
1706     }
1707
1708     private def Type createReturnTypeForUnion(GeneratedTOBuilder genTOBuilder, TypeDefinition<?> typeDef,
1709         GeneratedTypeBuilder typeBuilder, Module parentModule) {
1710         val Type returnType = new ReferencedTypeImpl(genTOBuilder.packageName, genTOBuilder.name);
1711         genTOBuilder.setTypedef(true);
1712         genTOBuilder.setIsUnion(true);
1713         (typeProvider as TypeProviderImpl).addUnitsToGenTO(genTOBuilder, typeDef.getUnits());
1714
1715         // union builder
1716         val GeneratedTOBuilder unionBuilder = new GeneratedTOBuilderImpl(typeBuilder.getPackageName(),
1717             genTOBuilder.getName() + "Builder");
1718         unionBuilder.setIsUnionBuilder(true);
1719         val MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance");
1720         method.setReturnType(returnType);
1721         method.addParameter(Types.STRING, "defaultValue");
1722         method.setAccessModifier(AccessModifier.PUBLIC);
1723         method.setStatic(true);
1724
1725         val Set<Type> types = (typeProvider as TypeProviderImpl).additionalTypes.get(parentModule);
1726         if (types == null) {
1727             (typeProvider as TypeProviderImpl).additionalTypes.put(parentModule,
1728                 Sets.newHashSet(unionBuilder.toInstance))
1729         } else {
1730             types.add(unionBuilder.toInstance)
1731         }
1732         return returnType
1733     }
1734
1735     private def GeneratedTypeBuilder addDefaultInterfaceDefinition(String packageName, SchemaNode schemaNode) {
1736         return addDefaultInterfaceDefinition(packageName, schemaNode, null);
1737     }
1738
1739     /**
1740      * Instantiates generated type builder with <code>packageName</code> and
1741      * <code>schemaNode</code>.
1742      *
1743      * The new builder always implements
1744      * {@link org.opendaylight.yangtools.yang.binding.DataObject DataObject}.<br />
1745      * If <code>schemaNode</code> is instance of GroupingDefinition it also
1746      * implements {@link org.opendaylight.yangtools.yang.binding.Augmentable
1747      * Augmentable}.<br />
1748      * If <code>schemaNode</code> is instance of
1749      * {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer
1750      * DataNodeContainer} it can also implement nodes which are specified in
1751      * <i>uses</i>.
1752      *
1753      * @param packageName
1754      *            string with the name of the package to which
1755      *            <code>schemaNode</code> belongs.
1756      * @param schemaNode
1757      *            schema node for which is created generated type builder
1758      * @param parent parent type (can be null)
1759      * @return generated type builder <code>schemaNode</code>
1760      */
1761     private def GeneratedTypeBuilder addDefaultInterfaceDefinition(String packageName, SchemaNode schemaNode,
1762         Type parent) {
1763         val it = addRawInterfaceDefinition(packageName, schemaNode, "");
1764         qnameConstant(BindingMapping.QNAME_STATIC_FIELD_NAME,schemaNode.QName);
1765         if (parent === null) {
1766             addImplementsType(DATA_OBJECT);
1767         } else {
1768             addImplementsType(BindingTypes.childOf(parent));
1769         }
1770         if (!(schemaNode instanceof GroupingDefinition)) {
1771             addImplementsType(augmentable(it));
1772         }
1773
1774         if (schemaNode instanceof DataNodeContainer) {
1775             addImplementedInterfaceFromUses(schemaNode as DataNodeContainer, it);
1776         }
1777
1778         return it;
1779     }
1780
1781     /**
1782      * Wraps the calling of the same overloaded method.
1783      *
1784      * @param packageName
1785      *            string with the package name to which returning generated type
1786      *            builder belongs
1787      * @param schemaNode
1788      *            schema node which provide data about the schema node name
1789      * @return generated type builder for <code>schemaNode</code>
1790      */
1791     private def GeneratedTypeBuilder addRawInterfaceDefinition(String packageName, SchemaNode schemaNode) {
1792         return addRawInterfaceDefinition(packageName, schemaNode, "");
1793     }
1794
1795     /**
1796      * Returns reference to generated type builder for specified
1797      * <code>schemaNode</code> with <code>packageName</code>.
1798      *
1799      * Firstly the generated type builder is searched in
1800      * {@link BindingGeneratorImpl#genTypeBuilders genTypeBuilders}. If it isn't
1801      * found it is created and added to <code>genTypeBuilders</code>.
1802      *
1803      * @param packageName
1804      *            string with the package name to which returning generated type
1805      *            builder belongs
1806      * @param schemaNode
1807      *            schema node which provide data about the schema node name
1808      * @param prefix return type name prefix
1809      * @return generated type builder for <code>schemaNode</code>
1810      * @throws IllegalArgumentException
1811      *             <ul>
1812      *             <li>if <code>schemaNode</code> equals null</li>
1813      *             <li>if <code>packageName</code> equals null</li>
1814      *             <li>if Q name of schema node is null</li>
1815      *             <li>if schema node name is null</li>
1816      *             </ul>
1817      *
1818      */
1819     private def GeneratedTypeBuilder addRawInterfaceDefinition(String packageName, SchemaNode schemaNode,
1820         String prefix) {
1821         checkArgument(schemaNode !== null, "Data Schema Node cannot be NULL.");
1822         checkArgument(packageName !== null, "Package Name for Generated Type cannot be NULL.");
1823         checkArgument(schemaNode.QName !== null, "QName for Data Schema Node cannot be NULL.");
1824         val schemaNodeName = schemaNode.QName.localName;
1825         checkArgument(schemaNodeName !== null, "Local Name of QName for Data Schema Node cannot be NULL.");
1826
1827         var String genTypeName;
1828         if (prefix === null) {
1829             genTypeName = BindingMapping.getClassName(schemaNodeName);
1830         } else {
1831             genTypeName = prefix + BindingMapping.getClassName(schemaNodeName);
1832         }
1833
1834         //FIXME: Validation of name conflict
1835         val newType = new GeneratedTypeBuilderImpl(packageName, genTypeName);
1836         if (!genTypeBuilders.containsKey(packageName)) {
1837             val Map<String, GeneratedTypeBuilder> builders = new HashMap();
1838             builders.put(genTypeName, newType);
1839             genTypeBuilders.put(packageName, builders);
1840         } else {
1841             val Map<String, GeneratedTypeBuilder> builders = genTypeBuilders.get(packageName);
1842             if (!builders.containsKey(genTypeName)) {
1843                 builders.put(genTypeName, newType);
1844             }
1845         }
1846         return newType;
1847     }
1848
1849     /**
1850      * Creates the name of the getter method from <code>methodName</code>.
1851      *
1852      * @param methodName
1853      *            string with the name of the getter method
1854      * @param returnType return type
1855      * @return string with the name of the getter method for
1856      *         <code>methodName</code> in JAVA method format
1857      */
1858     public static def String getterMethodName(String localName, Type returnType) {
1859         val method = new StringBuilder();
1860         if (BOOLEAN.equals(returnType)) {
1861             method.append("is");
1862         } else {
1863             method.append("get");
1864         }
1865         method.append(BindingMapping.getPropertyName(localName).toFirstUpper);
1866         return method.toString();
1867     }
1868
1869     /**
1870      * Created a method signature builder as part of
1871      * <code>interfaceBuilder</code>.
1872      *
1873      * The method signature builder is created for the getter method of
1874      * <code>schemaNodeName</code>. Also <code>comment</code> and
1875      * <code>returnType</code> information are added to the builder.
1876      *
1877      * @param interfaceBuilder
1878      *            generated type builder for which the getter method should be
1879      *            created
1880      * @param schemaNodeName
1881      *            string with schema node name. The name will be the part of the
1882      *            getter method name.
1883      * @param comment
1884      *            string with comment for the getter method
1885      * @param returnType
1886      *            type which represents the return type of the getter method
1887      * @return method signature builder which represents the getter method of
1888      *         <code>interfaceBuilder</code>
1889      */
1890     private def MethodSignatureBuilder constructGetter(GeneratedTypeBuilder interfaceBuilder, String schemaNodeName,
1891         String comment, Type returnType) {
1892         val getMethod = interfaceBuilder.addMethod(getterMethodName(schemaNodeName, returnType));
1893         getMethod.setComment(comment);
1894         getMethod.setReturnType(returnType);
1895         return getMethod;
1896     }
1897
1898     /**
1899      * Adds <code>schemaNode</code> to <code>typeBuilder</code> as getter method
1900      * or to <code>genTOBuilder</code> as property.
1901      *
1902      * @param basePackageName
1903      *            string contains the module package name
1904      * @param schemaNode
1905      *            data schema node which should be added as getter method to
1906      *            <code>typeBuilder</code> or as a property to
1907      *            <code>genTOBuilder</code> if is part of the list key
1908      * @param typeBuilder
1909      *            generated type builder for the list schema node
1910      * @param genTOBuilder
1911      *            generated TO builder for the list keys
1912      * @param listKeys
1913      *            list of string which contains names of the list keys
1914      * @param module current module
1915      * @throws IllegalArgumentException
1916      *             <ul>
1917      *             <li>if <code>schemaNode</code> equals null</li>
1918      *             <li>if <code>typeBuilder</code> equals null</li>
1919      *             </ul>
1920      */
1921     private def void addSchemaNodeToListBuilders(String basePackageName, DataSchemaNode schemaNode,
1922         GeneratedTypeBuilder typeBuilder, GeneratedTOBuilder genTOBuilder, List<String> listKeys, Module module) {
1923         checkArgument(schemaNode !== null, "Data Schema Node cannot be NULL.");
1924         checkArgument(typeBuilder !== null, "Generated Type Builder cannot be NULL.");
1925
1926         if (schemaNode instanceof LeafSchemaNode) {
1927             val leaf = schemaNode as LeafSchemaNode;
1928             val leafName = leaf.QName.localName;
1929             val Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf);
1930             if (listKeys.contains(leafName)) {
1931                 if (type == null) {
1932                     resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true, module)
1933                 } else {
1934                     resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, type, true)
1935                 }
1936             }
1937         } else if (!schemaNode.addedByUses) {
1938             if (schemaNode instanceof LeafListSchemaNode) {
1939                 resolveLeafListSchemaNode(typeBuilder, schemaNode as LeafListSchemaNode);
1940             } else if (schemaNode instanceof ContainerSchemaNode) {
1941                 containerToGenType(module, basePackageName, typeBuilder, typeBuilder, schemaNode as ContainerSchemaNode);
1942             } else if (schemaNode instanceof ChoiceNode) {
1943                 choiceToGeneratedType(module, basePackageName, typeBuilder, schemaNode as ChoiceNode);
1944             } else if (schemaNode instanceof ListSchemaNode) {
1945                 listToGenType(module, basePackageName, typeBuilder, typeBuilder, schemaNode as ListSchemaNode);
1946             }
1947         }
1948     }
1949
1950     private def typeBuildersToGenTypes(Module module, GeneratedTypeBuilder typeBuilder, GeneratedTOBuilder genTOBuilder) {
1951         checkArgument(typeBuilder !== null, "Generated Type Builder cannot be NULL.");
1952
1953         if (genTOBuilder !== null) {
1954             val genTO = genTOBuilder.toInstance();
1955             constructGetter(typeBuilder, "key", "Returns Primary Key of Yang List Type", genTO);
1956             genCtx.get(module).addGeneratedTOBuilder(genTOBuilder)
1957         }
1958     }
1959
1960     /**
1961      * Selects the names of the list keys from <code>list</code> and returns
1962      * them as the list of the strings
1963      *
1964      * @param list
1965      *            of string with names of the list keys
1966      * @return list of string which represents names of the list keys. If the
1967      *         <code>list</code> contains no keys then the empty list is
1968      *         returned.
1969      */
1970     private def listKeys(ListSchemaNode list) {
1971         val List<String> listKeys = new ArrayList();
1972
1973         if (list.keyDefinition !== null) {
1974             val keyDefinitions = list.keyDefinition;
1975             for (keyDefinition : keyDefinitions) {
1976                 listKeys.add(keyDefinition.localName);
1977             }
1978         }
1979         return listKeys;
1980     }
1981
1982     /**
1983      * Generates for the <code>list</code> which contains any list keys special
1984      * generated TO builder.
1985      *
1986      * @param packageName
1987      *            string with package name to which the list belongs
1988      * @param list
1989      *            list schema node which is source of data about the list name
1990      * @return generated TO builder which represents the keys of the
1991      *         <code>list</code> or null if <code>list</code> is null or list of
1992      *         key definitions is null or empty.
1993      */
1994     private def GeneratedTOBuilder resolveListKeyTOBuilder(String packageName, ListSchemaNode list) {
1995         var GeneratedTOBuilder genTOBuilder = null;
1996         if ((list.keyDefinition !== null) && (!list.keyDefinition.isEmpty())) {
1997             val listName = list.QName.localName + "Key";
1998             val String genTOName = BindingMapping.getClassName(listName);
1999             genTOBuilder = new GeneratedTOBuilderImpl(packageName, genTOName);
2000         }
2001         return genTOBuilder;
2002     }
2003
2004     /**
2005      * Builds generated TO builders for <code>typeDef</code> of type
2006      * {@link org.opendaylight.yangtools.yang.model.util.UnionType UnionType} or
2007      * {@link org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
2008      * BitsTypeDefinition} which are also added to <code>typeBuilder</code> as
2009      * enclosing transfer object.
2010      *
2011      * If more then one generated TO builder is created for enclosing then all
2012      * of the generated TO builders are added to <code>typeBuilder</code> as
2013      * enclosing transfer objects.
2014      *
2015      * @param typeDef
2016      *            type definition which can be of type <code>UnionType</code> or
2017      *            <code>BitsTypeDefinition</code>
2018      * @param typeBuilder
2019      *            generated type builder to which is added generated TO created
2020      *            from <code>typeDef</code>
2021      * @param leafName
2022      *            string with name for generated TO builder
2023      * @param leaf
2024      * @param parentModule
2025      * @return generated TO builder for <code>typeDef</code>
2026      */
2027     private def GeneratedTOBuilder addTOToTypeBuilder(TypeDefinition<?> typeDef, GeneratedTypeBuilder typeBuilder,
2028         DataSchemaNode leaf, Module parentModule) {
2029         val classNameFromLeaf = BindingMapping.getClassName(leaf.QName);
2030         val List<GeneratedTOBuilder> genTOBuilders = new ArrayList();
2031         val packageName = typeBuilder.fullyQualifiedName;
2032         if (typeDef instanceof UnionTypeDefinition) {
2033             val List<GeneratedTOBuilder> types = (typeProvider as TypeProviderImpl).
2034                     provideGeneratedTOBuildersForUnionTypeDef(packageName, (typeDef as UnionTypeDefinition),
2035                         classNameFromLeaf, leaf); 
2036             genTOBuilders.addAll(types);
2037                         
2038             
2039         var GeneratedTOBuilder resultTOBuilder = null;
2040         if (!types.isEmpty()) {
2041             resultTOBuilder = types.remove(0);
2042             for (GeneratedTOBuilder genTOBuilder : types) {
2043                 resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
2044             }
2045         }
2046
2047         val GeneratedPropertyBuilder genPropBuilder = resultTOBuilder.addProperty("value");
2048         genPropBuilder.setReturnType(Types.primitiveType("char[]", null));
2049         resultTOBuilder.addEqualsIdentity(genPropBuilder);
2050         resultTOBuilder.addHashIdentity(genPropBuilder);
2051         resultTOBuilder.addToStringProperty(genPropBuilder);
2052
2053         } else if (typeDef instanceof BitsTypeDefinition) {
2054             genTOBuilders.add(
2055                 ((typeProvider as TypeProviderImpl) ).
2056                     provideGeneratedTOBuilderForBitsTypeDefinition(packageName, typeDef, classNameFromLeaf));
2057         }
2058         if (genTOBuilders !== null && !genTOBuilders.isEmpty()) {
2059             for (genTOBuilder : genTOBuilders) {
2060                 typeBuilder.addEnclosingTransferObject(genTOBuilder);
2061             }
2062             return genTOBuilders.get(0);
2063         }
2064         return null;
2065
2066     }
2067
2068     /**
2069      * Adds the implemented types to type builder.
2070      *
2071      * The method passes through the list of <i>uses</i> in
2072      * {@code dataNodeContainer}. For every <i>use</i> is obtained corresponding
2073      * generated type from {@link BindingGeneratorImpl#allGroupings
2074      * allGroupings} which is added as <i>implements type</i> to
2075      * <code>builder</code>
2076      *
2077      * @param dataNodeContainer
2078      *            element which contains the list of used YANG groupings
2079      * @param builder
2080      *            builder to which are added implemented types according to
2081      *            <code>dataNodeContainer</code>
2082      * @return generated type builder with all implemented types
2083      */
2084     private def addImplementedInterfaceFromUses(DataNodeContainer dataNodeContainer, GeneratedTypeBuilder builder) {
2085         for (usesNode : dataNodeContainer.uses) {
2086             if (usesNode.groupingPath !== null) {
2087                 val genType = findGroupingByPath(usesNode.groupingPath).toInstance
2088                 if (genType === null) {
2089                     throw new IllegalStateException(
2090                         "Grouping " + usesNode.groupingPath + "is not resolved for " + builder.name);
2091                 }
2092                 builder.addImplementsType(genType);
2093             }
2094         }
2095         return builder;
2096     }
2097
2098     private def GeneratedTypeBuilder findChildNodeByPath(SchemaPath path) {
2099         for (ctx : genCtx.values) {
2100             var result = ctx.getChildNode(path)
2101             if (result !== null) {
2102                 return result
2103             }
2104         }
2105         return null
2106     }
2107
2108     private def GeneratedTypeBuilder findGroupingByPath(SchemaPath path) {
2109         for (ctx : genCtx.values) {
2110             var result = ctx.getGrouping(path)
2111             if (result !== null) {
2112                 return result
2113             }
2114         }
2115         return null
2116     }
2117
2118     private def GeneratedTypeBuilder findCaseByPath(SchemaPath path) {
2119         for (ctx : genCtx.values) {
2120             var result = ctx.getCase(path)
2121             if (result !== null) {
2122                 return result
2123             }
2124         }
2125         return null
2126     }
2127
2128     private def Module getParentModule(SchemaNode node) {
2129         val QName qname = node.getPath().getPath().get(0);
2130         val URI namespace = qname.getNamespace();
2131         val Date revision = qname.getRevision();
2132         return schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
2133     }
2134
2135     public def getModuleContexts() {
2136         genCtx;
2137     }
2138
2139 }