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