BUG-994: use SchemaPath.getPathTowardRoot()
[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                 «val Set<TypeDefinition<?>> typedefs = dataNode.typeDefinitions»
653                 «FOR typeDef : typedefs»
654                     «typeDef.restrictions»
655                 «ENDFOR»
656                 </ul>
657                 <ul>
658                 «FOR grouping : dataNode.groupings»
659                     «grouping.printGrouping»
660                 «ENDFOR»
661                 </ul>
662                 <ul>
663                 «FOR child : dataNode.childNodes»
664                     «child.printSchemaNodeInfo»
665                 «ENDFOR»
666                 </ul>
667             «ENDIF»
668             </ul>
669         '''
670     }
671
672     def String typeAnchorLink(SchemaPath path, CharSequence text) {
673         if(path !== null) {
674             val prefix = path.path.last.prefix
675             if(prefix == this.currentModule.prefix) {
676                 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
677             } else if(!prefix.nullOrEmpty){
678                 val String module = imports.get(prefix)
679                 if(!module.nullOrEmpty) {
680                     return '''«prefix»:«text»'''
681                     //to enable external (import) links
682                     //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
683                 }
684             }
685             return text.toString
686         }
687     }
688
689     def CharSequence printBaseInfo(SchemaNode node) {
690         if(node instanceof LeafSchemaNode) {
691             val LeafSchemaNode leafNode = (node as LeafSchemaNode)
692             return '''
693                 «printInfo(node, "leaf")»
694                 «listItem("type", typeAnchorLink(leafNode.type?.path, leafNode.type.QName.localName))»
695                 «listItem("units", leafNode.units)»
696                 «listItem("default", leafNode.^default)»
697                 </ul>
698             '''
699         } else if(node instanceof LeafListSchemaNode) {
700             val LeafListSchemaNode leafListNode = (node as LeafListSchemaNode)
701             return '''
702                 «printInfo(node, "leaf-list")»
703                 «listItem("type", leafListNode.type?.QName.localName)»
704                 </ul>
705             '''
706         } else if(node instanceof ListSchemaNode) {
707             val ListSchemaNode listNode = (node as ListSchemaNode)
708             return '''
709                 «printInfo(node, "list")»
710                 «FOR keyDef : listNode.keyDefinition»
711                     «listItem("key definition", keyDef.localName)»
712                 «ENDFOR»
713                 </ul>
714             '''
715         } else if(node instanceof ChoiceNode) {
716             val ChoiceNode choiceNode = (node as ChoiceNode)
717             return '''
718                 «printInfo(node, "choice")»
719                 «listItem("default case", choiceNode.defaultCase)»
720                 «FOR caseNode : choiceNode.cases»
721                     «caseNode.printSchemaNodeInfo»
722                 «ENDFOR»
723                 </ul>
724             '''
725         } else if(node instanceof ChoiceCaseNode) {
726             return '''
727                 «printInfo(node, "case")»
728                 </ul>
729             '''
730         } else if(node instanceof ContainerSchemaNode) {
731             return '''
732                 «printInfo(node, "container")»
733                 </ul>
734             '''
735         } else if(node instanceof AnyXmlSchemaNode) {
736             return '''
737                 «printInfo(node, "anyxml")»
738                 </ul>
739             '''
740         }
741     }
742
743     def CharSequence printInfo(SchemaNode node, String nodeType) {
744         return '''
745             «IF node instanceof AugmentationTarget»
746                 «IF node !== null»
747                     <strong>
748                     <li id="«node.path.schemaPathToId»">
749                         «nodeType»: «node.QName.localName»
750                     </li>
751                     </strong>
752                 «ENDIF»
753             «ELSE»
754                 «strong(listItem(nodeType, node.QName.localName))»
755             «ENDIF»
756             <ul>
757             «listItem("description", node.description)»
758             «listItem("reference", node.reference)»
759             «IF node instanceof DataSchemaNode»
760                 «listItem("when condition", (node as DataSchemaNode).constraints.whenCondition?.toString)»
761                 «listItem("min elements", (node as DataSchemaNode).constraints.minElements?.toString)»
762                 «listItem("max elements", (node as DataSchemaNode).constraints.maxElements?.toString)»
763             «ENDIF»
764         '''
765     }
766
767     def CharSequence printUses(UsesNode usesNode) {
768         return '''
769             «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
770             <ul>
771             <li>refines:
772                 <ul>
773                 «FOR sp : usesNode.refines.keySet»
774                     «listItem("node name", usesNode.refines.get(sp).QName.localName)»
775                 «ENDFOR»
776                 </ul>
777             </li>
778             «FOR augment : usesNode.augmentations»
779                 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
780             «ENDFOR»
781             </ul>
782         '''
783     }
784
785     def CharSequence printGrouping(GroupingDefinition grouping) {
786         return '''
787             «strong(listItem("grouping", grouping.QName.localName))»
788         '''
789     }
790
791     def CharSequence printChildren(Set<DataSchemaNode> nodes, int level, InstanceIdentifier path) {
792         val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
793         val leafNodes = nodes.filter(LeafSchemaNode)
794         val leafListNodes = nodes.filter(LeafListSchemaNode)
795         val choices = nodes.filter(ChoiceNode)
796         val cases = nodes.filter(ChoiceCaseNode)
797         val containers = nodes.filter(ContainerSchemaNode)
798         val lists = nodes.filter(ListSchemaNode)
799         return '''
800             «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
801             <h3>Direct children</h3>
802             <ul>
803             «FOR childNode : anyxmlNodes»
804                 «childNode.printShortInfo(level,path)»
805             «ENDFOR»
806             «FOR childNode : leafNodes»
807                 «childNode.printShortInfo(level,path)»
808             «ENDFOR»
809             «FOR childNode : leafListNodes»
810                 «childNode.printShortInfo(level,path)»
811             «ENDFOR»
812             «FOR childNode : containers»
813                 «childNode.printShortInfo(level,path)»
814             «ENDFOR»
815             «FOR childNode : lists»
816                 «childNode.printShortInfo(level,path)»
817             «ENDFOR»
818             </ul>
819             «ENDIF»
820
821             «IF !path.path.empty»
822             <h3>XML example</h3>
823             «nodes.xmlExample(path.path.last.nodeType,path)»
824             </h3>
825             «ENDIF»
826             «FOR childNode : containers»
827                 «childNode.printInfo(level,path)»
828             «ENDFOR»
829             «FOR childNode : lists»
830                 «childNode.printInfo(level,path)»
831             «ENDFOR»
832             «FOR childNode : choices»
833                 «childNode.printInfo(level,path)»
834             «ENDFOR»
835             «FOR childNode : cases»
836                 «childNode.printInfo(level,path)»
837             «ENDFOR»
838         '''
839     }
840
841     def CharSequence xmlExample(Set<DataSchemaNode> nodes, QName name,InstanceIdentifier path) '''
842     <pre>
843         «xmlExampleTag(name,nodes.xmplExampleTags(path))»
844     </pre>
845     '''
846
847     def CharSequence xmplExampleTags(Set<DataSchemaNode> nodes, InstanceIdentifier identifier) '''
848         <!-- Child nodes -->
849         «FOR node : nodes»
850         <!-- «node.QName.localName» -->
851             «node.asXmlExampleTag(identifier)»
852         «ENDFOR»
853
854     '''
855
856     private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, InstanceIdentifier identifier) '''
857         «node.QName.xmlExampleTag("...")»
858     '''
859
860     private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, InstanceIdentifier identifier) '''
861         &lt!-- This node could appear multiple times --&gt
862         «node.QName.xmlExampleTag("...")»
863     '''
864
865     private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, InstanceIdentifier identifier) '''
866         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
867         «node.QName.xmlExampleTag("...")»
868     '''
869
870
871     private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, InstanceIdentifier identifier) '''
872         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
873         &lt!-- This node could appear multiple times --&gt
874         «node.QName.xmlExampleTag("...")»
875     '''
876
877
878     private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, InstanceIdentifier identifier) '''
879         <!-- noop -->
880     '''
881
882
883     def xmlExampleTag(QName name, CharSequence data) {
884         return '''&lt;«name.localName» xmlns="«name.namespace»"&gt;«data»&lt;/«name.localName»&gt;'''
885     }
886
887     def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
888
889
890     def header(int level,InstanceIdentifier name) '''
891         <h«level» id="«FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
892             «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
893         </h«level»>
894     '''
895
896
897
898     private def dispatch CharSequence printInfo(DataSchemaNode node, int level, InstanceIdentifier path) '''
899         «header(level+1,node.QName)»
900     '''
901
902     private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) '''
903         «val newPath = path.append(node)»
904         «header(level,newPath)»
905         <dl>
906           <dt>XML Path</dt>
907           <dd>«newPath.asXmlPath»</dd>
908           <dt>Restconf path</dt>
909           <dd>«code(newPath.asRestconfPath)»</dd>
910         </dl>
911         «node.childNodes.printChildren(level,newPath)»
912     '''
913
914     private def dispatch CharSequence printInfo(ListSchemaNode node, int level, InstanceIdentifier path) '''
915         «val newPath = path.append(node)»
916         «header(level,newPath)»
917         <dl>
918           <dt>XML Path</dt>
919           <dd>«newPath.asXmlPath»</dd>
920           <dt>Restconf path</dt>
921           <dd>«code(newPath.asRestconfPath)»</dd>
922         </dl>
923         «node.childNodes.printChildren(level,newPath)»
924     '''
925
926     private def dispatch CharSequence printInfo(ChoiceNode node, int level, InstanceIdentifier path) '''
927         «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
928         «choiceCases.printChildren(level,path)»
929     '''
930
931     private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, InstanceIdentifier path) '''
932         «node.childNodes.printChildren(level,path)»
933     '''
934
935
936
937     def CharSequence printShortInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) {
938         val newPath = path.append(node);
939         return '''
940             <li>«strong(localLink(newPath,node.QName.localName))» (container)</li>
941         '''
942     }
943
944     def CharSequence printShortInfo(ListSchemaNode node, int level, InstanceIdentifier path) {
945         val newPath = path.append(node);
946         return '''
947             <li>«strong(localLink(newPath,node.QName.localName))» (list)</li>
948         '''
949     }
950
951     def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, InstanceIdentifier path) {
952         return '''
953             <li>«strong((node.QName.localName))» (anyxml)</li>
954         '''
955     }
956
957     def CharSequence printShortInfo(LeafSchemaNode node, int level, InstanceIdentifier path) {
958         return '''
959             <li>«strong((node.QName.localName))» (leaf)</li>
960         '''
961     }
962
963     def CharSequence printShortInfo(LeafListSchemaNode node, int level, InstanceIdentifier path) {
964         return '''
965             <li>«strong((node.QName.localName))» (leaf-list)</li>
966         '''
967     }
968
969     def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
970         return '''
971             <a href="#«anchor»">«text»</a>
972         '''
973     }
974
975     def CharSequence localLink(InstanceIdentifier identifier, CharSequence text) '''
976         <a href="#«FOR cmp : identifier.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
977     '''
978
979
980     private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ContainerSchemaNode node) {
981         return identifier.node(node.QName);
982     }
983
984     private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ListSchemaNode node) {
985         val keyValues = new LinkedHashMap<QName,Object>();
986         if(node.keyDefinition !== null) {
987             for(definition : node.keyDefinition) {
988                 keyValues.put(definition,new Object);
989             }
990         }
991
992         return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
993     }
994
995
996     def asXmlPath(InstanceIdentifier identifier) {
997         return "";
998     }
999
1000     def asRestconfPath(InstanceIdentifier identifier) {
1001         val it = new StringBuilder();
1002         append(currentModule.name)
1003         append(":")
1004         var previous = false;
1005         for(arg : identifier.path) {
1006             if(previous) append("/")
1007             append(arg.nodeType.localName);
1008             previous = true;
1009             if(arg instanceof NodeIdentifierWithPredicates) {
1010                 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
1011                 for(qname : nodeIdentifier.keyValues.keySet) {
1012                     append("/{");
1013                     append(qname.localName)
1014                     append("}")
1015                 }
1016             }
1017         }
1018
1019         return it.toString;
1020     }
1021
1022     private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1023             val List<QName> path = schemaPath.path
1024         val StringBuilder pathString = new StringBuilder()
1025         if (schemaPath.absolute) {
1026             pathString.append("/")
1027         }
1028
1029         val QName qname = path.get(0)
1030         var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1031
1032         for (name : path) {
1033             if (parent instanceof DataNodeContainer) {
1034                 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)
1035                 if (node == null && (parent instanceof Module)) {
1036                     val notifications = (parent as Module).notifications;
1037                     for (notification : notifications) {
1038                         if (notification.QName.equals(name)) {
1039                             node = notification
1040                         }
1041                     }
1042                 }
1043                 if (node == null && (parent instanceof Module)) {
1044                     val rpcs = (parent as Module).rpcs;
1045                     for (rpc : rpcs) {
1046                         if (rpc.QName.equals(name)) {
1047                             node = rpc
1048                         }
1049                     }
1050                 }
1051
1052                 var String prefix = name.prefix
1053                 var String moduleName
1054                 if (prefix == null || "".equals(prefix) || prefix.equals(module.prefix)) {
1055                     moduleName = module.name
1056                 } else {
1057                     moduleName = imports.get(prefix)
1058                 }
1059                 pathString.append(moduleName)
1060                 pathString.append(":")
1061                 pathString.append(name.localName)
1062                 pathString.append("/")
1063                 if(node instanceof ChoiceNode && dataNode !== null) {
1064                     val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1065                     if(caseNode !== null) {
1066                         pathString.append("(case)");
1067                         pathString.append(caseNode.QName.localName);
1068                     }
1069                 }
1070                 parent = node
1071             }
1072         }
1073         return pathString.toString;
1074     }
1075
1076
1077     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1078         «IF childNodes !== null && !childNodes.empty»
1079             «FOR child : childNodes.values»
1080                 «childInfo(child, childNodes)»
1081             «ENDFOR»
1082         «ENDIF»
1083     '''
1084
1085     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1086         «val String path = nodeSchemaPathToPath(node, childNodes)»
1087         «IF path != null»
1088             «code(path)»
1089                 «IF node !== null»
1090                 <ul>
1091                 «node.descAndRefLi»
1092                 </ul>
1093             «ENDIF»
1094         «ENDIF»
1095     '''
1096
1097     private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, InstanceIdentifier path) '''
1098         «IF childNodes !== null && !childNodes.empty»
1099             <ul>
1100             «FOR child : childNodes»
1101                 <li>
1102                     «child.tree(path)»
1103                 </li>
1104             «ENDFOR»
1105             </ul>
1106         «ENDIF»
1107     '''
1108
1109     def listKeys(ListSchemaNode node) '''
1110         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1111     '''
1112
1113     private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1114         <ul>
1115             «ext.descAndRefLi»
1116             «listItem("Argument", ext.argument)»
1117         </ul>
1118     '''
1119
1120     private def dispatch CharSequence tree(Void obj, InstanceIdentifier path) '''
1121     '''
1122
1123
1124
1125     /* #################### RESTRICTIONS #################### */
1126     private def restrictions(TypeDefinition<?> type) '''
1127         «type.baseType.toBaseStmt»
1128         «type.toLength»
1129         «type.toRange»
1130     '''
1131
1132     private def dispatch toLength(TypeDefinition<?> type) {
1133     }
1134
1135     private def dispatch toLength(BinaryTypeDefinition type) '''
1136         «type.lengthConstraints.toLengthStmt»
1137     '''
1138
1139     private def dispatch toLength(StringTypeDefinition type) '''
1140         «type.lengthConstraints.toLengthStmt»
1141     '''
1142
1143     private def dispatch toLength(ExtendedType type) '''
1144         «type.lengthConstraints.toLengthStmt»
1145     '''
1146
1147     private def dispatch toRange(TypeDefinition<?> type) {
1148     }
1149
1150     private def dispatch toRange(DecimalTypeDefinition type) '''
1151         «type.rangeConstraints.toRangeStmt»
1152     '''
1153
1154     private def dispatch toRange(IntegerTypeDefinition type) '''
1155         «type.rangeConstraints.toRangeStmt»
1156     '''
1157
1158     private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1159         «type.rangeConstraints.toRangeStmt»
1160     '''
1161
1162     private def dispatch toRange(ExtendedType type) '''
1163         «type.rangeConstraints.toRangeStmt»
1164     '''
1165
1166     def toLengthStmt(Collection<LengthConstraint> lengths) '''
1167         «IF lengths != null && !lengths.empty»
1168             «listItem("Length restrictions:")»
1169             <ul>
1170             «FOR length : lengths»
1171                 <li>
1172                 «IF length.min == length.max»
1173                     «length.min»
1174                 «ELSE»
1175                     &lt;«length.min», «length.max»&gt;
1176                 «ENDIF»
1177                 </li>
1178             «ENDFOR»
1179             </ul>
1180         «ENDIF»
1181     '''
1182
1183     def toRangeStmt(Collection<RangeConstraint> ranges) '''
1184         «IF ranges != null && !ranges.empty»
1185             «listItem("Range restrictions:")»
1186             <ul>
1187             «FOR range : ranges»
1188                 <li>
1189                 «IF range.min == range.max»
1190                     «range.min»
1191                 «ELSE»
1192                     &lt;«range.min», «range.max»&gt;
1193                 «ENDIF»
1194                 </li>
1195             «ENDFOR»
1196             </ul>
1197         «ENDIF»
1198     '''
1199
1200     def toBaseStmt(TypeDefinition<?> baseType) '''
1201         «IF baseType != null»
1202         «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1203         «ENDIF»
1204     '''
1205
1206
1207
1208     /* #################### UTILITY #################### */
1209     private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1210     private def italic(CharSequence str) '''<i>«str»</i>'''
1211
1212     def CharSequence descAndRefLi(SchemaNode node) '''
1213         «listItem("Description", node.description)»
1214         «listItem("Reference", node.reference)»
1215     '''
1216
1217     def CharSequence descAndRef(SchemaNode node) '''
1218         «node.description»
1219         «IF node.reference !== null»
1220             Reference «node.reference»
1221         «ENDIF»
1222     '''
1223
1224     private def listItem(String value) '''
1225         «IF value !== null && !value.empty»
1226             <li>
1227                 «value»
1228             </li>
1229         «ENDIF»
1230     '''
1231
1232     private def listItem(String name, String value) '''
1233         «IF value !== null && !value.empty»
1234             <li>
1235                 «name»: «value»
1236             </li>
1237         «ENDIF»
1238     '''
1239
1240     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1241         if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
1242             return null
1243         }
1244
1245         val path = node.path.path
1246         val absolute = node.path.absolute;
1247         var StringBuilder result = new StringBuilder
1248         if (absolute) {
1249             result.append("/")
1250         }
1251         if (path !== null && !path.empty) {
1252             val List<QName> actual = new ArrayList()
1253             var i = 0;
1254             for (pathElement : path) {
1255                 actual.add(pathElement)
1256                 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1257                 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1258                     result.append(pathElement.localName)
1259                     if (i != path.size - 1) {
1260                         result.append("/")
1261                     }
1262                 }
1263                 i = i + 1
1264             }
1265         }
1266         return result.toString
1267     }
1268
1269     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1270         for (node : source) {
1271             destination.put(node.path, node)
1272             if (node instanceof DataNodeContainer) {
1273                 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1274             }
1275             if (node instanceof ChoiceNode) {
1276                 val List<DataSchemaNode> choiceCases = new ArrayList()
1277                 for (caseNode : (node as ChoiceNode).cases) {
1278                     choiceCases.add(caseNode)
1279                 }
1280                 collectChildNodes(choiceCases, destination)
1281             }
1282         }
1283     }
1284
1285     private def dispatch addedByInfo(SchemaNode node) '''
1286     '''
1287
1288     private def dispatch addedByInfo(DataSchemaNode node) '''
1289         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1290     '''
1291
1292     private def dispatch isAddedBy(SchemaNode node) {
1293         return false;
1294     }
1295
1296     private def dispatch isAddedBy(DataSchemaNode node) {
1297         if (node.augmenting || node.addedByUses) {
1298             return true
1299         } else {
1300             return false;
1301         }
1302     }
1303
1304     private def dispatch nodeName(SchemaNode node) '''
1305         «IF node.isAddedBy»
1306             «italic(node.QName.localName)»«node.addedByInfo»
1307         «ELSE»
1308             «node.QName.localName»«node.addedByInfo»
1309         «ENDIF»
1310     '''
1311
1312     private def dispatch nodeName(ContainerSchemaNode node) '''
1313         «IF node.isAddedBy»
1314             «strong(italic(node.QName.localName))»«node.addedByInfo»
1315         «ELSE»
1316             «strong(node.QName.localName)»«node.addedByInfo»
1317         «ENDIF»
1318     '''
1319
1320     private def dispatch nodeName(ListSchemaNode node) '''
1321         «IF node.isAddedBy»
1322             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1323         «ELSE»
1324             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
1325         «ENDIF»
1326     '''
1327
1328 }