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