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 com.google.common.collect.Iterables
11 import java.io.BufferedWriter
13 import java.io.IOException
14 import java.io.OutputStreamWriter
15 import java.text.SimpleDateFormat
16 import java.util.ArrayList
17 import java.util.Collection
18 import java.util.HashMap
19 import java.util.HashSet
20 import java.util.LinkedHashMap
24 import org.opendaylight.yangtools.yang.common.QName
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
27 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
28 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
29 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
30 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
35 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
36 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
39 import org.opendaylight.yangtools.yang.model.api.Module
40 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext
42 import org.opendaylight.yangtools.yang.model.api.SchemaNode
43 import org.opendaylight.yangtools.yang.model.api.SchemaPath
44 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
45 import org.opendaylight.yangtools.yang.model.api.UsesNode
46 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
47 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
49 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
50 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
51 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
52 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
53 import org.opendaylight.yangtools.yang.model.util.ExtendedType
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
62 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
63 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
64 static val BuildContext CTX = new DefaultBuildContext();
65 var Module currentModule;
66 val Map<String, String> imports = new HashMap();
67 var SchemaContext ctx;
69 StringBuilder augmentChildNodesAsString
71 DataSchemaNode lastNodeInTargetPath = null
73 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
77 for (module : modulesToGen) {
78 add(generateDocumentation(module, context));
83 def generateDocumentation(Module module, SchemaContext ctx) {
84 val destination = new File(path, '''«module.name».html''')
86 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
88 val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
89 val bw = new BufferedWriter(fw)
90 currentModule = module;
91 bw.append(generate(module, ctx));
94 } catch (IOException e) {
95 LOG.error(e.getMessage());
100 def generate(Module module, SchemaContext ctx) '''
104 <title>«module.name»</title>
112 def body(Module module, SchemaContext ctx) '''
115 «typeDefinitionsSummary(module)»
116 «identitiesSummary(module)»
117 «groupingsSummary(module)»
118 «augmentationsSummary(module, ctx)»
119 «objectsSummary(module)»
120 «notificationsSummary(module)»
121 «rpcsSummary(module)»
122 «extensionsSummary(module)»
123 «featuresSummary(module)»
125 «typeDefinitions(module)»
135 «notifications(module)»
137 «augmentations(module, ctx)»
148 private def typeDefinitionsSummary(Module module) {
149 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
150 if (typedefs.empty) {
155 <h3>Type Definitions Summary</h3>
161 «FOR typedef : typedefs»
164 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
167 «typedef.description»
176 def typeDefinitions(Module module) {
177 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
178 if (typedefs.empty) {
182 <h2>Type Definitions</h2>
184 «FOR typedef : typedefs»
186 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
188 «typedef.descAndRefLi»
189 «typedef.restrictions»
197 private def identities(Module module) {
198 if (module.identities.empty) {
204 «FOR identity : module.identities»
206 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
208 «identity.descAndRefLi»
209 «IF identity.baseIdentity !== null»
210 «listItem("base", identity.baseIdentity.QName.localName)»
219 private def identitiesSummary(Module module) {
220 if (module.identities.empty) {
224 <h3>Identities Summary</h3>
230 «FOR identity : module.identities»
233 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
236 «identity.description»
244 private def groupings(Module module) {
245 if (module.groupings.empty) {
251 «FOR grouping : module.groupings»
253 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
255 «grouping.descAndRefLi»
256 «FOR childNode : grouping.childNodes»
257 «childNode.printSchemaNodeInfo»
266 private def groupingsSummary(Module module) {
267 if (module.groupings.empty) {
271 <h3>Groupings Summary</h3>
277 «FOR grouping : module.groupings»
280 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
283 «grouping.description»
291 def dataStore(Module module) {
292 if (module.childNodes.empty) {
296 <h2>Datastore Structure</h2>
301 def augmentations(Module module, SchemaContext context) {
302 if (module.augmentations.empty) {
306 <h2>Augmentations</h2>
309 «FOR augment : module.augmentations»
311 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
312 Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
313 «augment.description»
314 Status: «strong(String.valueOf(augment.status))»
315 «IF augment.reference !== null»
316 Reference «augment.reference»
318 «IF augment.whenCondition !== null»
319 When «augment.whenCondition.toString»
321 «FOR childNode : augment.childNodes»
322 «childNode.printSchemaNodeInfo»
326 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
327 «printNodeChildren(parseTargetPath(augment.targetPath))»
334 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
335 augmentChildNodesAsString = new StringBuilder();
336 augmentChildNodesAsString.append(printNodeChildren(childNodes))
340 private def parseTargetPath(SchemaPath path) {
341 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
342 for (QName pathElement : path.pathFromRoot) {
343 val module = ctx.findModuleByNamespaceAndRevision(pathElement.namespace, pathElement.revision);
344 if (module !== null) {
345 var foundNode = module.getDataChildByName(pathElement)
346 if(foundNode == null) {
347 val child = nodes.last
348 if (child instanceof DataNodeContainer) {
349 val dataContNode = child as DataNodeContainer
350 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
353 if(foundNode != null) {
354 nodes.add(foundNode);
359 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
362 val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
363 targetPathNodes.add(lastNodeInTargetPath)
365 return targetPathNodes
368 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<DataSchemaNode> childNodes) {
369 for(child : childNodes) {
370 if (child.QName.equals(findingNode))
374 for(child : childNodes) {
375 if(child instanceof ContainerSchemaNode) {
376 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
377 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.path 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 = schemaPath.path
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 toLength(ExtendedType type) '''
1312 «type.lengthConstraints.toLengthStmt»
1315 private def dispatch toRange(TypeDefinition<?> type) {
1318 private def dispatch toRange(DecimalTypeDefinition type) '''
1319 «type.rangeConstraints.toRangeStmt»
1322 private def dispatch toRange(IntegerTypeDefinition type) '''
1323 «type.rangeConstraints.toRangeStmt»
1326 private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1327 «type.rangeConstraints.toRangeStmt»
1330 private def dispatch toRange(ExtendedType type) '''
1331 «type.rangeConstraints.toRangeStmt»
1334 def toLengthStmt(Collection<LengthConstraint> lengths) '''
1335 «IF lengths != null && !lengths.empty»
1336 «listItem("Length restrictions:")»
1338 «FOR length : lengths»
1340 «IF length.min == length.max»
1343 <«length.min», «length.max»>
1351 def toRangeStmt(Collection<RangeConstraint> ranges) '''
1352 «IF ranges != null && !ranges.empty»
1353 «listItem("Range restrictions:")»
1355 «FOR range : ranges»
1357 «IF range.min == range.max»
1360 <«range.min», «range.max»>
1368 def toBaseStmt(TypeDefinition<?> baseType) '''
1369 «IF baseType != null»
1370 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1376 /* #################### UTILITY #################### */
1377 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1378 private def italic(CharSequence str) '''<i>«str»</i>'''
1380 def CharSequence descAndRefLi(SchemaNode node) '''
1381 «listItem("Description", node.description)»
1382 «listItem("Reference", node.reference)»
1385 def CharSequence descAndRef(SchemaNode node) '''
1387 «IF node.reference !== null»
1388 Reference «node.reference»
1392 private def listItem(String value) '''
1393 «IF value !== null && !value.empty»
1400 private def listItem(String name, String value) '''
1401 «IF value !== null && !value.empty»
1408 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1409 if (node instanceof ChoiceSchemaNode || node instanceof ChoiceCaseNode) {
1413 val path = node.path.path
1414 val absolute = node.path.absolute;
1415 var StringBuilder result = new StringBuilder
1419 if (path !== null && !path.empty) {
1420 val List<QName> actual = new ArrayList()
1422 for (pathElement : path) {
1423 actual.add(pathElement)
1424 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1425 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1426 result.append(pathElement.localName)
1427 if (i != path.size - 1) {
1434 return result.toString
1437 private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1438 for (node : source) {
1439 destination.put(node.path, node)
1440 if (node instanceof DataNodeContainer) {
1441 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1443 if (node instanceof ChoiceSchemaNode) {
1444 val List<DataSchemaNode> choiceCases = new ArrayList()
1445 for (caseNode : node.cases) {
1446 choiceCases.add(caseNode)
1448 collectChildNodes(choiceCases, destination)
1453 private def dispatch addedByInfo(SchemaNode node) '''
1456 private def dispatch addedByInfo(DataSchemaNode node) '''
1457 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1460 private def dispatch isAddedBy(SchemaNode node) {
1464 private def dispatch isAddedBy(DataSchemaNode node) {
1465 if (node.augmenting || node.addedByUses) {
1472 private def dispatch nodeName(SchemaNode node) '''
1474 «italic(node.QName.localName)»«node.addedByInfo»
1476 «node.QName.localName»«node.addedByInfo»
1480 private def dispatch nodeName(ContainerSchemaNode node) '''
1482 «strong(italic(node.QName.localName))»«node.addedByInfo»
1484 «strong(node.QName.localName)»«node.addedByInfo»
1488 private def dispatch nodeName(ListSchemaNode node) '''
1490 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1492 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»