Enhancement of documentation generator.
[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         «identities(module)»
86
87         «groupings(module)»
88
89         «dataStore(module)»
90
91         «notifications(module)»
92
93         «augmentations(module)»
94
95         «rpcs(module)»
96
97         «extensions(module)»
98
99         «features(module)»
100
101     '''
102
103
104     def typeDefinitions(Module module) {
105         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
106         if (typedefs.empty) {
107             return '';
108         }
109         return '''
110             <h2>Type Definitions</h2>
111             <ul>
112             «FOR typedef : typedefs»
113                 <li>
114                     «strong("typedef " + typedef.QName.localName)»
115                     <ul>
116                     «typedef.descAndRef»
117                     «typedef.restrictions»
118                     </ul>
119                 </li>
120             «ENDFOR»
121             </ul>
122         '''
123     }
124
125     private def identities(Module module) {
126         if (module.identities.empty) {
127             return '';
128         }
129         return '''
130             <h2>Identities</h2>
131             <ul>
132             «FOR identity : module.identities»
133                 <li>
134                     «strong("identity " + identity.QName.localName)»
135                     <ul>
136                     «identity.descAndRef»
137                     «IF identity.baseIdentity != null»
138                         «listItem("base", identity.baseIdentity.QName.localName)»
139                     «ENDIF»
140                     </ul>
141                 </li>
142             «ENDFOR»
143             </ul>
144         '''
145     }
146
147     private def groupings(Module module) {
148         if (module.groupings.empty) {
149             return '';
150         }
151         return '''
152             <h2>Groupings</h2>
153             <ul>
154             «FOR grouping : module.groupings»
155                 <li>
156                     «strong("grouping " + grouping.QName.localName)»
157                     <ul>
158                         «grouping.descAndRef»
159                     </ul>
160                 </li>
161             «ENDFOR»
162             </ul>
163         '''
164     }
165
166     def dataStore(Module module) {
167         if (module.childNodes.empty) {
168             return '';
169         }
170         return '''
171             <h2>Datastore Structure</h2>
172             «tree(module)»
173         '''
174     }
175
176     def augmentations(Module module) {
177         if (module.augmentations.empty) {
178             return '';
179         }
180         return '''
181             <h2>Augmentations</h2>
182
183             <ul>
184             «FOR augment : module.augmentations»
185                 <li>
186                     augment
187                     «augment.tree»
188                 </li>
189             «ENDFOR»
190             </ul>
191         '''
192     }
193
194     def notifications(Module module) {
195         val Set<NotificationDefinition> notificationdefs = module.notifications
196         if (notificationdefs.empty) {
197             return '';
198         }
199         return '''
200             <h2>Notifications</h2>
201
202             <ul>
203             «FOR notificationdef : notificationdefs»
204                 <li>
205                     «notificationdef.nodeName»
206                     «notificationdef.tree»
207                 </li>
208             «ENDFOR»
209             </ul>
210         '''
211     }
212
213     def rpcs(Module module) {
214         if (module.rpcs.empty) {
215             return '';
216         }
217         return '''
218             <h2>RPC Definitions</h2>
219
220             <ul>
221             «FOR rpc : module.rpcs»
222                 <li>
223                     «rpc.nodeName»
224                     «rpc.tree»
225                 </li>
226             «ENDFOR»
227             </ul>
228         '''
229     }
230
231     def extensions(Module module) {
232         if (module.extensionSchemaNodes.empty) {
233             return '';
234         }
235         return '''
236             <h2>Extensions</h2>
237
238             <ul>
239             «FOR ext : module.extensionSchemaNodes»
240                 <li>
241                     «ext.nodeName»
242                     «ext.tree»
243                 </li>
244             «ENDFOR»
245             </ul>
246         '''
247     }
248
249     def features(Module module) {
250         if (module.features.empty) {
251             return '';
252         }
253         return '''
254             <h2>Features</h2>
255
256             <ul>
257             «FOR feature : module.features»
258                 <li>
259                     «strong("feature " + feature.QName.localName)»
260                     <ul>
261                         «feature.descAndRef»
262                     </ul>
263                 </li>
264             «ENDFOR»
265             </ul>
266         '''
267     }
268
269     def header(Module module) '''
270         <h1>«module.name»</h1>
271         
272         <h2>Base Information</h2>
273         <dl>
274             <dt>Prefix</dt>
275             <dd>«pre(module.prefix)»</dd>
276             <dt>Namespace</dt>
277             <dd>«pre(module.namespace.toString)»</dd>
278             <dt>Revision</dt>
279             <dd>«pre(REVISION_FORMAT.format(module.revision))»</dd>
280             
281             «FOR imp : module.imports BEFORE "<dt>Imports</dt>" »
282                 <dd>«pre(imp.prefix)» = «pre(imp.moduleName)»</dd>
283             «ENDFOR»
284         </dl>
285     '''
286
287     def process(Module module) {
288         throw new UnsupportedOperationException("TODO: auto-generated method stub")
289     }
290
291
292
293     /* #################### TREE STRUCTURE #################### */
294     def dispatch CharSequence tree(Module module) '''
295         «strong("module " + module.name)»
296         «module.childNodes.tree»
297     '''
298
299     def dispatch CharSequence tree(DataNodeContainer node) '''
300         «IF node instanceof SchemaNode»
301             «(node as SchemaNode).nodeName»
302         «ENDIF»
303         «node.childNodes.tree»
304     '''
305
306     def dispatch CharSequence tree(DataSchemaNode node) '''
307         «node.nodeName»
308     '''
309
310     def dispatch CharSequence tree(ListSchemaNode node) '''
311         «node.nodeName»
312         «node.childNodes.tree»
313     '''
314
315     def dispatch CharSequence tree(Collection<DataSchemaNode> childNodes) '''
316         «IF childNodes !== null && !childNodes.empty»
317             <ul>
318             «FOR child : childNodes»
319                 <li>
320                     «child.tree»
321                 </li>
322             «ENDFOR»
323             </ul>
324         «ENDIF»
325     '''
326
327     def listKeys(ListSchemaNode node) '''
328         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
329     '''
330
331     def dispatch CharSequence tree(AugmentationSchema augment) '''
332         <ul>
333             «listItem(augment.description)»
334             «listItem("Reference", augment.reference)»
335             «IF augment.whenCondition !== null»
336                 «listItem("When", augment.whenCondition.toString)»
337             «ENDIF»
338             <li>
339                 Path «augment.targetPath.path.pathToTree»
340             </li>
341             <li>
342                 Child nodes
343                 «augment.childNodes.tree»
344             </li>
345         </ul>
346     '''
347
348     def dispatch CharSequence tree(NotificationDefinition notification) '''
349         <ul>
350             «notification.descAndRef»
351             <li>
352                 Child nodes
353                 «notification.childNodes.tree»
354             </li>
355         </ul>
356     '''
357
358     def dispatch CharSequence tree(RpcDefinition rpc) '''
359         <ul>
360             «rpc.descAndRef»
361             <li>
362                 «rpc.input.tree»
363             </li>
364             <li>
365                 «rpc.output.tree»
366             </li>
367         </ul>
368     '''
369
370     def dispatch CharSequence tree(ExtensionDefinition ext) '''
371         <ul>
372             «ext.descAndRef»
373             «listItem("Argument", ext.argument)»
374         </ul>
375     '''
376
377
378
379     /* #################### RESTRICTIONS #################### */
380     private def restrictions(TypeDefinition<?> type) '''
381         «type.toLength»
382         «type.toRange»
383     '''
384
385     def dispatch toLength(TypeDefinition<?> type) {
386     }
387
388     def dispatch toLength(BinaryTypeDefinition type) '''
389         «type.lengthConstraints.toLengthStmt»
390     '''
391
392     def dispatch toLength(StringTypeDefinition type) '''
393         «type.lengthConstraints.toLengthStmt»
394     '''
395
396     def dispatch toLength(ExtendedType type) '''
397         «type.lengthConstraints.toLengthStmt»
398     '''
399
400     def dispatch toRange(TypeDefinition<?> type) {
401     }
402
403     def dispatch toRange(DecimalTypeDefinition type) '''
404         «type.rangeConstraints.toRangeStmt»
405     '''
406
407     def dispatch toRange(IntegerTypeDefinition type) '''
408         «type.rangeConstraints.toRangeStmt»
409     '''
410
411     def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
412         «type.rangeConstraints.toRangeStmt»
413     '''
414
415     def dispatch toRange(ExtendedType type) '''
416         «type.rangeConstraints.toRangeStmt»
417     '''
418
419     def toLengthStmt(Collection<LengthConstraint> lengths) '''
420         «IF lengths != null && !lengths.empty»
421             «listItem("Length restrictions")»
422             <ul>
423             «FOR length : lengths»
424                 <li>
425                 «IF length.min == length.max»
426                     «length.min»
427                 «ELSE»
428                     &lt;«length.min», «length.max»&gt;
429                 «ENDIF»
430                 </li>
431             «ENDFOR»
432             </ul>
433         «ENDIF»
434     '''
435
436     def toRangeStmt(Collection<RangeConstraint> ranges) '''
437         «IF ranges != null && !ranges.empty»
438             «listItem("Range restrictions")»
439             <ul>
440             «FOR range : ranges»
441                 <li>
442                 «IF range.min == range.max»
443                     «range.min»
444                 «ELSE»
445                     &lt;«range.min», «range.max»&gt;
446                 «ENDIF»
447                 </li>
448             «ENDFOR»
449             </ul>
450         «ENDIF»
451     '''
452
453
454
455     /* #################### UTILITY #################### */
456     private def strong(String str) '''<strong>«str»</strong>'''
457     private def italic(String str) '''<i>«str»</i>'''
458     private def pre(String str) '''<pre>«str»</pre>'''
459
460     def CharSequence descAndRef(SchemaNode node) '''
461         «listItem(node.description)»
462         «listItem("Reference", node.reference)»
463     '''
464
465     private def listItem(String value) '''
466         «IF value !== null && !value.empty»
467             <li>
468                 «value»
469             </li>
470         «ENDIF»
471     '''
472
473     private def listItem(String name, String value) '''
474         «IF value !== null && !value.empty»
475             <li>
476                 «name»
477                 <ul>
478                     <li>
479                         «value»
480                     </li>
481                 </ul>
482             </li>
483         «ENDIF»
484     '''
485
486     private def CharSequence pathToTree(List<QName> path) '''
487         «IF path !== null && !path.empty»
488             <ul>
489             «FOR pathElement : path»
490                 <li>
491                     «pathElement.namespace» «pathElement.localName»
492                 </li>
493             «ENDFOR»
494             </ul>
495         «ENDIF»
496     '''
497
498     def dispatch addedByInfo(SchemaNode node) '''
499     '''
500
501     def dispatch addedByInfo(DataSchemaNode node) '''
502         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
503     '''
504
505     def dispatch isAddedBy(SchemaNode node) {
506         return false;
507     }
508
509     def dispatch isAddedBy(DataSchemaNode node) {
510         if (node.augmenting || node.addedByUses) {
511             return true
512         } else {
513             return false;
514         }
515     }
516
517     def dispatch nodeName(SchemaNode node) '''
518         «IF node.isAddedBy»
519             «italic(node.QName.localName)»«node.addedByInfo»
520         «ELSE»
521             «strong(node.QName.localName)»«node.addedByInfo»
522         «ENDIF»
523     '''
524
525     def dispatch nodeName(ListSchemaNode node) '''
526         «IF node.isAddedBy»
527             «italic(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
528         «ELSE»
529             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
530         «ENDIF»
531     '''
532
533 }