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.IdentityHashMap
19 import java.util.HashMap
20 import java.util.HashSet
21 import java.util.LinkedHashMap
24 import java.util.Optional
26 import org.gaul.modernizer_maven_annotations.SuppressModernizer
27 import org.opendaylight.yangtools.yang.common.QName
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
30 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode
31 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
32 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
38 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware
39 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
40 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
41 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
42 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
43 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
44 import org.opendaylight.yangtools.yang.model.api.MandatoryAware
45 import org.opendaylight.yangtools.yang.model.api.Module
46 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
47 import org.opendaylight.yangtools.yang.model.api.SchemaNode
48 import org.opendaylight.yangtools.yang.model.api.SchemaPath
49 import org.opendaylight.yangtools.yang.model.api.TypeAware
50 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
51 import org.opendaylight.yangtools.yang.model.api.UsesNode
52 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement
53 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier
54 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute
55 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
56 import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition
57 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
58 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition
59 import org.slf4j.Logger
60 import org.slf4j.LoggerFactory
61 import org.sonatype.plexus.build.incremental.BuildContext
66 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
68 val Map<String, String> imports = new HashMap
69 val Map<TypeDefinition<?>, SchemaPath> types = new IdentityHashMap
70 var Module currentModule
71 var EffectiveModelContext ctx
74 StringBuilder augmentChildNodesAsString
76 DataSchemaNode lastNodeInTargetPath = null
78 new(EffectiveModelContext context) {
80 fillTypes(SchemaPath.ROOT, context.moduleStatements.values)
83 private def void fillTypes(SchemaPath path, Collection<? extends EffectiveStatement<?, ?>> stmts) {
85 val arg = stmt.argument
86 if (arg instanceof QName) {
87 val stmtPath = path.createChild(arg)
88 if (stmt instanceof TypeDefinition) {
89 types.putIfAbsent(stmt, stmtPath)
90 } else if (stmt instanceof TypeAware) {
92 val typePath = stmtPath.createChild(type.QName)
93 types.putIfAbsent(type, typePath)
96 fillTypes(stmtPath, stmt.effectiveSubstatements)
101 def generate(BuildContext buildContext, File targetPath, Set<Module> modulesToGen) throws IOException {
103 Files.createDirectories(path.getParentFile().toPath())
105 for (module : modulesToGen) {
106 add(generateDocumentation(buildContext, module))
111 def generateDocumentation(BuildContext buildContext, Module module) {
112 val destination = new File(path, '''«module.name».html''')
113 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
114 var OutputStreamWriter fw
115 var BufferedWriter bw
117 fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
118 bw = new BufferedWriter(fw)
119 currentModule = module
120 bw.append(generate(module, ctx))
121 } catch (IOException e) {
122 LOG.error("Failed to emit file {}", destination, e);
134 def generate(Module module, EffectiveModelContext ctx) '''
138 <title>«module.name»</title>
146 def body(Module module, EffectiveModelContext ctx) '''
149 «typeDefinitionsSummary(module)»
150 «identitiesSummary(module)»
151 «groupingsSummary(module)»
152 «augmentationsSummary(module, ctx)»
153 «objectsSummary(module)»
154 «notificationsSummary(module)»
155 «rpcsSummary(module)»
156 «extensionsSummary(module)»
157 «featuresSummary(module)»
159 «typeDefinitions(module)»
169 «notifications(module)»
171 «augmentations(module, ctx)»
182 private def typeDefinitionsSummary(Module module) {
183 val Collection<? extends TypeDefinition<?>> typedefs = module.typeDefinitions
184 if (typedefs.empty) {
189 <h3>Type Definitions Summary</h3>
195 «FOR typedef : typedefs»
198 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
201 «typedef.description»
210 def typeDefinitions(Module module) {
211 val Collection<? extends TypeDefinition<?>> typedefs = module.typeDefinitions
212 if (typedefs.empty) {
216 <h2>Type Definitions</h2>
218 «FOR typedef : typedefs»
220 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
222 «typedef.descAndRefLi»
223 «typedef.restrictions»
231 private def identities(Module module) {
232 if (module.identities.empty) {
238 «FOR identity : module.identities»
240 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
242 «identity.descAndRefLi»
243 «IF !identity.baseIdentities.isEmpty»
244 «listItem("base", identity.baseIdentities.get(0).QName.localName)»
253 private def identitiesSummary(Module module) {
254 if (module.identities.empty) {
258 <h3>Identities Summary</h3>
264 «FOR identity : module.identities»
267 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
270 «identity.description»
278 private def groupings(Module module) {
279 if (module.groupings.empty) {
285 «FOR grouping : module.groupings»
287 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
289 «grouping.descAndRefLi»
290 «FOR childNode : grouping.childNodes»
291 «childNode.printSchemaNodeInfo»
300 private def groupingsSummary(Module module) {
301 if (module.groupings.empty) {
305 <h3>Groupings Summary</h3>
311 «FOR grouping : module.groupings»
314 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
317 «grouping.description»
325 def dataStore(Module module) {
326 if (module.childNodes.empty) {
330 <h2>Datastore Structure</h2>
335 def augmentations(Module module, EffectiveModelContext context) {
336 if (module.augmentations.empty) {
340 <h2>Augmentations</h2>
343 «FOR augment : module.augmentations»
345 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
346 Target [«typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
347 «augment.description»
348 Status: «strong(String.valueOf(augment.status))»
349 «IF augment.reference !== null»
350 Reference «augment.reference»
352 «IF augment.whenCondition !== null»
353 When «augment.whenCondition.toString»
355 «FOR childNode : augment.childNodes»
356 «childNode.printSchemaNodeInfo»
360 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
361 «printNodeChildren(parseTargetPath(augment.targetPath))»
368 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
369 augmentChildNodesAsString = new StringBuilder();
370 augmentChildNodesAsString.append(printNodeChildren(childNodes))
374 private def parseTargetPath(SchemaNodeIdentifier path) {
375 val nodes = new ArrayList<DataSchemaNode>();
376 for (QName pathElement : path.nodeIdentifiers) {
377 val module = ctx.findModule(pathElement.module)
378 if (module.isPresent) {
379 var foundNode = module.get.dataChildByName(pathElement)
380 if (foundNode === null) {
381 val child = nodes.last
382 if (child instanceof DataNodeContainer) {
383 val dataContNode = child as DataNodeContainer
384 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
387 if (foundNode !== null) {
388 nodes.add(foundNode);
393 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
396 val targetPathNodes = new ArrayList<DataSchemaNode>();
397 targetPathNodes.add(lastNodeInTargetPath)
399 return targetPathNodes
402 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<? extends DataSchemaNode> childNodes) {
403 for (child : childNodes) {
404 if (child.QName.equals(findingNode))
408 for(child : childNodes) {
409 if (child instanceof ContainerSchemaNode) {
410 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
411 if (foundChild !== null)
413 } else if (child instanceof ListSchemaNode) {
414 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
415 if (foundChild !== null)
421 private def printNodeChildren(List<DataSchemaNode> childNodes) {
422 if (childNodes.empty) {
429 «printAugmentedNode(childNodes.get(0))»
434 private def CharSequence printAugmentedNode(DataSchemaNode child) {
436 if(child instanceof CaseSchemaNode)
441 «IF child instanceof ContainerSchemaNode»
442 «printContainerNode(child)»
444 «IF child instanceof AnyxmlSchemaNode»
445 «printAnyXmlNode(child)»
447 «IF child instanceof LeafSchemaNode»
448 «printLeafNode(child)»
450 «IF child instanceof LeafListSchemaNode»
451 «printLeafListNode(child)»
453 «IF child instanceof ListSchemaNode»
454 «printListNode(child)»
456 «IF child instanceof ChoiceSchemaNode»
457 «printChoiceNode(child)»
462 private def printChoiceNode(ChoiceSchemaNode child) {
463 val cases = new ArrayList(child.cases)
465 val CaseSchemaNode aCase = cases.get(0)
466 for (caseChildNode : aCase.childNodes)
467 printAugmentedNode(caseChildNode)
471 private def printListNode(ListSchemaNode listNode) {
474 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
475 «FOR child : listNode.childNodes»
476 «printAugmentedNode(child)»
478 </«listNode.QName.localName»>
482 private def printContainerNode(ContainerSchemaNode containerNode) {
485 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
486 «FOR child : containerNode.childNodes»
487 «printAugmentedNode(child)»
489 </«containerNode.QName.localName»>
493 private def printLeafListNode(LeafListSchemaNode leafListNode) {
496 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
497 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
498 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
502 private def printAnyXmlNode(AnyxmlSchemaNode anyXmlNode) {
505 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
509 private def printLeafNode(LeafSchemaNode leafNode) {
512 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
516 private def augmentationsSummary(Module module, EffectiveModelContext context) {
517 if (module.augmentations.empty) {
521 <h3>Augmentations Summary</h3>
527 «FOR augment : module.augmentations»
530 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
531 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
534 «augment.description»
542 def notifications(Module module) {
543 val Collection<? extends NotificationDefinition> notificationdefs = module.notifications
544 if (notificationdefs.empty) {
549 <h2>Notifications</h2>
550 «FOR notificationdef : notificationdefs»
552 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
553 «notificationdef.descAndRef»
554 «FOR childNode : notificationdef.childNodes»
555 «childNode.printSchemaNodeInfo»
561 private def notificationsSummary(Module module) {
562 if (module.notifications.empty) {
566 <h3>Notifications Summary</h3>
572 «FOR notification : module.notifications»
575 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
578 «notification.description»
586 def rpcs(Module module) {
587 if (module.rpcs.empty) {
592 <h2>RPC Definitions</h2>
593 «FOR rpc : module.rpcs»
594 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
597 «rpc.input.printSchemaNodeInfo»
598 «rpc.output.printSchemaNodeInfo»
605 private def rpcsSummary(Module module) {
606 if (module.rpcs.empty) {
610 <h3>RPCs Summary</h3>
616 «FOR rpc : module.rpcs»
619 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
630 def extensions(Module module) {
631 if (module.extensionSchemaNodes.empty) {
636 «FOR ext : module.extensionSchemaNodes»
638 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
645 private def extensionsSummary(Module module) {
646 if (module.extensionSchemaNodes.empty) {
650 <h3>Extensions Summary</h3>
656 «FOR ext : module.extensionSchemaNodes»
659 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
670 def features(Module module) {
671 if (module.features.empty) {
678 «FOR feature : module.features»
680 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
682 «feature.descAndRefLi»
690 private def featuresSummary(Module module) {
691 if (module.features.empty) {
695 <h3>Features Summary</h3>
701 «FOR feature : module.features»
704 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
707 «feature.description»
715 private def objectsSummary(Module module) {
716 if (module.childNodes.empty) {
720 <h3>Child Nodes Summary</h3>
726 «FOR childNode : module.childNodes»
729 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
732 «childNode.description»
740 def header(Module module)
742 <h1>«module.name»</h1>
744 <h2>Base Information</h2>
747 <td>«strong("prefix")»</td>
748 <td>«module.prefix»</td>
751 <td>«strong("namespace")»</td>
752 <td>«module.namespace»</td>
755 «IF module.revision.isPresent»
756 <td>«strong("revision")»</td>
757 <td>«module.revision.get.toString»</td>
761 <td>«strong("description")»</td>
762 <td>«module.description»</td>
765 <td>«strong("yang-version")»</td>
766 <td>«module.yangVersion»</td>
769 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
770 «imp.prefix»:«imp.moduleName»«IF imp.revision.isPresent» «imp.revision.get.toString»«ENDIF»;
776 def CharSequence schemaPathToId(SchemaPath path) {
778 return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«ENDFOR»'''
782 def code(String string) '''<code>«string»</code>'''
784 def process(Module module) {
785 throw new UnsupportedOperationException("TODO: auto-generated method stub")
788 def CharSequence tree(Module module) '''
789 «strong(module.name)»
790 «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())»
793 private def CharSequence tree(ChoiceSchemaNode node, YangInstanceIdentifier path) '''
794 «node.nodeName» (choice)
795 «casesTree(node.cases, path)»
798 def casesTree(Collection<? extends CaseSchemaNode> nodes, YangInstanceIdentifier path) '''
803 «node.childNodes.treeSet(path)»
809 private def CharSequence tree(DataSchemaNode node, YangInstanceIdentifier path) {
810 if (node instanceof ChoiceSchemaNode) {
811 return tree(node, path)
812 } else if (node instanceof ListSchemaNode) {
813 return tree(node, path)
814 } else if (node instanceof ContainerSchemaNode) {
815 return tree(node, path)
820 private def CharSequence tree(ListSchemaNode node, YangInstanceIdentifier path) '''
821 «val newPath = path.append(node)»
822 «localLink(newPath,node.nodeName)»
823 «node.childNodes.treeSet(newPath)»
826 private def CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
827 «val newPath = path.append(node)»
828 «localLink(newPath,node.nodeName)»
829 «node.childNodes.treeSet(newPath)»
832 def CharSequence childNodes(Module module) '''
833 «val childNodes = module.childNodes»
834 «IF !childNodes.nullOrEmpty»
837 «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())»
841 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
845 «IF node instanceof DataNodeContainer»
846 «val dataNode = node as DataNodeContainer»
848 «FOR usesNode : dataNode.uses»
853 «FOR typeDef : dataNode.typeDefinitions»
854 «typeDef.restrictions»
858 «FOR grouping : dataNode.groupings»
859 «grouping.printGrouping»
863 «FOR child : dataNode.childNodes»
864 «child.printSchemaNodeInfo»
872 def String typeAnchorLink(SchemaPath path, CharSequence text) {
874 val lastElement = path.lastComponent
875 val ns = lastElement.namespace
876 if (ns == this.currentModule.namespace) {
877 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
879 return '''(«ns»)«text»'''
880 //to enable external (import) links
881 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
886 def CharSequence printBaseInfo(SchemaNode node) {
887 if(node instanceof LeafSchemaNode) {
889 «printInfo(node, "leaf")»
890 «listItem("type", typeAnchorLink(types.get(node.type), node.type.QName.localName))»
891 «listItem("units", node.type.units.orElse(null))»
892 «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))»
895 } else if(node instanceof LeafListSchemaNode) {
897 «printInfo(node, "leaf-list")»
898 «IF node.type !== null»
899 «listItem("type", node.type.QName.localName)»
903 } else if(node instanceof ListSchemaNode) {
905 «printInfo(node, "list")»
906 «FOR keyDef : node.keyDefinition»
907 «listItem("key definition", keyDef.localName)»
911 } else if(node instanceof ChoiceSchemaNode) {
913 «printInfo(node, "choice")»
914 «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))»
915 «FOR caseNode : node.cases»
916 «caseNode.printSchemaNodeInfo»
920 } else if(node instanceof CaseSchemaNode) {
922 «printInfo(node, "case")»
925 } else if(node instanceof ContainerSchemaNode) {
927 «printInfo(node, "container")»
930 } else if(node instanceof AnyxmlSchemaNode) {
932 «printInfo(node, "anyxml")»
938 def CharSequence printInfo(SchemaNode node, String nodeType) {
940 «IF node instanceof AugmentationTarget»
943 <li id="«node.path.schemaPathToId»">
944 «nodeType»: «node.QName.localName»
949 «strong(listItem(nodeType, node.QName.localName))»
952 «listItem("description", node.description.orElse(null))»
953 «listItem("reference", node.reference.orElse(null))»
954 «IF node instanceof DataSchemaNode»
955 «IF node.whenCondition.present»
956 «listItem("when condition", node.whenCondition.get.toString)»
959 «IF node instanceof ElementCountConstraintAware»
960 «IF node.elementCountConstraint.present»
961 «val constraint = node.elementCountConstraint.get»
962 «listItem("min elements", constraint.minElements?.toString)»
963 «listItem("max elements", constraint.maxElements?.toString)»
969 def CharSequence printUses(UsesNode usesNode) {
971 «strong(listItem("uses", typeAnchorLink(usesNode.sourceGrouping.path, usesNode.sourceGrouping.QName.localName)))»
975 «FOR sp : usesNode.refines.keySet»
976 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
980 «FOR augment : usesNode.augmentations»
981 «typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
987 def CharSequence printGrouping(GroupingDefinition grouping) {
989 «strong(listItem("grouping", grouping.QName.localName))»
993 def CharSequence printChildren(Iterable<? extends DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
994 val anyxmlNodes = nodes.filter(AnyxmlSchemaNode)
995 val leafNodes = nodes.filter(LeafSchemaNode)
996 val leafListNodes = nodes.filter(LeafListSchemaNode)
997 val choices = nodes.filter(ChoiceSchemaNode)
998 val cases = nodes.filter(CaseSchemaNode)
999 val containers = nodes.filter(ContainerSchemaNode)
1000 val lists = nodes.filter(ListSchemaNode)
1002 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
1003 <h3>Direct children</h3>
1005 «FOR childNode : anyxmlNodes»
1006 «childNode.printShortInfo(level,path)»
1008 «FOR childNode : leafNodes»
1009 «childNode.printShortInfo(level,path)»
1011 «FOR childNode : leafListNodes»
1012 «childNode.printShortInfo(level,path)»
1014 «FOR childNode : containers»
1015 «childNode.printShortInfo(level,path)»
1017 «FOR childNode : lists»
1018 «childNode.printShortInfo(level,path)»
1023 «IF path.pathArguments.iterator.hasNext»
1024 <h3>XML example</h3>
1025 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
1028 «FOR childNode : containers»
1029 «childNode.printInfo(level,path)»
1031 «FOR childNode : lists»
1032 «childNode.printInfo(level,path)»
1034 «FOR childNode : choices»
1035 «childNode.printInfo(level,path)»
1037 «FOR childNode : cases»
1038 «childNode.printInfo(level,path)»
1043 def CharSequence xmlExample(Iterable<? extends DataSchemaNode> nodes, QName name, YangInstanceIdentifier path) '''
1045 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1049 def CharSequence xmplExampleTags(Iterable<? extends DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1050 <!-- Child nodes -->
1052 <!-- «node.QName.localName» -->
1053 «node.asXmlExampleTag(identifier)»
1058 private def CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) {
1059 if (node instanceof LeafSchemaNode) {
1060 return '''«node.QName.xmlExampleTag("...")»'''
1062 if (node instanceof LeafListSchemaNode) {
1064 <!-- This node could appear multiple times -->
1065 «node.QName.xmlExampleTag("...")»
1068 if (node instanceof ContainerSchemaNode) {
1070 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1071 «node.QName.xmlExampleTag("...")»
1074 if (node instanceof ListSchemaNode) {
1076 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1077 <!-- This node could appear multiple times -->
1078 «node.QName.xmlExampleTag("...")»
1081 return "<!-- noop -->"
1084 def xmlExampleTag(QName name, CharSequence data) {
1085 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1088 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1091 def header(int level,YangInstanceIdentifier name) '''
1092 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1093 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1097 private def CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1098 «val newPath = path.append(node)»
1099 «header(level,newPath)»
1102 <dd>«newPath.asXmlPath»</dd>
1103 <dt>Restconf path</dt>
1104 <dd>«code(newPath.asRestconfPath)»</dd>
1106 «node.childNodes.printChildren(level,newPath)»
1109 private def CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1110 «val newPath = path.append(node)»
1111 «header(level,newPath)»
1114 <dd>«newPath.asXmlPath»</dd>
1115 <dt>Restconf path</dt>
1116 <dd>«code(newPath.asRestconfPath)»</dd>
1118 «node.childNodes.printChildren(level,newPath)»
1121 private def CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1122 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1123 «choiceCases.printChildren(level, path)»
1126 private def CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) '''
1127 «node.childNodes.printChildren(level, path)»
1132 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1133 val newPath = path.append(node);
1135 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1137 «node.configurationDataItem»
1143 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1144 val newPath = path.append(node);
1146 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1148 «node.configurationDataItem»
1154 def CharSequence printShortInfo(AnyxmlSchemaNode node, int level, YangInstanceIdentifier path) {
1156 <li>«strong((node.QName.localName))» (anyxml)
1158 «node.configurationDataItem»
1159 «node.mandatoryItem»
1165 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1167 <li>«strong((node.QName.localName))» (leaf)
1169 «node.configurationDataItem»
1170 «node.mandatoryItem»
1176 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1178 <li>«strong((node.QName.localName))» (leaf-list)
1180 «node.configurationDataItem»
1186 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1188 <a href="#«anchor»">«text»</a>
1192 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1193 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1196 private static def String configurationDataItem(DataSchemaNode node) {
1197 return node.effectiveConfig
1198 .map([config | "<li>configuration data: " + strong(String.valueOf(config)) + "</li>"])
1202 private static def CharSequence mandatoryItem(MandatoryAware node) '''
1203 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1206 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1207 return identifier.node(node.QName);
1210 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1211 val keyValues = new LinkedHashMap<QName,Object>();
1212 if(node.keyDefinition !== null) {
1213 for(definition : node.keyDefinition) {
1214 keyValues.put(definition,new Object);
1218 return identifier.node(NodeIdentifierWithPredicates.of(node.QName, keyValues));
1222 def asXmlPath(YangInstanceIdentifier identifier) {
1226 def asRestconfPath(YangInstanceIdentifier identifier) {
1227 val it = new StringBuilder();
1228 append(currentModule.name)
1230 var previous = false;
1231 for(arg : identifier.pathArguments) {
1232 if(previous) append('/')
1233 append(arg.nodeType.localName);
1235 if(arg instanceof NodeIdentifierWithPredicates) {
1236 for(qname : arg.keySet) {
1238 append(qname.localName)
1247 private def String schemaPathToString(Module module, SchemaNodeIdentifier schemaPath, EffectiveModelContext ctx,
1248 DataNodeContainer dataNode) {
1249 val path = schemaPath.nodeIdentifiers
1250 val StringBuilder pathString = new StringBuilder()
1251 if (schemaPath instanceof Absolute) {
1252 pathString.append('/')
1255 val QName qname = path.get(0)
1256 var Object parent = ctx.findModule(qname.module).orElse(null)
1259 if (parent instanceof DataNodeContainer) {
1260 var SchemaNode node = parent.dataChildByName(name)
1261 if (node === null && (parent instanceof Module)) {
1262 val notifications = (parent as Module).notifications;
1263 for (notification : notifications) {
1264 if (notification.QName.equals(name)) {
1269 if (node === null && (parent instanceof Module)) {
1270 val rpcs = (parent as Module).rpcs;
1272 if (rpc.QName.equals(name)) {
1278 val pathElementModule = ctx.findModule(name.module).get
1279 val String moduleName = pathElementModule.name
1280 pathString.append(moduleName)
1281 pathString.append(':')
1282 pathString.append(name.localName)
1283 pathString.append('/')
1284 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1285 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode];
1286 if(caseNode !== null) {
1287 pathString.append("(case)");
1288 pathString.append(caseNode.QName.localName);
1294 return pathString.toString;
1298 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1299 «IF childNodes !== null && !childNodes.empty»
1300 «FOR child : childNodes.values»
1301 «childInfo(child, childNodes)»
1306 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1307 «val String path = nodeSchemaPathToPath(node, childNodes)»
1318 private def CharSequence treeSet(Collection<? extends DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1319 «IF childNodes !== null && !childNodes.empty»
1321 «FOR child : childNodes»
1330 def listKeys(ListSchemaNode node) '''
1331 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1334 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1337 «listItem("Argument", ext.argument)»
1342 /* #################### RESTRICTIONS #################### */
1343 private def restrictions(TypeDefinition<?> type) '''
1344 «type.baseType.toBaseStmt»
1349 private def toLength(TypeDefinition<?> type) '''
1350 «IF type instanceof LengthRestrictedTypeDefinition»
1351 «type.lengthConstraint.toLengthStmt»
1355 private def toRange(TypeDefinition<?> type) '''
1356 «IF type instanceof RangeRestrictedTypeDefinition»
1357 «type.rangeConstraint.toRangeStmt»
1361 def toLengthStmt(Optional<LengthConstraint> lengths) '''
1362 «IF lengths.isPresent»
1363 «listItem("Length restrictions:")»
1365 «FOR length : lengths.get.allowedRanges.asRanges»
1367 «IF length.lowerEndpoint == length.upperEndpoint»
1368 «length.lowerEndpoint»
1370 <«length.lowerEndpoint», «length.upperEndpoint»>
1378 def toRangeStmt(Optional<? extends RangeConstraint<?>> constraint) '''
1379 «IF constraint.present»
1380 «listItem("Range restrictions:")»
1382 «FOR range : constraint.get.allowedRanges.asRanges»
1384 «IF range.lowerEndpoint == range.upperEndpoint»
1385 «range.lowerEndpoint»
1387 <«range.lowerEndpoint», «range.upperEndpoint»>
1395 def toBaseStmt(TypeDefinition<?> baseType) '''
1396 «IF baseType !== null»
1397 «listItem("Base type", typeAnchorLink(types.get(baseType), baseType.QName.localName))»
1401 /* #################### UTILITY #################### */
1402 private def static String strong(CharSequence str) '''<strong>«str»</strong>'''
1403 private def static italic(CharSequence str) '''<i>«str»</i>'''
1405 def CharSequence descAndRefLi(SchemaNode node) '''
1406 «listItem("Description", node.description.orElse(null))»
1407 «listItem("Reference", node.reference.orElse(null))»
1410 def CharSequence descAndRef(SchemaNode node) '''
1412 «IF node.reference !== null»
1413 Reference «node.reference»
1417 private def listItem(String value) '''
1418 «IF value !== null && !value.empty»
1425 private def listItem(String name, String value) '''
1426 «IF value !== null && !value.empty»
1433 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1434 if (node instanceof ChoiceSchemaNode || node instanceof CaseSchemaNode) {
1438 val path = node.path.pathFromRoot
1439 val absolute = node.path.absolute;
1440 var StringBuilder result = new StringBuilder
1444 if (path !== null && !path.empty) {
1445 val actual = new ArrayList()
1447 for (pathElement : path) {
1448 actual.add(pathElement)
1449 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1450 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof CaseSchemaNode)) {
1451 result.append(pathElement.localName)
1452 if (i != path.size - 1) {
1459 return result.toString
1462 private def addedByInfo(SchemaNode node) {
1463 if (node instanceof DataSchemaNode) {
1464 return addedByInfo(node)
1469 private def addedByInfo(DataSchemaNode node) '''
1470 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1473 private def isAddedBy(SchemaNode node) {
1474 if (node instanceof DataSchemaNode) {
1475 return node.augmenting || node.addedByUses
1480 private def nodeName(SchemaNode node) {
1481 if (node instanceof ContainerSchemaNode) {
1482 return nodeName(node);
1483 } else if (node instanceof ListSchemaNode) {
1484 return nodeName(node);
1486 val addedByInfo = node.addedByInfo
1487 if (node.isAddedBy) {
1488 return '''«italic(node.QName.localName)»«addedByInfo»'''
1490 return '''«node.QName.localName»«addedByInfo»'''
1493 private def nodeName(ContainerSchemaNode node) '''
1495 «strong(italic(node.QName.localName))»«node.addedByInfo»
1497 «strong(node.QName.localName)»«node.addedByInfo»
1501 private def 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»