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