1 package org.opendaylight.yangtools.yang.unified.doc.generator
3 import org.opendaylight.yangtools.yang.model.api.SchemaContext
6 import org.opendaylight.yangtools.yang.model.api.Module
7 import java.io.IOException
8 import java.util.HashSet
9 import java.io.FileWriter
10 import java.io.BufferedWriter
11 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
12 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
13 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
14 import org.opendaylight.yangtools.yang.model.api.SchemaNode
15 import org.opendaylight.yangtools.yang.model.util.ExtendedType
16 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
17 import java.text.SimpleDateFormat
18 import java.util.Collection
19 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint
20 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
21 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition
22 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
23 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition
24 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition
25 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
26 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
27 import org.slf4j.LoggerFactory
28 import org.slf4j.Logger
29 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
31 import org.opendaylight.yangtools.yang.common.QName
32 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
33 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition
34 import java.util.ArrayList
36 import org.opendaylight.yangtools.yang.model.api.SchemaPath
37 import java.util.LinkedHashMap
38 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
39 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
44 static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
45 static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl)
48 def generate(SchemaContext context, File targetPath, Set<Module> modulesToGen) throws IOException {
52 for (module : modulesToGen) {
53 add(module.generateDocumentation());
58 def generateDocumentation(Module module) {
59 val destination = new File(path, '''«module.name».html''')
61 val fw = new FileWriter(destination)
62 destination.createNewFile();
63 val bw = new BufferedWriter(fw)
65 bw.append(module.generate);
68 } catch (IOException e) {
69 LOG.error(e.getMessage());
74 def generate(Module module) '''
78 <title>«module.name»</title>
86 def body(Module module) '''
89 «typeDefinitions(module)»
99 «notifications(module)»
101 «augmentations(module)»
112 def typeDefinitions(Module module) {
113 val Set<TypeDefinition<?>> typedefs = module.typeDefinitions
114 if (typedefs.empty) {
118 <h2>Type Definitions</h2>
120 «FOR typedef : typedefs»
122 «strong("typedef " + typedef.QName.localName)»
125 «typedef.restrictions»
133 private def identities(Module module) {
134 if (module.identities.empty) {
140 «FOR identity : module.identities»
142 «strong("identity " + identity.QName.localName)»
144 «identity.descAndRef»
145 «IF identity.baseIdentity != null»
146 «listItem("base", identity.baseIdentity.QName.localName)»
155 private def groupings(Module module) {
156 if (module.groupings.empty) {
162 «FOR grouping : module.groupings»
164 «strong("grouping " + grouping.QName.localName)»
166 «grouping.descAndRef»
174 def dataStore(Module module) {
175 if (module.childNodes.empty) {
179 <h2>Datastore Structure</h2>
184 def augmentations(Module module) {
185 if (module.augmentations.empty) {
189 <h2>Augmentations</h2>
192 «FOR augment : module.augmentations»
202 def notifications(Module module) {
203 val Set<NotificationDefinition> notificationdefs = module.notifications
204 if (notificationdefs.empty) {
208 <h2>Notifications</h2>
211 «FOR notificationdef : notificationdefs»
213 «notificationdef.nodeName»
214 «notificationdef.tree»
221 def rpcs(Module module) {
222 if (module.rpcs.empty) {
226 <h2>RPC Definitions</h2>
229 «FOR rpc : module.rpcs»
239 def extensions(Module module) {
240 if (module.extensionSchemaNodes.empty) {
247 «FOR ext : module.extensionSchemaNodes»
257 def features(Module module) {
258 if (module.features.empty) {
265 «FOR feature : module.features»
267 «strong("feature " + feature.QName.localName)»
277 def header(Module module) '''
278 <h1>«module.name»</h1>
280 <h2>Base Information</h2>
283 <dd>«pre(module.prefix)»</dd>
285 <dd>«pre(module.namespace.toString)»</dd>
287 <dd>«pre(REVISION_FORMAT.format(module.revision))»</dd>
289 «FOR imp : module.imports BEFORE "<dt>Imports</dt>" »
290 <dd>«pre(imp.prefix)» = «pre(imp.moduleName)»</dd>
295 def process(Module module) {
296 throw new UnsupportedOperationException("TODO: auto-generated method stub")
301 /* #################### TREE STRUCTURE #################### */
302 def dispatch CharSequence tree(Module module) '''
303 «strong("module " + module.name)»
304 «module.childNodes.tree»
307 def dispatch CharSequence tree(DataNodeContainer node) '''
308 «IF node instanceof SchemaNode»
309 «(node as SchemaNode).nodeName»
311 «node.childNodes.tree»
314 def dispatch CharSequence tree(DataSchemaNode node) '''
318 def dispatch CharSequence tree(ListSchemaNode node) '''
320 «node.childNodes.tree»
323 def CharSequence childNodes(Module module) '''
324 «val Map<SchemaPath, DataSchemaNode> childNodes = new LinkedHashMap()»
325 «collectChildNodes(module.childNodes, childNodes)»
326 «IF childNodes !== null && !childNodes.empty»
329 «childNodes.childNodesInfoTree»
333 def CharSequence childNodesInfoTree(Map<SchemaPath, DataSchemaNode> childNodes) '''
334 «IF childNodes !== null && !childNodes.empty»
336 «FOR child : childNodes.values»
337 «childInfo(child, childNodes)»
343 def CharSequence childInfo(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) '''
344 «val String path = nodeSchemaPathToPath(node, childNodes)»
346 «listItem(strong(path))»
355 def dispatch CharSequence tree(Collection<DataSchemaNode> childNodes) '''
356 «IF childNodes !== null && !childNodes.empty»
358 «FOR child : childNodes»
367 def listKeys(ListSchemaNode node) '''
368 [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»]
371 def dispatch CharSequence tree(AugmentationSchema augment) '''
373 «listItem(augment.description)»
374 «listItem("Reference", augment.reference)»
375 «IF augment.whenCondition !== null»
376 «listItem("When", augment.whenCondition.toString)»
379 Path «augment.targetPath.path.pathToTree»
383 «augment.childNodes.tree»
388 def dispatch CharSequence tree(NotificationDefinition notification) '''
390 «notification.descAndRef»
393 «notification.childNodes.tree»
398 def dispatch CharSequence tree(RpcDefinition rpc) '''
410 def dispatch CharSequence tree(ExtensionDefinition ext) '''
413 «listItem("Argument", ext.argument)»
419 /* #################### RESTRICTIONS #################### */
420 private def restrictions(TypeDefinition<?> type) '''
425 def dispatch toLength(TypeDefinition<?> type) {
428 def dispatch toLength(BinaryTypeDefinition type) '''
429 «type.lengthConstraints.toLengthStmt»
432 def dispatch toLength(StringTypeDefinition type) '''
433 «type.lengthConstraints.toLengthStmt»
436 def dispatch toLength(ExtendedType type) '''
437 «type.lengthConstraints.toLengthStmt»
440 def dispatch toRange(TypeDefinition<?> type) {
443 def dispatch toRange(DecimalTypeDefinition type) '''
444 «type.rangeConstraints.toRangeStmt»
447 def dispatch toRange(IntegerTypeDefinition type) '''
448 «type.rangeConstraints.toRangeStmt»
451 def dispatch toRange(UnsignedIntegerTypeDefinition type) '''
452 «type.rangeConstraints.toRangeStmt»
455 def dispatch toRange(ExtendedType type) '''
456 «type.rangeConstraints.toRangeStmt»
459 def toLengthStmt(Collection<LengthConstraint> lengths) '''
460 «IF lengths != null && !lengths.empty»
461 «listItem("Length restrictions")»
463 «FOR length : lengths»
465 «IF length.min == length.max»
468 <«length.min», «length.max»>
476 def toRangeStmt(Collection<RangeConstraint> ranges) '''
477 «IF ranges != null && !ranges.empty»
478 «listItem("Range restrictions")»
482 «IF range.min == range.max»
485 <«range.min», «range.max»>
495 /* #################### UTILITY #################### */
496 private def String strong(String str) '''<strong>«str»</strong>'''
497 private def italic(String str) '''<i>«str»</i>'''
498 private def pre(String str) '''<pre>«str»</pre>'''
500 def CharSequence descAndRef(SchemaNode node) '''
501 «listItem(node.description)»
502 «listItem("Reference", node.reference)»
505 private def listItem(String value) '''
506 «IF value !== null && !value.empty»
513 private def listItem(String name, String value) '''
514 «IF value !== null && !value.empty»
526 private def String nodeSchemaPathToPath(DataSchemaNode node, Map<SchemaPath, DataSchemaNode> childNodes) {
527 if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) {
531 val path = node.path.path
532 val absolute = node.path.absolute;
533 var StringBuilder result = new StringBuilder
537 if (path !== null && !path.empty) {
538 val List<QName> actual = new ArrayList()
540 for (pathElement : path) {
541 actual.add(pathElement)
542 val DataSchemaNode nodeByPath = childNodes.get(new SchemaPath(actual, absolute))
543 if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) {
544 result.append(pathElement.localName)
545 if (i != path.size - 1) {
552 return result.toString
555 private def void collectChildNodes(Collection<DataSchemaNode> source, Map<SchemaPath, DataSchemaNode> destination) {
556 for (node : source) {
557 destination.put(node.path, node)
558 if (node instanceof DataNodeContainer) {
559 collectChildNodes((node as DataNodeContainer).childNodes, destination)
561 if (node instanceof ChoiceNode) {
562 val List<DataSchemaNode> choiceCases = new ArrayList()
563 for (caseNode : (node as ChoiceNode).cases) {
564 choiceCases.add(caseNode)
566 collectChildNodes(choiceCases, destination)
571 private def CharSequence pathToTree(List<QName> path) '''
572 «IF path !== null && !path.empty»
574 «FOR pathElement : path»
576 «pathElement.namespace» «pathElement.localName»
583 def dispatch addedByInfo(SchemaNode node) '''
586 def dispatch addedByInfo(DataSchemaNode node) '''
587 «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF»
590 def dispatch isAddedBy(SchemaNode node) {
594 def dispatch isAddedBy(DataSchemaNode node) {
595 if (node.augmenting || node.addedByUses) {
602 def dispatch nodeName(SchemaNode node) '''
604 «italic(node.QName.localName)»«node.addedByInfo»
606 «strong(node.QName.localName)»«node.addedByInfo»
610 def dispatch nodeName(ListSchemaNode node) '''
612 «italic(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo»
614 «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»