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