672f26eb1e04f3087e3063f12e9d5b9aa12ec56c
[yangtools.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.ChoiceNode
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 contChild = child as ContainerSchemaNode
377                 val foundChild = findNodeInChildNodes(findingNode, contChild.childNodes)
378                 if (foundChild != null)
379                     return foundChild;
380             }
381             else if(child instanceof ListSchemaNode) {
382                 val listChild = child as ListSchemaNode
383                 val foundChild = findNodeInChildNodes(findingNode, listChild.childNodes)
384                 if (foundChild != null)
385                     return foundChild;
386             }
387         }
388     }
389     
390     private def printNodeChildren(List<DataSchemaNode> childNodes) {
391         if (childNodes.empty) {
392             return ''
393         }
394         
395         return 
396         '''
397         <pre>
398         «printAugmentedNode(childNodes.get(0))»
399         </pre>
400         '''
401     }
402     
403     private def CharSequence printAugmentedNode(DataSchemaNode child) {
404         
405         if(child instanceof ChoiceCaseNode)
406             return ''
407             
408         return
409         '''
410         «IF child instanceof ContainerSchemaNode»
411             «printContainerNode(child as ContainerSchemaNode)»
412         «ENDIF»
413         «IF child instanceof AnyXmlSchemaNode»
414             «printAnyXmlNode(child as AnyXmlSchemaNode)»
415         «ENDIF»
416         «IF child instanceof LeafSchemaNode»
417             «printLeafNode(child as LeafSchemaNode)»
418         «ENDIF»
419         «IF child instanceof LeafListSchemaNode»
420             «printLeafListNode(child as LeafListSchemaNode)»
421         «ENDIF»
422         «IF child instanceof ListSchemaNode»
423             «printListNode(child as ListSchemaNode)»
424         «ENDIF»
425         «IF child instanceof ChoiceNode»
426             «printChoiceNode(child as ChoiceNode)»
427         «ENDIF»
428         '''
429     }
430     
431     private def printChoiceNode(ChoiceNode child) {
432         val List<ChoiceCaseNode> cases = new ArrayList(child.cases);
433         if(!cases.empty) {
434             val ChoiceCaseNode aCase = cases.get(0)
435             for(caseChildNode : aCase.childNodes)
436                 printAugmentedNode(caseChildNode)
437         }
438     }
439     
440     private def printListNode(ListSchemaNode listNode) {
441         return
442         '''
443             &lt;«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»&gt;
444                 «FOR child : listNode.childNodes»
445                     «printAugmentedNode(child)»
446                 «ENDFOR»
447             &lt;/«listNode.QName.localName»&gt;
448         '''
449     }
450     
451     private def printContainerNode(ContainerSchemaNode containerNode) {
452         return
453         '''
454             &lt;«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»&gt;
455                 «FOR child : containerNode.childNodes»
456                     «printAugmentedNode(child)»
457                 «ENDFOR»
458             &lt;/«containerNode.QName.localName»&gt;
459         '''
460     }
461     
462     private def printLeafListNode(LeafListSchemaNode leafListNode) {
463         return
464         '''
465             &lt;«leafListNode.QName.localName»&gt;. . .&lt;/«leafListNode.QName.localName»&gt;
466             &lt;«leafListNode.QName.localName»&gt;. . .&lt;/«leafListNode.QName.localName»&gt;
467             &lt;«leafListNode.QName.localName»&gt;. . .&lt;/«leafListNode.QName.localName»&gt;
468         '''
469     }
470     
471     private def printAnyXmlNode(AnyXmlSchemaNode anyXmlNode) {
472         return 
473         '''
474             &lt;«anyXmlNode.QName.localName»&gt;. . .&lt;/«anyXmlNode.QName.localName»&gt;
475         '''
476     }
477     
478     private def printLeafNode(LeafSchemaNode leafNode) {
479         return 
480         '''
481             &lt;«leafNode.QName.localName»&gt;. . .&lt;/«leafNode.QName.localName»&gt;
482         '''
483     }
484
485     private def augmentationsSummary(Module module, SchemaContext context) {
486         if (module.augmentations.empty) {
487             return '';
488         }
489         return '''
490         <h3>Augmentations Summary</h3>
491         <table>
492             <tr>
493                 <th>Target</th>
494                 <th>Description</th>
495             </tr>
496             «FOR augment : module.augmentations»
497             <tr>
498                 <td>
499                 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
500                 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
501                 </td>
502                 <td>
503                 «augment.description»
504                 </td>
505             </tr>
506             «ENDFOR»
507         </table>
508         '''
509     }
510
511     def notifications(Module module) {
512         val Set<NotificationDefinition> notificationdefs = module.notifications
513         if (notificationdefs.empty) {
514             return '';
515         }
516
517         return '''
518             <h2>Notifications</h2>
519             «FOR notificationdef : notificationdefs»
520
521                 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
522                     «notificationdef.descAndRef»
523                     «FOR childNode : notificationdef.childNodes»
524                         «childNode.printSchemaNodeInfo»
525                     «ENDFOR»
526             «ENDFOR»
527         '''
528     }
529
530     private def notificationsSummary(Module module) {
531         if (module.notifications.empty) {
532             return '';
533         }
534         return '''
535         <h3>Notifications Summary</h3>
536         <table>
537             <tr>
538                 <th>Name</th>
539                 <th>Description</th>
540             </tr>
541             «FOR notification : module.notifications»
542             <tr>
543                 <td>
544                 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
545                 </td>
546                 <td>
547                 «notification.description»
548                 </td>
549             </tr>
550             «ENDFOR»
551         </table>
552         '''
553     }
554
555     def rpcs(Module module) {
556         if (module.rpcs.empty) {
557             return '';
558         }
559
560         return '''
561             <h2>RPC Definitions</h2>
562             «FOR rpc : module.rpcs»
563                 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
564                     <ul>
565                         «rpc.descAndRefLi»
566                         «rpc.input.printSchemaNodeInfo»
567                         «rpc.output.printSchemaNodeInfo»
568                     </ul>
569             «ENDFOR»
570             </ul>
571         '''
572     }
573
574     private def rpcsSummary(Module module) {
575         if (module.rpcs.empty) {
576             return '';
577         }
578         return '''
579         <h3>RPCs Summary</h3>
580         <table>
581             <tr>
582                 <th>Name</th>
583                 <th>Description</th>
584             </tr>
585             «FOR rpc : module.rpcs»
586             <tr>
587                 <td>
588                 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
589                 </td>
590                 <td>
591                 «rpc.description»
592                 </td>
593             </tr>
594             «ENDFOR»
595         </table>
596         '''
597     }
598
599     def extensions(Module module) {
600         if (module.extensionSchemaNodes.empty) {
601             return '';
602         }
603         return '''
604             <h2>Extensions</h2>
605             «FOR ext : module.extensionSchemaNodes»
606                 <li>
607                     <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
608                 </li>
609                 «extensionInfo(ext)»
610             «ENDFOR»
611         '''
612     }
613
614     private def extensionsSummary(Module module) {
615         if (module.extensionSchemaNodes.empty) {
616             return '';
617         }
618         return '''
619         <h3>Extensions Summary</h3>
620         <table>
621             <tr>
622                 <th>Name</th>
623                 <th>Description</th>
624             </tr>
625             «FOR ext : module.extensionSchemaNodes»
626             <tr>
627                 <td>
628                 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
629                 </td>
630                 <td>
631                 «ext.description»
632                 </td>
633             </tr>
634             «ENDFOR»
635         </table>
636         '''
637     }
638
639     def features(Module module) {
640         if (module.features.empty) {
641             return '';
642         }
643         return '''
644             <h2>Features</h2>
645
646             <ul>
647             «FOR feature : module.features»
648                 <li>
649                     <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
650                     <ul>
651                         «feature.descAndRefLi»
652                     </ul>
653                 </li>
654             «ENDFOR»
655             </ul>
656         '''
657     }
658
659     private def featuresSummary(Module module) {
660         if (module.features.empty) {
661             return '';
662         }
663         return '''
664         <h3>Features Summary</h3>
665         <table>
666             <tr>
667                 <th>Name</th>
668                 <th>Description</th>
669             </tr>
670             «FOR feature : module.features»
671             <tr>
672                 <td>
673                 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
674                 </td>
675                 <td>
676                 «feature.description»
677                 </td>
678             </tr>
679             «ENDFOR»
680         </table>
681         '''
682     }
683
684     private def objectsSummary(Module module) {
685         if (module.childNodes.empty) {
686             return '';
687         }
688         return '''
689         <h3>Child Nodes Summary</h3>
690         <table>
691             <tr>
692                 <th>Name</th>
693                 <th>Description</th>
694             </tr>
695             «FOR childNode : module.childNodes»
696             <tr>
697                 <td>
698                 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
699                 </td>
700                 <td>
701                 «childNode.description»
702                 </td>
703             </tr>
704             «ENDFOR»
705         </table>
706         '''
707     }
708
709     def header(Module module)
710     '''
711         <h1>«module.name»</h1>
712
713         <h2>Base Information</h2>
714         <table>
715             <tr>
716                 <td>«strong("prefix")»</td>
717                 <td>«module.prefix»</td>
718             </tr>
719             <tr>
720                 <td>«strong("namespace")»</td>
721                 <td>«module.namespace»</td>
722             </tr>
723             <tr>
724                 <td>«strong("revision")»</td>
725                 <td>«REVISION_FORMAT.format(module.revision)»</td>
726             </tr>
727             <tr>
728                 <td>«strong("description")»</td>
729                 <td>«module.description»</td>
730             </tr>
731             <tr>
732                 <td>«strong("yang-version")»</td>
733                 <td>«module.yangVersion»</td>
734             </tr>
735             <tr>
736                 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
737                     «imp.prefix»:«imp.moduleName»«IF imp.revision !== null» «REVISION_FORMAT.format(imp.revision)»«ENDIF»;
738                 «ENDFOR»
739             </tr>
740         </table>
741     '''
742
743     def CharSequence schemaPathToId(SchemaPath path) {
744         if(path !== null) {
745             return '''«FOR qName : path.path SEPARATOR "/"»«qName.localName»«ENDFOR»'''
746         }
747     }
748
749     def code(String string) '''<code>«string»</code>'''
750
751     def process(Module module) {
752         throw new UnsupportedOperationException("TODO: auto-generated method stub")
753     }
754
755     def CharSequence tree(Module module) '''
756         «strong(module.name)»
757         «module.childNodes.treeSet(YangInstanceIdentifier.builder.toInstance())»
758     '''
759
760     private def dispatch CharSequence tree(ChoiceNode node,YangInstanceIdentifier path) '''
761         «node.nodeName» (choice)
762         «casesTree(node.cases,path)»
763     '''
764
765     def casesTree(Set<ChoiceCaseNode> nodes,YangInstanceIdentifier path) '''
766         <ul>
767         «FOR node : nodes»
768             <li>
769             «node.nodeName»
770             «node.childNodes.treeSet(path)»
771             </li>
772         «ENDFOR»
773         </ul>
774     '''
775
776     private def dispatch CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) '''
777         «node.nodeName»
778     '''
779
780     private def dispatch CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) '''
781         «val newPath = path.append(node)»
782         «localLink(newPath,node.nodeName)»
783         «node.childNodes.treeSet(newPath)»
784     '''
785
786     private def dispatch CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
787         «val newPath = path.append(node)»
788         «localLink(newPath,node.nodeName)»
789         «node.childNodes.treeSet(newPath)»
790     '''
791
792     def CharSequence childNodes(Module module) '''
793         «val childNodes = module.childNodes»
794         «IF !childNodes.nullOrEmpty»
795             <h2>Child nodes</h2>
796
797             «childNodes.printChildren(3,YangInstanceIdentifier.builder().toInstance())»
798         «ENDIF»
799     '''
800
801     def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
802         return '''
803             <ul>
804             «node.printBaseInfo»
805             «IF node instanceof DataNodeContainer»
806                 «val dataNode = node as DataNodeContainer»
807                 <ul>
808                 «FOR usesNode : dataNode.uses»
809                     «usesNode.printUses»
810                 «ENDFOR»
811                 </ul>
812                 <ul>
813                 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
814                 «FOR typeDef : typeDefinitions»
815                     «typeDef.restrictions»
816                 «ENDFOR»
817                 </ul>
818                 <ul>
819                 «FOR grouping : dataNode.groupings»
820                     «grouping.printGrouping»
821                 «ENDFOR»
822                 </ul>
823                 <ul>
824                 «FOR child : dataNode.childNodes»
825                     «child.printSchemaNodeInfo»
826                 «ENDFOR»
827                 </ul>
828             «ENDIF»
829             </ul>
830         '''
831     }
832
833     def String typeAnchorLink(SchemaPath path, CharSequence text) {
834         if(path !== null) {
835             val lastElement = Iterables.getLast(path.pathFromRoot)
836             val ns = lastElement.namespace
837             if (ns == this.currentModule.namespace) {
838                 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
839             } else {
840                 return '''(«ns»)«text»'''
841                 //to enable external (import) links
842                 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
843             }
844         }
845     }
846
847     def CharSequence printBaseInfo(SchemaNode node) {
848         if(node instanceof LeafSchemaNode) {
849             val LeafSchemaNode leafNode = (node as LeafSchemaNode)
850             return '''
851                 «printInfo(node, "leaf")»
852                 «listItem("type", typeAnchorLink(leafNode.type?.path, leafNode.type.QName.localName))»
853                 «listItem("units", leafNode.units)»
854                 «listItem("default", leafNode.^default)»
855                 </ul>
856             '''
857         } else if(node instanceof LeafListSchemaNode) {
858             val LeafListSchemaNode leafListNode = (node as LeafListSchemaNode)
859             return '''
860                 «printInfo(node, "leaf-list")»
861                 «listItem("type", leafListNode.type?.QName.localName)»
862                 </ul>
863             '''
864         } else if(node instanceof ListSchemaNode) {
865             val ListSchemaNode listNode = (node as ListSchemaNode)
866             return '''
867                 «printInfo(node, "list")»
868                 «FOR keyDef : listNode.keyDefinition»
869                     «listItem("key definition", keyDef.localName)»
870                 «ENDFOR»
871                 </ul>
872             '''
873         } else if(node instanceof ChoiceNode) {
874             val ChoiceNode choiceNode = (node as ChoiceNode)
875             return '''
876                 «printInfo(node, "choice")»
877                 «listItem("default case", choiceNode.defaultCase)»
878                 «FOR caseNode : choiceNode.cases»
879                     «caseNode.printSchemaNodeInfo»
880                 «ENDFOR»
881                 </ul>
882             '''
883         } else if(node instanceof ChoiceCaseNode) {
884             return '''
885                 «printInfo(node, "case")»
886                 </ul>
887             '''
888         } else if(node instanceof ContainerSchemaNode) {
889             return '''
890                 «printInfo(node, "container")»
891                 </ul>
892             '''
893         } else if(node instanceof AnyXmlSchemaNode) {
894             return '''
895                 «printInfo(node, "anyxml")»
896                 </ul>
897             '''
898         }
899     }
900
901     def CharSequence printInfo(SchemaNode node, String nodeType) {
902         return '''
903             «IF node instanceof AugmentationTarget»
904                 «IF node !== null»
905                     <strong>
906                     <li id="«node.path.schemaPathToId»">
907                         «nodeType»: «node.QName.localName»
908                     </li>
909                     </strong>
910                 «ENDIF»
911             «ELSE»
912                 «strong(listItem(nodeType, node.QName.localName))»
913             «ENDIF»
914             <ul>
915             «listItem("description", node.description)»
916             «listItem("reference", node.reference)»
917             «IF node instanceof DataSchemaNode»
918                 «listItem("when condition", (node as DataSchemaNode).constraints.whenCondition?.toString)»
919                 «listItem("min elements", (node as DataSchemaNode).constraints.minElements?.toString)»
920                 «listItem("max elements", (node as DataSchemaNode).constraints.maxElements?.toString)»
921             «ENDIF»
922         '''
923     }
924
925     def CharSequence printUses(UsesNode usesNode) {
926         return '''
927             «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
928             <ul>
929             <li>refines:
930                 <ul>
931                 «FOR sp : usesNode.refines.keySet»
932                     «listItem("node name", usesNode.refines.get(sp).QName.localName)»
933                 «ENDFOR»
934                 </ul>
935             </li>
936             «FOR augment : usesNode.augmentations»
937                 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
938             «ENDFOR»
939             </ul>
940         '''
941     }
942
943     def CharSequence printGrouping(GroupingDefinition grouping) {
944         return '''
945             «strong(listItem("grouping", grouping.QName.localName))»
946         '''
947     }
948
949     def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
950         val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
951         val leafNodes = nodes.filter(LeafSchemaNode)
952         val leafListNodes = nodes.filter(LeafListSchemaNode)
953         val choices = nodes.filter(ChoiceNode)
954         val cases = nodes.filter(ChoiceCaseNode)
955         val containers = nodes.filter(ContainerSchemaNode)
956         val lists = nodes.filter(ListSchemaNode)
957         return '''
958             «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
959             <h3>Direct children</h3>
960             <ul>
961             «FOR childNode : anyxmlNodes»
962                 «childNode.printShortInfo(level,path)»
963             «ENDFOR»
964             «FOR childNode : leafNodes»
965                 «childNode.printShortInfo(level,path)»
966             «ENDFOR»
967             «FOR childNode : leafListNodes»
968                 «childNode.printShortInfo(level,path)»
969             «ENDFOR»
970             «FOR childNode : containers»
971                 «childNode.printShortInfo(level,path)»
972             «ENDFOR»
973             «FOR childNode : lists»
974                 «childNode.printShortInfo(level,path)»
975             «ENDFOR»
976             </ul>
977             «ENDIF»
978
979             «IF path.pathArguments.iterator.hasNext»
980             <h3>XML example</h3>
981             «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
982             </h3>
983             «ENDIF»
984             «FOR childNode : containers»
985                 «childNode.printInfo(level,path)»
986             «ENDFOR»
987             «FOR childNode : lists»
988                 «childNode.printInfo(level,path)»
989             «ENDFOR»
990             «FOR childNode : choices»
991                 «childNode.printInfo(level,path)»
992             «ENDFOR»
993             «FOR childNode : cases»
994                 «childNode.printInfo(level,path)»
995             «ENDFOR»
996         '''
997     }
998
999     def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
1000     <pre>
1001         «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1002     </pre>
1003     '''
1004
1005     def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1006         <!-- Child nodes -->
1007         «FOR node : nodes»
1008         <!-- «node.QName.localName» -->
1009             «node.asXmlExampleTag(identifier)»
1010         «ENDFOR»
1011
1012     '''
1013
1014     private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1015         «node.QName.xmlExampleTag("...")»
1016     '''
1017
1018     private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1019         &lt!-- This node could appear multiple times --&gt
1020         «node.QName.xmlExampleTag("...")»
1021     '''
1022
1023     private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1024         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
1025         «node.QName.xmlExampleTag("...")»
1026     '''
1027
1028
1029     private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1030         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
1031         &lt!-- This node could appear multiple times --&gt
1032         «node.QName.xmlExampleTag("...")»
1033     '''
1034
1035
1036     private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1037         <!-- noop -->
1038     '''
1039
1040
1041     def xmlExampleTag(QName name, CharSequence data) {
1042         return '''&lt;«name.localName» xmlns="«name.namespace»"&gt;«data»&lt;/«name.localName»&gt;'''
1043     }
1044
1045     def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1046
1047
1048     def header(int level,YangInstanceIdentifier name) '''
1049         <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1050             «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1051         </h«level»>
1052     '''
1053
1054
1055
1056     private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1057         «header(level+1,node.QName)»
1058     '''
1059
1060     private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1061         «val newPath = path.append(node)»
1062         «header(level,newPath)»
1063         <dl>
1064           <dt>XML Path</dt>
1065           <dd>«newPath.asXmlPath»</dd>
1066           <dt>Restconf path</dt>
1067           <dd>«code(newPath.asRestconfPath)»</dd>
1068         </dl>
1069         «node.childNodes.printChildren(level,newPath)»
1070     '''
1071
1072     private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1073         «val newPath = path.append(node)»
1074         «header(level,newPath)»
1075         <dl>
1076           <dt>XML Path</dt>
1077           <dd>«newPath.asXmlPath»</dd>
1078           <dt>Restconf path</dt>
1079           <dd>«code(newPath.asRestconfPath)»</dd>
1080         </dl>
1081         «node.childNodes.printChildren(level,newPath)»
1082     '''
1083
1084     private def dispatch CharSequence printInfo(ChoiceNode node, int level, YangInstanceIdentifier path) '''
1085         «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1086         «choiceCases.printChildren(level,path)»
1087     '''
1088
1089     private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, YangInstanceIdentifier path) '''
1090         «node.childNodes.printChildren(level,path)»
1091     '''
1092
1093
1094
1095     def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1096         val newPath = path.append(node);
1097         return '''
1098             <li>«strong(localLink(newPath,node.QName.localName))» (container)
1099             <ul>
1100                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1101             </ul>
1102             </li>
1103         '''
1104     }
1105
1106     def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1107         val newPath = path.append(node);
1108         return '''
1109             <li>«strong(localLink(newPath,node.QName.localName))» (list)
1110             <ul>
1111                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1112             </ul>
1113             </li>
1114         '''
1115     }
1116
1117     def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, YangInstanceIdentifier path) {
1118         return '''
1119             <li>«strong((node.QName.localName))» (anyxml)
1120             <ul>
1121                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1122                 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1123             </ul>
1124             </li>
1125         '''
1126     }
1127
1128     def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1129         return '''
1130             <li>«strong((node.QName.localName))» (leaf)
1131             <ul>
1132                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1133                 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1134             </ul>
1135             </li>
1136         '''
1137     }
1138
1139     def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1140         return '''
1141             <li>«strong((node.QName.localName))» (leaf-list)
1142             <ul>
1143                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1144             </ul>
1145             </li>
1146         '''
1147     }
1148
1149     def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1150         return '''
1151             <a href="#«anchor»">«text»</a>
1152         '''
1153     }
1154
1155     def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1156         <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1157     '''
1158
1159
1160     private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1161         return identifier.node(node.QName);
1162     }
1163
1164     private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1165         val keyValues = new LinkedHashMap<QName,Object>();
1166         if(node.keyDefinition !== null) {
1167             for(definition : node.keyDefinition) {
1168                 keyValues.put(definition,new Object);
1169             }
1170         }
1171
1172         return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1173     }
1174
1175
1176     def asXmlPath(YangInstanceIdentifier identifier) {
1177         return "";
1178     }
1179
1180     def asRestconfPath(YangInstanceIdentifier identifier) {
1181         val it = new StringBuilder();
1182         append(currentModule.name)
1183         append(":")
1184         var previous = false;
1185         for(arg : identifier.pathArguments) {
1186             if(previous) append("/")
1187             append(arg.nodeType.localName);
1188             previous = true;
1189             if(arg instanceof NodeIdentifierWithPredicates) {
1190                 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
1191                 for(qname : nodeIdentifier.getKeyValues.keySet) {
1192                     append("/{");
1193                     append(qname.localName)
1194                     append("}")
1195                 }
1196             }
1197         }
1198
1199         return it.toString;
1200     }
1201
1202     private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1203             val List<QName> path = schemaPath.path
1204         val StringBuilder pathString = new StringBuilder()
1205         if (schemaPath.absolute) {
1206             pathString.append('/')
1207         }
1208
1209         val QName qname = path.get(0)
1210         var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1211
1212         for (name : path) {
1213             if (parent instanceof DataNodeContainer) {
1214                 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)
1215                 if (node == null && (parent instanceof Module)) {
1216                     val notifications = (parent as Module).notifications;
1217                     for (notification : notifications) {
1218                         if (notification.QName.equals(name)) {
1219                             node = notification
1220                         }
1221                     }
1222                 }
1223                 if (node == null && (parent instanceof Module)) {
1224                     val rpcs = (parent as Module).rpcs;
1225                     for (rpc : rpcs) {
1226                         if (rpc.QName.equals(name)) {
1227                             node = rpc
1228                         }
1229                     }
1230                 }
1231
1232                 val pathElementModule = ctx.findModuleByNamespaceAndRevision(name.namespace, name.revision)
1233                 val String moduleName = pathElementModule.name
1234                 pathString.append(moduleName)
1235                 pathString.append(':')
1236                 pathString.append(name.localName)
1237                 pathString.append('/')
1238                 if(node instanceof ChoiceNode && dataNode !== null) {
1239                     val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1240                     if(caseNode !== null) {
1241                         pathString.append("(case)");
1242                         pathString.append(caseNode.QName.localName);
1243                     }
1244                 }
1245                 parent = node
1246             }
1247         }
1248         return pathString.toString;
1249     }
1250
1251
1252     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1253         «IF childNodes !== null && !childNodes.empty»
1254             «FOR child : childNodes.values»
1255                 «childInfo(child, childNodes)»
1256             «ENDFOR»
1257         «ENDIF»
1258     '''
1259
1260     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1261         «val String path = nodeSchemaPathToPath(node, childNodes)»
1262         «IF path != null»
1263             «code(path)»
1264                 «IF node !== null»
1265                 <ul>
1266                 «node.descAndRefLi»
1267                 </ul>
1268             «ENDIF»
1269         «ENDIF»
1270     '''
1271
1272     private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1273         «IF childNodes !== null && !childNodes.empty»
1274             <ul>
1275             «FOR child : childNodes»
1276                 <li>
1277                     «child.tree(path)»
1278                 </li>
1279             «ENDFOR»
1280             </ul>
1281         «ENDIF»
1282     '''
1283
1284     def listKeys(ListSchemaNode node) '''
1285         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1286     '''
1287
1288     private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1289         <ul>
1290             «ext.descAndRefLi»
1291             «listItem("Argument", ext.argument)»
1292         </ul>
1293     '''
1294
1295     private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1296     '''
1297
1298
1299
1300     /* #################### RESTRICTIONS #################### */
1301     private def restrictions(TypeDefinition<?> type) '''
1302         «type.baseType.toBaseStmt»
1303         «type.toLength»
1304         «type.toRange»
1305     '''
1306
1307     private def dispatch toLength(TypeDefinition<?> type) {
1308     }
1309
1310     private def dispatch toLength(BinaryTypeDefinition type) '''
1311         «type.lengthConstraints.toLengthStmt»
1312     '''
1313
1314     private def dispatch toLength(StringTypeDefinition type) '''
1315         «type.lengthConstraints.toLengthStmt»
1316     '''
1317
1318     private def dispatch toLength(ExtendedType type) '''
1319         «type.lengthConstraints.toLengthStmt»
1320     '''
1321
1322     private def dispatch toRange(TypeDefinition<?> type) {
1323     }
1324
1325     private def dispatch toRange(DecimalTypeDefinition type) '''
1326         «type.rangeConstraints.toRangeStmt»
1327     '''
1328
1329     private def dispatch toRange(IntegerTypeDefinition type) '''
1330         «type.rangeConstraints.toRangeStmt»
1331     '''
1332
1333     private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1334         «type.rangeConstraints.toRangeStmt»
1335     '''
1336
1337     private def dispatch toRange(ExtendedType type) '''
1338         «type.rangeConstraints.toRangeStmt»
1339     '''
1340
1341     def toLengthStmt(Collection<LengthConstraint> lengths) '''
1342         «IF lengths != null && !lengths.empty»
1343             «listItem("Length restrictions:")»
1344             <ul>
1345             «FOR length : lengths»
1346                 <li>
1347                 «IF length.min == length.max»
1348                     «length.min»
1349                 «ELSE»
1350                     &lt;«length.min», «length.max»&gt;
1351                 «ENDIF»
1352                 </li>
1353             «ENDFOR»
1354             </ul>
1355         «ENDIF»
1356     '''
1357
1358     def toRangeStmt(Collection<RangeConstraint> ranges) '''
1359         «IF ranges != null && !ranges.empty»
1360             «listItem("Range restrictions:")»
1361             <ul>
1362             «FOR range : ranges»
1363                 <li>
1364                 «IF range.min == range.max»
1365                     «range.min»
1366                 «ELSE»
1367                     &lt;«range.min», «range.max»&gt;
1368                 «ENDIF»
1369                 </li>
1370             «ENDFOR»
1371             </ul>
1372         «ENDIF»
1373     '''
1374
1375     def toBaseStmt(TypeDefinition<?> baseType) '''
1376         «IF baseType != null»
1377         «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1378         «ENDIF»
1379     '''
1380
1381
1382
1383     /* #################### UTILITY #################### */
1384     private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1385     private def italic(CharSequence str) '''<i>«str»</i>'''
1386
1387     def CharSequence descAndRefLi(SchemaNode node) '''
1388         «listItem("Description", node.description)»
1389         «listItem("Reference", node.reference)»
1390     '''
1391
1392     def CharSequence descAndRef(SchemaNode node) '''
1393         «node.description»
1394         «IF node.reference !== null»
1395             Reference «node.reference»
1396         «ENDIF»
1397     '''
1398
1399     private def listItem(String value) '''
1400         «IF value !== null && !value.empty»
1401             <li>
1402                 «value»
1403             </li>
1404         «ENDIF»
1405     '''
1406
1407     private def listItem(String name, String value) '''
1408         «IF value !== null && !value.empty»
1409             <li>
1410                 «name»: «value»
1411             </li>
1412         «ENDIF»
1413     '''
1414
1415     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1416         if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
1417             return null
1418         }
1419
1420         val path = node.path.path
1421         val absolute = node.path.absolute;
1422         var StringBuilder result = new StringBuilder
1423         if (absolute) {
1424             result.append("/")
1425         }
1426         if (path !== null && !path.empty) {
1427             val List<QName> actual = new ArrayList()
1428             var i = 0;
1429             for (pathElement : path) {
1430                 actual.add(pathElement)
1431                 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1432                 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1433                     result.append(pathElement.localName)
1434                     if (i != path.size - 1) {
1435                         result.append("/")
1436                     }
1437                 }
1438                 i = i + 1
1439             }
1440         }
1441         return result.toString
1442     }
1443
1444     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1445         for (node : source) {
1446             destination.put(node.path, node)
1447             if (node instanceof DataNodeContainer) {
1448                 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1449             }
1450             if (node instanceof ChoiceNode) {
1451                 val List<DataSchemaNode> choiceCases = new ArrayList()
1452                 for (caseNode : (node as ChoiceNode).cases) {
1453                     choiceCases.add(caseNode)
1454                 }
1455                 collectChildNodes(choiceCases, destination)
1456             }
1457         }
1458     }
1459
1460     private def dispatch addedByInfo(SchemaNode node) '''
1461     '''
1462
1463     private def dispatch addedByInfo(DataSchemaNode node) '''
1464         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1465     '''
1466
1467     private def dispatch isAddedBy(SchemaNode node) {
1468         return false;
1469     }
1470
1471     private def dispatch isAddedBy(DataSchemaNode node) {
1472         if (node.augmenting || node.addedByUses) {
1473             return true
1474         } else {
1475             return false;
1476         }
1477     }
1478
1479     private def dispatch nodeName(SchemaNode node) '''
1480         «IF node.isAddedBy»
1481             «italic(node.QName.localName)»«node.addedByInfo»
1482         «ELSE»
1483             «node.QName.localName»«node.addedByInfo»
1484         «ENDIF»
1485     '''
1486
1487     private def dispatch nodeName(ContainerSchemaNode node) '''
1488         «IF node.isAddedBy»
1489             «strong(italic(node.QName.localName))»«node.addedByInfo»
1490         «ELSE»
1491             «strong(node.QName.localName)»«node.addedByInfo»
1492         «ENDIF»
1493     '''
1494
1495     private def dispatch nodeName(ListSchemaNode node) '''
1496         «IF node.isAddedBy»
1497             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1498         «ELSE»
1499             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
1500         «ENDIF»
1501     '''
1502
1503 }