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