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