BUG-7013: do not rely on default character encoding
[mdsal.git] / binding / maven-sal-api-gen-plugin / src / main / java / org / opendaylight / yangtools / 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.yangtools.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.HashSet
17 import java.util.List
18 import java.util.Set
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.LeafSchemaNode
23 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
24 import org.opendaylight.yangtools.yang.model.api.Module
25 import org.opendaylight.yangtools.yang.model.api.SchemaContext
26 import org.sonatype.plexus.build.incremental.BuildContext
27 import org.sonatype.plexus.build.incremental.DefaultBuildContext
28
29 class WadlRestconfGenerator {
30         
31         File path
32         static val BuildContext CTX = new DefaultBuildContext();
33         static val PATH_DELIMETER = '/'
34         var SchemaContext context;
35         var List<DataSchemaNode> configData;
36         var List<DataSchemaNode> operationalData;
37         var Module module;
38         var List<LeafSchemaNode> pathListParams;
39
40         new(File targetPath) {
41                 if (!targetPath.exists) targetPath.mkdirs
42                 path = targetPath
43         }
44
45         def generate(SchemaContext context, Set<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(CTX.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.findModuleByName(imprt.moduleName, imprt.revision).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 }