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.mdsal.binding.yang.unified.doc.generator
10 import com.google.common.collect.Iterables
11 import java.io.BufferedWriter
13 import java.io.IOException
14 import java.io.OutputStreamWriter
15 import java.nio.charset.StandardCharsets
16 import java.text.SimpleDateFormat
17 import java.util.ArrayList
18 import java.util.Collection
19 import java.util.HashMap
20 import java.util.HashSet
21 import java.util.LinkedHashMap
25 import org.opendaylight.yangtools.yang.common.QName
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
28 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
29 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
30 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
31 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
36 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
37 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
39 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
40 import org.opendaylight.yangtools.yang.model.api.Module
41 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext
43 import org.opendaylight.yangtools.yang.model.api.SchemaNode
44 import org.opendaylight.yangtools.yang.model.api.SchemaPath
45 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
46 import org.opendaylight.yangtools.yang.model.api.UsesNode
47 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
49 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
50 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
51 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
52 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
53 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
54 import org.slf4j.Logger
55 import org.slf4j.LoggerFactory
56 import org.sonatype.plexus.build.incremental.BuildContext
57 import org.sonatype.plexus.build.incremental.DefaultBuildContext
58 import com.google.common.collect.Lists
63 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
64 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
65 static val BuildContext CTX = new DefaultBuildContext();
66 var Module currentModule;
67 val Map<String, String> imports = new HashMap();
68 var SchemaContext ctx;
70 StringBuilder augmentChildNodesAsString
72 DataSchemaNode lastNodeInTargetPath = null
74 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
78 for (module : modulesToGen) {
79 add(generateDocumentation(module, context));
84 def generateDocumentation(Module module, SchemaContext ctx) {
85 val destination = new File(path, '''«module.name».html''')
87 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
89 val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination), StandardCharsets.UTF_8)
90 val bw = new BufferedWriter(fw)
91 currentModule = module;
92 bw.append(generate(module, ctx));
95 } catch (IOException e) {
96 LOG.error(e.getMessage());
101 def generate(Module module, SchemaContext ctx) '''
105 <title>«module.name»</title>
113 def body(Module module, SchemaContext ctx) '''
116 «typeDefinitionsSummary(module)»
117 «identitiesSummary(module)»
118 «groupingsSummary(module)»
119 «augmentationsSummary(module, ctx)»
120 «objectsSummary(module)»
121 «notificationsSummary(module)»
122 «rpcsSummary(module)»
123 «extensionsSummary(module)»
124 «featuresSummary(module)»
126 «typeDefinitions(module)»
136 «notifications(module)»
138 «augmentations(module, ctx)»
149 private def typeDefinitionsSummary(Module module) {
150 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
151 if (typedefs.empty) {
156 <h3>Type Definitions Summary</h3>
162 «FOR typedef : typedefs»
165 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
168 «typedef.description»
177 def typeDefinitions(Module module) {
178 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
179 if (typedefs.empty) {
183 <h2>Type Definitions</h2>
185 «FOR typedef : typedefs»
187 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
189 «typedef.descAndRefLi»
190 «typedef.restrictions»
198 private def identities(Module module) {
199 if (module.identities.empty) {
205 «FOR identity : module.identities»
207 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
209 «identity.descAndRefLi»
210 «IF !identity.baseIdentities.isEmpty»
211 «listItem("base", identity.baseIdentities.get(0).QName.localName)»
220 private def identitiesSummary(Module module) {
221 if (module.identities.empty) {
225 <h3>Identities Summary</h3>
231 «FOR identity : module.identities»
234 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
237 «identity.description»
245 private def groupings(Module module) {
246 if (module.groupings.empty) {
252 «FOR grouping : module.groupings»
254 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
256 «grouping.descAndRefLi»
257 «FOR childNode : grouping.childNodes»
258 «childNode.printSchemaNodeInfo»
267 private def groupingsSummary(Module module) {
268 if (module.groupings.empty) {
272 <h3>Groupings Summary</h3>
278 «FOR grouping : module.groupings»
281 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
284 «grouping.description»
292 def dataStore(Module module) {
293 if (module.childNodes.empty) {
297 <h2>Datastore Structure</h2>
302 def augmentations(Module module, SchemaContext context) {
303 if (module.augmentations.empty) {
307 <h2>Augmentations</h2>
310 «FOR augment : module.augmentations»
312 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
313 Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
314 «augment.description»
315 Status: «strong(String.valueOf(augment.status))»
316 «IF augment.reference !== null»
317 Reference «augment.reference»
319 «IF augment.whenCondition !== null»
320 When «augment.whenCondition.toString»
322 «FOR childNode : augment.childNodes»
323 «childNode.printSchemaNodeInfo»
327 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
328 «printNodeChildren(parseTargetPath(augment.targetPath))»
335 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
336 augmentChildNodesAsString = new StringBuilder();
337 augmentChildNodesAsString.append(printNodeChildren(childNodes))
341 private def parseTargetPath(SchemaPath path) {
342 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
343 for (QName pathElement : path.pathFromRoot) {
344 val module = ctx.findModuleByNamespaceAndRevision(pathElement.namespace, pathElement.revision);
345 if (module !== null) {
346 var foundNode = module.getDataChildByName(pathElement)
347 if (foundNode === null) {
348 val child = nodes.last
349 if (child instanceof DataNodeContainer) {
350 val dataContNode = child as DataNodeContainer
351 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
354 if (foundNode !== null) {
355 nodes.add(foundNode);
360 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
363 val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
364 targetPathNodes.add(lastNodeInTargetPath)
366 return targetPathNodes
369 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<DataSchemaNode> childNodes) {
370 for(child : childNodes) {
371 if (child.QName.equals(findingNode))
375 for(child : childNodes) {
376 if (child instanceof ContainerSchemaNode) {
377 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
378 if (foundChild !== null)
380 } else if (child instanceof ListSchemaNode) {
381 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
382 if (foundChild !== null)
388 private def printNodeChildren(List<DataSchemaNode> childNodes) {
389 if (childNodes.empty) {
396 «printAugmentedNode(childNodes.get(0))»
401 private def CharSequence printAugmentedNode(DataSchemaNode child) {
403 if(child instanceof ChoiceCaseNode)
408 «IF child instanceof ContainerSchemaNode»
409 «printContainerNode(child)»
411 «IF child instanceof AnyXmlSchemaNode»
412 «printAnyXmlNode(child)»
414 «IF child instanceof LeafSchemaNode»
415 «printLeafNode(child)»
417 «IF child instanceof LeafListSchemaNode»
418 «printLeafListNode(child)»
420 «IF child instanceof ListSchemaNode»
421 «printListNode(child)»
423 «IF child instanceof ChoiceSchemaNode»
424 «printChoiceNode(child)»
429 private def printChoiceNode(ChoiceSchemaNode child) {
430 val List<ChoiceCaseNode> cases = new ArrayList(child.cases);
432 val ChoiceCaseNode aCase = cases.get(0)
433 for(caseChildNode : aCase.childNodes)
434 printAugmentedNode(caseChildNode)
438 private def printListNode(ListSchemaNode listNode) {
441 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
442 «FOR child : listNode.childNodes»
443 «printAugmentedNode(child)»
445 </«listNode.QName.localName»>
449 private def printContainerNode(ContainerSchemaNode containerNode) {
452 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
453 «FOR child : containerNode.childNodes»
454 «printAugmentedNode(child)»
456 </«containerNode.QName.localName»>
460 private def printLeafListNode(LeafListSchemaNode leafListNode) {
463 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
464 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
465 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
469 private def printAnyXmlNode(AnyXmlSchemaNode anyXmlNode) {
472 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
476 private def printLeafNode(LeafSchemaNode leafNode) {
479 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
483 private def augmentationsSummary(Module module, SchemaContext context) {
484 if (module.augmentations.empty) {
488 <h3>Augmentations Summary</h3>
494 «FOR augment : module.augmentations»
497 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
498 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
501 «augment.description»
509 def notifications(Module module) {
510 val Set<NotificationDefinition> notificationdefs = module.notifications
511 if (notificationdefs.empty) {
516 <h2>Notifications</h2>
517 «FOR notificationdef : notificationdefs»
519 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
520 «notificationdef.descAndRef»
521 «FOR childNode : notificationdef.childNodes»
522 «childNode.printSchemaNodeInfo»
528 private def notificationsSummary(Module module) {
529 if (module.notifications.empty) {
533 <h3>Notifications Summary</h3>
539 «FOR notification : module.notifications»
542 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
545 «notification.description»
553 def rpcs(Module module) {
554 if (module.rpcs.empty) {
559 <h2>RPC Definitions</h2>
560 «FOR rpc : module.rpcs»
561 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
564 «rpc.input.printSchemaNodeInfo»
565 «rpc.output.printSchemaNodeInfo»
572 private def rpcsSummary(Module module) {
573 if (module.rpcs.empty) {
577 <h3>RPCs Summary</h3>
583 «FOR rpc : module.rpcs»
586 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
597 def extensions(Module module) {
598 if (module.extensionSchemaNodes.empty) {
603 «FOR ext : module.extensionSchemaNodes»
605 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
612 private def extensionsSummary(Module module) {
613 if (module.extensionSchemaNodes.empty) {
617 <h3>Extensions Summary</h3>
623 «FOR ext : module.extensionSchemaNodes»
626 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
637 def features(Module module) {
638 if (module.features.empty) {
645 «FOR feature : module.features»
647 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
649 «feature.descAndRefLi»
657 private def featuresSummary(Module module) {
658 if (module.features.empty) {
662 <h3>Features Summary</h3>
668 «FOR feature : module.features»
671 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
674 «feature.description»
682 private def objectsSummary(Module module) {
683 if (module.childNodes.empty) {
687 <h3>Child Nodes Summary</h3>
693 «FOR childNode : module.childNodes»
696 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
699 «childNode.description»
707 def header(Module module)
709 <h1>«module.name»</h1>
711 <h2>Base Information</h2>
714 <td>«strong("prefix")»</td>
715 <td>«module.prefix»</td>
718 <td>«strong("namespace")»</td>
719 <td>«module.namespace»</td>
722 <td>«strong("revision")»</td>
723 <td>«REVISION_FORMAT.format(module.revision)»</td>
726 <td>«strong("description")»</td>
727 <td>«module.description»</td>
730 <td>«strong("yang-version")»</td>
731 <td>«module.yangVersion»</td>
734 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
735 «imp.prefix»:«imp.moduleName»«IF imp.revision !== null» «REVISION_FORMAT.format(imp.revision)»«ENDIF»;
741 def CharSequence schemaPathToId(SchemaPath path) {
743 return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«ENDFOR»'''
747 def code(String string) '''<code>«string»</code>'''
749 def process(Module module) {
750 throw new UnsupportedOperationException("TODO: auto-generated method stub")
753 def CharSequence tree(Module module) '''
754 «strong(module.name)»
755 «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())»
758 private def dispatch CharSequence tree(ChoiceSchemaNode node,YangInstanceIdentifier path) '''
759 «node.nodeName» (choice)
760 «casesTree(node.cases,path)»
763 def casesTree(Set<ChoiceCaseNode> nodes,YangInstanceIdentifier path) '''
768 «node.childNodes.treeSet(path)»
774 private def dispatch CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) '''
778 private def dispatch CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) '''
779 «val newPath = path.append(node)»
780 «localLink(newPath,node.nodeName)»
781 «node.childNodes.treeSet(newPath)»
784 private def dispatch CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
785 «val newPath = path.append(node)»
786 «localLink(newPath,node.nodeName)»
787 «node.childNodes.treeSet(newPath)»
790 def CharSequence childNodes(Module module) '''
791 «val childNodes = module.childNodes»
792 «IF !childNodes.nullOrEmpty»
795 «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())»
799 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
803 «IF node instanceof DataNodeContainer»
804 «val dataNode = node as DataNodeContainer»
806 «FOR usesNode : dataNode.uses»
811 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
812 «FOR typeDef : typeDefinitions»
813 «typeDef.restrictions»
817 «FOR grouping : dataNode.groupings»
818 «grouping.printGrouping»
822 «FOR child : dataNode.childNodes»
823 «child.printSchemaNodeInfo»
831 def String typeAnchorLink(SchemaPath path, CharSequence text) {
833 val lastElement = Iterables.getLast(path.pathFromRoot)
834 val ns = lastElement.namespace
835 if (ns == this.currentModule.namespace) {
836 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
838 return '''(«ns»)«text»'''
839 //to enable external (import) links
840 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
845 def CharSequence printBaseInfo(SchemaNode node) {
846 if(node instanceof LeafSchemaNode) {
848 «printInfo(node, "leaf")»
849 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
850 «listItem("units", node.units)»
851 «listItem("default", node.^default)»
854 } else if(node instanceof LeafListSchemaNode) {
856 «printInfo(node, "leaf-list")»
857 «listItem("type", node.type?.QName.localName)»
860 } else if(node instanceof ListSchemaNode) {
862 «printInfo(node, "list")»
863 «FOR keyDef : node.keyDefinition»
864 «listItem("key definition", keyDef.localName)»
868 } else if(node instanceof ChoiceSchemaNode) {
870 «printInfo(node, "choice")»
871 «listItem("default case", node.defaultCase)»
872 «FOR caseNode : node.cases»
873 «caseNode.printSchemaNodeInfo»
877 } else if(node instanceof ChoiceCaseNode) {
879 «printInfo(node, "case")»
882 } else if(node instanceof ContainerSchemaNode) {
884 «printInfo(node, "container")»
887 } else if(node instanceof AnyXmlSchemaNode) {
889 «printInfo(node, "anyxml")»
895 def CharSequence printInfo(SchemaNode node, String nodeType) {
897 «IF node instanceof AugmentationTarget»
900 <li id="«node.path.schemaPathToId»">
901 «nodeType»: «node.QName.localName»
906 «strong(listItem(nodeType, node.QName.localName))»
909 «listItem("description", node.description)»
910 «listItem("reference", node.reference)»
911 «IF node instanceof DataSchemaNode»
912 «listItem("when condition", node.constraints.whenCondition?.toString)»
913 «listItem("min elements", node.constraints.minElements?.toString)»
914 «listItem("max elements", node.constraints.maxElements?.toString)»
919 def CharSequence printUses(UsesNode usesNode) {
921 «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
925 «FOR sp : usesNode.refines.keySet»
926 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
930 «FOR augment : usesNode.augmentations»
931 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
937 def CharSequence printGrouping(GroupingDefinition grouping) {
939 «strong(listItem("grouping", grouping.QName.localName))»
943 def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
944 val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
945 val leafNodes = nodes.filter(LeafSchemaNode)
946 val leafListNodes = nodes.filter(LeafListSchemaNode)
947 val choices = nodes.filter(ChoiceSchemaNode)
948 val cases = nodes.filter(ChoiceCaseNode)
949 val containers = nodes.filter(ContainerSchemaNode)
950 val lists = nodes.filter(ListSchemaNode)
952 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
953 <h3>Direct children</h3>
955 «FOR childNode : anyxmlNodes»
956 «childNode.printShortInfo(level,path)»
958 «FOR childNode : leafNodes»
959 «childNode.printShortInfo(level,path)»
961 «FOR childNode : leafListNodes»
962 «childNode.printShortInfo(level,path)»
964 «FOR childNode : containers»
965 «childNode.printShortInfo(level,path)»
967 «FOR childNode : lists»
968 «childNode.printShortInfo(level,path)»
973 «IF path.pathArguments.iterator.hasNext»
975 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
978 «FOR childNode : containers»
979 «childNode.printInfo(level,path)»
981 «FOR childNode : lists»
982 «childNode.printInfo(level,path)»
984 «FOR childNode : choices»
985 «childNode.printInfo(level,path)»
987 «FOR childNode : cases»
988 «childNode.printInfo(level,path)»
993 def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
995 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
999 def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1000 <!-- Child nodes -->
1002 <!-- «node.QName.localName» -->
1003 «node.asXmlExampleTag(identifier)»
1008 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1009 «node.QName.xmlExampleTag("...")»
1012 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1013 <!-- This node could appear multiple times -->
1014 «node.QName.xmlExampleTag("...")»
1017 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1018 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1019 «node.QName.xmlExampleTag("...")»
1023 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1024 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1025 <!-- This node could appear multiple times -->
1026 «node.QName.xmlExampleTag("...")»
1030 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1035 def xmlExampleTag(QName name, CharSequence data) {
1036 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1039 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1042 def header(int level,YangInstanceIdentifier name) '''
1043 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1044 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1050 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1051 «header(level+1,node.QName)»
1054 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1055 «val newPath = path.append(node)»
1056 «header(level,newPath)»
1059 <dd>«newPath.asXmlPath»</dd>
1060 <dt>Restconf path</dt>
1061 <dd>«code(newPath.asRestconfPath)»</dd>
1063 «node.childNodes.printChildren(level,newPath)»
1066 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1067 «val newPath = path.append(node)»
1068 «header(level,newPath)»
1071 <dd>«newPath.asXmlPath»</dd>
1072 <dt>Restconf path</dt>
1073 <dd>«code(newPath.asRestconfPath)»</dd>
1075 «node.childNodes.printChildren(level,newPath)»
1078 private def dispatch CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1079 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1080 «choiceCases.printChildren(level,path)»
1083 private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, YangInstanceIdentifier path) '''
1084 «node.childNodes.printChildren(level,path)»
1089 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1090 val newPath = path.append(node);
1092 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1094 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1100 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1101 val newPath = path.append(node);
1103 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1105 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1111 def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, YangInstanceIdentifier path) {
1113 <li>«strong((node.QName.localName))» (anyxml)
1115 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1116 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1122 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1124 <li>«strong((node.QName.localName))» (leaf)
1126 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1127 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1133 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1135 <li>«strong((node.QName.localName))» (leaf-list)
1137 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1143 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1145 <a href="#«anchor»">«text»</a>
1149 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1150 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1154 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1155 return identifier.node(node.QName);
1158 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1159 val keyValues = new LinkedHashMap<QName,Object>();
1160 if(node.keyDefinition !== null) {
1161 for(definition : node.keyDefinition) {
1162 keyValues.put(definition,new Object);
1166 return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1170 def asXmlPath(YangInstanceIdentifier identifier) {
1174 def asRestconfPath(YangInstanceIdentifier identifier) {
1175 val it = new StringBuilder();
1176 append(currentModule.name)
1178 var previous = false;
1179 for(arg : identifier.pathArguments) {
1180 if(previous) append('/')
1181 append(arg.nodeType.localName);
1183 if(arg instanceof NodeIdentifierWithPredicates) {
1184 for(qname : arg.getKeyValues.keySet) {
1186 append(qname.localName)
1195 private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1196 val List<QName> path = Lists.newArrayList(schemaPath.pathFromRoot);
1197 val StringBuilder pathString = new StringBuilder()
1198 if (schemaPath.absolute) {
1199 pathString.append('/')
1202 val QName qname = path.get(0)
1203 var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1206 if (parent instanceof DataNodeContainer) {
1207 var SchemaNode node = parent.getDataChildByName(name)
1208 if (node === null && (parent instanceof Module)) {
1209 val notifications = (parent as Module).notifications;
1210 for (notification : notifications) {
1211 if (notification.QName.equals(name)) {
1216 if (node === null && (parent instanceof Module)) {
1217 val rpcs = (parent as Module).rpcs;
1219 if (rpc.QName.equals(name)) {
1225 val pathElementModule = ctx.findModuleByNamespaceAndRevision(name.namespace, name.revision)
1226 val String moduleName = pathElementModule.name
1227 pathString.append(moduleName)
1228 pathString.append(':')
1229 pathString.append(name.localName)
1230 pathString.append('/')
1231 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1232 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1233 if(caseNode !== null) {
1234 pathString.append("(case)");
1235 pathString.append(caseNode.QName.localName);
1241 return pathString.toString;
1245 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1246 «IF childNodes !== null && !childNodes.empty»
1247 «FOR child : childNodes.values»
1248 «childInfo(child, childNodes)»
1253 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1254 «val String path = nodeSchemaPathToPath(node, childNodes)»
1265 private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1266 «IF childNodes !== null && !childNodes.empty»
1268 «FOR child : childNodes»
1277 def listKeys(ListSchemaNode node) '''
1278 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1281 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1284 «listItem("Argument", ext.argument)»
1288 private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1293 /* #################### RESTRICTIONS #################### */
1294 private def restrictions(TypeDefinition<?> type) '''
1295 «type.baseType.toBaseStmt»
1300 private def dispatch toLength(TypeDefinition<?> type) {
1303 private def dispatch toLength(BinaryTypeDefinition type) '''
1304 «type.lengthConstraints.toLengthStmt»
1307 private def dispatch toLength(StringTypeDefinition type) '''
1308 «type.lengthConstraints.toLengthStmt»
1311 private def dispatch toRange(TypeDefinition<?> type) {
1314 private def dispatch toRange(DecimalTypeDefinition type) '''
1315 «type.rangeConstraints.toRangeStmt»
1318 private def dispatch toRange(IntegerTypeDefinition type) '''
1319 «type.rangeConstraints.toRangeStmt»
1322 private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1323 «type.rangeConstraints.toRangeStmt»
1326 def toLengthStmt(Collection<LengthConstraint> lengths) '''
1327 «IF lengths !== null && !lengths.empty»
1328 «listItem("Length restrictions:")»
1330 «FOR length : lengths»
1332 «IF length.min == length.max»
1335 <«length.min», «length.max»>
1343 def toRangeStmt(Collection<RangeConstraint> ranges) '''
1344 «IF ranges !== null && !ranges.empty»
1345 «listItem("Range restrictions:")»
1347 «FOR range : ranges»
1349 «IF range.min == range.max»
1352 <«range.min», «range.max»>
1360 def toBaseStmt(TypeDefinition<?> baseType) '''
1361 «IF baseType !== null»
1362 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1368 /* #################### UTILITY #################### */
1369 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1370 private def italic(CharSequence str) '''<i>«str»</i>'''
1372 def CharSequence descAndRefLi(SchemaNode node) '''
1373 «listItem("Description", node.description)»
1374 «listItem("Reference", node.reference)»
1377 def CharSequence descAndRef(SchemaNode node) '''
1379 «IF node.reference !== null»
1380 Reference «node.reference»
1384 private def listItem(String value) '''
1385 «IF value !== null && !value.empty»
1392 private def listItem(String name, String value) '''
1393 «IF value !== null && !value.empty»
1400 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1401 if (node instanceof ChoiceSchemaNode || node instanceof ChoiceCaseNode) {
1405 val path = node.path.pathFromRoot
1406 val absolute = node.path.absolute;
1407 var StringBuilder result = new StringBuilder
1411 if (path !== null && !path.empty) {
1412 val List<QName> actual = new ArrayList()
1414 for (pathElement : path) {
1415 actual.add(pathElement)
1416 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1417 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1418 result.append(pathElement.localName)
1419 if (i != path.size - 1) {
1426 return result.toString
1429 private def dispatch addedByInfo(SchemaNode node) '''
1432 private def dispatch addedByInfo(DataSchemaNode node) '''
1433 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1436 private def dispatch isAddedBy(SchemaNode node) {
1440 private def dispatch isAddedBy(DataSchemaNode node) {
1441 if (node.augmenting || node.addedByUses) {
1448 private def dispatch nodeName(SchemaNode node) '''
1450 «italic(node.QName.localName)»«node.addedByInfo»
1452 «node.QName.localName»«node.addedByInfo»
1456 private def dispatch nodeName(ContainerSchemaNode node) '''
1458 «strong(italic(node.QName.localName))»«node.addedByInfo»
1460 «strong(node.QName.localName)»«node.addedByInfo»
1464 private def dispatch nodeName(ListSchemaNode node) '''
1466 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1468 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»