2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.unified.doc.generator
10 import java.io.BufferedWriter
12 import java.io.IOException
13 import java.io.OutputStreamWriter
14 import java.text.SimpleDateFormat
15 import java.util.ArrayList
16 import java.util.Collection
17 import java.util.HashMap
18 import java.util.HashSet
19 import java.util.LinkedHashMap
23 import org.opendaylight.yangtools.yang.common.QName
24 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
25 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
26 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
27 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
28 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
29 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
34 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
35 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.Module
39 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode
42 import org.opendaylight.yangtools.yang.model.api.SchemaPath
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
44 import org.opendaylight.yangtools.yang.model.api.UsesNode
45 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
46 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
47 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
49 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
50 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
51 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
52 import org.opendaylight.yangtools.yang.model.util.ExtendedType
53 import org.slf4j.Logger
54 import org.slf4j.LoggerFactory
55 import org.sonatype.plexus.build.incremental.BuildContext
56 import org.sonatype.plexus.build.incremental.DefaultBuildContext
61 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
62 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
63 static val BuildContext CTX = new DefaultBuildContext();
64 var Module currentModule;
65 val Map<String, String> imports = new HashMap();
66 var SchemaContext ctx;
68 StringBuilder augmentChildNodesAsString
70 DataSchemaNode lastNodeInTargetPath = null
72 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
76 for (module : modulesToGen) {
77 add(generateDocumentation(module, context));
82 def generateDocumentation(Module module, SchemaContext ctx) {
83 val destination = new File(path, '''«module.name».html''')
85 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
87 val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
88 val bw = new BufferedWriter(fw)
89 currentModule = module;
90 bw.append(generate(module, ctx));
93 } catch (IOException e) {
94 LOG.error(e.getMessage());
99 def generate(Module module, SchemaContext ctx) '''
103 <title>«module.name»</title>
111 def body(Module module, SchemaContext ctx) '''
114 «typeDefinitionsSummary(module)»
115 «identitiesSummary(module)»
116 «groupingsSummary(module)»
117 «augmentationsSummary(module, ctx)»
118 «objectsSummary(module)»
119 «notificationsSummary(module)»
120 «rpcsSummary(module)»
121 «extensionsSummary(module)»
122 «featuresSummary(module)»
124 «typeDefinitions(module)»
134 «notifications(module)»
136 «augmentations(module, ctx)»
147 private def typeDefinitionsSummary(Module module) {
148 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
149 if (typedefs.empty) {
154 <h3>Type Definitions Summary</h3>
160 «FOR typedef : typedefs»
163 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
166 «typedef.description»
175 def typeDefinitions(Module module) {
176 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
177 if (typedefs.empty) {
181 <h2>Type Definitions</h2>
183 «FOR typedef : typedefs»
185 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
187 «typedef.descAndRefLi»
188 «typedef.restrictions»
196 private def identities(Module module) {
197 if (module.identities.empty) {
203 «FOR identity : module.identities»
205 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
207 «identity.descAndRefLi»
208 «IF identity.baseIdentity !== null»
209 «listItem("base", identity.baseIdentity.QName.localName)»
218 private def identitiesSummary(Module module) {
219 if (module.identities.empty) {
223 <h3>Identities Summary</h3>
229 «FOR identity : module.identities»
232 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
235 «identity.description»
243 private def groupings(Module module) {
244 if (module.groupings.empty) {
250 «FOR grouping : module.groupings»
252 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
254 «grouping.descAndRefLi»
255 «FOR childNode : grouping.childNodes»
256 «childNode.printSchemaNodeInfo»
265 private def groupingsSummary(Module module) {
266 if (module.groupings.empty) {
270 <h3>Groupings Summary</h3>
276 «FOR grouping : module.groupings»
279 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
282 «grouping.description»
290 def dataStore(Module module) {
291 if (module.childNodes.empty) {
295 <h2>Datastore Structure</h2>
300 def augmentations(Module module, SchemaContext context) {
301 if (module.augmentations.empty) {
305 <h2>Augmentations</h2>
308 «FOR augment : module.augmentations»
310 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
311 Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
312 «augment.description»
313 Status: «strong(String.valueOf(augment.status))»
314 «IF augment.reference !== null»
315 Reference «augment.reference»
317 «IF augment.whenCondition !== null»
318 When «augment.whenCondition.toString»
320 «FOR childNode : augment.childNodes»
321 «childNode.printSchemaNodeInfo»
325 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
326 «printNodeChildren(parseTargetPath(augment.targetPath))»
333 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
334 augmentChildNodesAsString = new StringBuilder();
335 augmentChildNodesAsString.append(printNodeChildren(childNodes))
339 private def parseTargetPath(SchemaPath path) {
340 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
341 for (QName pathElement : path.pathFromRoot) {
342 val prefix = pathElement.prefix
343 val moduleName = imports.get(prefix)
344 if(moduleName != null) {
345 val revision = pathElement.revision
346 val module = ctx.findModuleByName(moduleName, revision)
347 var foundNode = module.getDataChildByName(pathElement)
348 if(foundNode == null) {
349 val child = nodes.last
350 if (child instanceof DataNodeContainer) {
351 val dataContNode = child as DataNodeContainer
352 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
355 if(foundNode != null) {
356 nodes.add(foundNode);
361 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
364 val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
365 targetPathNodes.add(lastNodeInTargetPath)
367 return targetPathNodes
370 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Set<DataSchemaNode> childNodes) {
371 for(child : childNodes) {
372 if (child.QName.equals(findingNode))
376 for(child : childNodes) {
377 if(child instanceof ContainerSchemaNode) {
378 val contChild = child as ContainerSchemaNode
379 val foundChild = findNodeInChildNodes(findingNode, contChild.childNodes)
380 if (foundChild != null)
383 else if(child instanceof ListSchemaNode) {
384 val listChild = child as ListSchemaNode
385 val foundChild = findNodeInChildNodes(findingNode, listChild.childNodes)
386 if (foundChild != null)
392 private def printNodeChildren(List<DataSchemaNode> childNodes) {
393 if (childNodes.empty) {
400 «printAugmentedNode(childNodes.get(0))»
405 private def printAugmentedNode(DataSchemaNode child) {
407 if(child instanceof ChoiceCaseNode)
412 «IF child instanceof ContainerSchemaNode»
413 «printContainerNode(child as ContainerSchemaNode)»
415 «IF child instanceof AnyXmlSchemaNode»
416 «printAnyXmlNode(child as AnyXmlSchemaNode)»
418 «IF child instanceof LeafSchemaNode»
419 «printLeafNode(child as LeafSchemaNode)»
421 «IF child instanceof LeafListSchemaNode»
422 «printLeafListNode(child as LeafListSchemaNode)»
424 «IF child instanceof ListSchemaNode»
425 «printListNode(child as ListSchemaNode)»
427 «IF child instanceof ChoiceNode»
428 «printChoiceNode(child as ChoiceNode)»
433 private def printChoiceNode(ChoiceNode child) {
434 val List<ChoiceCaseNode> cases = new ArrayList(child.cases);
436 val ChoiceCaseNode aCase = cases.get(0)
437 for(caseChildNode : aCase.childNodes)
438 printAugmentedNode(caseChildNode)
442 private def printListNode(ListSchemaNode listNode) {
445 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
446 «FOR child : listNode.childNodes»
447 «printAugmentedNode(child)»
449 </«listNode.QName.localName»>
453 private def printContainerNode(ContainerSchemaNode containerNode) {
456 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
457 «FOR child : containerNode.childNodes»
458 «printAugmentedNode(child)»
460 </«containerNode.QName.localName»>
464 private def printLeafListNode(LeafListSchemaNode leafListNode) {
467 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
468 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
469 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
473 private def printAnyXmlNode(AnyXmlSchemaNode anyXmlNode) {
476 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
480 private def printLeafNode(LeafSchemaNode leafNode) {
483 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
487 private def augmentationsSummary(Module module, SchemaContext context) {
488 if (module.augmentations.empty) {
492 <h3>Augmentations Summary</h3>
498 «FOR augment : module.augmentations»
501 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
502 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
505 «augment.description»
513 def notifications(Module module) {
514 val Set<NotificationDefinition> notificationdefs = module.notifications
515 if (notificationdefs.empty) {
520 <h2>Notifications</h2>
521 «FOR notificationdef : notificationdefs»
523 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
524 «notificationdef.descAndRef»
525 «FOR childNode : notificationdef.childNodes»
526 «childNode.printSchemaNodeInfo»
532 private def notificationsSummary(Module module) {
533 if (module.notifications.empty) {
537 <h3>Notifications Summary</h3>
543 «FOR notification : module.notifications»
546 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
549 «notification.description»
557 def rpcs(Module module) {
558 if (module.rpcs.empty) {
563 <h2>RPC Definitions</h2>
564 «FOR rpc : module.rpcs»
565 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
568 «rpc.input.printSchemaNodeInfo»
569 «rpc.output.printSchemaNodeInfo»
576 private def rpcsSummary(Module module) {
577 if (module.rpcs.empty) {
581 <h3>RPCs Summary</h3>
587 «FOR rpc : module.rpcs»
590 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
601 def extensions(Module module) {
602 if (module.extensionSchemaNodes.empty) {
607 «FOR ext : module.extensionSchemaNodes»
609 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
616 private def extensionsSummary(Module module) {
617 if (module.extensionSchemaNodes.empty) {
621 <h3>Extensions Summary</h3>
627 «FOR ext : module.extensionSchemaNodes»
630 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
641 def features(Module module) {
642 if (module.features.empty) {
649 «FOR feature : module.features»
651 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
653 «feature.descAndRefLi»
661 private def featuresSummary(Module module) {
662 if (module.features.empty) {
666 <h3>Features Summary</h3>
672 «FOR feature : module.features»
675 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
678 «feature.description»
686 private def objectsSummary(Module module) {
687 if (module.childNodes.empty) {
691 <h3>Child Nodes Summary</h3>
697 «FOR childNode : module.childNodes»
700 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
703 «childNode.description»
711 def header(Module module)
713 <h1>«module.name»</h1>
715 <h2>Base Information</h2>
718 <td>«strong("prefix")»</td>
719 <td>«module.prefix»</td>
722 <td>«strong("namespace")»</td>
723 <td>«module.namespace»</td>
726 <td>«strong("revision")»</td>
727 <td>«REVISION_FORMAT.format(module.revision)»</td>
730 <td>«strong("description")»</td>
731 <td>«module.description»</td>
734 <td>«strong("yang-version")»</td>
735 <td>«module.yangVersion»</td>
738 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
739 «imp.prefix»:«imp.moduleName»«IF imp.revision !== null» «REVISION_FORMAT.format(imp.revision)»«ENDIF»;
745 def CharSequence schemaPathToId(SchemaPath path) {
747 return '''«FOR qName : path.path SEPARATOR "/"»«qName.localName»«ENDFOR»'''
751 def code(String string) '''<code>«string»</code>'''
753 def process(Module module) {
754 throw new UnsupportedOperationException("TODO: auto-generated method stub")
757 def CharSequence tree(Module module) '''
758 «strong(module.name)»
759 «module.childNodes.treeSet(InstanceIdentifier.builder.toInstance())»
762 private def dispatch CharSequence tree(ChoiceNode node,InstanceIdentifier path) '''
763 «node.nodeName» (choice)
764 «casesTree(node.cases,path)»
767 def casesTree(Set<ChoiceCaseNode> nodes,InstanceIdentifier path) '''
772 «node.childNodes.treeSet(path)»
778 private def dispatch CharSequence tree(DataSchemaNode node,InstanceIdentifier path) '''
782 private def dispatch CharSequence tree(ListSchemaNode node,InstanceIdentifier path) '''
783 «val newPath = path.append(node)»
784 «localLink(newPath,node.nodeName)»
785 «node.childNodes.treeSet(newPath)»
788 private def dispatch CharSequence tree(ContainerSchemaNode node,InstanceIdentifier path) '''
789 «val newPath = path.append(node)»
790 «localLink(newPath,node.nodeName)»
791 «node.childNodes.treeSet(newPath)»
794 def CharSequence childNodes(Module module) '''
795 «val childNodes = module.childNodes»
796 «IF !childNodes.nullOrEmpty»
799 «childNodes.printChildren(3,InstanceIdentifier.builder().toInstance())»
803 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
807 «IF node instanceof DataNodeContainer»
808 «val dataNode = node as DataNodeContainer»
810 «FOR usesNode : dataNode.uses»
815 «FOR typeDef : dataNode.typeDefinitions»
816 «typeDef.restrictions»
820 «FOR grouping : dataNode.groupings»
821 «grouping.printGrouping»
825 «FOR child : dataNode.childNodes»
826 «child.printSchemaNodeInfo»
834 def String typeAnchorLink(SchemaPath path, CharSequence text) {
836 val prefix = path.path.last.prefix
837 if(prefix == this.currentModule.prefix) {
838 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
839 } else if(!prefix.nullOrEmpty){
840 val String module = imports.get(prefix)
841 if(!module.nullOrEmpty) {
842 return '''«prefix»:«text»'''
843 //to enable external (import) links
844 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
851 def CharSequence printBaseInfo(SchemaNode node) {
852 if(node instanceof LeafSchemaNode) {
853 val LeafSchemaNode leafNode = (node as LeafSchemaNode)
855 «printInfo(node, "leaf")»
856 «listItem("type", typeAnchorLink(leafNode.type?.path, leafNode.type.QName.localName))»
857 «listItem("units", leafNode.units)»
858 «listItem("default", leafNode.^default)»
861 } else if(node instanceof LeafListSchemaNode) {
862 val LeafListSchemaNode leafListNode = (node as LeafListSchemaNode)
864 «printInfo(node, "leaf-list")»
865 «listItem("type", leafListNode.type?.QName.localName)»
868 } else if(node instanceof ListSchemaNode) {
869 val ListSchemaNode listNode = (node as ListSchemaNode)
871 «printInfo(node, "list")»
872 «FOR keyDef : listNode.keyDefinition»
873 «listItem("key definition", keyDef.localName)»
877 } else if(node instanceof ChoiceNode) {
878 val ChoiceNode choiceNode = (node as ChoiceNode)
880 «printInfo(node, "choice")»
881 «listItem("default case", choiceNode.defaultCase)»
882 «FOR caseNode : choiceNode.cases»
883 «caseNode.printSchemaNodeInfo»
887 } else if(node instanceof ChoiceCaseNode) {
889 «printInfo(node, "case")»
892 } else if(node instanceof ContainerSchemaNode) {
894 «printInfo(node, "container")»
897 } else if(node instanceof AnyXmlSchemaNode) {
899 «printInfo(node, "anyxml")»
905 def CharSequence printInfo(SchemaNode node, String nodeType) {
907 «IF node instanceof AugmentationTarget»
910 <li id="«node.path.schemaPathToId»">
911 «nodeType»: «node.QName.localName»
916 «strong(listItem(nodeType, node.QName.localName))»
919 «listItem("description", node.description)»
920 «listItem("reference", node.reference)»
921 «IF node instanceof DataSchemaNode»
922 «listItem("when condition", (node as DataSchemaNode).constraints.whenCondition?.toString)»
923 «listItem("min elements", (node as DataSchemaNode).constraints.minElements?.toString)»
924 «listItem("max elements", (node as DataSchemaNode).constraints.maxElements?.toString)»
929 def CharSequence printUses(UsesNode usesNode) {
931 «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
935 «FOR sp : usesNode.refines.keySet»
936 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
940 «FOR augment : usesNode.augmentations»
941 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
947 def CharSequence printGrouping(GroupingDefinition grouping) {
949 «strong(listItem("grouping", grouping.QName.localName))»
953 def CharSequence printChildren(Set<DataSchemaNode> nodes, int level, InstanceIdentifier path) {
954 val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
955 val leafNodes = nodes.filter(LeafSchemaNode)
956 val leafListNodes = nodes.filter(LeafListSchemaNode)
957 val choices = nodes.filter(ChoiceNode)
958 val cases = nodes.filter(ChoiceCaseNode)
959 val containers = nodes.filter(ContainerSchemaNode)
960 val lists = nodes.filter(ListSchemaNode)
962 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
963 <h3>Direct children</h3>
965 «FOR childNode : anyxmlNodes»
966 «childNode.printShortInfo(level,path)»
968 «FOR childNode : leafNodes»
969 «childNode.printShortInfo(level,path)»
971 «FOR childNode : leafListNodes»
972 «childNode.printShortInfo(level,path)»
974 «FOR childNode : containers»
975 «childNode.printShortInfo(level,path)»
977 «FOR childNode : lists»
978 «childNode.printShortInfo(level,path)»
983 «IF !path.path.empty»
985 «nodes.xmlExample(path.path.last.nodeType,path)»
988 «FOR childNode : containers»
989 «childNode.printInfo(level,path)»
991 «FOR childNode : lists»
992 «childNode.printInfo(level,path)»
994 «FOR childNode : choices»
995 «childNode.printInfo(level,path)»
997 «FOR childNode : cases»
998 «childNode.printInfo(level,path)»
1003 def CharSequence xmlExample(Set<DataSchemaNode> nodes, QName name,InstanceIdentifier path) '''
1005 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1009 def CharSequence xmplExampleTags(Set<DataSchemaNode> nodes, InstanceIdentifier identifier) '''
1010 <!-- Child nodes -->
1012 <!-- «node.QName.localName» -->
1013 «node.asXmlExampleTag(identifier)»
1018 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, InstanceIdentifier identifier) '''
1019 «node.QName.xmlExampleTag("...")»
1022 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, InstanceIdentifier identifier) '''
1023 <!-- This node could appear multiple times -->
1024 «node.QName.xmlExampleTag("...")»
1027 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, InstanceIdentifier identifier) '''
1028 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1029 «node.QName.xmlExampleTag("...")»
1033 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, InstanceIdentifier identifier) '''
1034 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1035 <!-- This node could appear multiple times -->
1036 «node.QName.xmlExampleTag("...")»
1040 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, InstanceIdentifier identifier) '''
1045 def xmlExampleTag(QName name, CharSequence data) {
1046 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1049 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1052 def header(int level,InstanceIdentifier name) '''
1053 <h«level» id="«FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1054 «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1060 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, InstanceIdentifier path) '''
1061 «header(level+1,node.QName)»
1064 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) '''
1065 «val newPath = path.append(node)»
1066 «header(level,newPath)»
1069 <dd>«newPath.asXmlPath»</dd>
1070 <dt>Restconf path</dt>
1071 <dd>«code(newPath.asRestconfPath)»</dd>
1073 «node.childNodes.printChildren(level,newPath)»
1076 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, InstanceIdentifier path) '''
1077 «val newPath = path.append(node)»
1078 «header(level,newPath)»
1081 <dd>«newPath.asXmlPath»</dd>
1082 <dt>Restconf path</dt>
1083 <dd>«code(newPath.asRestconfPath)»</dd>
1085 «node.childNodes.printChildren(level,newPath)»
1088 private def dispatch CharSequence printInfo(ChoiceNode node, int level, InstanceIdentifier path) '''
1089 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1090 «choiceCases.printChildren(level,path)»
1093 private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, InstanceIdentifier path) '''
1094 «node.childNodes.printChildren(level,path)»
1099 def CharSequence printShortInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) {
1100 val newPath = path.append(node);
1102 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1104 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1110 def CharSequence printShortInfo(ListSchemaNode node, int level, InstanceIdentifier path) {
1111 val newPath = path.append(node);
1113 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1115 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1121 def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, InstanceIdentifier path) {
1123 <li>«strong((node.QName.localName))» (anyxml)
1125 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1126 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1132 def CharSequence printShortInfo(LeafSchemaNode node, int level, InstanceIdentifier path) {
1134 <li>«strong((node.QName.localName))» (leaf)
1136 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1137 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1143 def CharSequence printShortInfo(LeafListSchemaNode node, int level, InstanceIdentifier path) {
1145 <li>«strong((node.QName.localName))» (leaf-list)
1147 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1153 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1155 <a href="#«anchor»">«text»</a>
1159 def CharSequence localLink(InstanceIdentifier identifier, CharSequence text) '''
1160 <a href="#«FOR cmp : identifier.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1164 private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ContainerSchemaNode node) {
1165 return identifier.node(node.QName);
1168 private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ListSchemaNode node) {
1169 val keyValues = new LinkedHashMap<QName,Object>();
1170 if(node.keyDefinition !== null) {
1171 for(definition : node.keyDefinition) {
1172 keyValues.put(definition,new Object);
1176 return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1180 def asXmlPath(InstanceIdentifier identifier) {
1184 def asRestconfPath(InstanceIdentifier identifier) {
1185 val it = new StringBuilder();
1186 append(currentModule.name)
1188 var previous = false;
1189 for(arg : identifier.path) {
1190 if(previous) append("/")
1191 append(arg.nodeType.localName);
1193 if(arg instanceof NodeIdentifierWithPredicates) {
1194 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
1195 for(qname : nodeIdentifier.keyValues.keySet) {
1197 append(qname.localName)
1206 private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1207 val List<QName> path = schemaPath.path
1208 val StringBuilder pathString = new StringBuilder()
1209 if (schemaPath.absolute) {
1210 pathString.append("/")
1213 val QName qname = path.get(0)
1214 var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1217 if (parent instanceof DataNodeContainer) {
1218 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)
1219 if (node == null && (parent instanceof Module)) {
1220 val notifications = (parent as Module).notifications;
1221 for (notification : notifications) {
1222 if (notification.QName.equals(name)) {
1227 if (node == null && (parent instanceof Module)) {
1228 val rpcs = (parent as Module).rpcs;
1230 if (rpc.QName.equals(name)) {
1236 var String prefix = name.prefix
1237 var String moduleName
1238 if (prefix == null || "".equals(prefix) || prefix.equals(module.prefix)) {
1239 moduleName = module.name
1241 moduleName = imports.get(prefix)
1243 pathString.append(moduleName)
1244 pathString.append(":")
1245 pathString.append(name.localName)
1246 pathString.append("/")
1247 if(node instanceof ChoiceNode && dataNode !== null) {
1248 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1249 if(caseNode !== null) {
1250 pathString.append("(case)");
1251 pathString.append(caseNode.QName.localName);
1257 return pathString.toString;
1261 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1262 «IF childNodes !== null && !childNodes.empty»
1263 «FOR child : childNodes.values»
1264 «childInfo(child, childNodes)»
1269 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1270 «val String path = nodeSchemaPathToPath(node, childNodes)»
1281 private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, InstanceIdentifier path) '''
1282 «IF childNodes !== null && !childNodes.empty»
1284 «FOR child : childNodes»
1293 def listKeys(ListSchemaNode node) '''
1294 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1297 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1300 «listItem("Argument", ext.argument)»
1304 private def dispatch CharSequence tree(Void obj, InstanceIdentifier path) '''
1309 /* #################### RESTRICTIONS #################### */
1310 private def restrictions(TypeDefinition<?> type) '''
1311 «type.baseType.toBaseStmt»
1316 private def dispatch toLength(TypeDefinition<?> type) {
1319 private def dispatch toLength(BinaryTypeDefinition type) '''
1320 «type.lengthConstraints.toLengthStmt»
1323 private def dispatch toLength(StringTypeDefinition type) '''
1324 «type.lengthConstraints.toLengthStmt»
1327 private def dispatch toLength(ExtendedType type) '''
1328 «type.lengthConstraints.toLengthStmt»
1331 private def dispatch toRange(TypeDefinition<?> type) {
1334 private def dispatch toRange(DecimalTypeDefinition type) '''
1335 «type.rangeConstraints.toRangeStmt»
1338 private def dispatch toRange(IntegerTypeDefinition type) '''
1339 «type.rangeConstraints.toRangeStmt»
1342 private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1343 «type.rangeConstraints.toRangeStmt»
1346 private def dispatch toRange(ExtendedType type) '''
1347 «type.rangeConstraints.toRangeStmt»
1350 def toLengthStmt(Collection<LengthConstraint> lengths) '''
1351 «IF lengths != null && !lengths.empty»
1352 «listItem("Length restrictions:")»
1354 «FOR length : lengths»
1356 «IF length.min == length.max»
1359 <«length.min», «length.max»>
1367 def toRangeStmt(Collection<RangeConstraint> ranges) '''
1368 «IF ranges != null && !ranges.empty»
1369 «listItem("Range restrictions:")»
1371 «FOR range : ranges»
1373 «IF range.min == range.max»
1376 <«range.min», «range.max»>
1384 def toBaseStmt(TypeDefinition<?> baseType) '''
1385 «IF baseType != null»
1386 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1392 /* #################### UTILITY #################### */
1393 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1394 private def italic(CharSequence str) '''<i>«str»</i>'''
1396 def CharSequence descAndRefLi(SchemaNode node) '''
1397 «listItem("Description", node.description)»
1398 «listItem("Reference", node.reference)»
1401 def CharSequence descAndRef(SchemaNode node) '''
1403 «IF node.reference !== null»
1404 Reference «node.reference»
1408 private def listItem(String value) '''
1409 «IF value !== null && !value.empty»
1416 private def listItem(String name, String value) '''
1417 «IF value !== null && !value.empty»
1424 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1425 if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
1429 val path = node.path.path
1430 val absolute = node.path.absolute;
1431 var StringBuilder result = new StringBuilder
1435 if (path !== null && !path.empty) {
1436 val List<QName> actual = new ArrayList()
1438 for (pathElement : path) {
1439 actual.add(pathElement)
1440 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1441 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1442 result.append(pathElement.localName)
1443 if (i != path.size - 1) {
1450 return result.toString
1453 private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1454 for (node : source) {
1455 destination.put(node.path, node)
1456 if (node instanceof DataNodeContainer) {
1457 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1459 if (node instanceof ChoiceNode) {
1460 val List<DataSchemaNode> choiceCases = new ArrayList()
1461 for (caseNode : (node as ChoiceNode).cases) {
1462 choiceCases.add(caseNode)
1464 collectChildNodes(choiceCases, destination)
1469 private def dispatch addedByInfo(SchemaNode node) '''
1472 private def dispatch addedByInfo(DataSchemaNode node) '''
1473 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1476 private def dispatch isAddedBy(SchemaNode node) {
1480 private def dispatch isAddedBy(DataSchemaNode node) {
1481 if (node.augmenting || node.addedByUses) {
1488 private def dispatch nodeName(SchemaNode node) '''
1490 «italic(node.QName.localName)»«node.addedByInfo»
1492 «node.QName.localName»«node.addedByInfo»
1496 private def dispatch nodeName(ContainerSchemaNode node) '''
1498 «strong(italic(node.QName.localName))»«node.addedByInfo»
1500 «strong(node.QName.localName)»«node.addedByInfo»
1504 private def dispatch nodeName(ListSchemaNode node) '''
1506 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1508 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»