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»">
101 <include href="«module.name».yang"/>
102 «FOR imprt : module.imports»
103 <include href="«imprt.moduleName».yang"/>
107 <resources base="http://localhost:9998/restconf">
108 «IF !operationalData.nullOrEmpty»
109 <resource path="operational">
110 «FOR schemaNode : operationalData»
111 «schemaNode.firstResource(false)»
115 «IF !configData.nullOrEmpty»
116 <resource path="config">
117 «FOR schemaNode : configData»
118 «schemaNode.mehodPost»
120 «FOR schemaNode : configData»
121 «schemaNode.firstResource(true)»
125 «IF !module.rpcs.nullOrEmpty»
126 <resource path="operations">
127 «FOR rpc : module.rpcs»
128 <resource path="«module.name»:«rpc.QName.localName»">
129 «methodPostRpc(rpc.input !== null, rpc.output !== null)»
138 private def importsAsNamespaces(Module module) '''
139 «FOR imprt : module.imports»
140 xmlns:«imprt.prefix»="«context.findModule(imprt.moduleName, imprt.revision).get.namespace»"
144 private def String firstResource(DataSchemaNode schemaNode, boolean config) '''
145 <resource path="«module.name»:«schemaNode.createPath»">
146 «resourceBody(schemaNode, config)»
150 private def String resource(DataSchemaNode schemaNode, boolean config) '''
151 <resource path="«schemaNode.createPath»">
152 «resourceBody(schemaNode, config)»
156 private def String createPath(DataSchemaNode schemaNode) {
157 pathListParams = new ArrayList
158 var StringBuilder path = new StringBuilder
159 path.append(schemaNode.QName.localName)
160 if (schemaNode instanceof ListSchemaNode) {
161 for (listKey : schemaNode.keyDefinition) {
162 pathListParams.add((schemaNode as DataNodeContainer).getDataChildByName(listKey) as LeafSchemaNode)
163 path.append(PATH_DELIMETER)
165 path.append(listKey.localName)
172 private def String resourceBody(DataSchemaNode schemaNode, boolean config) '''
173 «IF !pathListParams.nullOrEmpty»
176 «schemaNode.methodGet»
177 «val children = (schemaNode as DataNodeContainer).childNodes.filter[it|it.listOrContainer]»
179 «schemaNode.methodDelete»
180 «schemaNode.mehodPut»
181 «FOR child : children»
185 «FOR child : children»
186 «child.resource(config)»
190 private def resourceParams() '''
191 «FOR pathParam : pathListParams»
192 «IF pathParam !== null»
193 «val type = pathParam.type.QName.localName»
194 <param required="true" style="template" name="«pathParam.QName.localName»" type="«type»"/>
199 private def methodGet(DataSchemaNode schemaNode) '''
202 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
207 private def mehodPut(DataSchemaNode schemaNode) '''
210 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
215 private def mehodPost(DataSchemaNode schemaNode) '''
218 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
223 private def methodPostRpc(boolean input, boolean output) '''
227 «representation(null, "input")»
232 «representation(null, "output")»
238 private def methodDelete(DataSchemaNode schemaNode) '''
239 <method name="DELETE" />
242 private def representation(XMLNamespace prefix, String name) '''
243 «val elementData = name»
244 <representation mediaType="application/xml" element="«elementData»"/>
245 <representation mediaType="text/xml" element="«elementData»"/>
246 <representation mediaType="application/json" element="«elementData»"/>
247 <representation mediaType="application/yang.data+xml" element="«elementData»"/>
248 <representation mediaType="application/yang.data+json" element="«elementData»"/>
251 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
252 justification = "https://github.com/spotbugs/spotbugs/issues/811")
253 private def boolean isListOrContainer(DataSchemaNode schemaNode) {
254 return (schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode)