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.ChoiceSchemaNode
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.slf4j.Logger
54 import org.slf4j.LoggerFactory
55 import org.sonatype.plexus.build.incremental.BuildContext
56 import org.sonatype.plexus.build.incremental.DefaultBuildContext
61 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
62 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
63 static val BuildContext CTX = new DefaultBuildContext();
64 var Module currentModule;
65 val Map<String, String> imports = new HashMap();
66 var SchemaContext ctx;
68 StringBuilder augmentChildNodesAsString
70 DataSchemaNode lastNodeInTargetPath = null
72 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
76 for (module : modulesToGen) {
77 add(generateDocumentation(module, context));
82 def generateDocumentation(Module module, SchemaContext ctx) {
83 val destination = new File(path, '''«module.name».html''')
85 module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)]
87 val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
88 val bw = new BufferedWriter(fw)
89 currentModule = module;
90 bw.append(generate(module, ctx));
93 } catch (IOException e) {
94 LOG.error(e.getMessage());
99 def generate(Module module, SchemaContext ctx) '''
103 <title>«module.name»</title>
111 def body(Module module, SchemaContext ctx) '''
114 «typeDefinitionsSummary(module)»
115 «identitiesSummary(module)»
116 «groupingsSummary(module)»
117 «augmentationsSummary(module, ctx)»
118 «objectsSummary(module)»
119 «notificationsSummary(module)»
120 «rpcsSummary(module)»
121 «extensionsSummary(module)»
122 «featuresSummary(module)»
124 «typeDefinitions(module)»
134 «notifications(module)»
136 «augmentations(module, ctx)»
147 private def typeDefinitionsSummary(Module module) {
148 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
149 if (typedefs.empty) {
154 <h3>Type Definitions Summary</h3>
160 «FOR typedef : typedefs»
163 «anchorLink(typedef.QName.localName, strong(typedef.QName.localName))»
166 «typedef.description»
175 def typeDefinitions(Module module) {
176 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
177 if (typedefs.empty) {
181 <h2>Type Definitions</h2>
183 «FOR typedef : typedefs»
185 <h3 id="«typedef.QName.localName»">«typedef.QName.localName»</h3>
187 «typedef.descAndRefLi»
188 «typedef.restrictions»
196 private def identities(Module module) {
197 if (module.identities.empty) {
203 «FOR identity : module.identities»
205 <h3 id="«identity.QName.localName»">«identity.QName.localName»</h3>
207 «identity.descAndRefLi»
208 «IF identity.baseIdentity !== null»
209 «listItem("base", identity.baseIdentity.QName.localName)»
218 private def identitiesSummary(Module module) {
219 if (module.identities.empty) {
223 <h3>Identities Summary</h3>
229 «FOR identity : module.identities»
232 «anchorLink(identity.QName.localName, strong(identity.QName.localName))»
235 «identity.description»
243 private def groupings(Module module) {
244 if (module.groupings.empty) {
250 «FOR grouping : module.groupings»
252 <h3 id="«grouping.QName.localName»">«grouping.QName.localName»</h3>
254 «grouping.descAndRefLi»
255 «FOR childNode : grouping.childNodes»
256 «childNode.printSchemaNodeInfo»
265 private def groupingsSummary(Module module) {
266 if (module.groupings.empty) {
270 <h3>Groupings Summary</h3>
276 «FOR grouping : module.groupings»
279 «anchorLink(grouping.QName.localName, strong(grouping.QName.localName))»
282 «grouping.description»
290 def dataStore(Module module) {
291 if (module.childNodes.empty) {
295 <h2>Datastore Structure</h2>
300 def augmentations(Module module, SchemaContext context) {
301 if (module.augmentations.empty) {
305 <h2>Augmentations</h2>
308 «FOR augment : module.augmentations»
310 <h3 id="«schemaPathToString(module, augment.targetPath, context, augment)»">
311 Target [«typeAnchorLink(augment.targetPath,schemaPathToString(module, augment.targetPath, context, augment))»]</h3>
312 «augment.description»
313 Status: «strong(String.valueOf(augment.status))»
314 «IF augment.reference !== null»
315 Reference «augment.reference»
317 «IF augment.whenCondition !== null»
318 When «augment.whenCondition.toString»
320 «FOR childNode : augment.childNodes»
321 «childNode.printSchemaNodeInfo»
325 «createAugmentChildNodesAsString(new ArrayList(augment.childNodes))»
326 «printNodeChildren(parseTargetPath(augment.targetPath))»
333 private def createAugmentChildNodesAsString(List<DataSchemaNode> childNodes) {
334 augmentChildNodesAsString = new StringBuilder();
335 augmentChildNodesAsString.append(printNodeChildren(childNodes))
339 private def parseTargetPath(SchemaPath path) {
340 val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
341 for (QName pathElement : path.pathFromRoot) {
342 val module = ctx.findModuleByNamespaceAndRevision(pathElement.namespace, pathElement.revision);
343 if (module !== null) {
344 var foundNode = module.getDataChildByName(pathElement)
345 if(foundNode == null) {
346 val child = nodes.last
347 if (child instanceof DataNodeContainer) {
348 val dataContNode = child as DataNodeContainer
349 foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes)
352 if(foundNode != null) {
353 nodes.add(foundNode);
358 lastNodeInTargetPath = nodes.get(nodes.size() - 1)
361 val List<DataSchemaNode> targetPathNodes = new ArrayList<DataSchemaNode>();
362 targetPathNodes.add(lastNodeInTargetPath)
364 return targetPathNodes
367 private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable<DataSchemaNode> childNodes) {
368 for(child : childNodes) {
369 if (child.QName.equals(findingNode))
373 for(child : childNodes) {
374 if(child instanceof ContainerSchemaNode) {
375 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
376 if (foundChild != null)
379 else if(child instanceof ListSchemaNode) {
380 val foundChild = findNodeInChildNodes(findingNode, child.childNodes)
381 if (foundChild != null)
387 private def printNodeChildren(List<DataSchemaNode> childNodes) {
388 if (childNodes.empty) {
395 «printAugmentedNode(childNodes.get(0))»
400 private def CharSequence printAugmentedNode(DataSchemaNode child) {
402 if(child instanceof ChoiceCaseNode)
407 «IF child instanceof ContainerSchemaNode»
408 «printContainerNode(child)»
410 «IF child instanceof AnyXmlSchemaNode»
411 «printAnyXmlNode(child)»
413 «IF child instanceof LeafSchemaNode»
414 «printLeafNode(child)»
416 «IF child instanceof LeafListSchemaNode»
417 «printLeafListNode(child)»
419 «IF child instanceof ListSchemaNode»
420 «printListNode(child)»
422 «IF child instanceof ChoiceSchemaNode»
423 «printChoiceNode(child)»
428 private def printChoiceNode(ChoiceSchemaNode child) {
429 val List<ChoiceCaseNode> cases = new ArrayList(child.cases);
431 val ChoiceCaseNode aCase = cases.get(0)
432 for(caseChildNode : aCase.childNodes)
433 printAugmentedNode(caseChildNode)
437 private def printListNode(ListSchemaNode listNode) {
440 <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»>
441 «FOR child : listNode.childNodes»
442 «printAugmentedNode(child)»
444 </«listNode.QName.localName»>
448 private def printContainerNode(ContainerSchemaNode containerNode) {
451 <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»>
452 «FOR child : containerNode.childNodes»
453 «printAugmentedNode(child)»
455 </«containerNode.QName.localName»>
459 private def printLeafListNode(LeafListSchemaNode leafListNode) {
462 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
463 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
464 <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»>
468 private def printAnyXmlNode(AnyXmlSchemaNode anyXmlNode) {
471 <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»>
475 private def printLeafNode(LeafSchemaNode leafNode) {
478 <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»>
482 private def augmentationsSummary(Module module, SchemaContext context) {
483 if (module.augmentations.empty) {
487 <h3>Augmentations Summary</h3>
493 «FOR augment : module.augmentations»
496 «anchorLink(schemaPathToString(module, augment.targetPath, context, augment),
497 strong(schemaPathToString(module, augment.targetPath, context, augment)))»
500 «augment.description»
508 def notifications(Module module) {
509 val Set<NotificationDefinition> notificationdefs = module.notifications
510 if (notificationdefs.empty) {
515 <h2>Notifications</h2>
516 «FOR notificationdef : notificationdefs»
518 <h3 id="«notificationdef.path.schemaPathToId»">«notificationdef.nodeName»</h3>
519 «notificationdef.descAndRef»
520 «FOR childNode : notificationdef.childNodes»
521 «childNode.printSchemaNodeInfo»
527 private def notificationsSummary(Module module) {
528 if (module.notifications.empty) {
532 <h3>Notifications Summary</h3>
538 «FOR notification : module.notifications»
541 «anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))»
544 «notification.description»
552 def rpcs(Module module) {
553 if (module.rpcs.empty) {
558 <h2>RPC Definitions</h2>
559 «FOR rpc : module.rpcs»
560 <h3 id="«rpc.QName.localName»">«rpc.nodeName»</h3>
563 «rpc.input.printSchemaNodeInfo»
564 «rpc.output.printSchemaNodeInfo»
571 private def rpcsSummary(Module module) {
572 if (module.rpcs.empty) {
576 <h3>RPCs Summary</h3>
582 «FOR rpc : module.rpcs»
585 «anchorLink(rpc.QName.localName, strong(rpc.QName.localName))»
596 def extensions(Module module) {
597 if (module.extensionSchemaNodes.empty) {
602 «FOR ext : module.extensionSchemaNodes»
604 <h3 id="«ext.QName.localName»">«ext.nodeName»</h3>
611 private def extensionsSummary(Module module) {
612 if (module.extensionSchemaNodes.empty) {
616 <h3>Extensions Summary</h3>
622 «FOR ext : module.extensionSchemaNodes»
625 «anchorLink(ext.QName.localName, strong(ext.QName.localName))»
636 def features(Module module) {
637 if (module.features.empty) {
644 «FOR feature : module.features»
646 <h3 id="«feature.QName.localName»">«feature.QName.localName»</h3>
648 «feature.descAndRefLi»
656 private def featuresSummary(Module module) {
657 if (module.features.empty) {
661 <h3>Features Summary</h3>
667 «FOR feature : module.features»
670 «anchorLink(feature.QName.localName, strong(feature.QName.localName))»
673 «feature.description»
681 private def objectsSummary(Module module) {
682 if (module.childNodes.empty) {
686 <h3>Child Nodes Summary</h3>
692 «FOR childNode : module.childNodes»
695 «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))»
698 «childNode.description»
706 def header(Module module)
708 <h1>«module.name»</h1>
710 <h2>Base Information</h2>
713 <td>«strong("prefix")»</td>
714 <td>«module.prefix»</td>
717 <td>«strong("namespace")»</td>
718 <td>«module.namespace»</td>
721 <td>«strong("revision")»</td>
722 <td>«REVISION_FORMAT.format(module.revision)»</td>
725 <td>«strong("description")»</td>
726 <td>«module.description»</td>
729 <td>«strong("yang-version")»</td>
730 <td>«module.yangVersion»</td>
733 «FOR imp : module.imports BEFORE '''<td>«strong("imports")»</td><td>''' AFTER '''</td>'''»
734 «imp.prefix»:«imp.moduleName»«IF imp.revision !== null» «REVISION_FORMAT.format(imp.revision)»«ENDIF»;
740 def CharSequence schemaPathToId(SchemaPath path) {
742 return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«ENDFOR»'''
746 def code(String string) '''<code>«string»</code>'''
748 def process(Module module) {
749 throw new UnsupportedOperationException("TODO: auto-generated method stub")
752 def CharSequence tree(Module module) '''
753 «strong(module.name)»
754 «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())»
757 private def dispatch CharSequence tree(ChoiceSchemaNode node,YangInstanceIdentifier path) '''
758 «node.nodeName» (choice)
759 «casesTree(node.cases,path)»
762 def casesTree(Set<ChoiceCaseNode> nodes,YangInstanceIdentifier path) '''
767 «node.childNodes.treeSet(path)»
773 private def dispatch CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) '''
777 private def dispatch CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) '''
778 «val newPath = path.append(node)»
779 «localLink(newPath,node.nodeName)»
780 «node.childNodes.treeSet(newPath)»
783 private def dispatch CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier path) '''
784 «val newPath = path.append(node)»
785 «localLink(newPath,node.nodeName)»
786 «node.childNodes.treeSet(newPath)»
789 def CharSequence childNodes(Module module) '''
790 «val childNodes = module.childNodes»
791 «IF !childNodes.nullOrEmpty»
794 «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())»
798 def CharSequence printSchemaNodeInfo(DataSchemaNode node) {
802 «IF node instanceof DataNodeContainer»
803 «val dataNode = node as DataNodeContainer»
805 «FOR usesNode : dataNode.uses»
810 «val Set<TypeDefinition<?>> typeDefinitions = dataNode.typeDefinitions»
811 «FOR typeDef : typeDefinitions»
812 «typeDef.restrictions»
816 «FOR grouping : dataNode.groupings»
817 «grouping.printGrouping»
821 «FOR child : dataNode.childNodes»
822 «child.printSchemaNodeInfo»
830 def String typeAnchorLink(SchemaPath path, CharSequence text) {
832 val lastElement = Iterables.getLast(path.pathFromRoot)
833 val ns = lastElement.namespace
834 if (ns == this.currentModule.namespace) {
835 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
837 return '''(«ns»)«text»'''
838 //to enable external (import) links
839 //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
844 def CharSequence printBaseInfo(SchemaNode node) {
845 if(node instanceof LeafSchemaNode) {
847 «printInfo(node, "leaf")»
848 «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))»
849 «listItem("units", node.units)»
850 «listItem("default", node.^default)»
853 } else if(node instanceof LeafListSchemaNode) {
855 «printInfo(node, "leaf-list")»
856 «listItem("type", node.type?.QName.localName)»
859 } else if(node instanceof ListSchemaNode) {
861 «printInfo(node, "list")»
862 «FOR keyDef : node.keyDefinition»
863 «listItem("key definition", keyDef.localName)»
867 } else if(node instanceof ChoiceSchemaNode) {
869 «printInfo(node, "choice")»
870 «listItem("default case", node.defaultCase)»
871 «FOR caseNode : node.cases»
872 «caseNode.printSchemaNodeInfo»
876 } else if(node instanceof ChoiceCaseNode) {
878 «printInfo(node, "case")»
881 } else if(node instanceof ContainerSchemaNode) {
883 «printInfo(node, "container")»
886 } else if(node instanceof AnyXmlSchemaNode) {
888 «printInfo(node, "anyxml")»
894 def CharSequence printInfo(SchemaNode node, String nodeType) {
896 «IF node instanceof AugmentationTarget»
899 <li id="«node.path.schemaPathToId»">
900 «nodeType»: «node.QName.localName»
905 «strong(listItem(nodeType, node.QName.localName))»
908 «listItem("description", node.description)»
909 «listItem("reference", node.reference)»
910 «IF node instanceof DataSchemaNode»
911 «listItem("when condition", node.constraints.whenCondition?.toString)»
912 «listItem("min elements", node.constraints.minElements?.toString)»
913 «listItem("max elements", node.constraints.maxElements?.toString)»
918 def CharSequence printUses(UsesNode usesNode) {
920 «strong(listItem("uses", typeAnchorLink(usesNode.groupingPath, usesNode.groupingPath.pathTowardsRoot.iterator.next.localName)))»
924 «FOR sp : usesNode.refines.keySet»
925 «listItem("node name", usesNode.refines.get(sp).QName.localName)»
929 «FOR augment : usesNode.augmentations»
930 «typeAnchorLink(augment.targetPath,schemaPathToString(currentModule, augment.targetPath, ctx, augment))»
936 def CharSequence printGrouping(GroupingDefinition grouping) {
938 «strong(listItem("grouping", grouping.QName.localName))»
942 def CharSequence printChildren(Iterable<DataSchemaNode> nodes, int level, YangInstanceIdentifier path) {
943 val anyxmlNodes = nodes.filter(AnyXmlSchemaNode)
944 val leafNodes = nodes.filter(LeafSchemaNode)
945 val leafListNodes = nodes.filter(LeafListSchemaNode)
946 val choices = nodes.filter(ChoiceSchemaNode)
947 val cases = nodes.filter(ChoiceCaseNode)
948 val containers = nodes.filter(ContainerSchemaNode)
949 val lists = nodes.filter(ListSchemaNode)
951 «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»
952 <h3>Direct children</h3>
954 «FOR childNode : anyxmlNodes»
955 «childNode.printShortInfo(level,path)»
957 «FOR childNode : leafNodes»
958 «childNode.printShortInfo(level,path)»
960 «FOR childNode : leafListNodes»
961 «childNode.printShortInfo(level,path)»
963 «FOR childNode : containers»
964 «childNode.printShortInfo(level,path)»
966 «FOR childNode : lists»
967 «childNode.printShortInfo(level,path)»
972 «IF path.pathArguments.iterator.hasNext»
974 «nodes.xmlExample(path.pathArguments.last.nodeType,path)»
977 «FOR childNode : containers»
978 «childNode.printInfo(level,path)»
980 «FOR childNode : lists»
981 «childNode.printInfo(level,path)»
983 «FOR childNode : choices»
984 «childNode.printInfo(level,path)»
986 «FOR childNode : cases»
987 «childNode.printInfo(level,path)»
992 def CharSequence xmlExample(Iterable<DataSchemaNode> nodes, QName name,YangInstanceIdentifier path) '''
994 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
998 def CharSequence xmplExampleTags(Iterable<DataSchemaNode> nodes, YangInstanceIdentifier identifier) '''
1001 <!-- «node.QName.localName» -->
1002 «node.asXmlExampleTag(identifier)»
1007 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, YangInstanceIdentifier identifier) '''
1008 «node.QName.xmlExampleTag("...")»
1011 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, YangInstanceIdentifier identifier) '''
1012 <!-- This node could appear multiple times -->
1013 «node.QName.xmlExampleTag("...")»
1016 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, YangInstanceIdentifier identifier) '''
1017 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1018 «node.QName.xmlExampleTag("...")»
1022 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, YangInstanceIdentifier identifier) '''
1023 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
1024 <!-- This node could appear multiple times -->
1025 «node.QName.xmlExampleTag("...")»
1029 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) '''
1034 def xmlExampleTag(QName name, CharSequence data) {
1035 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
1038 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
1041 def header(int level,YangInstanceIdentifier name) '''
1042 <h«level» id="«FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
1043 «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
1049 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, YangInstanceIdentifier path) '''
1050 «header(level+1,node.QName)»
1053 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) '''
1054 «val newPath = path.append(node)»
1055 «header(level,newPath)»
1058 <dd>«newPath.asXmlPath»</dd>
1059 <dt>Restconf path</dt>
1060 <dd>«code(newPath.asRestconfPath)»</dd>
1062 «node.childNodes.printChildren(level,newPath)»
1065 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) '''
1066 «val newPath = path.append(node)»
1067 «header(level,newPath)»
1070 <dd>«newPath.asXmlPath»</dd>
1071 <dt>Restconf path</dt>
1072 <dd>«code(newPath.asRestconfPath)»</dd>
1074 «node.childNodes.printChildren(level,newPath)»
1077 private def dispatch CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) '''
1078 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
1079 «choiceCases.printChildren(level,path)»
1082 private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, YangInstanceIdentifier path) '''
1083 «node.childNodes.printChildren(level,path)»
1088 def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) {
1089 val newPath = path.append(node);
1091 <li>«strong(localLink(newPath,node.QName.localName))» (container)
1093 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1099 def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) {
1100 val newPath = path.append(node);
1102 <li>«strong(localLink(newPath,node.QName.localName))» (list)
1104 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1110 def CharSequence printShortInfo(AnyXmlSchemaNode node, int level, YangInstanceIdentifier path) {
1112 <li>«strong((node.QName.localName))» (anyxml)
1114 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1115 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1121 def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) {
1123 <li>«strong((node.QName.localName))» (leaf)
1125 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1126 <li>mandatory: «strong(String.valueOf(node.constraints.mandatory))»</li>
1132 def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) {
1134 <li>«strong((node.QName.localName))» (leaf-list)
1136 <li>configuration data: «strong(String.valueOf(node.configuration))»</li>
1142 def CharSequence anchorLink(CharSequence anchor, CharSequence text) {
1144 <a href="#«anchor»">«text»</a>
1148 def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) '''
1149 <a href="#«FOR cmp : identifier.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
1153 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) {
1154 return identifier.node(node.QName);
1157 private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) {
1158 val keyValues = new LinkedHashMap<QName,Object>();
1159 if(node.keyDefinition !== null) {
1160 for(definition : node.keyDefinition) {
1161 keyValues.put(definition,new Object);
1165 return identifier.node(new NodeIdentifierWithPredicates(node.QName, keyValues));
1169 def asXmlPath(YangInstanceIdentifier identifier) {
1173 def asRestconfPath(YangInstanceIdentifier identifier) {
1174 val it = new StringBuilder();
1175 append(currentModule.name)
1177 var previous = false;
1178 for(arg : identifier.pathArguments) {
1179 if(previous) append('/')
1180 append(arg.nodeType.localName);
1182 if(arg instanceof NodeIdentifierWithPredicates) {
1183 for(qname : arg.getKeyValues.keySet) {
1185 append(qname.localName)
1194 private def String schemaPathToString(Module module, SchemaPath schemaPath, SchemaContext ctx, DataNodeContainer dataNode) {
1195 val List<QName> path = schemaPath.path
1196 val StringBuilder pathString = new StringBuilder()
1197 if (schemaPath.absolute) {
1198 pathString.append('/')
1201 val QName qname = path.get(0)
1202 var Object parent = ctx.findModuleByNamespaceAndRevision(qname.namespace, qname.revision)
1205 if (parent instanceof DataNodeContainer) {
1206 var SchemaNode node = parent.getDataChildByName(name)
1207 if (node == null && (parent instanceof Module)) {
1208 val notifications = (parent as Module).notifications;
1209 for (notification : notifications) {
1210 if (notification.QName.equals(name)) {
1215 if (node == null && (parent instanceof Module)) {
1216 val rpcs = (parent as Module).rpcs;
1218 if (rpc.QName.equals(name)) {
1224 val pathElementModule = ctx.findModuleByNamespaceAndRevision(name.namespace, name.revision)
1225 val String moduleName = pathElementModule.name
1226 pathString.append(moduleName)
1227 pathString.append(':')
1228 pathString.append(name.localName)
1229 pathString.append('/')
1230 if(node instanceof ChoiceSchemaNode && dataNode !== null) {
1231 val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof ChoiceCaseNode];
1232 if(caseNode !== null) {
1233 pathString.append("(case)");
1234 pathString.append(caseNode.QName.localName);
1240 return pathString.toString;
1244 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
1245 «IF childNodes !== null && !childNodes.empty»
1246 «FOR child : childNodes.values»
1247 «childInfo(child, childNodes)»
1252 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
1253 «val String path = nodeSchemaPathToPath(node, childNodes)»
1264 private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, YangInstanceIdentifier path) '''
1265 «IF childNodes !== null && !childNodes.empty»
1267 «FOR child : childNodes»
1276 def listKeys(ListSchemaNode node) '''
1277 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
1280 private def CharSequence extensionInfo(ExtensionDefinition ext) '''
1283 «listItem("Argument", ext.argument)»
1287 private def dispatch CharSequence tree(Void obj, YangInstanceIdentifier path) '''
1292 /* #################### RESTRICTIONS #################### */
1293 private def restrictions(TypeDefinition<?> type) '''
1294 «type.baseType.toBaseStmt»
1299 private def dispatch toLength(TypeDefinition<?> type) {
1302 private def dispatch toLength(BinaryTypeDefinition type) '''
1303 «type.lengthConstraints.toLengthStmt»
1306 private def dispatch toLength(StringTypeDefinition type) '''
1307 «type.lengthConstraints.toLengthStmt»
1310 private def dispatch toRange(TypeDefinition<?> type) {
1313 private def dispatch toRange(DecimalTypeDefinition type) '''
1314 «type.rangeConstraints.toRangeStmt»
1317 private def dispatch toRange(IntegerTypeDefinition type) '''
1318 «type.rangeConstraints.toRangeStmt»
1321 private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
1322 «type.rangeConstraints.toRangeStmt»
1325 def toLengthStmt(Collection<LengthConstraint> lengths) '''
1326 «IF lengths != null && !lengths.empty»
1327 «listItem("Length restrictions:")»
1329 «FOR length : lengths»
1331 «IF length.min == length.max»
1334 <«length.min», «length.max»>
1342 def toRangeStmt(Collection<RangeConstraint> ranges) '''
1343 «IF ranges != null && !ranges.empty»
1344 «listItem("Range restrictions:")»
1346 «FOR range : ranges»
1348 «IF range.min == range.max»
1351 <«range.min», «range.max»>
1359 def toBaseStmt(TypeDefinition<?> baseType) '''
1360 «IF baseType != null»
1361 «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))»
1367 /* #################### UTILITY #################### */
1368 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
1369 private def italic(CharSequence str) '''<i>«str»</i>'''
1371 def CharSequence descAndRefLi(SchemaNode node) '''
1372 «listItem("Description", node.description)»
1373 «listItem("Reference", node.reference)»
1376 def CharSequence descAndRef(SchemaNode node) '''
1378 «IF node.reference !== null»
1379 Reference «node.reference»
1383 private def listItem(String value) '''
1384 «IF value !== null && !value.empty»
1391 private def listItem(String name, String value) '''
1392 «IF value !== null && !value.empty»
1399 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
1400 if (node instanceof ChoiceSchemaNode || node instanceof ChoiceCaseNode) {
1404 val path = node.path.pathFromRoot
1405 val absolute = node.path.absolute;
1406 var StringBuilder result = new StringBuilder
1410 if (path !== null && !path.empty) {
1411 val List<QName> actual = new ArrayList()
1413 for (pathElement : path) {
1414 actual.add(pathElement)
1415 val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute))
1416 if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
1417 result.append(pathElement.localName)
1418 if (i != path.size - 1) {
1425 return result.toString
1428 private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
1429 for (node : source) {
1430 destination.put(node.path, node)
1431 if (node instanceof DataNodeContainer) {
1432 collectChildNodes((node as DataNodeContainer).childNodes, destination)
1434 if (node instanceof ChoiceSchemaNode) {
1435 val List<DataSchemaNode> choiceCases = new ArrayList()
1436 for (caseNode : node.cases) {
1437 choiceCases.add(caseNode)
1439 collectChildNodes(choiceCases, destination)
1444 private def dispatch addedByInfo(SchemaNode node) '''
1447 private def dispatch addedByInfo(DataSchemaNode node) '''
1448 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
1451 private def dispatch isAddedBy(SchemaNode node) {
1455 private def dispatch isAddedBy(DataSchemaNode node) {
1456 if (node.augmenting || node.addedByUses) {
1463 private def dispatch nodeName(SchemaNode node) '''
1465 «italic(node.QName.localName)»«node.addedByInfo»
1467 «node.QName.localName»«node.addedByInfo»
1471 private def dispatch nodeName(ContainerSchemaNode node) '''
1473 «strong(italic(node.QName.localName))»«node.addedByInfo»
1475 «strong(node.QName.localName)»«node.addedByInfo»
1479 private def dispatch nodeName(ListSchemaNode node) '''
1481 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
1483 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»