Fix spotbugs complaints around logging
[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.ElementCountConstraintAware
38 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
43 import org.opendaylight.yangtools.yang.model.api.Module
44 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext
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 SchemaContext ctx;
75     var File path
76
77     StringBuilder augmentChildNodesAsString
78
79     DataSchemaNode lastNodeInTargetPath = null
80
81     def generate(BuildContext buildContext, SchemaContext 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, SchemaContext 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, SchemaContext 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, SchemaContext 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 Set<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 Set<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, SchemaContext 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<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, SchemaContext 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 Set<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                 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
822                 «FOR typeDef : typeDefinitions»
823                     «typeDef.restrictions»
824                 «ENDFOR»
825                 </ul>
826                 <ul>
827                 «FOR grouping : dataNode.groupings»
828                     «grouping.printGrouping»
829                 «ENDFOR»
830                 </ul>
831                 <ul>
832                 «FOR child : dataNode.childNodes»
833                     «child.printSchemaNodeInfo»
834                 «ENDFOR»
835                 </ul>
836             «ENDIF»
837             </ul>
838         '''
839     }
840
841     def String typeAnchorLink(SchemaPath path, CharSequence text) {
842         if(path !== null) {
843             val lastElement = Iterables.getLast(path.pathFromRoot)
844             val ns = lastElement.namespace
845             if (ns == this.currentModule.namespace) {
846                 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
847             } else {
848                 return '''(«ns»)«text»'''
849                 //to enable external (import) links
850                 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
851             }
852         }
853     }
854
855     def CharSequence printBaseInfo(SchemaNode node) {
856         if(node instanceof LeafSchemaNode) {
857             return '''
858                 «printInfo(node, "leaf")»
859                 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
860                 «listItem("units", node.type.units.orElse(null))»
861                 «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))»
862                 </ul>
863             '''
864         } else if(node instanceof LeafListSchemaNode) {
865             return '''
866                 «printInfo(node, "leaf-list")»
867                 «listItem("type", node.type?.QName.localName)»
868                 </ul>
869             '''
870         } else if(node instanceof ListSchemaNode) {
871             return '''
872                 «printInfo(node, "list")»
873                 «FOR keyDef : node.keyDefinition»
874                     «listItem("key definition", keyDef.localName)»
875                 «ENDFOR»
876                 </ul>
877             '''
878         } else if(node instanceof ChoiceSchemaNode) {
879             return '''
880                 «printInfo(node, "choice")»
881                 «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))»
882                 «FOR caseNode : node.cases.values»
883                     «caseNode.printSchemaNodeInfo»
884                 «ENDFOR»
885                 </ul>
886             '''
887         } else if(node instanceof CaseSchemaNode) {
888             return '''
889                 «printInfo(node, "case")»
890                 </ul>
891             '''
892         } else if(node instanceof ContainerSchemaNode) {
893             return '''
894                 «printInfo(node, "container")»
895                 </ul>
896             '''
897         } else if(node instanceof AnyxmlSchemaNode) {
898             return '''
899                 «printInfo(node, "anyxml")»
900                 </ul>
901             '''
902         }
903     }
904
905     def CharSequence printInfo(SchemaNode node, String nodeType) {
906         return '''
907             «IF node instanceof AugmentationTarget»
908                 «IF node !== null»
909                     <strong>
910                     <li id="«node.path.schemaPathToId»">
911                         «nodeType»: «node.QName.localName»
912                     </li>
913                     </strong>
914                 «ENDIF»
915             «ELSE»
916                 «strong(listItem(nodeType, node.QName.localName))»
917             «ENDIF»
918             <ul>
919             «listItem("description", node.description.orElse(null))»
920             «listItem("reference", node.reference.orElse(null))»
921             «IF node instanceof DataSchemaNode»
922                 «IF node.whenCondition.present»
923                 «listItem("when condition", node.whenCondition.get.toString)»
924                 «ENDIF»
925             «ENDIF»
926             «IF node instanceof ElementCountConstraintAware»
927                 «IF node.elementCountConstraint.present»
928                     «val constraint = node.elementCountConstraint.get»
929                     «listItem("min elements", constraint.minElements?.toString)»
930                     «listItem("max elements", constraint.maxElements?.toString)»
931                 «ENDIF»
932             «ENDIF»
933         '''
934     }
935
936     def CharSequence printUses(UsesNode usesNode) {
937         return '''
938             «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
939             <ul>
940             <li>refines:
941                 <ul>
942                 «FOR sp : usesNode.refines.keySet»
943                     «listItem("node name", usesNode.refines.get(sp).QName.localName)»
944                 «ENDFOR»
945                 </ul>
946             </li>
947             «FOR augment : usesNode.augmentations»
948                 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
949             «ENDFOR»
950             </ul>
951         '''
952     }
953
954     def CharSequence printGrouping(GroupingDefinition grouping) {
955         return '''
956             «strong(listItem("grouping", grouping.QName.localName))»
957         '''
958     }
959
960     def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
961         val anyxmlNodes = nodes.filter(AnyxmlSchemaNode)
962         val leafNodes = nodes.filter(LeafSchemaNode)
963         val leafListNodes = nodes.filter(LeafListSchemaNode)
964         val choices = nodes.filter(ChoiceSchemaNode)
965         val cases = nodes.filter(CaseSchemaNode)
966         val containers = nodes.filter(ContainerSchemaNode)
967         val lists = nodes.filter(ListSchemaNode)
968         return '''
969             «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
970             <h3>Direct children</h3>
971             <ul>
972             «FOR childNode : anyxmlNodes»
973                 «childNode.printShortInfo(level,path)»
974             «ENDFOR»
975             «FOR childNode : leafNodes»
976                 «childNode.printShortInfo(level,path)»
977             «ENDFOR»
978             «FOR childNode : leafListNodes»
979                 «childNode.printShortInfo(level,path)»
980             «ENDFOR»
981             «FOR childNode : containers»
982                 «childNode.printShortInfo(level,path)»
983             «ENDFOR»
984             «FOR childNode : lists»
985                 «childNode.printShortInfo(level,path)»
986             «ENDFOR»
987             </ul>
988             «ENDIF»
989
990             «IF path.pathArguments.iterator.hasNext»
991             <h3>XML example</h3>
992             «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
993             </h3>
994             «ENDIF»
995             «FOR childNode : containers»
996                 «childNode.printInfo(level,path)»
997             «ENDFOR»
998             «FOR childNode : lists»
999                 «childNode.printInfo(level,path)»
1000             «ENDFOR»
1001             «FOR childNode : choices»
1002                 «childNode.printInfo(level,path)»
1003             «ENDFOR»
1004             «FOR childNode : cases»
1005                 «childNode.printInfo(level,path)»
1006             «ENDFOR»
1007         '''
1008     }
1009
1010     def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
1011     <pre>
1012         «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1013     </pre>
1014     '''
1015
1016     def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1017         <!-- Child nodes -->
1018         «FOR node : nodes»
1019         <!-- «node.QName.localName» -->
1020             «node.asXmlExampleTag(identifier)»
1021         «ENDFOR»
1022
1023     '''
1024
1025     private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1026         «node.QName.xmlExampleTag("...")»
1027     '''
1028
1029     private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1030         &lt!-- This node could appear multiple times --&gt
1031         «node.QName.xmlExampleTag("...")»
1032     '''
1033
1034     private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1035         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
1036         «node.QName.xmlExampleTag("...")»
1037     '''
1038
1039
1040     private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1041         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
1042         &lt!-- This node could appear multiple times --&gt
1043         «node.QName.xmlExampleTag("...")»
1044     '''
1045
1046
1047     private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1048         <!-- noop -->
1049     '''
1050
1051
1052     def xmlExampleTag(QName name, CharSequence data) {
1053         return '''&lt;«name.localName» xmlns="«name.namespace»"&gt;«data»&lt;/«name.localName»&gt;'''
1054     }
1055
1056     def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1057
1058
1059     def header(int level,YangInstanceIdentifier name) '''
1060         <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1061             «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1062         </h«level»>
1063     '''
1064
1065
1066
1067     private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1068         «header(level+1,node.QName)»
1069     '''
1070
1071     private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1072         «val newPath = path.append(node)»
1073         «header(level,newPath)»
1074         <dl>
1075           <dt>XML Path</dt>
1076           <dd>«newPath.asXmlPath»</dd>
1077           <dt>Restconf path</dt>
1078           <dd>«code(newPath.asRestconfPath)»</dd>
1079         </dl>
1080         «node.childNodes.printChildren(level,newPath)»
1081     '''
1082
1083     private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1084         «val newPath = path.append(node)»
1085         «header(level,newPath)»
1086         <dl>
1087           <dt>XML Path</dt>
1088           <dd>«newPath.asXmlPath»</dd>
1089           <dt>Restconf path</dt>
1090           <dd>«code(newPath.asRestconfPath)»</dd>
1091         </dl>
1092         «node.childNodes.printChildren(level,newPath)»
1093     '''
1094
1095     private def dispatch CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1096         «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases.values)»
1097         «choiceCases.printChildren(level, path)»
1098     '''
1099
1100     private def dispatch CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) '''
1101         «node.childNodes.printChildren(level, path)»
1102     '''
1103
1104
1105
1106     def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1107         val newPath = path.append(node);
1108         return '''
1109             <li>«strong(localLink(newPath,node.QName.localName))» (container)
1110             <ul>
1111                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1112             </ul>
1113             </li>
1114         '''
1115     }
1116
1117     def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1118         val newPath = path.append(node);
1119         return '''
1120             <li>«strong(localLink(newPath,node.QName.localName))» (list)
1121             <ul>
1122                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1123             </ul>
1124             </li>
1125         '''
1126     }
1127
1128     def CharSequence printShortInfo(AnyxmlSchemaNode node, int level, YangInstanceIdentifier path) {
1129         return '''
1130             <li>«strong((node.QName.localName))» (anyxml)
1131             <ul>
1132                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1133                 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1134             </ul>
1135             </li>
1136         '''
1137     }
1138
1139     def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1140         return '''
1141             <li>«strong((node.QName.localName))» (leaf)
1142             <ul>
1143                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1144                 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1145             </ul>
1146             </li>
1147         '''
1148     }
1149
1150     def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1151         return '''
1152             <li>«strong((node.QName.localName))» (leaf-list)
1153             <ul>
1154                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1155             </ul>
1156             </li>
1157         '''
1158     }
1159
1160     def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1161         return '''
1162             <a href="#«anchor»">«text»</a>
1163         '''
1164     }
1165
1166     def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1167         <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1168     '''
1169
1170
1171     private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1172         return identifier.node(node.QName);
1173     }
1174
1175     private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1176         val keyValues = new LinkedHashMap<QName,Object>();
1177         if(node.keyDefinition !== null) {
1178             for(definition : node.keyDefinition) {
1179                 keyValues.put(definition,new Object);
1180             }
1181         }
1182
1183         return identifier.node(NodeIdentifierWithPredicates.of(node.QName, keyValues));
1184     }
1185
1186
1187     def asXmlPath(YangInstanceIdentifier identifier) {
1188         return "";
1189     }
1190
1191     def asRestconfPath(YangInstanceIdentifier identifier) {
1192         val it = new StringBuilder();
1193         append(currentModule.name)
1194         append(':')
1195         var previous = false;
1196         for(arg : identifier.pathArguments) {
1197             if(previous) append('/')
1198             append(arg.nodeType.localName);
1199             previous = true;
1200             if(arg instanceof NodeIdentifierWithPredicates) {
1201                 for(qname : arg.keySet) {
1202                     append("/{");
1203                     append(qname.localName)
1204                     append('}')
1205                 }
1206             }
1207         }
1208
1209         return it.toString;
1210     }
1211
1212     private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1213             val List<QName> path = Lists.newArrayList(schemaPath.pathFromRoot);
1214         val StringBuilder pathString = new StringBuilder()
1215         if (schemaPath.absolute) {
1216             pathString.append('/')
1217         }
1218
1219         val QName qname = path.get(0)
1220         var Object parent = ctx.findModule(qname.module).orElse(null)
1221
1222         for (name : path) {
1223             if (parent instanceof DataNodeContainer) {
1224                 var SchemaNode node = parent.getDataChildByName(name)
1225                 if (node === null && (parent instanceof Module)) {
1226                     val notifications = (parent as Module).notifications;
1227                     for (notification : notifications) {
1228                         if (notification.QName.equals(name)) {
1229                             node = notification
1230                         }
1231                     }
1232                 }
1233                 if (node === null && (parent instanceof Module)) {
1234                     val rpcs = (parent as Module).rpcs;
1235                     for (rpc : rpcs) {
1236                         if (rpc.QName.equals(name)) {
1237                             node = rpc
1238                         }
1239                     }
1240                 }
1241
1242                 val pathElementModule = ctx.findModule(name.module).get
1243                 val String moduleName = pathElementModule.name
1244                 pathString.append(moduleName)
1245                 pathString.append(':')
1246                 pathString.append(name.localName)
1247                 pathString.append('/')
1248                 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1249                     val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode];
1250                     if(caseNode !== null) {
1251                         pathString.append("(case)");
1252                         pathString.append(caseNode.QName.localName);
1253                     }
1254                 }
1255                 parent = node
1256             }
1257         }
1258         return pathString.toString;
1259     }
1260
1261
1262     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1263         «IF childNodes !== null && !childNodes.empty»
1264             «FOR child : childNodes.values»
1265                 «childInfo(child, childNodes)»
1266             «ENDFOR»
1267         «ENDIF»
1268     '''
1269
1270     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1271         «val String path = nodeSchemaPathToPath(node, childNodes)»
1272         «IF path !== null»
1273             «code(path)»
1274                 «IF node !== null»
1275                 <ul>
1276                 «node.descAndRefLi»
1277                 </ul>
1278             «ENDIF»
1279         «ENDIF»
1280     '''
1281
1282     private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1283         «IF childNodes !== null && !childNodes.empty»
1284             <ul>
1285             «FOR child : childNodes»
1286                 <li>
1287                     «child.tree(path)»
1288                 </li>
1289             «ENDFOR»
1290             </ul>
1291         «ENDIF»
1292     '''
1293
1294     def listKeys(ListSchemaNode node) '''
1295         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1296     '''
1297
1298     private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1299         <ul>
1300             «ext.descAndRefLi»
1301             «listItem("Argument", ext.argument)»
1302         </ul>
1303     '''
1304
1305     private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1306     '''
1307
1308
1309
1310     /* #################### RESTRICTIONS #################### */
1311     private def restrictions(TypeDefinition<?> type) '''
1312         «type.baseType.toBaseStmt»
1313         «type.toLength»
1314         «type.toRange»
1315     '''
1316
1317     private def dispatch toLength(TypeDefinition<?> type) {
1318     }
1319
1320     private def dispatch toLength(BinaryTypeDefinition type) '''
1321         «type.lengthConstraint.toLengthStmt»
1322     '''
1323
1324     private def dispatch toLength(StringTypeDefinition type) '''
1325         «type.lengthConstraint.toLengthStmt»
1326     '''
1327
1328     private def dispatch toRange(TypeDefinition<?> type) {
1329     }
1330
1331     private def dispatch toRange(DecimalTypeDefinition type) '''
1332         «type.rangeConstraint.toRangeStmt»
1333     '''
1334
1335     private def dispatch toRange(Int8TypeDefinition type) '''
1336         «type.rangeConstraint.toRangeStmt»
1337     '''
1338
1339     private def dispatch toRange(Int16TypeDefinition type) '''
1340         «type.rangeConstraint.toRangeStmt»
1341     '''
1342
1343     private def dispatch toRange(Int32TypeDefinition type) '''
1344         «type.rangeConstraint.toRangeStmt»
1345     '''
1346
1347     private def dispatch toRange(Int64TypeDefinition type) '''
1348         «type.rangeConstraint.toRangeStmt»
1349     '''
1350
1351     private def dispatch toRange(Uint8TypeDefinition type) '''
1352         «type.rangeConstraint.toRangeStmt»
1353     '''
1354
1355     private def dispatch toRange(Uint16TypeDefinition type) '''
1356         «type.rangeConstraint.toRangeStmt»
1357     '''
1358
1359     private def dispatch toRange(Uint32TypeDefinition type) '''
1360         «type.rangeConstraint.toRangeStmt»
1361     '''
1362
1363     private def dispatch toRange(Uint64TypeDefinition type) '''
1364         «type.rangeConstraint.toRangeStmt»
1365     '''
1366
1367     def toLengthStmt(Optional<LengthConstraint> lengths) '''
1368         «IF lengths.isPresent»
1369             «listItem("Length restrictions:")»
1370             <ul>
1371             «FOR length : lengths.get.allowedRanges.asRanges»
1372                 <li>
1373                 «IF length.lowerEndpoint == length.upperEndpoint»
1374                     «length.lowerEndpoint»
1375                 «ELSE»
1376                     &lt;«length.lowerEndpoint», «length.upperEndpoint»&gt;
1377                 «ENDIF»
1378                 </li>
1379             «ENDFOR»
1380             </ul>
1381         «ENDIF»
1382     '''
1383
1384     def toRangeStmt(Optional<? extends RangeConstraint<?>> constraint) '''
1385         «IF constraint.present»
1386             «listItem("Range restrictions:")»
1387             <ul>
1388             «FOR range : constraint.get.allowedRanges.asRanges»
1389                 <li>
1390                 «IF range.lowerEndpoint == range.upperEndpoint»
1391                     «range.lowerEndpoint»
1392                 «ELSE»
1393                     &lt;«range.lowerEndpoint», «range.upperEndpoint»&gt;
1394                 «ENDIF»
1395                 </li>
1396             «ENDFOR»
1397             </ul>
1398         «ENDIF»
1399     '''
1400
1401     def toBaseStmt(TypeDefinition<?> baseType) '''
1402         «IF baseType !== null»
1403         «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1404         «ENDIF»
1405     '''
1406
1407
1408
1409     /* #################### UTILITY #################### */
1410     private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1411     private def italic(CharSequence str) '''<i>«str»</i>'''
1412
1413     def CharSequence descAndRefLi(SchemaNode node) '''
1414         «listItem("Description", node.description.orElse(null))»
1415         «listItem("Reference", node.reference.orElse(null))»
1416     '''
1417
1418     def CharSequence descAndRef(SchemaNode node) '''
1419         «node.description»
1420         «IF node.reference !== null»
1421             Reference «node.reference»
1422         «ENDIF»
1423     '''
1424
1425     private def listItem(String value) '''
1426         «IF value !== null && !value.empty»
1427             <li>
1428                 «value»
1429             </li>
1430         «ENDIF»
1431     '''
1432
1433     private def listItem(String name, String value) '''
1434         «IF value !== null && !value.empty»
1435             <li>
1436                 «name»: «value»
1437             </li>
1438         «ENDIF»
1439     '''
1440
1441     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1442         if (node instanceof ChoiceSchemaNode || node instanceof CaseSchemaNode) {
1443             return null
1444         }
1445
1446         val path = node.path.pathFromRoot
1447         val absolute = node.path.absolute;
1448         var StringBuilder result = new StringBuilder
1449         if (absolute) {
1450             result.append('/')
1451         }
1452         if (path !== null && !path.empty) {
1453             val List<QName> actual = new ArrayList()
1454             var i = 0;
1455             for (pathElement : path) {
1456                 actual.add(pathElement)
1457                 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1458                 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof CaseSchemaNode)) {
1459                     result.append(pathElement.localName)
1460                     if (i != path.size - 1) {
1461                         result.append('/')
1462                     }
1463                 }
1464                 i = i + 1
1465             }
1466         }
1467         return result.toString
1468     }
1469
1470     private def dispatch addedByInfo(SchemaNode node) '''
1471     '''
1472
1473     private def dispatch addedByInfo(DataSchemaNode node) '''
1474         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1475     '''
1476
1477     private def dispatch isAddedBy(SchemaNode node) {
1478         return false;
1479     }
1480
1481     private def dispatch isAddedBy(DataSchemaNode node) {
1482         if (node.augmenting || node.addedByUses) {
1483             return true
1484         } else {
1485             return false;
1486         }
1487     }
1488
1489     private def dispatch nodeName(SchemaNode node) '''
1490         «IF node.isAddedBy»
1491             «italic(node.QName.localName)»«node.addedByInfo»
1492         «ELSE»
1493             «node.QName.localName»«node.addedByInfo»
1494         «ENDIF»
1495     '''
1496
1497     private def dispatch nodeName(ContainerSchemaNode node) '''
1498         «IF node.isAddedBy»
1499             «strong(italic(node.QName.localName))»«node.addedByInfo»
1500         «ELSE»
1501             «strong(node.QName.localName)»«node.addedByInfo»
1502         «ENDIF»
1503     '''
1504
1505     private def dispatch nodeName(ListSchemaNode node) '''
1506         «IF node.isAddedBy»
1507             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1508         «ELSE»
1509             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
1510         «ENDIF»
1511     '''
1512
1513 }