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