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