/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.binding.yang.unified.doc.generator import static com.google.common.base.Preconditions.checkState; import java.io.BufferedWriter import java.io.File import java.io.IOException import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets import java.nio.file.Files import java.util.ArrayList import java.util.Collection import java.util.HashMap import java.util.HashSet import java.util.LinkedHashMap import java.util.List import java.util.Map import java.util.Optional import java.util.Set import org.gaul.modernizer_maven_annotations.SuppressModernizer import org.opendaylight.yangtools.yang.common.QName import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode import org.opendaylight.yangtools.yang.model.api.AugmentationTarget import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode import org.opendaylight.yangtools.yang.model.api.DataNodeContainer import org.opendaylight.yangtools.yang.model.api.DataSchemaNode import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition import org.opendaylight.yangtools.yang.model.api.GroupingDefinition import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode import org.opendaylight.yangtools.yang.model.api.ListSchemaNode import org.opendaylight.yangtools.yang.model.api.Module import org.opendaylight.yangtools.yang.model.api.NotificationDefinition import org.opendaylight.yangtools.yang.model.api.SchemaNode import org.opendaylight.yangtools.yang.model.api.SchemaPath import org.opendaylight.yangtools.yang.model.api.TypeDefinition import org.opendaylight.yangtools.yang.model.api.UsesNode import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute 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.Int8TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition import org.slf4j.Logger import org.slf4j.LoggerFactory import org.sonatype.plexus.build.incremental.BuildContext @SuppressModernizer class GeneratorImpl { static val Logger LOG = LoggerFactory.getLogger(GeneratorImpl) val Map imports = new HashMap(); var Module currentModule; var EffectiveModelContext ctx; var File path StringBuilder augmentChildNodesAsString DataSchemaNode lastNodeInTargetPath = null def generate(BuildContext buildContext, EffectiveModelContext context, File targetPath, Set modulesToGen) throws IOException { path = targetPath; Files.createDirectories(path.getParentFile().toPath()) val it = new HashSet; for (module : modulesToGen) { add(generateDocumentation(buildContext, module, context)); } return it; } def generateDocumentation(BuildContext buildContext, Module module, EffectiveModelContext ctx) { val destination = new File(path, '''«module.name».html''') this.ctx = ctx; module.imports.forEach[importModule | this.imports.put(importModule.prefix, importModule.moduleName)] var OutputStreamWriter fw var BufferedWriter bw try { fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8) bw = new BufferedWriter(fw) currentModule = module; bw.append(generate(module, ctx)); } catch (IOException e) { LOG.error("Failed to emit file {}", destination, e); } finally { if (bw !== null) { bw.close(); } if (fw !== null) { fw.close(); } } return destination; } def generate(Module module, EffectiveModelContext ctx) ''' «module.name» «body(module, ctx)» ''' def body(Module module, EffectiveModelContext ctx) ''' «header(module)» «typeDefinitionsSummary(module)» «identitiesSummary(module)» «groupingsSummary(module)» «augmentationsSummary(module, ctx)» «objectsSummary(module)» «notificationsSummary(module)» «rpcsSummary(module)» «extensionsSummary(module)» «featuresSummary(module)» «typeDefinitions(module)» «identities(module)» «groupings(module)» «dataStore(module)» «childNodes(module)» «notifications(module)» «augmentations(module, ctx)» «rpcs(module)» «extensions(module)» «features(module)» ''' private def typeDefinitionsSummary(Module module) { val Collection> typedefs = module.typeDefinitions if (typedefs.empty) { return ''; } return '''

Type Definitions Summary

«FOR typedef : typedefs» «ENDFOR»
Name Description
«anchorLink(typedef.QName.localName, strong(typedef.QName.localName))» «typedef.description»
''' } def typeDefinitions(Module module) { val Collection> typedefs = module.typeDefinitions if (typedefs.empty) { return ''; } return '''

Type Definitions

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

Identities

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

Identities Summary

«FOR identity : module.identities» «ENDFOR»
Name Description
«anchorLink(identity.QName.localName, strong(identity.QName.localName))» «identity.description»
''' } private def groupings(Module module) { if (module.groupings.empty) { return ''; } return '''

Groupings

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

Groupings Summary

«FOR grouping : module.groupings» «ENDFOR»
Name Description
«anchorLink(grouping.QName.localName, strong(grouping.QName.localName))» «grouping.description»
''' } def dataStore(Module module) { if (module.childNodes.empty) { return ''; } return '''

Datastore Structure

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

Augmentations

''' } private def createAugmentChildNodesAsString(List childNodes) { augmentChildNodesAsString = new StringBuilder(); augmentChildNodesAsString.append(printNodeChildren(childNodes)) return '' } private def parseTargetPath(SchemaNodeIdentifier path) { val nodes = new ArrayList(); for (QName pathElement : path.nodeIdentifiers) { val module = ctx.findModule(pathElement.module) if (module.isPresent) { var foundNode = module.get.getDataChildByName(pathElement) if (foundNode === null) { val child = nodes.last if (child instanceof DataNodeContainer) { val dataContNode = child as DataNodeContainer foundNode = findNodeInChildNodes(pathElement, dataContNode.childNodes) } } if (foundNode !== null) { nodes.add(foundNode); } } } if (!nodes.empty) { lastNodeInTargetPath = nodes.get(nodes.size() - 1) } val targetPathNodes = new ArrayList(); targetPathNodes.add(lastNodeInTargetPath) return targetPathNodes } private def DataSchemaNode findNodeInChildNodes(QName findingNode, Iterable childNodes) { for (child : childNodes) { if (child.QName.equals(findingNode)) return child; } // find recursively for(child : childNodes) { if (child instanceof ContainerSchemaNode) { val foundChild = findNodeInChildNodes(findingNode, child.childNodes) if (foundChild !== null) return foundChild; } else if (child instanceof ListSchemaNode) { val foundChild = findNodeInChildNodes(findingNode, child.childNodes) if (foundChild !== null) return foundChild; } } } private def printNodeChildren(List childNodes) { if (childNodes.empty) { return '' } return '''
        «printAugmentedNode(childNodes.get(0))»
        
''' } private def CharSequence printAugmentedNode(DataSchemaNode child) { if(child instanceof CaseSchemaNode) return '' return ''' «IF child instanceof ContainerSchemaNode» «printContainerNode(child)» «ENDIF» «IF child instanceof AnyxmlSchemaNode» «printAnyXmlNode(child)» «ENDIF» «IF child instanceof LeafSchemaNode» «printLeafNode(child)» «ENDIF» «IF child instanceof LeafListSchemaNode» «printLeafListNode(child)» «ENDIF» «IF child instanceof ListSchemaNode» «printListNode(child)» «ENDIF» «IF child instanceof ChoiceSchemaNode» «printChoiceNode(child)» «ENDIF» ''' } private def printChoiceNode(ChoiceSchemaNode child) { val cases = new ArrayList(child.cases) if (!cases.empty) { val CaseSchemaNode aCase = cases.get(0) for (caseChildNode : aCase.childNodes) printAugmentedNode(caseChildNode) } } private def printListNode(ListSchemaNode listNode) { return ''' <«listNode.QName.localName»«IF !listNode.QName.namespace.equals(currentModule.namespace)» xmlns="«listNode.QName.namespace»"«ENDIF»> «FOR child : listNode.childNodes» «printAugmentedNode(child)» «ENDFOR» </«listNode.QName.localName»> ''' } private def printContainerNode(ContainerSchemaNode containerNode) { return ''' <«containerNode.QName.localName»«IF !containerNode.QName.namespace.equals(currentModule.namespace)» xmlns="«containerNode.QName.namespace»"«ENDIF»> «FOR child : containerNode.childNodes» «printAugmentedNode(child)» «ENDFOR» </«containerNode.QName.localName»> ''' } private def printLeafListNode(LeafListSchemaNode leafListNode) { return ''' <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»> <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»> <«leafListNode.QName.localName»>. . .</«leafListNode.QName.localName»> ''' } private def printAnyXmlNode(AnyxmlSchemaNode anyXmlNode) { return ''' <«anyXmlNode.QName.localName»>. . .</«anyXmlNode.QName.localName»> ''' } private def printLeafNode(LeafSchemaNode leafNode) { return ''' <«leafNode.QName.localName»>. . .</«leafNode.QName.localName»> ''' } private def augmentationsSummary(Module module, EffectiveModelContext context) { if (module.augmentations.empty) { return ''; } return '''

Augmentations Summary

«FOR augment : module.augmentations» «ENDFOR»
Target Description
«anchorLink(schemaPathToString(module, augment.targetPath, context, augment), strong(schemaPathToString(module, augment.targetPath, context, augment)))» «augment.description»
''' } def notifications(Module module) { val Collection notificationdefs = module.notifications if (notificationdefs.empty) { return ''; } return '''

Notifications

«FOR notificationdef : notificationdefs»

«notificationdef.nodeName»

«notificationdef.descAndRef» «FOR childNode : notificationdef.childNodes» «childNode.printSchemaNodeInfo» «ENDFOR» «ENDFOR» ''' } private def notificationsSummary(Module module) { if (module.notifications.empty) { return ''; } return '''

Notifications Summary

«FOR notification : module.notifications» «ENDFOR»
Name Description
«anchorLink(notification.path.schemaPathToId, strong(notification.QName.localName))» «notification.description»
''' } def rpcs(Module module) { if (module.rpcs.empty) { return ''; } return '''

RPC Definitions

«FOR rpc : module.rpcs»

«rpc.nodeName»

    «rpc.descAndRefLi» «rpc.input.printSchemaNodeInfo» «rpc.output.printSchemaNodeInfo»
«ENDFOR» ''' } private def rpcsSummary(Module module) { if (module.rpcs.empty) { return ''; } return '''

RPCs Summary

«FOR rpc : module.rpcs» «ENDFOR»
Name Description
«anchorLink(rpc.QName.localName, strong(rpc.QName.localName))» «rpc.description»
''' } def extensions(Module module) { if (module.extensionSchemaNodes.empty) { return ''; } return '''

Extensions

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

  • «extensionInfo(ext)» «ENDFOR» ''' } private def extensionsSummary(Module module) { if (module.extensionSchemaNodes.empty) { return ''; } return '''

    Extensions Summary

    «FOR ext : module.extensionSchemaNodes» «ENDFOR»
    Name Description
    «anchorLink(ext.QName.localName, strong(ext.QName.localName))» «ext.description»
    ''' } def features(Module module) { if (module.features.empty) { return ''; } return '''

    Features

      «FOR feature : module.features»
    • «feature.QName.localName»

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

    Features Summary

    «FOR feature : module.features» «ENDFOR»
    Name Description
    «anchorLink(feature.QName.localName, strong(feature.QName.localName))» «feature.description»
    ''' } private def objectsSummary(Module module) { if (module.childNodes.empty) { return ''; } return '''

    Child Nodes Summary

    «FOR childNode : module.childNodes» «ENDFOR»
    Name Description
    «anchorLink(childNode.QName.localName, strong(childNode.QName.localName))» «childNode.description»
    ''' } def header(Module module) '''

    «module.name»

    Base Information

    «IF module.revision.isPresent» «ENDIF» «FOR imp : module.imports BEFORE ''''''» «imp.prefix»:«imp.moduleName»«IF imp.revision.isPresent» «imp.revision.get.toString»«ENDIF»; «ENDFOR»
    «strong("prefix")» «module.prefix»
    «strong("namespace")» «module.namespace»
    «strong("revision")» «module.revision.get.toString»
    «strong("description")» «module.description»
    «strong("yang-version")» «module.yangVersion»
    «strong("imports")»''' AFTER '''
    ''' def CharSequence schemaPathToId(SchemaPath path) { if(path !== null) { return '''«FOR qName : path.pathFromRoot SEPARATOR "/"»«qName.localName»«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.name)» «module.childNodes.treeSet(YangInstanceIdentifier.builder.build())» ''' private def CharSequence tree(ChoiceSchemaNode node,YangInstanceIdentifier path) ''' «node.nodeName» (choice) «casesTree(node.cases, path)» ''' def casesTree(Collection nodes, YangInstanceIdentifier path) '''
      «FOR node : nodes»
    • «node.nodeName» «node.childNodes.treeSet(path)»
    • «ENDFOR»
    ''' private def CharSequence tree(DataSchemaNode node,YangInstanceIdentifier path) { if (node instanceof ChoiceSchemaNode) { return tree(node as ChoiceSchemaNode, path) } if (node instanceof ListSchemaNode) { return tree(node as ListSchemaNode, path) } if (node instanceof ContainerSchemaNode) { return tree(node as ContainerSchemaNode, path) } return node.nodeName } private def CharSequence tree(ListSchemaNode node,YangInstanceIdentifier path) ''' «val newPath = path.append(node)» «localLink(newPath,node.nodeName)» «node.childNodes.treeSet(newPath)» ''' private def CharSequence tree(ContainerSchemaNode node,YangInstanceIdentifier 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.nullOrEmpty»

    Child nodes

    «childNodes.printChildren(3,YangInstanceIdentifier.builder().build())» «ENDIF» ''' def CharSequence printSchemaNodeInfo(DataSchemaNode node) { return '''
      «node.printBaseInfo» «IF node instanceof DataNodeContainer» «val dataNode = node as DataNodeContainer»
        «FOR usesNode : dataNode.uses» «usesNode.printUses» «ENDFOR»
        «FOR typeDef : dataNode.typeDefinitions» «typeDef.restrictions» «ENDFOR»
        «FOR grouping : dataNode.groupings» «grouping.printGrouping» «ENDFOR»
        «FOR child : dataNode.childNodes» «child.printSchemaNodeInfo» «ENDFOR»
      «ENDIF»
    ''' } def String typeAnchorLink(SchemaPath path, CharSequence text) { if(path !== null) { val lastElement = path.lastComponent val ns = lastElement.namespace if (ns == this.currentModule.namespace) { return '''«text»''' } else { return '''(«ns»)«text»''' //to enable external (import) links //return '''«prefix»:«text»''' } } } def CharSequence printBaseInfo(SchemaNode node) { if(node instanceof LeafSchemaNode) { return ''' «printInfo(node, "leaf")» «listItem("type", typeAnchorLink(node.type?.path, node.type.QName.localName))» «listItem("units", node.type.units.orElse(null))» «listItem("default", node.type.defaultValue.map([ Object o | o.toString]).orElse(null))» ''' } else if(node instanceof LeafListSchemaNode) { return ''' «printInfo(node, "leaf-list")» «IF node.type !== null» «listItem("type", node.type.QName.localName)» «ENDIF» ''' } else if(node instanceof ListSchemaNode) { return ''' «printInfo(node, "list")» «FOR keyDef : node.keyDefinition» «listItem("key definition", keyDef.localName)» «ENDFOR» ''' } else if(node instanceof ChoiceSchemaNode) { return ''' «printInfo(node, "choice")» «listItem("default case", node.defaultCase.map([ CaseSchemaNode n | n.getQName.localName]).orElse(null))» «FOR caseNode : node.cases» «caseNode.printSchemaNodeInfo» «ENDFOR» ''' } else if(node instanceof CaseSchemaNode) { return ''' «printInfo(node, "case")» ''' } else if(node instanceof ContainerSchemaNode) { return ''' «printInfo(node, "container")» ''' } else if(node instanceof AnyxmlSchemaNode) { return ''' «printInfo(node, "anyxml")» ''' } } def CharSequence printInfo(SchemaNode node, String nodeType) { return ''' «IF node instanceof AugmentationTarget» «IF node !== null»
  • «nodeType»: «node.QName.localName»
  • «ENDIF» «ELSE» «strong(listItem(nodeType, node.QName.localName))» «ENDIF»
      «listItem("description", node.description.orElse(null))» «listItem("reference", node.reference.orElse(null))» «IF node instanceof DataSchemaNode» «IF node.whenCondition.present» «listItem("when condition", node.whenCondition.get.toString)» «ENDIF» «ENDIF» «IF node instanceof ElementCountConstraintAware» «IF node.elementCountConstraint.present» «val constraint = node.elementCountConstraint.get» «listItem("min elements", constraint.minElements?.toString)» «listItem("max elements", constraint.maxElements?.toString)» «ENDIF» «ENDIF» ''' } def CharSequence printUses(UsesNode usesNode) { return ''' «strong(listItem("uses", typeAnchorLink(usesNode.sourceGrouping.path, usesNode.sourceGrouping.path.pathTowardsRoot.iterator.next.localName)))»
      • refines:
          «FOR sp : usesNode.refines.keySet» «listItem("node name", usesNode.refines.get(sp).QName.localName)» «ENDFOR»
      • «FOR augment : usesNode.augmentations» «typeAnchorLink(augment.targetPath.asSchemaPath, schemaPathToString(currentModule, augment.targetPath, ctx, augment))» «ENDFOR»
      ''' } def CharSequence printGrouping(GroupingDefinition grouping) { return ''' «strong(listItem("grouping", grouping.QName.localName))» ''' } def CharSequence printChildren(Iterable nodes, int level, YangInstanceIdentifier path) { val anyxmlNodes = nodes.filter(AnyxmlSchemaNode) val leafNodes = nodes.filter(LeafSchemaNode) val leafListNodes = nodes.filter(LeafListSchemaNode) val choices = nodes.filter(ChoiceSchemaNode) val cases = nodes.filter(CaseSchemaNode) val containers = nodes.filter(ContainerSchemaNode) val lists = nodes.filter(ListSchemaNode) return ''' «IF ((anyxmlNodes.size + leafNodes.size + leafListNodes.size + containers.size + lists.size) > 0)»

      Direct children

        «FOR childNode : anyxmlNodes» «childNode.printShortInfo(level,path)» «ENDFOR» «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»
      «ENDIF» «IF path.pathArguments.iterator.hasNext»

      XML example

      «nodes.xmlExample(path.pathArguments.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(Iterable nodes, QName name, YangInstanceIdentifier path) '''
              «xmlExampleTag(name,nodes.xmplExampleTags(path))»
          
      ''' def CharSequence xmplExampleTags(Iterable nodes, YangInstanceIdentifier identifier) ''' «FOR node : nodes» «node.asXmlExampleTag(identifier)» «ENDFOR» ''' private def CharSequence asXmlExampleTag(DataSchemaNode node, YangInstanceIdentifier identifier) { if (node instanceof LeafSchemaNode) { return '''«node.QName.xmlExampleTag("...")»''' } if (node instanceof LeafListSchemaNode) { return ''' <!-- This node could appear multiple times --> «node.QName.xmlExampleTag("...")» ''' } if (node instanceof ContainerSchemaNode) { return ''' <!-- See «localLink(identifier.append(node),"definition")» for child nodes. --> «node.QName.xmlExampleTag("...")» ''' } if (node instanceof ListSchemaNode) { return ''' <!-- See «localLink(identifier.append(node),"definition")» for child nodes. --> <!-- This node could appear multiple times --> «node.QName.xmlExampleTag("...")» ''' } return "" } def xmlExampleTag(QName name, CharSequence data) { return '''<«name.localName» xmlns="«name.namespace»">«data»</«name.localName»>''' } def header(int level,QName name) '''«name.localName»''' def header(int level,YangInstanceIdentifier name) ''' «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR» ''' private def CharSequence printInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier 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 CharSequence printInfo(ListSchemaNode node, int level, YangInstanceIdentifier 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 CharSequence printInfo(ChoiceSchemaNode node, int level, YangInstanceIdentifier path) ''' «val Set choiceCases = new HashSet(node.cases)» «choiceCases.printChildren(level, path)» ''' private def CharSequence printInfo(CaseSchemaNode node, int level, YangInstanceIdentifier path) ''' «node.childNodes.printChildren(level, path)» ''' def CharSequence printShortInfo(ContainerSchemaNode node, int level, YangInstanceIdentifier path) { val newPath = path.append(node); return '''
    • «strong(localLink(newPath,node.QName.localName))» (container)
      • configuration data: «strong(String.valueOf(node.configuration))»
    • ''' } def CharSequence printShortInfo(ListSchemaNode node, int level, YangInstanceIdentifier path) { val newPath = path.append(node); return '''
    • «strong(localLink(newPath,node.QName.localName))» (list)
      • configuration data: «strong(String.valueOf(node.configuration))»
    • ''' } def CharSequence printShortInfo(AnyxmlSchemaNode node, int level, YangInstanceIdentifier path) { return '''
    • «strong((node.QName.localName))» (anyxml)
      • configuration data: «strong(String.valueOf(node.configuration))»
      • mandatory: «strong(String.valueOf(node.mandatory))»
    • ''' } def CharSequence printShortInfo(LeafSchemaNode node, int level, YangInstanceIdentifier path) { return '''
    • «strong((node.QName.localName))» (leaf)
      • configuration data: «strong(String.valueOf(node.configuration))»
      • mandatory: «strong(String.valueOf(node.mandatory))»
    • ''' } def CharSequence printShortInfo(LeafListSchemaNode node, int level, YangInstanceIdentifier path) { return '''
    • «strong((node.QName.localName))» (leaf-list)
      • configuration data: «strong(String.valueOf(node.configuration))»
    • ''' } def CharSequence anchorLink(CharSequence anchor, CharSequence text) { return ''' «text» ''' } def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) ''' «text» ''' private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ContainerSchemaNode node) { return identifier.node(node.QName); } private def dispatch YangInstanceIdentifier append(YangInstanceIdentifier identifier, ListSchemaNode node) { val keyValues = new LinkedHashMap(); if(node.keyDefinition !== null) { for(definition : node.keyDefinition) { keyValues.put(definition,new Object); } } return identifier.node(NodeIdentifierWithPredicates.of(node.QName, keyValues)); } def asXmlPath(YangInstanceIdentifier identifier) { return ""; } def asRestconfPath(YangInstanceIdentifier identifier) { val it = new StringBuilder(); append(currentModule.name) append(':') var previous = false; for(arg : identifier.pathArguments) { if(previous) append('/') append(arg.nodeType.localName); previous = true; if(arg instanceof NodeIdentifierWithPredicates) { for(qname : arg.keySet) { append("/{"); append(qname.localName) append('}') } } } return it.toString; } private def String schemaPathToString(Module module, SchemaNodeIdentifier schemaPath, EffectiveModelContext ctx, DataNodeContainer dataNode) { val path = schemaPath.nodeIdentifiers val StringBuilder pathString = new StringBuilder() if (schemaPath instanceof Absolute) { pathString.append('/') } val QName qname = path.get(0) var Object parent = ctx.findModule(qname.module).orElse(null) for (name : path) { if (parent instanceof DataNodeContainer) { var SchemaNode node = parent.getDataChildByName(name) if (node === null && (parent instanceof Module)) { val notifications = (parent as Module).notifications; for (notification : notifications) { if (notification.QName.equals(name)) { node = notification } } } if (node === null && (parent instanceof Module)) { val rpcs = (parent as Module).rpcs; for (rpc : rpcs) { if (rpc.QName.equals(name)) { node = rpc } } } val pathElementModule = ctx.findModule(name.module).get val String moduleName = pathElementModule.name pathString.append(moduleName) pathString.append(':') pathString.append(name.localName) pathString.append('/') if(node instanceof ChoiceSchemaNode && dataNode !== null) { val DataSchemaNode caseNode = dataNode.childNodes.findFirst[DataSchemaNode e | e instanceof CaseSchemaNode]; if(caseNode !== null) { pathString.append("(case)"); pathString.append(caseNode.QName.localName); } } parent = node } } return pathString.toString; } 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.descAndRefLi»
      «ENDIF» «ENDIF» ''' private def CharSequence treeSet(Collection childNodes, YangInstanceIdentifier 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 extensionInfo(ExtensionDefinition ext) '''
        «ext.descAndRefLi» «listItem("Argument", ext.argument)»
      ''' /* #################### RESTRICTIONS #################### */ private def restrictions(TypeDefinition type) ''' «type.baseType.toBaseStmt» «type.toLength» «type.toRange» ''' private def toLength(TypeDefinition type) ''' «IF type instanceof LengthRestrictedTypeDefinition» «type.lengthConstraint.toLengthStmt» «ENDIF» ''' private def toRange(TypeDefinition type) ''' «IF type instanceof RangeRestrictedTypeDefinition» «type.rangeConstraint.toRangeStmt» «ENDIF» ''' def toLengthStmt(Optional lengths) ''' «IF lengths.isPresent» «listItem("Length restrictions:")»
        «FOR length : lengths.get.allowedRanges.asRanges»
      • «IF length.lowerEndpoint == length.upperEndpoint» «length.lowerEndpoint» «ELSE» <«length.lowerEndpoint», «length.upperEndpoint»> «ENDIF»
      • «ENDFOR»
      «ENDIF» ''' def toRangeStmt(Optional> constraint) ''' «IF constraint.present» «listItem("Range restrictions:")»
        «FOR range : constraint.get.allowedRanges.asRanges»
      • «IF range.lowerEndpoint == range.upperEndpoint» «range.lowerEndpoint» «ELSE» <«range.lowerEndpoint», «range.upperEndpoint»> «ENDIF»
      • «ENDFOR»
      «ENDIF» ''' def toBaseStmt(TypeDefinition baseType) ''' «IF baseType !== null» «listItem("Base type", typeAnchorLink(baseType?.path, baseType.QName.localName))» «ENDIF» ''' /* #################### UTILITY #################### */ private def String strong(CharSequence str) '''«str»''' private def italic(CharSequence str) '''«str»''' def CharSequence descAndRefLi(SchemaNode node) ''' «listItem("Description", node.description.orElse(null))» «listItem("Reference", node.reference.orElse(null))» ''' def CharSequence descAndRef(SchemaNode node) ''' «node.description» «IF node.reference !== null» Reference «node.reference» «ENDIF» ''' 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 ChoiceSchemaNode || node instanceof CaseSchemaNode) { return null } val path = node.path.pathFromRoot val absolute = node.path.absolute; var StringBuilder result = new StringBuilder if (absolute) { result.append('/') } if (path !== null && !path.empty) { val actual = new ArrayList() var i = 0; for (pathElement : path) { actual.add(pathElement) val DataSchemaNode nodeByPath = childNodes.get(SchemaPath.create(actual, absolute)) if (!(nodeByPath instanceof ChoiceSchemaNode) && !(nodeByPath instanceof CaseSchemaNode)) { result.append(pathElement.localName) if (i != path.size - 1) { result.append('/') } } i = i + 1 } } return result.toString } private def addedByInfo(SchemaNode node) { if (node instanceof DataSchemaNode) { return addedByInfo(node as DataSchemaNode) } return "" } private def addedByInfo(DataSchemaNode node) ''' «IF node.augmenting»(A)«ENDIF»«IF node.addedByUses»(U)«ENDIF» ''' private def isAddedBy(SchemaNode node) { if (node instanceof DataSchemaNode) { val dataSchemaNode = node as DataSchemaNode return dataSchemaNode.augmenting || dataSchemaNode.addedByUses } return false } private def nodeName(SchemaNode node) { if (node instanceof ContainerSchemaNode) { return nodeName(node as ContainerSchemaNode); } if (node instanceof ListSchemaNode) { return nodeName(node as ListSchemaNode); } val addedByInfo = node.addedByInfo if (node.isAddedBy) { return '''«italic(node.QName.localName)»«addedByInfo»''' } return '''«node.QName.localName»«addedByInfo»''' } private def nodeName(ContainerSchemaNode node) ''' «IF node.isAddedBy» «strong(italic(node.QName.localName))»«node.addedByInfo» «ELSE» «strong(node.QName.localName)»«node.addedByInfo» «ENDIF» ''' private def 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» ''' }