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