2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.yang.unified.doc.generator
10 import com.google.common.collect.Iterables
11 import com.google.common.collect.Lists
12 import java.io.BufferedWriter
14 import java.io.IOException
15 import java.io.OutputStreamWriter
16 import java.nio.charset.StandardCharsets
17 import java.util.ArrayList
18 import java.util.Collection
19 import java.util.HashMap
20 import java.util.HashSet
21 import java.util.LinkedHashMap
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.Module
45 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
46 import org.opendaylight.yangtools.yang.model.api.SchemaNode
47 import org.opendaylight.yangtools.yang.model.api.SchemaPath
48 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
49 import org.opendaylight.yangtools.yang.model.api.UsesNode
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,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(SchemaPath path) {
350 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
351 for (QName pathElement : path.pathFromRoot) {
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 List<DataSchemaNode> 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 List<CaseSchemaNode> cases = new ArrayList(child.cases.values);
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.values, path)»
773 def casesTree(Collection<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 = Iterables.getLast(path.pathFromRoot)
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.values»
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.groupingPath, usesNode.groupingPath.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,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.values)»
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, SchemaPath schemaPath, EffectiveModelContext ctx, DataNodeContainer dataNode) {
1212 val List<QName> path = Lists.newArrayList(schemaPath.pathFromRoot);
1213 val StringBuilder pathString = new StringBuilder()
1214 if (schemaPath.absolute) {
1215 pathString.append('/')
1218 val QName qname = path.get(0)
1219 var Object parent = ctx.findModule(qname.module).orElse(null)
1222 if (parent instanceof DataNodeContainer) {
1223 var SchemaNode node = parent.getDataChildByName(name)
1224 if (node === null && (parent instanceof Module)) {
1225 val notifications = (parent as Module).notifications;
1226 for (notification : notifications) {
1227 if (notification.QName.equals(name)) {
1232 if (node === null && (parent instanceof Module)) {
1233 val rpcs = (parent as Module).rpcs;
1235 if (rpc.QName.equals(name)) {
1241 val pathElementModule = ctx.findModule(name.module).get
1242 val String moduleName = pathElementModule.name
1243 pathString.append(moduleName)
1244 pathString.append(':')
1245 pathString.append(name.localName)
1246 pathString.append('/')
1247 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1248 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode];
1249 if(caseNode !== null) {
1250 pathString.append("(case)");
1251 pathString.append(caseNode.QName.localName);
1257 return pathString.toString;
1261 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1262 «IF childNodes !== null && !childNodes.empty»
1263 «FOR child : childNodes.values»
1264 «childInfo(child, childNodes)»
1269 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1270 «val String path = nodeSchemaPathToPath(node, childNodes)»
1281 private def CharSequence treeSet(Collection<? extends DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1282 «IF childNodes !== null && !childNodes.empty»
1284 «FOR child : childNodes»
1293 def listKeys(ListSchemaNode node) '''
1294 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1297 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1300 «listItem("Argument", ext.argument)»
1304 private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1309 /* #################### RESTRICTIONS #################### */
1310 private def restrictions(TypeDefinition<?> type) '''
1311 «type.baseType.toBaseStmt»
1316 private def dispatch toLength(TypeDefinition<?> type) {
1319 private def dispatch toLength(BinaryTypeDefinition type) '''
1320 «type.lengthConstraint.toLengthStmt»
1323 private def dispatch toLength(StringTypeDefinition type) '''
1324 «type.lengthConstraint.toLengthStmt»
1327 private def dispatch toRange(TypeDefinition<?> type) {
1330 private def dispatch toRange(DecimalTypeDefinition type) '''
1331 «type.rangeConstraint.toRangeStmt»
1334 private def dispatch toRange(Int8TypeDefinition type) '''
1335 «type.rangeConstraint.toRangeStmt»
1338 private def dispatch toRange(Int16TypeDefinition type) '''
1339 «type.rangeConstraint.toRangeStmt»
1342 private def dispatch toRange(Int32TypeDefinition type) '''
1343 «type.rangeConstraint.toRangeStmt»
1346 private def dispatch toRange(Int64TypeDefinition type) '''
1347 «type.rangeConstraint.toRangeStmt»
1350 private def dispatch toRange(Uint8TypeDefinition type) '''
1351 «type.rangeConstraint.toRangeStmt»
1354 private def dispatch toRange(Uint16TypeDefinition type) '''
1355 «type.rangeConstraint.toRangeStmt»
1358 private def dispatch toRange(Uint32TypeDefinition type) '''
1359 «type.rangeConstraint.toRangeStmt»
1362 private def dispatch toRange(Uint64TypeDefinition type) '''
1363 «type.rangeConstraint.toRangeStmt»
1366 def toLengthStmt(Optional<LengthConstraint> lengths) '''
1367 «IF lengths.isPresent»
1368 «listItem("Length restrictions:")»
1370 «FOR length : lengths.get.allowedRanges.asRanges»
1372 «IF length.lowerEndpoint == length.upperEndpoint»
1373 «length.lowerEndpoint»
1375 <«length.lowerEndpoint», «length.upperEndpoint»>
1383 def toRangeStmt(Optional<? extends RangeConstraint<?>> constraint) '''
1384 «IF constraint.present»
1385 «listItem("Range restrictions:")»
1387 «FOR range : constraint.get.allowedRanges.asRanges»
1389 «IF range.lowerEndpoint == range.upperEndpoint»
1390 «range.lowerEndpoint»
1392 <«range.lowerEndpoint», «range.upperEndpoint»>
1400 def toBaseStmt(TypeDefinition<?> baseType) '''
1401 «IF baseType !== null»
1402 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1408 /* #################### UTILITY #################### */
1409 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1410 private def italic(CharSequence str) '''<i>«str»</i>'''
1412 def CharSequence descAndRefLi(SchemaNode node) '''
1413 «listItem("Description", node.description.orElse(null))»
1414 «listItem("Reference", node.reference.orElse(null))»
1417 def CharSequence descAndRef(SchemaNode node) '''
1419 «IF node.reference !== null»
1420 Reference «node.reference»
1424 private def listItem(String value) '''
1425 «IF value !== null && !value.empty»
1432 private def listItem(String name, String value) '''
1433 «IF value !== null && !value.empty»
1440 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1441 if (node instanceof ChoiceSchemaNode || node instanceof CaseSchemaNode) {
1445 val path = node.path.pathFromRoot
1446 val absolute = node.path.absolute;
1447 var StringBuilder result = new StringBuilder
1451 if (path !== null && !path.empty) {
1452 val List<QName> actual = new ArrayList()
1454 for (pathElement : path) {
1455 actual.add(pathElement)
1456 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1457 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof CaseSchemaNode)) {
1458 result.append(pathElement.localName)
1459 if (i != path.size - 1) {
1466 return result.toString
1469 private def dispatch addedByInfo(SchemaNode node) '''
1472 private def dispatch addedByInfo(DataSchemaNode node) '''
1473 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1476 private def dispatch isAddedBy(SchemaNode node) {
1480 private def dispatch isAddedBy(DataSchemaNode node) {
1481 if (node.augmenting || node.addedByUses) {
1488 private def dispatch nodeName(SchemaNode node) '''
1490 «italic(node.QName.localName)»«node.addedByInfo»
1492 «node.QName.localName»«node.addedByInfo»
1496 private def dispatch nodeName(ContainerSchemaNode node) '''
1498 «strong(italic(node.QName.localName))»«node.addedByInfo»
1500 «strong(node.QName.localName)»«node.addedByInfo»
1504 private def dispatch nodeName(ListSchemaNode node) '''
1506 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1508 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»