6cd4960c66203c16820024a5a2b707e3014d1bbb
[mdsal.git] / code-generator / 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.opendaylight.yangtools.yang.model.util.ExtendedType
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
59 class GeneratorImpl {
60
61     File path
62     static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
63     static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
64     static val BuildContext CTX = new DefaultBuildContext();
65     var Module currentModule;
66     val Map<String, String> imports = new HashMap();
67     var SchemaContext ctx;
68     
69     StringBuilder augmentChildNodesAsString
70     
71     DataSchemaNode lastNodeInTargetPath = null
72
73     def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
74         path = targetPath;
75         path.mkdirs();
76         val it = new HashSet;
77         for (module : modulesToGen) {
78             add(generateDocumentation(module, context));
79         }
80         return it;
81     }
82
83     def generateDocumentation(Module module, SchemaContext ctx) {
84         val destination = new File(path, '''«module.name».html''')
85         this.ctx = ctx;
86         module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
87         try {
88             val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
89             val bw = new BufferedWriter(fw)
90             currentModule = module;
91             bw.append(generate(module, ctx));
92             bw.close();
93             fw.close();
94         } catch (IOException e) {
95             LOG.error(e.getMessage());
96         }
97         return destination;
98     }
99
100     def generate(Module module, SchemaContext ctx) '''
101         <!DOCTYPE html>
102         <html lang="en">
103           <head>
104             <title>«module.name»</title>
105           </head>
106           <body>
107             «body(module, ctx)»
108           </body>
109         </html>
110     '''
111
112     def body(Module module, SchemaContext ctx) '''
113         «header(module)»
114
115         «typeDefinitionsSummary(module)»
116         «identitiesSummary(module)»
117         «groupingsSummary(module)»
118         «augmentationsSummary(module, ctx)»
119         «objectsSummary(module)»
120         «notificationsSummary(module)»
121         «rpcsSummary(module)»
122         «extensionsSummary(module)»
123         «featuresSummary(module)»
124
125         «typeDefinitions(module)»
126
127         «identities(module)»
128
129         «groupings(module)»
130
131         «dataStore(module)»
132
133         «childNodes(module)»
134
135         «notifications(module)»
136
137         «augmentations(module, ctx)»
138
139         «rpcs(module)»
140
141         «extensions(module)»
142
143         «features(module)»
144
145     '''
146
147
148     private def typeDefinitionsSummary(Module module) {
149         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
150         if (typedefs.empty) {
151             return '';
152         }
153         return '''
154         <div>
155             <h3>Type Definitions Summary</h3>
156             <table>
157                 <tr>
158                     <th>Name</th>
159                     <th>Description</th>
160                 </tr>
161                 «FOR typedef : typedefs»
162                 <tr>
163                     <td>
164                     «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
165                     </td>
166                     <td>
167                     «typedef.description»
168                     </td>
169                 </tr>
170                 «ENDFOR»
171             </table>
172         </div>
173         '''
174     }
175
176     def typeDefinitions(Module module) {
177         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
178         if (typedefs.empty) {
179             return '';
180         }
181         return '''
182             <h2>Type Definitions</h2>
183             <ul>
184             «FOR typedef : typedefs»
185                 <li>
186                     <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
187                     <ul>
188                     «typedef.descAndRefLi»
189                     «typedef.restrictions»
190                     </ul>
191                 </li>
192             «ENDFOR»
193             </ul>
194         '''
195     }
196
197     private def identities(Module module) {
198         if (module.identities.empty) {
199             return '';
200         }
201         return '''
202             <h2>Identities</h2>
203             <ul>
204             «FOR identity : module.identities»
205                 <li>
206                     <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
207                     <ul>
208                     «identity.descAndRefLi»
209                     «IF identity.baseIdentity !== null»
210                         «listItem("base", identity.baseIdentity.QName.localName)»
211                     «ENDIF»
212                     </ul>
213                 </li>
214             «ENDFOR»
215             </ul>
216         '''
217     }
218
219     private def identitiesSummary(Module module) {
220         if (module.identities.empty) {
221             return '';
222         }
223         return '''
224         <h3>Identities Summary</h3>
225         <table>
226             <tr>
227                 <th>Name</th>
228                 <th>Description</th>
229             </tr>
230             «FOR identity : module.identities»
231             <tr>
232                 <td>
233                 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
234                 </td>
235                 <td>
236                 «identity.description»
237                 </td>
238             </tr>
239             «ENDFOR»
240         </table>
241         '''
242     }
243
244     private def groupings(Module module) {
245         if (module.groupings.empty) {
246             return '';
247         }
248         return '''
249             <h2>Groupings</h2>
250             <ul>
251             «FOR grouping : module.groupings»
252                 <li>
253                     <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
254                     <ul>
255                         «grouping.descAndRefLi»
256                         «FOR childNode : grouping.childNodes»
257                             «childNode.printSchemaNodeInfo»
258                         «ENDFOR»
259                     </ul>
260                 </li>
261             «ENDFOR»
262             </ul>
263         '''
264     }
265
266     private def groupingsSummary(Module module) {
267         if (module.groupings.empty) {
268             return '';
269         }
270         return '''
271         <h3>Groupings Summary</h3>
272         <table>
273             <tr>
274                 <th>Name</th>
275                 <th>Description</th>
276             </tr>
277             «FOR grouping : module.groupings»
278             <tr>
279                 <td>
280                 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
281                 </td>
282                 <td>
283                 «grouping.description»
284                 </td>
285             </tr>
286             «ENDFOR»
287         </table>
288         '''
289     }
290
291     def dataStore(Module module) {
292         if (module.childNodes.empty) {
293             return '';
294         }
295         return '''
296             <h2>Datastore Structure</h2>
297             «tree(module)»
298         '''
299     }
300
301     def augmentations(Module module, SchemaContext context) {
302         if (module.augmentations.empty) {
303             return '';
304         }
305         return '''
306             <h2>Augmentations</h2>
307             
308             <ul>
309             «FOR augment : module.augmentations»
310                 <li>
311                     <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
312                     Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
313                     «augment.description»
314                         Status: «strong(String.valueOf(augment.status))»
315                     «IF augment.reference !== null»
316                         Reference «augment.reference»
317                     «ENDIF»
318                     «IF augment.whenCondition !== null»
319                         When «augment.whenCondition.toString»
320                     «ENDIF»
321                     «FOR childNode : augment.childNodes»
322                         «childNode.printSchemaNodeInfo»
323                     «ENDFOR»
324                     
325                     <h3>Example</h3>
326                     «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
327                     «printNodeChildren(parseTargetPath(augment.targetPath))»
328                 </li>
329             «ENDFOR»
330             </ul>
331         '''
332     }
333     
334     private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
335         augmentChildNodesAsString = new StringBuilder();
336         augmentChildNodesAsString.append(printNodeChildren(childNodes))
337         return ''
338     }
339     
340     private def parseTargetPath(SchemaPath path) {
341         val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
342         for (QName pathElement : path.pathFromRoot) {
343             val module = ctx.findModuleByNamespaceAndRevision(pathElement.namespace, pathElement.revision);
344             if (module !== null) {
345                 var foundNode = module.getDataChildByName(pathElement)
346                 if(foundNode == null) {
347                     val child = nodes.last
348                     if (child instanceof DataNodeContainer) {
349                         val dataContNode = child as DataNodeContainer
350                         foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
351                     }
352                 }
353                 if(foundNode != null) {
354                     nodes.add(foundNode);
355                 }
356             }
357         }
358         if(! nodes.empty) {
359             lastNodeInTargetPath = nodes.get(nodes.size() - 1)
360         }
361         
362         val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
363         targetPathNodes.add(lastNodeInTargetPath)
364         
365         return targetPathNodes
366     }
367     
368     private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<DataSchemaNode> childNodes) {
369         for(child : childNodes) {
370             if (child.QName.equals(findingNode))
371                 return child;
372         }
373         // find recursively
374         for(child : childNodes) {
375             if(child instanceof ContainerSchemaNode) {
376                 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
377                 if (foundChild != null)
378                     return foundChild;
379             }
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.path 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 = schemaPath.path
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 toLength(ExtendedType type) '''
1312         «type.lengthConstraints.toLengthStmt»
1313     '''
1314
1315     private def dispatch toRange(TypeDefinition<?> type) {
1316     }
1317
1318     private def dispatch toRange(DecimalTypeDefinition type) '''
1319         «type.rangeConstraints.toRangeStmt»
1320     '''
1321
1322     private def dispatch toRange(IntegerTypeDefinition type) '''
1323         «type.rangeConstraints.toRangeStmt»
1324     '''
1325
1326     private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1327         «type.rangeConstraints.toRangeStmt»
1328     '''
1329
1330     private def dispatch toRange(ExtendedType type) '''
1331         «type.rangeConstraints.toRangeStmt»
1332     '''
1333
1334     def toLengthStmt(Collection<LengthConstraint> lengths) '''
1335         «IF lengths != null && !lengths.empty»
1336             «listItem("Length restrictions:")»
1337             <ul>
1338             «FOR length : lengths»
1339                 <li>
1340                 «IF length.min == length.max»
1341                     «length.min»
1342                 «ELSE»
1343                     &lt;«length.min», «length.max»&gt;
1344                 «ENDIF»
1345                 </li>
1346             «ENDFOR»
1347             </ul>
1348         «ENDIF»
1349     '''
1350
1351     def toRangeStmt(Collection<RangeConstraint> ranges) '''
1352         «IF ranges != null && !ranges.empty»
1353             «listItem("Range restrictions:")»
1354             <ul>
1355             «FOR range : ranges»
1356                 <li>
1357                 «IF range.min == range.max»
1358                     «range.min»
1359                 «ELSE»
1360                     &lt;«range.min», «range.max»&gt;
1361                 «ENDIF»
1362                 </li>
1363             «ENDFOR»
1364             </ul>
1365         «ENDIF»
1366     '''
1367
1368     def toBaseStmt(TypeDefinition<?> baseType) '''
1369         «IF baseType != null»
1370         «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1371         «ENDIF»
1372     '''
1373
1374
1375
1376     /* #################### UTILITY #################### */
1377     private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1378     private def italic(CharSequence str) '''<i>«str»</i>'''
1379
1380     def CharSequence descAndRefLi(SchemaNode node) '''
1381         «listItem("Description", node.description)»
1382         «listItem("Reference", node.reference)»
1383     '''
1384
1385     def CharSequence descAndRef(SchemaNode node) '''
1386         «node.description»
1387         «IF node.reference !== null»
1388             Reference «node.reference»
1389         «ENDIF»
1390     '''
1391
1392     private def listItem(String value) '''
1393         «IF value !== null && !value.empty»
1394             <li>
1395                 «value»
1396             </li>
1397         «ENDIF»
1398     '''
1399
1400     private def listItem(String name, String value) '''
1401         «IF value !== null && !value.empty»
1402             <li>
1403                 «name»: «value»
1404             </li>
1405         «ENDIF»
1406     '''
1407
1408     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1409         if (node instanceof ChoiceSchemaNode || node instanceof ChoiceCaseNode) {
1410             return null
1411         }
1412
1413         val path = node.path.path
1414         val absolute = node.path.absolute;
1415         var StringBuilder result = new StringBuilder
1416         if (absolute) {
1417             result.append("/")
1418         }
1419         if (path !== null && !path.empty) {
1420             val List<QName> actual = new ArrayList()
1421             var i = 0;
1422             for (pathElement : path) {
1423                 actual.add(pathElement)
1424                 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1425                 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1426                     result.append(pathElement.localName)
1427                     if (i != path.size - 1) {
1428                         result.append("/")
1429                     }
1430                 }
1431                 i = i + 1
1432             }
1433         }
1434         return result.toString
1435     }
1436
1437     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1438         for (node : source) {
1439             destination.put(node.path, node)
1440             if (node instanceof DataNodeContainer) {
1441                 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1442             }
1443             if (node instanceof ChoiceSchemaNode) {
1444                 val List<DataSchemaNode> choiceCases = new ArrayList()
1445                 for (caseNode : node.cases) {
1446                     choiceCases.add(caseNode)
1447                 }
1448                 collectChildNodes(choiceCases, destination)
1449             }
1450         }
1451     }
1452
1453     private def dispatch addedByInfo(SchemaNode node) '''
1454     '''
1455
1456     private def dispatch addedByInfo(DataSchemaNode node) '''
1457         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1458     '''
1459
1460     private def dispatch isAddedBy(SchemaNode node) {
1461         return false;
1462     }
1463
1464     private def dispatch isAddedBy(DataSchemaNode node) {
1465         if (node.augmenting || node.addedByUses) {
1466             return true
1467         } else {
1468             return false;
1469         }
1470     }
1471
1472     private def dispatch nodeName(SchemaNode node) '''
1473         «IF node.isAddedBy»
1474             «italic(node.QName.localName)»«node.addedByInfo»
1475         «ELSE»
1476             «node.QName.localName»«node.addedByInfo»
1477         «ENDIF»
1478     '''
1479
1480     private def dispatch nodeName(ContainerSchemaNode node) '''
1481         «IF node.isAddedBy»
1482             «strong(italic(node.QName.localName))»«node.addedByInfo»
1483         «ELSE»
1484             «strong(node.QName.localName)»«node.addedByInfo»
1485         «ENDIF»
1486     '''
1487
1488     private def dispatch nodeName(ListSchemaNode node) '''
1489         «IF node.isAddedBy»
1490             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1491         «ELSE»
1492             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
1493         «ENDIF»
1494     '''
1495
1496 }