2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.yang.wadl.generator
10 import static com.google.common.base.Preconditions.checkState;
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.io.BufferedWriter
15 import java.io.IOException
16 import java.io.OutputStreamWriter
17 import java.nio.charset.StandardCharsets
18 import java.util.ArrayList
19 import java.util.Collection
20 import java.util.HashSet
22 import org.opendaylight.yangtools.yang.common.XMLNamespace
23 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
26 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
29 import org.opendaylight.yangtools.yang.model.api.Module
30 import org.slf4j.Logger
31 import org.slf4j.LoggerFactory
32 import org.sonatype.plexus.build.incremental.BuildContext
34 class WadlRestconfGenerator {
36 static val Logger LOG = LoggerFactory.getLogger(WadlRestconfGenerator)
38 static val PATH_DELIMETER = '/'
39 val BuildContext buildContext;
41 var EffectiveModelContext context;
42 var List<DataSchemaNode> configData;
43 var List<DataSchemaNode> operationalData;
45 var List<LeafSchemaNode> pathListParams;
47 new(BuildContext buildContext, File targetPath) {
48 if (!targetPath.exists) {
49 checkState(targetPath.mkdirs, "Unable to create directory: %s", targetPath);
52 this.buildContext = buildContext
55 def generate(EffectiveModelContext context, Collection<? extends Module> modules) {
56 val result = new HashSet;
57 this.context = context
58 for (module : modules) {
59 val dataContainers = module.childNodes.filter[it|it.listOrContainer]
60 if (!dataContainers.empty || !module.rpcs.nullOrEmpty) {
61 configData = new ArrayList
62 operationalData = new ArrayList
64 for (data : dataContainers) {
65 if (data.configuration) {
68 operationalData.add(data)
73 val destination = new File(path, '''«module.name».wadl''')
74 var OutputStreamWriter fw
77 fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
78 bw = new BufferedWriter(fw)
79 bw.append(application);
80 } catch (IOException e) {
81 LOG.error("Failed to emit file {}", destination, e);
90 result.add(destination)
96 private def application() '''
98 <application xmlns="http://wadl.dev.java.net/2009/02" «module.importsAsNamespaces» xmlns:«module.prefix»="«module.namespace»">
106 private def importsAsNamespaces(Module module) '''
107 «FOR imprt : module.imports»
108 xmlns:«imprt.prefix»="«context.findModule(imprt.moduleName, imprt.revision).get.namespace»"
112 private def grammars() '''
114 <include href="«module.name».yang"/>
115 «FOR imprt : module.imports»
116 <include href="«imprt.moduleName».yang"/>
121 private def resources() '''
122 <resources base="http://localhost:9998/restconf">
123 «resourceOperational»
129 private def resourceOperational() '''
130 «IF !operationalData.nullOrEmpty»
131 <resource path="operational">
132 «FOR schemaNode : operationalData»
133 «schemaNode.firstResource(false)»
139 private def resourceConfig() '''
140 «IF !configData.nullOrEmpty»
141 <resource path="config">
142 «FOR schemaNode : configData»
143 «schemaNode.mehodPost»
145 «FOR schemaNode : configData»
146 «schemaNode.firstResource(true)»
152 private def resourceOperations() '''
153 «IF !module.rpcs.nullOrEmpty»
154 <resource path="operations">
155 «FOR rpc : module.rpcs»
156 <resource path="«module.name»:«rpc.QName.localName»">
157 «methodPostRpc(rpc.input !== null, rpc.output !== null)»
164 private def String firstResource(DataSchemaNode schemaNode, boolean config) '''
165 <resource path="«module.name»:«schemaNode.createPath»">
166 «resourceBody(schemaNode, config)»
170 private def String resource(DataSchemaNode schemaNode, boolean config) '''
171 <resource path="«schemaNode.createPath»">
172 «resourceBody(schemaNode, config)»
176 private def String createPath(DataSchemaNode schemaNode) {
177 pathListParams = new ArrayList
178 var StringBuilder path = new StringBuilder
179 path.append(schemaNode.QName.localName)
180 if (schemaNode instanceof ListSchemaNode) {
181 for (listKey : schemaNode.keyDefinition) {
182 pathListParams.add((schemaNode as DataNodeContainer).getDataChildByName(listKey) as LeafSchemaNode)
183 path.append(PATH_DELIMETER)
185 path.append(listKey.localName)
192 private def String resourceBody(DataSchemaNode schemaNode, boolean config) '''
193 «IF !pathListParams.nullOrEmpty»
196 «schemaNode.methodGet»
197 «val children = (schemaNode as DataNodeContainer).childNodes.filter[it|it.listOrContainer]»
199 «schemaNode.methodDelete»
200 «schemaNode.mehodPut»
201 «FOR child : children»
205 «FOR child : children»
206 «child.resource(config)»
210 private def resourceParams() '''
211 «FOR pathParam : pathListParams»
212 «IF pathParam !== null»
213 «val type = pathParam.type.QName.localName»
214 <param required="true" style="template" name="«pathParam.QName.localName»" type="«type»"/>
219 private def methodGet(DataSchemaNode schemaNode) '''
222 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
227 private def mehodPut(DataSchemaNode schemaNode) '''
230 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
235 private def mehodPost(DataSchemaNode schemaNode) '''
238 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
243 private def methodPostRpc(boolean input, boolean output) '''
247 «representation(null, "input")»
252 «representation(null, "output")»
258 private def methodDelete(DataSchemaNode schemaNode) '''
259 <method name="DELETE" />
262 private def representation(XMLNamespace prefix, String name) '''
263 «val elementData = name»
264 <representation mediaType="application/xml" element="«elementData»"/>
265 <representation mediaType="text/xml" element="«elementData»"/>
266 <representation mediaType="application/json" element="«elementData»"/>
267 <representation mediaType="application/yang.data+xml" element="«elementData»"/>
268 <representation mediaType="application/yang.data+json" element="«elementData»"/>
271 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
272 justification = "https://github.com/spotbugs/spotbugs/issues/811")
273 private def boolean isListOrContainer(DataSchemaNode schemaNode) {
274 return (schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode)