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.ElementCountConstraintAware
38 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
43 import org.opendaylight.yangtools.yang.model.api.Module
44 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext
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 SchemaContext ctx;
77 StringBuilder augmentChildNodesAsString
79 DataSchemaNode lastNodeInTargetPath = null
81 def generate(BuildContext buildContext, SchemaContext context, File targetPath, Set<Module> modulesToGen)
86 for (module : modulesToGen) {
87 add(generateDocumentation(buildContext, module, context));
92 def generateDocumentation(BuildContext buildContext, Module module, SchemaContext 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(e.getMessage());
109 def generate(Module module, SchemaContext ctx) '''
113 <title>«module.name»</title>
121 def body(Module module, SchemaContext 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 Set<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 Set<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, SchemaContext 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<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, SchemaContext 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 Set<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 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
822 «FOR typeDef : typeDefinitions»
823 «typeDef.restrictions»
827 «FOR grouping : dataNode.groupings»
828 «grouping.printGrouping»
832 «FOR child : dataNode.childNodes»
833 «child.printSchemaNodeInfo»
841 def String typeAnchorLink(SchemaPath path, CharSequence text) {
843 val lastElement = Iterables.getLast(path.pathFromRoot)
844 val ns = lastElement.namespace
845 if (ns == this.currentModule.namespace) {
846 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
848 return '''(«ns»)«text»'''
849 //to enable external (import) links
850 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
855 def CharSequence printBaseInfo(SchemaNode node) {
856 if(node instanceof LeafSchemaNode) {
858 «printInfo(node, "leaf")»
859 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
860 «listItem("units", node.type.units.orElse(null))»
861 «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))»
864 } else if(node instanceof LeafListSchemaNode) {
866 «printInfo(node, "leaf-list")»
867 «listItem("type", node.type?.QName.localName)»
870 } else if(node instanceof ListSchemaNode) {
872 «printInfo(node, "list")»
873 «FOR keyDef : node.keyDefinition»
874 «listItem("key definition", keyDef.localName)»
878 } else if(node instanceof ChoiceSchemaNode) {
880 «printInfo(node, "choice")»
881 «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))»
882 «FOR caseNode : node.cases.values»
883 «caseNode.printSchemaNodeInfo»
887 } else if(node instanceof CaseSchemaNode) {
889 «printInfo(node, "case")»
892 } else if(node instanceof ContainerSchemaNode) {
894 «printInfo(node, "container")»
897 } else if(node instanceof AnyxmlSchemaNode) {
899 «printInfo(node, "anyxml")»
905 def CharSequence printInfo(SchemaNode node, String nodeType) {
907 «IF node instanceof AugmentationTarget»
910 <li id="«node.path.schemaPathToId»">
911 «nodeType»: «node.QName.localName»
916 «strong(listItem(nodeType, node.QName.localName))»
919 «listItem("description", node.description.orElse(null))»
920 «listItem("reference", node.reference.orElse(null))»
921 «IF node instanceof DataSchemaNode»
922 «IF node.whenCondition.present»
923 «listItem("when condition", node.whenCondition.get.toString)»
926 «IF node instanceof ElementCountConstraintAware»
927 «IF node.elementCountConstraint.present»
928 «val constraint = node.elementCountConstraint.get»
929 «listItem("min elements", constraint.minElements?.toString)»
930 «listItem("max elements", constraint.maxElements?.toString)»
936 def CharSequence printUses(UsesNode usesNode) {
938 «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
942 «FOR sp : usesNode.refines.keySet»
943 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
947 «FOR augment : usesNode.augmentations»
948 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
954 def CharSequence printGrouping(GroupingDefinition grouping) {
956 «strong(listItem("grouping", grouping.QName.localName))»
960 def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
961 val anyxmlNodes = nodes.filter(AnyxmlSchemaNode)
962 val leafNodes = nodes.filter(LeafSchemaNode)
963 val leafListNodes = nodes.filter(LeafListSchemaNode)
964 val choices = nodes.filter(ChoiceSchemaNode)
965 val cases = nodes.filter(CaseSchemaNode)
966 val containers = nodes.filter(ContainerSchemaNode)
967 val lists = nodes.filter(ListSchemaNode)
969 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
970 <h3>Direct children</h3>
972 «FOR childNode : anyxmlNodes»
973 «childNode.printShortInfo(level,path)»
975 «FOR childNode : leafNodes»
976 «childNode.printShortInfo(level,path)»
978 «FOR childNode : leafListNodes»
979 «childNode.printShortInfo(level,path)»
981 «FOR childNode : containers»
982 «childNode.printShortInfo(level,path)»
984 «FOR childNode : lists»
985 «childNode.printShortInfo(level,path)»
990 «IF path.pathArguments.iterator.hasNext»
992 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
995 «FOR childNode : containers»
996 «childNode.printInfo(level,path)»
998 «FOR childNode : lists»
999 «childNode.printInfo(level,path)»
1001 «FOR childNode : choices»
1002 «childNode.printInfo(level,path)»
1004 «FOR childNode : cases»
1005 «childNode.printInfo(level,path)»
1010 def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
1012 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1016 def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1017 <!-- Child nodes -->
1019 <!-- «node.QName.localName» -->
1020 «node.asXmlExampleTag(identifier)»
1025 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1026 «node.QName.xmlExampleTag("...")»
1029 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1030 <!-- This node could appear multiple times -->
1031 «node.QName.xmlExampleTag("...")»
1034 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1035 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1036 «node.QName.xmlExampleTag("...")»
1040 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1041 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1042 <!-- This node could appear multiple times -->
1043 «node.QName.xmlExampleTag("...")»
1047 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1052 def xmlExampleTag(QName name, CharSequence data) {
1053 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1056 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1059 def header(int level,YangInstanceIdentifier name) '''
1060 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1061 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1067 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1068 «header(level+1,node.QName)»
1071 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1072 «val newPath = path.append(node)»
1073 «header(level,newPath)»
1076 <dd>«newPath.asXmlPath»</dd>
1077 <dt>Restconf path</dt>
1078 <dd>«code(newPath.asRestconfPath)»</dd>
1080 «node.childNodes.printChildren(level,newPath)»
1083 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1084 «val newPath = path.append(node)»
1085 «header(level,newPath)»
1088 <dd>«newPath.asXmlPath»</dd>
1089 <dt>Restconf path</dt>
1090 <dd>«code(newPath.asRestconfPath)»</dd>
1092 «node.childNodes.printChildren(level,newPath)»
1095 private def dispatch CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1096 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases.values)»
1097 «choiceCases.printChildren(level, path)»
1100 private def dispatch CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) '''
1101 «node.childNodes.printChildren(level, path)»
1106 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1107 val newPath = path.append(node);
1109 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1111 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1117 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1118 val newPath = path.append(node);
1120 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1122 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1128 def CharSequence printShortInfo(AnyxmlSchemaNode node, int level, YangInstanceIdentifier path) {
1130 <li>«strong((node.QName.localName))» (anyxml)
1132 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1133 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1139 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1141 <li>«strong((node.QName.localName))» (leaf)
1143 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1144 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1150 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1152 <li>«strong((node.QName.localName))» (leaf-list)
1154 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1160 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1162 <a href="#«anchor»">«text»</a>
1166 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1167 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1171 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1172 return identifier.node(node.QName);
1175 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1176 val keyValues = new LinkedHashMap<QName,Object>();
1177 if(node.keyDefinition !== null) {
1178 for(definition : node.keyDefinition) {
1179 keyValues.put(definition,new Object);
1183 return identifier.node(NodeIdentifierWithPredicates.of(node.QName, keyValues));
1187 def asXmlPath(YangInstanceIdentifier identifier) {
1191 def asRestconfPath(YangInstanceIdentifier identifier) {
1192 val it = new StringBuilder();
1193 append(currentModule.name)
1195 var previous = false;
1196 for(arg : identifier.pathArguments) {
1197 if(previous) append('/')
1198 append(arg.nodeType.localName);
1200 if(arg instanceof NodeIdentifierWithPredicates) {
1201 for(qname : arg.keySet) {
1203 append(qname.localName)
1212 private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1213 val List<QName> path = Lists.newArrayList(schemaPath.pathFromRoot);
1214 val StringBuilder pathString = new StringBuilder()
1215 if (schemaPath.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<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 List<QName> 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 if (node.augmenting || node.addedByUses) {
1489 private def dispatch nodeName(SchemaNode node) '''
1491 «italic(node.QName.localName)»«node.addedByInfo»
1493 «node.QName.localName»«node.addedByInfo»
1497 private def dispatch nodeName(ContainerSchemaNode node) '''
1499 «strong(italic(node.QName.localName))»«node.addedByInfo»
1501 «strong(node.QName.localName)»«node.addedByInfo»
1505 private def dispatch nodeName(ListSchemaNode node) '''
1507 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1509 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»