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