Code generator prototype - Binding specification v2
[mdsal.git] / binding2 / mdsal-binding2-generator-impl / src / main / java / org / opendaylight / mdsal / binding / javav2 / generator / impl / GenHelperUtil.java
1 /*
2  * Copyright (c) 2017 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
9 package org.opendaylight.mdsal.binding.javav2.generator.impl;
10
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.addTOToTypeBuilder;
13 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.annotateDeprecatedIfNecessary;
14 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.augGenTypeName;
15 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.constructGetter;
16 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.createDescription;
17 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.createReturnTypeForUnion;
18 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.getAugmentIdentifier;
19 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.isInnerType;
20 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.qNameConstant;
21 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.resolveInnerEnumFromTypeDefinition;
22 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.resolveListKeyTOBuilder;
23 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.computeDefaultSUID;
24 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.encodeAngleBrackets;
25 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.packageNameForGeneratedType;
26 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.NOTIFICATION;
27 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.parameterizedTypeFor;
28 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
29
30 import com.google.common.annotations.Beta;
31 import com.google.common.base.Preconditions;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
36 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
37 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
38 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
39 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
40 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
41 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedPropertyBuilderImpl;
42 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTypeBuilderImpl;
43 import org.opendaylight.mdsal.binding.javav2.generator.yang.types.TypeProviderImpl;
44 import org.opendaylight.mdsal.binding.javav2.model.api.AccessModifier;
45 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
46 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
47 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
48 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
49 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.EnumBuilder;
50 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedPropertyBuilder;
51 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTOBuilder;
52 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
53 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
54 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
55 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentable;
56 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
57 import org.opendaylight.yangtools.yang.common.QName;
58 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
59 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
61 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
62 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
63 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.Module;
66 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
67 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
68 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
69 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
70 import org.opendaylight.yangtools.yang.model.api.Status;
71 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
72 import org.opendaylight.yangtools.yang.model.api.UsesNode;
73 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
74 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
75 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
76 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
77
78 /**
79  * Helper util class used for generation of types in Binding spec v2.
80  */
81 @Beta
82 final class GenHelperUtil {
83
84     private GenHelperUtil() {
85         throw new UnsupportedOperationException("Util class");
86     }
87
88     /**
89      * Create GeneratedTypeBuilder object from module argument.
90      *
91      * @param module
92      *            Module object from which builder will be created
93      * @param genCtx
94      * @param verboseClassComments
95      *
96      * @return <code>GeneratedTypeBuilder</code> which is internal
97      *         representation of the module
98      * @throws IllegalArgumentException
99      *             if module is null
100      */
101     static GeneratedTypeBuilder moduleToDataType(final Module module, final Map<Module, ModuleContext> genCtx, final boolean verboseClassComments) {
102         Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
103
104         final GeneratedTypeBuilder moduleDataTypeBuilder = moduleTypeBuilder(module, "Data", verboseClassComments);
105         addImplementedInterfaceFromUses(module, moduleDataTypeBuilder, genCtx);
106         moduleDataTypeBuilder.addImplementsType(BindingTypes.TREE_ROOT);
107         moduleDataTypeBuilder.addComment(module.getDescription());
108         moduleDataTypeBuilder.setDescription(createDescription(module, verboseClassComments));
109         moduleDataTypeBuilder.setReference(module.getReference());
110         return moduleDataTypeBuilder;
111     }
112
113     /**
114      * Generates type builder for <code>module</code>.
115      *
116      * @param module
117      *            Module which is source of package name for generated type
118      *            builder
119      * @param postfix
120      *            string which is added to the module class name representation
121      *            as suffix
122      * @param verboseClassComments
123      * @return instance of GeneratedTypeBuilder which represents
124      *         <code>module</code>.
125      * @throws IllegalArgumentException
126      *             if <code>module</code> is null
127      */
128     static GeneratedTypeBuilder moduleTypeBuilder(final Module module, final String postfix, final boolean
129             verboseClassComments) {
130         Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
131         final String packageName = BindingMapping.getRootPackageName(module);
132         // underscore used as separator for distinction of module name parts
133         final String moduleName = new StringBuilder(module.getName()).append('_').append(postfix).toString();
134
135         final GeneratedTypeBuilderImpl moduleBuilder = new GeneratedTypeBuilderImpl(packageName, moduleName);
136         moduleBuilder.setDescription(createDescription(module, verboseClassComments));
137         moduleBuilder.setReference(module.getReference());
138         moduleBuilder.setModuleName(moduleName);
139
140         return moduleBuilder;
141     }
142
143     /**
144      * Adds the implemented types to type builder.
145      *
146      * The method passes through the list of <i>uses</i> in
147      * {@code dataNodeContainer}. For every <i>use</i> is obtained corresponding
148      * generated type from all groupings
149      * allGroupings} which is added as <i>implements type</i> to
150      * <code>builder</code>
151      *
152      * @param dataNodeContainer
153      *            element which contains the list of used YANG groupings
154      * @param builder
155      *            builder to which are added implemented types according to
156      *            <code>dataNodeContainer</code>
157      * @param genCtx generated context
158      * @return generated type builder with all implemented types
159      */
160     static GeneratedTypeBuilder addImplementedInterfaceFromUses(final DataNodeContainer dataNodeContainer,
161                           final GeneratedTypeBuilder builder, final Map<Module, ModuleContext> genCtx) {
162         for (final UsesNode usesNode : dataNodeContainer.getUses()) {
163             final GeneratedType genType = findGroupingByPath(usesNode.getGroupingPath(), genCtx).toInstance();
164             if (genType == null) {
165                 throw new IllegalStateException("Grouping " + usesNode.getGroupingPath() + "is not resolved for "
166                     + builder.getName());
167             }
168             builder.addImplementsType(genType);
169         }
170         return builder;
171     }
172
173      static GeneratedTypeBuilder findGroupingByPath(final SchemaPath path, final Map<Module, ModuleContext> genCtx) {
174         for (final ModuleContext ctx : genCtx.values()) {
175             final GeneratedTypeBuilder result = ctx.getGrouping(path);
176             if (result != null) {
177                 return result;
178             }
179         }
180         return null;
181      }
182
183     /**
184      * Adds the methods to <code>typeBuilder</code> which represent subnodes of
185      * node for which <code>typeBuilder</code> was created.
186      *
187      * The subnodes aren't mapped to the methods if they are part of grouping or
188      * augment (in this case are already part of them).
189      *
190      * @param module
191      *            current module
192      * @param basePackageName
193      *            string contains the module package name
194      * @param parent
195      *            generated type builder which represents any node. The subnodes
196      *            of this node are added to the <code>typeBuilder</code> as
197      *            methods. The subnode can be of type leaf, leaf-list, list,
198      *            container, choice.
199      * @param childOf
200      *            parent type
201      * @param schemaNodes
202      *            set of data schema nodes which are the children of the node
203      *            for which <code>typeBuilder</code> was created
204      * @return generated type builder which is the same builder as input
205      *         parameter. The getter methods (representing child nodes) could be
206      *         added to it.
207      */
208     static GeneratedTypeBuilder resolveDataSchemaNodes(final Module module, final String basePackageName,
209                           final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf,
210                           final Iterable<DataSchemaNode> schemaNodes, final Map<Module, ModuleContext> genCtx,
211                           final SchemaContext schemaContext, final boolean verboseClassComments,
212                           final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
213                           final TypeProvider typeProvider) {
214
215         if (schemaNodes != null && parent != null) {
216             for (final DataSchemaNode schemaNode : schemaNodes) {
217                 if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) {
218                     addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, parent, childOf, module, genCtx,
219                             schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
220                 }
221             }
222         }
223         return parent;
224     }
225
226     static GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode
227             schemaNode, final Module module, final Map<Module, ModuleContext> genCtx, final SchemaContext schemaContext,
228                                                               final boolean verboseClassComments, final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
229         return addDefaultInterfaceDefinition(packageName, schemaNode, null, module, genCtx, schemaContext,
230                 verboseClassComments, genTypeBuilders);
231     }
232
233     static Map<Module, ModuleContext> processUsesAugments(final SchemaContext schemaContext, final
234                         DataNodeContainer node, final Module module, Map<Module, ModuleContext> genCtx,
235                         final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
236                         final boolean verboseClassComments, final TypeProvider typeProvider) {
237         final String basePackageName = BindingMapping.getRootPackageName(module);
238         for (final UsesNode usesNode : node.getUses()) {
239             for (final AugmentationSchema augment : usesNode.getAugmentations()) {
240                 genCtx = AugmentToGenType.usesAugmentationToGenTypes(schemaContext, basePackageName, augment, module,
241                         usesNode, node, genCtx, genTypeBuilders, verboseClassComments, typeProvider);
242                 genCtx = processUsesAugments(schemaContext, augment, module, genCtx, genTypeBuilders,
243                         verboseClassComments, typeProvider);
244             }
245         }
246         return genCtx;
247     }
248
249     static GeneratedTypeBuilder findChildNodeByPath(final SchemaPath path, final Map<Module, ModuleContext> genCtx) {
250         for (final ModuleContext ctx : genCtx.values()) {
251             final GeneratedTypeBuilder result = ctx.getChildNode(path);
252             if (result != null) {
253                 return result;
254             }
255         }
256         return null;
257     }
258
259     static GeneratedTypeBuilder findCaseByPath(final SchemaPath path, final Map<Module, ModuleContext> genCtx) {
260         for (final ModuleContext ctx : genCtx.values()) {
261             final GeneratedTypeBuilder result = ctx.getCase(path);
262             if (result != null) {
263                 return result;
264             }
265         }
266         return null;
267     }
268
269     /**
270      * Returns a generated type builder for an augmentation.
271      *
272      * The name of the type builder is equal to the name of augmented node with
273      * serial number as suffix.
274      *
275      * @param module
276      *            current module
277      * @param augmentPackageName
278      *            string with contains the package name to which the augment
279      *            belongs
280      * @param basePackageName
281      *            string with the package name to which the augmented node
282      *            belongs
283      * @param targetTypeRef
284      *            target type
285      * @param augSchema
286      *            augmentation schema which contains data about the child nodes
287      *            and uses of augment
288      * @return generated type builder for augment in genCtx
289      */
290     static Map<Module, ModuleContext> addRawAugmentGenTypeDefinition(final Module module, final String augmentPackageName,
291                 final String basePackageName, final Type targetTypeRef, final AugmentationSchema augSchema,
292                 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final Map<Module, ModuleContext> genCtx) {
293
294         Map<String, GeneratedTypeBuilder> augmentBuilders = genTypeBuilders.get(augmentPackageName);
295         if (augmentBuilders == null) {
296             augmentBuilders = new HashMap<>();
297             genTypeBuilders.put(augmentPackageName, augmentBuilders);
298         }
299         String augIdentifier = getAugmentIdentifier(augSchema.getUnknownSchemaNodes());
300
301         if (augIdentifier == null) {
302             augIdentifier = augGenTypeName(augmentBuilders, targetTypeRef.getName());
303         }
304
305         GeneratedTypeBuilder augTypeBuilder = new GeneratedTypeBuilderImpl(augmentPackageName, augIdentifier);
306
307         augTypeBuilder.addImplementsType(BindingTypes.TREE_NODE);
308         augTypeBuilder.addImplementsType(Types.augmentationTypeFor(targetTypeRef));
309         annotateDeprecatedIfNecessary(augSchema.getStatus(), augTypeBuilder);
310         augTypeBuilder = addImplementedInterfaceFromUses(augSchema, augTypeBuilder, genCtx);
311
312         augTypeBuilder = augSchemaNodeToMethods(module, basePackageName, augTypeBuilder, augTypeBuilder, augSchema
313                 .getChildNodes());
314         augmentBuilders.put(augTypeBuilder.getName(), augTypeBuilder);
315
316         if(!augSchema.getChildNodes().isEmpty()) {
317             genCtx.get(module).addTypeToAugmentation(augTypeBuilder, augSchema);
318
319         }
320         genCtx.get(module).addAugmentType(augTypeBuilder);
321         return genCtx;
322     }
323
324     /**
325      * Adds the methods to <code>typeBuilder</code> what represents subnodes of
326      * node for which <code>typeBuilder</code> was created.
327      *
328      * @param module
329      *            current module
330      * @param basePackageName
331      *            string contains the module package name
332      * @param typeBuilder
333      *            generated type builder which represents any node. The subnodes
334      *            of this node are added to the <code>typeBuilder</code> as
335      *            methods. The subnode can be of type leaf, leaf-list, list,
336      *            container, choice.
337      * @param childOf
338      *            parent type
339      * @param schemaNodes
340      *            set of data schema nodes which are the children of the node
341      *            for which <code>typeBuilder</code> was created
342      * @return generated type builder which is the same object as the input
343      *         parameter <code>typeBuilder</code>. The getter method could be
344      *         added to it.
345      */
346     private static GeneratedTypeBuilder augSchemaNodeToMethods(final Module module, final String basePackageName,
347             final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf,
348             final Iterable<DataSchemaNode> schemaNodes) {
349         if (schemaNodes != null && typeBuilder != null) {
350             for (final DataSchemaNode schemaNode : schemaNodes) {
351                 if (!schemaNode.isAugmenting()) {
352                     //TODO: design decomposition and implement it
353                     //addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, typeBuilder, childOf, module);
354                 }
355             }
356         }
357         return typeBuilder;
358     }
359
360     /**
361      * Instantiates generated type builder with <code>packageName</code> and
362      * <code>schemaNode</code>.
363      *
364      * The new builder always implements
365      * {@link TreeNode TreeNode}.<br>
366      * If <code>schemaNode</code> is instance of GroupingDefinition it also
367      * implements {@link Augmentable
368      * Augmentable}.<br>
369      * If <code>schemaNode</code> is instance of
370      * {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer
371      * DataNodeContainer} it can also implement nodes which are specified in
372      * <i>uses</i>.
373      *
374      * @param packageName
375      *            string with the name of the package to which
376      *            <code>schemaNode</code> belongs.
377      * @param schemaNode
378      *            schema node for which is created generated type builder
379      * @param parent
380      *            parent type (can be null)
381      * @param schemaContext schema context
382      * @return generated type builder <code>schemaNode</code>
383      */
384     static GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode
385             schemaNode, final Type parent, final Module module, final Map<Module, ModuleContext> genCtx,
386             final SchemaContext schemaContext, final boolean verboseClassComments, final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
387
388         GeneratedTypeBuilder it = addRawInterfaceDefinition(packageName, schemaNode, schemaContext, "",
389                 verboseClassComments, genTypeBuilders);
390         if (parent == null) {
391             it.addImplementsType(BindingTypes.TREE_NODE);
392         } else {
393             if (parent instanceof ListSchemaNode) {
394                 it.addImplementsType(parameterizedTypeFor(BindingTypes.TREE_CHILD_NODE, parent, parameterizedTypeFor
395                         (BindingTypes.IDENTIFIABLE_ITEM, parent)));
396             } else {
397                 it.addImplementsType(parameterizedTypeFor(BindingTypes.TREE_CHILD_NODE, parent, parameterizedTypeFor
398                         (BindingTypes.ITEM, parent)));
399                 it.addImplementsType(parameterizedTypeFor(BindingTypes.INSTANTIABLE, it));
400             }
401         }
402
403         if (!(schemaNode instanceof GroupingDefinition)) {
404             it.addImplementsType(BindingTypes.augmentable(it));
405         }
406
407         if (schemaNode instanceof DataNodeContainer) {
408             //TODO: design decomposition and implement it
409             //groupingsToGenTypes(module, ((DataNodeContainer) schemaNode).getGroupings());
410             it = addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it, genCtx);
411         }
412
413         return it;
414     }
415
416     static GeneratedTypeBuilder resolveNotification(final GeneratedTypeBuilder listenerInterface, String
417             parentName, final String basePackageName, final NotificationDefinition notification, final Module module,
418             final SchemaContext schemaContext, final boolean verboseClassComments, Map<String, Map<String, GeneratedTypeBuilder>>
419             genTypeBuilders, TypeProvider typeProvider, Map<Module, ModuleContext> genCtx) {
420
421         processUsesAugments(schemaContext, notification, module, genCtx, genTypeBuilders,
422                 verboseClassComments, typeProvider);
423
424         final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition
425                 (basePackageName, notification, null, module, genCtx, schemaContext,
426                         verboseClassComments, genTypeBuilders);
427         annotateDeprecatedIfNecessary(notification.getStatus(), notificationInterface);
428         notificationInterface.addImplementsType(NOTIFICATION);
429         genCtx.get(module).addChildNodeType(notification, notificationInterface);
430
431         // Notification object
432         resolveDataSchemaNodes(module, basePackageName, notificationInterface,
433                 notificationInterface, notification.getChildNodes(), genCtx, schemaContext,
434                 verboseClassComments, genTypeBuilders, typeProvider);
435
436         //in case of tied notification, incorporate parent's localName
437         final StringBuilder sb = new StringBuilder("on_");
438         if (parentName != null) {
439             sb.append(parentName).append('_');
440         }
441         sb.append(notificationInterface.getName());
442
443         listenerInterface.addMethod(JavaIdentifierNormalizer.normalizeSpecificIdentifier(sb.toString(), JavaIdentifier.METHOD))
444                 .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification")
445                 .setComment(encodeAngleBrackets(notification.getDescription())).setReturnType(Types.VOID);
446         return listenerInterface;
447     }
448
449     /**
450      * Returns reference to generated type builder for specified
451      * <code>schemaNode</code> with <code>packageName</code>.
452      *
453      * Firstly the generated type builder is searched in
454      * {@link BindingGeneratorImpl#genTypeBuilders genTypeBuilders}. If it isn't
455      * found it is created and added to <code>genTypeBuilders</code>.
456      *
457      * @param packageName
458      *            string with the package name to which returning generated type
459      *            builder belongs
460      * @param schemaNode
461      *            schema node which provide data about the schema node name
462      * @param schemaContext
463      * @param prefix
464      *            return type name prefix
465      * @return generated type builder for <code>schemaNode</code>
466      * @throws IllegalArgumentException
467      *             <ul>
468      *             <li>if <code>schemaNode</code> is null</li>
469      *             <li>if <code>packageName</code> is null</li>
470      *             <li>if QName of schema node is null</li>
471      *             <li>if schemaNode name is null</li>
472      *             </ul>
473      *
474      */
475     static GeneratedTypeBuilder addRawInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
476                        final SchemaContext schemaContext, final String prefix, final boolean verboseClassComments,
477                        final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
478
479         Preconditions.checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
480         Preconditions.checkArgument(packageName != null, "Package Name for Generated Type cannot be NULL.");
481         String schemaNodeName = schemaNode.getQName().getLocalName();
482         Preconditions.checkArgument(schemaNodeName != null, "Local Name of QName for Data Schema Node cannot be NULL.");
483
484         if (prefix != null && !prefix.isEmpty()) {
485             // underscore used as separator for distinction of class name parts
486             schemaNodeName = new StringBuilder(prefix).append('_').append(schemaNodeName).toString();
487         }
488
489         final GeneratedTypeBuilderImpl newType = new GeneratedTypeBuilderImpl(packageName, schemaNodeName);
490         final Module module = SchemaContextUtil.findParentModule(schemaContext, schemaNode);
491         qNameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, schemaNode.getQName());
492         newType.addComment(schemaNode.getDescription());
493         newType.setDescription(createDescription(schemaNode, newType.getFullyQualifiedName(), schemaContext, verboseClassComments));
494         newType.setReference(schemaNode.getReference());
495         newType.setSchemaPath((List<QName>) schemaNode.getPath().getPathFromRoot());
496         newType.setModuleName(module.getName());
497
498         if (!genTypeBuilders.containsKey(packageName)) {
499             final Map<String, GeneratedTypeBuilder> builders = new HashMap<>();
500             builders.put(newType.getName(), newType);
501             genTypeBuilders.put(packageName, builders);
502         } else {
503             final Map<String, GeneratedTypeBuilder> builders = genTypeBuilders.get(packageName);
504             if (!builders.containsKey(newType.getName())) {
505                 builders.put(newType.getName(), newType);
506             }
507         }
508         return newType;
509
510     }
511
512     private static void addSchemaNodeToBuilderAsMethod(final String basePackageName, final DataSchemaNode node,
513         final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf, final Module module,
514         final Map<Module, ModuleContext> genCtx, final SchemaContext schemaContext, final boolean verboseClassComments,
515         final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
516         //TODO: implement rest of schema nodes GTO building
517         if (node != null && typeBuilder != null) {
518             if (node instanceof ContainerSchemaNode) {
519                 containerToGenType(module, basePackageName, typeBuilder, childOf, (ContainerSchemaNode) node,
520                         schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
521             } else if (node instanceof LeafSchemaNode) {
522                 resolveLeafSchemaNodeAsMethod(schemaContext, typeBuilder, genCtx, (LeafSchemaNode) node, module,
523                         typeProvider);
524             } else if (node instanceof ListSchemaNode) {
525                 listToGenType(module, basePackageName, typeBuilder, childOf, (ListSchemaNode) node, schemaContext,
526                         verboseClassComments, genCtx, genTypeBuilders, typeProvider);
527             }
528         }
529
530     }
531
532     private static void containerToGenType(final Module module, final String basePackageName,
533         final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ContainerSchemaNode node,
534         final SchemaContext schemaContext, final boolean verboseClassComments, final Map<Module, ModuleContext> genCtx,
535         final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
536
537         final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node,
538                 schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
539         if (genType != null) {
540             constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), genType, node.getStatus());
541             resolveDataSchemaNodes(module, basePackageName, genType, genType, node.getChildNodes(), genCtx,
542                     schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
543         }
544     }
545
546     private static void listToGenType(final Module module, final String basePackageName, final GeneratedTypeBuilder
547             parent, final GeneratedTypeBuilder childOf, final ListSchemaNode node, final SchemaContext schemaContext,
548             final boolean verboseClassComments, final Map<Module, ModuleContext> genCtx,
549             final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
550         final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node,
551                 schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
552         if (genType != null) {
553             final String nodeName = node.getQName().getLocalName();
554             constructGetter(parent, nodeName, node.getDescription(),
555                     Types.listTypeFor(genType), node.getStatus());
556             final List<QName> listKeys = node.getKeyDefinition();
557             final String packageName = new StringBuilder(packageNameForGeneratedType(basePackageName, node.getPath(),
558                     BindingNamespaceType.Key)).append('.').append(nodeName).toString();
559
560             final GeneratedTOBuilder genTOBuilder = resolveListKeyTOBuilder(packageName, node);
561
562             for (final DataSchemaNode schemaNode : node.getChildNodes()) {
563                 if (!schemaNode.isAugmenting()) {
564                     addSchemaNodeToListBuilders(nodeName, basePackageName, schemaNode, genType, genTOBuilder, listKeys,
565                             module, typeProvider, schemaContext, genCtx, genTypeBuilders, verboseClassComments);
566                 }
567             }
568
569             // serialVersionUID
570             if (genTOBuilder != null) {
571                 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
572                 prop.setValue(Long.toString(computeDefaultSUID(genTOBuilder)));
573                 genTOBuilder.setSUID(prop);
574             }
575
576             typeBuildersToGenTypes(module, genType, genTOBuilder, genCtx);
577         }
578     }
579
580     private static void typeBuildersToGenTypes(final Module module, final GeneratedTypeBuilder typeBuilder,
581             final GeneratedTOBuilder genTOBuilder, final Map<Module, ModuleContext> genCtx) {
582         checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL.");
583         if (genTOBuilder != null) {
584             final GeneratedTransferObject genTO = genTOBuilder.toInstance();
585             constructGetter(typeBuilder, "key", "Returns Primary Key of Yang List Type", genTO, Status.CURRENT);
586             genCtx.get(module).addGeneratedTOBuilder(genTOBuilder);
587         }
588     }
589
590     /**
591      * Converts <code>leaf</code> to the getter method which is added to
592      * <code>typeBuilder</code>.
593      *
594      * @param typeBuilder
595      *            generated type builder to which is added getter method as
596      *            <code>leaf</code> mapping
597      * @param leaf
598      *            leaf schema node which is mapped as getter method which is
599      *            added to <code>typeBuilder</code>
600      * @param module
601      *            Module in which type was defined
602      * @return boolean value
603      *         <ul>
604      *         <li>false - if <code>leaf</code> or <code>typeBuilder</code> are
605      *         null</li>
606      *         <li>true - in other cases</li>
607      *         </ul>
608      */
609     private static Type resolveLeafSchemaNodeAsMethod(final SchemaContext schemaContext, final GeneratedTypeBuilder
610             typeBuilder, final Map<Module, ModuleContext> genCtx, final LeafSchemaNode leaf, final Module module,
611             final TypeProvider typeProvider) {
612         if (leaf == null || typeBuilder == null || leaf.isAddedByUses()) {
613             return null;
614         }
615
616         final String leafName = leaf.getQName().getLocalName();
617         if (leafName == null) {
618             return null;
619         }
620
621         final Module parentModule = findParentModule(schemaContext, leaf);
622         Type returnType = null;
623
624         final TypeDefinition<?> typeDef = leaf.getType();
625         if (isInnerType(leaf, typeDef)) {
626             if (typeDef instanceof EnumTypeDefinition) {
627                 returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf);
628                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
629                 final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.getQName(),
630                         genCtx, typeBuilder, module);
631                 if (enumBuilder != null) {
632                     returnType = enumBuilder.toInstance(typeBuilder);
633                 }
634                 ((TypeProviderImpl) typeProvider).putReferencedType(leaf.getPath(), returnType);
635             } else if (typeDef instanceof UnionTypeDefinition) {
636                 final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule,
637                         typeProvider, schemaContext);
638                 if (genTOBuilder != null) {
639                     //TODO: https://bugs.opendaylight.org/show_bug.cgi?id=2289
640                     returnType = createReturnTypeForUnion(genTOBuilder, typeDef, typeBuilder, parentModule, typeProvider);
641                 }
642             } else if (typeDef instanceof BitsTypeDefinition) {
643                 final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule,
644                         typeProvider, schemaContext);
645                 if (genTOBuilder != null) {
646                     returnType = genTOBuilder.toInstance();
647                 }
648             } else {
649                 // It is constrained version of already declared type (inner declared type exists,
650                 // onlyfor special cases (Enum, Union, Bits), which were already checked.
651                 // In order to get proper class we need to look up closest derived type
652                 // and apply restrictions from leaf type
653                 final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
654                 returnType = typeProvider.javaTypeForSchemaDefinitionType(getBaseOrDeclaredType(typeDef), leaf,
655                         restrictions);
656             }
657         } else {
658             final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
659             returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions);
660         }
661
662         if (returnType == null) {
663             return null;
664         }
665
666         if (typeDef instanceof EnumTypeDefinition) {
667             ((TypeProviderImpl) typeProvider).putReferencedType(leaf.getPath(), returnType);
668         }
669
670         String leafDesc = leaf.getDescription();
671         if (leafDesc == null) {
672             leafDesc = "";
673         }
674
675         constructGetter(typeBuilder, leafName, leafDesc, returnType, leaf.getStatus());
676         genCtx.get(module).addChildNodeType(leaf, typeBuilder);
677         return returnType;
678     }
679
680     /**
681      * Adds <code>schemaNode</code> to <code>typeBuilder</code> as getter method
682      * or to <code>genTOBuilder</code> as property.
683      *
684      * @param nodeName
685      *            string contains the name of list
686      * @param basePackageName
687      *            string contains the module package name
688      * @param schemaNode
689      *            data schema node which should be added as getter method to
690      *            <code>typeBuilder</code> or as a property to
691      *            <code>genTOBuilder</code> if is part of the list key
692      * @param typeBuilder
693      *            generated type builder for the list schema node
694      * @param genTOBuilder
695      *            generated TO builder for the list keys
696      * @param listKeys
697      *            list of string which contains QNames of the list keys
698      * @param module
699      *            current module
700      * @param typeProvider
701      *            provider that defines contract for generated types
702      * @param schemaContext
703      *            schema context
704      * @param genCtx
705      *            map of generated entities in context of YANG modules
706      * @param genTypeBuilders
707      *            map of generated type builders
708      * @param verboseClassComments
709      *            generate verbose comments
710      * @throws IllegalArgumentException
711      *             <ul>
712      *             <li>if <code>schemaNode</code> equals null</li>
713      *             <li>if <code>typeBuilder</code> equals null</li>
714      *             </ul>
715      */
716     private static void addSchemaNodeToListBuilders(final String nodeName, final String basePackageName,
717             final DataSchemaNode schemaNode, final GeneratedTypeBuilder typeBuilder,
718             final GeneratedTOBuilder genTOBuilder, final List<QName> listKeys, final Module module,
719             final TypeProvider typeProvider, final SchemaContext schemaContext, final Map<Module, ModuleContext> genCtx,
720             final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
721         checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
722         checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL.");
723
724         if (schemaNode instanceof LeafSchemaNode) {
725             final LeafSchemaNode leaf = (LeafSchemaNode) schemaNode;
726             final QName leafQName = leaf.getQName();
727             final String leafName = leafQName.getLocalName();
728             String leafPckgName = basePackageName;
729             boolean isKeyPart = false;
730             if (listKeys.contains(leafQName)) {
731                 leafPckgName = new StringBuilder(leafPckgName).append('.').append(BindingNamespaceType.Key).append('.')
732                         .append(nodeName).toString();
733                 isKeyPart = true;
734             } else {
735                 leafPckgName = new StringBuilder(leafPckgName).append('.').append(BindingNamespaceType.Data).append('.')
736                         .append(nodeName).toString();
737             }
738
739             final String leafGTOName = new StringBuilder(nodeName).append('_').append(leafName).toString();
740             final GeneratedTypeBuilder leafGTp = new GeneratedTypeBuilderImpl(leafPckgName, leafGTOName);
741             resolveLeafSchemaNodeAsMethod(schemaContext, leafGTp, genCtx, leaf, module,
742                     typeProvider);
743
744             constructGetter(typeBuilder, leafGTOName, schemaNode.getDescription(), leafGTp, Status.CURRENT);
745
746             if (isKeyPart) {
747                 AuxiliaryGenUtils.resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, leafGTp, true);
748             }
749         } else if (!schemaNode.isAddedByUses()) {
750             //TODO: implement leaf list to generated type
751             //TODO: implement choice to generated type
752             if (schemaNode instanceof ContainerSchemaNode) {
753                 containerToGenType(module, basePackageName, typeBuilder, typeBuilder, (ContainerSchemaNode) schemaNode,
754                         schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
755             } else if (schemaNode instanceof ListSchemaNode) {
756                 listToGenType(module, basePackageName, typeBuilder, typeBuilder, (ListSchemaNode) schemaNode,
757                         schemaContext, verboseClassComments, genCtx, genTypeBuilders, typeProvider);
758             }
759         }
760     }
761
762     private static TypeDefinition<?> getBaseOrDeclaredType(final TypeDefinition<?> typeDef) {
763         final TypeDefinition<?> baseType = typeDef.getBaseType();
764         return (baseType != null && baseType.getBaseType() != null) ? baseType : typeDef;
765     }
766
767     @SuppressWarnings({ "rawtypes", "unchecked" })
768     private static GeneratedTypeBuilder processDataSchemaNode(final Module module, final String basePackageName,
769         final GeneratedTypeBuilder childOf, final DataSchemaNode node, final SchemaContext schemaContext,
770         final boolean verboseClassComments, final Map<Module, ModuleContext> genCtx, final Map<String, Map<String,
771         GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
772
773         if (node.isAugmenting() || node.isAddedByUses()) {
774             return null;
775         }
776         final String packageName = packageNameForGeneratedType(basePackageName, node.getPath(), BindingNamespaceType.Data);
777         final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf, module,
778                 genCtx, schemaContext, verboseClassComments, genTypeBuilders);
779         genType.addComment(node.getDescription());
780         annotateDeprecatedIfNecessary(node.getStatus(), genType);
781         genType.setDescription(createDescription(node, genType.getFullyQualifiedName(), schemaContext, verboseClassComments));
782         genType.setModuleName(module.getName());
783         genType.setReference(node.getReference());
784         genType.setSchemaPath((List) node.getPath().getPathFromRoot());
785         if (node instanceof DataNodeContainer) {
786             genCtx.get(module).addChildNodeType(node, genType);
787             //TODO: implement groupings to GTO building first
788             // groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings());
789             processUsesAugments(schemaContext, (DataNodeContainer) node, module, genCtx, genTypeBuilders,
790                     verboseClassComments, typeProvider);
791         }
792         return genType;
793     }
794 }