965fd75235dac8705088b8905167fb41b72e756d
[mdsal.git] / code-generator / maven-sal-api-gen-plugin / src / main / java / org / opendaylight / yangtools / yang / unified / doc / generator / GeneratorImpl.xtend
1 package org.opendaylight.yangtools.yang.unified.doc.generator
2
3 import org.opendaylight.yangtools.yang.model.api.SchemaContext
4 import java.io.File
5 import java.util.Set
6 import org.opendaylight.yangtools.yang.model.api.Module
7 import java.io.IOException
8 import java.util.HashSet
9 import java.io.FileWriter
10 import java.io.BufferedWriter
11 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
12 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
13 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
14 import org.opendaylight.yangtools.yang.model.api.SchemaNode
15 import org.opendaylight.yangtools.yang.model.util.ExtendedType
16 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
17 import java.text.SimpleDateFormat
18 import java.util.Collection
19 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
20 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
21 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
22 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
23 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
24 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
25 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
26 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
27 import org.slf4j.LoggerFactory
28 import org.slf4j.Logger
29 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
30 import java.util.List
31 import org.opendaylight.yangtools.yang.common.QName
32 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
33 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
34
35 class GeneratorImpl {
36
37     File path
38     static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
39     static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
40
41
42     def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
43         path = targetPath;
44         path.mkdirs();
45         val it = new HashSet;
46         for (module : modulesToGen) {
47             add(module.generateDocumentation());
48         }
49         return it;
50     }
51
52     def generateDocumentation(Module module) {
53         val destination = new File(path, '''«module.name».html''')
54         try {
55             val fw = new FileWriter(destination)
56             destination.createNewFile();
57             val bw = new BufferedWriter(fw)
58
59             bw.append(module.generate);
60             bw.close();
61             fw.close();
62         } catch (IOException e) {
63             LOG.error(e.getMessage());
64         }
65         return destination;
66     }
67
68     def generate(Module module) '''
69         <!DOCTYPE html>
70         <html lang="en">
71           <head>
72             <title>«module.name»</title>
73           </head>
74           <body>
75             «module.body»
76           </body>
77         </html>
78     '''
79
80     def body(Module module) '''
81         «header(module)»
82
83         «typeDefinitions(module)»
84
85         «groupings(module)»
86
87         «dataStore(module)»
88
89         «notifications(module)»
90
91         «augmentations(module)»
92
93         «rpcs(module)»
94
95         «extensions(module)»
96
97     '''
98
99
100     def typeDefinitions(Module module) {
101         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
102         if (typedefs.empty) {
103             return '';
104         }
105         return '''
106             <h2>Type Definitions</h2>
107             «list(typedefs)»
108
109             «FOR typedef : typedefs»
110             «typeDefinition(typedef)»
111             «ENDFOR»
112         '''
113     }
114
115     private def CharSequence typeDefinition(TypeDefinition<?> type) '''
116         «header(type)»
117         «body(type)»
118         «restrictions(type)»
119     '''
120
121     def groupings(Module module) {
122         if (module.groupings.empty) {
123             return '';
124         }
125         return '''
126             <h2>Groupings</h2>
127             «list(module.groupings)»
128
129             «FOR grouping : module.groupings»
130             «headerAndBody(grouping)»
131             «ENDFOR»
132         '''
133     }
134
135     def dataStore(Module module) {
136         if (module.childNodes.empty) {
137             return '';
138         }
139         return '''
140             <h2>Datastore Structure</h2>
141             «tree(module)»
142         '''
143     }
144
145     def augmentations(Module module) {
146         if (module.augmentations.empty) {
147             return '';
148         }
149         return '''
150             <h2>Augmentations</h2>
151
152             <ul>
153             «FOR augment : module.augmentations»
154                 <li>
155                     augment
156                     «augment.tree»
157                 </li>
158             «ENDFOR»
159             </ul>
160         '''
161     }
162
163     def notifications(Module module) {
164         val Set<NotificationDefinition> notificationdefs = module.notifications
165         if (notificationdefs.empty) {
166             return '';
167         }
168         return '''
169             <h2>Notifications</h2>
170
171             <ul>
172             «FOR notificationdef : notificationdefs»
173                 <li>
174                     «notificationdef.nodeName»
175                     «notificationdef.tree»
176                 </li>
177             «ENDFOR»
178             </ul>
179         '''
180     }
181
182     def rpcs(Module module) {
183         if (module.rpcs.empty) {
184             return '';
185         }
186         return '''
187             <h2>RPC Definitions</h2>
188
189             <ul>
190             «FOR rpc : module.rpcs»
191                 <li>
192                     «rpc.nodeName»
193                     «rpc.tree»
194                 </li>
195             «ENDFOR»
196             </ul>
197         '''
198     }
199
200     def extensions(Module module) {
201         if (module.extensionSchemaNodes.empty) {
202             return '';
203         }
204         return '''
205             <h2>Extensions</h2>
206
207             <ul>
208             «FOR ext : module.extensionSchemaNodes»
209                 <li>
210                     «ext.nodeName»
211                     «ext.tree»
212                 </li>
213             «ENDFOR»
214             </ul>
215         '''
216     }
217
218     def CharSequence headerAndBody(SchemaNode node) '''
219         «header(node)»
220         «body(node)»
221     '''
222
223     def header(SchemaNode type) '''
224         <h3>«type.QName.localName»</h3>
225     '''
226
227     def body(SchemaNode definition) '''
228
229         «paragraphs(definition.description)»
230
231         «definition.reference»
232     '''
233
234
235     def list(Set<? extends SchemaNode> definitions) '''
236         <ul>
237         «FOR nodeDef : definitions» 
238             <li>«nodeDef.QName.localName»</li>
239         «ENDFOR»
240         </ul>
241     '''
242
243     def header(Module module) '''
244         <h1>«module.name»</h1>
245         
246         <h2>Base Information</h2>
247         <dl>
248             <dt>Prefix</dt>
249             <dd><pre>«module.prefix»</pre></dd>
250             <dt>Namespace</dt>
251             <dd><pre>«module.namespace»</pre></dd>
252             <dt>Revision</dt>
253             <dd>«REVISION_FORMAT.format(module.revision)»</dd>
254             
255             «FOR imp : module.imports BEFORE "<dt>Imports</dt>" »
256                 <dd>«pre(imp.prefix)» = «pre(imp.moduleName)»</dd>
257             «ENDFOR»
258         </dl>
259     '''
260
261
262     def process(Module module) {
263         throw new UnsupportedOperationException("TODO: auto-generated method stub")
264     }
265
266
267
268     /* #################### TREE STRUCTURE #################### */
269     def dispatch CharSequence tree(Module module) '''
270         «strong("module " + module.name)»
271         «module.childNodes.childrenToTree»
272     '''
273
274     def dispatch CharSequence tree(DataNodeContainer node) '''
275         «IF node instanceof SchemaNode»
276             «(node as SchemaNode).nodeName»
277         «ENDIF»
278         «node.childNodes.childrenToTree»
279     '''
280
281     def dispatch CharSequence tree(DataSchemaNode node) '''
282         «node.nodeName»
283     '''
284
285     def dispatch CharSequence tree(ListSchemaNode node) '''
286         «node.nodeName»
287         «node.childNodes.childrenToTree»
288     '''
289
290     private def CharSequence childrenToTree(Collection<DataSchemaNode> childNodes) '''
291         «IF childNodes !== null && !childNodes.empty»
292             <ul>
293             «FOR child : childNodes»
294                 <li>
295                     «child.tree»
296                 </li>
297             «ENDFOR»
298             </ul>
299         «ENDIF»
300     '''
301
302     def listKeys(ListSchemaNode node) '''
303         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
304     '''
305
306     def dispatch CharSequence tree(AugmentationSchema augment) '''
307         <ul>
308             «listItem("Description", augment.description)»
309             «listItem("Reference", augment.reference)»
310             «IF augment.whenCondition !== null»
311                 «listItem("When", augment.whenCondition.toString)»
312             «ENDIF»
313             <li>
314                 Path «augment.targetPath.path.pathToTree»
315             </li>
316             <li>
317                 Child nodes
318                 «augment.childNodes.childrenToTree»
319             </li>
320         </ul>
321     '''
322
323     private def CharSequence pathToTree(List<QName> path) '''
324         «IF path !== null && !path.empty»
325             <ul>
326             «FOR pathElement : path»
327                 <li>
328                     «pathElement.namespace» «pathElement.localName»
329                 </li>
330             «ENDFOR»
331             </ul>
332         «ENDIF»
333     '''
334
335     def dispatch CharSequence tree(NotificationDefinition notification) '''
336         <ul>
337             «listItem("Description", notification.description)»
338             «listItem("Reference", notification.reference)»
339             <li>
340                 Child nodes
341                 «notification.childNodes.childrenToTree»
342             </li>
343         </ul>
344     '''
345
346     def dispatch CharSequence tree(RpcDefinition rpc) '''
347         <ul>
348             «listItem("Description", rpc.description)»
349             «listItem("Reference", rpc.reference)»
350             <li>
351                 «rpc.input.tree»
352             </li>
353             <li>
354                 «rpc.output.tree»
355             </li>
356         </ul>
357     '''
358
359     def dispatch CharSequence tree(ExtensionDefinition ext) '''
360         <ul>
361             «listItem("Description", ext.description)»
362             «listItem("Reference", ext.reference)»
363             «listItem("Argument", ext.argument)»
364         </ul>
365     '''
366
367
368     /* #################### RESTRICTIONS #################### */
369     private def restrictions(TypeDefinition<?> type) '''
370         «type.toLength»
371         «type.toRange»
372     '''
373
374     def dispatch toLength(TypeDefinition<?> type) {
375     }
376
377     def dispatch toLength(BinaryTypeDefinition type) '''
378         «type.lengthConstraints.toLengthStmt»
379     '''
380
381     def dispatch toLength(StringTypeDefinition type) '''
382         «type.lengthConstraints.toLengthStmt»
383     '''
384
385     def dispatch toLength(ExtendedType type) '''
386         «type.lengthConstraints.toLengthStmt»
387     '''
388
389     def dispatch toRange(TypeDefinition<?> type) {
390     }
391
392     def dispatch toRange(DecimalTypeDefinition type) '''
393         «type.rangeConstraints.toRangeStmt»
394     '''
395
396     def dispatch toRange(IntegerTypeDefinition type) '''
397         «type.rangeConstraints.toRangeStmt»
398     '''
399
400     def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
401         «type.rangeConstraints.toRangeStmt»
402     '''
403
404     def dispatch toRange(ExtendedType type) '''
405         «type.rangeConstraints.toRangeStmt»
406     '''
407
408     def toLengthStmt(Collection<LengthConstraint> lengths) '''
409         «IF lengths != null && !lengths.empty»
410             «strong("Length restrictions")»
411             <ul>
412             «FOR length : lengths»
413                 <li>
414                 «IF length.min == length.max»
415                     «length.min»
416                 «ELSE»
417                     &lt;«length.min», «length.max»&gt;
418                 «ENDIF»
419                 </li>
420             «ENDFOR»
421             </ul>
422         «ENDIF»
423     '''
424
425     def toRangeStmt(Collection<RangeConstraint> ranges) '''
426         «IF ranges != null && !ranges.empty»
427             «strong("Range restrictions")»
428             <ul>
429             «FOR range : ranges»
430                 <li>
431                 «IF range.min == range.max»
432                     «range.min»
433                 «ELSE»
434                     &lt;«range.min», «range.max»&gt;
435                 «ENDIF»
436                 </li>
437             «ENDFOR»
438             </ul>
439         «ENDIF»
440     '''
441
442
443
444     /* #################### UTILITY #################### */
445     def strong(String str) '''
446         <strong>«str»</strong>
447     '''
448
449     def italic(String str) '''
450         <i>«str»</i>
451     '''
452
453     def pre(String string) '''<pre>«string»</pre>'''
454
455     def paragraphs(String body) '''
456         <p>«body»</p>
457     '''
458
459     def listItem(String name, String value) '''
460         «IF value !== null && !value.empty»
461             <li>
462                 «name»
463                 <ul>
464                     <li>
465                         «value»
466                     </li>
467                 </ul>
468             </li>
469         «ENDIF»
470     '''
471
472     def dispatch addedByInfo(SchemaNode node) '''
473     '''
474
475     def dispatch addedByInfo(DataSchemaNode node) '''
476         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
477     '''
478
479     def dispatch isAddedBy(SchemaNode node) {
480         return false;
481     }
482
483     def dispatch isAddedBy(DataSchemaNode node) {
484         if (node.augmenting || node.addedByUses) {
485             return true
486         } else {
487             return false;
488         }
489     }
490
491     def nodeName(SchemaNode node) '''
492         «IF node.isAddedBy»
493             «italic(node.QName.localName)»«node.addedByInfo»
494         «ELSE»
495             «strong(node.QName.localName)»«node.addedByInfo»
496         «ENDIF»
497     '''
498
499     def nodeName(ListSchemaNode node) '''
500         «IF node.isAddedBy»
501             «italic(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
502         «ELSE»
503             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
504         «ENDIF»
505     '''
506
507 }