2 * Copyright (c) 2017 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
9 package org.opendaylight.mdsal.binding.javav2.generator.impl;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
13 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.annotateDeprecatedIfNecessary;
14 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.checkModuleAndModuleName;
15 import static org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil.addImplementedInterfaceFromUses;
16 import static org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil.addRawInterfaceDefinition;
17 import static org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil.moduleTypeBuilder;
18 import static org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil.processUsesImplements;
19 import static org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil.resolveDataSchemaNodesCheck;
20 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.encodeAngleBrackets;
21 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.ACTION;
22 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.INPUT;
23 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.INSTANCE_IDENTIFIER;
24 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.INSTANTIABLE;
25 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.KEYED_INSTANCE_IDENTIFIER;
26 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.LIST_ACTION;
27 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.OUTPUT;
28 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.RPC;
29 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.RPC_CALLBACK;
30 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.TREE_NODE;
31 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes.augmentable;
32 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.CLASS;
33 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.VOID;
34 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.parameterizedTypeFor;
36 import com.google.common.annotations.Beta;
37 import com.google.common.annotations.VisibleForTesting;
38 import java.util.Collection;
40 import java.util.Optional;
42 import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
43 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
44 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
45 import org.opendaylight.mdsal.binding.javav2.generator.util.TypeComments;
46 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
47 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
48 import org.opendaylight.mdsal.binding.javav2.model.api.YangSourceDefinition;
49 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
50 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.MethodSignatureBuilder;
51 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
52 import org.opendaylight.yangtools.yang.common.QName;
53 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
54 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
55 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
57 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
58 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
59 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.Module;
61 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
62 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
63 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
64 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
67 * Util class used for generation of types for RPCs, routedRPCs and Actions (YANG 1.1 only)
68 * in Binding spec. v2. In case of routed RPC detected in input YANG, RPC is turned to Action.
71 final class RpcActionGenHelper {
73 static final QName CONTEXT_REFERENCE =
74 QName.create("urn:opendaylight:yang:extension:yang-ext", "2013-07-09", "context-reference").intern();
76 private RpcActionGenHelper() {
77 throw new UnsupportedOperationException("Util class");
81 * Let's find out what context we are talking about
86 * In 1st case, we need Binding Generator behave like YANG 1.1 Action
88 * @param schemaNode RPC input node
89 * @return presence optional
92 static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
93 for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
94 if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
95 return Optional.ofNullable(extension.getQName());
98 return Optional.empty();
101 private static void resolveActions(final DataNodeContainer parent, final Module module,
102 final SchemaContext schemaContext, final boolean verboseClassComments,
103 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final
104 Map<Module, ModuleContext> genCtx,
105 final TypeProvider typeProvider, final BindingNamespaceType namespaceType) {
106 requireNonNull(parent, "Parent should not be NULL.");
107 final Collection<DataSchemaNode> potentials = parent.getChildNodes();
108 for (DataSchemaNode potential : potentials) {
109 if (resolveDataSchemaNodesCheck(module, schemaContext, potential)) {
110 BindingNamespaceType namespaceType1 = namespaceType;
111 if (BindingNamespaceType.isData(namespaceType)) {
112 if (potential instanceof GroupingDefinition) {
113 namespaceType1 = BindingNamespaceType.Grouping;
117 if (potential instanceof ActionNodeContainer) {
118 final Set<ActionDefinition> actions = ((ActionNodeContainer) potential).getActions();
119 for (ActionDefinition action : actions) {
120 final GeneratedTypeBuilder typeBuilder = resolveOperation(potential, action, module,
121 schemaContext, verboseClassComments, genTypeBuilders, genCtx, typeProvider, true,
123 genCtx.get(module).addTopLevelNodeType(typeBuilder);
124 genCtx.get(module).addTypeToSchema(typeBuilder, action);
128 if (potential instanceof DataNodeContainer) {
129 resolveActions((DataNodeContainer) potential, module, schemaContext, verboseClassComments,
130 genTypeBuilders, genCtx, typeProvider, namespaceType1);
137 * Converts Yang 1.1 <b>Actions</b> to list of <code>Type</code> objects.
139 * @param module module from which is obtained set of all Action objects to
141 * @param genCtx input, generated context
142 * @param verboseClassComments verbosity switch
143 * @return generated context
145 static Map<Module, ModuleContext> actionMethodsToGenType(final Module module, final Map<Module, ModuleContext>
147 final SchemaContext schemaContext, final boolean
148 verboseClassComments,
149 final Map<String, Map<String, GeneratedTypeBuilder>>
150 genTypeBuilders, final TypeProvider typeProvider) {
152 checkModuleAndModuleName(module);
153 resolveActions(module, module, schemaContext, verboseClassComments, genTypeBuilders, genCtx, typeProvider,
154 BindingNamespaceType.Operation);
159 * Converts global <b>RPCs</b> inputs and outputs sub-statements of the module
160 * to the list of <code>Type</code> objects. In addition, containers
161 * and lists which belong to input or output are also part of returning list.
162 * Detected routed RPCs are turned to Yang 1.1 Actions
164 * @param module module from which is obtained set of all RPC objects to
166 * @param genCtx input, generated context
167 * @param verboseClassComments verbosity switch
168 * @return generated context
169 * @throws IllegalArgumentException <ul>
170 * <li>if the module is null</li>
171 * <li>if the name of module is null</li>
173 * @throws IllegalStateException if set of RPCs from module is null
175 static Map<Module, ModuleContext> rpcMethodsToGenType(final Module module, final Map<Module, ModuleContext> genCtx,
176 final SchemaContext schemaContext, final boolean
177 verboseClassComments, final Map<String, Map<String,
178 GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
180 checkModuleAndModuleName(module);
181 final Set<RpcDefinition> rpcDefinitions = module.getRpcs();
182 checkState(rpcDefinitions != null, "Set of RPCs from module " + module.getName() + " cannot be NULL.");
183 if (rpcDefinitions.isEmpty()) {
187 for (final RpcDefinition rpc : rpcDefinitions) {
188 final GeneratedTypeBuilder typeBuilder = resolveOperation(null, rpc, module, schemaContext,
189 verboseClassComments, genTypeBuilders, genCtx, typeProvider, false,
190 BindingNamespaceType.Operation);
191 genCtx.get(module).addTopLevelNodeType(typeBuilder);
192 genCtx.get(module).addTypeToSchema(typeBuilder, rpc);
199 * Converts RPC, Action or routed RPC into generated type.
201 * @return generated type
203 private static GeneratedTypeBuilder resolveOperation(final DataSchemaNode parent, final OperationDefinition
205 final Module module, final SchemaContext schemaContext,
206 final boolean verboseClassComments,
207 final Map<String, Map<String, GeneratedTypeBuilder>>
208 genTypeBuilders, final Map<Module, ModuleContext>
210 final TypeProvider typeProvider, final boolean isAction,
211 final BindingNamespaceType namespaceType) {
214 final String operationName = operation.getQName().getLocalName();
215 //concrete operation name
216 final StringBuilder sb = new StringBuilder(operationName).append('_');
222 final GeneratedTypeBuilder interfaceBuilder = moduleTypeBuilder(module, sb.toString(),
223 verboseClassComments, genCtx.get(module));
225 final String basePackageName = interfaceBuilder.getPackageName();
227 if (verboseClassComments) {
228 interfaceBuilder.addComment(TypeComments.javadoc(
229 "Interface for implementing the following YANG Operation defined in module <b>"
230 + module.getName() + "</b>").get());
231 YangSourceDefinition.of(module, operation).ifPresent(interfaceBuilder::setYangSourceDefinition);
234 final String operationComment = encodeAngleBrackets(operation.getDescription().orElse(null));
235 final MethodSignatureBuilder operationMethod = interfaceBuilder.addMethod("invoke");
238 final ContainerSchemaNode input = operation.getInput();
239 final GeneratedTypeBuilder inType = resolveOperationNode(interfaceBuilder, module, operation.getInput(),
240 basePackageName, schemaContext, operationName, verboseClassComments, typeProvider, genTypeBuilders,
241 genCtx, true, namespaceType);
242 annotateDeprecatedIfNecessary(operation.getStatus(), inType);
243 inType.setParentTypeForBuilder(interfaceBuilder);
244 genCtx.get(module).addChildNodeType(input, inType);
247 final ContainerSchemaNode output = operation.getOutput();
248 final GeneratedTypeBuilder outType = resolveOperationNode(interfaceBuilder, module, operation.getOutput(),
249 basePackageName, schemaContext, operationName, verboseClassComments, typeProvider, genTypeBuilders,
250 genCtx, false, namespaceType);
251 annotateDeprecatedIfNecessary(operation.getStatus(), outType);
252 outType.setParentTypeForBuilder(interfaceBuilder);
253 genCtx.get(module).addChildNodeType(output, outType);
255 final GeneratedType inTypeInstance = inType.toInstance();
256 operationMethod.addParameter(inTypeInstance, "input");
259 requireNonNull(parent, "Parent must be specified for action.");
261 GeneratedTypeBuilder parentType = genCtx.get(module).getChildNode(parent.getPath());
262 checkState(parentType != null, "Parent generated type for " + parent
263 + " data schema node must have been generated already");
264 annotateDeprecatedIfNecessary(parent.getStatus(), parentType);
266 if (parent instanceof ListSchemaNode) {
268 GeneratedTransferObject keyType = null;
269 for (MethodSignatureBuilder method : parentType.getMethodDefinitions()) {
270 if (method.getName().equals("getIdentifier")) {
271 keyType = (GeneratedTransferObject) method.toInstance(parentType).getReturnType();
275 operationMethod.addParameter(parameterizedTypeFor(KEYED_INSTANCE_IDENTIFIER, parentType, keyType),
277 interfaceBuilder.addImplementsType(parameterizedTypeFor(LIST_ACTION, parentType, keyType, inType,
281 operationMethod.addParameter(parameterizedTypeFor(INSTANCE_IDENTIFIER, parentType), "ii");
282 interfaceBuilder.addImplementsType(parameterizedTypeFor(ACTION, parentType,
283 parameterizedTypeFor(INSTANCE_IDENTIFIER, parentType), inType, outType));
287 interfaceBuilder.addImplementsType(parameterizedTypeFor(RPC, inType, outType));
290 interfaceBuilder.addImplementsType(TREE_NODE);
291 operationMethod.addParameter(parameterizedTypeFor(RPC_CALLBACK, outType), "callback");
293 operationMethod.setComment(operationComment);
294 operationMethod.setReturnType(VOID);
296 return interfaceBuilder;
299 private static GeneratedTypeBuilder resolveOperationNode(final GeneratedTypeBuilder parent, final Module module,
300 final ContainerSchemaNode operationNode, final String basePackageName, final SchemaContext schemaContext,
301 final String operationName, final boolean verboseClassComments, final TypeProvider typeProvider,
302 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
303 final Map<Module, ModuleContext> genCtx, final boolean isInput, final BindingNamespaceType namespaceType) {
304 final GeneratedTypeBuilder nodeType = addRawInterfaceDefinition(basePackageName, operationNode, schemaContext,
305 operationName, "", verboseClassComments, genTypeBuilders, namespaceType, genCtx.get(module));
306 addImplementedInterfaceFromUses(operationNode, nodeType, genCtx);
308 nodeType.addImplementsType(parameterizedTypeFor(BindingTypes.TREE_CHILD_NODE, parent,
309 parameterizedTypeFor(BindingTypes.ITEM, nodeType)));
312 nodeType.addImplementsType(parameterizedTypeFor(INPUT, nodeType));
314 nodeType.addImplementsType(parameterizedTypeFor(OUTPUT, nodeType));
316 nodeType.addImplementsType(parameterizedTypeFor(INSTANTIABLE, nodeType));
317 nodeType.addImplementsType(augmentable(nodeType));
318 GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, nodeType, nodeType, operationNode.getChildNodes(),
319 genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider, namespaceType);
321 final MethodSignatureBuilder nodeMethod = nodeType.addMethod("implementedInterface");
322 nodeMethod.setReturnType(parameterizedTypeFor(CLASS, nodeType));
323 nodeMethod.addAnnotation("", "Override");
325 processUsesImplements(operationNode, module, schemaContext, genCtx, namespaceType);