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 java.io.BufferedWriter
12 import java.io.IOException
13 import java.io.OutputStreamWriter
14 import java.nio.charset.StandardCharsets
15 import java.nio.file.Files
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
23 import java.util.Optional
25 import org.gaul.modernizer_maven_annotations.SuppressModernizer
26 import org.opendaylight.yangtools.yang.common.QName
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
29 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode
30 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
31 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
36 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
37 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware
38 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
43 import org.opendaylight.yangtools.yang.model.api.Module
44 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
45 import org.opendaylight.yangtools.yang.model.api.SchemaNode
46 import org.opendaylight.yangtools.yang.model.api.SchemaPath
47 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.UsesNode
49 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier
50 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute
51 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
52 import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition
53 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
54 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition
55 import org.slf4j.Logger
56 import org.slf4j.LoggerFactory
57 import org.sonatype.plexus.build.incremental.BuildContext
62 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
64 val Map<String, String> imports = new HashMap();
65 var Module currentModule;
66 var EffectiveModelContext ctx;
69 StringBuilder augmentChildNodesAsString
71 DataSchemaNode lastNodeInTargetPath = null
73 def generate(BuildContext buildContext, EffectiveModelContext context, File targetPath, Set<Module> modulesToGen)
76 Files.createDirectories(path.getParentFile().toPath())
78 for (module : modulesToGen) {
79 add(generateDocumentation(buildContext, module, context));
84 def generateDocumentation(BuildContext buildContext, Module module, EffectiveModelContext ctx) {
85 val destination = new File(path, '''«module.name».html''')
87 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
88 var OutputStreamWriter fw
91 fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
92 bw = new BufferedWriter(fw)
93 currentModule = module;
94 bw.append(generate(module, ctx));
95 } catch (IOException e) {
96 LOG.error("Failed to emit file {}", destination, e);
108 def generate(Module module, EffectiveModelContext ctx) '''
112 <title>«module.name»</title>
120 def body(Module module, EffectiveModelContext ctx) '''
123 «typeDefinitionsSummary(module)»
124 «identitiesSummary(module)»
125 «groupingsSummary(module)»
126 «augmentationsSummary(module, ctx)»
127 «objectsSummary(module)»
128 «notificationsSummary(module)»
129 «rpcsSummary(module)»
130 «extensionsSummary(module)»
131 «featuresSummary(module)»
133 «typeDefinitions(module)»
143 «notifications(module)»
145 «augmentations(module, ctx)»
156 private def typeDefinitionsSummary(Module module) {
157 val Collection<? extends TypeDefinition<?>> typedefs = module.typeDefinitions
158 if (typedefs.empty) {
163 <h3>Type Definitions Summary</h3>
169 «FOR typedef : typedefs»
172 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
175 «typedef.description»
184 def typeDefinitions(Module module) {
185 val Collection<? extends TypeDefinition<?>> typedefs = module.typeDefinitions
186 if (typedefs.empty) {
190 <h2>Type Definitions</h2>
192 «FOR typedef : typedefs»
194 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
196 «typedef.descAndRefLi»
197 «typedef.restrictions»
205 private def identities(Module module) {
206 if (module.identities.empty) {
212 «FOR identity : module.identities»
214 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
216 «identity.descAndRefLi»
217 «IF !identity.baseIdentities.isEmpty»
218 «listItem("base", identity.baseIdentities.get(0).QName.localName)»
227 private def identitiesSummary(Module module) {
228 if (module.identities.empty) {
232 <h3>Identities Summary</h3>
238 «FOR identity : module.identities»
241 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
244 «identity.description»
252 private def groupings(Module module) {
253 if (module.groupings.empty) {
259 «FOR grouping : module.groupings»
261 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
263 «grouping.descAndRefLi»
264 «FOR childNode : grouping.childNodes»
265 «childNode.printSchemaNodeInfo»
274 private def groupingsSummary(Module module) {
275 if (module.groupings.empty) {
279 <h3>Groupings Summary</h3>
285 «FOR grouping : module.groupings»
288 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
291 «grouping.description»
299 def dataStore(Module module) {
300 if (module.childNodes.empty) {
304 <h2>Datastore Structure</h2>
309 def augmentations(Module module, EffectiveModelContext context) {
310 if (module.augmentations.empty) {
314 <h2>Augmentations</h2>
317 «FOR augment : module.augmentations»
319 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
320 Target [«typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
321 «augment.description»
322 Status: «strong(String.valueOf(augment.status))»
323 «IF augment.reference !== null»
324 Reference «augment.reference»
326 «IF augment.whenCondition !== null»
327 When «augment.whenCondition.toString»
329 «FOR childNode : augment.childNodes»
330 «childNode.printSchemaNodeInfo»
334 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
335 «printNodeChildren(parseTargetPath(augment.targetPath))»
342 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
343 augmentChildNodesAsString = new StringBuilder();
344 augmentChildNodesAsString.append(printNodeChildren(childNodes))
348 private def parseTargetPath(SchemaNodeIdentifier path) {
349 val nodes = new ArrayList<DataSchemaNode>();
350 for (QName pathElement : path.nodeIdentifiers) {
351 val module = ctx.findModule(pathElement.module)
352 if (module.isPresent) {
353 var foundNode = module.get.dataChildByName(pathElement)
354 if (foundNode === null) {
355 val child = nodes.last
356 if (child instanceof DataNodeContainer) {
357 val dataContNode = child as DataNodeContainer
358 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
361 if (foundNode !== null) {
362 nodes.add(foundNode);
367 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
370 val targetPathNodes = new ArrayList<DataSchemaNode>();
371 targetPathNodes.add(lastNodeInTargetPath)
373 return targetPathNodes
376 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<? extends DataSchemaNode> childNodes) {
377 for (child : childNodes) {
378 if (child.QName.equals(findingNode))
382 for(child : childNodes) {
383 if (child instanceof ContainerSchemaNode) {
384 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
385 if (foundChild !== null)
387 } else if (child instanceof ListSchemaNode) {
388 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
389 if (foundChild !== null)
395 private def printNodeChildren(List<DataSchemaNode> childNodes) {
396 if (childNodes.empty) {
403 «printAugmentedNode(childNodes.get(0))»
408 private def CharSequence printAugmentedNode(DataSchemaNode child) {
410 if(child instanceof CaseSchemaNode)
415 «IF child instanceof ContainerSchemaNode»
416 «printContainerNode(child)»
418 «IF child instanceof AnyxmlSchemaNode»
419 «printAnyXmlNode(child)»
421 «IF child instanceof LeafSchemaNode»
422 «printLeafNode(child)»
424 «IF child instanceof LeafListSchemaNode»
425 «printLeafListNode(child)»
427 «IF child instanceof ListSchemaNode»
428 «printListNode(child)»
430 «IF child instanceof ChoiceSchemaNode»
431 «printChoiceNode(child)»
436 private def printChoiceNode(ChoiceSchemaNode child) {
437 val cases = new ArrayList(child.cases)
439 val CaseSchemaNode aCase = cases.get(0)
440 for (caseChildNode : aCase.childNodes)
441 printAugmentedNode(caseChildNode)
445 private def printListNode(ListSchemaNode listNode) {
448 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
449 «FOR child : listNode.childNodes»
450 «printAugmentedNode(child)»
452 </«listNode.QName.localName»>
456 private def printContainerNode(ContainerSchemaNode containerNode) {
459 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
460 «FOR child : containerNode.childNodes»
461 «printAugmentedNode(child)»
463 </«containerNode.QName.localName»>
467 private def printLeafListNode(LeafListSchemaNode leafListNode) {
470 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
471 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
472 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
476 private def printAnyXmlNode(AnyxmlSchemaNode anyXmlNode) {
479 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
483 private def printLeafNode(LeafSchemaNode leafNode) {
486 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
490 private def augmentationsSummary(Module module, EffectiveModelContext context) {
491 if (module.augmentations.empty) {
495 <h3>Augmentations Summary</h3>
501 «FOR augment : module.augmentations»
504 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
505 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
508 «augment.description»
516 def notifications(Module module) {
517 val Collection<? extends NotificationDefinition> notificationdefs = module.notifications
518 if (notificationdefs.empty) {
523 <h2>Notifications</h2>
524 «FOR notificationdef : notificationdefs»
526 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
527 «notificationdef.descAndRef»
528 «FOR childNode : notificationdef.childNodes»
529 «childNode.printSchemaNodeInfo»
535 private def notificationsSummary(Module module) {
536 if (module.notifications.empty) {
540 <h3>Notifications Summary</h3>
546 «FOR notification : module.notifications»
549 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
552 «notification.description»
560 def rpcs(Module module) {
561 if (module.rpcs.empty) {
566 <h2>RPC Definitions</h2>
567 «FOR rpc : module.rpcs»
568 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
571 «rpc.input.printSchemaNodeInfo»
572 «rpc.output.printSchemaNodeInfo»
579 private def rpcsSummary(Module module) {
580 if (module.rpcs.empty) {
584 <h3>RPCs Summary</h3>
590 «FOR rpc : module.rpcs»
593 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
604 def extensions(Module module) {
605 if (module.extensionSchemaNodes.empty) {
610 «FOR ext : module.extensionSchemaNodes»
612 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
619 private def extensionsSummary(Module module) {
620 if (module.extensionSchemaNodes.empty) {
624 <h3>Extensions Summary</h3>
630 «FOR ext : module.extensionSchemaNodes»
633 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
644 def features(Module module) {
645 if (module.features.empty) {
652 «FOR feature : module.features»
654 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
656 «feature.descAndRefLi»
664 private def featuresSummary(Module module) {
665 if (module.features.empty) {
669 <h3>Features Summary</h3>
675 «FOR feature : module.features»
678 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
681 «feature.description»
689 private def objectsSummary(Module module) {
690 if (module.childNodes.empty) {
694 <h3>Child Nodes Summary</h3>
700 «FOR childNode : module.childNodes»
703 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
706 «childNode.description»
714 def header(Module module)
716 <h1>«module.name»</h1>
718 <h2>Base Information</h2>
721 <td>«strong("prefix")»</td>
722 <td>«module.prefix»</td>
725 <td>«strong("namespace")»</td>
726 <td>«module.namespace»</td>
729 «IF module.revision.isPresent»
730 <td>«strong("revision")»</td>
731 <td>«module.revision.get.toString»</td>
735 <td>«strong("description")»</td>
736 <td>«module.description»</td>
739 <td>«strong("yang-version")»</td>
740 <td>«module.yangVersion»</td>
743 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
744 «imp.prefix»:«imp.moduleName»«IF imp.revision.isPresent» «imp.revision.get.toString»«ENDIF»;
750 def CharSequence schemaPathToId(SchemaPath path) {
752 return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«ENDFOR»'''
756 def code(String string) '''<code>«string»</code>'''
758 def process(Module module) {
759 throw new UnsupportedOperationException("TODO: auto-generated method stub")
762 def CharSequence tree(Module module) '''
763 «strong(module.name)»
764 «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())»
767 private def CharSequence tree(ChoiceSchemaNode node, YangInstanceIdentifier path) '''
768 «node.nodeName» (choice)
769 «casesTree(node.cases, path)»
772 def casesTree(Collection<? extends CaseSchemaNode> nodes, YangInstanceIdentifier path) '''
777 «node.childNodes.treeSet(path)»
783 private def CharSequence tree(DataSchemaNode node, YangInstanceIdentifier path) {
784 if (node instanceof ChoiceSchemaNode) {
785 return tree(node, path)
786 } else if (node instanceof ListSchemaNode) {
787 return tree(node, path)
788 } else if (node instanceof ContainerSchemaNode) {
789 return tree(node, path)
794 private def CharSequence tree(ListSchemaNode node, YangInstanceIdentifier path) '''
795 «val newPath = path.append(node)»
796 «localLink(newPath,node.nodeName)»
797 «node.childNodes.treeSet(newPath)»
800 private def CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
801 «val newPath = path.append(node)»
802 «localLink(newPath,node.nodeName)»
803 «node.childNodes.treeSet(newPath)»
806 def CharSequence childNodes(Module module) '''
807 «val childNodes = module.childNodes»
808 «IF !childNodes.nullOrEmpty»
811 «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())»
815 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
819 «IF node instanceof DataNodeContainer»
820 «val dataNode = node as DataNodeContainer»
822 «FOR usesNode : dataNode.uses»
827 «FOR typeDef : dataNode.typeDefinitions»
828 «typeDef.restrictions»
832 «FOR grouping : dataNode.groupings»
833 «grouping.printGrouping»
837 «FOR child : dataNode.childNodes»
838 «child.printSchemaNodeInfo»
846 def String typeAnchorLink(SchemaPath path, CharSequence text) {
848 val lastElement = path.lastComponent
849 val ns = lastElement.namespace
850 if (ns == this.currentModule.namespace) {
851 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
853 return '''(«ns»)«text»'''
854 //to enable external (import) links
855 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
860 def CharSequence printBaseInfo(SchemaNode node) {
861 if(node instanceof LeafSchemaNode) {
863 «printInfo(node, "leaf")»
864 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
865 «listItem("units", node.type.units.orElse(null))»
866 «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))»
869 } else if(node instanceof LeafListSchemaNode) {
871 «printInfo(node, "leaf-list")»
872 «IF node.type !== null»
873 «listItem("type", node.type.QName.localName)»
877 } else if(node instanceof ListSchemaNode) {
879 «printInfo(node, "list")»
880 «FOR keyDef : node.keyDefinition»
881 «listItem("key definition", keyDef.localName)»
885 } else if(node instanceof ChoiceSchemaNode) {
887 «printInfo(node, "choice")»
888 «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))»
889 «FOR caseNode : node.cases»
890 «caseNode.printSchemaNodeInfo»
894 } else if(node instanceof CaseSchemaNode) {
896 «printInfo(node, "case")»
899 } else if(node instanceof ContainerSchemaNode) {
901 «printInfo(node, "container")»
904 } else if(node instanceof AnyxmlSchemaNode) {
906 «printInfo(node, "anyxml")»
912 def CharSequence printInfo(SchemaNode node, String nodeType) {
914 «IF node instanceof AugmentationTarget»
917 <li id="«node.path.schemaPathToId»">
918 «nodeType»: «node.QName.localName»
923 «strong(listItem(nodeType, node.QName.localName))»
926 «listItem("description", node.description.orElse(null))»
927 «listItem("reference", node.reference.orElse(null))»
928 «IF node instanceof DataSchemaNode»
929 «IF node.whenCondition.present»
930 «listItem("when condition", node.whenCondition.get.toString)»
933 «IF node instanceof ElementCountConstraintAware»
934 «IF node.elementCountConstraint.present»
935 «val constraint = node.elementCountConstraint.get»
936 «listItem("min elements", constraint.minElements?.toString)»
937 «listItem("max elements", constraint.maxElements?.toString)»
943 def CharSequence printUses(UsesNode usesNode) {
945 «strong(listItem("uses", typeAnchorLink(usesNode.sourceGrouping.path, usesNode.sourceGrouping.path.pathTowardsRoot.iterator.next.localName)))»
949 «FOR sp : usesNode.refines.keySet»
950 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
954 «FOR augment : usesNode.augmentations»
955 «typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
961 def CharSequence printGrouping(GroupingDefinition grouping) {
963 «strong(listItem("grouping", grouping.QName.localName))»
967 def CharSequence printChildren(Iterable<? extends DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
968 val anyxmlNodes = nodes.filter(AnyxmlSchemaNode)
969 val leafNodes = nodes.filter(LeafSchemaNode)
970 val leafListNodes = nodes.filter(LeafListSchemaNode)
971 val choices = nodes.filter(ChoiceSchemaNode)
972 val cases = nodes.filter(CaseSchemaNode)
973 val containers = nodes.filter(ContainerSchemaNode)
974 val lists = nodes.filter(ListSchemaNode)
976 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
977 <h3>Direct children</h3>
979 «FOR childNode : anyxmlNodes»
980 «childNode.printShortInfo(level,path)»
982 «FOR childNode : leafNodes»
983 «childNode.printShortInfo(level,path)»
985 «FOR childNode : leafListNodes»
986 «childNode.printShortInfo(level,path)»
988 «FOR childNode : containers»
989 «childNode.printShortInfo(level,path)»
991 «FOR childNode : lists»
992 «childNode.printShortInfo(level,path)»
997 «IF path.pathArguments.iterator.hasNext»
999 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
1002 «FOR childNode : containers»
1003 «childNode.printInfo(level,path)»
1005 «FOR childNode : lists»
1006 «childNode.printInfo(level,path)»
1008 «FOR childNode : choices»
1009 «childNode.printInfo(level,path)»
1011 «FOR childNode : cases»
1012 «childNode.printInfo(level,path)»
1017 def CharSequence xmlExample(Iterable<? extends DataSchemaNode> nodes, QName name, YangInstanceIdentifier path) '''
1019 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1023 def CharSequence xmplExampleTags(Iterable<? extends DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1024 <!-- Child nodes -->
1026 <!-- «node.QName.localName» -->
1027 «node.asXmlExampleTag(identifier)»
1032 private def CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) {
1033 if (node instanceof LeafSchemaNode) {
1034 return '''«node.QName.xmlExampleTag("...")»'''
1036 if (node instanceof LeafListSchemaNode) {
1038 <!-- This node could appear multiple times -->
1039 «node.QName.xmlExampleTag("...")»
1042 if (node instanceof ContainerSchemaNode) {
1044 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1045 «node.QName.xmlExampleTag("...")»
1048 if (node instanceof ListSchemaNode) {
1050 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1051 <!-- This node could appear multiple times -->
1052 «node.QName.xmlExampleTag("...")»
1055 return "<!-- noop -->"
1058 def xmlExampleTag(QName name, CharSequence data) {
1059 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1062 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1065 def header(int level,YangInstanceIdentifier name) '''
1066 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1067 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1071 private def CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1072 «val newPath = path.append(node)»
1073 «header(level,newPath)»
1076 <dd>«newPath.asXmlPath»</dd>
1077 <dt>Restconf path</dt>
1078 <dd>«code(newPath.asRestconfPath)»</dd>
1080 «node.childNodes.printChildren(level,newPath)»
1083 private def CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1084 «val newPath = path.append(node)»
1085 «header(level,newPath)»
1088 <dd>«newPath.asXmlPath»</dd>
1089 <dt>Restconf path</dt>
1090 <dd>«code(newPath.asRestconfPath)»</dd>
1092 «node.childNodes.printChildren(level,newPath)»
1095 private def CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1096 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1097 «choiceCases.printChildren(level, path)»
1100 private def CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) '''
1101 «node.childNodes.printChildren(level, path)»
1106 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1107 val newPath = path.append(node);
1109 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1111 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1117 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1118 val newPath = path.append(node);
1120 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1122 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1128 def CharSequence printShortInfo(AnyxmlSchemaNode node, int level, YangInstanceIdentifier path) {
1130 <li>«strong((node.QName.localName))» (anyxml)
1132 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1133 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1139 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1141 <li>«strong((node.QName.localName))» (leaf)
1143 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1144 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1150 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1152 <li>«strong((node.QName.localName))» (leaf-list)
1154 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1160 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1162 <a href="#«anchor»">«text»</a>
1166 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1167 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1171 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1172 return identifier.node(node.QName);
1175 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1176 val keyValues = new LinkedHashMap<QName,Object>();
1177 if(node.keyDefinition !== null) {
1178 for(definition : node.keyDefinition) {
1179 keyValues.put(definition,new Object);
1183 return identifier.node(NodeIdentifierWithPredicates.of(node.QName, keyValues));
1187 def asXmlPath(YangInstanceIdentifier identifier) {
1191 def asRestconfPath(YangInstanceIdentifier identifier) {
1192 val it = new StringBuilder();
1193 append(currentModule.name)
1195 var previous = false;
1196 for(arg : identifier.pathArguments) {
1197 if(previous) append('/')
1198 append(arg.nodeType.localName);
1200 if(arg instanceof NodeIdentifierWithPredicates) {
1201 for(qname : arg.keySet) {
1203 append(qname.localName)
1212 private def String schemaPathToString(Module module, SchemaNodeIdentifier schemaPath, EffectiveModelContext ctx,
1213 DataNodeContainer dataNode) {
1214 val path = schemaPath.nodeIdentifiers
1215 val StringBuilder pathString = new StringBuilder()
1216 if (schemaPath instanceof Absolute) {
1217 pathString.append('/')
1220 val QName qname = path.get(0)
1221 var Object parent = ctx.findModule(qname.module).orElse(null)
1224 if (parent instanceof DataNodeContainer) {
1225 var SchemaNode node = parent.dataChildByName(name)
1226 if (node === null && (parent instanceof Module)) {
1227 val notifications = (parent as Module).notifications;
1228 for (notification : notifications) {
1229 if (notification.QName.equals(name)) {
1234 if (node === null && (parent instanceof Module)) {
1235 val rpcs = (parent as Module).rpcs;
1237 if (rpc.QName.equals(name)) {
1243 val pathElementModule = ctx.findModule(name.module).get
1244 val String moduleName = pathElementModule.name
1245 pathString.append(moduleName)
1246 pathString.append(':')
1247 pathString.append(name.localName)
1248 pathString.append('/')
1249 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1250 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode];
1251 if(caseNode !== null) {
1252 pathString.append("(case)");
1253 pathString.append(caseNode.QName.localName);
1259 return pathString.toString;
1263 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1264 «IF childNodes !== null && !childNodes.empty»
1265 «FOR child : childNodes.values»
1266 «childInfo(child, childNodes)»
1271 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1272 «val String path = nodeSchemaPathToPath(node, childNodes)»
1283 private def CharSequence treeSet(Collection<? extends DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1284 «IF childNodes !== null && !childNodes.empty»
1286 «FOR child : childNodes»
1295 def listKeys(ListSchemaNode node) '''
1296 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1299 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1302 «listItem("Argument", ext.argument)»
1307 /* #################### RESTRICTIONS #################### */
1308 private def restrictions(TypeDefinition<?> type) '''
1309 «type.baseType.toBaseStmt»
1314 private def toLength(TypeDefinition<?> type) '''
1315 «IF type instanceof LengthRestrictedTypeDefinition»
1316 «type.lengthConstraint.toLengthStmt»
1320 private def toRange(TypeDefinition<?> type) '''
1321 «IF type instanceof RangeRestrictedTypeDefinition»
1322 «type.rangeConstraint.toRangeStmt»
1326 def toLengthStmt(Optional<LengthConstraint> lengths) '''
1327 «IF lengths.isPresent»
1328 «listItem("Length restrictions:")»
1330 «FOR length : lengths.get.allowedRanges.asRanges»
1332 «IF length.lowerEndpoint == length.upperEndpoint»
1333 «length.lowerEndpoint»
1335 <«length.lowerEndpoint», «length.upperEndpoint»>
1343 def toRangeStmt(Optional<? extends RangeConstraint<?>> constraint) '''
1344 «IF constraint.present»
1345 «listItem("Range restrictions:")»
1347 «FOR range : constraint.get.allowedRanges.asRanges»
1349 «IF range.lowerEndpoint == range.upperEndpoint»
1350 «range.lowerEndpoint»
1352 <«range.lowerEndpoint», «range.upperEndpoint»>
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.orElse(null))»
1374 «listItem("Reference", node.reference.orElse(null))»
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 CaseSchemaNode) {
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 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 CaseSchemaNode)) {
1418 result.append(pathElement.localName)
1419 if (i != path.size - 1) {
1426 return result.toString
1429 private def addedByInfo(SchemaNode node) {
1430 if (node instanceof DataSchemaNode) {
1431 return addedByInfo(node)
1436 private def addedByInfo(DataSchemaNode node) '''
1437 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1440 private def isAddedBy(SchemaNode node) {
1441 if (node instanceof DataSchemaNode) {
1442 return node.augmenting || node.addedByUses
1447 private def nodeName(SchemaNode node) {
1448 if (node instanceof ContainerSchemaNode) {
1449 return nodeName(node);
1450 } else if (node instanceof ListSchemaNode) {
1451 return nodeName(node);
1453 val addedByInfo = node.addedByInfo
1454 if (node.isAddedBy) {
1455 return '''«italic(node.QName.localName)»«addedByInfo»'''
1457 return '''«node.QName.localName»«addedByInfo»'''
1460 private def nodeName(ContainerSchemaNode node) '''
1462 «strong(italic(node.QName.localName))»«node.addedByInfo»
1464 «strong(node.QName.localName)»«node.addedByInfo»
1468 private def nodeName(ListSchemaNode node) '''
1470 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1472 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»