343d9ef0e597f53e7de27065d17b05b1cf9f423b
[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 boolean isCompositeKey(final String keyDefinition) {
333         if (keyDefinition.contains(" ")) {
334             return true;
335         }
336         return false;
337     }
338
339     private String resolveBasePackageName(final URI moduleNamespace,
340             final String yangVersion) {
341         final StringBuilder packageNameBuilder = new StringBuilder();
342
343         packageNameBuilder.append("com.cisco.yang.gen.v");
344         packageNameBuilder.append(yangVersion);
345         packageNameBuilder.append(".rev");
346         packageNameBuilder.append(calendar.get(Calendar.YEAR));
347         packageNameBuilder.append((calendar.get(Calendar.MONTH) + 1));
348         packageNameBuilder.append(calendar.get(Calendar.DAY_OF_MONTH));
349         packageNameBuilder.append(".");
350
351         String namespace = moduleNamespace.toString();
352         namespace = namespace.replace(":", ".");
353         namespace = namespace.replace("-", ".");
354
355         packageNameBuilder.append(namespace);
356
357         return packageNameBuilder.toString();
358     }
359
360     private List<Type> listToGenType(final ListSchemaNode list) {
361         if (list == null) {
362             return null;
363         }
364         final GeneratedTypeBuilder typeBuilder = resolveListTypeBuilder(list);
365         final List<String> listKeys = listKeys(list);
366         GeneratedTOBuilder genTOBuilder = null;
367         if (listKeys.size() > 0) {
368             genTOBuilder = resolveListKey(list);
369         }
370
371         final Set<DataSchemaNode> schemaNodes = list.getChildNodes();
372         for (final DataSchemaNode node : schemaNodes) {
373
374             if (node instanceof LeafSchemaNode) {
375                 final LeafSchemaNode leaf = (LeafSchemaNode) node;
376                 if (!isPartOfListKey(leaf, listKeys)) {
377                     resolveLeafSchemaNodeAsMethod(typeBuilder, leaf);
378                 } else {
379                     resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
380                 }
381             } else if (node instanceof LeafListSchemaNode) {
382                 resolveLeafListSchemaNode(typeBuilder,
383                         (LeafListSchemaNode) node);
384             } else if (node instanceof ContainerSchemaNode) {
385                 resolveContainerSchemaNode(typeBuilder,
386                         (ContainerSchemaNode) node);
387             } else if (node instanceof ListSchemaNode) {
388                 resolveListSchemaNode(typeBuilder, (ListSchemaNode) node);
389             }
390         }
391
392         final List<Type> genTypes = new ArrayList<Type>();
393         if (genTOBuilder != null) {
394             final GeneratedTransferObject genTO = genTOBuilder.toInstance();
395             constructGetter(typeBuilder, genTO.getName(), "Returns Primary Key of Yang List Type", genTO);
396             genTypes.add(genTO);
397         }
398         genTypes.add(typeBuilder.toInstance());
399         return genTypes;
400     }
401
402     /**
403      * @param list
404      * @return
405      */
406     private GeneratedTOBuilder resolveListKey(final ListSchemaNode list) {
407         final String packageName = resolveGeneratedTypePackageName(list
408                 .getPath());
409         final String listName = list.getQName().getLocalName() + "Key";
410
411         if ((packageName != null) && (list != null) && (listName != null)) {
412             final String genTOName = CodeGeneratorHelper
413                     .parseToClassName(listName);
414             final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(
415                     packageName, genTOName);
416
417             return newType;
418         }
419         return null;
420     }
421
422     private boolean isPartOfListKey(final LeafSchemaNode leaf,
423             final List<String> keys) {
424         if ((leaf != null) && (keys != null) && (leaf.getQName() != null)) {
425             final String leafName = leaf.getQName().getLocalName();
426             if (keys.contains(leafName)) {
427                 return true;
428             }
429         }
430         return false;
431     }
432
433     private List<String> listKeys(final ListSchemaNode list) {
434         final List<String> listKeys = new ArrayList<String>();
435
436         if (list.getKeyDefinition() != null) {
437             final List<QName> keyDefinitions = list.getKeyDefinition();
438             
439             for (final QName keyDefinition : keyDefinitions) {
440                 listKeys.add(keyDefinition.getLocalName());
441             }
442         }
443         return listKeys;
444     }
445
446     private GeneratedTypeBuilder resolveListTypeBuilder(
447             final ListSchemaNode list) {
448         final String packageName = resolveGeneratedTypePackageName(list
449                 .getPath());
450         final String schemaNodeName = list.getQName().getLocalName();
451         final String genTypeName = CodeGeneratorHelper
452                 .parseToClassName(schemaNodeName);
453
454         GeneratedTypeBuilder typeBuilder = null;
455         if (genTypeBuilders.containsKey(packageName)) {
456             final Map<String, GeneratedTypeBuilder> builders = new HashMap<String, GeneratedTypeBuilder>();
457             typeBuilder = builders.get(genTypeName);
458
459             if (null == typeBuilder) {
460                 typeBuilder = addRawInterfaceDefinition(list);
461             }
462         }
463         return typeBuilder;
464     }
465
466     private void traverseModule(final Module module) {
467         final Set<DataSchemaNode> schemaNodes = module.getChildNodes();
468
469         for (DataSchemaNode node : schemaNodes) {
470             if (node instanceof ContainerSchemaNode) {
471                 schemaContainers.add((ContainerSchemaNode) node);
472                 traverse((ContainerSchemaNode) node);
473             }
474         }
475     }
476
477     private void traverse(final DataNodeContainer dataNode) {
478         if (!containChildDataNodeContainer(dataNode)) {
479             return;
480         }
481
482         final Set<DataSchemaNode> childs = dataNode.getChildNodes();
483         if (childs != null) {
484             for (DataSchemaNode childNode : childs) {
485                 if (childNode instanceof ContainerSchemaNode) {
486                     final ContainerSchemaNode container = (ContainerSchemaNode) childNode;
487                     schemaContainers.add(container);
488                     traverse(container);
489                 }
490
491                 if (childNode instanceof ListSchemaNode) {
492                     final ListSchemaNode list = (ListSchemaNode) childNode;
493                     schemaLists.add(list);
494                     traverse(list);
495                 }
496             }
497         }
498     }
499
500     /**
501      * Returns <code>true</code> if and only if the child node contain at least
502      * one child container schema node or child list schema node, otherwise will
503      * always returns <code>false</code>
504      * 
505      * @param container
506      * @return <code>true</code> if and only if the child node contain at least
507      *         one child container schema node or child list schema node,
508      *         otherwise will always returns <code>false</code>
509      */
510     private boolean containChildDataNodeContainer(
511             final DataNodeContainer container) {
512         if (container != null) {
513             final Set<DataSchemaNode> childs = container.getChildNodes();
514             if ((childs != null) && (childs.size() > 0)) {
515                 for (final DataSchemaNode childNode : childs) {
516                     if (childNode instanceof DataNodeContainer) {
517                         return true;
518                     }
519                 }
520             }
521         }
522         return false;
523     }
524 }