Merge "BUG-869: added proper handling of nullable parameter"
[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 java.io.BufferedWriter
11 import java.io.File
12 import java.io.IOException
13 import java.io.OutputStreamWriter
14 import java.text.SimpleDateFormat
15 import java.util.ArrayList
16 import java.util.Collection
17 import java.util.HashMap
18 import java.util.HashSet
19 import java.util.LinkedHashMap
20 import java.util.List
21 import java.util.Map
22 import java.util.Set
23 import org.opendaylight.yangtools.yang.common.QName
24 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
25 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
26 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
27 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
28 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
29 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
31 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
32 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
33 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
36 import org.opendaylight.yangtools.yang.model.api.Module
37 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext
39 import org.opendaylight.yangtools.yang.model.api.SchemaNode
40 import org.opendaylight.yangtools.yang.model.api.SchemaPath
41 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
42 import org.opendaylight.yangtools.yang.model.api.UsesNode
43 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
44 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
45 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
46 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
47 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
48 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
49 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
50 import org.opendaylight.yangtools.yang.model.util.ExtendedType
51 import org.slf4j.Logger
52 import org.slf4j.LoggerFactory
53 import org.sonatype.plexus.build.incremental.BuildContext
54 import org.sonatype.plexus.build.incremental.DefaultBuildContext
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
56 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
57 import com.google.common.collect.Iterables
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 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                 «FOR typeDef : dataNode.typeDefinitions»
814                     «typeDef.restrictions»
815                 «ENDFOR»
816                 </ul>
817                 <ul>
818                 «FOR grouping : dataNode.groupings»
819                     «grouping.printGrouping»
820                 «ENDFOR»
821                 </ul>
822                 <ul>
823                 «FOR child : dataNode.childNodes»
824                     «child.printSchemaNodeInfo»
825                 «ENDFOR»
826                 </ul>
827             «ENDIF»
828             </ul>
829         '''
830     }
831
832     def String typeAnchorLink(SchemaPath path, CharSequence text) {
833         if(path !== null) {
834             val lastElement = Iterables.getLast(path.pathFromRoot)
835             val ns = lastElement.namespace
836             if (ns == this.currentModule.namespace) {
837                 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
838             } else {
839                 return '''(«ns»)«text»'''
840                 //to enable external (import) links
841                 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
842             }
843         }
844     }
845
846     def CharSequence printBaseInfo(SchemaNode node) {
847         if(node instanceof LeafSchemaNode) {
848             val LeafSchemaNode leafNode = (node as LeafSchemaNode)
849             return '''
850                 «printInfo(node, "leaf")»
851                 «listItem("type", typeAnchorLink(leafNode.type?.path, leafNode.type.QName.localName))»
852                 «listItem("units", leafNode.units)»
853                 «listItem("default", leafNode.^default)»
854                 </ul>
855             '''
856         } else if(node instanceof LeafListSchemaNode) {
857             val LeafListSchemaNode leafListNode = (node as LeafListSchemaNode)
858             return '''
859                 «printInfo(node, "leaf-list")»
860                 «listItem("type", leafListNode.type?.QName.localName)»
861                 </ul>
862             '''
863         } else if(node instanceof ListSchemaNode) {
864             val ListSchemaNode listNode = (node as ListSchemaNode)
865             return '''
866                 «printInfo(node, "list")»
867                 «FOR keyDef : listNode.keyDefinition»
868                     «listItem("key definition", keyDef.localName)»
869                 «ENDFOR»
870                 </ul>
871             '''
872         } else if(node instanceof ChoiceNode) {
873             val ChoiceNode choiceNode = (node as ChoiceNode)
874             return '''
875                 «printInfo(node, "choice")»
876                 «listItem("default case", choiceNode.defaultCase)»
877                 «FOR caseNode : choiceNode.cases»
878                     «caseNode.printSchemaNodeInfo»
879                 «ENDFOR»
880                 </ul>
881             '''
882         } else if(node instanceof ChoiceCaseNode) {
883             return '''
884                 «printInfo(node, "case")»
885                 </ul>
886             '''
887         } else if(node instanceof ContainerSchemaNode) {
888             return '''
889                 «printInfo(node, "container")»
890                 </ul>
891             '''
892         } else if(node instanceof AnyXmlSchemaNode) {
893             return '''
894                 «printInfo(node, "anyxml")»
895                 </ul>
896             '''
897         }
898     }
899
900     def CharSequence printInfo(SchemaNode node, String nodeType) {
901         return '''
902             «IF node instanceof AugmentationTarget»
903                 «IF node !== null»
904                     <strong>
905                     <li id="«node.path.schemaPathToId»">
906                         «nodeType»: «node.QName.localName»
907                     </li>
908                     </strong>
909                 «ENDIF»
910             «ELSE»
911                 «strong(listItem(nodeType, node.QName.localName))»
912             «ENDIF»
913             <ul>
914             «listItem("description", node.description)»
915             «listItem("reference", node.reference)»
916             «IF node instanceof DataSchemaNode»
917                 «listItem("when condition", (node as DataSchemaNode).constraints.whenCondition?.toString)»
918                 «listItem("min elements", (node as DataSchemaNode).constraints.minElements?.toString)»
919                 «listItem("max elements", (node as DataSchemaNode).constraints.maxElements?.toString)»
920             «ENDIF»
921         '''
922     }
923
924     def CharSequence printUses(UsesNode usesNode) {
925         return '''
926             «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
927             <ul>
928             <li>refines:
929                 <ul>
930                 «FOR sp : usesNode.refines.keySet»
931                     «listItem("node name", usesNode.refines.get(sp).QName.localName)»
932                 «ENDFOR»
933                 </ul>
934             </li>
935             «FOR augment : usesNode.augmentations»
936                 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
937             «ENDFOR»
938             </ul>
939         '''
940     }
941
942     def CharSequence printGrouping(GroupingDefinition grouping) {
943         return '''
944             «strong(listItem("grouping", grouping.QName.localName))»
945         '''
946     }
947
948     def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
949         val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
950         val leafNodes = nodes.filter(LeafSchemaNode)
951         val leafListNodes = nodes.filter(LeafListSchemaNode)
952         val choices = nodes.filter(ChoiceNode)
953         val cases = nodes.filter(ChoiceCaseNode)
954         val containers = nodes.filter(ContainerSchemaNode)
955         val lists = nodes.filter(ListSchemaNode)
956         return '''
957             «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
958             <h3>Direct children</h3>
959             <ul>
960             «FOR childNode : anyxmlNodes»
961                 «childNode.printShortInfo(level,path)»
962             «ENDFOR»
963             «FOR childNode : leafNodes»
964                 «childNode.printShortInfo(level,path)»
965             «ENDFOR»
966             «FOR childNode : leafListNodes»
967                 «childNode.printShortInfo(level,path)»
968             «ENDFOR»
969             «FOR childNode : containers»
970                 «childNode.printShortInfo(level,path)»
971             «ENDFOR»
972             «FOR childNode : lists»
973                 «childNode.printShortInfo(level,path)»
974             «ENDFOR»
975             </ul>
976             «ENDIF»
977
978             «IF path.pathArguments.iterator.hasNext»
979             <h3>XML example</h3>
980             «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
981             </h3>
982             «ENDIF»
983             «FOR childNode : containers»
984                 «childNode.printInfo(level,path)»
985             «ENDFOR»
986             «FOR childNode : lists»
987                 «childNode.printInfo(level,path)»
988             «ENDFOR»
989             «FOR childNode : choices»
990                 «childNode.printInfo(level,path)»
991             «ENDFOR»
992             «FOR childNode : cases»
993                 «childNode.printInfo(level,path)»
994             «ENDFOR»
995         '''
996     }
997
998     def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
999     <pre>
1000         «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1001     </pre>
1002     '''
1003
1004     def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1005         <!-- Child nodes -->
1006         «FOR node : nodes»
1007         <!-- «node.QName.localName» -->
1008             «node.asXmlExampleTag(identifier)»
1009         «ENDFOR»
1010
1011     '''
1012
1013     private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1014         «node.QName.xmlExampleTag("...")»
1015     '''
1016
1017     private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1018         &lt!-- This node could appear multiple times --&gt
1019         «node.QName.xmlExampleTag("...")»
1020     '''
1021
1022     private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1023         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
1024         «node.QName.xmlExampleTag("...")»
1025     '''
1026
1027
1028     private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1029         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt
1030         &lt!-- This node could appear multiple times --&gt
1031         «node.QName.xmlExampleTag("...")»
1032     '''
1033
1034
1035     private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1036         <!-- noop -->
1037     '''
1038
1039
1040     def xmlExampleTag(QName name, CharSequence data) {
1041         return '''&lt;«name.localName» xmlns="«name.namespace»"&gt;«data»&lt;/«name.localName»&gt;'''
1042     }
1043
1044     def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1045
1046
1047     def header(int level,YangInstanceIdentifier name) '''
1048         <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1049             «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1050         </h«level»>
1051     '''
1052
1053
1054
1055     private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1056         «header(level+1,node.QName)»
1057     '''
1058
1059     private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1060         «val newPath = path.append(node)»
1061         «header(level,newPath)»
1062         <dl>
1063           <dt>XML Path</dt>
1064           <dd>«newPath.asXmlPath»</dd>
1065           <dt>Restconf path</dt>
1066           <dd>«code(newPath.asRestconfPath)»</dd>
1067         </dl>
1068         «node.childNodes.printChildren(level,newPath)»
1069     '''
1070
1071     private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1072         «val newPath = path.append(node)»
1073         «header(level,newPath)»
1074         <dl>
1075           <dt>XML Path</dt>
1076           <dd>«newPath.asXmlPath»</dd>
1077           <dt>Restconf path</dt>
1078           <dd>«code(newPath.asRestconfPath)»</dd>
1079         </dl>
1080         «node.childNodes.printChildren(level,newPath)»
1081     '''
1082
1083     private def dispatch CharSequence printInfo(ChoiceNode node, int level, YangInstanceIdentifier path) '''
1084         «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1085         «choiceCases.printChildren(level,path)»
1086     '''
1087
1088     private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, YangInstanceIdentifier path) '''
1089         «node.childNodes.printChildren(level,path)»
1090     '''
1091
1092
1093
1094     def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1095         val newPath = path.append(node);
1096         return '''
1097             <li>«strong(localLink(newPath,node.QName.localName))» (container)
1098             <ul>
1099                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1100             </ul>
1101             </li>
1102         '''
1103     }
1104
1105     def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1106         val newPath = path.append(node);
1107         return '''
1108             <li>«strong(localLink(newPath,node.QName.localName))» (list)
1109             <ul>
1110                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1111             </ul>
1112             </li>
1113         '''
1114     }
1115
1116     def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, YangInstanceIdentifier path) {
1117         return '''
1118             <li>«strong((node.QName.localName))» (anyxml)
1119             <ul>
1120                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1121                 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1122             </ul>
1123             </li>
1124         '''
1125     }
1126
1127     def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1128         return '''
1129             <li>«strong((node.QName.localName))» (leaf)
1130             <ul>
1131                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1132                 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1133             </ul>
1134             </li>
1135         '''
1136     }
1137
1138     def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1139         return '''
1140             <li>«strong((node.QName.localName))» (leaf-list)
1141             <ul>
1142                 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1143             </ul>
1144             </li>
1145         '''
1146     }
1147
1148     def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1149         return '''
1150             <a href="#«anchor»">«text»</a>
1151         '''
1152     }
1153
1154     def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1155         <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1156     '''
1157
1158
1159     private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1160         return identifier.node(node.QName);
1161     }
1162
1163     private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1164         val keyValues = new LinkedHashMap<QName,Object>();
1165         if(node.keyDefinition !== null) {
1166             for(definition : node.keyDefinition) {
1167                 keyValues.put(definition,new Object);
1168             }
1169         }
1170
1171         return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1172     }
1173
1174
1175     def asXmlPath(YangInstanceIdentifier identifier) {
1176         return "";
1177     }
1178
1179     def asRestconfPath(YangInstanceIdentifier identifier) {
1180         val it = new StringBuilder();
1181         append(currentModule.name)
1182         append(":")
1183         var previous = false;
1184         for(arg : identifier.pathArguments) {
1185             if(previous) append("/")
1186             append(arg.nodeType.localName);
1187             previous = true;
1188             if(arg instanceof NodeIdentifierWithPredicates) {
1189                 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
1190                 for(qname : nodeIdentifier.getKeyValues.keySet) {
1191                     append("/{");
1192                     append(qname.localName)
1193                     append("}")
1194                 }
1195             }
1196         }
1197
1198         return it.toString;
1199     }
1200
1201     private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1202             val List<QName> path = schemaPath.path
1203         val StringBuilder pathString = new StringBuilder()
1204         if (schemaPath.absolute) {
1205             pathString.append('/')
1206         }
1207
1208         val QName qname = path.get(0)
1209         var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1210
1211         for (name : path) {
1212             if (parent instanceof DataNodeContainer) {
1213                 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)
1214                 if (node == null && (parent instanceof Module)) {
1215                     val notifications = (parent as Module).notifications;
1216                     for (notification : notifications) {
1217                         if (notification.QName.equals(name)) {
1218                             node = notification
1219                         }
1220                     }
1221                 }
1222                 if (node == null && (parent instanceof Module)) {
1223                     val rpcs = (parent as Module).rpcs;
1224                     for (rpc : rpcs) {
1225                         if (rpc.QName.equals(name)) {
1226                             node = rpc
1227                         }
1228                     }
1229                 }
1230
1231                 val pathElementModule = ctx.findModuleByNamespaceAndRevision(name.namespace, name.revision)
1232                 val String moduleName = pathElementModule.name
1233                 pathString.append(moduleName)
1234                 pathString.append(':')
1235                 pathString.append(name.localName)
1236                 pathString.append('/')
1237                 if(node instanceof ChoiceNode && dataNode !== null) {
1238                     val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1239                     if(caseNode !== null) {
1240                         pathString.append("(case)");
1241                         pathString.append(caseNode.QName.localName);
1242                     }
1243                 }
1244                 parent = node
1245             }
1246         }
1247         return pathString.toString;
1248     }
1249
1250
1251     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1252         «IF childNodes !== null && !childNodes.empty»
1253             «FOR child : childNodes.values»
1254                 «childInfo(child, childNodes)»
1255             «ENDFOR»
1256         «ENDIF»
1257     '''
1258
1259     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1260         «val String path = nodeSchemaPathToPath(node, childNodes)»
1261         «IF path != null»
1262             «code(path)»
1263                 «IF node !== null»
1264                 <ul>
1265                 «node.descAndRefLi»
1266                 </ul>
1267             «ENDIF»
1268         «ENDIF»
1269     '''
1270
1271     private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1272         «IF childNodes !== null && !childNodes.empty»
1273             <ul>
1274             «FOR child : childNodes»
1275                 <li>
1276                     «child.tree(path)»
1277                 </li>
1278             «ENDFOR»
1279             </ul>
1280         «ENDIF»
1281     '''
1282
1283     def listKeys(ListSchemaNode node) '''
1284         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1285     '''
1286
1287     private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1288         <ul>
1289             «ext.descAndRefLi»
1290             «listItem("Argument", ext.argument)»
1291         </ul>
1292     '''
1293
1294     private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1295     '''
1296
1297
1298
1299     /* #################### RESTRICTIONS #################### */
1300     private def restrictions(TypeDefinition<?> type) '''
1301         «type.baseType.toBaseStmt»
1302         «type.toLength»
1303         «type.toRange»
1304     '''
1305
1306     private def dispatch toLength(TypeDefinition<?> type) {
1307     }
1308
1309     private def dispatch toLength(BinaryTypeDefinition type) '''
1310         «type.lengthConstraints.toLengthStmt»
1311     '''
1312
1313     private def dispatch toLength(StringTypeDefinition type) '''
1314         «type.lengthConstraints.toLengthStmt»
1315     '''
1316
1317     private def dispatch toLength(ExtendedType type) '''
1318         «type.lengthConstraints.toLengthStmt»
1319     '''
1320
1321     private def dispatch toRange(TypeDefinition<?> type) {
1322     }
1323
1324     private def dispatch toRange(DecimalTypeDefinition type) '''
1325         «type.rangeConstraints.toRangeStmt»
1326     '''
1327
1328     private def dispatch toRange(IntegerTypeDefinition type) '''
1329         «type.rangeConstraints.toRangeStmt»
1330     '''
1331
1332     private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1333         «type.rangeConstraints.toRangeStmt»
1334     '''
1335
1336     private def dispatch toRange(ExtendedType type) '''
1337         «type.rangeConstraints.toRangeStmt»
1338     '''
1339
1340     def toLengthStmt(Collection<LengthConstraint> lengths) '''
1341         «IF lengths != null && !lengths.empty»
1342             «listItem("Length restrictions:")»
1343             <ul>
1344             «FOR length : lengths»
1345                 <li>
1346                 «IF length.min == length.max»
1347                     «length.min»
1348                 «ELSE»
1349                     &lt;«length.min», «length.max»&gt;
1350                 «ENDIF»
1351                 </li>
1352             «ENDFOR»
1353             </ul>
1354         «ENDIF»
1355     '''
1356
1357     def toRangeStmt(Collection<RangeConstraint> ranges) '''
1358         «IF ranges != null && !ranges.empty»
1359             «listItem("Range restrictions:")»
1360             <ul>
1361             «FOR range : ranges»
1362                 <li>
1363                 «IF range.min == range.max»
1364                     «range.min»
1365                 «ELSE»
1366                     &lt;«range.min», «range.max»&gt;
1367                 «ENDIF»
1368                 </li>
1369             «ENDFOR»
1370             </ul>
1371         «ENDIF»
1372     '''
1373
1374     def toBaseStmt(TypeDefinition<?> baseType) '''
1375         «IF baseType != null»
1376         «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1377         «ENDIF»
1378     '''
1379
1380
1381
1382     /* #################### UTILITY #################### */
1383     private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1384     private def italic(CharSequence str) '''<i>«str»</i>'''
1385
1386     def CharSequence descAndRefLi(SchemaNode node) '''
1387         «listItem("Description", node.description)»
1388         «listItem("Reference", node.reference)»
1389     '''
1390
1391     def CharSequence descAndRef(SchemaNode node) '''
1392         «node.description»
1393         «IF node.reference !== null»
1394             Reference «node.reference»
1395         «ENDIF»
1396     '''
1397
1398     private def listItem(String value) '''
1399         «IF value !== null && !value.empty»
1400             <li>
1401                 «value»
1402             </li>
1403         «ENDIF»
1404     '''
1405
1406     private def listItem(String name, String value) '''
1407         «IF value !== null && !value.empty»
1408             <li>
1409                 «name»: «value»
1410             </li>
1411         «ENDIF»
1412     '''
1413
1414     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1415         if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
1416             return null
1417         }
1418
1419         val path = node.path.path
1420         val absolute = node.path.absolute;
1421         var StringBuilder result = new StringBuilder
1422         if (absolute) {
1423             result.append("/")
1424         }
1425         if (path !== null && !path.empty) {
1426             val List<QName> actual = new ArrayList()
1427             var i = 0;
1428             for (pathElement : path) {
1429                 actual.add(pathElement)
1430                 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1431                 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1432                     result.append(pathElement.localName)
1433                     if (i != path.size - 1) {
1434                         result.append("/")
1435                     }
1436                 }
1437                 i = i + 1
1438             }
1439         }
1440         return result.toString
1441     }
1442
1443     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1444         for (node : source) {
1445             destination.put(node.path, node)
1446             if (node instanceof DataNodeContainer) {
1447                 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1448             }
1449             if (node instanceof ChoiceNode) {
1450                 val List<DataSchemaNode> choiceCases = new ArrayList()
1451                 for (caseNode : (node as ChoiceNode).cases) {
1452                     choiceCases.add(caseNode)
1453                 }
1454                 collectChildNodes(choiceCases, destination)
1455             }
1456         }
1457     }
1458
1459     private def dispatch addedByInfo(SchemaNode node) '''
1460     '''
1461
1462     private def dispatch addedByInfo(DataSchemaNode node) '''
1463         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1464     '''
1465
1466     private def dispatch isAddedBy(SchemaNode node) {
1467         return false;
1468     }
1469
1470     private def dispatch isAddedBy(DataSchemaNode node) {
1471         if (node.augmenting || node.addedByUses) {
1472             return true
1473         } else {
1474             return false;
1475         }
1476     }
1477
1478     private def dispatch nodeName(SchemaNode node) '''
1479         «IF node.isAddedBy»
1480             «italic(node.QName.localName)»«node.addedByInfo»
1481         «ELSE»
1482             «node.QName.localName»«node.addedByInfo»
1483         «ENDIF»
1484     '''
1485
1486     private def dispatch nodeName(ContainerSchemaNode node) '''
1487         «IF node.isAddedBy»
1488             «strong(italic(node.QName.localName))»«node.addedByInfo»
1489         «ELSE»
1490             «strong(node.QName.localName)»«node.addedByInfo»
1491         «ENDIF»
1492     '''
1493
1494     private def dispatch nodeName(ListSchemaNode node) '''
1495         «IF node.isAddedBy»
1496             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1497         «ELSE»
1498             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
1499         «ENDIF»
1500     '''
1501
1502 }