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