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.util.ArrayList
16 import java.util.Collection
17 import java.util.HashMap
18 import java.util.HashSet
19 import java.util.LinkedHashMap
22 import java.util.Optional
24 import org.gaul.modernizer_maven_annotations.SuppressModernizer
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.CaseSchemaNode
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.EffectiveModelContext
36 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware
37 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
38 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
39 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
40 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
41 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
42 import org.opendaylight.yangtools.yang.model.api.Module
43 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
44 import org.opendaylight.yangtools.yang.model.api.SchemaNode
45 import org.opendaylight.yangtools.yang.model.api.SchemaPath
46 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
47 import org.opendaylight.yangtools.yang.model.api.UsesNode
48 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier
49 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute
50 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
51 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
52 import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition
53 import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition
54 import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition
55 import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition
56 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
57 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
58 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
59 import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition
60 import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition
61 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition
62 import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition
63 import org.slf4j.Logger
64 import org.slf4j.LoggerFactory
65 import org.sonatype.plexus.build.incremental.BuildContext
70 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
72 val Map<String, String> imports = new HashMap();
73 var Module currentModule;
74 var EffectiveModelContext ctx;
77 StringBuilder augmentChildNodesAsString
79 DataSchemaNode lastNodeInTargetPath = null
81 def generate(BuildContext buildContext, EffectiveModelContext context, File targetPath, Set<Module> modulesToGen)
86 for (module : modulesToGen) {
87 add(generateDocumentation(buildContext, module, context));
92 def generateDocumentation(BuildContext buildContext, Module module, EffectiveModelContext ctx) {
93 val destination = new File(path, '''«module.name».html''')
95 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
97 val fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
98 val bw = new BufferedWriter(fw)
99 currentModule = module;
100 bw.append(generate(module, ctx));
103 } catch (IOException e) {
104 LOG.error("Failed to emit file {}", destination, e);
109 def generate(Module module, EffectiveModelContext ctx) '''
113 <title>«module.name»</title>
121 def body(Module module, EffectiveModelContext ctx) '''
124 «typeDefinitionsSummary(module)»
125 «identitiesSummary(module)»
126 «groupingsSummary(module)»
127 «augmentationsSummary(module, ctx)»
128 «objectsSummary(module)»
129 «notificationsSummary(module)»
130 «rpcsSummary(module)»
131 «extensionsSummary(module)»
132 «featuresSummary(module)»
134 «typeDefinitions(module)»
144 «notifications(module)»
146 «augmentations(module, ctx)»
157 private def typeDefinitionsSummary(Module module) {
158 val Collection<? extends TypeDefinition<?>> typedefs = module.typeDefinitions
159 if (typedefs.empty) {
164 <h3>Type Definitions Summary</h3>
170 «FOR typedef : typedefs»
173 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
176 «typedef.description»
185 def typeDefinitions(Module module) {
186 val Collection<? extends TypeDefinition<?>> typedefs = module.typeDefinitions
187 if (typedefs.empty) {
191 <h2>Type Definitions</h2>
193 «FOR typedef : typedefs»
195 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
197 «typedef.descAndRefLi»
198 «typedef.restrictions»
206 private def identities(Module module) {
207 if (module.identities.empty) {
213 «FOR identity : module.identities»
215 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
217 «identity.descAndRefLi»
218 «IF !identity.baseIdentities.isEmpty»
219 «listItem("base", identity.baseIdentities.get(0).QName.localName)»
228 private def identitiesSummary(Module module) {
229 if (module.identities.empty) {
233 <h3>Identities Summary</h3>
239 «FOR identity : module.identities»
242 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
245 «identity.description»
253 private def groupings(Module module) {
254 if (module.groupings.empty) {
260 «FOR grouping : module.groupings»
262 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
264 «grouping.descAndRefLi»
265 «FOR childNode : grouping.childNodes»
266 «childNode.printSchemaNodeInfo»
275 private def groupingsSummary(Module module) {
276 if (module.groupings.empty) {
280 <h3>Groupings Summary</h3>
286 «FOR grouping : module.groupings»
289 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
292 «grouping.description»
300 def dataStore(Module module) {
301 if (module.childNodes.empty) {
305 <h2>Datastore Structure</h2>
310 def augmentations(Module module, EffectiveModelContext context) {
311 if (module.augmentations.empty) {
315 <h2>Augmentations</h2>
318 «FOR augment : module.augmentations»
320 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
321 Target [«typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
322 «augment.description»
323 Status: «strong(String.valueOf(augment.status))»
324 «IF augment.reference !== null»
325 Reference «augment.reference»
327 «IF augment.whenCondition !== null»
328 When «augment.whenCondition.toString»
330 «FOR childNode : augment.childNodes»
331 «childNode.printSchemaNodeInfo»
335 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
336 «printNodeChildren(parseTargetPath(augment.targetPath))»
343 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
344 augmentChildNodesAsString = new StringBuilder();
345 augmentChildNodesAsString.append(printNodeChildren(childNodes))
349 private def parseTargetPath(SchemaNodeIdentifier path) {
350 val nodes = new ArrayList<DataSchemaNode>();
351 for (QName pathElement : path.nodeIdentifiers) {
352 val module = ctx.findModule(pathElement.module)
353 if (module.isPresent) {
354 var foundNode = module.get.getDataChildByName(pathElement)
355 if (foundNode === null) {
356 val child = nodes.last
357 if (child instanceof DataNodeContainer) {
358 val dataContNode = child as DataNodeContainer
359 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
362 if (foundNode !== null) {
363 nodes.add(foundNode);
368 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
371 val targetPathNodes = new ArrayList<DataSchemaNode>();
372 targetPathNodes.add(lastNodeInTargetPath)
374 return targetPathNodes
377 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<? extends DataSchemaNode> childNodes) {
378 for (child : childNodes) {
379 if (child.QName.equals(findingNode))
383 for(child : childNodes) {
384 if (child instanceof ContainerSchemaNode) {
385 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
386 if (foundChild !== null)
388 } else if (child instanceof ListSchemaNode) {
389 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
390 if (foundChild !== null)
396 private def printNodeChildren(List<DataSchemaNode> childNodes) {
397 if (childNodes.empty) {
404 «printAugmentedNode(childNodes.get(0))»
409 private def CharSequence printAugmentedNode(DataSchemaNode child) {
411 if(child instanceof CaseSchemaNode)
416 «IF child instanceof ContainerSchemaNode»
417 «printContainerNode(child)»
419 «IF child instanceof AnyxmlSchemaNode»
420 «printAnyXmlNode(child)»
422 «IF child instanceof LeafSchemaNode»
423 «printLeafNode(child)»
425 «IF child instanceof LeafListSchemaNode»
426 «printLeafListNode(child)»
428 «IF child instanceof ListSchemaNode»
429 «printListNode(child)»
431 «IF child instanceof ChoiceSchemaNode»
432 «printChoiceNode(child)»
437 private def printChoiceNode(ChoiceSchemaNode child) {
438 val cases = new ArrayList(child.cases)
440 val CaseSchemaNode aCase = cases.get(0)
441 for (caseChildNode : aCase.childNodes)
442 printAugmentedNode(caseChildNode)
446 private def printListNode(ListSchemaNode listNode) {
449 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
450 «FOR child : listNode.childNodes»
451 «printAugmentedNode(child)»
453 </«listNode.QName.localName»>
457 private def printContainerNode(ContainerSchemaNode containerNode) {
460 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
461 «FOR child : containerNode.childNodes»
462 «printAugmentedNode(child)»
464 </«containerNode.QName.localName»>
468 private def printLeafListNode(LeafListSchemaNode leafListNode) {
471 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
472 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
473 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
477 private def printAnyXmlNode(AnyxmlSchemaNode anyXmlNode) {
480 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
484 private def printLeafNode(LeafSchemaNode leafNode) {
487 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
491 private def augmentationsSummary(Module module, EffectiveModelContext context) {
492 if (module.augmentations.empty) {
496 <h3>Augmentations Summary</h3>
502 «FOR augment : module.augmentations»
505 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
506 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
509 «augment.description»
517 def notifications(Module module) {
518 val Collection<? extends NotificationDefinition> notificationdefs = module.notifications
519 if (notificationdefs.empty) {
524 <h2>Notifications</h2>
525 «FOR notificationdef : notificationdefs»
527 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
528 «notificationdef.descAndRef»
529 «FOR childNode : notificationdef.childNodes»
530 «childNode.printSchemaNodeInfo»
536 private def notificationsSummary(Module module) {
537 if (module.notifications.empty) {
541 <h3>Notifications Summary</h3>
547 «FOR notification : module.notifications»
550 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
553 «notification.description»
561 def rpcs(Module module) {
562 if (module.rpcs.empty) {
567 <h2>RPC Definitions</h2>
568 «FOR rpc : module.rpcs»
569 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
572 «rpc.input.printSchemaNodeInfo»
573 «rpc.output.printSchemaNodeInfo»
580 private def rpcsSummary(Module module) {
581 if (module.rpcs.empty) {
585 <h3>RPCs Summary</h3>
591 «FOR rpc : module.rpcs»
594 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
605 def extensions(Module module) {
606 if (module.extensionSchemaNodes.empty) {
611 «FOR ext : module.extensionSchemaNodes»
613 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
620 private def extensionsSummary(Module module) {
621 if (module.extensionSchemaNodes.empty) {
625 <h3>Extensions Summary</h3>
631 «FOR ext : module.extensionSchemaNodes»
634 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
645 def features(Module module) {
646 if (module.features.empty) {
653 «FOR feature : module.features»
655 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
657 «feature.descAndRefLi»
665 private def featuresSummary(Module module) {
666 if (module.features.empty) {
670 <h3>Features Summary</h3>
676 «FOR feature : module.features»
679 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
682 «feature.description»
690 private def objectsSummary(Module module) {
691 if (module.childNodes.empty) {
695 <h3>Child Nodes Summary</h3>
701 «FOR childNode : module.childNodes»
704 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
707 «childNode.description»
715 def header(Module module)
717 <h1>«module.name»</h1>
719 <h2>Base Information</h2>
722 <td>«strong("prefix")»</td>
723 <td>«module.prefix»</td>
726 <td>«strong("namespace")»</td>
727 <td>«module.namespace»</td>
730 «IF module.revision.isPresent»
731 <td>«strong("revision")»</td>
732 <td>«module.revision.get.toString»</td>
736 <td>«strong("description")»</td>
737 <td>«module.description»</td>
740 <td>«strong("yang-version")»</td>
741 <td>«module.yangVersion»</td>
744 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
745 «imp.prefix»:«imp.moduleName»«IF imp.revision.isPresent» «imp.revision.get.toString»«ENDIF»;
751 def CharSequence schemaPathToId(SchemaPath path) {
753 return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«ENDFOR»'''
757 def code(String string) '''<code>«string»</code>'''
759 def process(Module module) {
760 throw new UnsupportedOperationException("TODO: auto-generated method stub")
763 def CharSequence tree(Module module) '''
764 «strong(module.name)»
765 «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())»
768 private def dispatch CharSequence tree(ChoiceSchemaNode node,YangInstanceIdentifier path) '''
769 «node.nodeName» (choice)
770 «casesTree(node.cases, path)»
773 def casesTree(Collection<? extends CaseSchemaNode> nodes, YangInstanceIdentifier path) '''
778 «node.childNodes.treeSet(path)»
784 private def dispatch CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) '''
788 private def dispatch CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) '''
789 «val newPath = path.append(node)»
790 «localLink(newPath,node.nodeName)»
791 «node.childNodes.treeSet(newPath)»
794 private def dispatch CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
795 «val newPath = path.append(node)»
796 «localLink(newPath,node.nodeName)»
797 «node.childNodes.treeSet(newPath)»
800 def CharSequence childNodes(Module module) '''
801 «val childNodes = module.childNodes»
802 «IF !childNodes.nullOrEmpty»
805 «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())»
809 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
813 «IF node instanceof DataNodeContainer»
814 «val dataNode = node as DataNodeContainer»
816 «FOR usesNode : dataNode.uses»
821 «FOR typeDef : dataNode.typeDefinitions»
822 «typeDef.restrictions»
826 «FOR grouping : dataNode.groupings»
827 «grouping.printGrouping»
831 «FOR child : dataNode.childNodes»
832 «child.printSchemaNodeInfo»
840 def String typeAnchorLink(SchemaPath path, CharSequence text) {
842 val lastElement = path.lastComponent
843 val ns = lastElement.namespace
844 if (ns == this.currentModule.namespace) {
845 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
847 return '''(«ns»)«text»'''
848 //to enable external (import) links
849 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
854 def CharSequence printBaseInfo(SchemaNode node) {
855 if(node instanceof LeafSchemaNode) {
857 «printInfo(node, "leaf")»
858 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
859 «listItem("units", node.type.units.orElse(null))»
860 «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))»
863 } else if(node instanceof LeafListSchemaNode) {
865 «printInfo(node, "leaf-list")»
866 «listItem("type", node.type?.QName.localName)»
869 } else if(node instanceof ListSchemaNode) {
871 «printInfo(node, "list")»
872 «FOR keyDef : node.keyDefinition»
873 «listItem("key definition", keyDef.localName)»
877 } else if(node instanceof ChoiceSchemaNode) {
879 «printInfo(node, "choice")»
880 «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))»
881 «FOR caseNode : node.cases»
882 «caseNode.printSchemaNodeInfo»
886 } else if(node instanceof CaseSchemaNode) {
888 «printInfo(node, "case")»
891 } else if(node instanceof ContainerSchemaNode) {
893 «printInfo(node, "container")»
896 } else if(node instanceof AnyxmlSchemaNode) {
898 «printInfo(node, "anyxml")»
904 def CharSequence printInfo(SchemaNode node, String nodeType) {
906 «IF node instanceof AugmentationTarget»
909 <li id="«node.path.schemaPathToId»">
910 «nodeType»: «node.QName.localName»
915 «strong(listItem(nodeType, node.QName.localName))»
918 «listItem("description", node.description.orElse(null))»
919 «listItem("reference", node.reference.orElse(null))»
920 «IF node instanceof DataSchemaNode»
921 «IF node.whenCondition.present»
922 «listItem("when condition", node.whenCondition.get.toString)»
925 «IF node instanceof ElementCountConstraintAware»
926 «IF node.elementCountConstraint.present»
927 «val constraint = node.elementCountConstraint.get»
928 «listItem("min elements", constraint.minElements?.toString)»
929 «listItem("max elements", constraint.maxElements?.toString)»
935 def CharSequence printUses(UsesNode usesNode) {
937 «strong(listItem("uses", typeAnchorLink(usesNode.sourceGrouping.path, usesNode.sourceGrouping.path.pathTowardsRoot.iterator.next.localName)))»
941 «FOR sp : usesNode.refines.keySet»
942 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
946 «FOR augment : usesNode.augmentations»
947 «typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
953 def CharSequence printGrouping(GroupingDefinition grouping) {
955 «strong(listItem("grouping", grouping.QName.localName))»
959 def CharSequence printChildren(Iterable<? extends DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
960 val anyxmlNodes = nodes.filter(AnyxmlSchemaNode)
961 val leafNodes = nodes.filter(LeafSchemaNode)
962 val leafListNodes = nodes.filter(LeafListSchemaNode)
963 val choices = nodes.filter(ChoiceSchemaNode)
964 val cases = nodes.filter(CaseSchemaNode)
965 val containers = nodes.filter(ContainerSchemaNode)
966 val lists = nodes.filter(ListSchemaNode)
968 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
969 <h3>Direct children</h3>
971 «FOR childNode : anyxmlNodes»
972 «childNode.printShortInfo(level,path)»
974 «FOR childNode : leafNodes»
975 «childNode.printShortInfo(level,path)»
977 «FOR childNode : leafListNodes»
978 «childNode.printShortInfo(level,path)»
980 «FOR childNode : containers»
981 «childNode.printShortInfo(level,path)»
983 «FOR childNode : lists»
984 «childNode.printShortInfo(level,path)»
989 «IF path.pathArguments.iterator.hasNext»
991 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
994 «FOR childNode : containers»
995 «childNode.printInfo(level,path)»
997 «FOR childNode : lists»
998 «childNode.printInfo(level,path)»
1000 «FOR childNode : choices»
1001 «childNode.printInfo(level,path)»
1003 «FOR childNode : cases»
1004 «childNode.printInfo(level,path)»
1009 def CharSequence xmlExample(Iterable<? extends DataSchemaNode> nodes, QName name, YangInstanceIdentifier path) '''
1011 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1015 def CharSequence xmplExampleTags(Iterable<? extends DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1016 <!-- Child nodes -->
1018 <!-- «node.QName.localName» -->
1019 «node.asXmlExampleTag(identifier)»
1024 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1025 «node.QName.xmlExampleTag("...")»
1028 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1029 <!-- This node could appear multiple times -->
1030 «node.QName.xmlExampleTag("...")»
1033 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1034 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1035 «node.QName.xmlExampleTag("...")»
1039 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1040 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1041 <!-- This node could appear multiple times -->
1042 «node.QName.xmlExampleTag("...")»
1046 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1051 def xmlExampleTag(QName name, CharSequence data) {
1052 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1055 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1058 def header(int level,YangInstanceIdentifier name) '''
1059 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1060 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1066 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1067 «header(level+1,node.QName)»
1070 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1071 «val newPath = path.append(node)»
1072 «header(level,newPath)»
1075 <dd>«newPath.asXmlPath»</dd>
1076 <dt>Restconf path</dt>
1077 <dd>«code(newPath.asRestconfPath)»</dd>
1079 «node.childNodes.printChildren(level,newPath)»
1082 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1083 «val newPath = path.append(node)»
1084 «header(level,newPath)»
1087 <dd>«newPath.asXmlPath»</dd>
1088 <dt>Restconf path</dt>
1089 <dd>«code(newPath.asRestconfPath)»</dd>
1091 «node.childNodes.printChildren(level,newPath)»
1094 private def dispatch CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1095 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1096 «choiceCases.printChildren(level, path)»
1099 private def dispatch CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) '''
1100 «node.childNodes.printChildren(level, path)»
1105 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1106 val newPath = path.append(node);
1108 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1110 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1116 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1117 val newPath = path.append(node);
1119 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1121 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1127 def CharSequence printShortInfo(AnyxmlSchemaNode node, int level, YangInstanceIdentifier path) {
1129 <li>«strong((node.QName.localName))» (anyxml)
1131 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1132 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1138 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1140 <li>«strong((node.QName.localName))» (leaf)
1142 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1143 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1149 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1151 <li>«strong((node.QName.localName))» (leaf-list)
1153 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1159 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1161 <a href="#«anchor»">«text»</a>
1165 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1166 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1170 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1171 return identifier.node(node.QName);
1174 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1175 val keyValues = new LinkedHashMap<QName,Object>();
1176 if(node.keyDefinition !== null) {
1177 for(definition : node.keyDefinition) {
1178 keyValues.put(definition,new Object);
1182 return identifier.node(NodeIdentifierWithPredicates.of(node.QName, keyValues));
1186 def asXmlPath(YangInstanceIdentifier identifier) {
1190 def asRestconfPath(YangInstanceIdentifier identifier) {
1191 val it = new StringBuilder();
1192 append(currentModule.name)
1194 var previous = false;
1195 for(arg : identifier.pathArguments) {
1196 if(previous) append('/')
1197 append(arg.nodeType.localName);
1199 if(arg instanceof NodeIdentifierWithPredicates) {
1200 for(qname : arg.keySet) {
1202 append(qname.localName)
1211 private def String schemaPathToString(Module module, SchemaNodeIdentifier schemaPath, EffectiveModelContext ctx,
1212 DataNodeContainer dataNode) {
1213 val path = schemaPath.nodeIdentifiers
1214 val StringBuilder pathString = new StringBuilder()
1215 if (schemaPath instanceof Absolute) {
1216 pathString.append('/')
1219 val QName qname = path.get(0)
1220 var Object parent = ctx.findModule(qname.module).orElse(null)
1223 if (parent instanceof DataNodeContainer) {
1224 var SchemaNode node = parent.getDataChildByName(name)
1225 if (node === null && (parent instanceof Module)) {
1226 val notifications = (parent as Module).notifications;
1227 for (notification : notifications) {
1228 if (notification.QName.equals(name)) {
1233 if (node === null && (parent instanceof Module)) {
1234 val rpcs = (parent as Module).rpcs;
1236 if (rpc.QName.equals(name)) {
1242 val pathElementModule = ctx.findModule(name.module).get
1243 val String moduleName = pathElementModule.name
1244 pathString.append(moduleName)
1245 pathString.append(':')
1246 pathString.append(name.localName)
1247 pathString.append('/')
1248 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1249 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode];
1250 if(caseNode !== null) {
1251 pathString.append("(case)");
1252 pathString.append(caseNode.QName.localName);
1258 return pathString.toString;
1262 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1263 «IF childNodes !== null && !childNodes.empty»
1264 «FOR child : childNodes.values»
1265 «childInfo(child, childNodes)»
1270 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1271 «val String path = nodeSchemaPathToPath(node, childNodes)»
1282 private def CharSequence treeSet(Collection<? extends DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1283 «IF childNodes !== null && !childNodes.empty»
1285 «FOR child : childNodes»
1294 def listKeys(ListSchemaNode node) '''
1295 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1298 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1301 «listItem("Argument", ext.argument)»
1305 private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1310 /* #################### RESTRICTIONS #################### */
1311 private def restrictions(TypeDefinition<?> type) '''
1312 «type.baseType.toBaseStmt»
1317 private def dispatch toLength(TypeDefinition<?> type) {
1320 private def dispatch toLength(BinaryTypeDefinition type) '''
1321 «type.lengthConstraint.toLengthStmt»
1324 private def dispatch toLength(StringTypeDefinition type) '''
1325 «type.lengthConstraint.toLengthStmt»
1328 private def dispatch toRange(TypeDefinition<?> type) {
1331 private def dispatch toRange(DecimalTypeDefinition type) '''
1332 «type.rangeConstraint.toRangeStmt»
1335 private def dispatch toRange(Int8TypeDefinition type) '''
1336 «type.rangeConstraint.toRangeStmt»
1339 private def dispatch toRange(Int16TypeDefinition type) '''
1340 «type.rangeConstraint.toRangeStmt»
1343 private def dispatch toRange(Int32TypeDefinition type) '''
1344 «type.rangeConstraint.toRangeStmt»
1347 private def dispatch toRange(Int64TypeDefinition type) '''
1348 «type.rangeConstraint.toRangeStmt»
1351 private def dispatch toRange(Uint8TypeDefinition type) '''
1352 «type.rangeConstraint.toRangeStmt»
1355 private def dispatch toRange(Uint16TypeDefinition type) '''
1356 «type.rangeConstraint.toRangeStmt»
1359 private def dispatch toRange(Uint32TypeDefinition type) '''
1360 «type.rangeConstraint.toRangeStmt»
1363 private def dispatch toRange(Uint64TypeDefinition type) '''
1364 «type.rangeConstraint.toRangeStmt»
1367 def toLengthStmt(Optional<LengthConstraint> lengths) '''
1368 «IF lengths.isPresent»
1369 «listItem("Length restrictions:")»
1371 «FOR length : lengths.get.allowedRanges.asRanges»
1373 «IF length.lowerEndpoint == length.upperEndpoint»
1374 «length.lowerEndpoint»
1376 <«length.lowerEndpoint», «length.upperEndpoint»>
1384 def toRangeStmt(Optional<? extends RangeConstraint<?>> constraint) '''
1385 «IF constraint.present»
1386 «listItem("Range restrictions:")»
1388 «FOR range : constraint.get.allowedRanges.asRanges»
1390 «IF range.lowerEndpoint == range.upperEndpoint»
1391 «range.lowerEndpoint»
1393 <«range.lowerEndpoint», «range.upperEndpoint»>
1401 def toBaseStmt(TypeDefinition<?> baseType) '''
1402 «IF baseType !== null»
1403 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1409 /* #################### UTILITY #################### */
1410 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1411 private def italic(CharSequence str) '''<i>«str»</i>'''
1413 def CharSequence descAndRefLi(SchemaNode node) '''
1414 «listItem("Description", node.description.orElse(null))»
1415 «listItem("Reference", node.reference.orElse(null))»
1418 def CharSequence descAndRef(SchemaNode node) '''
1420 «IF node.reference !== null»
1421 Reference «node.reference»
1425 private def listItem(String value) '''
1426 «IF value !== null && !value.empty»
1433 private def listItem(String name, String value) '''
1434 «IF value !== null && !value.empty»
1441 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1442 if (node instanceof ChoiceSchemaNode || node instanceof CaseSchemaNode) {
1446 val path = node.path.pathFromRoot
1447 val absolute = node.path.absolute;
1448 var StringBuilder result = new StringBuilder
1452 if (path !== null && !path.empty) {
1453 val actual = new ArrayList()
1455 for (pathElement : path) {
1456 actual.add(pathElement)
1457 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1458 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof CaseSchemaNode)) {
1459 result.append(pathElement.localName)
1460 if (i != path.size - 1) {
1467 return result.toString
1470 private def dispatch addedByInfo(SchemaNode node) '''
1473 private def dispatch addedByInfo(DataSchemaNode node) '''
1474 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1477 private def dispatch isAddedBy(SchemaNode node) {
1481 private def dispatch isAddedBy(DataSchemaNode node) {
1482 return node.augmenting || node.addedByUses
1485 private def dispatch nodeName(SchemaNode node) '''
1487 «italic(node.QName.localName)»«node.addedByInfo»
1489 «node.QName.localName»«node.addedByInfo»
1493 private def dispatch nodeName(ContainerSchemaNode node) '''
1495 «strong(italic(node.QName.localName))»«node.addedByInfo»
1497 «strong(node.QName.localName)»«node.addedByInfo»
1501 private def dispatch nodeName(ListSchemaNode node) '''
1503 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1505 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»