org.ballerinalang.swagger.code.generator.BallerinaConnectorCodeGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.swagger.code.generator.BallerinaConnectorCodeGenerator.java

Source

/*
*  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. licenses this file to you under the Apache License,
*  Version 2.0 (the "License"); you may not use this file except
*  in compliance with the License.
*  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/
package org.ballerinalang.swagger.code.generator;

import io.swagger.codegen.CliOption;
import io.swagger.codegen.CodegenConfig;
import io.swagger.codegen.CodegenConstants;
import io.swagger.codegen.CodegenOperation;
import io.swagger.codegen.CodegenType;
import io.swagger.codegen.DefaultCodegen;
import io.swagger.codegen.SupportingFile;
import io.swagger.models.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

/**
 * This the ballerina connector generator class. Here we can add/update templates to generate
 * different connectors, types services etc.
 */
public class BallerinaConnectorCodeGenerator extends DefaultCodegen implements CodegenConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(BallerinaConnectorCodeGenerator.class);

    protected String apiVersion = "1.0.0";
    protected String apiPath = "";

    public BallerinaConnectorCodeGenerator() {
        super();

        // set the output folder here
        outputFolder = "generated-code/ballerina-connector";

        /*
         * Models.  You can write model files using the modelTemplateFiles map.
         * if you want to create one template for file, you can do so here.
         * for multiple files for model, just put another entry in the `modelTemplateFiles` with
         * a different extension
         */
        modelTemplateFiles.clear();

        /*
         * Api classes.  You can write classes for each Api file with the apiTemplateFiles map.
         * as with models, add multiple entries with different extensions for multiple files per
         * class
         */
        apiTemplateFiles.put("controller.mustache", // the template to use
                ".bal"); // the extension for each file to write

        /*
         * Template Location.  This is the location which templates will be read from.  The generator
         * will use the resource stream to attempt to read the templates.
         */
        embeddedTemplateDir = templateDir = "ballerina-connector";

        /*
         * Reserved words.  Override this with reserved words specific to your language
         */
        setReservedWordsLowerCase(Arrays.asList("action", "all", "any", "as", "boolean", "break", "catch",
                "connector", "const", "datatable", "double", "else", "exception", "fork", "function", "if",
                "import", "int", "json", "map", "message", "native", "package", "reply", "resource", "return",
                "service", "string", "struct", "throws", "timeout", "try", "typemapper", "while", "worker", "xml",
                "join"));

        defaultIncludes = new HashSet<String>(Arrays.asList("map", "array"));

        languageSpecificPrimitives = new HashSet<String>(
                Arrays.asList("string", "boolean", "int", "double", "map", "array"));

        instantiationTypes.clear();
        instantiationTypes.put("array", "string[]");
        instantiationTypes.put("map", "map");
        typeMapping.clear();
        typeMapping.put("integer", "int");
        typeMapping.put("long", "long");
        typeMapping.put("number", "float");
        typeMapping.put("float", "float");
        typeMapping.put("double", "double");
        typeMapping.put("boolean", "boolean");
        typeMapping.put("string", "string");
        typeMapping.put("date", "string");
        typeMapping.put("DateTime", "long");
        typeMapping.put("password", "string");
        typeMapping.put("binary", "string");
        typeMapping.put("ByteArray", "string");
        typeMapping.put("array", "string[]");
        importMapping = new HashMap<String, String>();

        cliOptions.clear();
        cliOptions
                .add(new CliOption(CodegenConstants.PACKAGE_NAME, "Ballerina package name (convention: lowercase).")
                        .defaultValue("swagger"));
        /*
         * Additional Properties.  These values can be passed to the templates and
         * are available in models, apis, and supporting files
         */
        additionalProperties.put("apiVersion", apiVersion);
        additionalProperties.put("apiPath", apiPath);
        /*
         * Supporting Files.  You can write single files for the generator with the
         * entire object tree available.  If the input file has a suffix of `.mustache
         * it will be processed by the template engine.  Otherwise, it will be copied
         */
        //supportingFiles.add(new SupportingFile("model.mustache", apiPath, "types.bal"));
        supportingFiles.add(new SupportingFile("json-model.mustache", apiPath, "types.json"));
        writeOptional(outputFolder, new SupportingFile("README.mustache", apiPath, "README.md"));
        writeOptional(outputFolder, new SupportingFile("json-model.mustache", apiPath, "types.json"));
    }

    @Override
    public String apiPackage() {
        return apiPath;
    }

    /**
     * Configures the type of generator.
     *
     * @return the CodegenType for this generator
     * @see io.swagger.codegen.CodegenType
     */
    @Override
    public CodegenType getTag() {
        return CodegenType.SERVER;
    }

    /**
     * Configures a friendly name for the generator.  This will be used by the generator
     * to select the library with the -l flag.
     *
     * @return the friendly name for the generator
     */
    @Override
    public String getName() {
        return "ballerina-connector";
    }

    /**
     * Returns human-friendly help for the generator.  Provide the consumer with help
     * tips, parameters here
     *
     * @return A string value for the help message
     */
    @Override
    public String getHelp() {
        return "Generates a Go server library using the swagger-tools project.  By default, "
                + "it will also generate service classes--which you can disable with the `-Dnoservice` environment variable.";
    }

    @Override
    public String toApiName(String name) {
        if (name.length() == 0) {
            return "DefaultController";
        }
        return initialCaps(name);
    }

    /**
     * Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
     * those terms here.  This logic is only called if a variable matches the reseved words
     *
     * @return the escaped term
     */
    //@Override
    /*public String escapeReservedWord(String name) {
    if (this.reservedWordsMappings().containsKey(name)) {
        return this.reservedWordsMappings().get(name);
    }
    return "_" + name;
    }*/

    @Override
    public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
        super.postProcessOperations(objs);
        if (objs != null) {
            Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
            if (operations != null) {
                List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
                for (CodegenOperation operation : ops) {
                    operation.httpMethod = operation.httpMethod.toLowerCase();
                }
            }
        }

        return objs;
    }

    /**
     * Location to write api files.  You can use the apiPackage() as defined when the class is
     * instantiated
     */
    @Override
    public String apiFileFolder() {
        return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
    }

    @Override
    public String toModelName(String name) {
        return camelize(toModelFilename(name));
    }

    @Override
    public String toOperationId(String operationId) {
        // method name cannot use reserved keyword, e.g. return
        if (isReservedWord(operationId)) {
            LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to "
                    + camelize(sanitizeName("call_" + operationId)));
            operationId = "call_" + operationId;
        }
        String name = camelize(operationId);
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }

    @Override
    public String toModelFilename(String name) {
        if (!StringUtils.isEmpty(modelNamePrefix)) {
            name = modelNamePrefix + "_" + name;
        }

        if (!StringUtils.isEmpty(modelNameSuffix)) {
            name = name + "_" + modelNameSuffix;
        }

        name = sanitizeName(name);

        // model name cannot use reserved keyword, e.g. return
        if (isReservedWord(name)) {
            LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to "
                    + camelize("model_" + name));
            name = "model_" + name;
        }

        return underscore(name);
    }

    /*    @Override
        public String toApiFilename(String name) {
    name = name.replaceAll("-", "_");
    return underscore(name);
        }
    */
    @Override
    public String escapeQuotationMark(String input) {
        // remove " to avoid code injection
        return input.replace("\"", "");
    }

    @Override
    public String escapeUnsafeCharacters(String input) {
        return input.replace("*/", "*_/").replace("/*", "/_*");
    }

    @Override
    protected String getOrGenerateOperationId(Operation operation, String path, String httpMethod) {
        String operationId = operation.getOperationId();
        if (path.contains("?")) {
            path = path.substring(0, path.indexOf("?"));
        }
        if (StringUtils.isBlank(operationId)) {
            String tmpPath = path.replaceAll("\\{", "");
            tmpPath = tmpPath.replaceAll("\\}", "");
            String[] parts = (tmpPath + "/" + httpMethod).split("/");
            StringBuilder builder = new StringBuilder();
            if ("/".equals(tmpPath)) {
                builder.append("root");
            }
            for (int i = 0; i < parts.length; ++i) {
                String part = parts[i];
                if (part.length() > 0) {
                    if (builder.toString().length() == 0) {
                        part = Character.toLowerCase(part.charAt(0)) + part.substring(1);
                    } else {
                        part = this.initialCaps(part);
                    }

                    builder.append(part);
                }
            }

            operationId = this.sanitizeName(builder.toString());
            LOGGER.warn("Empty operationId found for path: " + httpMethod + " " + path
                    + ". Renamed to auto-generated operationId: " + operationId);
        }

        return operationId;
    }
}