f28de9d50eb167da4471285297a2e077dfe010a0
[netconf.git] / restconf / wadl-generator / src / main / java / org / opendaylight / netconf / restconf / wadl / generator / WadlTemplate.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2021 PANTHEON.tech, s.r.o.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.netconf.restconf.wadl.generator;
10
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.Iterables;
14 import java.util.ArrayList;
15 import java.util.List;
16 import org.eclipse.xtend2.lib.StringConcatenation;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.common.XMLNamespace;
19 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
21 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
23 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.Module;
26 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
27
28 final class WadlTemplate {
29     private final List<DataSchemaNode> operationalData = new ArrayList<>();
30     private final List<DataSchemaNode> configData = new ArrayList<>();
31     private final EffectiveModelContext context;
32     private final Module module;
33
34     private List<LeafSchemaNode> pathListParams;
35
36     WadlTemplate(final EffectiveModelContext context, final Module module) {
37         this.context = requireNonNull(context);
38         this.module = requireNonNull(module);
39         for (final DataSchemaNode child : module.getChildNodes()) {
40             if (child instanceof ContainerSchemaNode || child instanceof ListSchemaNode) {
41                 if (child.isConfiguration()) {
42                     configData.add(child);
43                 } else {
44                     operationalData.add(child);
45                 }
46             }
47         }
48     }
49
50     public CharSequence body() {
51         if (!module.getRpcs().isEmpty() || !configData.isEmpty() || !operationalData.isEmpty()) {
52             return application();
53         }
54         return null;
55     }
56
57     private CharSequence application() {
58         StringConcatenation builder = new StringConcatenation();
59         builder.append("<?xml version=\"1.0\"?>");
60         builder.newLine();
61         builder.append("<application xmlns=\"http://wadl.dev.java.net/2009/02\" ");
62         builder.append(importsAsNamespaces());
63         builder.append(" xmlns:");
64         builder.append(module.getPrefix());
65         builder.append("=\"");
66         builder.append(module.getNamespace());
67         builder.append("\">");
68         builder.newLineIfNotEmpty();
69         builder.newLine();
70         builder.append("    ");
71         builder.append("<grammars>");
72         builder.newLine();
73         builder.append("        ");
74         builder.append("<include href=\"");
75         builder.append(module.getName(), "        ");
76         builder.append(".yang\"/>");
77         builder.newLineIfNotEmpty();
78
79         for (final ModuleImport imprt : module.getImports()) {
80             builder.append("        ");
81             builder.append("<include href=\"");
82             builder.append(imprt.getModuleName(), "        ");
83             builder.append(".yang\"/>");
84             builder.newLineIfNotEmpty();
85         }
86
87         builder.append("    ");
88         builder.append("</grammars>");
89         builder.newLine();
90         builder.newLine();
91         builder.append("    ");
92         builder.append("<resources base=\"http://localhost:9998/restconf\">");
93         builder.newLine();
94
95         if (!operationalData.isEmpty()) {
96             builder.append("        ");
97             builder.append("<resource path=\"operational\">");
98             builder.newLine();
99             for (final DataSchemaNode schemaNode : operationalData) {
100                 builder.append("        ");
101                 builder.append("    ");
102                 builder.append(firstResource(schemaNode, false), "            ");
103                 builder.newLineIfNotEmpty();
104             }
105             builder.append("        ");
106             builder.append("</resource>");
107             builder.newLine();
108         }
109
110         if (!configData.isEmpty()) {
111             builder.append("        ");
112             builder.append("<resource path=\"config\">");
113             builder.newLine();
114
115             for (final DataSchemaNode schemaNode : configData) {
116                 builder.append("        ");
117                 builder.append("    ");
118                 builder.append(mehodPost(schemaNode), "            ");
119                 builder.newLineIfNotEmpty();
120             }
121             for (final DataSchemaNode schemaNode : configData) {
122                 builder.append("        ");
123                 builder.append("    ");
124                 builder.append(firstResource(schemaNode, true), "            ");
125                 builder.newLineIfNotEmpty();
126             }
127             builder.append("        ");
128             builder.append("</resource>");
129             builder.newLine();
130         }
131
132         final var rpcs = module.getRpcs();
133         if (!rpcs.isEmpty()) {
134             builder.append("        ");
135             builder.append("<resource path=\"operations\">");
136             builder.newLine();
137             for (var rpc : rpcs) {
138                 builder.append("        ");
139                 builder.append("    ");
140                 builder.append("<resource path=\"");
141                 builder.append(module.getName(), "            ");
142                 builder.append(":");
143                 builder.append(rpc.getQName().getLocalName(), "            ");
144                 builder.append("\">");
145                 builder.newLineIfNotEmpty();
146                 builder.append("        ");
147                 builder.append("    ");
148                 builder.append("    ");
149                 builder.append(methodPostRpc(), "                ");
150                 builder.newLineIfNotEmpty();
151                 builder.append("        ");
152                 builder.append("    ");
153                 builder.append("</resource>");
154                 builder.newLine();
155             }
156             builder.append("        ");
157             builder.append("</resource>");
158             builder.newLine();
159         }
160
161         builder.append("    ");
162         builder.append("</resources>");
163         builder.newLine();
164         builder.append("</application>");
165         builder.newLine();
166         return builder;
167     }
168
169     private CharSequence importsAsNamespaces() {
170         StringConcatenation builder = new StringConcatenation();
171         for (final ModuleImport imprt : module.getImports()) {
172             builder.append("xmlns:");
173             builder.append(imprt.getPrefix());
174             builder.append("=\"");
175             builder.append(context.findModule(imprt.getModuleName(), imprt.getRevision()).get().getNamespace());
176             builder.append("\"");
177             builder.newLineIfNotEmpty();
178         }
179         return builder;
180     }
181
182     private String firstResource(final DataSchemaNode schemaNode, final boolean config) {
183         StringConcatenation builder = new StringConcatenation();
184         builder.append("<resource path=\"");
185         builder.append(module.getName());
186         builder.append(":");
187         builder.append(createPath(schemaNode));
188         builder.append("\">");
189         builder.newLineIfNotEmpty();
190         builder.append("    ");
191         builder.append(resourceBody(schemaNode, config), "    ");
192         builder.newLineIfNotEmpty();
193         builder.append("</resource>");
194         builder.newLine();
195         return builder.toString();
196     }
197
198     private String resource(final DataSchemaNode schemaNode, final boolean config) {
199         StringConcatenation builder = new StringConcatenation();
200         builder.append("<resource path=\"");
201         builder.append(createPath(schemaNode));
202         builder.append("\">");
203         builder.newLineIfNotEmpty();
204         builder.append("    ");
205         builder.append(resourceBody(schemaNode, config), "    ");
206         builder.newLineIfNotEmpty();
207         builder.append("</resource>");
208         builder.newLine();
209         return builder.toString();
210     }
211
212     private String createPath(final DataSchemaNode schemaNode) {
213         pathListParams = new ArrayList<>();
214         StringBuilder path = new StringBuilder().append(schemaNode.getQName().getLocalName());
215         if (schemaNode instanceof ListSchemaNode) {
216             final var listNode = (ListSchemaNode) schemaNode;
217             for (final QName listKey : listNode.getKeyDefinition()) {
218                 pathListParams.add((LeafSchemaNode) listNode.getDataChildByName(listKey));
219                 path.append("'/{").append(listKey.getLocalName()).append('}');
220             }
221         }
222         return path.toString();
223     }
224
225     private String resourceBody(final DataSchemaNode schemaNode, final boolean config) {
226         StringConcatenation builder = new StringConcatenation();
227         if (pathListParams != null && !pathListParams.isEmpty()) {
228             builder.append(resourceParams());
229             builder.newLineIfNotEmpty();
230         }
231         builder.append(methodGet(schemaNode));
232         builder.newLineIfNotEmpty();
233         builder.newLineIfNotEmpty();
234         final var children = Iterables.filter(((DataNodeContainer) schemaNode).getChildNodes(),
235             WadlTemplate::isListOrContainer);
236         if (config) {
237             builder.append(methodDelete(schemaNode));
238             builder.newLineIfNotEmpty();
239             builder.append(methodPut(schemaNode));
240             builder.newLineIfNotEmpty();
241             for (final DataSchemaNode child : children) {
242                 builder.append(mehodPost(child));
243                 builder.newLineIfNotEmpty();
244             }
245         }
246         for (final DataSchemaNode child : children) {
247             builder.append(resource(child, config));
248             builder.newLineIfNotEmpty();
249         }
250         return builder.toString();
251     }
252
253     private CharSequence resourceParams() {
254         StringConcatenation builder = new StringConcatenation();
255         for (final LeafSchemaNode pathParam : pathListParams) {
256             if (pathParam != null) {
257                 builder.newLineIfNotEmpty();
258                 builder.append("<param required=\"true\" style=\"template\" name=\"");
259                 builder.append(pathParam.getQName().getLocalName());
260                 builder.append("\" type=\"");
261                 builder.append(pathParam.getType().getQName().getLocalName());
262                 builder.append("\"/>");
263                 builder.newLineIfNotEmpty();
264             }
265         }
266         return builder;
267     }
268
269     private static CharSequence methodGet(final DataSchemaNode schemaNode) {
270         StringConcatenation builder = new StringConcatenation();
271         builder.append("<method name=\"GET\">");
272         builder.newLine();
273         builder.append("    ");
274         builder.append("<response>");
275         builder.newLine();
276         builder.append("        ");
277         builder.append(representation(schemaNode.getQName()), "        ");
278         builder.newLineIfNotEmpty();
279         builder.append("    ");
280         builder.append("</response>");
281         builder.newLine();
282         builder.append("</method>");
283         builder.newLine();
284         return builder;
285     }
286
287     private static CharSequence methodPut(final DataSchemaNode schemaNode) {
288         StringConcatenation builder = new StringConcatenation();
289         builder.append("<method name=\"PUT\">");
290         builder.newLine();
291         builder.append("    ");
292         builder.append("<request>");
293         builder.newLine();
294         builder.append("        ");
295         builder.append(representation(schemaNode.getQName()), "        ");
296         builder.newLineIfNotEmpty();
297         builder.append("    ");
298         builder.append("</request>");
299         builder.newLine();
300         builder.append("</method>");
301         builder.newLine();
302         return builder;
303     }
304
305     private static CharSequence mehodPost(final DataSchemaNode schemaNode) {
306         StringConcatenation builder = new StringConcatenation();
307         builder.append("<method name=\"POST\">");
308         builder.newLine();
309         builder.append("    ");
310         builder.append("<request>");
311         builder.newLine();
312         builder.append("        ");
313         builder.append(representation(schemaNode.getQName()), "        ");
314         builder.newLineIfNotEmpty();
315         builder.append("    ");
316         builder.append("</request>");
317         builder.newLine();
318         builder.append("</method>");
319         builder.newLine();
320         return builder;
321     }
322
323     private static CharSequence methodPostRpc() {
324         StringConcatenation builder = new StringConcatenation();
325         builder.append("<method name=\"POST\">");
326         builder.newLine();
327         builder.append("    ");
328         builder.append("<request>");
329         builder.newLine();
330         builder.append("    ");
331         builder.append("    ");
332         builder.append(representation(null, "input"), "        ");
333         builder.newLineIfNotEmpty();
334         builder.append("    ");
335         builder.append("</request>");
336         builder.newLine();
337         builder.append("    ");
338         builder.append("<response>");
339         builder.newLine();
340         builder.append("    ");
341         builder.append("    ");
342         builder.append(representation(null, "output"), "        ");
343         builder.newLineIfNotEmpty();
344         builder.append("    ");
345         builder.append("</response>");
346         builder.newLine();
347         builder.append("</method>");
348         builder.newLine();
349         return builder;
350     }
351
352     private static CharSequence methodDelete(final DataSchemaNode schemaNode) {
353         StringConcatenation builder = new StringConcatenation();
354         builder.append("<method name=\"DELETE\" />");
355         builder.newLine();
356         return builder;
357     }
358
359     private static CharSequence representation(final QName qname) {
360         return representation(qname.getNamespace(), qname.getLocalName());
361     }
362
363     private static CharSequence representation(final XMLNamespace prefix, final String name) {
364         StringConcatenation builder = new StringConcatenation();
365         builder.newLineIfNotEmpty();
366         builder.append("<representation mediaType=\"application/xml\" element=\"");
367         builder.append(name);
368         builder.append("\"/>");
369         builder.newLineIfNotEmpty();
370         builder.append("<representation mediaType=\"text/xml\" element=\"");
371         builder.append(name);
372         builder.append("\"/>");
373         builder.newLineIfNotEmpty();
374         builder.append("<representation mediaType=\"application/json\" element=\"");
375         builder.append(name);
376         builder.append("\"/>");
377         builder.newLineIfNotEmpty();
378         builder.append("<representation mediaType=\"application/yang.data+xml\" element=\"");
379         builder.append(name);
380         builder.append("\"/>");
381         builder.newLineIfNotEmpty();
382         builder.append("<representation mediaType=\"application/yang.data+json\" element=\"");
383         builder.append(name);
384         builder.append("\"/>");
385         builder.newLineIfNotEmpty();
386         return builder;
387     }
388
389     private static boolean isListOrContainer(final DataSchemaNode schemaNode) {
390         return schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode;
391     }
392 }