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