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