1 package org.opendaylight.yangtools.yang.unified.doc.generator
\r
3 import org.opendaylight.yangtools.yang.model.api.SchemaContext
\r
6 import org.opendaylight.yangtools.yang.model.api.Module
\r
7 import java.io.IOException
\r
8 import java.util.HashSet
\r
9 import java.io.BufferedWriter
\r
10 import java.io.OutputStream;
\r
11 import java.io.OutputStreamWriter;
\r
12 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
\r
13 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
\r
14 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
\r
15 import org.opendaylight.yangtools.yang.model.api.SchemaNode
\r
16 import org.opendaylight.yangtools.yang.model.util.ExtendedType
\r
17 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
\r
18 import java.text.SimpleDateFormat
\r
19 import java.util.Collection
\r
20 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
\r
21 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
\r
22 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
\r
23 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
\r
24 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
\r
25 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
\r
26 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
\r
27 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
\r
28 import org.slf4j.LoggerFactory
\r
29 import org.slf4j.Logger
\r
30 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
\r
31 import java.util.List
\r
32 import org.opendaylight.yangtools.yang.common.QName
\r
33 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
\r
34 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
\r
35 import java.util.ArrayList
\r
36 import java.util.Map
\r
37 import org.opendaylight.yangtools.yang.model.api.SchemaPath
\r
39 import org.sonatype.plexus.build.incremental.BuildContext;
\r
40 import org.sonatype.plexus.build.incremental.DefaultBuildContext;
\r
42 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
\r
43 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
\r
44 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
\r
45 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
\r
46 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
\r
47 import java.util.LinkedHashMap
\r
48 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier
\r
49 import com.google.common.collect.FluentIterable
\r
50 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
\r
51 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
\r
52 import java.net.URLEncoder
\r
53 import javax.swing.text.StyledEditorKit.ForegroundAction
\r
55 class GeneratorImpl {
\r
58 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
\r
59 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
\r
60 static val BuildContext CTX = new DefaultBuildContext();
\r
61 var Module currentModule;
\r
64 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
\r
67 val it = new HashSet;
\r
68 for (module : modulesToGen) {
\r
69 add(module.generateDocumentation());
\r
74 def generateDocumentation(Module module) {
\r
75 val destination = new File(path, '''«module.name».html''')
\r
77 val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination))
\r
78 val bw = new BufferedWriter(fw)
\r
79 currentModule = module;
\r
80 bw.append(module.generate);
\r
83 } catch (IOException e) {
\r
84 LOG.error(e.getMessage());
\r
89 def generate(Module module) '''
\r
93 <title>«module.name»</title>
\r
101 def body(Module module) '''
\r
104 «typeDefinitions(module)»
\r
106 «identities(module)»
\r
108 «groupings(module)»
\r
110 «dataStore(module)»
\r
112 «childNodes(module)»
\r
114 «notifications(module)»
\r
116 «augmentations(module)»
\r
120 «extensions(module)»
\r
127 def typeDefinitions(Module module) {
\r
128 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
\r
129 if (typedefs.empty) {
\r
133 <h2>Type Definitions</h2>
\r
135 «FOR typedef : typedefs»
\r
137 «strong("typedef " + typedef.QName.localName)»
\r
139 «typedef.descAndRef»
\r
140 «typedef.restrictions»
\r
148 private def identities(Module module) {
\r
149 if (module.identities.empty) {
\r
153 <h2>Identities</h2>
\r
155 «FOR identity : module.identities»
\r
157 «strong("identity " + identity.QName.localName)»
\r
159 «identity.descAndRef»
\r
160 «IF identity.baseIdentity != null»
\r
161 «listItem("base", identity.baseIdentity.QName.localName)»
\r
170 private def groupings(Module module) {
\r
171 if (module.groupings.empty) {
\r
177 «FOR grouping : module.groupings»
\r
179 «strong("grouping " + grouping.QName.localName)»
\r
181 «grouping.descAndRef»
\r
189 def dataStore(Module module) {
\r
190 if (module.childNodes.empty) {
\r
194 <h2>Datastore Structure</h2>
\r
199 def augmentations(Module module) {
\r
200 if (module.augmentations.empty) {
\r
204 <h2>Augmentations</h2>
\r
207 «FOR augment : module.augmentations»
\r
210 «augment.augmentationInfo(InstanceIdentifier.builder().toInstance())»
\r
211 «augment.childNodes.printChildren(2,InstanceIdentifier.builder().toInstance())»
\r
218 def notifications(Module module) {
\r
219 val Set<NotificationDefinition> notificationdefs = module.notifications
\r
220 if (notificationdefs.empty) {
\r
225 <h2>Notifications</h2>
\r
226 «FOR notificationdef : notificationdefs»
\r
228 <h3>«notificationdef.nodeName»</h3>
\r
229 «notificationdef.notificationInfo(InstanceIdentifier.builder().node(notificationdef.QName).toInstance())»
\r
234 def rpcs(Module module) {
\r
235 if (module.rpcs.empty) {
\r
240 <h2>RPC Definitions</h2>
\r
241 «FOR rpc : module.rpcs»
\r
242 <h3>«rpc.nodeName»</h3>
\r
243 «rpc.rpcInfo(InstanceIdentifier.builder().node(rpc.QName).toInstance())»
\r
249 def extensions(Module module) {
\r
250 if (module.extensionSchemaNodes.empty) {
\r
254 <h2>Extensions</h2>
\r
255 «FOR ext : module.extensionSchemaNodes»
\r
257 <h3>«ext.nodeName»</h3>
\r
263 def features(Module module) {
\r
264 if (module.features.empty) {
\r
271 «FOR feature : module.features»
\r
273 «strong("feature " + feature.QName.localName)»
\r
275 «feature.descAndRef»
\r
283 def header(Module module) '''
\r
284 <h1>«module.name»</h1>
\r
286 <h2>Base Information</h2>
\r
289 <dd>«pre(module.prefix)»</dd>
\r
291 <dd>«pre(module.namespace.toString)»</dd>
\r
293 <dd>«pre(REVISION_FORMAT.format(module.revision))»</dd>
\r
295 «FOR imp : module.imports BEFORE "<dt>Imports</dt>" »
\r
296 <dd>«code(imp.prefix)» = «code(imp.moduleName)»</dd>
\r
301 def code(String string) '''<code>«string»</code>'''
\r
303 def process(Module module) {
\r
304 throw new UnsupportedOperationException("TODO: auto-generated method stub")
\r
307 def CharSequence tree(Module module) '''
\r
308 «strong("module " + module.name)»
\r
309 «module.childNodes.treeSet(InstanceIdentifier.builder.toInstance())»
\r
312 private def dispatch CharSequence tree(ChoiceNode node,InstanceIdentifier path) '''
\r
313 «node.nodeName» (choice)
\r
314 «casesTree(node.cases,path)»
\r
317 def casesTree(Set<ChoiceCaseNode> nodes,InstanceIdentifier path) '''
\r
322 «node.childNodes.treeSet(path)»
\r
328 private def dispatch CharSequence tree(DataSchemaNode node,InstanceIdentifier path) '''
\r
332 private def dispatch CharSequence tree(ListSchemaNode node,InstanceIdentifier path) '''
\r
333 «val newPath = path.append(node)»
\r
334 «localLink(newPath,node.nodeName)»
\r
335 «node.childNodes.treeSet(newPath)»
\r
338 private def dispatch CharSequence tree(ContainerSchemaNode node,InstanceIdentifier path) '''
\r
339 «val newPath = path.append(node)»
\r
340 «localLink(newPath,node.nodeName)»
\r
341 «node.childNodes.treeSet(newPath)»
\r
344 def CharSequence childNodes(Module module) '''
\r
345 «val childNodes = module.childNodes»
\r
346 «IF childNodes !== null && !childNodes.empty»
\r
347 <h2>Child nodes</h2>
\r
349 «childNodes.printChildren(2,InstanceIdentifier.builder().toInstance())»
\r
353 def CharSequence printChildren(Set<DataSchemaNode> nodes, int level, InstanceIdentifier path) {
\r
354 val leafNodes = nodes.filter(LeafSchemaNode)
\r
355 val leafListNodes = nodes.filter(LeafListSchemaNode)
\r
356 val choices = nodes.filter(ChoiceNode)
\r
357 val cases = nodes.filter(ChoiceCaseNode)
\r
358 val containers = nodes.filter(ContainerSchemaNode)
\r
359 val lists = nodes.filter(ListSchemaNode)
\r
361 <h3>Direct children</h3>
\r
363 «FOR childNode : leafNodes»
\r
364 «childNode.printShortInfo(level,path)»
\r
366 «FOR childNode : leafListNodes»
\r
367 «childNode.printShortInfo(level,path)»
\r
369 «FOR childNode : containers»
\r
370 «childNode.printShortInfo(level,path)»
\r
372 «FOR childNode : lists»
\r
373 «childNode.printShortInfo(level,path)»
\r
377 «IF !path.path.empty»
\r
378 <h3>XML example</h3>
\r
379 «nodes.xmlExample(path.path.last.nodeType,path)»
\r
382 «FOR childNode : containers»
\r
383 «childNode.printInfo(level,path)»
\r
385 «FOR childNode : lists»
\r
386 «childNode.printInfo(level,path)»
\r
388 «FOR childNode : choices»
\r
389 «childNode.printInfo(level,path)»
\r
391 «FOR childNode : cases»
\r
392 «childNode.printInfo(level,path)»
\r
398 def CharSequence xmlExample(Set<DataSchemaNode> nodes, QName name,InstanceIdentifier path) '''
\r
400 «xmlExampleTag(name,nodes.xmplExampleTags(path))»
\r
404 def CharSequence xmplExampleTags(Set<DataSchemaNode> nodes, InstanceIdentifier identifier) '''
\r
405 <!-- Child nodes -->
\r
407 <!-- «node.QName.localName» -->
\r
408 «node.asXmlExampleTag(identifier)»
\r
413 private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, InstanceIdentifier identifier) '''
\r
414 «node.QName.xmlExampleTag("...")»
\r
417 private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, InstanceIdentifier identifier) '''
\r
418 <!-- This node could appear multiple times -->
\r
419 «node.QName.xmlExampleTag("...")»
\r
422 private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, InstanceIdentifier identifier) '''
\r
423 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
\r
424 «node.QName.xmlExampleTag("...")»
\r
428 private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, InstanceIdentifier identifier) '''
\r
429 <!-- See «localLink(identifier.append(node),"definition")» for child nodes. -->
\r
430 <!-- This node could appear multiple times -->
\r
431 «node.QName.xmlExampleTag("...")»
\r
435 private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, InstanceIdentifier identifier) '''
\r
440 def xmlExampleTag(QName name, CharSequence data) {
\r
441 return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>'''
\r
444 private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) '''
\r
445 «val newPath = path.append(node)»
\r
446 «header(level,newPath)»
\r
449 <dd>«newPath.asXmlPath»</dd>
\r
450 <dt>Restconf path</dt>
\r
451 <dd>«code(newPath.asRestconfPath)»</dd>
\r
453 «node.childNodes.printChildren(level,newPath)»
\r
456 def header(int level,QName name) '''<h«level»>«name.localName»</h«level»>'''
\r
459 def header(int level,InstanceIdentifier name)
\r
461 <h«level» id="«FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">
\r
462 «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»
\r
466 private def dispatch CharSequence printInfo(ListSchemaNode node, int level, InstanceIdentifier path) '''
\r
467 «val newPath = path.append(node)»
\r
468 «header(level,newPath)»
\r
471 <dd>«newPath.asXmlPath»</dd>
\r
472 <dt>Restconf path</dt>
\r
473 <dd>«code(newPath.asRestconfPath)»</dd>
\r
475 «node.childNodes.printChildren(level,newPath)»
\r
478 private def dispatch CharSequence printInfo(ChoiceNode node, int level, InstanceIdentifier path) '''
\r
479 «val Set<DataSchemaNode> choiceCases = new HashSet(node.cases)»
\r
480 «choiceCases.printChildren(level,path)»
\r
483 private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, InstanceIdentifier path) '''
\r
484 «node.childNodes.printChildren(level,path)»
\r
487 def CharSequence printShortInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) {
\r
488 val newPath = path.append(node);
\r
490 <li>«strong(localLink(newPath,node.QName.localName))» (container)</li>
\r
494 def CharSequence printShortInfo(ListSchemaNode node, int level, InstanceIdentifier path) {
\r
495 val newPath = path.append(node);
\r
497 <li>«strong(localLink(newPath,node.QName.localName))» (list)</li>
\r
501 def CharSequence printShortInfo(LeafSchemaNode node, int level, InstanceIdentifier path) {
\r
503 <li>«strong((node.QName.localName))» (leaf)</li>
\r
507 def CharSequence printShortInfo(LeafListSchemaNode node, int level, InstanceIdentifier path) {
\r
509 <li>«strong((node.QName.localName))» (leaf-list)</li>
\r
513 def CharSequence localLink(InstanceIdentifier identifier, CharSequence text) '''
\r
514 <a href="#«FOR cmp : identifier.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR»">«text»</a>
\r
518 private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ContainerSchemaNode node) {
\r
519 val pathArguments = new ArrayList(identifier.path)
\r
520 pathArguments.add(new NodeIdentifier(node.QName));
\r
521 return new InstanceIdentifier(pathArguments);
\r
524 private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ListSchemaNode node) {
\r
525 val pathArguments = new ArrayList(identifier.path)
\r
526 val keyValues = new LinkedHashMap<QName,Object>();
\r
527 if(node.keyDefinition != null) {
\r
528 for(definition : node.keyDefinition) {
\r
529 keyValues.put(definition,new Object);
\r
532 pathArguments.add(new NodeIdentifierWithPredicates(node.QName,keyValues));
\r
533 return new InstanceIdentifier(pathArguments);
\r
537 def asXmlPath(InstanceIdentifier identifier) {
\r
541 def asRestconfPath(InstanceIdentifier identifier) {
\r
542 val it = new StringBuilder();
\r
543 append(currentModule.name)
\r
545 var previous = false;
\r
546 for(arg : identifier.path) {
\r
547 if(previous) append("/")
\r
548 append(arg.nodeType.localName);
\r
550 if(arg instanceof NodeIdentifierWithPredicates) {
\r
551 val nodeIdentifier = arg as NodeIdentifierWithPredicates;
\r
552 for(qname : nodeIdentifier.keyValues.keySet) {
\r
554 append(qname.localName)
\r
560 return it.toString;
\r
564 private def dispatch CharSequence printInfo(DataSchemaNode node, int level, InstanceIdentifier path) '''
\r
565 «header(level+1,node.QName)»
\r
572 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
\r
573 «IF childNodes !== null && !childNodes.empty»
\r
574 «FOR child : childNodes.values»
\r
575 «childInfo(child, childNodes)»
\r
580 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
\r
581 «val String path = nodeSchemaPathToPath(node, childNodes)»
\r
592 private def CharSequence treeSet(Collection<DataSchemaNode> childNodes, InstanceIdentifier path) '''
\r
593 «IF childNodes !== null && !childNodes.empty»
\r
595 «FOR child : childNodes»
\r
604 def listKeys(ListSchemaNode node) '''
\r
605 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
\r
608 private def CharSequence augmentationInfo(AugmentationSchema augment, InstanceIdentifier path) '''
\r
610 «listItem(augment.description)»
\r
611 «listItem("Reference", augment.reference)»
\r
612 «IF augment.whenCondition !== null»
\r
613 «listItem("When", augment.whenCondition.toString)»
\r
616 Path «augment.targetPath.path.pathToTree»
\r
620 «augment.childNodes.treeSet(path)»
\r
625 private def CharSequence notificationInfo(NotificationDefinition notification,InstanceIdentifier path) '''
\r
627 «notification.descAndRef»
\r
630 «notification.childNodes.treeSet(path)»
\r
635 private def CharSequence rpcInfo(RpcDefinition rpc,InstanceIdentifier path) '''
\r
639 «rpc.input.tree(path)»
\r
642 «rpc.output.tree(path)»
\r
647 private def CharSequence extensionInfo(ExtensionDefinition ext, InstanceIdentifier path) '''
\r
650 «listItem("Argument", ext.argument)»
\r
654 private def dispatch CharSequence tree(Void obj, InstanceIdentifier path) '''
\r
659 /* #################### RESTRICTIONS #################### */
\r
660 private def restrictions(TypeDefinition<?> type) '''
\r
665 private def dispatch toLength(TypeDefinition<?> type) {
\r
668 private def dispatch toLength(BinaryTypeDefinition type) '''
\r
669 «type.lengthConstraints.toLengthStmt»
\r
672 private def dispatch toLength(StringTypeDefinition type) '''
\r
673 «type.lengthConstraints.toLengthStmt»
\r
676 private def dispatch toLength(ExtendedType type) '''
\r
677 «type.lengthConstraints.toLengthStmt»
\r
680 private def dispatch toRange(TypeDefinition<?> type) {
\r
683 private def dispatch toRange(DecimalTypeDefinition type) '''
\r
684 «type.rangeConstraints.toRangeStmt»
\r
687 private def dispatch toRange(IntegerTypeDefinition type) '''
\r
688 «type.rangeConstraints.toRangeStmt»
\r
691 private def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
\r
692 «type.rangeConstraints.toRangeStmt»
\r
695 private def dispatch toRange(ExtendedType type) '''
\r
696 «type.rangeConstraints.toRangeStmt»
\r
699 def toLengthStmt(Collection<LengthConstraint> lengths) '''
\r
700 «IF lengths != null && !lengths.empty»
\r
701 «listItem("Length restrictions")»
\r
703 «FOR length : lengths»
\r
705 «IF length.min == length.max»
\r
708 <«length.min», «length.max»>
\r
716 def toRangeStmt(Collection<RangeConstraint> ranges) '''
\r
717 «IF ranges != null && !ranges.empty»
\r
718 «listItem("Range restrictions")»
\r
720 «FOR range : ranges»
\r
722 «IF range.min == range.max»
\r
725 <«range.min», «range.max»>
\r
735 /* #################### UTILITY #################### */
\r
736 private def String strong(CharSequence str) '''<strong>«str»</strong>'''
\r
737 private def italic(CharSequence str) '''<i>«str»</i>'''
\r
738 private def pre(CharSequence str) '''<pre>«str»</pre>'''
\r
740 def CharSequence descAndRef(SchemaNode node) '''
\r
741 «listItem(node.description)»
\r
742 «listItem("Reference", node.reference)»
\r
745 private def listItem(String value) '''
\r
746 «IF value !== null && !value.empty»
\r
753 private def listItem(String name, String value) '''
\r
754 «IF value !== null && !value.empty»
\r
766 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
\r
767 if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
\r
771 val path = node.path.path
\r
772 val absolute = node.path.absolute;
\r
773 var StringBuilder result = new StringBuilder
\r
777 if (path !== null && !path.empty) {
\r
778 val List<QName> actual = new ArrayList()
\r
780 for (pathElement : path) {
\r
781 actual.add(pathElement)
\r
782 val DataSchemaNode nodeByPath = childNodes.get(new SchemaPath(actual, absolute))
\r
783 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
\r
784 result.append(pathElement.localName)
\r
785 if (i != path.size - 1) {
\r
792 return result.toString
\r
795 private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
\r
796 for (node : source) {
\r
797 destination.put(node.path, node)
\r
798 if (node instanceof DataNodeContainer) {
\r
799 collectChildNodes((node as DataNodeContainer).childNodes, destination)
\r
801 if (node instanceof ChoiceNode) {
\r
802 val List<DataSchemaNode> choiceCases = new ArrayList()
\r
803 for (caseNode : (node as ChoiceNode).cases) {
\r
804 choiceCases.add(caseNode)
\r
806 collectChildNodes(choiceCases, destination)
\r
811 private def CharSequence pathToTree(List<QName> path) '''
\r
812 «IF path !== null && !path.empty»
\r
814 «FOR pathElement : path»
\r
816 «pathElement.namespace» «pathElement.localName»
\r
823 private def dispatch addedByInfo(SchemaNode node) '''
\r
826 private def dispatch addedByInfo(DataSchemaNode node) '''
\r
827 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
\r
830 private def dispatch isAddedBy(SchemaNode node) {
\r
834 private def dispatch isAddedBy(DataSchemaNode node) {
\r
835 if (node.augmenting || node.addedByUses) {
\r
842 private def dispatch nodeName(SchemaNode node) '''
\r
843 «IF node.isAddedBy»
\r
844 «italic(node.QName.localName)»«node.addedByInfo»
\r
846 «node.QName.localName»«node.addedByInfo»
\r
850 private def dispatch nodeName(ContainerSchemaNode node) '''
\r
851 «IF node.isAddedBy»
\r
852 «strong(italic(node.QName.localName))»«node.addedByInfo»
\r
854 «strong(node.QName.localName)»«node.addedByInfo»
\r
858 private def dispatch nodeName(ListSchemaNode node) '''
\r
859 «IF node.isAddedBy»
\r
860 «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
\r
862 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»
\r