Bump to odlparent-9.0.0/yangtools-7.0.1-SNAPSHOT
[mdsal.git] / binding / maven-sal-api-gen-plugin / src / main / java / org / opendaylight / mdsal / binding / yang / wadl / generator / WadlRestconfGenerator.xtend
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.mdsal.binding.yang.wadl.generator
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.io.BufferedWriter
14 import java.io.File
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
21 import java.util.List
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
33
34 class WadlRestconfGenerator {
35
36     static val Logger LOG = LoggerFactory.getLogger(WadlRestconfGenerator)
37
38     static val PATH_DELIMETER = '/'
39     val BuildContext buildContext;
40     val File path
41     var EffectiveModelContext context;
42     var List<DataSchemaNode> configData;
43     var List<DataSchemaNode> operationalData;
44     var Module module;
45     var List<LeafSchemaNode> pathListParams;
46
47     new(BuildContext buildContext, File targetPath) {
48         if (!targetPath.exists) {
49             checkState(targetPath.mkdirs, "Unable to create directory: %s", targetPath);
50         }
51         path = targetPath
52         this.buildContext = buildContext
53     }
54
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
63
64                 for (data : dataContainers) {
65                     if (data.configuration) {
66                         configData.add(data)
67                     } else {
68                         operationalData.add(data)
69                     }
70                 }
71
72                 this.module = module
73                 val destination = new File(path, '''«module.name».wadl''')
74                 var OutputStreamWriter fw
75                 var BufferedWriter bw
76                 try {
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);
82                 } finally {
83                     if (bw !== null) {
84                         bw.close();
85                     }
86                     if (fw !== null) {
87                         fw.close();
88                     }
89                 }
90                 result.add(destination)
91             }
92         }
93         return result
94     }
95
96     private def application() '''
97         <?xml version="1.0"?>
98         <application xmlns="http://wadl.dev.java.net/2009/02" «module.importsAsNamespaces» xmlns:«module.prefix»="«module.namespace»">
99
100             «grammars»
101
102             «resources»
103         </application>
104     '''
105
106     private def importsAsNamespaces(Module module) '''
107         «FOR imprt : module.imports»
108             xmlns:«imprt.prefix»="«context.findModule(imprt.moduleName, imprt.revision).get.namespace»"
109         «ENDFOR»
110     '''
111
112     private def grammars() '''
113         <grammars>
114             <include href="«module.name».yang"/>
115             «FOR imprt : module.imports»
116                 <include href="«imprt.moduleName».yang"/>
117             «ENDFOR»
118         </grammars>
119     '''
120
121     private def resources() '''
122         <resources base="http://localhost:9998/restconf">
123             «resourceOperational»
124             «resourceConfig»
125             «resourceOperations»
126         </resources>
127     '''
128
129     private def resourceOperational() '''
130         «IF !operationalData.nullOrEmpty»
131             <resource path="operational">
132                 «FOR schemaNode : operationalData»
133                     «schemaNode.firstResource(false)»
134                 «ENDFOR»
135             </resource>
136         «ENDIF»
137     '''
138
139     private def resourceConfig() '''
140         «IF !configData.nullOrEmpty»
141             <resource path="config">
142                 «FOR schemaNode : configData»
143                     «schemaNode.mehodPost»
144                 «ENDFOR»
145                 «FOR schemaNode : configData»
146                     «schemaNode.firstResource(true)»
147                 «ENDFOR»
148             </resource>
149         «ENDIF»
150     '''
151
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)»
158                     </resource>
159                 «ENDFOR»
160             </resource>
161         «ENDIF»
162     '''
163
164     private def String firstResource(DataSchemaNode schemaNode, boolean config) '''
165         <resource path="«module.name»:«schemaNode.createPath»">
166             «resourceBody(schemaNode, config)»
167         </resource>
168     '''
169
170     private def String resource(DataSchemaNode schemaNode, boolean config) '''
171         <resource path="«schemaNode.createPath»">
172             «resourceBody(schemaNode, config)»
173         </resource>
174     '''
175
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)
184                 path.append('{')
185                 path.append(listKey.localName)
186                 path.append('}')
187             }
188         }
189         return path.toString
190     }
191
192     private def String resourceBody(DataSchemaNode schemaNode, boolean config) '''
193         «IF !pathListParams.nullOrEmpty»
194             «resourceParams»
195         «ENDIF»
196         «schemaNode.methodGet»
197         «val children = (schemaNode as DataNodeContainer).childNodes.filter[it|it.listOrContainer]»
198         «IF config»
199             «schemaNode.methodDelete»
200             «schemaNode.mehodPut»
201             «FOR child : children»
202                 «child.mehodPost»
203             «ENDFOR»
204         «ENDIF»
205         «FOR child : children»
206             «child.resource(config)»
207         «ENDFOR»
208     '''
209
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»"/>
215             «ENDIF»
216         «ENDFOR»
217     '''
218
219     private def methodGet(DataSchemaNode schemaNode) '''
220         <method name="GET">
221             <response>
222                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
223             </response>
224         </method>
225     '''
226
227     private def mehodPut(DataSchemaNode schemaNode) '''
228         <method name="PUT">
229             <request>
230                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
231             </request>
232         </method>
233     '''
234
235     private def mehodPost(DataSchemaNode schemaNode) '''
236         <method name="POST">
237             <request>
238                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
239             </request>
240         </method>
241     '''
242
243     private def methodPostRpc(boolean input, boolean output) '''
244         <method name="POST">
245             «IF input»
246             <request>
247                 «representation(null, "input")»
248             </request>
249             «ENDIF»
250             «IF output»
251             <response>
252                 «representation(null, "output")»
253             </response>
254             «ENDIF»
255         </method>
256     '''
257
258     private def methodDelete(DataSchemaNode schemaNode) '''
259         <method name="DELETE" />
260     '''
261
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»"/>
269     '''
270
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)
275     }
276
277 }