2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.binding.generator.impl;
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;
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.EnumBuilder;
29 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
30 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTOBuilder;
31 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
32 import org.opendaylight.controller.sal.binding.model.api.type.builder.MethodSignatureBuilder;
33 import org.opendaylight.controller.sal.binding.yang.types.TypeProviderImpl;
34 import org.opendaylight.controller.yang.common.QName;
35 import org.opendaylight.controller.yang.model.api.ContainerSchemaNode;
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.NotificationDefinition;
42 import org.opendaylight.controller.yang.model.api.RpcDefinition;
43 import org.opendaylight.controller.yang.model.api.SchemaContext;
44 import org.opendaylight.controller.yang.model.api.SchemaPath;
45 import org.opendaylight.controller.yang.model.api.TypeDefinition;
46 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition;
47 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition.EnumPair;
48 import org.opendaylight.controller.yang.model.util.DataNodeIterator;
49 import org.opendaylight.controller.yang.model.util.ExtendedType;
51 public class BindingGeneratorImpl implements BindingGenerator {
53 private static final String[] SET_VALUES = new String[] { "abstract",
54 "assert", "boolean", "break", "byte", "case", "catch", "char",
55 "class", "const", "continue", "default", "double", "do", "else",
56 "enum", "extends", "false", "final", "finally", "float", "for",
57 "goto", "if", "implements", "import", "instanceof", "int",
58 "interface", "long", "native", "new", "null", "package", "private",
59 "protected", "public", "return", "short", "static", "strictfp",
60 "super", "switch", "synchronized", "this", "throw", "throws",
61 "transient", "true", "try", "void", "volatile", "while" };
63 public static final Set<String> JAVA_RESERVED_WORDS = new HashSet<String>(
64 Arrays.asList(SET_VALUES));
66 private static Calendar calendar = new GregorianCalendar();
67 private Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders;
68 private TypeProvider typeProvider;
69 private String basePackageName;
71 public BindingGeneratorImpl() {
75 private static String validatePackage(final String packageName) {
76 if (packageName != null) {
77 final String[] packNameParts = packageName.split("\\.");
78 if (packNameParts != null) {
79 final StringBuilder builder = new StringBuilder();
80 for (int i = 0; i < packNameParts.length; ++i) {
81 if (JAVA_RESERVED_WORDS.contains(packNameParts[i])) {
82 packNameParts[i] = "_" + packNameParts[i];
87 builder.append(packNameParts[i]);
89 return builder.toString();
96 public List<Type> generateTypes(final SchemaContext context) {
97 final List<Type> genTypes = new ArrayList<Type>();
99 typeProvider = new TypeProviderImpl(context);
100 if (context != null) {
101 final Set<Module> modules = context.getModules();
103 if (modules != null) {
104 for (final Module module : modules) {
105 DataNodeIterator moduleIterator = new DataNodeIterator(module);
107 genTypeBuilders = new HashMap<String, Map<String, GeneratedTypeBuilder>>();
108 final List<ContainerSchemaNode> schemaContainers = moduleIterator.allContainers();
109 final List<ListSchemaNode> schemaLists = moduleIterator.allLists();
111 basePackageName = resolveBasePackageName(
112 module.getNamespace(), module.getYangVersion());
114 if (schemaContainers.size() > 0) {
115 for (final ContainerSchemaNode container : schemaContainers) {
116 genTypes.add(containerToGenType(container));
119 if (schemaLists.size() > 0) {
120 for (final ListSchemaNode list : schemaLists) {
121 genTypes.addAll(listToGenType(list));
125 final GeneratedType genDataType = moduleToDataType(module);
126 final GeneratedType genRpcType = rpcMethodsToGenType(module);
127 final GeneratedType genNotifyType = notifycationsToGenType(module);
129 if (genDataType != null) {
130 genTypes.add(genDataType);
132 if (genRpcType != null) {
133 genTypes.add(genRpcType);
135 if (genNotifyType != null) {
136 genTypes.add(genNotifyType);
144 private GeneratedType moduleToDataType(final Module module) {
145 if (module != null) {
146 final Set<TypeDefinition<?>> typeDefinitions = module
147 .getTypeDefinitions();
148 final GeneratedTypeBuilder moduleDataTypeBuilder = moduleTypeBuilder(
151 if (moduleDataTypeBuilder != null) {
152 if (typeDefinitions != null) {
153 for (final TypeDefinition<?> typedef : typeDefinitions) {
154 if (isDerivedFromEnumerationType(typedef)) {
155 final EnumTypeDefinition enumBaseType = enumTypeDefFromExtendedType(typedef);
156 resolveEnumFromTypeDefinition(enumBaseType, typedef
157 .getQName().getLocalName(),
158 moduleDataTypeBuilder);
163 final Set<DataSchemaNode> dataNodes = module.getChildNodes();
164 resolveTypesFromDataSchemaNode(moduleDataTypeBuilder, dataNodes);
165 return moduleDataTypeBuilder.toInstance();
171 private boolean isDerivedFromEnumerationType(
172 final TypeDefinition<?> typeDefinition) {
173 if (typeDefinition != null) {
174 if (typeDefinition.getBaseType() instanceof EnumTypeDefinition) {
176 } else if (typeDefinition.getBaseType() instanceof ExtendedType) {
177 return isDerivedFromEnumerationType(typeDefinition
184 private EnumTypeDefinition enumTypeDefFromExtendedType(
185 final TypeDefinition<?> typeDefinition) {
186 if (typeDefinition != null) {
187 if (typeDefinition.getBaseType() instanceof EnumTypeDefinition) {
188 return (EnumTypeDefinition) typeDefinition.getBaseType();
189 } else if (typeDefinition.getBaseType() instanceof ExtendedType) {
190 return enumTypeDefFromExtendedType(typeDefinition.getBaseType());
196 private EnumBuilder resolveEnumFromTypeDefinition(
197 final EnumTypeDefinition enumTypeDef, final String enumName,
198 final GeneratedTypeBuilder typeBuilder) {
199 if ((enumTypeDef != null) && (typeBuilder != null)
200 && (enumTypeDef.getQName() != null)
201 && (enumTypeDef.getQName().getLocalName() != null)) {
203 final String enumerationName = CodeGeneratorHelper
204 .parseToClassName(enumName);
205 final EnumBuilder enumBuilder = typeBuilder
206 .addEnumeration(enumerationName);
208 if (enumBuilder != null) {
209 final List<EnumPair> enums = enumTypeDef.getValues();
212 for (final EnumPair enumPair : enums) {
213 if (enumPair != null) {
214 final String enumPairName = CodeGeneratorHelper
215 .parseToClassName(enumPair.getName());
216 Integer enumPairValue = enumPair.getValue();
218 if (enumPairValue == null) {
219 enumPairValue = listIndex;
221 enumBuilder.addValue(enumPairName, enumPairValue);
232 private GeneratedTypeBuilder moduleTypeBuilder(final Module module,
233 final String postfix) {
234 if (module != null) {
235 String packageName = resolveBasePackageName(module.getNamespace(),
236 module.getYangVersion());
237 final String moduleName = CodeGeneratorHelper
238 .parseToClassName(module.getName()) + postfix;
240 if (packageName != null) {
241 packageName = validatePackage(packageName);
242 return new GeneratedTypeBuilderImpl(packageName, moduleName);
248 private GeneratedType rpcMethodsToGenType(final Module module) {
249 if (module != null) {
250 final Set<RpcDefinition> rpcDefinitions = module.getRpcs();
252 if ((rpcDefinitions != null) && !rpcDefinitions.isEmpty()) {
253 final GeneratedTypeBuilder rpcTypeBuilder = moduleTypeBuilder(
256 for (final RpcDefinition rpc : rpcDefinitions) {
266 private GeneratedType notifycationsToGenType(final Module module) {
267 if (module != null) {
268 final Set<NotificationDefinition> notifications = module
271 if ((notifications != null) && !notifications.isEmpty()) {
272 final GeneratedTypeBuilder notifyTypeBuilder = moduleTypeBuilder(
273 module, "Notification");
275 for (final NotificationDefinition notification : notifications) {
276 if (notification != null) {
285 private String resolveGeneratedTypePackageName(final SchemaPath schemaPath) {
286 final StringBuilder builder = new StringBuilder();
287 builder.append(basePackageName);
288 if ((schemaPath != null) && (schemaPath.getPath() != null)) {
289 final List<QName> pathToNode = schemaPath.getPath();
290 final int traversalSteps = (pathToNode.size() - 1);
291 for (int i = 0; i < traversalSteps; ++i) {
293 String nodeLocalName = pathToNode.get(i).getLocalName();
295 // TODO: create method
296 nodeLocalName = nodeLocalName.replace(":", ".");
297 nodeLocalName = nodeLocalName.replace("-", ".");
298 builder.append(nodeLocalName);
300 return validatePackage(builder.toString());
305 private GeneratedType containerToGenType(ContainerSchemaNode container) {
306 if (container == null) {
309 final Set<DataSchemaNode> schemaNodes = container.getChildNodes();
310 final GeneratedTypeBuilder typeBuilder = addRawInterfaceDefinition(container);
312 resolveTypesFromDataSchemaNode(typeBuilder, schemaNodes);
313 return typeBuilder.toInstance();
316 private GeneratedTypeBuilder resolveTypesFromDataSchemaNode(
317 final GeneratedTypeBuilder typeBuilder,
318 final Set<DataSchemaNode> schemaNodes) {
320 if ((schemaNodes != null) && (typeBuilder != null)) {
321 for (final DataSchemaNode node : schemaNodes) {
322 if (node instanceof LeafSchemaNode) {
323 resolveLeafSchemaNodeAsMethod(typeBuilder,
324 (LeafSchemaNode) node);
325 } else if (node instanceof LeafListSchemaNode) {
326 resolveLeafListSchemaNode(typeBuilder,
327 (LeafListSchemaNode) node);
329 } else if (node instanceof ContainerSchemaNode) {
330 resolveContainerSchemaNode(typeBuilder,
331 (ContainerSchemaNode) node);
332 } else if (node instanceof ListSchemaNode) {
333 resolveListSchemaNode(typeBuilder, (ListSchemaNode) node);
340 private boolean resolveLeafSchemaNodeAsMethod(
341 final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf) {
342 if ((leaf != null) && (typeBuilder != null)) {
343 final String leafName = leaf.getQName().getLocalName();
344 String leafDesc = leaf.getDescription();
345 if (leafDesc == null) {
349 if (leafName != null) {
350 final TypeDefinition<?> typeDef = leaf.getType();
353 if (!(typeDef instanceof EnumTypeDefinition)
354 && !isDerivedFromEnumerationType(typeDef)) {
356 .javaTypeForSchemaDefinitionType(typeDef);
358 if (isImported(leaf.getPath(), typeDef.getPath())) {
359 //TODO: resolving of imported enums as references to GeneratedTypeData interface
361 final EnumTypeDefinition enumTypeDef = enumTypeDefFromExtendedType(typeDef);
362 final EnumBuilder enumBuilder = resolveEnumFromTypeDefinition(enumTypeDef, leafName,
365 if (enumBuilder != null) {
366 type = new ReferencedTypeImpl(enumBuilder.getPackageName(), enumBuilder.getName());
371 constructGetter(typeBuilder, leafName, leafDesc, type);
372 if (!leaf.isConfiguration()) {
373 constructSetter(typeBuilder, leafName, leafDesc, type);
381 private boolean isImported(final SchemaPath leafPath,
382 final SchemaPath typeDefPath) {
383 if ((leafPath != null) && (leafPath.getPath() != null)
384 && (typeDefPath != null) && (typeDefPath.getPath() != null)) {
386 final QName leafPathQName = leafPath.getPath().get(0);
387 final QName typePathQName = typeDefPath.getPath().get(0);
389 if ((leafPathQName != null)
390 && (leafPathQName.getNamespace() != null)
391 && (typePathQName != null)
392 && (typePathQName.getNamespace() != null)) {
394 return !leafPathQName.getNamespace().equals(
395 typePathQName.getNamespace());
401 private boolean resolveLeafSchemaNodeAsProperty(
402 final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf,
403 boolean isReadOnly) {
404 if ((leaf != null) && (toBuilder != null)) {
405 final String leafName = leaf.getQName().getLocalName();
406 String leafDesc = leaf.getDescription();
407 if (leafDesc == null) {
411 if (leafName != null) {
412 final TypeDefinition<?> typeDef = leaf.getType();
414 // TODO: properly resolve enum types
415 final Type javaType = typeProvider
416 .javaTypeForSchemaDefinitionType(typeDef);
418 final GeneratedPropertyBuilder propBuilder = toBuilder
419 .addProperty(CodeGeneratorHelper
420 .parseToClassName(leafName));
422 propBuilder.setReadOnly(isReadOnly);
423 propBuilder.addReturnType(javaType);
424 propBuilder.addComment(leafDesc);
426 toBuilder.addEqualsIdentity(propBuilder);
427 toBuilder.addHashIdentity(propBuilder);
428 toBuilder.addToStringProperty(propBuilder);
436 private boolean resolveLeafListSchemaNode(
437 final GeneratedTypeBuilder typeBuilder,
438 final LeafListSchemaNode node) {
439 if ((node != null) && (typeBuilder != null)) {
440 final String nodeName = node.getQName().getLocalName();
441 String nodeDesc = node.getDescription();
442 if (nodeDesc == null) {
446 if (nodeName != null) {
447 final TypeDefinition<?> type = node.getType();
448 final Type listType = Types.listTypeFor(typeProvider
449 .javaTypeForSchemaDefinitionType(type));
451 constructGetter(typeBuilder, nodeName, nodeDesc, listType);
452 if (!node.isConfiguration()) {
453 constructSetter(typeBuilder, nodeName, nodeDesc, listType);
461 private boolean resolveContainerSchemaNode(
462 final GeneratedTypeBuilder typeBuilder,
463 final ContainerSchemaNode node) {
464 if ((node != null) && (typeBuilder != null)) {
465 final String nodeName = node.getQName().getLocalName();
467 if (nodeName != null) {
468 final GeneratedTypeBuilder rawGenType = addRawInterfaceDefinition(node);
469 constructGetter(typeBuilder, nodeName, "", rawGenType);
477 private boolean resolveListSchemaNode(
478 final GeneratedTypeBuilder typeBuilder, final ListSchemaNode node) {
479 if ((node != null) && (typeBuilder != null)) {
480 final String nodeName = node.getQName().getLocalName();
482 if (nodeName != null) {
483 final GeneratedTypeBuilder rawGenType = addRawInterfaceDefinition(node);
484 constructGetter(typeBuilder, nodeName, "",
485 Types.listTypeFor(rawGenType));
486 if (!node.isConfiguration()) {
487 constructSetter(typeBuilder, nodeName, "",
488 Types.listTypeFor(rawGenType));
496 private GeneratedTypeBuilder addRawInterfaceDefinition(
497 final DataSchemaNode schemaNode) {
498 if (schemaNode == null) {
502 final String packageName = resolveGeneratedTypePackageName(schemaNode
504 final String schemaNodeName = schemaNode.getQName().getLocalName();
506 if ((packageName != null) && (schemaNode != null)
507 && (schemaNodeName != null)) {
508 final String genTypeName = CodeGeneratorHelper
509 .parseToClassName(schemaNodeName);
510 final GeneratedTypeBuilder newType = new GeneratedTypeBuilderImpl(
511 packageName, genTypeName);
513 if (!genTypeBuilders.containsKey(packageName)) {
514 final Map<String, GeneratedTypeBuilder> builders = new HashMap<String, GeneratedTypeBuilder>();
515 builders.put(genTypeName, newType);
516 genTypeBuilders.put(packageName, builders);
518 final Map<String, GeneratedTypeBuilder> builders = genTypeBuilders
520 if (!builders.containsKey(genTypeName)) {
521 builders.put(genTypeName, newType);
529 private String getterMethodName(final String methodName) {
530 final StringBuilder method = new StringBuilder();
531 method.append("get");
532 method.append(CodeGeneratorHelper.parseToClassName(methodName));
533 return method.toString();
536 private String setterMethodName(final String methodName) {
537 final StringBuilder method = new StringBuilder();
538 method.append("set");
539 method.append(CodeGeneratorHelper.parseToClassName(methodName));
540 return method.toString();
543 private MethodSignatureBuilder constructGetter(
544 final GeneratedTypeBuilder interfaceBuilder,
545 final String schemaNodeName, final String comment,
546 final Type returnType) {
547 final MethodSignatureBuilder getMethod = interfaceBuilder
548 .addMethod(getterMethodName(schemaNodeName));
550 getMethod.addComment(comment);
551 getMethod.addReturnType(returnType);
556 private MethodSignatureBuilder constructSetter(
557 final GeneratedTypeBuilder interfaceBuilder,
558 final String schemaNodeName, final String comment,
559 final Type parameterType) {
560 final MethodSignatureBuilder setMethod = interfaceBuilder
561 .addMethod(setterMethodName(schemaNodeName));
563 setMethod.addComment(comment);
564 setMethod.addParameter(parameterType,
565 CodeGeneratorHelper.parseToParamName(schemaNodeName));
566 setMethod.addReturnType(Types.voidType());
571 private String resolveBasePackageName(final URI moduleNamespace,
572 final String yangVersion) {
573 final StringBuilder packageNameBuilder = new StringBuilder();
575 packageNameBuilder.append("org.opendaylight.yang.gen.v");
576 packageNameBuilder.append(yangVersion);
577 packageNameBuilder.append(".rev");
578 packageNameBuilder.append(calendar.get(Calendar.YEAR));
579 packageNameBuilder.append((calendar.get(Calendar.MONTH) + 1));
580 packageNameBuilder.append(calendar.get(Calendar.DAY_OF_MONTH));
581 packageNameBuilder.append(".");
583 String namespace = moduleNamespace.toString();
584 namespace = namespace.replace(":", ".");
585 namespace = namespace.replace("-", ".");
587 packageNameBuilder.append(namespace);
589 return packageNameBuilder.toString();
592 private List<Type> listToGenType(final ListSchemaNode list) {
596 final GeneratedTypeBuilder typeBuilder = resolveListTypeBuilder(list);
597 final List<String> listKeys = listKeys(list);
598 GeneratedTOBuilder genTOBuilder = null;
599 if (listKeys.size() > 0) {
600 genTOBuilder = resolveListKey(list);
603 final Set<DataSchemaNode> schemaNodes = list.getChildNodes();
604 for (final DataSchemaNode node : schemaNodes) {
606 if (node instanceof LeafSchemaNode) {
607 final LeafSchemaNode leaf = (LeafSchemaNode) node;
608 if (!isPartOfListKey(leaf, listKeys)) {
609 resolveLeafSchemaNodeAsMethod(typeBuilder, leaf);
611 resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
613 } else if (node instanceof LeafListSchemaNode) {
614 resolveLeafListSchemaNode(typeBuilder,
615 (LeafListSchemaNode) node);
616 } else if (node instanceof ContainerSchemaNode) {
617 resolveContainerSchemaNode(typeBuilder,
618 (ContainerSchemaNode) node);
619 } else if (node instanceof ListSchemaNode) {
620 resolveListSchemaNode(typeBuilder, (ListSchemaNode) node);
624 final List<Type> genTypes = new ArrayList<Type>();
625 if (genTOBuilder != null) {
626 final GeneratedTransferObject genTO = genTOBuilder.toInstance();
627 constructGetter(typeBuilder, genTO.getName(),
628 "Returns Primary Key of Yang List Type", genTO);
631 genTypes.add(typeBuilder.toInstance());
639 private GeneratedTOBuilder resolveListKey(final ListSchemaNode list) {
640 final String packageName = resolveGeneratedTypePackageName(list
642 final String listName = list.getQName().getLocalName() + "Key";
644 if ((packageName != null) && (list != null) && (listName != null)) {
645 final String genTOName = CodeGeneratorHelper
646 .parseToClassName(listName);
647 final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(
648 packageName, genTOName);
655 private boolean isPartOfListKey(final LeafSchemaNode leaf,
656 final List<String> keys) {
657 if ((leaf != null) && (keys != null) && (leaf.getQName() != null)) {
658 final String leafName = leaf.getQName().getLocalName();
659 if (keys.contains(leafName)) {
666 private List<String> listKeys(final ListSchemaNode list) {
667 final List<String> listKeys = new ArrayList<String>();
669 if (list.getKeyDefinition() != null) {
670 final List<QName> keyDefinitions = list.getKeyDefinition();
672 for (final QName keyDefinition : keyDefinitions) {
673 listKeys.add(keyDefinition.getLocalName());
679 private GeneratedTypeBuilder resolveListTypeBuilder(
680 final ListSchemaNode list) {
681 final String packageName = resolveGeneratedTypePackageName(list
683 final String schemaNodeName = list.getQName().getLocalName();
684 final String genTypeName = CodeGeneratorHelper
685 .parseToClassName(schemaNodeName);
687 GeneratedTypeBuilder typeBuilder = null;
688 if (genTypeBuilders.containsKey(packageName)) {
689 final Map<String, GeneratedTypeBuilder> builders = new HashMap<String, GeneratedTypeBuilder>();
690 typeBuilder = builders.get(genTypeName);
692 if (null == typeBuilder) {
693 typeBuilder = addRawInterfaceDefinition(list);