Fixed bugs in naming conventions of packages.
[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 java.net.URI;
11 import java.util.ArrayList;
12 import java.util.Calendar;
13 import java.util.GregorianCalendar;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.opendaylight.controller.binding.generator.util.CodeGeneratorHelper;
20 import org.opendaylight.controller.binding.generator.util.Types;
21 import org.opendaylight.controller.sal.binding.generator.api.BindingGenerator;
22 import org.opendaylight.controller.sal.binding.generator.spi.TypeProvider;
23 import org.opendaylight.controller.sal.binding.model.api.GeneratedTransferObject;
24 import org.opendaylight.controller.sal.binding.model.api.GeneratedType;
25 import org.opendaylight.controller.sal.binding.model.api.Type;
26 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
27 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTOBuilder;
28 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
29 import org.opendaylight.controller.sal.binding.model.api.type.builder.MethodSignatureBuilder;
30 import org.opendaylight.controller.sal.binding.yang.types.TypeProviderImpl;
31 import org.opendaylight.controller.yang.common.QName;
32 import org.opendaylight.controller.yang.model.api.ContainerSchemaNode;
33 import org.opendaylight.controller.yang.model.api.DataNodeContainer;
34 import org.opendaylight.controller.yang.model.api.DataSchemaNode;
35 import org.opendaylight.controller.yang.model.api.LeafListSchemaNode;
36 import org.opendaylight.controller.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.controller.yang.model.api.ListSchemaNode;
38 import org.opendaylight.controller.yang.model.api.Module;
39 import org.opendaylight.controller.yang.model.api.SchemaContext;
40 import org.opendaylight.controller.yang.model.api.SchemaPath;
41 import org.opendaylight.controller.yang.model.api.TypeDefinition;
42
43 public class BindingGeneratorImpl implements BindingGenerator {
44
45     private static Calendar calendar = new GregorianCalendar();
46     private final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders;
47     private final List<ContainerSchemaNode> schemaContainers;
48     private final List<ListSchemaNode> schemaLists;
49
50     private final TypeProvider typeProvider;
51
52     private String basePackageName;
53
54     public BindingGeneratorImpl() {
55         super();
56         genTypeBuilders = new HashMap<String, Map<String, GeneratedTypeBuilder>>();
57         schemaContainers = new ArrayList<ContainerSchemaNode>();
58         schemaLists = new ArrayList<ListSchemaNode>();
59
60         // TODO: reimplement in better way
61         typeProvider = new TypeProviderImpl();
62     }
63
64     @Override
65     public List<Type> generateTypes(final SchemaContext context) {
66         final List<Type> genTypes = new ArrayList<Type>();
67         
68         if (context != null) {
69             final Set<Module> modules = context.getModules();
70             
71             if (modules != null) {
72                 for (final Module module : modules) {
73                     basePackageName = resolveBasePackageName(module.getNamespace(),
74                             module.getYangVersion());
75
76                     traverseModule(module);
77                     if (schemaContainers.size() > 0) {
78                         for (final ContainerSchemaNode container : schemaContainers) {
79                             genTypes.add(containerToGenType(container));
80                         }
81                     }
82
83                     if (schemaLists.size() > 0) {
84                         for (final ListSchemaNode list : schemaLists) {
85                             genTypes.addAll(listToGenType(list));
86                         }
87                     }
88                 }
89             }
90         }
91
92         return genTypes;
93     }
94     
95     private String resolveGeneratedTypePackageName(final SchemaPath schemaPath) {
96         final StringBuilder builder = new StringBuilder();
97         builder.append(basePackageName);
98         if ((schemaPath != null) && (schemaPath.getPath() != null)) {
99             final List<QName> pathToNode = schemaPath.getPath();
100             for (int i = 0; i < pathToNode.size(); ++i) {
101                 builder.append(".");
102                 String nodeLocalName = pathToNode.get(i).getLocalName();
103
104                 // TODO: create method
105                 nodeLocalName = nodeLocalName.replace(":", ".");
106                 nodeLocalName = nodeLocalName.replace("-", ".");
107                 builder.append(nodeLocalName);
108             }
109             return builder.toString();
110         }
111         return null;
112     }
113
114     private GeneratedType containerToGenType(ContainerSchemaNode container) {
115         if (container == null) {
116             return null;
117         }
118         final Set<DataSchemaNode> schemaNodes = container.getChildNodes();
119         final GeneratedTypeBuilder typeBuilder = addRawInterfaceDefinition(container);
120
121         for (final DataSchemaNode node : schemaNodes) {
122             if (node instanceof LeafSchemaNode) {
123                 resolveLeafSchemaNodeAsMethod(typeBuilder,
124                         (LeafSchemaNode) node);
125             } else if (node instanceof LeafListSchemaNode) {
126                 resolveLeafListSchemaNode(typeBuilder,
127                         (LeafListSchemaNode) node);
128
129             } else if (node instanceof ContainerSchemaNode) {
130                 resolveContainerSchemaNode(typeBuilder,
131                         (ContainerSchemaNode) node);
132             } else if (node instanceof ListSchemaNode) {
133                 resolveListSchemaNode(typeBuilder, (ListSchemaNode) node);
134             }
135         }
136         return typeBuilder.toInstance();
137     }
138
139     private boolean resolveLeafSchemaNodeAsMethod(
140             final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf) {
141         if ((leaf != null) && (typeBuilder != null)) {
142             final String leafName = leaf.getQName().getLocalName();
143             String leafDesc = leaf.getDescription();
144             if (leafDesc == null) {
145                 leafDesc = "";
146             }
147
148             if (leafName != null) {
149                 final TypeDefinition<?> typeDef = leaf.getType();
150                 final Type javaType = typeProvider
151                         .javaTypeForSchemaDefinitionType(typeDef);
152
153                 constructGetter(typeBuilder, leafName, leafDesc, javaType);
154                 if (!leaf.isConfiguration()) {
155                     constructSetter(typeBuilder, leafName, leafDesc, javaType);
156                 }
157                 return true;
158             }
159         }
160         return false;
161     }
162
163     private boolean resolveLeafSchemaNodeAsProperty(
164             final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf,
165             boolean isReadOnly) {
166         if ((leaf != null) && (toBuilder != null)) {
167             final String leafName = leaf.getQName().getLocalName();
168             String leafDesc = leaf.getDescription();
169             if (leafDesc == null) {
170                 leafDesc = "";
171             }
172
173             if (leafName != null) {
174                 final TypeDefinition<?> typeDef = leaf.getType();
175                 final Type javaType = typeProvider
176                         .javaTypeForSchemaDefinitionType(typeDef);
177
178                 final GeneratedPropertyBuilder propBuilder = toBuilder
179                         .addProperty(CodeGeneratorHelper
180                                 .parseToClassName(leafName));
181
182                 propBuilder.setReadOnly(isReadOnly);
183                 propBuilder.addReturnType(javaType);
184                 propBuilder.addComment(leafDesc);
185
186                 toBuilder.addEqualsIdentity(propBuilder);
187                 toBuilder.addHashIdentity(propBuilder);
188                 toBuilder.addToStringProperty(propBuilder);
189
190                 return true;
191             }
192         }
193         return false;
194     }
195
196     private boolean resolveLeafListSchemaNode(
197             final GeneratedTypeBuilder typeBuilder,
198             final LeafListSchemaNode node) {
199         if ((node != null) && (typeBuilder != null)) {
200             final String nodeName = node.getQName().getLocalName();
201             String nodeDesc = node.getDescription();
202             if (nodeDesc == null) {
203                 nodeDesc = "";
204             }
205
206             if (nodeName != null) {
207                 final TypeDefinition<?> type = node.getType();
208                 final Type listType = Types.listTypeFor(typeProvider
209                         .javaTypeForSchemaDefinitionType(type));
210
211                 constructGetter(typeBuilder, nodeName, nodeDesc, listType);
212                 if (!node.isConfiguration()) {
213                     constructSetter(typeBuilder, nodeName, nodeDesc, listType);
214                 }
215                 return true;
216             }
217         }
218         return false;
219     }
220
221     private boolean resolveContainerSchemaNode(
222             final GeneratedTypeBuilder typeBuilder,
223             final ContainerSchemaNode node) {
224         if ((node != null) && (typeBuilder != null)) {
225             final String nodeName = node.getQName().getLocalName();
226
227             if (nodeName != null) {
228                 final GeneratedTypeBuilder rawGenType = addRawInterfaceDefinition(node);
229                 constructGetter(typeBuilder, nodeName, "", rawGenType);
230
231                 return true;
232             }
233         }
234         return false;
235     }
236
237     private boolean resolveListSchemaNode(
238             final GeneratedTypeBuilder typeBuilder, final ListSchemaNode node) {
239         if ((node != null) && (typeBuilder != null)) {
240             final String nodeName = node.getQName().getLocalName();
241
242             if (nodeName != null) {
243                 final GeneratedTypeBuilder rawGenType = addRawInterfaceDefinition(node);
244                 constructGetter(typeBuilder, nodeName, "",
245                         Types.listTypeFor(rawGenType));
246                 if (!node.isConfiguration()) {
247                     constructSetter(typeBuilder, nodeName, "",
248                             Types.listTypeFor(rawGenType));
249                 }
250                 return true;
251             }
252         }
253         return false;
254     }
255
256     private GeneratedTypeBuilder addRawInterfaceDefinition(
257             final DataSchemaNode schemaNode) {
258         if (schemaNode == null) {
259             return null;
260         }
261
262         final String packageName = resolveGeneratedTypePackageName(schemaNode
263                 .getPath());
264         final String schemaNodeName = schemaNode.getQName().getLocalName();
265
266         if ((packageName != null) && (schemaNode != null)
267                 && (schemaNodeName != null)) {
268             final String genTypeName = CodeGeneratorHelper
269                     .parseToClassName(schemaNodeName);
270             final GeneratedTypeBuilder newType = new GeneratedTypeBuilderImpl(
271                     packageName, genTypeName);
272
273             if (!genTypeBuilders.containsKey(packageName)) {
274                 final Map<String, GeneratedTypeBuilder> builders = new HashMap<String, GeneratedTypeBuilder>();
275                 builders.put(genTypeName, newType);
276                 genTypeBuilders.put(packageName, builders);
277             } else {
278                 final Map<String, GeneratedTypeBuilder> builders = genTypeBuilders
279                         .get(packageName);
280                 if (!builders.containsKey(genTypeName)) {
281                     builders.put(genTypeName, newType);
282                 }
283             }
284
285             return newType;
286         }
287         return null;
288     }
289
290     private String getterMethodName(final String methodName) {
291         final StringBuilder method = new StringBuilder();
292         method.append("get");
293         method.append(CodeGeneratorHelper.parseToClassName(methodName));
294         return method.toString();
295     }
296
297     private String setterMethodName(final String methodName) {
298         final StringBuilder method = new StringBuilder();
299         method.append("set");
300         method.append(CodeGeneratorHelper.parseToClassName(methodName));
301         return method.toString();
302     }
303
304     private MethodSignatureBuilder constructGetter(
305             final GeneratedTypeBuilder interfaceBuilder,
306             final String schemaNodeName, final String comment,
307             final Type returnType) {
308         final MethodSignatureBuilder getMethod = interfaceBuilder
309                 .addMethod(getterMethodName(schemaNodeName));
310
311         getMethod.addComment(comment);
312         getMethod.addReturnType(returnType);
313
314         return getMethod;
315     }
316
317     private MethodSignatureBuilder constructSetter(
318             final GeneratedTypeBuilder interfaceBuilder,
319             final String schemaNodeName, final String comment,
320             final Type parameterType) {
321         final MethodSignatureBuilder setMethod = interfaceBuilder
322                 .addMethod(setterMethodName(schemaNodeName));
323
324         setMethod.addComment(comment);
325         setMethod.addParameter(parameterType,
326                 CodeGeneratorHelper.parseToParamName(schemaNodeName));
327         setMethod.addReturnType(Types.voidType());
328
329         return setMethod;
330     }
331
332     private String resolveBasePackageName(final URI moduleNamespace,
333             final String yangVersion) {
334         final StringBuilder packageNameBuilder = new StringBuilder();
335
336         packageNameBuilder.append("com.cisco.yang.gen.v");
337         packageNameBuilder.append(yangVersion);
338         packageNameBuilder.append(".rev");
339         packageNameBuilder.append(calendar.get(Calendar.YEAR));
340         packageNameBuilder.append((calendar.get(Calendar.MONTH) + 1));
341         packageNameBuilder.append(calendar.get(Calendar.DAY_OF_MONTH));
342         packageNameBuilder.append(".");
343
344         String namespace = moduleNamespace.toString();
345         namespace = namespace.replace(":", ".");
346         namespace = namespace.replace("-", ".");
347
348         packageNameBuilder.append(namespace);
349
350         return packageNameBuilder.toString();
351     }
352
353     private List<Type> listToGenType(final ListSchemaNode list) {
354         if (list == null) {
355             return null;
356         }
357         final GeneratedTypeBuilder typeBuilder = resolveListTypeBuilder(list);
358         final List<String> listKeys = listKeys(list);
359         GeneratedTOBuilder genTOBuilder = null;
360         if (listKeys.size() > 0) {
361             genTOBuilder = resolveListKey(list);
362         }
363
364         final Set<DataSchemaNode> schemaNodes = list.getChildNodes();
365         for (final DataSchemaNode node : schemaNodes) {
366
367             if (node instanceof LeafSchemaNode) {
368                 final LeafSchemaNode leaf = (LeafSchemaNode) node;
369                 if (!isPartOfListKey(leaf, listKeys)) {
370                     resolveLeafSchemaNodeAsMethod(typeBuilder, leaf);
371                 } else {
372                     resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
373                 }
374             } else if (node instanceof LeafListSchemaNode) {
375                 resolveLeafListSchemaNode(typeBuilder,
376                         (LeafListSchemaNode) node);
377             } else if (node instanceof ContainerSchemaNode) {
378                 resolveContainerSchemaNode(typeBuilder,
379                         (ContainerSchemaNode) node);
380             } else if (node instanceof ListSchemaNode) {
381                 resolveListSchemaNode(typeBuilder, (ListSchemaNode) node);
382             }
383         }
384
385         final List<Type> genTypes = new ArrayList<Type>();
386         if (genTOBuilder != null) {
387             final GeneratedTransferObject genTO = genTOBuilder.toInstance();
388             constructGetter(typeBuilder, genTO.getName(), "Returns Primary Key of Yang List Type", genTO);
389             genTypes.add(genTO);
390         }
391         genTypes.add(typeBuilder.toInstance());
392         return genTypes;
393     }
394
395     /**
396      * @param list
397      * @return
398      */
399     private GeneratedTOBuilder resolveListKey(final ListSchemaNode list) {
400         final String packageName = resolveGeneratedTypePackageName(list
401                 .getPath());
402         final String listName = list.getQName().getLocalName() + "Key";
403
404         if ((packageName != null) && (list != null) && (listName != null)) {
405             final String genTOName = CodeGeneratorHelper
406                     .parseToClassName(listName);
407             final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(
408                     packageName, genTOName);
409
410             return newType;
411         }
412         return null;
413     }
414
415     private boolean isPartOfListKey(final LeafSchemaNode leaf,
416             final List<String> keys) {
417         if ((leaf != null) && (keys != null) && (leaf.getQName() != null)) {
418             final String leafName = leaf.getQName().getLocalName();
419             if (keys.contains(leafName)) {
420                 return true;
421             }
422         }
423         return false;
424     }
425
426     private List<String> listKeys(final ListSchemaNode list) {
427         final List<String> listKeys = new ArrayList<String>();
428
429         if (list.getKeyDefinition() != null) {
430             final List<QName> keyDefinitions = list.getKeyDefinition();
431             
432             for (final QName keyDefinition : keyDefinitions) {
433                 listKeys.add(keyDefinition.getLocalName());
434             }
435         }
436         return listKeys;
437     }
438
439     private GeneratedTypeBuilder resolveListTypeBuilder(
440             final ListSchemaNode list) {
441         final String packageName = resolveGeneratedTypePackageName(list
442                 .getPath());
443         final String schemaNodeName = list.getQName().getLocalName();
444         final String genTypeName = CodeGeneratorHelper
445                 .parseToClassName(schemaNodeName);
446
447         GeneratedTypeBuilder typeBuilder = null;
448         if (genTypeBuilders.containsKey(packageName)) {
449             final Map<String, GeneratedTypeBuilder> builders = new HashMap<String, GeneratedTypeBuilder>();
450             typeBuilder = builders.get(genTypeName);
451
452             if (null == typeBuilder) {
453                 typeBuilder = addRawInterfaceDefinition(list);
454             }
455         }
456         return typeBuilder;
457     }
458
459     private void traverseModule(final Module module) {
460         final Set<DataSchemaNode> schemaNodes = module.getChildNodes();
461
462         for (DataSchemaNode node : schemaNodes) {
463             if (node instanceof ContainerSchemaNode) {
464                 schemaContainers.add((ContainerSchemaNode) node);
465                 traverse((ContainerSchemaNode) node);
466             }
467         }
468     }
469
470     private void traverse(final DataNodeContainer dataNode) {
471         if (!containChildDataNodeContainer(dataNode)) {
472             return;
473         }
474
475         final Set<DataSchemaNode> childs = dataNode.getChildNodes();
476         if (childs != null) {
477             for (DataSchemaNode childNode : childs) {
478                 if (childNode instanceof ContainerSchemaNode) {
479                     final ContainerSchemaNode container = (ContainerSchemaNode) childNode;
480                     schemaContainers.add(container);
481                     traverse(container);
482                 }
483
484                 if (childNode instanceof ListSchemaNode) {
485                     final ListSchemaNode list = (ListSchemaNode) childNode;
486                     schemaLists.add(list);
487                     traverse(list);
488                 }
489             }
490         }
491     }
492
493     /**
494      * Returns <code>true</code> if and only if the child node contain at least
495      * one child container schema node or child list schema node, otherwise will
496      * always returns <code>false</code>
497      * 
498      * @param container
499      * @return <code>true</code> if and only if the child node contain at least
500      *         one child container schema node or child list schema node,
501      *         otherwise will always returns <code>false</code>
502      */
503     private boolean containChildDataNodeContainer(
504             final DataNodeContainer container) {
505         if (container != null) {
506             final Set<DataSchemaNode> childs = container.getChildNodes();
507             if ((childs != null) && (childs.size() > 0)) {
508                 for (final DataSchemaNode childNode : childs) {
509                     if (childNode instanceof DataNodeContainer) {
510                         return true;
511                     }
512                 }
513             }
514         }
515         return false;
516     }
517 }