package org.opendaylight.yangtools.yang.unified.doc.generator import org.opendaylight.yangtools.yang.model.api.SchemaContext import java.io.File import java.util.Set import org.opendaylight.yangtools.yang.model.api.Module import java.io.IOException import java.util.HashSet import java.io.BufferedWriter import java.io.OutputStream; import java.io.OutputStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode import org.opendaylight.yangtools.yang.model.api.ListSchemaNode import org.opendaylight.yangtools.yang.model.api.TypeDefinition import org.opendaylight.yangtools.yang.model.api.SchemaNode import org.opendaylight.yangtools.yang.model.util.ExtendedType import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition import java.text.SimpleDateFormat import java.util.Collection import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition import org.opendaylight.yangtools.yang.model.api.NotificationDefinition import org.opendaylight.yangtools.yang.model.api.DataNodeContainer import org.slf4j.LoggerFactory import org.slf4j.Logger import org.opendaylight.yangtools.yang.model.api.AugmentationSchema import java.util.List import org.opendaylight.yangtools.yang.common.QName import org.opendaylight.yangtools.yang.model.api.RpcDefinition import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition import java.util.ArrayList import java.util.Map import org.opendaylight.yangtools.yang.model.api.SchemaPath import org.sonatype.plexus.build.incremental.BuildContext; import org.sonatype.plexus.build.incremental.DefaultBuildContext; import org.opendaylight.yangtools.yang.model.api.ChoiceNode import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates import java.util.LinkedHashMap import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier import com.google.common.collect.FluentIterable import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode import java.net.URLEncoder import javax.swing.text.StyledEditorKit.ForegroundAction class GeneratorImpl { File path static val REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd") static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl) static val BuildContext CTX = new DefaultBuildContext(); var Module currentModule; def generate(SchemaContext context, File targetPath, Set modulesToGen) throws IOException { path = targetPath; path.mkdirs(); val it = new HashSet; for (module : modulesToGen) { add(module.generateDocumentation()); } return it; } def generateDocumentation(Module module) { val destination = new File(path, '''«module.name».html''') try { val fw = new OutputStreamWriter(CTX.newFileOutputStream(destination)) val bw = new BufferedWriter(fw) currentModule = module; bw.append(module.generate); bw.close(); fw.close(); } catch (IOException e) { LOG.error(e.getMessage()); } return destination; } def generate(Module module) ''' «module.name» «module.body» ''' def body(Module module) ''' «header(module)» «typeDefinitions(module)» «identities(module)» «groupings(module)» «dataStore(module)» «childNodes(module)» «notifications(module)» «augmentations(module)» «rpcs(module)» «extensions(module)» «features(module)» ''' def typeDefinitions(Module module) { val Set> typedefs = module.typeDefinitions if (typedefs.empty) { return ''; } return '''

Type Definitions

''' } private def identities(Module module) { if (module.identities.empty) { return ''; } return '''

Identities

''' } private def groupings(Module module) { if (module.groupings.empty) { return ''; } return '''

Groupings

''' } def dataStore(Module module) { if (module.childNodes.empty) { return ''; } return '''

Datastore Structure

«tree(module)» ''' } def augmentations(Module module) { if (module.augmentations.empty) { return ''; } return '''

Augmentations

''' } def notifications(Module module) { val Set notificationdefs = module.notifications if (notificationdefs.empty) { return ''; } return '''

Notifications

«FOR notificationdef : notificationdefs»

«notificationdef.nodeName»

«notificationdef.notificationInfo(InstanceIdentifier.builder().node(notificationdef.QName).toInstance())» «ENDFOR» ''' } def rpcs(Module module) { if (module.rpcs.empty) { return ''; } return '''

RPC Definitions

«FOR rpc : module.rpcs»

«rpc.nodeName»

«rpc.rpcInfo(InstanceIdentifier.builder().node(rpc.QName).toInstance())» «ENDFOR» ''' } def extensions(Module module) { if (module.extensionSchemaNodes.empty) { return ''; } return '''

Extensions

«FOR ext : module.extensionSchemaNodes»
  • «ext.nodeName»

  • «ENDFOR» ''' } def features(Module module) { if (module.features.empty) { return ''; } return '''

    Features

      «FOR feature : module.features»
    • «strong("feature " + feature.QName.localName)»
        «feature.descAndRef»
    • «ENDFOR»
    ''' } def header(Module module) '''

    «module.name»

    Base Information

    Prefix
    «pre(module.prefix)»
    Namespace
    «pre(module.namespace.toString)»
    Revision
    «pre(REVISION_FORMAT.format(module.revision))»
    «FOR imp : module.imports BEFORE "
    Imports
    " »
    «code(imp.prefix)» = «code(imp.moduleName)»
    «ENDFOR»
    ''' def code(String string) '''«string»''' def process(Module module) { throw new UnsupportedOperationException("TODO: auto-generated method stub") } def CharSequence tree(Module module) ''' «strong("module " + module.name)» «module.childNodes.treeSet(InstanceIdentifier.builder.toInstance())» ''' private def dispatch CharSequence tree(ChoiceNode node,InstanceIdentifier path) ''' «node.nodeName» (choice) «casesTree(node.cases,path)» ''' def casesTree(Set nodes,InstanceIdentifier path) '''
      «FOR node : nodes»
    • «node.nodeName» «node.childNodes.treeSet(path)»
    • «ENDFOR»
    ''' private def dispatch CharSequence tree(DataSchemaNode node,InstanceIdentifier path) ''' «node.nodeName» ''' private def dispatch CharSequence tree(ListSchemaNode node,InstanceIdentifier path) ''' «val newPath = path.append(node)» «localLink(newPath,node.nodeName)» «node.childNodes.treeSet(newPath)» ''' private def dispatch CharSequence tree(ContainerSchemaNode node,InstanceIdentifier path) ''' «val newPath = path.append(node)» «localLink(newPath,node.nodeName)» «node.childNodes.treeSet(newPath)» ''' def CharSequence childNodes(Module module) ''' «val childNodes = module.childNodes» «IF childNodes !== null && !childNodes.empty»

    Child nodes

    «childNodes.printChildren(2,InstanceIdentifier.builder().toInstance())» «ENDIF» ''' def CharSequence printChildren(Set nodes, int level, InstanceIdentifier path) { val leafNodes = nodes.filter(LeafSchemaNode) val leafListNodes = nodes.filter(LeafListSchemaNode) val choices = nodes.filter(ChoiceNode) val cases = nodes.filter(ChoiceCaseNode) val containers = nodes.filter(ContainerSchemaNode) val lists = nodes.filter(ListSchemaNode) return '''

    Direct children

      «FOR childNode : leafNodes» «childNode.printShortInfo(level,path)» «ENDFOR» «FOR childNode : leafListNodes» «childNode.printShortInfo(level,path)» «ENDFOR» «FOR childNode : containers» «childNode.printShortInfo(level,path)» «ENDFOR» «FOR childNode : lists» «childNode.printShortInfo(level,path)» «ENDFOR»
    «IF !path.path.empty»

    XML example

    «nodes.xmlExample(path.path.last.nodeType,path)» «ENDIF» «FOR childNode : containers» «childNode.printInfo(level,path)» «ENDFOR» «FOR childNode : lists» «childNode.printInfo(level,path)» «ENDFOR» «FOR childNode : choices» «childNode.printInfo(level,path)» «ENDFOR» «FOR childNode : cases» «childNode.printInfo(level,path)» «ENDFOR» ''' } def CharSequence xmlExample(Set nodes, QName name,InstanceIdentifier path) '''
            «xmlExampleTag(name,nodes.xmplExampleTags(path))»
        
    ''' def CharSequence xmplExampleTags(Set nodes, InstanceIdentifier identifier) ''' «FOR node : nodes» «node.asXmlExampleTag(identifier)» «ENDFOR» ''' private def dispatch CharSequence asXmlExampleTag(LeafSchemaNode node, InstanceIdentifier identifier) ''' «node.QName.xmlExampleTag("...")» ''' private def dispatch CharSequence asXmlExampleTag(LeafListSchemaNode node, InstanceIdentifier identifier) ''' <!-- This node could appear multiple times --> «node.QName.xmlExampleTag("...")» ''' private def dispatch CharSequence asXmlExampleTag(ContainerSchemaNode node, InstanceIdentifier identifier) ''' <!-- See «localLink(identifier.append(node),"definition")» for child nodes. --> «node.QName.xmlExampleTag("...")» ''' private def dispatch CharSequence asXmlExampleTag(ListSchemaNode node, InstanceIdentifier identifier) ''' <!-- See «localLink(identifier.append(node),"definition")» for child nodes. --> <!-- This node could appear multiple times --> «node.QName.xmlExampleTag("...")» ''' private def dispatch CharSequence asXmlExampleTag(DataSchemaNode node, InstanceIdentifier identifier) ''' ''' def xmlExampleTag(QName name, CharSequence data) { return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>''' } private def dispatch CharSequence printInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) ''' «val newPath = path.append(node)» «header(level,newPath)»
    XML Path
    «newPath.asXmlPath»
    Restconf path
    «code(newPath.asRestconfPath)»
    «node.childNodes.printChildren(level,newPath)» ''' def header(int level,QName name) '''«name.localName»''' def header(int level,InstanceIdentifier name) ''' «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR» ''' private def dispatch CharSequence printInfo(ListSchemaNode node, int level, InstanceIdentifier path) ''' «val newPath = path.append(node)» «header(level,newPath)»
    XML Path
    «newPath.asXmlPath»
    Restconf path
    «code(newPath.asRestconfPath)»
    «node.childNodes.printChildren(level,newPath)» ''' private def dispatch CharSequence printInfo(ChoiceNode node, int level, InstanceIdentifier path) ''' «val Set choiceCases = new HashSet(node.cases)» «choiceCases.printChildren(level,path)» ''' private def dispatch CharSequence printInfo(ChoiceCaseNode node, int level, InstanceIdentifier path) ''' «node.childNodes.printChildren(level,path)» ''' def CharSequence printShortInfo(ContainerSchemaNode node, int level, InstanceIdentifier path) { val newPath = path.append(node); return '''
  • «strong(localLink(newPath,node.QName.localName))» (container)
  • ''' } def CharSequence printShortInfo(ListSchemaNode node, int level, InstanceIdentifier path) { val newPath = path.append(node); return '''
  • «strong(localLink(newPath,node.QName.localName))» (list)
  • ''' } def CharSequence printShortInfo(LeafSchemaNode node, int level, InstanceIdentifier path) { return '''
  • «strong((node.QName.localName))» (leaf)
  • ''' } def CharSequence printShortInfo(LeafListSchemaNode node, int level, InstanceIdentifier path) { return '''
  • «strong((node.QName.localName))» (leaf-list)
  • ''' } def CharSequence localLink(InstanceIdentifier identifier, CharSequence text) ''' «text» ''' private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ContainerSchemaNode node) { val pathArguments = new ArrayList(identifier.path) pathArguments.add(new NodeIdentifier(node.QName)); return new InstanceIdentifier(pathArguments); } private def dispatch InstanceIdentifier append(InstanceIdentifier identifier, ListSchemaNode node) { val pathArguments = new ArrayList(identifier.path) val keyValues = new LinkedHashMap(); if(node.keyDefinition != null) { for(definition : node.keyDefinition) { keyValues.put(definition,new Object); } } pathArguments.add(new NodeIdentifierWithPredicates(node.QName,keyValues)); return new InstanceIdentifier(pathArguments); } def asXmlPath(InstanceIdentifier identifier) { return ""; } def asRestconfPath(InstanceIdentifier identifier) { val it = new StringBuilder(); append(currentModule.name) append(":") var previous = false; for(arg : identifier.path) { if(previous) append("/") append(arg.nodeType.localName); previous = true; if(arg instanceof NodeIdentifierWithPredicates) { val nodeIdentifier = arg as NodeIdentifierWithPredicates; for(qname : nodeIdentifier.keyValues.keySet) { append("/{"); append(qname.localName) append("}") } } } return it.toString; } private def dispatch CharSequence printInfo(DataSchemaNode node, int level, InstanceIdentifier path) ''' «header(level+1,node.QName)» ''' def CharSequence childNodesInfoTree(Map childNodes) ''' «IF childNodes !== null && !childNodes.empty» «FOR child : childNodes.values» «childInfo(child, childNodes)» «ENDFOR» «ENDIF» ''' def CharSequence childInfo(DataSchemaNode node, Map childNodes) ''' «val String path = nodeSchemaPathToPath(node, childNodes)» «IF path != null» «code(path)» «IF node !== null»
      «node.descAndRef»
    «ENDIF» «ENDIF» ''' private def CharSequence treeSet(Collection childNodes, InstanceIdentifier path) ''' «IF childNodes !== null && !childNodes.empty»
      «FOR child : childNodes»
    • «child.tree(path)»
    • «ENDFOR»
    «ENDIF» ''' def listKeys(ListSchemaNode node) ''' [«FOR key : node.keyDefinition SEPARATOR " "»«key.localName»«ENDFOR»] ''' private def CharSequence augmentationInfo(AugmentationSchema augment, InstanceIdentifier path) '''
      «listItem(augment.description)» «listItem("Reference", augment.reference)» «IF augment.whenCondition !== null» «listItem("When", augment.whenCondition.toString)» «ENDIF»
    • Path «augment.targetPath.path.pathToTree»
    • Child nodes «augment.childNodes.treeSet(path)»
    ''' private def CharSequence notificationInfo(NotificationDefinition notification,InstanceIdentifier path) '''
      «notification.descAndRef»
    • Child nodes «notification.childNodes.treeSet(path)»
    ''' private def CharSequence rpcInfo(RpcDefinition rpc,InstanceIdentifier path) '''
      «rpc.descAndRef»
    • «rpc.input.tree(path)»
    • «rpc.output.tree(path)»
    ''' private def CharSequence extensionInfo(ExtensionDefinition ext, InstanceIdentifier path) '''
      «ext.descAndRef» «listItem("Argument", ext.argument)»
    ''' private def dispatch CharSequence tree(Void obj, InstanceIdentifier path) ''' ''' /* #################### RESTRICTIONS #################### */ private def restrictions(TypeDefinition type) ''' «type.toLength» «type.toRange» ''' private def dispatch toLength(TypeDefinition type) { } private def dispatch toLength(BinaryTypeDefinition type) ''' «type.lengthConstraints.toLengthStmt» ''' private def dispatch toLength(StringTypeDefinition type) ''' «type.lengthConstraints.toLengthStmt» ''' private def dispatch toLength(ExtendedType type) ''' «type.lengthConstraints.toLengthStmt» ''' private def dispatch toRange(TypeDefinition type) { } private def dispatch toRange(DecimalTypeDefinition type) ''' «type.rangeConstraints.toRangeStmt» ''' private def dispatch toRange(IntegerTypeDefinition type) ''' «type.rangeConstraints.toRangeStmt» ''' private def dispatch toRange(UnsignedIntegerTypeDefinition type) ''' «type.rangeConstraints.toRangeStmt» ''' private def dispatch toRange(ExtendedType type) ''' «type.rangeConstraints.toRangeStmt» ''' def toLengthStmt(Collection lengths) ''' «IF lengths != null && !lengths.empty» «listItem("Length restrictions")»
      «FOR length : lengths»
    • «IF length.min == length.max» «length.min» «ELSE» <«length.min», «length.max»> «ENDIF»
    • «ENDFOR»
    «ENDIF» ''' def toRangeStmt(Collection ranges) ''' «IF ranges != null && !ranges.empty» «listItem("Range restrictions")»
      «FOR range : ranges»
    • «IF range.min == range.max» «range.min» «ELSE» <«range.min», «range.max»> «ENDIF»
    • «ENDFOR»
    «ENDIF» ''' /* #################### UTILITY #################### */ private def String strong(CharSequence str) '''«str»''' private def italic(CharSequence str) '''«str»''' private def pre(CharSequence str) '''
    «str»
    ''' def CharSequence descAndRef(SchemaNode node) ''' «listItem(node.description)» «listItem("Reference", node.reference)» ''' private def listItem(String value) ''' «IF value !== null && !value.empty»
  • «value»
  • «ENDIF» ''' private def listItem(String name, String value) ''' «IF value !== null && !value.empty»
  • «name»
    • «value»
  • «ENDIF» ''' private def String nodeSchemaPathToPath(DataSchemaNode node, Map childNodes) { if (node instanceof ChoiceNode || node instanceof ChoiceCaseNode) { return null } val path = node.path.path val absolute = node.path.absolute; var StringBuilder result = new StringBuilder if (absolute) { result.append("/") } if (path !== null && !path.empty) { val List actual = new ArrayList() var i = 0; for (pathElement : path) { actual.add(pathElement) val DataSchemaNode nodeByPath = childNodes.get(new SchemaPath(actual, absolute)) if (!(nodeByPath instanceof ChoiceNode) && !(nodeByPath instanceof ChoiceCaseNode)) { result.append(pathElement.localName) if (i != path.size - 1) { result.append("/") } } i = i + 1 } } return result.toString } private def void collectChildNodes(Collection source, Map destination) { for (node : source) { destination.put(node.path, node) if (node instanceof DataNodeContainer) { collectChildNodes((node as DataNodeContainer).childNodes, destination) } if (node instanceof ChoiceNode) { val List choiceCases = new ArrayList() for (caseNode : (node as ChoiceNode).cases) { choiceCases.add(caseNode) } collectChildNodes(choiceCases, destination) } } } private def CharSequence pathToTree(List path) ''' «IF path !== null && !path.empty»
      «FOR pathElement : path»
    • «pathElement.namespace» «pathElement.localName»
    • «ENDFOR»
    «ENDIF» ''' private def dispatch addedByInfo(SchemaNode node) ''' ''' private def dispatch addedByInfo(DataSchemaNode node) ''' «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF» ''' private def dispatch isAddedBy(SchemaNode node) { return false; } private def dispatch isAddedBy(DataSchemaNode node) { if (node.augmenting || node.addedByUses) { return true } else { return false; } } private def dispatch nodeName(SchemaNode node) ''' «IF node.isAddedBy» «italic(node.QName.localName)»«node.addedByInfo» «ELSE» «node.QName.localName»«node.addedByInfo» «ENDIF» ''' private def dispatch nodeName(ContainerSchemaNode node) ''' «IF node.isAddedBy» «strong(italic(node.QName.localName))»«node.addedByInfo» «ELSE» «strong(node.QName.localName)»«node.addedByInfo» «ENDIF» ''' private def dispatch nodeName(ListSchemaNode node) ''' «IF node.isAddedBy» «strong(italic(node.QName.localName))» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF»«node.addedByInfo» «ELSE» «strong(node.QName.localName)» «IF node.keyDefinition !== null && !node.keyDefinition.empty»«node.listKeys»«ENDIF» «ENDIF» ''' }