b9237e0bab0ae02acfff4ac42867a4125eb6a632
[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 java.io.BufferedWriter
11 import java.io.File
12 import java.io.OutputStreamWriter
13 import java.net.URI
14 import java.nio.charset.StandardCharsets
15 import java.util.ArrayList
16 import java.util.Collection
17 import java.util.HashSet
18 import java.util.List
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.sonatype.plexus.build.incremental.BuildContext
27
28 class WadlRestconfGenerator {
29
30     static val PATH_DELIMETER = '/'
31     val BuildContext buildContext;
32     val File path
33     var EffectiveModelContext context;
34     var List<DataSchemaNode> configData;
35     var List<DataSchemaNode> operationalData;
36     var Module module;
37     var List<LeafSchemaNode> pathListParams;
38
39     new(BuildContext buildContext, File targetPath) {
40         if (!targetPath.exists) targetPath.mkdirs
41         path = targetPath
42         this.buildContext = buildContext
43     }
44
45     def generate(EffectiveModelContext context, Collection<? extends Module> modules) {
46         val result = new HashSet;
47         this.context = context
48         for (module : modules) {
49             val dataContainers = module.childNodes.filter[it|it.listOrContainer]
50             if (!dataContainers.empty || !module.rpcs.nullOrEmpty) {
51                 configData = new ArrayList
52                 operationalData = new ArrayList
53
54                 for (data : dataContainers) {
55                     if (data.configuration) {
56                         configData.add(data)
57                     } else {
58                         operationalData.add(data)
59                     }
60                 }
61
62                 this.module = module
63                 val destination = new File(path, '''«module.name».wadl''')
64                 val fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
65                 val bw = new BufferedWriter(fw)
66                 bw.append(application);
67                 bw.close();
68                 fw.close();
69                 result.add(destination)
70             }
71         }
72         return result
73     }
74
75     private def application() '''
76         <?xml version="1.0"?>
77         <application xmlns="http://wadl.dev.java.net/2009/02" «module.importsAsNamespaces» xmlns:«module.prefix»="«module.namespace»">
78
79             «grammars»
80
81             «resources»
82         </application>
83     '''
84
85     private def importsAsNamespaces(Module module) '''
86         «FOR imprt : module.imports»
87             xmlns:«imprt.prefix»="«context.findModule(imprt.moduleName, imprt.revision).get.namespace»"
88         «ENDFOR»
89     '''
90
91     private def grammars() '''
92         <grammars>
93             <include href="«module.name».yang"/>
94             «FOR imprt : module.imports»
95                 <include href="«imprt.moduleName».yang"/>
96             «ENDFOR»
97         </grammars>
98     '''
99
100     private def resources() '''
101         <resources base="http://localhost:9998/restconf">
102             «resourceOperational»
103             «resourceConfig»
104             «resourceOperations»
105         </resources>
106     '''
107
108     private def resourceOperational() '''
109         «IF !operationalData.nullOrEmpty»
110             <resource path="operational">
111                 «FOR schemaNode : operationalData»
112                     «schemaNode.firstResource(false)»
113                 «ENDFOR»
114             </resource>
115         «ENDIF»
116     '''
117
118     private def resourceConfig() '''
119         «IF !configData.nullOrEmpty»
120             <resource path="config">
121                 «FOR schemaNode : configData»
122                     «schemaNode.mehodPost»
123                 «ENDFOR»
124                 «FOR schemaNode : configData»
125                     «schemaNode.firstResource(true)»
126                 «ENDFOR»
127             </resource>
128         «ENDIF»
129     '''
130
131     private def resourceOperations() '''
132         «IF !module.rpcs.nullOrEmpty»
133             <resource path="operations">
134                 «FOR rpc : module.rpcs»
135                     <resource path="«module.name»:«rpc.QName.localName»">
136                         «methodPostRpc(rpc.input !== null, rpc.output !== null)»
137                     </resource>
138                 «ENDFOR»
139             </resource>
140         «ENDIF»
141     '''
142
143     private def String firstResource(DataSchemaNode schemaNode, boolean config) '''
144         <resource path="«module.name»:«schemaNode.createPath»">
145             «resourceBody(schemaNode, config)»
146         </resource>
147     '''
148
149     private def String resource(DataSchemaNode schemaNode, boolean config) '''
150         <resource path="«schemaNode.createPath»">
151             «resourceBody(schemaNode, config)»
152         </resource>
153     '''
154
155     private def String createPath(DataSchemaNode schemaNode) {
156         pathListParams = new ArrayList
157         var StringBuilder path = new StringBuilder
158         path.append(schemaNode.QName.localName)
159         if (schemaNode instanceof ListSchemaNode) {
160             for (listKey : schemaNode.keyDefinition) {
161                 pathListParams.add((schemaNode as DataNodeContainer).getDataChildByName(listKey) as LeafSchemaNode)
162                 path.append(PATH_DELIMETER)
163                 path.append('{')
164                 path.append(listKey.localName)
165                 path.append('}')
166             }
167         }
168         return path.toString
169     }
170
171     private def String resourceBody(DataSchemaNode schemaNode, boolean config) '''
172         «IF !pathListParams.nullOrEmpty»
173             «resourceParams»
174         «ENDIF»
175         «schemaNode.methodGet»
176         «val children = (schemaNode as DataNodeContainer).childNodes.filter[it|it.listOrContainer]»
177         «IF config»
178             «schemaNode.methodDelete»
179             «schemaNode.mehodPut»
180             «FOR child : children»
181                 «child.mehodPost»
182             «ENDFOR»
183         «ENDIF»
184         «FOR child : children»
185             «child.resource(config)»
186         «ENDFOR»
187     '''
188
189     private def resourceParams() '''
190         «FOR pathParam : pathListParams»
191             «IF pathParam !== null»
192             «val type = pathParam.type.QName.localName»
193             <param required="true" style="template" name="«pathParam.QName.localName»" type="«type»"/>
194             «ENDIF»
195         «ENDFOR»
196     '''
197
198     private def methodGet(DataSchemaNode schemaNode) '''
199         <method name="GET">
200             <response>
201                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
202             </response>
203         </method>
204     '''
205
206     private def mehodPut(DataSchemaNode schemaNode) '''
207         <method name="PUT">
208             <request>
209                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
210             </request>
211         </method>
212     '''
213
214     private def mehodPost(DataSchemaNode schemaNode) '''
215         <method name="POST">
216             <request>
217                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
218             </request>
219         </method>
220     '''
221
222     private def methodPostRpc(boolean input, boolean output) '''
223         <method name="POST">
224             «IF input»
225             <request>
226                 «representation(null, "input")»
227             </request>
228             «ENDIF»
229             «IF output»
230             <response>
231                 «representation(null, "output")»
232             </response>
233             «ENDIF»
234         </method>
235     '''
236
237     private def methodDelete(DataSchemaNode schemaNode) '''
238         <method name="DELETE" />
239     '''
240
241     private def representation(URI prefix, String name) '''
242         «val elementData = name»
243         <representation mediaType="application/xml" element="«elementData»"/>
244         <representation mediaType="text/xml" element="«elementData»"/>
245         <representation mediaType="application/json" element="«elementData»"/>
246         <representation mediaType="application/yang.data+xml" element="«elementData»"/>
247         <representation mediaType="application/yang.data+json" element="«elementData»"/>
248     '''
249
250     private def boolean isListOrContainer(DataSchemaNode schemaNode) {
251         return (schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode)
252     }
253
254 }