/* * 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.wadl.generator import static com.google.common.base.Preconditions.checkState; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.BufferedWriter import java.io.File import java.io.IOException import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets import java.util.ArrayList import java.util.Collection import java.util.HashSet import java.util.List import org.opendaylight.yangtools.yang.common.XMLNamespace 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.LeafSchemaNode import org.opendaylight.yangtools.yang.model.api.ListSchemaNode import org.opendaylight.yangtools.yang.model.api.Module import org.slf4j.Logger import org.slf4j.LoggerFactory import org.sonatype.plexus.build.incremental.BuildContext class WadlRestconfGenerator { static val Logger LOG = LoggerFactory.getLogger(WadlRestconfGenerator) static val PATH_DELIMETER = '/' val BuildContext buildContext; val File path var EffectiveModelContext context; var List configData; var List operationalData; var Module module; var List pathListParams; new(BuildContext buildContext, File targetPath) { if (!targetPath.exists) { checkState(targetPath.mkdirs, "Unable to create directory: %s", targetPath); } path = targetPath this.buildContext = buildContext } def generate(EffectiveModelContext context, Collection modules) { val result = new HashSet; this.context = context for (module : modules) { val dataContainers = module.childNodes.filter[it|it.listOrContainer] if (!dataContainers.empty || !module.rpcs.nullOrEmpty) { configData = new ArrayList operationalData = new ArrayList for (data : dataContainers) { if (data.configuration) { configData.add(data) } else { operationalData.add(data) } } this.module = module val destination = new File(path, '''«module.name».wadl''') var OutputStreamWriter fw var BufferedWriter bw try { fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8) bw = new BufferedWriter(fw) bw.append(application); } catch (IOException e) { LOG.error("Failed to emit file {}", destination, e); } finally { if (bw !== null) { bw.close(); } if (fw !== null) { fw.close(); } } result.add(destination) } } return result } private def application() ''' «FOR imprt : module.imports» «ENDFOR» «IF !operationalData.nullOrEmpty» «FOR schemaNode : operationalData» «schemaNode.firstResource(false)» «ENDFOR» «ENDIF» «IF !configData.nullOrEmpty» «FOR schemaNode : configData» «schemaNode.mehodPost» «ENDFOR» «FOR schemaNode : configData» «schemaNode.firstResource(true)» «ENDFOR» «ENDIF» «IF !module.rpcs.nullOrEmpty» «FOR rpc : module.rpcs» «methodPostRpc(rpc.input !== null, rpc.output !== null)» «ENDFOR» «ENDIF» ''' private def importsAsNamespaces(Module module) ''' «FOR imprt : module.imports» xmlns:«imprt.prefix»="«context.findModule(imprt.moduleName, imprt.revision).get.namespace»" «ENDFOR» ''' private def String firstResource(DataSchemaNode schemaNode, boolean config) ''' «resourceBody(schemaNode, config)» ''' private def String resource(DataSchemaNode schemaNode, boolean config) ''' «resourceBody(schemaNode, config)» ''' private def String createPath(DataSchemaNode schemaNode) { pathListParams = new ArrayList var StringBuilder path = new StringBuilder path.append(schemaNode.QName.localName) if (schemaNode instanceof ListSchemaNode) { for (listKey : schemaNode.keyDefinition) { pathListParams.add((schemaNode as DataNodeContainer).getDataChildByName(listKey) as LeafSchemaNode) path.append(PATH_DELIMETER) path.append('{') path.append(listKey.localName) path.append('}') } } return path.toString } private def String resourceBody(DataSchemaNode schemaNode, boolean config) ''' «IF !pathListParams.nullOrEmpty» «resourceParams» «ENDIF» «schemaNode.methodGet» «val children = (schemaNode as DataNodeContainer).childNodes.filter[it|it.listOrContainer]» «IF config» «schemaNode.methodDelete» «schemaNode.mehodPut» «FOR child : children» «child.mehodPost» «ENDFOR» «ENDIF» «FOR child : children» «child.resource(config)» «ENDFOR» ''' private def resourceParams() ''' «FOR pathParam : pathListParams» «IF pathParam !== null» «val type = pathParam.type.QName.localName» «ENDIF» «ENDFOR» ''' private def methodGet(DataSchemaNode schemaNode) ''' «representation(schemaNode.QName.namespace, schemaNode.QName.localName)» ''' private def mehodPut(DataSchemaNode schemaNode) ''' «representation(schemaNode.QName.namespace, schemaNode.QName.localName)» ''' private def mehodPost(DataSchemaNode schemaNode) ''' «representation(schemaNode.QName.namespace, schemaNode.QName.localName)» ''' private def methodPostRpc(boolean input, boolean output) ''' «IF input» «representation(null, "input")» «ENDIF» «IF output» «representation(null, "output")» «ENDIF» ''' private def methodDelete(DataSchemaNode schemaNode) ''' ''' private def representation(XMLNamespace prefix, String name) ''' «val elementData = name» ''' @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "https://github.com/spotbugs/spotbugs/issues/811") private def boolean isListOrContainer(DataSchemaNode schemaNode) { return (schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode) } }