Binding generator v2 - uses statement - uses of rpc & action
[mdsal.git] / binding2 / mdsal-binding2-generator-impl / src / main / java / org / opendaylight / mdsal / binding / javav2 / generator / impl / RpcActionGenHelper.java
1 /*
2  * Copyright (c) 2017 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
9 package org.opendaylight.mdsal.binding.javav2.generator.impl;
10
11 import static com.google.common.base.Preconditions.checkState;
12 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.annotateDeprecatedIfNecessary;
13 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.checkModuleAndModuleName;
14 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.createDescription;
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.util.BindingGeneratorUtil.encodeAngleBrackets;
20 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.packageNameForGeneratedType;
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;
35
36 import com.google.common.annotations.Beta;
37 import com.google.common.base.Optional;
38 import java.util.Collection;
39 import java.util.Map;
40 import java.util.Set;
41 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
42 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
43 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
44 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
45 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
46 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.MethodSignatureBuilder;
47 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
48 import org.opendaylight.yangtools.yang.common.QName;
49 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
50 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
51 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.Module;
55 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
56 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
59
60 /**
61  *
62  * Util class used for generation of types for RPCs, routedRPCs and Actions (YANG 1.1 only)
63  * in Binding spec. v2. In case of routed RPC detected in input YANG, RPC is turned to Action.
64  *
65  */
66 @Beta
67 final class RpcActionGenHelper {
68
69     private static final QName CONTEXT_REFERENCE =
70             QName.create("urn:opendaylight:yang:extension:yang-ext", "2013-07-09", "context-reference").intern();
71
72     private RpcActionGenHelper() {
73         throw new UnsupportedOperationException("Util class");
74     }
75
76     /**
77      * Let's find out what context we are talking about
78      * 1. routed RPC
79      * 2. global RPC
80      *
81      * In 1st case, we need Binding Generator behave like YANG 1.1 Action
82      *
83      * @param schemaNode RPC input node
84      * @return presence optional
85      */
86     private static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
87         for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
88             if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
89                 return Optional.fromNullable(extension.getQName());
90             }
91         }
92         return Optional.absent();
93     }
94
95     /**
96      * Converts Yang 1.1 <b>Actions</b> to list of <code>Type</code> objects.
97      * @param module  module from which is obtained set of all Action objects to
98      *            iterate over them
99      * @param genCtx input, generated context
100      * @param verboseClassComments verbosity switch
101      * @return generated context
102      */
103     static Map<Module, ModuleContext> actionMethodsToGenType(final Module module, Map<Module, ModuleContext> genCtx,
104             final SchemaContext schemaContext, final boolean verboseClassComments, Map<String, Map<String,
105             GeneratedTypeBuilder>> genTypeBuilders, TypeProvider typeProvider) {
106
107         checkModuleAndModuleName(module);
108         final Collection<DataSchemaNode> potentials = module.getChildNodes();
109         for (DataSchemaNode potential : potentials) {
110             if (potential instanceof ActionNodeContainer) {
111                 final Set<ActionDefinition> actions = ((ActionNodeContainer) potential).getActions();
112                 for (ActionDefinition action: actions) {
113                     genCtx.get(module).addTopLevelNodeType(resolveOperation(potential, action, module,
114                             schemaContext, verboseClassComments, genTypeBuilders, genCtx, typeProvider, true,
115                             BindingNamespaceType.Data));
116                 }
117             }
118         }
119         return genCtx;
120     }
121
122     /**
123      * Converts global <b>RPCs</b> inputs and outputs sub-statements of the module
124      * to the list of <code>Type</code> objects. In addition, containers
125      * and lists which belong to input or output are also part of returning list.
126      * Detected routed RPCs are turned to Yang 1.1 Actions
127      *
128      * @param module
129      *            module from which is obtained set of all RPC objects to
130      *            iterate over them
131      * @param genCtx input, generated context
132      * @param verboseClassComments verbosity switch
133      *
134      * @throws IllegalArgumentException
135      *             <ul>
136      *             <li>if the module is null</li>
137      *             <li>if the name of module is null</li>
138      *             </ul>
139      * @throws IllegalStateException
140      *             if set of RPCs from module is null
141      *
142      * @return generated context
143      */
144      static Map<Module, ModuleContext> rpcMethodsToGenType(final Module module, Map<Module, ModuleContext> genCtx,
145             final SchemaContext schemaContext, final boolean verboseClassComments, Map<String, Map<String,
146              GeneratedTypeBuilder>> genTypeBuilders, TypeProvider typeProvider) {
147
148         checkModuleAndModuleName(module);
149         final Set<RpcDefinition> rpcDefinitions = module.getRpcs();
150         checkState(rpcDefinitions != null, "Set of RPCs from module " + module.getName() + " cannot be NULL.");
151         if (rpcDefinitions.isEmpty()) {
152             return genCtx;
153         }
154
155         for (final RpcDefinition rpc : rpcDefinitions) {
156             //FIXME: get correct parent for routed RPCs only
157             DataSchemaNode parent = null;
158
159             ContainerSchemaNode input = rpc.getInput();
160             boolean isAction = false;
161             if (input != null) {
162                 for (DataSchemaNode schemaNode : input.getChildNodes()) {
163                     if (getRoutingContext(schemaNode).isPresent()) {
164                         isAction = true;
165                         break;
166                     }
167                 }
168             }
169
170             //routedRPC?
171             if (isAction) {
172                 genCtx.get(module).addTopLevelNodeType(resolveOperation(parent, rpc, module, schemaContext,
173                         verboseClassComments, genTypeBuilders, genCtx, typeProvider, true,
174                         BindingNamespaceType.Data));
175             } else {
176                 //global RPC only
177                 genCtx.get(module).addTopLevelNodeType(resolveOperation(parent, rpc, module, schemaContext,
178                         verboseClassComments, genTypeBuilders, genCtx, typeProvider, false,
179                         BindingNamespaceType.Data));
180
181             }
182         }
183         return genCtx;
184     }
185
186     //TODO: This method should be reusable for convertion of action that is in grouping.
187     /**
188      * Converts RPC, Action or routed RPC into generated type
189      * @return generated type
190      */
191     private static GeneratedTypeBuilder resolveOperation(final DataSchemaNode parent, final OperationDefinition operation,
192             final Module module, final SchemaContext schemaContext, final boolean verboseClassComments,
193             Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final Map<Module, ModuleContext> genCtx,
194             TypeProvider typeProvider, final boolean isAction, final BindingNamespaceType namespaceType) {
195
196         //operation name
197         final String operationName = operation.getQName().getLocalName();
198         //concrete operation name
199         final StringBuilder sb = new StringBuilder(operationName).append('_');
200         if (isAction) {
201             sb.append("Action");
202         } else {
203             sb.append("Rpc");
204         }
205         final GeneratedTypeBuilder interfaceBuilder = moduleTypeBuilder(module, sb.toString(),
206                 verboseClassComments);
207
208         final String basePackageName = interfaceBuilder.getPackageName();
209
210         interfaceBuilder.setDescription(createDescription(operation, interfaceBuilder.getFullyQualifiedName(),
211                 schemaContext, verboseClassComments, namespaceType));
212         final String operationComment = encodeAngleBrackets(operation.getDescription());
213         final MethodSignatureBuilder operationMethod = interfaceBuilder.addMethod("invoke");
214
215         //input
216         final ContainerSchemaNode input = operation.getInput();
217         final GeneratedTypeBuilder inType = resolveOperationNode(interfaceBuilder, module, operation.getInput(),
218                 basePackageName, schemaContext, operationName, verboseClassComments, typeProvider, genTypeBuilders,
219                 genCtx, true, namespaceType);
220         annotateDeprecatedIfNecessary(operation.getStatus(), inType);
221         inType.setParentTypeForBuilder(interfaceBuilder);
222         genCtx.get(module).addChildNodeType(input, inType);
223
224         //output
225         final ContainerSchemaNode output = operation.getOutput();
226         final GeneratedTypeBuilder outType = resolveOperationNode(interfaceBuilder, module, operation.getOutput(),
227                 basePackageName, schemaContext, operationName, verboseClassComments, typeProvider, genTypeBuilders,
228                 genCtx, false, namespaceType);
229         annotateDeprecatedIfNecessary(operation.getStatus(), outType);
230         outType.setParentTypeForBuilder(interfaceBuilder);
231         genCtx.get(module).addChildNodeType(output, outType);
232
233         final GeneratedType inTypeInstance = inType.toInstance();
234         operationMethod.addParameter(inTypeInstance, "input");
235
236         if (isAction) {
237             //action, routed RPC
238             checkState(parent != null, "Parent node of " + operation.getQName().getLocalName() + " can't be NULL");
239             GeneratedTypeBuilder parentType = genCtx.get(module).getChildNode(parent.getPath());
240             checkState(parentType != null, "Parent generated type for " + parent
241                     + " data schema node must have been generated already");
242             annotateDeprecatedIfNecessary(parent.getStatus(), parentType);
243
244             if (parent instanceof ListSchemaNode) {
245                 //ListAction
246                 GeneratedTransferObject keyType = null;
247                 for (MethodSignatureBuilder method : parentType.getMethodDefinitions()) {
248                     if (method.getName().equals("getKey")) {
249                         keyType = (GeneratedTransferObject) method.toInstance(parentType).getReturnType();
250                     }
251                 }
252
253                 operationMethod.addParameter(
254                         parameterizedTypeFor(KEYED_INSTANCE_IDENTIFIER, parentType, keyType), "kii");
255                 interfaceBuilder.addImplementsType(parameterizedTypeFor(LIST_ACTION, parentType, inType, outType));
256             } else {
257                 //Action
258                 GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, parentType, parentType,
259                         ((ContainerSchemaNode) parent).getChildNodes(), genCtx, schemaContext, verboseClassComments,
260                         genTypeBuilders, typeProvider, namespaceType);
261                 operationMethod.addParameter(parameterizedTypeFor(INSTANCE_IDENTIFIER, parentType), "ii");
262                 interfaceBuilder.addImplementsType(parameterizedTypeFor(ACTION, parentType, inType, outType));
263             }
264         } else {
265             //RPC
266             interfaceBuilder.addImplementsType(parameterizedTypeFor(RPC, inType, outType));
267         }
268
269         interfaceBuilder.addImplementsType(TREE_NODE);
270         operationMethod.addParameter(parameterizedTypeFor(RPC_CALLBACK, outType), "callback");
271
272         operationMethod.setComment(operationComment);
273         operationMethod.setReturnType(VOID);
274
275         return interfaceBuilder;
276     }
277
278     private static GeneratedTypeBuilder resolveOperationNode(GeneratedTypeBuilder parent, final Module module, final
279             ContainerSchemaNode operationNode, final String basePackageName, final SchemaContext schemaContext, final String
280             operationName, final boolean verboseClassComments, TypeProvider typeProvider, Map<String, Map<String,
281             GeneratedTypeBuilder>> genTypeBuilders, final Map<Module, ModuleContext> genCtx, final boolean isInput,
282             final BindingNamespaceType namespaceType) {
283         final GeneratedTypeBuilder nodeType = addRawInterfaceDefinition(basePackageName, operationNode, schemaContext,
284                 operationName, "", verboseClassComments, genTypeBuilders, namespaceType);
285         addImplementedInterfaceFromUses(operationNode, nodeType, genCtx);
286         nodeType.addImplementsType(parameterizedTypeFor(BindingTypes.TREE_CHILD_NODE, parent, parameterizedTypeFor
287                 (BindingTypes.ITEM, parent)));
288         if (isInput) {
289             nodeType.addImplementsType(parameterizedTypeFor(INPUT, nodeType));
290         } else {
291             nodeType.addImplementsType(parameterizedTypeFor(OUTPUT, nodeType));
292         }
293         nodeType.addImplementsType(parameterizedTypeFor(INSTANTIABLE, nodeType));
294         nodeType.addImplementsType(augmentable(nodeType));
295         GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, nodeType, nodeType, operationNode.getChildNodes(), genCtx,
296                 schemaContext, verboseClassComments, genTypeBuilders, typeProvider, namespaceType);
297
298         final MethodSignatureBuilder nodeMethod = nodeType.addMethod("implementedInterface");
299         nodeMethod.setReturnType(parameterizedTypeFor(CLASS, nodeType));
300         nodeMethod.addAnnotation("", "Override");
301
302         processUsesImplements(operationNode, module, schemaContext, genCtx, namespaceType);
303
304         return nodeType;
305     }
306 }