2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2021 PANTHEON.tech, s.r.o.
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
9 package org.opendaylight.netconf.restconf.wadl.generator;
11 import static java.util.Objects.requireNonNull;
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;
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;
34 private List<LeafSchemaNode> pathListParams;
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);
44 operationalData.add(child);
50 public CharSequence body() {
51 if (!module.getRpcs().isEmpty() || !configData.isEmpty() || !operationalData.isEmpty()) {
57 private CharSequence application() {
58 StringConcatenation builder = new StringConcatenation();
59 builder.append("<?xml version=\"1.0\"?>");
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();
71 builder.append("<grammars>");
74 builder.append("<include href=\"");
75 builder.append(module.getName(), " ");
76 builder.append(".yang\"/>");
77 builder.newLineIfNotEmpty();
79 for (final ModuleImport imprt : module.getImports()) {
81 builder.append("<include href=\"");
82 builder.append(imprt.getModuleName(), " ");
83 builder.append(".yang\"/>");
84 builder.newLineIfNotEmpty();
88 builder.append("</grammars>");
92 builder.append("<resources base=\"http://localhost:9998/restconf\">");
95 if (!operationalData.isEmpty()) {
97 builder.append("<resource path=\"operational\">");
99 for (final DataSchemaNode schemaNode : operationalData) {
102 builder.append(firstResource(schemaNode, false), " ");
103 builder.newLineIfNotEmpty();
106 builder.append("</resource>");
110 if (!configData.isEmpty()) {
112 builder.append("<resource path=\"config\">");
115 for (final DataSchemaNode schemaNode : configData) {
118 builder.append(mehodPost(schemaNode), " ");
119 builder.newLineIfNotEmpty();
121 for (final DataSchemaNode schemaNode : configData) {
124 builder.append(firstResource(schemaNode, true), " ");
125 builder.newLineIfNotEmpty();
128 builder.append("</resource>");
132 final var rpcs = module.getRpcs();
133 if (!rpcs.isEmpty()) {
135 builder.append("<resource path=\"operations\">");
137 for (var rpc : rpcs) {
140 builder.append("<resource path=\"");
141 builder.append(module.getName(), " ");
143 builder.append(rpc.getQName().getLocalName(), " ");
144 builder.append("\">");
145 builder.newLineIfNotEmpty();
149 builder.append(methodPostRpc(), " ");
150 builder.newLineIfNotEmpty();
153 builder.append("</resource>");
157 builder.append("</resource>");
162 builder.append("</resources>");
164 builder.append("</application>");
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();
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());
187 builder.append(createPath(schemaNode));
188 builder.append("\">");
189 builder.newLineIfNotEmpty();
191 builder.append(resourceBody(schemaNode, config), " ");
192 builder.newLineIfNotEmpty();
193 builder.append("</resource>");
195 return builder.toString();
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();
205 builder.append(resourceBody(schemaNode, config), " ");
206 builder.newLineIfNotEmpty();
207 builder.append("</resource>");
209 return builder.toString();
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('}');
222 return path.toString();
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();
231 builder.append(methodGet(schemaNode));
232 builder.newLineIfNotEmpty();
233 builder.newLineIfNotEmpty();
234 final var children = Iterables.filter(((DataNodeContainer) schemaNode).getChildNodes(),
235 WadlTemplate::isListOrContainer);
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();
246 for (final DataSchemaNode child : children) {
247 builder.append(resource(child, config));
248 builder.newLineIfNotEmpty();
250 return builder.toString();
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();
269 private static CharSequence methodGet(final DataSchemaNode schemaNode) {
270 StringConcatenation builder = new StringConcatenation();
271 builder.append("<method name=\"GET\">");
274 builder.append("<response>");
277 builder.append(representation(schemaNode.getQName()), " ");
278 builder.newLineIfNotEmpty();
280 builder.append("</response>");
282 builder.append("</method>");
287 private static CharSequence methodPut(final DataSchemaNode schemaNode) {
288 StringConcatenation builder = new StringConcatenation();
289 builder.append("<method name=\"PUT\">");
292 builder.append("<request>");
295 builder.append(representation(schemaNode.getQName()), " ");
296 builder.newLineIfNotEmpty();
298 builder.append("</request>");
300 builder.append("</method>");
305 private static CharSequence mehodPost(final DataSchemaNode schemaNode) {
306 StringConcatenation builder = new StringConcatenation();
307 builder.append("<method name=\"POST\">");
310 builder.append("<request>");
313 builder.append(representation(schemaNode.getQName()), " ");
314 builder.newLineIfNotEmpty();
316 builder.append("</request>");
318 builder.append("</method>");
323 private static CharSequence methodPostRpc() {
324 StringConcatenation builder = new StringConcatenation();
325 builder.append("<method name=\"POST\">");
328 builder.append("<request>");
332 builder.append(representation(null, "input"), " ");
333 builder.newLineIfNotEmpty();
335 builder.append("</request>");
338 builder.append("<response>");
342 builder.append(representation(null, "output"), " ");
343 builder.newLineIfNotEmpty();
345 builder.append("</response>");
347 builder.append("</method>");
352 private static CharSequence methodDelete(final DataSchemaNode schemaNode) {
353 StringConcatenation builder = new StringConcatenation();
354 builder.append("<method name=\"DELETE\" />");
359 private static CharSequence representation(final QName qname) {
360 return representation(qname.getNamespace(), qname.getLocalName());
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();
389 private static boolean isListOrContainer(final DataSchemaNode schemaNode) {
390 return schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode;