Added path to child nodes in 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 import java.util.ArrayList
35 import java.util.Map
36 import org.opendaylight.yangtools.yang.model.api.SchemaPath
37 import java.util.LinkedHashMap
38 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
39 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
40
41 class GeneratorImpl {
42
43     File path
44     static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
45     static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
46
47
48     def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
49         path = targetPath;
50         path.mkdirs();
51         val it = new HashSet;
52         for (module : modulesToGen) {
53             add(module.generateDocumentation());
54         }
55         return it;
56     }
57
58     def generateDocumentation(Module module) {
59         val destination = new File(path, '''«module.name».html''')
60         try {
61             val fw = new FileWriter(destination)
62             destination.createNewFile();
63             val bw = new BufferedWriter(fw)
64
65             bw.append(module.generate);
66             bw.close();
67             fw.close();
68         } catch (IOException e) {
69             LOG.error(e.getMessage());
70         }
71         return destination;
72     }
73
74     def generate(Module module) '''
75         <!DOCTYPE html>
76         <html lang="en">
77           <head>
78             <title>«module.name»</title>
79           </head>
80           <body>
81             «module.body»
82           </body>
83         </html>
84     '''
85
86     def body(Module module) '''
87         «header(module)»
88
89         «typeDefinitions(module)»
90
91         «identities(module)»
92
93         «groupings(module)»
94
95         «childNodes(module)»
96
97         «dataStore(module)»
98
99         «notifications(module)»
100
101         «augmentations(module)»
102
103         «rpcs(module)»
104
105         «extensions(module)»
106
107         «features(module)»
108
109     '''
110
111
112     def typeDefinitions(Module module) {
113         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
114         if (typedefs.empty) {
115             return '';
116         }
117         return '''
118             <h2>Type Definitions</h2>
119             <ul>
120             «FOR typedef : typedefs»
121                 <li>
122                     «strong("typedef " + typedef.QName.localName)»
123                     <ul>
124                     «typedef.descAndRef»
125                     «typedef.restrictions»
126                     </ul>
127                 </li>
128             «ENDFOR»
129             </ul>
130         '''
131     }
132
133     private def identities(Module module) {
134         if (module.identities.empty) {
135             return '';
136         }
137         return '''
138             <h2>Identities</h2>
139             <ul>
140             «FOR identity : module.identities»
141                 <li>
142                     «strong("identity " + identity.QName.localName)»
143                     <ul>
144                     «identity.descAndRef»
145                     «IF identity.baseIdentity != null»
146                         «listItem("base", identity.baseIdentity.QName.localName)»
147                     «ENDIF»
148                     </ul>
149                 </li>
150             «ENDFOR»
151             </ul>
152         '''
153     }
154
155     private def groupings(Module module) {
156         if (module.groupings.empty) {
157             return '';
158         }
159         return '''
160             <h2>Groupings</h2>
161             <ul>
162             «FOR grouping : module.groupings»
163                 <li>
164                     «strong("grouping " + grouping.QName.localName)»
165                     <ul>
166                         «grouping.descAndRef»
167                     </ul>
168                 </li>
169             «ENDFOR»
170             </ul>
171         '''
172     }
173
174     def dataStore(Module module) {
175         if (module.childNodes.empty) {
176             return '';
177         }
178         return '''
179             <h2>Datastore Structure</h2>
180             «tree(module)»
181         '''
182     }
183
184     def augmentations(Module module) {
185         if (module.augmentations.empty) {
186             return '';
187         }
188         return '''
189             <h2>Augmentations</h2>
190
191             <ul>
192             «FOR augment : module.augmentations»
193                 <li>
194                     augment
195                     «augment.tree»
196                 </li>
197             «ENDFOR»
198             </ul>
199         '''
200     }
201
202     def notifications(Module module) {
203         val Set<NotificationDefinition> notificationdefs = module.notifications
204         if (notificationdefs.empty) {
205             return '';
206         }
207         return '''
208             <h2>Notifications</h2>
209
210             <ul>
211             «FOR notificationdef : notificationdefs»
212                 <li>
213                     «notificationdef.nodeName»
214                     «notificationdef.tree»
215                 </li>
216             «ENDFOR»
217             </ul>
218         '''
219     }
220
221     def rpcs(Module module) {
222         if (module.rpcs.empty) {
223             return '';
224         }
225         return '''
226             <h2>RPC Definitions</h2>
227
228             <ul>
229             «FOR rpc : module.rpcs»
230                 <li>
231                     «rpc.nodeName»
232                     «rpc.tree»
233                 </li>
234             «ENDFOR»
235             </ul>
236         '''
237     }
238
239     def extensions(Module module) {
240         if (module.extensionSchemaNodes.empty) {
241             return '';
242         }
243         return '''
244             <h2>Extensions</h2>
245
246             <ul>
247             «FOR ext : module.extensionSchemaNodes»
248                 <li>
249                     «ext.nodeName»
250                     «ext.tree»
251                 </li>
252             «ENDFOR»
253             </ul>
254         '''
255     }
256
257     def features(Module module) {
258         if (module.features.empty) {
259             return '';
260         }
261         return '''
262             <h2>Features</h2>
263
264             <ul>
265             «FOR feature : module.features»
266                 <li>
267                     «strong("feature " + feature.QName.localName)»
268                     <ul>
269                         «feature.descAndRef»
270                     </ul>
271                 </li>
272             «ENDFOR»
273             </ul>
274         '''
275     }
276
277     def header(Module module) '''
278         <h1>«module.name»</h1>
279         
280         <h2>Base Information</h2>
281         <dl>
282             <dt>Prefix</dt>
283             <dd>«pre(module.prefix)»</dd>
284             <dt>Namespace</dt>
285             <dd>«pre(module.namespace.toString)»</dd>
286             <dt>Revision</dt>
287             <dd>«pre(REVISION_FORMAT.format(module.revision))»</dd>
288             
289             «FOR imp : module.imports BEFORE "<dt>Imports</dt>" »
290                 <dd>«pre(imp.prefix)» = «pre(imp.moduleName)»</dd>
291             «ENDFOR»
292         </dl>
293     '''
294
295     def process(Module module) {
296         throw new UnsupportedOperationException("TODO: auto-generated method stub")
297     }
298
299
300
301     /* #################### TREE STRUCTURE #################### */
302     def dispatch CharSequence tree(Module module) '''
303         «strong("module " + module.name)»
304         «module.childNodes.tree»
305     '''
306
307     def dispatch CharSequence tree(DataNodeContainer node) '''
308         «IF node instanceof SchemaNode»
309             «(node as SchemaNode).nodeName»
310         «ENDIF»
311         «node.childNodes.tree»
312     '''
313
314     def dispatch CharSequence tree(DataSchemaNode node) '''
315         «node.nodeName»
316     '''
317
318     def dispatch CharSequence tree(ListSchemaNode node) '''
319         «node.nodeName»
320         «node.childNodes.tree»
321     '''
322
323     def CharSequence childNodes(Module module) '''
324         «val Map<SchemaPath, DataSchemaNode> childNodes = new LinkedHashMap()»
325         «collectChildNodes(module.childNodes, childNodes)»
326         «IF childNodes !== null && !childNodes.empty»
327             <h2>Child nodes</h2>
328
329             «childNodes.childNodesInfoTree»
330         «ENDIF»
331     '''
332
333     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
334         «IF childNodes !== null && !childNodes.empty»
335             <ul>
336             «FOR child : childNodes.values»
337                 «childInfo(child, childNodes)»
338             «ENDFOR»
339             </ul>
340         «ENDIF»
341     '''
342
343     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
344         «val String path = nodeSchemaPathToPath(node, childNodes)»
345         «IF path != null»
346             «listItem(strong(path))»
347                 «IF node !== null»
348                 <ul>
349                 «node.descAndRef»
350                 </ul>
351             «ENDIF»
352         «ENDIF»
353     '''
354
355     def dispatch CharSequence tree(Collection<DataSchemaNode> childNodes) '''
356         «IF childNodes !== null && !childNodes.empty»
357             <ul>
358             «FOR child : childNodes»
359                 <li>
360                     «child.tree»
361                 </li>
362             «ENDFOR»
363             </ul>
364         «ENDIF»
365     '''
366
367     def listKeys(ListSchemaNode node) '''
368         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
369     '''
370
371     def dispatch CharSequence tree(AugmentationSchema augment) '''
372         <ul>
373             «listItem(augment.description)»
374             «listItem("Reference", augment.reference)»
375             «IF augment.whenCondition !== null»
376                 «listItem("When", augment.whenCondition.toString)»
377             «ENDIF»
378             <li>
379                 Path «augment.targetPath.path.pathToTree»
380             </li>
381             <li>
382                 Child nodes
383                 «augment.childNodes.tree»
384             </li>
385         </ul>
386     '''
387
388     def dispatch CharSequence tree(NotificationDefinition notification) '''
389         <ul>
390             «notification.descAndRef»
391             <li>
392                 Child nodes
393                 «notification.childNodes.tree»
394             </li>
395         </ul>
396     '''
397
398     def dispatch CharSequence tree(RpcDefinition rpc) '''
399         <ul>
400             «rpc.descAndRef»
401             <li>
402                 «rpc.input.tree»
403             </li>
404             <li>
405                 «rpc.output.tree»
406             </li>
407         </ul>
408     '''
409
410     def dispatch CharSequence tree(ExtensionDefinition ext) '''
411         <ul>
412             «ext.descAndRef»
413             «listItem("Argument", ext.argument)»
414         </ul>
415     '''
416
417
418
419     /* #################### RESTRICTIONS #################### */
420     private def restrictions(TypeDefinition<?> type) '''
421         «type.toLength»
422         «type.toRange»
423     '''
424
425     def dispatch toLength(TypeDefinition<?> type) {
426     }
427
428     def dispatch toLength(BinaryTypeDefinition type) '''
429         «type.lengthConstraints.toLengthStmt»
430     '''
431
432     def dispatch toLength(StringTypeDefinition type) '''
433         «type.lengthConstraints.toLengthStmt»
434     '''
435
436     def dispatch toLength(ExtendedType type) '''
437         «type.lengthConstraints.toLengthStmt»
438     '''
439
440     def dispatch toRange(TypeDefinition<?> type) {
441     }
442
443     def dispatch toRange(DecimalTypeDefinition type) '''
444         «type.rangeConstraints.toRangeStmt»
445     '''
446
447     def dispatch toRange(IntegerTypeDefinition type) '''
448         «type.rangeConstraints.toRangeStmt»
449     '''
450
451     def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
452         «type.rangeConstraints.toRangeStmt»
453     '''
454
455     def dispatch toRange(ExtendedType type) '''
456         «type.rangeConstraints.toRangeStmt»
457     '''
458
459     def toLengthStmt(Collection<LengthConstraint> lengths) '''
460         «IF lengths != null && !lengths.empty»
461             «listItem("Length restrictions")»
462             <ul>
463             «FOR length : lengths»
464                 <li>
465                 «IF length.min == length.max»
466                     «length.min»
467                 «ELSE»
468                     &lt;«length.min», «length.max»&gt;
469                 «ENDIF»
470                 </li>
471             «ENDFOR»
472             </ul>
473         «ENDIF»
474     '''
475
476     def toRangeStmt(Collection<RangeConstraint> ranges) '''
477         «IF ranges != null && !ranges.empty»
478             «listItem("Range restrictions")»
479             <ul>
480             «FOR range : ranges»
481                 <li>
482                 «IF range.min == range.max»
483                     «range.min»
484                 «ELSE»
485                     &lt;«range.min», «range.max»&gt;
486                 «ENDIF»
487                 </li>
488             «ENDFOR»
489             </ul>
490         «ENDIF»
491     '''
492
493
494
495     /* #################### UTILITY #################### */
496     private def String strong(String str) '''<strong>«str»</strong>'''
497     private def italic(String str) '''<i>«str»</i>'''
498     private def pre(String str) '''<pre>«str»</pre>'''
499
500     def CharSequence descAndRef(SchemaNode node) '''
501         «listItem(node.description)»
502         «listItem("Reference", node.reference)»
503     '''
504
505     private def listItem(String value) '''
506         «IF value !== null && !value.empty»
507             <li>
508                 «value»
509             </li>
510         «ENDIF»
511     '''
512
513     private def listItem(String name, String value) '''
514         «IF value !== null && !value.empty»
515             <li>
516                 «name»
517                 <ul>
518                     <li>
519                         «value»
520                     </li>
521                 </ul>
522             </li>
523         «ENDIF»
524     '''
525
526     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
527         if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
528             return null
529         }
530
531         val path = node.path.path
532         val absolute = node.path.absolute;
533         var StringBuilder result = new StringBuilder
534         if (absolute) {
535             result.append("/")
536         }
537         if (path !== null && !path.empty) {
538             val List<QName> actual = new ArrayList()
539             var i = 0;
540             for (pathElement : path) {
541                 actual.add(pathElement)
542                 val DataSchemaNode nodeByPath = childNodes.get(new SchemaPath(actual, absolute)) 
543                 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
544                     result.append(pathElement.localName)
545                     if (i != path.size - 1) {
546                         result.append("/")
547                     }
548                 }
549                 i = i + 1
550             }
551         }
552         return result.toString
553     }
554
555     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
556         for (node : source) {
557             destination.put(node.path, node)
558             if (node instanceof DataNodeContainer) {
559                 collectChildNodes((node as DataNodeContainer).childNodes, destination)
560             }
561             if (node instanceof ChoiceNode) {
562                 val List<DataSchemaNode> choiceCases = new ArrayList()
563                 for (caseNode : (node as ChoiceNode).cases) {
564                     choiceCases.add(caseNode)
565                 }
566                 collectChildNodes(choiceCases, destination)
567             }
568         }
569     }
570
571     private def CharSequence pathToTree(List<QName> path) '''
572         «IF path !== null && !path.empty»
573             <ul>
574             «FOR pathElement : path»
575                 <li>
576                     «pathElement.namespace» «pathElement.localName»
577                 </li>
578             «ENDFOR»
579             </ul>
580         «ENDIF»
581     '''
582
583     def dispatch addedByInfo(SchemaNode node) '''
584     '''
585
586     def dispatch addedByInfo(DataSchemaNode node) '''
587         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
588     '''
589
590     def dispatch isAddedBy(SchemaNode node) {
591         return false;
592     }
593
594     def dispatch isAddedBy(DataSchemaNode node) {
595         if (node.augmenting || node.addedByUses) {
596             return true
597         } else {
598             return false;
599         }
600     }
601
602     def dispatch nodeName(SchemaNode node) '''
603         «IF node.isAddedBy»
604             «italic(node.QName.localName)»«node.addedByInfo»
605         «ELSE»
606             «strong(node.QName.localName)»«node.addedByInfo»
607         «ENDIF»
608     '''
609
610     def dispatch nodeName(ListSchemaNode node) '''
611         «IF node.isAddedBy»
612             «italic(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
613         «ELSE»
614             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
615         «ENDIF»
616     '''
617
618 }