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.opendaylight.yangtools.yang.common.QName
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
29 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
30 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
31 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
36 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware
37 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
38 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
39 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
40 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
41 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
42 import org.opendaylight.yangtools.yang.model.api.Module
43 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext
45 import org.opendaylight.yangtools.yang.model.api.SchemaNode
46 import org.opendaylight.yangtools.yang.model.api.SchemaPath
47 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.UsesNode
49 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
50 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
51 import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition
52 import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition
53 import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition
54 import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition
55 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
56 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
57 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
58 import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition
59 import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition
60 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition
61 import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition
62 import org.slf4j.Logger
63 import org.slf4j.LoggerFactory
64 import org.sonatype.plexus.build.incremental.BuildContext
68 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
70 val Map<String, String> imports = new HashMap();
71 var Module currentModule;
72 var SchemaContext ctx;
75 StringBuilder augmentChildNodesAsString
77 DataSchemaNode lastNodeInTargetPath = null
79 def generate(BuildContext buildContext, SchemaContext context, File targetPath, Set<Module> modulesToGen)
84 for (module : modulesToGen) {
85 add(generateDocumentation(buildContext, module, context));
90 def generateDocumentation(BuildContext buildContext, Module module, SchemaContext ctx) {
91 val destination = new File(path, '''«module.name».html''')
93 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
95 val fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
96 val bw = new BufferedWriter(fw)
97 currentModule = module;
98 bw.append(generate(module, ctx));
101 } catch (IOException e) {
102 LOG.error("Failed to emit file {}", destination, e);
107 def generate(Module module, SchemaContext ctx) '''
111 <title>«module.name»</title>
119 def body(Module module, SchemaContext ctx) '''
122 «typeDefinitionsSummary(module)»
123 «identitiesSummary(module)»
124 «groupingsSummary(module)»
125 «augmentationsSummary(module, ctx)»
126 «objectsSummary(module)»
127 «notificationsSummary(module)»
128 «rpcsSummary(module)»
129 «extensionsSummary(module)»
130 «featuresSummary(module)»
132 «typeDefinitions(module)»
142 «notifications(module)»
144 «augmentations(module, ctx)»
155 private def typeDefinitionsSummary(Module module) {
156 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
157 if (typedefs.empty) {
162 <h3>Type Definitions Summary</h3>
168 «FOR typedef : typedefs»
171 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
174 «typedef.description»
183 def typeDefinitions(Module module) {
184 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
185 if (typedefs.empty) {
189 <h2>Type Definitions</h2>
191 «FOR typedef : typedefs»
193 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
195 «typedef.descAndRefLi»
196 «typedef.restrictions»
204 private def identities(Module module) {
205 if (module.identities.empty) {
211 «FOR identity : module.identities»
213 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
215 «identity.descAndRefLi»
216 «IF !identity.baseIdentities.isEmpty»
217 «listItem("base", identity.baseIdentities.get(0).QName.localName)»
226 private def identitiesSummary(Module module) {
227 if (module.identities.empty) {
231 <h3>Identities Summary</h3>
237 «FOR identity : module.identities»
240 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
243 «identity.description»
251 private def groupings(Module module) {
252 if (module.groupings.empty) {
258 «FOR grouping : module.groupings»
260 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
262 «grouping.descAndRefLi»
263 «FOR childNode : grouping.childNodes»
264 «childNode.printSchemaNodeInfo»
273 private def groupingsSummary(Module module) {
274 if (module.groupings.empty) {
278 <h3>Groupings Summary</h3>
284 «FOR grouping : module.groupings»
287 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
290 «grouping.description»
298 def dataStore(Module module) {
299 if (module.childNodes.empty) {
303 <h2>Datastore Structure</h2>
308 def augmentations(Module module, SchemaContext context) {
309 if (module.augmentations.empty) {
313 <h2>Augmentations</h2>
316 «FOR augment : module.augmentations»
318 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
319 Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
320 «augment.description»
321 Status: «strong(String.valueOf(augment.status))»
322 «IF augment.reference !== null»
323 Reference «augment.reference»
325 «IF augment.whenCondition !== null»
326 When «augment.whenCondition.toString»
328 «FOR childNode : augment.childNodes»
329 «childNode.printSchemaNodeInfo»
333 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
334 «printNodeChildren(parseTargetPath(augment.targetPath))»
341 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
342 augmentChildNodesAsString = new StringBuilder();
343 augmentChildNodesAsString.append(printNodeChildren(childNodes))
347 private def parseTargetPath(SchemaPath path) {
348 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
349 for (QName pathElement : path.pathFromRoot) {
350 val module = ctx.findModule(pathElement.module)
351 if (module.isPresent) {
352 var foundNode = module.get.getDataChildByName(pathElement)
353 if (foundNode === null) {
354 val child = nodes.last
355 if (child instanceof DataNodeContainer) {
356 val dataContNode = child as DataNodeContainer
357 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
360 if (foundNode !== null) {
361 nodes.add(foundNode);
366 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
369 val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
370 targetPathNodes.add(lastNodeInTargetPath)
372 return targetPathNodes
375 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<DataSchemaNode> childNodes) {
376 for(child : childNodes) {
377 if (child.QName.equals(findingNode))
381 for(child : childNodes) {
382 if (child instanceof ContainerSchemaNode) {
383 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
384 if (foundChild !== null)
386 } else if (child instanceof ListSchemaNode) {
387 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
388 if (foundChild !== null)
394 private def printNodeChildren(List<DataSchemaNode> childNodes) {
395 if (childNodes.empty) {
402 «printAugmentedNode(childNodes.get(0))»
407 private def CharSequence printAugmentedNode(DataSchemaNode child) {
409 if(child instanceof CaseSchemaNode)
414 «IF child instanceof ContainerSchemaNode»
415 «printContainerNode(child)»
417 «IF child instanceof AnyXmlSchemaNode»
418 «printAnyXmlNode(child)»
420 «IF child instanceof LeafSchemaNode»
421 «printLeafNode(child)»
423 «IF child instanceof LeafListSchemaNode»
424 «printLeafListNode(child)»
426 «IF child instanceof ListSchemaNode»
427 «printListNode(child)»
429 «IF child instanceof ChoiceSchemaNode»
430 «printChoiceNode(child)»
435 private def printChoiceNode(ChoiceSchemaNode child) {
436 val List<CaseSchemaNode> cases = new ArrayList(child.cases.values);
438 val CaseSchemaNode aCase = cases.get(0)
439 for(caseChildNode : aCase.childNodes)
440 printAugmentedNode(caseChildNode)
444 private def printListNode(ListSchemaNode listNode) {
447 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
448 «FOR child : listNode.childNodes»
449 «printAugmentedNode(child)»
451 </«listNode.QName.localName»>
455 private def printContainerNode(ContainerSchemaNode containerNode) {
458 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
459 «FOR child : containerNode.childNodes»
460 «printAugmentedNode(child)»
462 </«containerNode.QName.localName»>
466 private def printLeafListNode(LeafListSchemaNode leafListNode) {
469 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
470 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
471 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
475 private def printAnyXmlNode(AnyXmlSchemaNode anyXmlNode) {
478 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
482 private def printLeafNode(LeafSchemaNode leafNode) {
485 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
489 private def augmentationsSummary(Module module, SchemaContext context) {
490 if (module.augmentations.empty) {
494 <h3>Augmentations Summary</h3>
500 «FOR augment : module.augmentations»
503 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
504 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
507 «augment.description»
515 def notifications(Module module) {
516 val Set<NotificationDefinition> notificationdefs = module.notifications
517 if (notificationdefs.empty) {
522 <h2>Notifications</h2>
523 «FOR notificationdef : notificationdefs»
525 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
526 «notificationdef.descAndRef»
527 «FOR childNode : notificationdef.childNodes»
528 «childNode.printSchemaNodeInfo»
534 private def notificationsSummary(Module module) {
535 if (module.notifications.empty) {
539 <h3>Notifications Summary</h3>
545 «FOR notification : module.notifications»
548 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
551 «notification.description»
559 def rpcs(Module module) {
560 if (module.rpcs.empty) {
565 <h2>RPC Definitions</h2>
566 «FOR rpc : module.rpcs»
567 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
570 «rpc.input.printSchemaNodeInfo»
571 «rpc.output.printSchemaNodeInfo»
578 private def rpcsSummary(Module module) {
579 if (module.rpcs.empty) {
583 <h3>RPCs Summary</h3>
589 «FOR rpc : module.rpcs»
592 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
603 def extensions(Module module) {
604 if (module.extensionSchemaNodes.empty) {
609 «FOR ext : module.extensionSchemaNodes»
611 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
618 private def extensionsSummary(Module module) {
619 if (module.extensionSchemaNodes.empty) {
623 <h3>Extensions Summary</h3>
629 «FOR ext : module.extensionSchemaNodes»
632 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
643 def features(Module module) {
644 if (module.features.empty) {
651 «FOR feature : module.features»
653 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
655 «feature.descAndRefLi»
663 private def featuresSummary(Module module) {
664 if (module.features.empty) {
668 <h3>Features Summary</h3>
674 «FOR feature : module.features»
677 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
680 «feature.description»
688 private def objectsSummary(Module module) {
689 if (module.childNodes.empty) {
693 <h3>Child Nodes Summary</h3>
699 «FOR childNode : module.childNodes»
702 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
705 «childNode.description»
713 def header(Module module)
715 <h1>«module.name»</h1>
717 <h2>Base Information</h2>
720 <td>«strong("prefix")»</td>
721 <td>«module.prefix»</td>
724 <td>«strong("namespace")»</td>
725 <td>«module.namespace»</td>
728 «IF module.revision.isPresent»
729 <td>«strong("revision")»</td>
730 <td>«module.revision.get.toString»</td>
734 <td>«strong("description")»</td>
735 <td>«module.description»</td>
738 <td>«strong("yang-version")»</td>
739 <td>«module.yangVersion»</td>
742 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
743 «imp.prefix»:«imp.moduleName»«IF imp.revision.isPresent» «imp.revision.get.toString»«ENDIF»;
749 def CharSequence schemaPathToId(SchemaPath path) {
751 return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«ENDFOR»'''
755 def code(String string) '''<code>«string»</code>'''
757 def process(Module module) {
758 throw new UnsupportedOperationException("TODO: auto-generated method stub")
761 def CharSequence tree(Module module) '''
762 «strong(module.name)»
763 «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())»
766 private def dispatch CharSequence tree(ChoiceSchemaNode node,YangInstanceIdentifier path) '''
767 «node.nodeName» (choice)
768 «casesTree(node.cases.values, path)»
771 def casesTree(Collection<CaseSchemaNode> nodes, YangInstanceIdentifier path) '''
776 «node.childNodes.treeSet(path)»
782 private def dispatch CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) '''
786 private def dispatch CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) '''
787 «val newPath = path.append(node)»
788 «localLink(newPath,node.nodeName)»
789 «node.childNodes.treeSet(newPath)»
792 private def dispatch CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
793 «val newPath = path.append(node)»
794 «localLink(newPath,node.nodeName)»
795 «node.childNodes.treeSet(newPath)»
798 def CharSequence childNodes(Module module) '''
799 «val childNodes = module.childNodes»
800 «IF !childNodes.nullOrEmpty»
803 «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())»
807 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
811 «IF node instanceof DataNodeContainer»
812 «val dataNode = node as DataNodeContainer»
814 «FOR usesNode : dataNode.uses»
819 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
820 «FOR typeDef : typeDefinitions»
821 «typeDef.restrictions»
825 «FOR grouping : dataNode.groupings»
826 «grouping.printGrouping»
830 «FOR child : dataNode.childNodes»
831 «child.printSchemaNodeInfo»
839 def String typeAnchorLink(SchemaPath path, CharSequence text) {
841 val lastElement = Iterables.getLast(path.pathFromRoot)
842 val ns = lastElement.namespace
843 if (ns == this.currentModule.namespace) {
844 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
846 return '''(«ns»)«text»'''
847 //to enable external (import) links
848 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
853 def CharSequence printBaseInfo(SchemaNode node) {
854 if(node instanceof LeafSchemaNode) {
856 «printInfo(node, "leaf")»
857 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
858 «listItem("units", node.type.units.orElse(null))»
859 «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))»
862 } else if(node instanceof LeafListSchemaNode) {
864 «printInfo(node, "leaf-list")»
865 «listItem("type", node.type?.QName.localName)»
868 } else if(node instanceof ListSchemaNode) {
870 «printInfo(node, "list")»
871 «FOR keyDef : node.keyDefinition»
872 «listItem("key definition", keyDef.localName)»
876 } else if(node instanceof ChoiceSchemaNode) {
878 «printInfo(node, "choice")»
879 «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))»
880 «FOR caseNode : node.cases.values»
881 «caseNode.printSchemaNodeInfo»
885 } else if(node instanceof CaseSchemaNode) {
887 «printInfo(node, "case")»
890 } else if(node instanceof ContainerSchemaNode) {
892 «printInfo(node, "container")»
895 } else if(node instanceof AnyXmlSchemaNode) {
897 «printInfo(node, "anyxml")»
903 def CharSequence printInfo(SchemaNode node, String nodeType) {
905 «IF node instanceof AugmentationTarget»
908 <li id="«node.path.schemaPathToId»">
909 «nodeType»: «node.QName.localName»
914 «strong(listItem(nodeType, node.QName.localName))»
917 «listItem("description", node.description.orElse(null))»
918 «listItem("reference", node.reference.orElse(null))»
919 «IF node instanceof DataSchemaNode»
920 «IF node.whenCondition.present»
921 «listItem("when condition", node.whenCondition.get.toString)»
924 «IF node instanceof ElementCountConstraintAware»
925 «IF node.elementCountConstraint.present»
926 «val constraint = node.elementCountConstraint.get»
927 «listItem("min elements", constraint.minElements?.toString)»
928 «listItem("max elements", constraint.maxElements?.toString)»
934 def CharSequence printUses(UsesNode usesNode) {
936 «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
940 «FOR sp : usesNode.refines.keySet»
941 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
945 «FOR augment : usesNode.augmentations»
946 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
952 def CharSequence printGrouping(GroupingDefinition grouping) {
954 «strong(listItem("grouping", grouping.QName.localName))»
958 def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
959 val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
960 val leafNodes = nodes.filter(LeafSchemaNode)
961 val leafListNodes = nodes.filter(LeafListSchemaNode)
962 val choices = nodes.filter(ChoiceSchemaNode)
963 val cases = nodes.filter(CaseSchemaNode)
964 val containers = nodes.filter(ContainerSchemaNode)
965 val lists = nodes.filter(ListSchemaNode)
967 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
968 <h3>Direct children</h3>
970 «FOR childNode : anyxmlNodes»
971 «childNode.printShortInfo(level,path)»
973 «FOR childNode : leafNodes»
974 «childNode.printShortInfo(level,path)»
976 «FOR childNode : leafListNodes»
977 «childNode.printShortInfo(level,path)»
979 «FOR childNode : containers»
980 «childNode.printShortInfo(level,path)»
982 «FOR childNode : lists»
983 «childNode.printShortInfo(level,path)»
988 «IF path.pathArguments.iterator.hasNext»
990 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
993 «FOR childNode : containers»
994 «childNode.printInfo(level,path)»
996 «FOR childNode : lists»
997 «childNode.printInfo(level,path)»
999 «FOR childNode : choices»
1000 «childNode.printInfo(level,path)»
1002 «FOR childNode : cases»
1003 «childNode.printInfo(level,path)»
1008 def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
1010 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1014 def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1015 <!-- Child nodes -->
1017 <!-- «node.QName.localName» -->
1018 «node.asXmlExampleTag(identifier)»
1023 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1024 «node.QName.xmlExampleTag("...")»
1027 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1028 <!-- This node could appear multiple times -->
1029 «node.QName.xmlExampleTag("...")»
1032 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1033 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1034 «node.QName.xmlExampleTag("...")»
1038 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1039 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1040 <!-- This node could appear multiple times -->
1041 «node.QName.xmlExampleTag("...")»
1045 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1050 def xmlExampleTag(QName name, CharSequence data) {
1051 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1054 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1057 def header(int level,YangInstanceIdentifier name) '''
1058 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1059 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1065 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1066 «header(level+1,node.QName)»
1069 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1070 «val newPath = path.append(node)»
1071 «header(level,newPath)»
1074 <dd>«newPath.asXmlPath»</dd>
1075 <dt>Restconf path</dt>
1076 <dd>«code(newPath.asRestconfPath)»</dd>
1078 «node.childNodes.printChildren(level,newPath)»
1081 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1082 «val newPath = path.append(node)»
1083 «header(level,newPath)»
1086 <dd>«newPath.asXmlPath»</dd>
1087 <dt>Restconf path</dt>
1088 <dd>«code(newPath.asRestconfPath)»</dd>
1090 «node.childNodes.printChildren(level,newPath)»
1093 private def dispatch CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1094 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases.values)»
1095 «choiceCases.printChildren(level, path)»
1098 private def dispatch CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) '''
1099 «node.childNodes.printChildren(level, path)»
1104 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1105 val newPath = path.append(node);
1107 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1109 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1115 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1116 val newPath = path.append(node);
1118 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1120 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1126 def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, YangInstanceIdentifier path) {
1128 <li>«strong((node.QName.localName))» (anyxml)
1130 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1131 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1137 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1139 <li>«strong((node.QName.localName))» (leaf)
1141 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1142 <li>mandatory: «strong(String.valueOf(node.mandatory))»</li>
1148 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1150 <li>«strong((node.QName.localName))» (leaf-list)
1152 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1158 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1160 <a href="#«anchor»">«text»</a>
1164 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1165 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1169 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1170 return identifier.node(node.QName);
1173 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1174 val keyValues = new LinkedHashMap<QName,Object>();
1175 if(node.keyDefinition !== null) {
1176 for(definition : node.keyDefinition) {
1177 keyValues.put(definition,new Object);
1181 return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1185 def asXmlPath(YangInstanceIdentifier identifier) {
1189 def asRestconfPath(YangInstanceIdentifier identifier) {
1190 val it = new StringBuilder();
1191 append(currentModule.name)
1193 var previous = false;
1194 for(arg : identifier.pathArguments) {
1195 if(previous) append('/')
1196 append(arg.nodeType.localName);
1198 if(arg instanceof NodeIdentifierWithPredicates) {
1199 for(qname : arg.getKeyValues.keySet) {
1201 append(qname.localName)
1210 private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1211 val List<QName> path = Lists.newArrayList(schemaPath.pathFromRoot);
1212 val StringBuilder pathString = new StringBuilder()
1213 if (schemaPath.absolute) {
1214 pathString.append('/')
1217 val QName qname = path.get(0)
1218 var Object parent = ctx.findModule(qname.module).orElse(null)
1221 if (parent instanceof DataNodeContainer) {
1222 var SchemaNode node = parent.getDataChildByName(name)
1223 if (node === null && (parent instanceof Module)) {
1224 val notifications = (parent as Module).notifications;
1225 for (notification : notifications) {
1226 if (notification.QName.equals(name)) {
1231 if (node === null && (parent instanceof Module)) {
1232 val rpcs = (parent as Module).rpcs;
1234 if (rpc.QName.equals(name)) {
1240 val pathElementModule = ctx.findModule(name.module).get
1241 val String moduleName = pathElementModule.name
1242 pathString.append(moduleName)
1243 pathString.append(':')
1244 pathString.append(name.localName)
1245 pathString.append('/')
1246 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1247 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode];
1248 if(caseNode !== null) {
1249 pathString.append("(case)");
1250 pathString.append(caseNode.QName.localName);
1256 return pathString.toString;
1260 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1261 «IF childNodes !== null && !childNodes.empty»
1262 «FOR child : childNodes.values»
1263 «childInfo(child, childNodes)»
1268 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1269 «val String path = nodeSchemaPathToPath(node, childNodes)»
1280 private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1281 «IF childNodes !== null && !childNodes.empty»
1283 «FOR child : childNodes»
1292 def listKeys(ListSchemaNode node) '''
1293 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1296 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1299 «listItem("Argument", ext.argument)»
1303 private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1308 /* #################### RESTRICTIONS #################### */
1309 private def restrictions(TypeDefinition<?> type) '''
1310 «type.baseType.toBaseStmt»
1315 private def dispatch toLength(TypeDefinition<?> type) {
1318 private def dispatch toLength(BinaryTypeDefinition type) '''
1319 «type.lengthConstraint.toLengthStmt»
1322 private def dispatch toLength(StringTypeDefinition type) '''
1323 «type.lengthConstraint.toLengthStmt»
1326 private def dispatch toRange(TypeDefinition<?> type) {
1329 private def dispatch toRange(DecimalTypeDefinition type) '''
1330 «type.rangeConstraint.toRangeStmt»
1333 private def dispatch toRange(Int8TypeDefinition type) '''
1334 «type.rangeConstraint.toRangeStmt»
1337 private def dispatch toRange(Int16TypeDefinition type) '''
1338 «type.rangeConstraint.toRangeStmt»
1341 private def dispatch toRange(Int32TypeDefinition type) '''
1342 «type.rangeConstraint.toRangeStmt»
1345 private def dispatch toRange(Int64TypeDefinition type) '''
1346 «type.rangeConstraint.toRangeStmt»
1349 private def dispatch toRange(Uint8TypeDefinition type) '''
1350 «type.rangeConstraint.toRangeStmt»
1353 private def dispatch toRange(Uint16TypeDefinition type) '''
1354 «type.rangeConstraint.toRangeStmt»
1357 private def dispatch toRange(Uint32TypeDefinition type) '''
1358 «type.rangeConstraint.toRangeStmt»
1361 private def dispatch toRange(Uint64TypeDefinition type) '''
1362 «type.rangeConstraint.toRangeStmt»
1365 def toLengthStmt(Optional<LengthConstraint> lengths) '''
1366 «IF lengths.isPresent»
1367 «listItem("Length restrictions:")»
1369 «FOR length : lengths.get.allowedRanges.asRanges»
1371 «IF length.lowerEndpoint == length.upperEndpoint»
1372 «length.lowerEndpoint»
1374 <«length.lowerEndpoint», «length.upperEndpoint»>
1382 def toRangeStmt(Optional<? extends RangeConstraint<?>> constraint) '''
1383 «IF constraint.present»
1384 «listItem("Range restrictions:")»
1386 «FOR range : constraint.get.allowedRanges.asRanges»
1388 «IF range.lowerEndpoint == range.upperEndpoint»
1389 «range.lowerEndpoint»
1391 <«range.lowerEndpoint», «range.upperEndpoint»>
1399 def toBaseStmt(TypeDefinition<?> baseType) '''
1400 «IF baseType !== null»
1401 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1407 /* #################### UTILITY #################### */
1408 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1409 private def italic(CharSequence str) '''<i>«str»</i>'''
1411 def CharSequence descAndRefLi(SchemaNode node) '''
1412 «listItem("Description", node.description.orElse(null))»
1413 «listItem("Reference", node.reference.orElse(null))»
1416 def CharSequence descAndRef(SchemaNode node) '''
1418 «IF node.reference !== null»
1419 Reference «node.reference»
1423 private def listItem(String value) '''
1424 «IF value !== null && !value.empty»
1431 private def listItem(String name, String value) '''
1432 «IF value !== null && !value.empty»
1439 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1440 if (node instanceof ChoiceSchemaNode || node instanceof CaseSchemaNode) {
1444 val path = node.path.pathFromRoot
1445 val absolute = node.path.absolute;
1446 var StringBuilder result = new StringBuilder
1450 if (path !== null && !path.empty) {
1451 val List<QName> actual = new ArrayList()
1453 for (pathElement : path) {
1454 actual.add(pathElement)
1455 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1456 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof CaseSchemaNode)) {
1457 result.append(pathElement.localName)
1458 if (i != path.size - 1) {
1465 return result.toString
1468 private def dispatch addedByInfo(SchemaNode node) '''
1471 private def dispatch addedByInfo(DataSchemaNode node) '''
1472 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1475 private def dispatch isAddedBy(SchemaNode node) {
1479 private def dispatch isAddedBy(DataSchemaNode node) {
1480 if (node.augmenting || node.addedByUses) {
1487 private def dispatch nodeName(SchemaNode node) '''
1489 «italic(node.QName.localName)»«node.addedByInfo»
1491 «node.QName.localName»«node.addedByInfo»
1495 private def dispatch nodeName(ContainerSchemaNode node) '''
1497 «strong(italic(node.QName.localName))»«node.addedByInfo»
1499 «strong(node.QName.localName)»«node.addedByInfo»
1503 private def dispatch nodeName(ListSchemaNode node) '''
1505 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1507 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»