65d4b48dbe57b1a9b2b27f6b83b7cfdc507011b8
[controller.git] / opendaylight / sal / yang-prototype / code-generator / binding-generator-impl / src / main / java / org / opendaylight / controller / sal / binding / generator / impl / BindingGeneratorImpl.java
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.controller.sal.binding.generator.impl;
9
10 import org.opendaylight.controller.binding.generator.util.ReferencedTypeImpl;
11 import org.opendaylight.controller.binding.generator.util.Types;
12 import org.opendaylight.controller.binding.generator.util.generated.type.builder.GeneratedTypeBuilderImpl;
13 import org.opendaylight.controller.sal.binding.generator.api.BindingGenerator;
14 import org.opendaylight.controller.sal.binding.generator.spi.TypeProvider;
15 import org.opendaylight.controller.sal.binding.model.api.*;
16 import org.opendaylight.controller.sal.binding.model.api.type.builder.*;
17 import org.opendaylight.controller.sal.binding.yang.types.TypeProviderImpl;
18 import org.opendaylight.controller.yang.common.QName;
19 import org.opendaylight.controller.yang.model.api.*;
20 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition;
21 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition.EnumPair;
22 import org.opendaylight.controller.yang.model.util.DataNodeIterator;
23 import org.opendaylight.controller.yang.model.util.ExtendedType;
24
25 import java.util.*;
26
27 import static org.opendaylight.controller.binding.generator.util.BindingGeneratorUtil.*;
28 import static org.opendaylight.controller.yang.model.util.SchemaContextUtil.findDataSchemaNode;
29 import static org.opendaylight.controller.yang.model.util.SchemaContextUtil.findParentModule;
30
31 public final class BindingGeneratorImpl implements BindingGenerator {
32
33     private Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders;
34     private TypeProvider typeProvider;
35     private SchemaContext schemaContext;
36
37     public BindingGeneratorImpl() {
38         super();
39     }
40
41     @Override
42     public List<Type> generateTypes(final SchemaContext context) {
43         if (context == null) {
44             throw new IllegalArgumentException("Schema Context reference " +
45                     "cannot be NULL!");
46         }
47         if (context.getModules() == null) {
48             throw new IllegalStateException("Schema Context does not contain " +
49                     "defined modules!");
50         }
51
52         final List<Type> generatedTypes = new ArrayList<>();
53         schemaContext = context;
54         typeProvider = new TypeProviderImpl(context);
55         final Set<Module> modules = context.getModules();
56         genTypeBuilders = new HashMap<>();
57         for (final Module module : modules) {
58             generatedTypes.add(moduleToDataType(module));
59             generatedTypes.addAll(allTypeDefinitionsToGenTypes(module));
60             generatedTypes.addAll(allContainersToGenTypes(module));
61             generatedTypes.addAll(allListsToGenTypes(module));
62             generatedTypes.addAll(allAugmentsToGenTypes(module));
63             generatedTypes.addAll(allRPCMethodsToGenType(module));
64             generatedTypes.addAll(allNotifycationsToGenType(module));
65         }
66         return generatedTypes;
67     }
68
69     @Override
70     public List<Type> generateTypes(final SchemaContext context,
71                                     final Set<Module> modules) {
72         if (context == null) {
73             throw new IllegalArgumentException("Schema Context reference " +
74                     "cannot be NULL!");
75         }
76         if (context.getModules() == null) {
77             throw new IllegalStateException("Schema Context does not contain " +
78                     "defined modules!");
79         }
80         if (modules == null) {
81             throw new IllegalArgumentException("Sef of Modules cannot be " +
82                     "NULL!");
83         }
84
85         final List<Type> filteredGenTypes = new ArrayList<>();
86         schemaContext = context;
87         typeProvider = new TypeProviderImpl(context);
88         final Set<Module> contextModules = context.getModules();
89         genTypeBuilders = new HashMap<>();
90         for (final Module contextModule : contextModules) {
91             final List<Type> generatedTypes = new ArrayList<>();
92
93             generatedTypes.add(moduleToDataType(contextModule));
94             generatedTypes.addAll(allTypeDefinitionsToGenTypes(contextModule));
95             generatedTypes.addAll(allContainersToGenTypes(contextModule));
96             generatedTypes.addAll(allListsToGenTypes(contextModule));
97             generatedTypes.addAll(allAugmentsToGenTypes(contextModule));
98             generatedTypes.addAll(allRPCMethodsToGenType(contextModule));
99             generatedTypes.addAll(allNotifycationsToGenType(contextModule));
100
101             if (modules.contains(contextModule)) {
102                 filteredGenTypes.addAll(generatedTypes);
103             }
104         }
105         return filteredGenTypes;
106     }
107
108     private List<Type> allTypeDefinitionsToGenTypes(final Module module) {
109         if (module == null) {
110             throw new IllegalArgumentException("Module reference cannot be NULL!");
111         }
112         if (module.getName() == null) {
113             throw new IllegalArgumentException("Module name cannot be NULL!");
114         }
115         if (module.getTypeDefinitions() == null) {
116             throw new IllegalArgumentException("Type Definitions for module " +
117                     module.getName() + " cannot be NULL!");
118         }
119
120         final Set<TypeDefinition<?>> typeDefinitions = module
121                 .getTypeDefinitions();
122         final List<Type> generatedTypes = new ArrayList<>();
123         for (final TypeDefinition<?> typedef : typeDefinitions) {
124             if (typedef != null) {
125                 final Type type = ((TypeProviderImpl)typeProvider)
126                         .generatedTypeForExtendedDefinitionType(typedef);
127                 if ((type != null) && !generatedTypes.contains(type)) {
128                     generatedTypes.add(type);
129                 }
130             }
131         }
132         return generatedTypes;
133     }
134
135     private List<Type> allContainersToGenTypes(final Module module) {
136         if (module == null) {
137             throw new IllegalArgumentException("Module reference cannot be NULL!");
138         }
139
140         if (module.getName() == null) {
141             throw new IllegalArgumentException("Module name cannot be NULL!");
142         }
143
144         if (module.getChildNodes() == null) {
145             throw new IllegalArgumentException("Reference to Set of Child " +
146                     "Nodes in module " + module.getName() + " cannot be " +
147                     "NULL!");
148         }
149
150         final List<Type> generatedTypes = new ArrayList<>();
151         final DataNodeIterator it = new DataNodeIterator(
152                 module);
153         final List<ContainerSchemaNode> schemaContainers = it.allContainers();
154         final String basePackageName = moduleNamespaceToPackageName(module);
155         for (final ContainerSchemaNode container : schemaContainers) {
156             generatedTypes.add(containerToGenType(basePackageName,
157                     container));
158         }
159         return generatedTypes;
160     }
161
162     private List<Type> allListsToGenTypes(final Module module) {
163         if (module == null) {
164             throw new IllegalArgumentException("Module reference cannot be NULL!");
165         }
166
167         if (module.getName() == null) {
168             throw new IllegalArgumentException("Module name cannot be NULL!");
169         }
170
171         if (module.getChildNodes() == null) {
172             throw new IllegalArgumentException("Reference to Set of Child " +
173                     "Nodes in module " + module.getName() + " cannot be " +
174                     "NULL!");
175         }
176
177         final List<Type> generatedTypes = new ArrayList<>();
178         final DataNodeIterator it = new DataNodeIterator(
179                 module);
180         final List<ListSchemaNode> schemaLists = it.allLists();
181         final String basePackageName = moduleNamespaceToPackageName(module);
182         if (schemaLists != null) {
183             for (final ListSchemaNode list : schemaLists) {
184                 generatedTypes.addAll(listToGenType(basePackageName, list));
185             }
186         }
187         return generatedTypes;
188     }
189
190     private List<Type> allAugmentsToGenTypes(final Module module) {
191         if (module == null) {
192             throw new IllegalArgumentException("Module reference cannot be NULL!");
193         }
194
195         if (module.getName() == null) {
196             throw new IllegalArgumentException("Module name cannot be NULL!");
197         }
198
199         if (module.getChildNodes() == null) {
200             throw new IllegalArgumentException("Reference to Set of " +
201                     "Augmentation Definitions in module " + module.getName()
202                     + " cannot be NULL!");
203         }
204
205         final List<Type> generatedTypes = new ArrayList<>();
206         final String basePackageName = moduleNamespaceToPackageName(module);
207         final List<AugmentationSchema> augmentations = resolveAugmentations(module);
208         for (final AugmentationSchema augment : augmentations) {
209             generatedTypes.addAll(augmentationToGenTypes(basePackageName, augment));
210         }
211         return generatedTypes;
212     }
213
214     private List<AugmentationSchema> resolveAugmentations(final Module module) {
215         if (module == null) {
216             throw new IllegalArgumentException("Module reference cannot be NULL!");
217         }
218         if (module.getAugmentations() == null) {
219             throw new IllegalStateException("Augmentations Set cannot be NULL!");
220         }
221
222         final Set<AugmentationSchema> augmentations = module
223                 .getAugmentations();
224         final List<AugmentationSchema> sortedAugmentations = new ArrayList<>(
225                 augmentations);
226         Collections.sort(sortedAugmentations,
227                 new Comparator<AugmentationSchema>() {
228
229                     @Override
230                     public int compare(
231                             AugmentationSchema augSchema1,
232                             AugmentationSchema augSchema2) {
233
234                         if (augSchema1.getTargetPath().getPath()
235                                 .size() > augSchema2
236                                 .getTargetPath().getPath().size()) {
237                             return 1;
238                         } else if (augSchema1.getTargetPath()
239                                 .getPath().size() < augSchema2
240                                 .getTargetPath().getPath().size()) {
241                             return -1;
242                         }
243                         return 0;
244
245                     }
246                 });
247
248         return sortedAugmentations;
249     }
250
251     private GeneratedType moduleToDataType(final Module module) {
252         if (module == null) {
253             throw new IllegalArgumentException("Module reference cannot be NULL!");
254         }
255
256         final GeneratedTypeBuilder moduleDataTypeBuilder = moduleTypeBuilder(
257                 module, "Data");
258
259         final String basePackageName = moduleNamespaceToPackageName(module);
260         if (moduleDataTypeBuilder != null) {
261             final Set<DataSchemaNode> dataNodes = module.getChildNodes();
262             resolveDataSchemaNodes(basePackageName, moduleDataTypeBuilder, dataNodes);
263         }
264         return moduleDataTypeBuilder.toInstance();
265     }
266
267     private List<GeneratedType> allRPCMethodsToGenType(final Module module) {
268         if (module == null) {
269             throw new IllegalArgumentException("Module reference cannot be NULL!");
270         }
271
272         if (module.getName() == null) {
273             throw new IllegalArgumentException("Module name cannot be NULL!");
274         }
275
276         if (module.getChildNodes() == null) {
277             throw new IllegalArgumentException("Reference to Set of " +
278                     "RPC Method Definitions in module " + module.getName()
279                     + " cannot be NULL!");
280         }
281
282         final String basePackageName = moduleNamespaceToPackageName(module);
283         final Set<RpcDefinition> rpcDefinitions = module.getRpcs();
284         final List<GeneratedType> genRPCTypes = new ArrayList<>();
285
286         for (final RpcDefinition rpc : rpcDefinitions) {
287             if (rpc != null) {
288                 final List<DataNodeIterator> rpcInOut = new ArrayList<>();
289                 rpcInOut.add(new DataNodeIterator(rpc.getInput()));
290                 rpcInOut.add(new DataNodeIterator(rpc.getOutput()));
291
292                 for (DataNodeIterator it : rpcInOut) {
293                     List<ContainerSchemaNode> nContainers = it.allContainers();
294                     if ((nContainers != null) && !nContainers.isEmpty()) {
295                         for (final ContainerSchemaNode container : nContainers) {
296                             genRPCTypes.add(containerToGenType(basePackageName, container));
297                         }
298                     }
299                 }
300             }
301         }
302         return genRPCTypes;
303     }
304
305     private List<Type> allNotifycationsToGenType(final Module module) {
306         if (module == null) {
307             throw new IllegalArgumentException("Module reference cannot be NULL!");
308         }
309
310         if (module.getName() == null) {
311             throw new IllegalArgumentException("Module name cannot be NULL!");
312         }
313
314         if (module.getChildNodes() == null) {
315             throw new IllegalArgumentException("Reference to Set of " +
316                     "Notification Definitions in module " + module.getName()
317                     + " cannot be NULL!");
318         }
319
320         final String basePackageName = moduleNamespaceToPackageName(module);
321         final List<Type> genNotifyTypes = new ArrayList<>();
322         final Set<NotificationDefinition> notifications = module
323                 .getNotifications();
324
325         for (final NotificationDefinition notification : notifications) {
326             if (notification != null) {
327                 final List<DataNodeIterator> notifyChildren = new ArrayList<>();
328
329                 for (DataSchemaNode childNode : notification.getChildNodes()) {
330                     if (childNode instanceof DataNodeContainer) {
331                         notifyChildren.add(new DataNodeIterator((DataNodeContainer) childNode));
332                     }
333                 }
334
335                 for (DataNodeIterator it : notifyChildren) {
336                     List<ContainerSchemaNode> nContainers = it.allContainers();
337                     List<ListSchemaNode> nLists = it.allLists();
338                     if ((nContainers != null) && !nContainers.isEmpty()) {
339                         for (final ContainerSchemaNode container : nContainers) {
340                             genNotifyTypes.add(containerToGenType(basePackageName, container));
341                         }
342                     }
343                     if ((nLists != null) && !nLists.isEmpty()) {
344                         for (final ListSchemaNode list : nLists) {
345                             genNotifyTypes.addAll(listToGenType(basePackageName, list));
346                         }
347                     }
348                 }
349             }
350         }
351         return genNotifyTypes;
352     }
353
354     private EnumTypeDefinition enumTypeDefFromExtendedType(
355             final TypeDefinition<?> typeDefinition) {
356         if (typeDefinition != null) {
357             if (typeDefinition.getBaseType() instanceof EnumTypeDefinition) {
358                 return (EnumTypeDefinition) typeDefinition.getBaseType();
359             } else if (typeDefinition.getBaseType() instanceof ExtendedType) {
360                 return enumTypeDefFromExtendedType(typeDefinition.getBaseType());
361             }
362         }
363         return null;
364     }
365
366     private EnumBuilder resolveInnerEnumFromTypeDefinition(
367             final EnumTypeDefinition enumTypeDef, final String enumName,
368             final GeneratedTypeBuilder typeBuilder) {
369         if ((enumTypeDef != null) && (typeBuilder != null)
370                 && (enumTypeDef.getQName() != null)
371                 && (enumTypeDef.getQName().getLocalName() != null)) {
372
373             final String enumerationName = parseToClassName(enumName);
374             final EnumBuilder enumBuilder = typeBuilder
375                     .addEnumeration(enumerationName);
376
377             if (enumBuilder != null) {
378                 final List<EnumPair> enums = enumTypeDef.getValues();
379                 if (enums != null) {
380                     int listIndex = 0;
381                     for (final EnumPair enumPair : enums) {
382                         if (enumPair != null) {
383                             final String enumPairName = parseToClassName(enumPair
384                                     .getName());
385                             Integer enumPairValue = enumPair.getValue();
386
387                             if (enumPairValue == null) {
388                                 enumPairValue = listIndex;
389                             }
390                             enumBuilder.addValue(enumPairName, enumPairValue);
391                             listIndex++;
392                         }
393                     }
394                 }
395                 return enumBuilder;
396             }
397         }
398         return null;
399     }
400
401     private GeneratedTypeBuilder moduleTypeBuilder(final Module module,
402                                                    final String postfix) {
403         if (module == null) {
404             throw new IllegalArgumentException("Module reference cannot be NULL!");
405         }
406         String packageName = moduleNamespaceToPackageName(module);
407         final String moduleName = parseToClassName(module.getName())
408                 + postfix;
409
410         return new GeneratedTypeBuilderImpl(packageName, moduleName);
411
412     }
413
414     private List<Type> augmentationToGenTypes(final String augmentPackageName,
415                                               final AugmentationSchema augSchema) {
416         if (augmentPackageName == null) {
417             throw new IllegalArgumentException("Package Name cannot be NULL!");
418         }
419         if (augSchema == null) {
420             throw new IllegalArgumentException(
421                     "Augmentation Schema cannot be NULL!");
422         }
423         if (augSchema.getTargetPath() == null) {
424             throw new IllegalStateException(
425                     "Augmentation Schema does not contain Target Path (Target Path is NULL).");
426         }
427
428         final List<Type> genTypes = new ArrayList<>();
429
430         // EVERY augmented interface will extends Augmentation<T> interface
431         // and DataObject interface!!!
432         final SchemaPath targetPath = augSchema.getTargetPath();
433         final DataSchemaNode targetSchemaNode = findDataSchemaNode(schemaContext,
434                 targetPath);
435         if ((targetSchemaNode != null) &&
436                 (targetSchemaNode.getQName() != null) &&
437                 (targetSchemaNode.getQName().getLocalName() != null)) {
438             final Module targetModule = findParentModule(schemaContext,
439                     targetSchemaNode);
440
441             final String targetBasePackage = moduleNamespaceToPackageName(targetModule);
442             final String targetPackageName = packageNameForGeneratedType(targetBasePackage,
443                     targetSchemaNode.getPath());
444
445             final String targetSchemaNodeName = targetSchemaNode.getQName().getLocalName();
446             final Set<DataSchemaNode> augChildNodes = augSchema
447                     .getChildNodes();
448             final GeneratedTypeBuilder augTypeBuilder = addRawAugmentGenTypeDefinition(
449                     augmentPackageName, targetPackageName, targetSchemaNodeName, augSchema);
450             if (augTypeBuilder != null) {
451                 genTypes.add(augTypeBuilder.toInstance());
452             }
453             genTypes.addAll(augmentationBodyToGenTypes(augmentPackageName,
454                     augChildNodes));
455
456         }
457         return genTypes;
458     }
459
460     private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(
461             final String augmentPackageName, final String targetPackageName,
462             final String targetSchemaNodeName,
463             final AugmentationSchema augSchema) {
464         final String targetTypeName = parseToClassName(targetSchemaNodeName);
465         Map<String, GeneratedTypeBuilder> augmentBuilders = genTypeBuilders
466                 .get(augmentPackageName);
467         if (augmentBuilders == null) {
468             augmentBuilders = new HashMap<>();
469             genTypeBuilders.put(augmentPackageName, augmentBuilders);
470         }
471
472         final String augTypeName = augGenTypeName(augmentBuilders, targetTypeName);
473         final Type targetTypeRef = new ReferencedTypeImpl(targetPackageName, targetTypeName);
474         final Set<DataSchemaNode> augChildNodes = augSchema
475                 .getChildNodes();
476
477         final GeneratedTypeBuilder augTypeBuilder = new GeneratedTypeBuilderImpl(
478                 augmentPackageName, augTypeName);
479
480         augTypeBuilder.addImplementsType(Types.DATA_OBJECT);
481         augTypeBuilder.addImplementsType(Types
482                 .augmentationTypeFor(targetTypeRef));
483
484         augSchemaNodeToMethods(augmentPackageName, augTypeBuilder, augChildNodes);
485         augmentBuilders.put(augTypeName, augTypeBuilder);
486         return augTypeBuilder;
487     }
488
489     private List<Type> augmentationBodyToGenTypes(final String augBasePackageName,
490                                                   final Set<DataSchemaNode> augChildNodes) {
491         final List<Type> genTypes = new ArrayList<>();
492         final List<DataNodeIterator> augSchemaIts = new ArrayList<>();
493         for (final DataSchemaNode childNode : augChildNodes) {
494             if (childNode instanceof DataNodeContainer) {
495                 augSchemaIts.add(new DataNodeIterator(
496                         (DataNodeContainer) childNode));
497
498                 if (childNode instanceof ContainerSchemaNode) {
499                     genTypes.add(containerToGenType(augBasePackageName,
500                             (ContainerSchemaNode) childNode));
501                 } else if (childNode instanceof ListSchemaNode) {
502                     genTypes.addAll(listToGenType(augBasePackageName,
503                             (ListSchemaNode) childNode));
504                 }
505             }
506         }
507
508         for (final DataNodeIterator it : augSchemaIts) {
509             final List<ContainerSchemaNode> augContainers = it.allContainers();
510             final List<ListSchemaNode> augLists = it.allLists();
511
512             if ((augContainers != null) && !augContainers.isEmpty()) {
513                 for (final ContainerSchemaNode container : augContainers) {
514                     genTypes.add(containerToGenType(augBasePackageName, container));
515                 }
516             }
517             if ((augLists != null) && !augLists.isEmpty()) {
518                 for (final ListSchemaNode list : augLists) {
519                     genTypes.addAll(listToGenType(augBasePackageName, list));
520                 }
521             }
522         }
523         return genTypes;
524     }
525
526     private String augGenTypeName(
527             final Map<String, GeneratedTypeBuilder> builders,
528             final String genTypeName) {
529         String augTypeName = genTypeName;
530
531         int index = 1;
532         while ((builders != null) && builders.containsKey(genTypeName + index)) {
533             index++;
534         }
535         augTypeName += index;
536         return augTypeName;
537     }
538
539     private GeneratedType containerToGenType(final String basePackageName,
540                                              ContainerSchemaNode containerNode) {
541         if (containerNode == null) {
542             return null;
543         }
544
545         final String packageName = packageNameForGeneratedType(
546                 basePackageName, containerNode.getPath());
547         final Set<DataSchemaNode> schemaNodes = containerNode.getChildNodes();
548         final GeneratedTypeBuilder typeBuilder = addRawInterfaceDefinition(
549                 packageName, containerNode);
550
551         resolveDataSchemaNodes(basePackageName, typeBuilder, schemaNodes);
552         return typeBuilder.toInstance();
553     }
554
555     private GeneratedTypeBuilder resolveDataSchemaNodes(
556             final String basePackageName,
557             final GeneratedTypeBuilder typeBuilder,
558             final Set<DataSchemaNode> schemaNodes) {
559
560         if ((schemaNodes != null) && (typeBuilder != null)) {
561             for (final DataSchemaNode schemaNode : schemaNodes) {
562                 if (schemaNode.isAugmenting()) {
563                     continue;
564                 }
565                 addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, typeBuilder);
566             }
567         }
568         return typeBuilder;
569     }
570
571     private GeneratedTypeBuilder augSchemaNodeToMethods(
572             final String basePackageName,
573             final GeneratedTypeBuilder typeBuilder,
574             final Set<DataSchemaNode> schemaNodes) {
575
576         if ((schemaNodes != null) && (typeBuilder != null)) {
577             for (final DataSchemaNode schemaNode : schemaNodes) {
578                 if (schemaNode.isAugmenting()) {
579                     addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, typeBuilder);
580                 }
581             }
582         }
583         return typeBuilder;
584     }
585
586     private void addSchemaNodeToBuilderAsMethod(
587             final String basePackageName,
588             final DataSchemaNode schemaNode,
589             final GeneratedTypeBuilder typeBuilder) {
590         if (schemaNode != null && typeBuilder != null) {
591             if (schemaNode instanceof LeafSchemaNode) {
592                 resolveLeafSchemaNodeAsMethod(typeBuilder,
593                         (LeafSchemaNode) schemaNode);
594             } else if (schemaNode instanceof LeafListSchemaNode) {
595                 resolveLeafListSchemaNode(typeBuilder,
596                         (LeafListSchemaNode) schemaNode);
597             } else if (schemaNode instanceof ContainerSchemaNode) {
598                 resolveContainerSchemaNode(basePackageName, typeBuilder,
599                         (ContainerSchemaNode) schemaNode);
600             } else if (schemaNode instanceof ListSchemaNode) {
601                 resolveListSchemaNode(basePackageName, typeBuilder,
602                         (ListSchemaNode) schemaNode);
603             }
604         }
605     }
606
607     private boolean resolveLeafSchemaNodeAsMethod(
608             final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf) {
609         if ((leaf != null) && (typeBuilder != null)) {
610             final String leafName = leaf.getQName().getLocalName();
611             String leafDesc = leaf.getDescription();
612             if (leafDesc == null) {
613                 leafDesc = "";
614             }
615
616             if (leafName != null) {
617                 final TypeDefinition<?> typeDef = leaf.getType();
618
619                 Type returnType = null;
620                 if (!(typeDef instanceof EnumTypeDefinition)) {
621                     returnType = typeProvider
622                             .javaTypeForSchemaDefinitionType(typeDef);
623                 } else {
624                     final EnumTypeDefinition enumTypeDef = enumTypeDefFromExtendedType(typeDef);
625                     final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(
626                             enumTypeDef, leafName, typeBuilder);
627
628                     if (enumBuilder != null) {
629                         returnType = new ReferencedTypeImpl(
630                                 enumBuilder.getPackageName(),
631                                 enumBuilder.getName());
632                     }
633                     ((TypeProviderImpl)typeProvider).putReferencedType(leaf
634                             .getPath(), returnType);
635                 }
636                 if (returnType != null) {
637                     constructGetter(typeBuilder, leafName, leafDesc, returnType);
638                     if (!leaf.isConfiguration()) {
639                         constructSetter(typeBuilder, leafName, leafDesc, returnType);
640                     }
641                     return true;
642                 }
643             }
644         }
645         return false;
646     }
647
648     private boolean resolveLeafSchemaNodeAsProperty(
649             final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf,
650             boolean isReadOnly) {
651         if ((leaf != null) && (toBuilder != null)) {
652             final String leafName = leaf.getQName().getLocalName();
653             String leafDesc = leaf.getDescription();
654             if (leafDesc == null) {
655                 leafDesc = "";
656             }
657
658             if (leafName != null) {
659                 final TypeDefinition<?> typeDef = leaf.getType();
660
661                 // TODO: properly resolve enum types
662                 final Type returnType = typeProvider
663                         .javaTypeForSchemaDefinitionType(typeDef);
664
665                 if (returnType != null) {
666                     final GeneratedPropertyBuilder propBuilder = toBuilder
667                             .addProperty(parseToClassName(leafName));
668
669                     propBuilder.setReadOnly(isReadOnly);
670                     propBuilder.addReturnType(returnType);
671                     propBuilder.addComment(leafDesc);
672
673                     toBuilder.addEqualsIdentity(propBuilder);
674                     toBuilder.addHashIdentity(propBuilder);
675                     toBuilder.addToStringProperty(propBuilder);
676
677                     return true;
678                 }
679             }
680         }
681         return false;
682     }
683
684     private boolean resolveLeafListSchemaNode(
685             final GeneratedTypeBuilder typeBuilder,
686             final LeafListSchemaNode node) {
687         if ((node != null) && (typeBuilder != null)) {
688             final String nodeName = node.getQName().getLocalName();
689             String nodeDesc = node.getDescription();
690             if (nodeDesc == null) {
691                 nodeDesc = "";
692             }
693
694             if (nodeName != null) {
695                 final TypeDefinition<?> type = node.getType();
696                 final Type listType = Types.listTypeFor(typeProvider
697                         .javaTypeForSchemaDefinitionType(type));
698
699                 constructGetter(typeBuilder, nodeName, nodeDesc, listType);
700                 if (!node.isConfiguration()) {
701                     constructSetter(typeBuilder, nodeName, nodeDesc, listType);
702                 }
703                 return true;
704             }
705         }
706         return false;
707     }
708
709     private boolean resolveContainerSchemaNode(final String basePackageName,
710                                                final GeneratedTypeBuilder typeBuilder,
711                                                final ContainerSchemaNode containerNode) {
712         if ((containerNode != null) && (typeBuilder != null)) {
713             final String nodeName = containerNode.getQName().getLocalName();
714
715             if (nodeName != null) {
716                 final String packageName = packageNameForGeneratedType(
717                         basePackageName, containerNode.getPath());
718
719                 final GeneratedTypeBuilder rawGenType = addRawInterfaceDefinition(
720                         packageName, containerNode);
721                 constructGetter(typeBuilder, nodeName, "", rawGenType);
722
723                 return true;
724             }
725         }
726         return false;
727     }
728
729     private boolean resolveListSchemaNode(final String basePackageName,
730                                           final GeneratedTypeBuilder typeBuilder,
731                                           final ListSchemaNode schemaNode) {
732         if ((schemaNode != null) && (typeBuilder != null)) {
733             final String listName = schemaNode.getQName().getLocalName();
734
735             if (listName != null) {
736                 final String packageName = packageNameForGeneratedType(
737                         basePackageName, schemaNode.getPath());
738                 final GeneratedTypeBuilder rawGenType = addRawInterfaceDefinition(
739                         packageName, schemaNode);
740                 constructGetter(typeBuilder, listName, "",
741                         Types.listTypeFor(rawGenType));
742                 if (!schemaNode.isConfiguration()) {
743                     constructSetter(typeBuilder, listName, "",
744                             Types.listTypeFor(rawGenType));
745                 }
746                 return true;
747             }
748         }
749         return false;
750     }
751
752     private GeneratedTypeBuilder addRawInterfaceDefinition(
753             final String packageName, final DataSchemaNode schemaNode) {
754         if (schemaNode == null) {
755             return null;
756         }
757
758         final String schemaNodeName = schemaNode.getQName().getLocalName();
759
760         if ((packageName != null) && (schemaNodeName != null)) {
761             final String genTypeName = parseToClassName(schemaNodeName);
762             final GeneratedTypeBuilder newType = new GeneratedTypeBuilderImpl(
763                     packageName, genTypeName);
764
765             newType.addImplementsType(Types.DATA_OBJECT);
766             newType.addImplementsType(Types.augmentableTypeFor(newType));
767
768             if (!genTypeBuilders.containsKey(packageName)) {
769                 final Map<String, GeneratedTypeBuilder> builders = new HashMap<>();
770                 builders.put(genTypeName, newType);
771                 genTypeBuilders.put(packageName, builders);
772             } else {
773                 final Map<String, GeneratedTypeBuilder> builders = genTypeBuilders
774                         .get(packageName);
775                 if (!builders.containsKey(genTypeName)) {
776                     builders.put(genTypeName, newType);
777                 }
778             }
779             return newType;
780         }
781         return null;
782     }
783
784     private String getterMethodName(final String methodName) {
785         final StringBuilder method = new StringBuilder();
786         method.append("get");
787         method.append(parseToClassName(methodName));
788         return method.toString();
789     }
790
791     private String setterMethodName(final String methodName) {
792         final StringBuilder method = new StringBuilder();
793         method.append("set");
794         method.append(parseToClassName(methodName));
795         return method.toString();
796     }
797
798     private MethodSignatureBuilder constructGetter(
799             final GeneratedTypeBuilder interfaceBuilder,
800             final String schemaNodeName, final String comment,
801             final Type returnType) {
802         final MethodSignatureBuilder getMethod = interfaceBuilder
803                 .addMethod(getterMethodName(schemaNodeName));
804
805         getMethod.addComment(comment);
806         getMethod.addReturnType(returnType);
807
808         return getMethod;
809     }
810
811     private MethodSignatureBuilder constructSetter(
812             final GeneratedTypeBuilder interfaceBuilder,
813             final String schemaNodeName, final String comment,
814             final Type parameterType) {
815         final MethodSignatureBuilder setMethod = interfaceBuilder
816                 .addMethod(setterMethodName(schemaNodeName));
817
818         setMethod.addComment(comment);
819         setMethod.addParameter(parameterType,
820                 parseToValidParamName(schemaNodeName));
821         setMethod.addReturnType(Types.voidType());
822
823         return setMethod;
824     }
825
826
827     private List<Type> listToGenType(final String basePackageName,
828                                      final ListSchemaNode list) {
829         if (basePackageName == null) {
830             throw new IllegalArgumentException(
831                     "Package Name for Generated Type cannot be NULL!");
832         }
833         if (list == null) {
834             throw new IllegalArgumentException(
835                     "List Schema Node cannot be NULL!");
836         }
837
838         final String packageName = packageNameForGeneratedType(
839                 basePackageName, list.getPath());
840         final GeneratedTypeBuilder typeBuilder = resolveListTypeBuilder(
841                 packageName, list);
842         final List<String> listKeys = listKeys(list);
843         GeneratedTOBuilder genTOBuilder = resolveListKeyTOBuilder(packageName,
844                 list, listKeys);
845
846         final Set<DataSchemaNode> schemaNodes = list.getChildNodes();
847
848         for (final DataSchemaNode schemaNode : schemaNodes) {
849             if (schemaNode.isAugmenting()) {
850                 continue;
851             }
852             addSchemaNodeToListBuilders(basePackageName, schemaNode, typeBuilder,
853                     genTOBuilder, listKeys);
854         }
855         return typeBuildersToGenTypes(typeBuilder, genTOBuilder);
856     }
857
858     private void addSchemaNodeToListBuilders(final String basePackageName,
859                                              final DataSchemaNode schemaNode,
860                                              final GeneratedTypeBuilder typeBuilder,
861                                              final GeneratedTOBuilder genTOBuilder,
862                                              final List<String> listKeys) {
863         if (schemaNode == null) {
864             throw new IllegalArgumentException(
865                     "Data Schema Node cannot be NULL!");
866         }
867
868         if (typeBuilder == null) {
869             throw new IllegalArgumentException(
870                     "Generated Type Builder cannot be NULL!");
871         }
872
873         if (schemaNode instanceof LeafSchemaNode) {
874             final LeafSchemaNode leaf = (LeafSchemaNode) schemaNode;
875             if (!isPartOfListKey(leaf, listKeys)) {
876                 resolveLeafSchemaNodeAsMethod(typeBuilder, leaf);
877             } else {
878                 resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
879             }
880         } else if (schemaNode instanceof LeafListSchemaNode) {
881             resolveLeafListSchemaNode(typeBuilder,
882                     (LeafListSchemaNode) schemaNode);
883         } else if (schemaNode instanceof ContainerSchemaNode) {
884             resolveContainerSchemaNode(basePackageName, typeBuilder,
885                     (ContainerSchemaNode) schemaNode);
886         } else if (schemaNode instanceof ListSchemaNode) {
887             resolveListSchemaNode(basePackageName, typeBuilder, (ListSchemaNode) schemaNode);
888         }
889     }
890
891     private List<Type> typeBuildersToGenTypes(
892             final GeneratedTypeBuilder typeBuilder,
893             GeneratedTOBuilder genTOBuilder) {
894         final List<Type> genTypes = new ArrayList<>();
895         if (typeBuilder == null) {
896             throw new IllegalArgumentException(
897                     "Generated Type Builder cannot be NULL!");
898         }
899
900         if (genTOBuilder != null) {
901             final GeneratedTransferObject genTO = genTOBuilder.toInstance();
902             constructGetter(typeBuilder, genTO.getName(),
903                     "Returns Primary Key of Yang List Type", genTO);
904             genTypes.add(genTO);
905         }
906         genTypes.add(typeBuilder.toInstance());
907         return genTypes;
908     }
909
910     /**
911      * @param list
912      * @return
913      */
914     private GeneratedTOBuilder resolveListKey(final String packageName,
915                                               final ListSchemaNode list) {
916         final String listName = list.getQName().getLocalName() + "Key";
917         return schemaNodeToTransferObjectBuilder(packageName, list, listName);
918     }
919
920     private boolean isPartOfListKey(final LeafSchemaNode leaf,
921                                     final List<String> keys) {
922         if ((leaf != null) && (keys != null) && (leaf.getQName() != null)) {
923             final String leafName = leaf.getQName().getLocalName();
924             if (keys.contains(leafName)) {
925                 return true;
926             }
927         }
928         return false;
929     }
930
931     private List<String> listKeys(final ListSchemaNode list) {
932         final List<String> listKeys = new ArrayList<>();
933
934         if (list.getKeyDefinition() != null) {
935             final List<QName> keyDefinitions = list.getKeyDefinition();
936
937             for (final QName keyDefinition : keyDefinitions) {
938                 listKeys.add(keyDefinition.getLocalName());
939             }
940         }
941         return listKeys;
942     }
943
944     private GeneratedTypeBuilder resolveListTypeBuilder(
945             final String packageName, final ListSchemaNode list) {
946         if (packageName == null) {
947             throw new IllegalArgumentException(
948                     "Package Name for Generated Type cannot be NULL!");
949         }
950         if (list == null) {
951             throw new IllegalArgumentException(
952                     "List Schema Node cannot be NULL!");
953         }
954
955         final String schemaNodeName = list.getQName().getLocalName();
956         final String genTypeName = parseToClassName(schemaNodeName);
957
958         GeneratedTypeBuilder typeBuilder = null;
959         final Map<String, GeneratedTypeBuilder> builders = genTypeBuilders.get(packageName);
960         if (builders != null) {
961             typeBuilder = builders.get(genTypeName);
962         }
963         if (typeBuilder == null) {
964             typeBuilder = addRawInterfaceDefinition(packageName, list);
965         }
966         return typeBuilder;
967     }
968
969     private GeneratedTOBuilder resolveListKeyTOBuilder(
970             final String packageName, final ListSchemaNode list,
971             final List<String> listKeys) {
972         GeneratedTOBuilder genTOBuilder = null;
973         if (listKeys.size() > 0) {
974             genTOBuilder = resolveListKey(packageName, list);
975         }
976         return genTOBuilder;
977     }
978 }