Documentation generator improvements.
[yangtools.git] / code-generator / maven-sal-api-gen-plugin / src / main / java / org / opendaylight / yangtools / yang / unified / doc / generator / GeneratorImpl.xtend
1 package org.opendaylight.yangtools.yang.unified.doc.generator\r
2 \r
3 import org.opendaylight.yangtools.yang.model.api.SchemaContext\r
4 import java.io.File\r
5 import java.util.Set\r
6 import org.opendaylight.yangtools.yang.model.api.Module\r
7 import java.io.IOException\r
8 import java.util.HashSet\r
9 import java.io.BufferedWriter\r
10 import java.io.OutputStreamWriter;\r
11 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode\r
12 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode\r
13 import org.opendaylight.yangtools.yang.model.api.TypeDefinition\r
14 import org.opendaylight.yangtools.yang.model.api.SchemaNode\r
15 import org.opendaylight.yangtools.yang.model.util.ExtendedType\r
16 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition\r
17 import java.text.SimpleDateFormat\r
18 import java.util.Collection\r
19 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint\r
20 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition\r
21 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition\r
22 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint\r
23 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition\r
24 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition\r
25 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition\r
26 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer\r
27 import org.slf4j.LoggerFactory\r
28 import org.slf4j.Logger\r
29 import java.util.List\r
30 import org.opendaylight.yangtools.yang.common.QName\r
31 import org.opendaylight.yangtools.yang.model.api.RpcDefinition\r
32 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition\r
33 import java.util.ArrayList\r
34 import java.util.Map\r
35 import org.opendaylight.yangtools.yang.model.api.SchemaPath\r
36 \r
37 import org.sonatype.plexus.build.incremental.BuildContext;\r
38 import org.sonatype.plexus.build.incremental.DefaultBuildContext;\r
39 \r
40 import org.opendaylight.yangtools.yang.model.api.ChoiceNode\r
41 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode\r
42 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode\r
43 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier\r
44 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates\r
45 import java.util.LinkedHashMap\r
46 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier\r
47 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode\r
48 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode\rimport java.util.HashMap
49 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
50
51 class GeneratorImpl {\r
52 \r
53     File path\r
54     static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")\r
55     static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)\r
56     static val BuildContext CTX = new DefaultBuildContext();\r
57     var Module currentModule;\r
58 \r
59 \r
60     def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {\r
61         path = targetPath;\r
62         path.mkdirs();\r
63         val it = new HashSet;\r
64         for (module : modulesToGen) {\r
65             add(generateDocumentation(module, context));\r
66         }\r
67         return it;\r
68     }\r
69 \r
70     def generateDocumentation(Module module, SchemaContext ctx) {\r
71         val destination = new File(path, '''«module.name».html''')\r
72         try {\r
73             val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))\r
74             val bw = new BufferedWriter(fw)\r
75             currentModule = module;\r
76             bw.append(generate(module, ctx));\r
77             bw.close();\r
78             fw.close();\r
79         } catch (IOException e) {\r
80             LOG.error(e.getMessage());\r
81         }\r
82         return destination;\r
83     }\r
84 \r
85     def generate(Module module, SchemaContext ctx) '''\r
86         <!DOCTYPE html>\r
87         <html lang="en">\r
88           <head>\r
89             <title>«module.name»</title>\r
90           </head>\r
91           <body>\r
92             «body(module, ctx)»\r
93           </body>\r
94         </html>\r
95     '''\r
96 \r
97     def body(Module module, SchemaContext ctx) '''\r
98         «header(module)»\r
99 \r
100         «typeDefinitions(module)»\r
101 \r
102         «identities(module)»\r
103 \r
104         «groupings(module)»\r
105 \r
106         «dataStore(module)»\r
107 \r
108         «childNodes(module)»\r
109 \r
110         «notifications(module)»\r
111 \r
112         «augmentations(module, ctx)»\r
113 \r
114         «rpcs(module)»\r
115 \r
116         «extensions(module)»\r
117 \r
118         «features(module)»\r
119 \r
120     '''\r
121 \r
122 \r
123     def typeDefinitions(Module module) {\r
124         val Set<TypeDefinition<?>> typedefs = module.typeDefinitions\r
125         if (typedefs.empty) {\r
126             return '';\r
127         }\r
128         return '''\r
129             <h2>Type Definitions</h2>\r
130             <ul>\r
131             «FOR typedef : typedefs»\r
132                 <li>\r
133                     «strong("typedef " + typedef.QName.localName)»\r
134                     <ul>\r
135                     «typedef.descAndRefLi»\r
136                     «typedef.restrictions»\r
137                     </ul>\r
138                 </li>\r
139             «ENDFOR»\r
140             </ul>\r
141         '''\r
142     }\r
143 \r
144     private def identities(Module module) {\r
145         if (module.identities.empty) {\r
146             return '';\r
147         }\r
148         return '''\r
149             <h2>Identities</h2>\r
150             <ul>\r
151             «FOR identity : module.identities»\r
152                 <li>\r
153                     «strong("identity " + identity.QName.localName)»\r
154                     <ul>\r
155                     «identity.descAndRefLi»\r
156                     «IF identity.baseIdentity != null»\r
157                         «listItem("base", identity.baseIdentity.QName.localName)»\r
158                     «ENDIF»\r
159                     </ul>\r
160                 </li>\r
161             «ENDFOR»\r
162             </ul>\r
163         '''\r
164     }\r
165 \r
166     private def groupings(Module module) {\r
167         if (module.groupings.empty) {\r
168             return '';\r
169         }\r
170         return '''\r
171             <h2>Groupings</h2>\r
172             <ul>\r
173             «FOR grouping : module.groupings»\r
174                 <li>\r
175                     «strong("grouping " + grouping.QName.localName)»\r
176                     <ul>\r
177                         «grouping.descAndRefLi»\r
178                     </ul>\r
179                 </li>\r
180             «ENDFOR»\r
181             </ul>\r
182         '''\r
183     }\r
184 \r
185     def dataStore(Module module) {\r
186         if (module.childNodes.empty) {\r
187             return '';\r
188         }\r
189         return '''\r
190             <h2>Datastore Structure</h2>\r
191             «tree(module)»\r
192         '''\r
193     }\r
194 \r
195     def augmentations(Module module, SchemaContext context) {\r
196         if (module.augmentations.empty) {\r
197             return '';\r
198         }\r
199         return '''\r
200             <h2>Augmentations</h2>\r
201 \r
202             <ul>\r
203             «FOR augment : module.augmentations»\r
204                 <li>\r
205                     <h3>Target [«schemaPathAsRestconfPath(module, augment.targetPath, context)»]</h3>\r
206                     «augment.description»\r
207                     «IF augment.reference !== null»\r
208                         Reference «augment.reference»\r
209                     «ENDIF»\r
210                     «IF augment.whenCondition !== null»\r
211                         When «augment.whenCondition.toString»\r
212                     «ENDIF»\r
213                     «augment.childNodes.printChildren(3,InstanceIdentifier.builder().toInstance())»\r
214                 </li>\r
215             «ENDFOR»\r
216             </ul>\r
217         '''\r
218     }\r
219 \r
220     def notifications(Module module) {\r
221         val Set<NotificationDefinition> notificationdefs = module.notifications\r
222         if (notificationdefs.empty) {\r
223             return '';\r
224         }\r
225 \r
226         return '''\r
227             <h2>Notifications</h2>\r
228             «FOR notificationdef : notificationdefs»\r
229 \r
230                 <h3>«notificationdef.nodeName»</h3>\r
231                     «notificationdef.descAndRef»\r
232                     «notificationdef.childNodes.printChildren(3,InstanceIdentifier.builder().toInstance())»\r
233             «ENDFOR»\r
234         '''\r
235     }\r
236 \r
237     def rpcs(Module module) {\r
238         if (module.rpcs.empty) {\r
239             return '';\r
240         }\r
241 \r
242         return '''\r
243             <h2>RPC Definitions</h2>\r
244             «FOR rpc : module.rpcs»\r
245                 <h3>«rpc.nodeName»</h3>\r
246                     «rpc.rpcInfo(InstanceIdentifier.builder().node(rpc.QName).toInstance())»\r
247             «ENDFOR»\r
248             </ul>\r
249         '''\r
250     }\r
251 \r
252     def extensions(Module module) {\r
253         if (module.extensionSchemaNodes.empty) {\r
254             return '';\r
255         }\r
256         return '''\r
257             <h2>Extensions</h2>\r
258             «FOR ext : module.extensionSchemaNodes»\r
259                 <li>\r
260                     <h3>«ext.nodeName»</h3>\r
261                 </li>\r
262                 «extensionInfo(ext)»\r
263             «ENDFOR»\r
264         '''\r
265     }\r
266 \r
267     def features(Module module) {\r
268         if (module.features.empty) {\r
269             return '';\r
270         }\r
271         return '''\r
272             <h2>Features</h2>\r
273 \r
274             <ul>\r
275             «FOR feature : module.features»\r
276                 <li>\r
277                     «strong("feature " + feature.QName.localName)»\r
278                     <ul>\r
279                         «feature.descAndRefLi»\r
280                     </ul>\r
281                 </li>\r
282             «ENDFOR»\r
283             </ul>\r
284         '''\r
285     }\r
286 \r
287     def header(Module module) '''\r
288         <h1>«module.name»</h1>\r
289 \r
290         <h2>Base Information</h2>\r
291         <dl>\r
292             <dt>Prefix</dt>\r
293             <dd>«pre(module.prefix)»</dd>\r
294             <dt>Namespace</dt>\r
295             <dd>«pre(module.namespace.toString)»</dd>\r
296             <dt>Revision</dt>\r
297             <dd>«pre(REVISION_FORMAT.format(module.revision))»</dd>\r
298 \r
299             «FOR imp : module.imports BEFORE "<dt>Imports</dt>" »\r
300                 <dd>«code(imp.prefix)» = «code(imp.moduleName)»</dd>\r
301             «ENDFOR»\r
302         </dl>\r
303     '''\r
304 \r
305     def code(String string) '''<code>«string»</code>'''\r
306 \r
307     def process(Module module) {\r
308         throw new UnsupportedOperationException("TODO: auto-generated method stub")\r
309     }\r
310 \r
311     def CharSequence tree(Module module) '''\r
312         «strong("module " + module.name)»\r
313         «module.childNodes.treeSet(InstanceIdentifier.builder.toInstance())»\r
314     '''\r
315 \r
316     private def dispatch CharSequence tree(ChoiceNode node,InstanceIdentifier path) '''\r
317         «node.nodeName» (choice)\r
318         «casesTree(node.cases,path)»\r
319     '''\r
320 \r
321     def casesTree(Set<ChoiceCaseNode> nodes,InstanceIdentifier path) '''\r
322         <ul>\r
323         «FOR node : nodes»\r
324             <li>\r
325             «node.nodeName»\r
326             «node.childNodes.treeSet(path)»\r
327             </li>\r
328         «ENDFOR»\r
329         </ul>\r
330     '''\r
331 \r
332     private def dispatch CharSequence tree(DataSchemaNode node,InstanceIdentifier path) '''\r
333         «node.nodeName»\r
334     '''\r
335 \r
336     private def dispatch CharSequence tree(ListSchemaNode node,InstanceIdentifier path) '''\r
337         «val newPath = path.append(node)»\r
338         «localLink(newPath,node.nodeName)»\r
339         «node.childNodes.treeSet(newPath)»\r
340     '''\r
341 \r
342     private def dispatch CharSequence tree(ContainerSchemaNode node,InstanceIdentifier path) '''\r
343         «val newPath = path.append(node)»\r
344         «localLink(newPath,node.nodeName)»\r
345         «node.childNodes.treeSet(newPath)»\r
346     '''\r
347 \r
348     def CharSequence childNodes(Module module) '''\r
349         «val childNodes = module.childNodes»\r
350         «IF childNodes !== null && !childNodes.empty»\r
351             <h2>Child nodes</h2>\r
352 \r
353             «childNodes.printChildren(3,InstanceIdentifier.builder().toInstance())»\r
354         «ENDIF»\r
355     '''\r
356 \r
357     def CharSequence printChildren(Set<DataSchemaNode> nodes, int level, InstanceIdentifier path) {\r
358     val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)\r
359     val leafNodes = nodes.filter(LeafSchemaNode)\r
360     val leafListNodes = nodes.filter(LeafListSchemaNode)\r
361     val choices = nodes.filter(ChoiceNode)\r
362     val cases = nodes.filter(ChoiceCaseNode)\r
363     val containers = nodes.filter(ContainerSchemaNode)\r
364     val lists = nodes.filter(ListSchemaNode)\r
365     return '''\r
366         «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»\r
367         <h3>Direct children</h3>\r
368         <ul>\r
369         «FOR childNode : anyxmlNodes»\r
370             «childNode.printShortInfo(level,path)»\r
371         «ENDFOR»\r
372         «FOR childNode : leafNodes»\r
373             «childNode.printShortInfo(level,path)»\r
374         «ENDFOR»\r
375         «FOR childNode : leafListNodes»\r
376             «childNode.printShortInfo(level,path)»\r
377         «ENDFOR»\r
378         «FOR childNode : containers»\r
379             «childNode.printShortInfo(level,path)»\r
380         «ENDFOR»\r
381         «FOR childNode : lists»\r
382             «childNode.printShortInfo(level,path)»\r
383         «ENDFOR»\r
384         </ul>\r
385         «ENDIF»\r
386 \r
387         «IF !path.path.empty»\r
388         <h3>XML example</h3>\r
389         «nodes.xmlExample(path.path.last.nodeType,path)»\r
390         </h3>\r
391         «ENDIF»\r
392         «FOR childNode : containers»\r
393             «childNode.printInfo(level,path)»\r
394         «ENDFOR»\r
395         «FOR childNode : lists»\r
396             «childNode.printInfo(level,path)»\r
397         «ENDFOR»\r
398         «FOR childNode : choices»\r
399             «childNode.printInfo(level,path)»\r
400         «ENDFOR»\r
401         «FOR childNode : cases»\r
402             «childNode.printInfo(level,path)»\r
403         «ENDFOR»\r
404         \r
405     '''\r
406     }\r
407 \r
408     def CharSequence xmlExample(Set<DataSchemaNode> nodes, QName name,InstanceIdentifier path) '''\r
409     <pre>\r
410         «xmlExampleTag(name,nodes.xmplExampleTags(path))»\r
411     </pre>\r
412     '''\r
413 \r
414     def CharSequence xmplExampleTags(Set<DataSchemaNode> nodes, InstanceIdentifier identifier) '''\r
415         <!-- Child nodes -->\r
416         «FOR node : nodes»\r
417         <!-- «node.QName.localName» -->\r
418             «node.asXmlExampleTag(identifier)»\r
419         «ENDFOR»\r
420 \r
421     '''\r
422 \r
423     private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, InstanceIdentifier identifier) '''\r
424         «node.QName.xmlExampleTag("...")»\r
425     '''\r
426 \r
427     private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, InstanceIdentifier identifier) '''\r
428         &lt!-- This node could appear multiple times --&gt\r
429         «node.QName.xmlExampleTag("...")»\r
430     '''\r
431 \r
432     private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, InstanceIdentifier identifier) '''\r
433         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt\r
434         «node.QName.xmlExampleTag("...")»\r
435     '''\r
436 \r
437 \r
438     private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, InstanceIdentifier identifier) '''\r
439         &lt!-- See «localLink(identifier.append(node),"definition")» for child nodes.  --&gt\r
440         &lt!-- This node could appear multiple times --&gt\r
441         «node.QName.xmlExampleTag("...")»\r
442     '''\r
443 \r
444 \r
445     private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, InstanceIdentifier identifier) '''\r
446         <!-- noop -->\r
447     '''\r
448 \r
449 \r
450     def xmlExampleTag(QName name, CharSequence data) {\r
451         return '''&lt;«name.localName» xmlns="«name.namespace»"&gt;«data»&lt;/«name.localName»&gt;'''\r
452     }\r
453 \r
454     def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''\r
455 \r
456 \r
457     def header(int level,InstanceIdentifier name) '''\r
458         <h«level» id="«FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">\r
459             «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»\r
460         </h«level»>\r
461     '''\r
462 \r
463 \r
464 \r
465     private def dispatch CharSequence printInfo(DataSchemaNode node, int level, InstanceIdentifier path) '''\r
466         «header(level+1,node.QName)»\r
467     '''\r
468 \r
469     private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) '''\r
470         «val newPath = path.append(node)»\r
471         «header(level,newPath)»\r
472         <dl>\r
473           <dt>XML Path</dt>\r
474           <dd>«newPath.asXmlPath»</dd>\r
475           <dt>Restconf path</dt>\r
476           <dd>«code(newPath.asRestconfPath)»</dd>\r
477         </dl>\r
478         «node.childNodes.printChildren(level,newPath)»\r
479     '''\r
480 \r
481     private def dispatch CharSequence printInfo(ListSchemaNode node, int level, InstanceIdentifier path) '''\r
482         «val newPath = path.append(node)»\r
483         «header(level,newPath)»\r
484         <dl>\r
485           <dt>XML Path</dt>\r
486           <dd>«newPath.asXmlPath»</dd>\r
487           <dt>Restconf path</dt>\r
488           <dd>«code(newPath.asRestconfPath)»</dd>\r
489         </dl>\r
490         «node.childNodes.printChildren(level,newPath)»\r
491     '''\r
492 \r
493     private def dispatch CharSequence printInfo(ChoiceNode node, int level, InstanceIdentifier path) '''\r
494         «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»\r
495         «choiceCases.printChildren(level,path)»\r
496     '''\r
497 \r
498     private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, InstanceIdentifier path) '''\r
499         «node.childNodes.printChildren(level,path)»\r
500     '''\r
501 \r
502 \r
503 \r
504     def CharSequence printShortInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) {\r
505         val newPath = path.append(node);\r
506         return '''\r
507             <li>«strong(localLink(newPath,node.QName.localName))» (container)</li>\r
508         '''\r
509     }\r
510 \r
511     def CharSequence printShortInfo(ListSchemaNode node, int level, InstanceIdentifier path) {\r
512         val newPath = path.append(node);\r
513         return '''\r
514             <li>«strong(localLink(newPath,node.QName.localName))» (list)</li>\r
515         '''\r
516     }\r
517 \r
518     def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, InstanceIdentifier path) {\r
519         return '''\r
520             <li>«strong((node.QName.localName))» (anyxml)</li>\r
521         '''\r
522     }\r
523 \r
524     def CharSequence printShortInfo(LeafSchemaNode node, int level, InstanceIdentifier path) {\r
525         return '''\r
526             <li>«strong((node.QName.localName))» (leaf)</li>\r
527         '''\r
528     }\r
529 \r
530     def CharSequence printShortInfo(LeafListSchemaNode node, int level, InstanceIdentifier path) {\r
531         return '''\r
532             <li>«strong((node.QName.localName))» (leaf-list)</li>\r
533         '''\r
534     }\r
535 \r
536     def CharSequence localLink(InstanceIdentifier identifier, CharSequence text) '''\r
537         <a href="#«FOR cmp : identifier.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>\r
538     '''\r
539 \r
540 \r
541     private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ContainerSchemaNode node) {\r
542         val pathArguments = new ArrayList(identifier.path)\r
543         pathArguments.add(new NodeIdentifier(node.QName));\r
544         return new InstanceIdentifier(pathArguments);\r
545     }\r
546 \r
547     private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ListSchemaNode node) {\r
548         val pathArguments = new ArrayList(identifier.path)\r
549         val keyValues = new LinkedHashMap<QName,Object>();\r
550         if(node.keyDefinition != null) {\r
551             for(definition : node.keyDefinition) {\r
552                 keyValues.put(definition,new Object);\r
553             }\r
554         }\r
555         pathArguments.add(new NodeIdentifierWithPredicates(node.QName,keyValues));\r
556         return new InstanceIdentifier(pathArguments);\r
557     }\r
558 \r
559 \r
560     def asXmlPath(InstanceIdentifier identifier) {\r
561         return "";\r
562     }\r
563 \r
564     def asRestconfPath(InstanceIdentifier identifier) {\r
565         val it = new StringBuilder();\r
566         append(currentModule.name)\r
567         append(":")\r
568         var previous = false;\r
569         for(arg : identifier.path) {\r
570             if(previous) append("/")\r
571             append(arg.nodeType.localName);\r
572             previous = true;\r
573             if(arg instanceof NodeIdentifierWithPredicates) {\r
574                 val nodeIdentifier = arg as NodeIdentifierWithPredicates;\r
575                 for(qname : nodeIdentifier.keyValues.keySet) {\r
576                     append("/{");\r
577                     append(qname.localName)\r
578                     append("}")\r
579                 }\r
580             }\r
581         }\r
582 \r
583         return it.toString;\r
584     }\r
585 \r
586     private def String schemaPathAsRestconfPath(Module module, SchemaPath schemaPath, SchemaContext ctx) {\r
587         val Map<String, String> imports = new HashMap();\r
588         for (mImport : module.imports) {\r
589             imports.put(mImport.prefix, mImport.moduleName)\r
590         }\r
591 \r
592         val List<QName> path = schemaPath.path\r
593         val StringBuilder pathString = new StringBuilder()\r
594         if (schemaPath.absolute) {\r
595             pathString.append("/")\r
596         }\r
597 \r
598         val QName qname = path.get(0)\r
599         var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)\r
600 \r
601         for (name : path) {\r
602             if (parent instanceof DataNodeContainer) {\r
603                 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)\r
604                 if (node == null && (parent instanceof Module)) {\r
605                     val notifications = (parent as Module).notifications;\r
606                     for (notification : notifications) {\r
607                         if (notification.QName.localName.equals(name.localName)) {\r
608                             node = notification\r
609                         }\r
610                     }\r
611                 }\r
612 \r
613                 if (!(node instanceof ChoiceNode) && !(node instanceof ChoiceCaseNode)) {\r
614                     var String prefix = node.QName.prefix\r
615                     var String moduleName\r
616                     if (prefix == null || "".equals(prefix) || prefix.equals(module.prefix)) {\r
617                         moduleName = module.name\r
618                     } else {\r
619                         moduleName = imports.get(prefix)\r
620                     }\r
621                     pathString.append(moduleName)\r
622                     pathString.append(":")\r
623                     pathString.append(node.QName.localName)\r
624                     pathString.append("/")\r
625                 }\r
626                 parent = node\r
627             } else if (parent instanceof ChoiceNode) {\r
628                 parent = (parent as ChoiceNode).getCaseNodeByName(qname.localName)\r
629             }\r
630         }\r
631         return pathString.toString;\r
632     }\r
633 \r
634 \r
635     def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''\r
636         «IF childNodes !== null && !childNodes.empty»\r
637             «FOR child : childNodes.values»\r
638                 «childInfo(child, childNodes)»\r
639             «ENDFOR»\r
640         «ENDIF»\r
641     '''\r
642 \r
643     def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''\r
644         «val String path = nodeSchemaPathToPath(node, childNodes)»\r
645         «IF path != null»\r
646             «code(path)»\r
647                 «IF node !== null»\r
648                 <ul>\r
649                 «node.descAndRefLi»\r
650                 </ul>\r
651             «ENDIF»\r
652         «ENDIF»\r
653     '''\r
654 \r
655     private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, InstanceIdentifier path) '''\r
656         «IF childNodes !== null && !childNodes.empty»\r
657             <ul>\r
658             «FOR child : childNodes»\r
659                 <li>\r
660                     «child.tree(path)»\r
661                 </li>\r
662             «ENDFOR»\r
663             </ul>\r
664         «ENDIF»\r
665     '''\r
666 \r
667     def listKeys(ListSchemaNode node) '''\r
668         [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]\r
669     '''\r
670 \r
671     private def CharSequence rpcInfo(RpcDefinition rpc,InstanceIdentifier path) '''\r
672         <ul>\r
673             «rpc.descAndRefLi»\r
674             <li>\r
675                 «rpc.input.tree(path)»\r
676             </li>\r
677             <li>\r
678                 «rpc.output.tree(path)»\r
679             </li>\r
680         </ul>\r
681     '''\r
682 \r
683     private def CharSequence extensionInfo(ExtensionDefinition ext) '''\r
684         <ul>\r
685             «ext.descAndRefLi»\r
686             «listItem("Argument", ext.argument)»\r
687         </ul>\r
688     '''\r
689 \r
690     private def dispatch CharSequence tree(Void obj, InstanceIdentifier path) '''\r
691     '''\r
692 \r
693 \r
694 \r
695     /* #################### RESTRICTIONS #################### */\r
696     private def restrictions(TypeDefinition<?> type) '''\r
697         «type.toLength»\r
698         «type.toRange»\r
699     '''\r
700 \r
701     private def dispatch toLength(TypeDefinition<?> type) {\r
702     }\r
703 \r
704     private def dispatch toLength(BinaryTypeDefinition type) '''\r
705         «type.lengthConstraints.toLengthStmt»\r
706     '''\r
707 \r
708     private def dispatch toLength(StringTypeDefinition type) '''\r
709         «type.lengthConstraints.toLengthStmt»\r
710     '''\r
711 \r
712     private def dispatch toLength(ExtendedType type) '''\r
713         «type.lengthConstraints.toLengthStmt»\r
714     '''\r
715 \r
716     private def dispatch toRange(TypeDefinition<?> type) {\r
717     }\r
718 \r
719     private def dispatch toRange(DecimalTypeDefinition type) '''\r
720         «type.rangeConstraints.toRangeStmt»\r
721     '''\r
722 \r
723     private def dispatch toRange(IntegerTypeDefinition type) '''\r
724         «type.rangeConstraints.toRangeStmt»\r
725     '''\r
726 \r
727     private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''\r
728         «type.rangeConstraints.toRangeStmt»\r
729     '''\r
730 \r
731     private def dispatch toRange(ExtendedType type) '''\r
732         «type.rangeConstraints.toRangeStmt»\r
733     '''\r
734 \r
735     def toLengthStmt(Collection<LengthConstraint> lengths) '''\r
736         «IF lengths != null && !lengths.empty»\r
737             «listItem("Length restrictions")»\r
738             <ul>\r
739             «FOR length : lengths»\r
740                 <li>\r
741                 «IF length.min == length.max»\r
742                     «length.min»\r
743                 «ELSE»\r
744                     &lt;«length.min», «length.max»&gt;\r
745                 «ENDIF»\r
746                 </li>\r
747             «ENDFOR»\r
748             </ul>\r
749         «ENDIF»\r
750     '''\r
751 \r
752     def toRangeStmt(Collection<RangeConstraint> ranges) '''\r
753         «IF ranges != null && !ranges.empty»\r
754             «listItem("Range restrictions")»\r
755             <ul>\r
756             «FOR range : ranges»\r
757                 <li>\r
758                 «IF range.min == range.max»\r
759                     «range.min»\r
760                 «ELSE»\r
761                     &lt;«range.min», «range.max»&gt;\r
762                 «ENDIF»\r
763                 </li>\r
764             «ENDFOR»\r
765             </ul>\r
766         «ENDIF»\r
767     '''\r
768 \r
769 \r
770 \r
771     /* #################### UTILITY #################### */\r
772     private def String strong(CharSequence str) '''<strong>«str»</strong>'''\r
773     private def italic(CharSequence str) '''<i>«str»</i>'''\r
774     private def pre(CharSequence str) '''<pre>«str»</pre>'''\r
775 \r
776     def CharSequence descAndRefLi(SchemaNode node) '''\r
777         «listItem(node.description)»\r
778         «listItem("Reference", node.reference)»\r
779     '''\r
780 \r
781     def CharSequence descAndRef(SchemaNode node) '''\r
782         «node.description»\r
783         «IF node.reference !== null»\r
784             Reference «node.reference»\r
785         «ENDIF»\r
786     '''\r
787 \r
788     private def listItem(String value) '''\r
789         «IF value !== null && !value.empty»\r
790             <li>\r
791                 «value»\r
792             </li>\r
793         «ENDIF»\r
794     '''\r
795 \r
796     private def listItem(String name, String value) '''\r
797         «IF value !== null && !value.empty»\r
798             <li>\r
799                 «name»: «value»\r
800             </li>\r
801         «ENDIF»\r
802     '''\r
803 \r
804     private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {\r
805         if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {\r
806             return null\r
807         }\r
808 \r
809         val path = node.path.path\r
810         val absolute = node.path.absolute;\r
811         var StringBuilder result = new StringBuilder\r
812         if (absolute) {\r
813             result.append("/")\r
814         }\r
815         if (path !== null && !path.empty) {\r
816             val List<QName> actual = new ArrayList()\r
817             var i = 0;\r
818             for (pathElement : path) {\r
819                 actual.add(pathElement)\r
820                 val DataSchemaNode nodeByPath = childNodes.get(new SchemaPath(actual, absolute)) \r
821                 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {\r
822                     result.append(pathElement.localName)\r
823                     if (i != path.size - 1) {\r
824                         result.append("/")\r
825                     }\r
826                 }\r
827                 i = i + 1\r
828             }\r
829         }\r
830         return result.toString\r
831     }\r
832 \r
833     private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {\r
834         for (node : source) {\r
835             destination.put(node.path, node)\r
836             if (node instanceof DataNodeContainer) {\r
837                 collectChildNodes((node as DataNodeContainer).childNodes, destination)\r
838             }\r
839             if (node instanceof ChoiceNode) {\r
840                 val List<DataSchemaNode> choiceCases = new ArrayList()\r
841                 for (caseNode : (node as ChoiceNode).cases) {\r
842                     choiceCases.add(caseNode)\r
843                 }\r
844                 collectChildNodes(choiceCases, destination)\r
845             }\r
846         }\r
847     }\r
848 \r
849     private def dispatch addedByInfo(SchemaNode node) '''\r
850     '''\r
851 \r
852     private def dispatch addedByInfo(DataSchemaNode node) '''\r
853         «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»\r
854     '''\r
855 \r
856     private def dispatch isAddedBy(SchemaNode node) {\r
857         return false;\r
858     }\r
859 \r
860     private def dispatch isAddedBy(DataSchemaNode node) {\r
861         if (node.augmenting || node.addedByUses) {\r
862             return true\r
863         } else {\r
864             return false;\r
865         }\r
866     }\r
867 \r
868     private def dispatch nodeName(SchemaNode node) '''\r
869         «IF node.isAddedBy»\r
870             «italic(node.QName.localName)»«node.addedByInfo»\r
871         «ELSE»\r
872             «node.QName.localName»«node.addedByInfo»\r
873         «ENDIF»\r
874     '''\r
875     \r
876     private def dispatch nodeName(ContainerSchemaNode node) '''\r
877         «IF node.isAddedBy»\r
878             «strong(italic(node.QName.localName))»«node.addedByInfo»\r
879         «ELSE»\r
880             «strong(node.QName.localName)»«node.addedByInfo»\r
881         «ENDIF»\r
882     '''\r
883 \r
884     private def dispatch nodeName(ListSchemaNode node) '''\r
885         «IF node.isAddedBy»\r
886             «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»\r
887         «ELSE»\r
888             «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»\r
889         «ENDIF»\r
890     '''\r
891 \r
892 }\r