Merge "BUG-865: removed unnecessary test file."
[yangtools.git] / code-generator / maven-sal-api-gen-plugin / src / main / java / org / opendaylight / yangtools / yang / unified / doc / generator / GeneratorImpl.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.unified.doc.generator
9
10 import java.io.BufferedWriter
11 import java.io.File
12 import java.io.IOException
13 import java.io.OutputStreamWriter
14 import java.text.SimpleDateFormat
15 import java.util.ArrayList
16 import java.util.Collection
17 import java.util.HashMap
18 import java.util.HashSet
19 import java.util.LinkedHashMap
20 import java.util.List
21 import java.util.Map
22 import java.util.Set
23 import org.opendaylight.yangtools.yang.common.QName
24 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
25 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
26 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
27 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
28 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
29 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
34 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
35 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.Module
39 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode
42 import org.opendaylight.yangtools.yang.model.api.SchemaPath
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
44 import org.opendaylight.yangtools.yang.model.api.UsesNode
45 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
46 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
47 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
49 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
50 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
51 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
52 import org.opendaylight.yangtools.yang.model.util.ExtendedType
53 import org.slf4j.Logger
54 import org.slf4j.LoggerFactory
55 import org.sonatype.plexus.build.incremental.BuildContext
56 import org.sonatype.plexus.build.incremental.DefaultBuildContext
57
58 class GeneratorImpl {
59
60     File path
61     static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
62     static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
63     static val BuildContext CTX = new DefaultBuildContext();
64     var Module currentModule;
65     val Map<String, String> imports = new HashMap();
66     var SchemaContext ctx;
67
68     def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
69         path = targetPath;
70         path.mkdirs();
71         val it = new HashSet;
72         for (module : modulesToGen) {
73             add(generateDocumentation(module, context));
74         }
75         return it;
76     }
77
78     def generateDocumentation(Module module, SchemaContext ctx) {
79         val destination = new File(path, '''«module.name».html''')
80         this.ctx = ctx;
81         module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
82         try {
83             val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
84             val bw = new BufferedWriter(fw)
85             currentModule = module;
86             bw.append(generate(module, ctx));
87             bw.close();
88             fw.close();
89         } catch (IOException e) {
90             LOG.error(e.getMessage());
91         }
92         return destination;
93     }
94
95     def generate(Module module, SchemaContext ctx) '''
96         <!DOCTYPE html>
97         <html lang="en">
98           <head>
99             <title>«module.name»</title>
100           </head>
101           <body>
102             «body(module, ctx)»
103           </body>
104         </html>
105     '''
106
107     def body(Module module, SchemaContext ctx) '''
108         «header(module)»
109
110         «typeDefinitionsSummary(module)»
111         «identitiesSummary(module)»
112         «groupingsSummary(module)»
113         «augmentationsSummary(module, ctx)»
114         «objectsSummary(module)»
115         «notificationsSummary(module)»
116         «rpcsSummary(module)»
117         «extensionsSummary(module)»
118         «featuresSummary(module)»
119
120         «typeDefinitions(module)»
121
122         «identities(module)»
123
124         «groupings(module)»
125
126         «dataStore(module)»
127
128         «childNodes(module)»
129
130         «notifications(module)»
131
132         «augmentations(module, ctx)»
133
134         «rpcs(module)»
135
136         «extensions(module)»
137
138         «features(module)»
139
140     '''
141
142
143     private def typeDefinitionsSummary(Module module) {
144         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
145         if (typedefs.empty) {
146             return '';
147         }
148         return '''
149         <div>
150             <h3>Type Definitions Summary</h3>
151             <table>
152                 <tr>
153                     <th>Name</th>
154                     <th>Description</th>
155                 </tr>
156                 «FOR typedef : typedefs»
157                 <tr>
158                     <td>
159                     «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
160                     </td>
161                     <td>
162                     «typedef.description»
163                     </td>
164                 </tr>
165                 «ENDFOR»
166             </table>
167         </div>
168         '''
169     }
170
171     def typeDefinitions(Module module) {
172         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
173         if (typedefs.empty) {
174             return '';
175         }
176         return '''
177             <h2>Type Definitions</h2>
178             <ul>
179             «FOR typedef : typedefs»
180                 <li>
181                     <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
182                     <ul>
183                     «typedef.descAndRefLi»
184                     «typedef.restrictions»
185                     </ul>
186                 </li>
187             «ENDFOR»
188             </ul>
189         '''
190     }
191
192     private def identities(Module module) {
193         if (module.identities.empty) {
194             return '';
195         }
196         return '''
197             <h2>Identities</h2>
198             <ul>
199             «FOR identity : module.identities»
200                 <li>
201                     <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
202                     <ul>
203                     «identity.descAndRefLi»
204                     «IF identity.baseIdentity !== null»
205                         «listItem("base", identity.baseIdentity.QName.localName)»
206                     «ENDIF»
207                     </ul>
208                 </li>
209             «ENDFOR»
210             </ul>
211         '''
212     }
213
214     private def identitiesSummary(Module module) {
215         if (module.identities.empty) {
216             return '';
217         }
218         return '''
219         <h3>Identities Summary</h3>
220         <table>
221             <tr>
222                 <th>Name</th>
223                 <th>Description</th>
224             </tr>
225             «FOR identity : module.identities»
226             <tr>
227                 <td>
228                 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
229                 </td>
230                 <td>
231                 «identity.description»
232                 </td>
233             </tr>
234             «ENDFOR»
235         </table>
236         '''
237     }
238
239     private def groupings(Module module) {
240         if (module.groupings.empty) {
241             return '';
242         }
243         return '''
244             <h2>Groupings</h2>
245             <ul>
246             «FOR grouping : module.groupings»
247                 <li>
248                     <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
249                     <ul>
250                         «grouping.descAndRefLi»
251                         «FOR childNode : grouping.childNodes»
252                             «childNode.printSchemaNodeInfo»
253                         «ENDFOR»
254                     </ul>
255                 </li>
256             «ENDFOR»
257             </ul>
258         '''
259     }
260
261     private def groupingsSummary(Module module) {
262         if (module.groupings.empty) {
263             return '';
264         }
265         return '''
266         <h3>Groupings Summary</h3>
267         <table>
268             <tr>
269                 <th>Name</th>
270                 <th>Description</th>
271             </tr>
272             «FOR grouping : module.groupings»
273             <tr>
274                 <td>
275                 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
276                 </td>
277                 <td>
278                 «grouping.description»
279                 </td>
280             </tr>
281             «ENDFOR»
282         </table>
283         '''
284     }
285
286     def dataStore(Module module) {
287         if (module.childNodes.empty) {
288             return '';
289         }
290         return '''
291             <h2>Datastore Structure</h2>
292             «tree(module)»
293         '''
294     }
295
296     def augmentations(Module module, SchemaContext context) {
297         if (module.augmentations.empty) {
298             return '';
299         }
300         return '''
301             <h2>Augmentations</h2>
302
303             <ul>
304             «FOR augment : module.augmentations»
305                 <li>
306                     <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
307                     Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
308                     «augment.description»
309                     «IF augment.reference !== null»
310                         Reference «augment.reference»
311                     «ENDIF»
312                     «IF augment.whenCondition !== null»
313                         When «augment.whenCondition.toString»
314                     «ENDIF»
315                     «FOR childNode : augment.childNodes»
316                         «childNode.printSchemaNodeInfo»
317                     «ENDFOR»
318                 </li>
319             «ENDFOR»
320             </ul>
321         '''
322     }
323
324     private def augmentationsSummary(Module module, SchemaContext context) {
325         if (module.augmentations.empty) {
326             return '';
327         }
328         return '''
329         <h3>Augmentations Summary</h3>
330         <table>
331             <tr>
332                 <th>Target</th>
333                 <th>Description</th>
334             </tr>
335             «FOR augment : module.augmentations»
336             <tr>
337                 <td>
338                 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
339                 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
340                 </td>
341                 <td>
342                 «augment.description»
343                 </td>
344             </tr>
345             «ENDFOR»
346         </table>
347         '''
348     }
349
350     def notifications(Module module) {
351         val Set<NotificationDefinition> notificationdefs = module.notifications
352         if (notificationdefs.empty) {
353             return '';
354         }
355
356         return '''
357             <h2>Notifications</h2>
358             «FOR notificationdef : notificationdefs»
359
360                 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
361                     «notificationdef.descAndRef»
362                     «FOR childNode : notificationdef.childNodes»
363                         «childNode.printSchemaNodeInfo»
364                     «ENDFOR»
365             «ENDFOR»
366         '''
367     }
368
369     private def notificationsSummary(Module module) {
370         if (module.notifications.empty) {
371             return '';
372         }
373         return '''
374         <h3>Notifications Summary</h3>
375         <table>
376             <tr>
377                 <th>Name</th>
378                 <th>Description</th>
379             </tr>
380             «FOR notification : module.notifications»
381             <tr>
382                 <td>
383                 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
384                 </td>
385                 <td>
386                 «notification.description»
387                 </td>
388             </tr>
389             «ENDFOR»
390         </table>
391         '''
392     }
393
394     def rpcs(Module module) {
395         if (module.rpcs.empty) {
396             return '';
397         }
398
399         return '''
400             <h2>RPC Definitions</h2>
401             «FOR rpc : module.rpcs»
402                 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
403                     <ul>
404                         «rpc.descAndRefLi»
405                         «rpc.input.printSchemaNodeInfo»
406                         «rpc.output.printSchemaNodeInfo»
407                     </ul>
408             «ENDFOR»
409             </ul>
410         '''
411     }
412
413     private def rpcsSummary(Module module) {
414         if (module.rpcs.empty) {
415             return '';
416         }
417         return '''
418         <h3>RPCs Summary</h3>
419         <table>
420             <tr>
421                 <th>Name</th>
422                 <th>Description</th>
423             </tr>
424             «FOR rpc : module.rpcs»
425             <tr>
426                 <td>
427                 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
428                 </td>
429                 <td>
430                 «rpc.description»
431                 </td>
432             </tr>
433             «ENDFOR»
434         </table>
435         '''
436     }
437
438     def extensions(Module module) {
439         if (module.extensionSchemaNodes.empty) {
440             return '';
441         }
442         return '''
443             <h2>Extensions</h2>
444             «FOR ext : module.extensionSchemaNodes»
445                 <li>
446                     <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
447                 </li>
448                 «extensionInfo(ext)»
449             «ENDFOR»
450         '''
451     }
452
453     private def extensionsSummary(Module module) {
454         if (module.extensionSchemaNodes.empty) {
455             return '';
456         }
457         return '''
458         <h3>Extensions Summary</h3>
459         <table>
460             <tr>
461                 <th>Name</th>
462                 <th>Description</th>
463             </tr>
464             «FOR ext : module.extensionSchemaNodes»
465             <tr>
466                 <td>
467                 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
468                 </td>
469                 <td>
470                 «ext.description»
471                 </td>
472             </tr>
473             «ENDFOR»
474         </table>
475         '''
476     }
477
478     def features(Module module) {
479         if (module.features.empty) {
480             return '';
481         }
482         return '''
483             <h2>Features</h2>
484
485             <ul>
486             «FOR feature : module.features»
487                 <li>
488                     <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
489                     <ul>
490                         «feature.descAndRefLi»
491                     </ul>
492                 </li>
493             «ENDFOR»
494             </ul>
495         '''
496     }
497
498     private def featuresSummary(Module module) {
499         if (module.features.empty) {
500             return '';
501         }
502         return '''
503         <h3>Features Summary</h3>
504         <table>
505             <tr>
506                 <th>Name</th>
507                 <th>Description</th>
508             </tr>
509             «FOR feature : module.features»
510             <tr>
511                 <td>
512                 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
513                 </td>
514                 <td>
515                 «feature.description»
516                 </td>
517             </tr>
518             «ENDFOR»
519         </table>
520         '''
521     }
522
523     private def objectsSummary(Module module) {
524         if (module.childNodes.empty) {
525             return '';
526         }
527         return '''
528         <h3>Child Nodes Summary</h3>
529         <table>
530             <tr>
531                 <th>Name</th>
532                 <th>Description</th>
533             </tr>
534             «FOR childNode : module.childNodes»
535             <tr>
536                 <td>
537                 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
538                 </td>
539                 <td>
540                 «childNode.description»
541                 </td>
542             </tr>
543             «ENDFOR»
544         </table>
545         '''
546     }
547
548     def header(Module module)
549     '''
550         <h1>«module.name»</h1>
551
552         <h2>Base Information</h2>
553         <table>
554             <tr>
555                 <td>«strong("prefix")»</td>
556                 <td>«module.prefix»</td>
557             </tr>
558             <tr>
559                 <td>«strong("namespace")»</td>
560                 <td>«module.namespace»</td>
561             </tr>
562             <tr>
563                 <td>«strong("revision")»</td>
564                 <td>«REVISION_FORMAT.format(module.revision)»</td>
565             </tr>
566             <tr>
567                 <td>«strong("description")»</td>
568                 <td>«module.description»</td>
569             </tr>
570             <tr>
571                 <td>«strong("yang-version")»</td>
572                 <td>«module.yangVersion»</td>
573             </tr>
574             <tr>
575                 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
576                     «imp.prefix»:«imp.moduleName»«IF imp.revision !== null» «REVISION_FORMAT.format(imp.revision)»«ENDIF»;
577                 «ENDFOR»
578             </tr>
579         </table>
580     '''
581
582     def CharSequence schemaPathToId(SchemaPath path) {
583         if(path !== null) {
584             return '''«FOR qName : path.path SEPARATOR "/"»«qName.localName»«ENDFOR»'''
585         }
586     }
587
588     def code(String string) '''<code>«string»</code>'''
589
590     def process(Module module) {
591         throw new UnsupportedOperationException("TODO: auto-generated method stub")
592     }
593
594     def CharSequence tree(Module module) '''
595         «strong(module.name)»
596         «module.childNodes.treeSet(InstanceIdentifier.builder.toInstance())»
597     '''
598
599     private def dispatch CharSequence tree(ChoiceNode node,InstanceIdentifier path) '''
600         «node.nodeName» (choice)
601         «casesTree(node.cases,path)»
602     '''
603
604     def casesTree(Set<ChoiceCaseNode> nodes,InstanceIdentifier path) '''
605         <ul>
606         «FOR node : nodes»
607             <li>
608             «node.nodeName»
609             «node.childNodes.treeSet(path)»
610             </li>
611         «ENDFOR»
612         </ul>
613     '''
614
615     private def dispatch CharSequence tree(DataSchemaNode node,InstanceIdentifier path) '''
616         «node.nodeName»
617     '''
618
619     private def dispatch CharSequence tree(ListSchemaNode node,InstanceIdentifier path) '''
620         «val newPath = path.append(node)»
621         «localLink(newPath,node.nodeName)»
622         «node.childNodes.treeSet(newPath)»
623     '''
624
625     private def dispatch CharSequence tree(ContainerSchemaNode node,InstanceIdentifier path) '''
626         «val newPath = path.append(node)»
627         «localLink(newPath,node.nodeName)»
628         «node.childNodes.treeSet(newPath)»
629     '''
630
631     def CharSequence childNodes(Module module) '''
632         «val childNodes = module.childNodes»
633         «IF !childNodes.nullOrEmpty»
634             <h2>Child nodes</h2>
635
636             «childNodes.printChildren(3,InstanceIdentifier.builder().toInstance())»
637         «ENDIF»
638     '''
639
640     def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
641         return '''
642             <ul>
643             «node.printBaseInfo»
644             «IF node instanceof DataNodeContainer»
645                 «val dataNode = node as DataNodeContainer»
646                 <ul>
647                 «FOR usesNode : dataNode.uses»
648                     «usesNode.printUses»
649                 «ENDFOR»
650                 </ul>
651                 <ul>
652                 «FOR typeDef : dataNode.typeDefinitions»
653                     «typeDef.restrictions»
654                 «ENDFOR»
655                 </ul>
656                 <ul>
657                 «FOR grouping : dataNode.groupings»
658                     «grouping.printGrouping»
659                 «ENDFOR»
660                 </ul>
661                 <ul>
662                 «FOR child : dataNode.childNodes»
663                     «child.printSchemaNodeInfo»
664                 «ENDFOR»
665                 </ul>
666             «ENDIF»
667             </ul>
668         '''
669     }
670
671     def String typeAnchorLink(SchemaPath path, CharSequence text) {
672         if(path !== null) {
673             val prefix = path.path.last.prefix
674             if(prefix == this.currentModule.prefix) {
675                 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
676             } else if(!prefix.nullOrEmpty){
677                 val String module = imports.get(prefix)
678                 if(!module.nullOrEmpty) {
679                     return '''«prefix»:«text»'''
680                     //to enable external (import) links
681                     //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
682                 }
683             }
684             return text.toString
685         }
686     }
687
688     def CharSequence printBaseInfo(SchemaNode node) {
689         if(node instanceof LeafSchemaNode) {
690             val LeafSchemaNode leafNode = (node as LeafSchemaNode)
691             return '''
692                 «printInfo(node, "leaf")»
693                 «listItem("type", typeAnchorLink(leafNode.type?.path, leafNode.type.QName.localName))»
694                 «listItem("units", leafNode.units)»
695                 «listItem("default", leafNode.^default)»
696                 </ul>
697             '''
698         } else if(node instanceof LeafListSchemaNode) {
699             val LeafListSchemaNode leafListNode = (node as LeafListSchemaNode)
700             return '''
701                 «printInfo(node, "leaf-list")»
702                 «listItem("type", leafListNode.type?.QName.localName)»
703                 </ul>
704             '''
705         } else if(node instanceof ListSchemaNode) {
706             val ListSchemaNode listNode = (node as ListSchemaNode)
707             return '''
708                 «printInfo(node, "list")»
709                 «FOR keyDef : listNode.keyDefinition»
710                     «listItem("key definition", keyDef.localName)»
711                 «ENDFOR»
712                 </ul>
713             '''
714         } else if(node instanceof ChoiceNode) {
715             val ChoiceNode choiceNode = (node as ChoiceNode)
716             return '''
717                 «printInfo(node, "choice")»
718                 «listItem("default case", choiceNode.defaultCase)»
719                 «FOR caseNode : choiceNode.cases»
720                     «caseNode.printSchemaNodeInfo»
721                 «ENDFOR»
722                 </ul>
723             '''
724         } else if(node instanceof ChoiceCaseNode) {
725             return '''
726                 «printInfo(node, "case")»
727                 </ul>
728             '''
729         } else if(node instanceof ContainerSchemaNode) {
730             return '''
731                 «printInfo(node, "container")»
732                 </ul>
733             '''
734         } else if(node instanceof AnyXmlSchemaNode) {
735             return '''
736                 «printInfo(node, "anyxml")»
737                 </ul>
738             '''
739         }
740     }
741
742     def CharSequence printInfo(SchemaNode node, String nodeType) {
743         return '''
744             «IF node instanceof AugmentationTarget»
745                 «IF node !== null»
746                     <strong>
747                     <li id="«node.path.schemaPathToId»">
748                         «nodeType»: «node.QName.localName»
749                     </li>
750                     </strong>
751                 «ENDIF»
752             «ELSE»
753                 «strong(listItem(nodeType, node.QName.localName))»
754             «ENDIF»
755             <ul>
756             «listItem("description", node.description)»
757             «listItem("reference", node.reference)»
758             «IF node instanceof DataSchemaNode»
759                 «listItem("when condition", (node as DataSchemaNode).constraints.whenCondition?.toString)»
760                 «listItem("min elements", (node as DataSchemaNode).constraints.minElements?.toString)»
761                 «listItem("max elements", (node as DataSchemaNode).constraints.maxElements?.toString)»
762             «ENDIF»
763         '''
764     }
765
766     def CharSequence printUses(UsesNode usesNode) {
767         return '''
768             «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.path.last.localName)))»
769             <ul>
770             <li>refines:
771                 <ul>
772                 «FOR sp : usesNode.refines.keySet»
773                     «listItem("node name", usesNode.refines.get(sp).QName.localName)»
774                 «ENDFOR»
775                 </ul>
776             </li>
777             «FOR augment : usesNode.augmentations»
778                 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
779             «ENDFOR»
780             </ul>
781         '''
782     }
783
784     def CharSequence printGrouping(GroupingDefinition grouping) {
785         return '''
786             «strong(listItem("grouping", grouping.QName.localName))»
787         '''
788     }
789
790     def CharSequence printChildren(Set<DataSchemaNode> nodes, int level, InstanceIdentifier path) {
791         val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
792         val leafNodes = nodes.filter(LeafSchemaNode)
793         val leafListNodes = nodes.filter(LeafListSchemaNode)
794         val choices = nodes.filter(ChoiceNode)
795         val cases = nodes.filter(ChoiceCaseNode)
796         val containers = nodes.filter(ContainerSchemaNode)
797         val lists = nodes.filter(ListSchemaNode)
798         return '''
799             «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
800             <h3>Direct children</h3>
801             <ul>
802             «FOR childNode : anyxmlNodes»
803                 «childNode.printShortInfo(level,path)»
804             «ENDFOR»
805             «FOR childNode : leafNodes»
806                 «childNode.printShortInfo(level,path)»
807             «ENDFOR»
808             «FOR childNode : leafListNodes»
809                 «childNode.printShortInfo(level,path)»
810             «ENDFOR»
811             «FOR childNode : containers»
812                 «childNode.printShortInfo(level,path)»
813             «ENDFOR»
814             «FOR childNode : lists»
815                 «childNode.printShortInfo(level,path)»
816             «ENDFOR»
817             </ul>
818             «ENDIF»
819
820             «IF !path.path.empty»
821             <h3>XML example</h3>
822             «nodes.xmlExample(path.path.last.nodeType,path)»
823             </h3>
824             «ENDIF»
825             «FOR childNode : containers»
826                 «childNode.printInfo(level,path)»
827             «ENDFOR»
828             «FOR childNode : lists»
829                 «childNode.printInfo(level,path)»
830             «ENDFOR»
831             «FOR childNode : choices»
832                 «childNode.printInfo(level,path)»
833             «ENDFOR»
834             «FOR childNode : cases»
835                 «childNode.printInfo(level,path)»
836             «ENDFOR»
837         '''
838     }
839
840     def CharSequence xmlExample(Set<DataSchemaNode> nodes, QName name,InstanceIdentifier path) '''
841     <pre>
842         «xmlExampleTag(name,nodes.xmplExampleTags(path))»
843     </pre>
844     '''
845
846     def CharSequence xmplExampleTags(Set<DataSchemaNode> nodes, InstanceIdentifier identifier) '''
847         <!-- Child nodes -->
848         «FOR node : nodes»
849         <!-- «node.QName.localName» -->
850             «node.asXmlExampleTag(identifier)»
851         «ENDFOR»
852
853     '''
854
855     private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, InstanceIdentifier identifier) '''
856         «node.QName.xmlExampleTag("...")»
857     '''
858
859     private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, InstanceIdentifier identifier) '''
860         &lt!-- This node could appear multiple times --&gt
861         «node.QName.xmlExampleTag("...")»
862     '''
863
864     private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, InstanceIdentifier identifier) '''
865         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
866         «node.QName.xmlExampleTag("...")»
867     '''
868
869
870     private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, InstanceIdentifier identifier) '''
871         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
872         &lt!-- This node could appear multiple times --&gt
873         «node.QName.xmlExampleTag("...")»
874     '''
875
876
877     private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, InstanceIdentifier identifier) '''
878         <!-- noop -->
879     '''
880
881
882     def xmlExampleTag(QName name, CharSequence data) {
883         return '''&lt;«name.localName» xmlns="«name.namespace»"&gt;«data»&lt;/«name.localName»&gt;'''
884     }
885
886     def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
887
888
889     def header(int level,InstanceIdentifier name) '''
890         <h«level» id="«FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
891             «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
892         </h«level»>
893     '''
894
895
896
897     private def dispatch CharSequence printInfo(DataSchemaNode node, int level, InstanceIdentifier path) '''
898         «header(level+1,node.QName)»
899     '''
900
901     private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) '''
902         «val newPath = path.append(node)»
903         «header(level,newPath)»
904         <dl>
905           <dt>XML Path</dt>
906           <dd>«newPath.asXmlPath»</dd>
907           <dt>Restconf path</dt>
908           <dd>«code(newPath.asRestconfPath)»</dd>
909         </dl>
910         «node.childNodes.printChildren(level,newPath)»
911     '''
912
913     private def dispatch CharSequence printInfo(ListSchemaNode node, int level, InstanceIdentifier path) '''
914         «val newPath = path.append(node)»
915         «header(level,newPath)»
916         <dl>
917           <dt>XML Path</dt>
918           <dd>«newPath.asXmlPath»</dd>
919           <dt>Restconf path</dt>
920           <dd>«code(newPath.asRestconfPath)»</dd>
921         </dl>
922         «node.childNodes.printChildren(level,newPath)»
923     '''
924
925     private def dispatch CharSequence printInfo(ChoiceNode node, int level, InstanceIdentifier path) '''
926         «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
927         «choiceCases.printChildren(level,path)»
928     '''
929
930     private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, InstanceIdentifier path) '''
931         «node.childNodes.printChildren(level,path)»
932     '''
933
934
935
936     def CharSequence printShortInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) {
937         val newPath = path.append(node);
938         return '''
939             <li>«strong(localLink(newPath,node.QName.localName))» (container)</li>
940         '''
941     }
942
943     def CharSequence printShortInfo(ListSchemaNode node, int level, InstanceIdentifier path) {
944         val newPath = path.append(node);
945         return '''
946             <li>«strong(localLink(newPath,node.QName.localName))» (list)</li>
947         '''
948     }
949
950     def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, InstanceIdentifier path) {
951         return '''
952             <li>«strong((node.QName.localName))» (anyxml)</li>
953         '''
954     }
955
956     def CharSequence printShortInfo(LeafSchemaNode node, int level, InstanceIdentifier path) {
957         return '''
958             <li>«strong((node.QName.localName))» (leaf)</li>
959         '''
960     }
961
962     def CharSequence printShortInfo(LeafListSchemaNode node, int level, InstanceIdentifier path) {
963         return '''
964             <li>«strong((node.QName.localName))» (leaf-list)</li>
965         '''
966     }
967
968     def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
969         return '''
970             <a href="#«anchor»">«text»</a>
971         '''
972     }
973
974     def CharSequence localLink(InstanceIdentifier identifier, CharSequence text) '''
975         <a href="#«FOR cmp : identifier.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
976     '''
977
978
979     private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ContainerSchemaNode node) {
980         return identifier.node(node.QName);
981     }
982
983     private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ListSchemaNode node) {
984         val keyValues = new LinkedHashMap<QName,Object>();
985         if(node.keyDefinition !== null) {
986             for(definition : node.keyDefinition) {
987                 keyValues.put(definition,new Object);
988             }
989         }
990
991         return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
992     }
993
994
995     def asXmlPath(InstanceIdentifier identifier) {
996         return "";
997     }
998
999     def asRestconfPath(InstanceIdentifier identifier) {
1000         val it = new StringBuilder();
1001         append(currentModule.name)
1002         append(":")
1003         var previous = false;
1004         for(arg : identifier.path) {
1005             if(previous) append("/")
1006             append(arg.nodeType.localName);
1007             previous = true;
1008             if(arg instanceof NodeIdentifierWithPredicates) {
1009                 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
1010                 for(qname : nodeIdentifier.keyValues.keySet) {
1011                     append("/{");
1012                     append(qname.localName)
1013                     append("}")
1014                 }
1015             }
1016         }
1017
1018         return it.toString;
1019     }
1020
1021     private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1022             val List<QName> path = schemaPath.path
1023         val StringBuilder pathString = new StringBuilder()
1024         if (schemaPath.absolute) {
1025             pathString.append("/")
1026         }
1027
1028         val QName qname = path.get(0)
1029         var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1030
1031         for (name : path) {
1032             if (parent instanceof DataNodeContainer) {
1033                 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)
1034                 if (node == null && (parent instanceof Module)) {
1035                     val notifications = (parent as Module).notifications;
1036                     for (notification : notifications) {
1037                         if (notification.QName.equals(name)) {
1038                             node = notification
1039                         }
1040                     }
1041                 }
1042                 if (node == null && (parent instanceof Module)) {
1043                     val rpcs = (parent as Module).rpcs;
1044                     for (rpc : rpcs) {
1045                         if (rpc.QName.equals(name)) {
1046                             node = rpc
1047                         }
1048                     }
1049                 }
1050
1051                 var String prefix = name.prefix
1052                 var String moduleName
1053                 if (prefix == null || "".equals(prefix) || prefix.equals(module.prefix)) {
1054                     moduleName = module.name
1055                 } else {
1056                     moduleName = imports.get(prefix)
1057                 }
1058                 pathString.append(moduleName)
1059                 pathString.append(":")
1060                 pathString.append(name.localName)
1061                 pathString.append("/")
1062                 if(node instanceof ChoiceNode && dataNode !== null) {
1063                     val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1064                     if(caseNode !== null) {
1065                         pathString.append("(case)");
1066                         pathString.append(caseNode.QName.localName);
1067                     }
1068                 }
1069                 parent = node
1070             }
1071         }
1072         return pathString.toString;
1073     }
1074
1075
1076     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1077         «IF childNodes !== null && !childNodes.empty»
1078             «FOR child : childNodes.values»
1079                 «childInfo(child, childNodes)»
1080             «ENDFOR»
1081         «ENDIF»
1082     '''
1083
1084     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1085         «val String path = nodeSchemaPathToPath(node, childNodes)»
1086         «IF path != null»
1087             «code(path)»
1088                 «IF node !== null»
1089                 <ul>
1090                 «node.descAndRefLi»
1091                 </ul>
1092             «ENDIF»
1093         «ENDIF»
1094     '''
1095
1096     private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, InstanceIdentifier path) '''
1097         «IF childNodes !== null && !childNodes.empty»
1098             <ul>
1099             «FOR child : childNodes»
1100                 <li>
1101                     «child.tree(path)»
1102                 </li>
1103             «ENDFOR»
1104             </ul>
1105         «ENDIF»
1106     '''
1107
1108     def listKeys(ListSchemaNode node) '''
1109         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1110     '''
1111
1112     private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1113         <ul>
1114             «ext.descAndRefLi»
1115             «listItem("Argument", ext.argument)»
1116         </ul>
1117     '''
1118
1119     private def dispatch CharSequence tree(Void obj, InstanceIdentifier path) '''
1120     '''
1121
1122
1123
1124     /* #################### RESTRICTIONS #################### */
1125     private def restrictions(TypeDefinition<?> type) '''
1126         «type.baseType.toBaseStmt»
1127         «type.toLength»
1128         «type.toRange»
1129     '''
1130
1131     private def dispatch toLength(TypeDefinition<?> type) {
1132     }
1133
1134     private def dispatch toLength(BinaryTypeDefinition type) '''
1135         «type.lengthConstraints.toLengthStmt»
1136     '''
1137
1138     private def dispatch toLength(StringTypeDefinition type) '''
1139         «type.lengthConstraints.toLengthStmt»
1140     '''
1141
1142     private def dispatch toLength(ExtendedType type) '''
1143         «type.lengthConstraints.toLengthStmt»
1144     '''
1145
1146     private def dispatch toRange(TypeDefinition<?> type) {
1147     }
1148
1149     private def dispatch toRange(DecimalTypeDefinition type) '''
1150         «type.rangeConstraints.toRangeStmt»
1151     '''
1152
1153     private def dispatch toRange(IntegerTypeDefinition type) '''
1154         «type.rangeConstraints.toRangeStmt»
1155     '''
1156
1157     private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1158         «type.rangeConstraints.toRangeStmt»
1159     '''
1160
1161     private def dispatch toRange(ExtendedType type) '''
1162         «type.rangeConstraints.toRangeStmt»
1163     '''
1164
1165     def toLengthStmt(Collection<LengthConstraint> lengths) '''
1166         «IF lengths != null && !lengths.empty»
1167             «listItem("Length restrictions:")»
1168             <ul>
1169             «FOR length : lengths»
1170                 <li>
1171                 «IF length.min == length.max»
1172                     «length.min»
1173                 «ELSE»
1174                     &lt;«length.min», «length.max»&gt;
1175                 «ENDIF»
1176                 </li>
1177             «ENDFOR»
1178             </ul>
1179         «ENDIF»
1180     '''
1181
1182     def toRangeStmt(Collection<RangeConstraint> ranges) '''
1183         «IF ranges != null && !ranges.empty»
1184             «listItem("Range restrictions:")»
1185             <ul>
1186             «FOR range : ranges»
1187                 <li>
1188                 «IF range.min == range.max»
1189                     «range.min»
1190                 «ELSE»
1191                     &lt;«range.min», «range.max»&gt;
1192                 «ENDIF»
1193                 </li>
1194             «ENDFOR»
1195             </ul>
1196         «ENDIF»
1197     '''
1198
1199     def toBaseStmt(TypeDefinition<?> baseType) '''
1200         «IF baseType != null»
1201         «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1202         «ENDIF»
1203     '''
1204
1205
1206
1207     /* #################### UTILITY #################### */
1208     private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1209     private def italic(CharSequence str) '''<i>«str»</i>'''
1210
1211     def CharSequence descAndRefLi(SchemaNode node) '''
1212         «listItem("Description", node.description)»
1213         «listItem("Reference", node.reference)»
1214     '''
1215
1216     def CharSequence descAndRef(SchemaNode node) '''
1217         «node.description»
1218         «IF node.reference !== null»
1219             Reference «node.reference»
1220         «ENDIF»
1221     '''
1222
1223     private def listItem(String value) '''
1224         «IF value !== null && !value.empty»
1225             <li>
1226                 «value»
1227             </li>
1228         «ENDIF»
1229     '''
1230
1231     private def listItem(String name, String value) '''
1232         «IF value !== null && !value.empty»
1233             <li>
1234                 «name»: «value»
1235             </li>
1236         «ENDIF»
1237     '''
1238
1239     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1240         if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
1241             return null
1242         }
1243
1244         val path = node.path.path
1245         val absolute = node.path.absolute;
1246         var StringBuilder result = new StringBuilder
1247         if (absolute) {
1248             result.append("/")
1249         }
1250         if (path !== null && !path.empty) {
1251             val List<QName> actual = new ArrayList()
1252             var i = 0;
1253             for (pathElement : path) {
1254                 actual.add(pathElement)
1255                 val DataSchemaNode nodeByPath = childNodes.get(new SchemaPath(actual, absolute))
1256                 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1257                     result.append(pathElement.localName)
1258                     if (i != path.size - 1) {
1259                         result.append("/")
1260                     }
1261                 }
1262                 i = i + 1
1263             }
1264         }
1265         return result.toString
1266     }
1267
1268     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1269         for (node : source) {
1270             destination.put(node.path, node)
1271             if (node instanceof DataNodeContainer) {
1272                 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1273             }
1274             if (node instanceof ChoiceNode) {
1275                 val List<DataSchemaNode> choiceCases = new ArrayList()
1276                 for (caseNode : (node as ChoiceNode).cases) {
1277                     choiceCases.add(caseNode)
1278                 }
1279                 collectChildNodes(choiceCases, destination)
1280             }
1281         }
1282     }
1283
1284     private def dispatch addedByInfo(SchemaNode node) '''
1285     '''
1286
1287     private def dispatch addedByInfo(DataSchemaNode node) '''
1288         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1289     '''
1290
1291     private def dispatch isAddedBy(SchemaNode node) {
1292         return false;
1293     }
1294
1295     private def dispatch isAddedBy(DataSchemaNode node) {
1296         if (node.augmenting || node.addedByUses) {
1297             return true
1298         } else {
1299             return false;
1300         }
1301     }
1302
1303     private def dispatch nodeName(SchemaNode node) '''
1304         «IF node.isAddedBy»
1305             «italic(node.QName.localName)»«node.addedByInfo»
1306         «ELSE»
1307             «node.QName.localName»«node.addedByInfo»
1308         «ENDIF»
1309     '''
1310
1311     private def dispatch nodeName(ContainerSchemaNode node) '''
1312         «IF node.isAddedBy»
1313             «strong(italic(node.QName.localName))»«node.addedByInfo»
1314         «ELSE»
1315             «strong(node.QName.localName)»«node.addedByInfo»
1316         «ENDIF»
1317     '''
1318
1319     private def dispatch nodeName(ListSchemaNode node) '''
1320         «IF node.isAddedBy»
1321             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1322         «ELSE»
1323             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
1324         «ENDIF»
1325     '''
1326
1327 }