Fix WadlRestconfGenerator checkstyle
[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.net.URI
18 import java.nio.charset.StandardCharsets
19 import java.util.ArrayList
20 import java.util.Collection
21 import java.util.HashSet
22 import java.util.List
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(URI 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 }