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.yangtools.yang.unified.doc.generator
10 import com.google.common.collect.Iterables
11 import java.io.BufferedWriter
13 import java.io.IOException
14 import java.io.OutputStreamWriter
15 import java.text.SimpleDateFormat
16 import java.util.ArrayList
17 import java.util.Collection
18 import java.util.HashMap
19 import java.util.HashSet
20 import java.util.LinkedHashMap
24 import org.opendaylight.yangtools.yang.common.QName
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
27 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
28 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget
29 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
30 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
35 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
36 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
39 import org.opendaylight.yangtools.yang.model.api.Module
40 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext
42 import org.opendaylight.yangtools.yang.model.api.SchemaNode
43 import org.opendaylight.yangtools.yang.model.api.SchemaPath
44 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
45 import org.opendaylight.yangtools.yang.model.api.UsesNode
46 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
47 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
48 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
49 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
50 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
51 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
52 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
53 import org.opendaylight.yangtools.yang.model.util.ExtendedType
54 import org.slf4j.Logger
55 import org.slf4j.LoggerFactory
56 import org.sonatype.plexus.build.incremental.BuildContext
57 import org.sonatype.plexus.build.incremental.DefaultBuildContext
62 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
63 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
64 static val BuildContext CTX = new DefaultBuildContext();
65 var Module currentModule;
66 val Map<String, String> imports = new HashMap();
67 var SchemaContext ctx;
69 StringBuilder augmentChildNodesAsString
71 DataSchemaNode lastNodeInTargetPath = null
73 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
77 for (module : modulesToGen) {
78 add(generateDocumentation(module, context));
83 def generateDocumentation(Module module, SchemaContext ctx) {
84 val destination = new File(path, '''«module.name».html''')
86 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
88 val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
89 val bw = new BufferedWriter(fw)
90 currentModule = module;
91 bw.append(generate(module, ctx));
94 } catch (IOException e) {
95 LOG.error(e.getMessage());
100 def generate(Module module, SchemaContext ctx) '''
104 <title>«module.name»</title>
112 def body(Module module, SchemaContext ctx) '''
115 «typeDefinitionsSummary(module)»
116 «identitiesSummary(module)»
117 «groupingsSummary(module)»
118 «augmentationsSummary(module, ctx)»
119 «objectsSummary(module)»
120 «notificationsSummary(module)»
121 «rpcsSummary(module)»
122 «extensionsSummary(module)»
123 «featuresSummary(module)»
125 «typeDefinitions(module)»
135 «notifications(module)»
137 «augmentations(module, ctx)»
148 private def typeDefinitionsSummary(Module module) {
149 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
150 if (typedefs.empty) {
155 <h3>Type Definitions Summary</h3>
161 «FOR typedef : typedefs»
164 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
167 «typedef.description»
176 def typeDefinitions(Module module) {
177 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
178 if (typedefs.empty) {
182 <h2>Type Definitions</h2>
184 «FOR typedef : typedefs»
186 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
188 «typedef.descAndRefLi»
189 «typedef.restrictions»
197 private def identities(Module module) {
198 if (module.identities.empty) {
204 «FOR identity : module.identities»
206 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
208 «identity.descAndRefLi»
209 «IF identity.baseIdentity !== null»
210 «listItem("base", identity.baseIdentity.QName.localName)»
219 private def identitiesSummary(Module module) {
220 if (module.identities.empty) {
224 <h3>Identities Summary</h3>
230 «FOR identity : module.identities»
233 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
236 «identity.description»
244 private def groupings(Module module) {
245 if (module.groupings.empty) {
251 «FOR grouping : module.groupings»
253 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
255 «grouping.descAndRefLi»
256 «FOR childNode : grouping.childNodes»
257 «childNode.printSchemaNodeInfo»
266 private def groupingsSummary(Module module) {
267 if (module.groupings.empty) {
271 <h3>Groupings Summary</h3>
277 «FOR grouping : module.groupings»
280 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
283 «grouping.description»
291 def dataStore(Module module) {
292 if (module.childNodes.empty) {
296 <h2>Datastore Structure</h2>
301 def augmentations(Module module, SchemaContext context) {
302 if (module.augmentations.empty) {
306 <h2>Augmentations</h2>
309 «FOR augment : module.augmentations»
311 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
312 Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
313 «augment.description»
314 Status: «strong(String.valueOf(augment.status))»
315 «IF augment.reference !== null»
316 Reference «augment.reference»
318 «IF augment.whenCondition !== null»
319 When «augment.whenCondition.toString»
321 «FOR childNode : augment.childNodes»
322 «childNode.printSchemaNodeInfo»
326 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
327 «printNodeChildren(parseTargetPath(augment.targetPath))»
334 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
335 augmentChildNodesAsString = new StringBuilder();
336 augmentChildNodesAsString.append(printNodeChildren(childNodes))
340 private def parseTargetPath(SchemaPath path) {
341 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
342 for (QName pathElement : path.pathFromRoot) {
343 val module = ctx.findModuleByNamespaceAndRevision(pathElement.namespace, pathElement.revision);
344 if (module !== null) {
345 var foundNode = module.getDataChildByName(pathElement)
346 if(foundNode == null) {
347 val child = nodes.last
348 if (child instanceof DataNodeContainer) {
349 val dataContNode = child as DataNodeContainer
350 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
353 if(foundNode != null) {
354 nodes.add(foundNode);
359 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
362 val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
363 targetPathNodes.add(lastNodeInTargetPath)
365 return targetPathNodes
368 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<DataSchemaNode> childNodes) {
369 for(child : childNodes) {
370 if (child.QName.equals(findingNode))
374 for(child : childNodes) {
375 if(child instanceof ContainerSchemaNode) {
376 val contChild = child as ContainerSchemaNode
377 val foundChild = findNodeInChildNodes(findingNode, contChild.childNodes)
378 if (foundChild != null)
381 else if(child instanceof ListSchemaNode) {
382 val listChild = child as ListSchemaNode
383 val foundChild = findNodeInChildNodes(findingNode, listChild.childNodes)
384 if (foundChild != null)
390 private def printNodeChildren(List<DataSchemaNode> childNodes) {
391 if (childNodes.empty) {
398 «printAugmentedNode(childNodes.get(0))»
403 private def CharSequence printAugmentedNode(DataSchemaNode child) {
405 if(child instanceof ChoiceCaseNode)
410 «IF child instanceof ContainerSchemaNode»
411 «printContainerNode(child as ContainerSchemaNode)»
413 «IF child instanceof AnyXmlSchemaNode»
414 «printAnyXmlNode(child as AnyXmlSchemaNode)»
416 «IF child instanceof LeafSchemaNode»
417 «printLeafNode(child as LeafSchemaNode)»
419 «IF child instanceof LeafListSchemaNode»
420 «printLeafListNode(child as LeafListSchemaNode)»
422 «IF child instanceof ListSchemaNode»
423 «printListNode(child as ListSchemaNode)»
425 «IF child instanceof ChoiceNode»
426 «printChoiceNode(child as ChoiceNode)»
431 private def printChoiceNode(ChoiceNode child) {
432 val List<ChoiceCaseNode> cases = new ArrayList(child.cases);
434 val ChoiceCaseNode aCase = cases.get(0)
435 for(caseChildNode : aCase.childNodes)
436 printAugmentedNode(caseChildNode)
440 private def printListNode(ListSchemaNode listNode) {
443 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
444 «FOR child : listNode.childNodes»
445 «printAugmentedNode(child)»
447 </«listNode.QName.localName»>
451 private def printContainerNode(ContainerSchemaNode containerNode) {
454 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
455 «FOR child : containerNode.childNodes»
456 «printAugmentedNode(child)»
458 </«containerNode.QName.localName»>
462 private def printLeafListNode(LeafListSchemaNode leafListNode) {
465 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
466 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
467 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
471 private def printAnyXmlNode(AnyXmlSchemaNode anyXmlNode) {
474 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
478 private def printLeafNode(LeafSchemaNode leafNode) {
481 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
485 private def augmentationsSummary(Module module, SchemaContext context) {
486 if (module.augmentations.empty) {
490 <h3>Augmentations Summary</h3>
496 «FOR augment : module.augmentations»
499 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
500 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
503 «augment.description»
511 def notifications(Module module) {
512 val Set<NotificationDefinition> notificationdefs = module.notifications
513 if (notificationdefs.empty) {
518 <h2>Notifications</h2>
519 «FOR notificationdef : notificationdefs»
521 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
522 «notificationdef.descAndRef»
523 «FOR childNode : notificationdef.childNodes»
524 «childNode.printSchemaNodeInfo»
530 private def notificationsSummary(Module module) {
531 if (module.notifications.empty) {
535 <h3>Notifications Summary</h3>
541 «FOR notification : module.notifications»
544 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
547 «notification.description»
555 def rpcs(Module module) {
556 if (module.rpcs.empty) {
561 <h2>RPC Definitions</h2>
562 «FOR rpc : module.rpcs»
563 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
566 «rpc.input.printSchemaNodeInfo»
567 «rpc.output.printSchemaNodeInfo»
574 private def rpcsSummary(Module module) {
575 if (module.rpcs.empty) {
579 <h3>RPCs Summary</h3>
585 «FOR rpc : module.rpcs»
588 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
599 def extensions(Module module) {
600 if (module.extensionSchemaNodes.empty) {
605 «FOR ext : module.extensionSchemaNodes»
607 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
614 private def extensionsSummary(Module module) {
615 if (module.extensionSchemaNodes.empty) {
619 <h3>Extensions Summary</h3>
625 «FOR ext : module.extensionSchemaNodes»
628 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
639 def features(Module module) {
640 if (module.features.empty) {
647 «FOR feature : module.features»
649 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
651 «feature.descAndRefLi»
659 private def featuresSummary(Module module) {
660 if (module.features.empty) {
664 <h3>Features Summary</h3>
670 «FOR feature : module.features»
673 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
676 «feature.description»
684 private def objectsSummary(Module module) {
685 if (module.childNodes.empty) {
689 <h3>Child Nodes Summary</h3>
695 «FOR childNode : module.childNodes»
698 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
701 «childNode.description»
709 def header(Module module)
711 <h1>«module.name»</h1>
713 <h2>Base Information</h2>
716 <td>«strong("prefix")»</td>
717 <td>«module.prefix»</td>
720 <td>«strong("namespace")»</td>
721 <td>«module.namespace»</td>
724 <td>«strong("revision")»</td>
725 <td>«REVISION_FORMAT.format(module.revision)»</td>
728 <td>«strong("description")»</td>
729 <td>«module.description»</td>
732 <td>«strong("yang-version")»</td>
733 <td>«module.yangVersion»</td>
736 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
737 «imp.prefix»:«imp.moduleName»«IF imp.revision !== null» «REVISION_FORMAT.format(imp.revision)»«ENDIF»;
743 def CharSequence schemaPathToId(SchemaPath path) {
745 return '''«FOR qName : path.path SEPARATOR "/"»«qName.localName»«ENDFOR»'''
749 def code(String string) '''<code>«string»</code>'''
751 def process(Module module) {
752 throw new UnsupportedOperationException("TODO: auto-generated method stub")
755 def CharSequence tree(Module module) '''
756 «strong(module.name)»
757 «module.childNodes.treeSet(YangInstanceIdentifier.builder.toInstance())»
760 private def dispatch CharSequence tree(ChoiceNode node,YangInstanceIdentifier path) '''
761 «node.nodeName» (choice)
762 «casesTree(node.cases,path)»
765 def casesTree(Set<ChoiceCaseNode> nodes,YangInstanceIdentifier path) '''
770 «node.childNodes.treeSet(path)»
776 private def dispatch CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) '''
780 private def dispatch CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) '''
781 «val newPath = path.append(node)»
782 «localLink(newPath,node.nodeName)»
783 «node.childNodes.treeSet(newPath)»
786 private def dispatch CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
787 «val newPath = path.append(node)»
788 «localLink(newPath,node.nodeName)»
789 «node.childNodes.treeSet(newPath)»
792 def CharSequence childNodes(Module module) '''
793 «val childNodes = module.childNodes»
794 «IF !childNodes.nullOrEmpty»
797 «childNodes.printChildren(3,YangInstanceIdentifier.builder().toInstance())»
801 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
805 «IF node instanceof DataNodeContainer»
806 «val dataNode = node as DataNodeContainer»
808 «FOR usesNode : dataNode.uses»
813 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
814 «FOR typeDef : typeDefinitions»
815 «typeDef.restrictions»
819 «FOR grouping : dataNode.groupings»
820 «grouping.printGrouping»
824 «FOR child : dataNode.childNodes»
825 «child.printSchemaNodeInfo»
833 def String typeAnchorLink(SchemaPath path, CharSequence text) {
835 val lastElement = Iterables.getLast(path.pathFromRoot)
836 val ns = lastElement.namespace
837 if (ns == this.currentModule.namespace) {
838 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
840 return '''(«ns»)«text»'''
841 //to enable external (import) links
842 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
847 def CharSequence printBaseInfo(SchemaNode node) {
848 if(node instanceof LeafSchemaNode) {
849 val LeafSchemaNode leafNode = (node as LeafSchemaNode)
851 «printInfo(node, "leaf")»
852 «listItem("type", typeAnchorLink(leafNode.type?.path, leafNode.type.QName.localName))»
853 «listItem("units", leafNode.units)»
854 «listItem("default", leafNode.^default)»
857 } else if(node instanceof LeafListSchemaNode) {
858 val LeafListSchemaNode leafListNode = (node as LeafListSchemaNode)
860 «printInfo(node, "leaf-list")»
861 «listItem("type", leafListNode.type?.QName.localName)»
864 } else if(node instanceof ListSchemaNode) {
865 val ListSchemaNode listNode = (node as ListSchemaNode)
867 «printInfo(node, "list")»
868 «FOR keyDef : listNode.keyDefinition»
869 «listItem("key definition", keyDef.localName)»
873 } else if(node instanceof ChoiceNode) {
874 val ChoiceNode choiceNode = (node as ChoiceNode)
876 «printInfo(node, "choice")»
877 «listItem("default case", choiceNode.defaultCase)»
878 «FOR caseNode : choiceNode.cases»
879 «caseNode.printSchemaNodeInfo»
883 } else if(node instanceof ChoiceCaseNode) {
885 «printInfo(node, "case")»
888 } else if(node instanceof ContainerSchemaNode) {
890 «printInfo(node, "container")»
893 } else if(node instanceof AnyXmlSchemaNode) {
895 «printInfo(node, "anyxml")»
901 def CharSequence printInfo(SchemaNode node, String nodeType) {
903 «IF node instanceof AugmentationTarget»
906 <li id="«node.path.schemaPathToId»">
907 «nodeType»: «node.QName.localName»
912 «strong(listItem(nodeType, node.QName.localName))»
915 «listItem("description", node.description)»
916 «listItem("reference", node.reference)»
917 «IF node instanceof DataSchemaNode»
918 «listItem("when condition", (node as DataSchemaNode).constraints.whenCondition?.toString)»
919 «listItem("min elements", (node as DataSchemaNode).constraints.minElements?.toString)»
920 «listItem("max elements", (node as DataSchemaNode).constraints.maxElements?.toString)»
925 def CharSequence printUses(UsesNode usesNode) {
927 «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
931 «FOR sp : usesNode.refines.keySet»
932 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
936 «FOR augment : usesNode.augmentations»
937 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
943 def CharSequence printGrouping(GroupingDefinition grouping) {
945 «strong(listItem("grouping", grouping.QName.localName))»
949 def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
950 val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
951 val leafNodes = nodes.filter(LeafSchemaNode)
952 val leafListNodes = nodes.filter(LeafListSchemaNode)
953 val choices = nodes.filter(ChoiceNode)
954 val cases = nodes.filter(ChoiceCaseNode)
955 val containers = nodes.filter(ContainerSchemaNode)
956 val lists = nodes.filter(ListSchemaNode)
958 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
959 <h3>Direct children</h3>
961 «FOR childNode : anyxmlNodes»
962 «childNode.printShortInfo(level,path)»
964 «FOR childNode : leafNodes»
965 «childNode.printShortInfo(level,path)»
967 «FOR childNode : leafListNodes»
968 «childNode.printShortInfo(level,path)»
970 «FOR childNode : containers»
971 «childNode.printShortInfo(level,path)»
973 «FOR childNode : lists»
974 «childNode.printShortInfo(level,path)»
979 «IF path.pathArguments.iterator.hasNext»
981 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
984 «FOR childNode : containers»
985 «childNode.printInfo(level,path)»
987 «FOR childNode : lists»
988 «childNode.printInfo(level,path)»
990 «FOR childNode : choices»
991 «childNode.printInfo(level,path)»
993 «FOR childNode : cases»
994 «childNode.printInfo(level,path)»
999 def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
1001 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
1005 def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1006 <!-- Child nodes -->
1008 <!-- «node.QName.localName» -->
1009 «node.asXmlExampleTag(identifier)»
1014 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1015 «node.QName.xmlExampleTag("...")»
1018 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1019 <!-- This node could appear multiple times -->
1020 «node.QName.xmlExampleTag("...")»
1023 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1024 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1025 «node.QName.xmlExampleTag("...")»
1029 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1030 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1031 <!-- This node could appear multiple times -->
1032 «node.QName.xmlExampleTag("...")»
1036 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1041 def xmlExampleTag(QName name, CharSequence data) {
1042 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1045 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1048 def header(int level,YangInstanceIdentifier name) '''
1049 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1050 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1056 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1057 «header(level+1,node.QName)»
1060 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1061 «val newPath = path.append(node)»
1062 «header(level,newPath)»
1065 <dd>«newPath.asXmlPath»</dd>
1066 <dt>Restconf path</dt>
1067 <dd>«code(newPath.asRestconfPath)»</dd>
1069 «node.childNodes.printChildren(level,newPath)»
1072 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1073 «val newPath = path.append(node)»
1074 «header(level,newPath)»
1077 <dd>«newPath.asXmlPath»</dd>
1078 <dt>Restconf path</dt>
1079 <dd>«code(newPath.asRestconfPath)»</dd>
1081 «node.childNodes.printChildren(level,newPath)»
1084 private def dispatch CharSequence printInfo(ChoiceNode node, int level, YangInstanceIdentifier path) '''
1085 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1086 «choiceCases.printChildren(level,path)»
1089 private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, YangInstanceIdentifier path) '''
1090 «node.childNodes.printChildren(level,path)»
1095 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1096 val newPath = path.append(node);
1098 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1100 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1106 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1107 val newPath = path.append(node);
1109 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1111 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1117 def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, YangInstanceIdentifier path) {
1119 <li>«strong((node.QName.localName))» (anyxml)
1121 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1122 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1128 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1130 <li>«strong((node.QName.localName))» (leaf)
1132 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1133 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1139 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1141 <li>«strong((node.QName.localName))» (leaf-list)
1143 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1149 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1151 <a href="#«anchor»">«text»</a>
1155 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1156 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1160 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1161 return identifier.node(node.QName);
1164 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1165 val keyValues = new LinkedHashMap<QName,Object>();
1166 if(node.keyDefinition !== null) {
1167 for(definition : node.keyDefinition) {
1168 keyValues.put(definition,new Object);
1172 return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1176 def asXmlPath(YangInstanceIdentifier identifier) {
1180 def asRestconfPath(YangInstanceIdentifier identifier) {
1181 val it = new StringBuilder();
1182 append(currentModule.name)
1184 var previous = false;
1185 for(arg : identifier.pathArguments) {
1186 if(previous) append("/")
1187 append(arg.nodeType.localName);
1189 if(arg instanceof NodeIdentifierWithPredicates) {
1190 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
1191 for(qname : nodeIdentifier.getKeyValues.keySet) {
1193 append(qname.localName)
1202 private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1203 val List<QName> path = schemaPath.path
1204 val StringBuilder pathString = new StringBuilder()
1205 if (schemaPath.absolute) {
1206 pathString.append('/')
1209 val QName qname = path.get(0)
1210 var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1213 if (parent instanceof DataNodeContainer) {
1214 var SchemaNode node = (parent as DataNodeContainer).getDataChildByName(name)
1215 if (node == null && (parent instanceof Module)) {
1216 val notifications = (parent as Module).notifications;
1217 for (notification : notifications) {
1218 if (notification.QName.equals(name)) {
1223 if (node == null && (parent instanceof Module)) {
1224 val rpcs = (parent as Module).rpcs;
1226 if (rpc.QName.equals(name)) {
1232 val pathElementModule = ctx.findModuleByNamespaceAndRevision(name.namespace, name.revision)
1233 val String moduleName = pathElementModule.name
1234 pathString.append(moduleName)
1235 pathString.append(':')
1236 pathString.append(name.localName)
1237 pathString.append('/')
1238 if(node instanceof ChoiceNode && dataNode !== null) {
1239 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1240 if(caseNode !== null) {
1241 pathString.append("(case)");
1242 pathString.append(caseNode.QName.localName);
1248 return pathString.toString;
1252 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1253 «IF childNodes !== null && !childNodes.empty»
1254 «FOR child : childNodes.values»
1255 «childInfo(child, childNodes)»
1260 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1261 «val String path = nodeSchemaPathToPath(node, childNodes)»
1272 private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1273 «IF childNodes !== null && !childNodes.empty»
1275 «FOR child : childNodes»
1284 def listKeys(ListSchemaNode node) '''
1285 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1288 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1291 «listItem("Argument", ext.argument)»
1295 private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1300 /* #################### RESTRICTIONS #################### */
1301 private def restrictions(TypeDefinition<?> type) '''
1302 «type.baseType.toBaseStmt»
1307 private def dispatch toLength(TypeDefinition<?> type) {
1310 private def dispatch toLength(BinaryTypeDefinition type) '''
1311 «type.lengthConstraints.toLengthStmt»
1314 private def dispatch toLength(StringTypeDefinition type) '''
1315 «type.lengthConstraints.toLengthStmt»
1318 private def dispatch toLength(ExtendedType type) '''
1319 «type.lengthConstraints.toLengthStmt»
1322 private def dispatch toRange(TypeDefinition<?> type) {
1325 private def dispatch toRange(DecimalTypeDefinition type) '''
1326 «type.rangeConstraints.toRangeStmt»
1329 private def dispatch toRange(IntegerTypeDefinition type) '''
1330 «type.rangeConstraints.toRangeStmt»
1333 private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1334 «type.rangeConstraints.toRangeStmt»
1337 private def dispatch toRange(ExtendedType type) '''
1338 «type.rangeConstraints.toRangeStmt»
1341 def toLengthStmt(Collection<LengthConstraint> lengths) '''
1342 «IF lengths != null && !lengths.empty»
1343 «listItem("Length restrictions:")»
1345 «FOR length : lengths»
1347 «IF length.min == length.max»
1350 <«length.min», «length.max»>
1358 def toRangeStmt(Collection<RangeConstraint> ranges) '''
1359 «IF ranges != null && !ranges.empty»
1360 «listItem("Range restrictions:")»
1362 «FOR range : ranges»
1364 «IF range.min == range.max»
1367 <«range.min», «range.max»>
1375 def toBaseStmt(TypeDefinition<?> baseType) '''
1376 «IF baseType != null»
1377 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1383 /* #################### UTILITY #################### */
1384 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1385 private def italic(CharSequence str) '''<i>«str»</i>'''
1387 def CharSequence descAndRefLi(SchemaNode node) '''
1388 «listItem("Description", node.description)»
1389 «listItem("Reference", node.reference)»
1392 def CharSequence descAndRef(SchemaNode node) '''
1394 «IF node.reference !== null»
1395 Reference «node.reference»
1399 private def listItem(String value) '''
1400 «IF value !== null && !value.empty»
1407 private def listItem(String name, String value) '''
1408 «IF value !== null && !value.empty»
1415 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1416 if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
1420 val path = node.path.path
1421 val absolute = node.path.absolute;
1422 var StringBuilder result = new StringBuilder
1426 if (path !== null && !path.empty) {
1427 val List<QName> actual = new ArrayList()
1429 for (pathElement : path) {
1430 actual.add(pathElement)
1431 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1432 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1433 result.append(pathElement.localName)
1434 if (i != path.size - 1) {
1441 return result.toString
1444 private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1445 for (node : source) {
1446 destination.put(node.path, node)
1447 if (node instanceof DataNodeContainer) {
1448 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1450 if (node instanceof ChoiceNode) {
1451 val List<DataSchemaNode> choiceCases = new ArrayList()
1452 for (caseNode : (node as ChoiceNode).cases) {
1453 choiceCases.add(caseNode)
1455 collectChildNodes(choiceCases, destination)
1460 private def dispatch addedByInfo(SchemaNode node) '''
1463 private def dispatch addedByInfo(DataSchemaNode node) '''
1464 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1467 private def dispatch isAddedBy(SchemaNode node) {
1471 private def dispatch isAddedBy(DataSchemaNode node) {
1472 if (node.augmenting || node.addedByUses) {
1479 private def dispatch nodeName(SchemaNode node) '''
1481 «italic(node.QName.localName)»«node.addedByInfo»
1483 «node.QName.localName»«node.addedByInfo»
1487 private def dispatch nodeName(ContainerSchemaNode node) '''
1489 «strong(italic(node.QName.localName))»«node.addedByInfo»
1491 «strong(node.QName.localName)»«node.addedByInfo»
1495 private def dispatch nodeName(ListSchemaNode node) '''
1497 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1499 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»