com.spectralogic.ds3autogen.net.NetCodeGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.spectralogic.ds3autogen.net.NetCodeGenerator.java

Source

/*
 * ******************************************************************************
 *   Copyright 2014-2015 Spectra Logic Corporation. All Rights Reserved.
 *   Licensed under the Apache License, Version 2.0 (the "License"). You may not use
 *   this file except in compliance with the License. A copy of the License is located at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   or in the "license" file accompanying this file.
 *   This file 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 com.spectralogic.ds3autogen.net;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.spectralogic.ds3autogen.api.CodeGenerator;
import com.spectralogic.ds3autogen.api.FileUtils;
import com.spectralogic.ds3autogen.api.models.apispec.Ds3ApiSpec;
import com.spectralogic.ds3autogen.api.models.apispec.Ds3Request;
import com.spectralogic.ds3autogen.api.models.apispec.Ds3ResponseCode;
import com.spectralogic.ds3autogen.api.models.apispec.Ds3Type;
import com.spectralogic.ds3autogen.api.models.docspec.Ds3DocSpec;
import com.spectralogic.ds3autogen.net.generators.clientmodels.BaseClientGenerator;
import com.spectralogic.ds3autogen.net.generators.clientmodels.ClientModelGenerator;
import com.spectralogic.ds3autogen.net.generators.parsers.response.BaseResponseParserGenerator;
import com.spectralogic.ds3autogen.net.generators.parsers.response.JobListPayloadParserGenerator;
import com.spectralogic.ds3autogen.net.generators.parsers.response.ResponseParserModelGenerator;
import com.spectralogic.ds3autogen.net.generators.parsers.typeset.BaseTypeParserSetGenerator;
import com.spectralogic.ds3autogen.net.generators.parsers.typeset.TypeParserSetGenerator;
import com.spectralogic.ds3autogen.net.generators.requestmodels.*;
import com.spectralogic.ds3autogen.net.generators.responsemodels.BaseResponseGenerator;
import com.spectralogic.ds3autogen.net.generators.responsemodels.ResponseModelGenerator;
import com.spectralogic.ds3autogen.net.generators.typemodels.BaseTypeGenerator;
import com.spectralogic.ds3autogen.net.generators.typemodels.NoneEnumGenerator;
import com.spectralogic.ds3autogen.net.generators.typemodels.ObjectsGenerator;
import com.spectralogic.ds3autogen.net.generators.typemodels.TypeModelGenerator;
import com.spectralogic.ds3autogen.net.model.client.BaseClient;
import com.spectralogic.ds3autogen.net.model.parser.BaseParser;
import com.spectralogic.ds3autogen.net.model.request.BaseRequest;
import com.spectralogic.ds3autogen.net.model.response.BaseResponse;
import com.spectralogic.ds3autogen.net.model.type.BaseType;
import com.spectralogic.ds3autogen.net.model.typeparser.BaseTypeParserSet;
import com.spectralogic.ds3autogen.utils.Helper;
import com.spectralogic.ds3autogen.utils.ResponsePayloadUtil;
import freemarker.template.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Path;
import java.nio.file.Paths;

import static com.spectralogic.ds3autogen.net.utils.GeneratorUtils.hasResponseHandlerAndParser;
import static com.spectralogic.ds3autogen.utils.ConverterUtil.*;
import static com.spectralogic.ds3autogen.utils.Ds3RequestClassificationUtil.*;
import static com.spectralogic.ds3autogen.utils.Ds3TypeClassificationUtil.*;
import static com.spectralogic.ds3autogen.utils.ResponsePayloadUtil.hasSpecifiedPayload;

/**
 * Generates the .Net SDK code based on the contents of the Ds3ApiSpec
 */
public class NetCodeGenerator implements CodeGenerator {

    private static final Logger LOG = LoggerFactory.getLogger(NetCodeGenerator.class);
    private static final String COMMANDS_NAMESPACE = "Ds3.Calls";
    private static final String CLIENT_NAMESPACE = "Ds3.";
    private static final String MODEL_PARSER_NAMESPACE = "Ds3.ResponseParsers";
    private static final String PARSER_NAMESPACE = CLIENT_NAMESPACE + "ResponseParsers";
    private static final String TYPES_NAMESPACE = CLIENT_NAMESPACE + "Models";
    private static final Path BASE_PROJECT_PATH = Paths.get("");

    private final Configuration config = new Configuration(Configuration.VERSION_2_3_23);

    private Ds3ApiSpec spec;
    private FileUtils fileUtils;
    private Path destDir;

    public NetCodeGenerator() throws TemplateModelException {
        config.setDefaultEncoding("UTF-8");
        config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        config.setClassForTemplateLoading(NetCodeGenerator.class, "/tmpls/net");
        config.setSharedVariable("netHelper", NetHelper.getInstance());
        config.setSharedVariable("helper", Helper.getInstance());
    }

    @Override
    public void generate(final Ds3ApiSpec spec, final FileUtils fileUtils, final Path destDir,
            final Ds3DocSpec docSpec) throws IOException {
        this.spec = spec;
        this.fileUtils = fileUtils;
        this.destDir = destDir;

        try {
            final ImmutableList<Ds3Request> requests = spec.getRequests();
            final ImmutableMap<String, Ds3Type> typeMap = removeUnusedTypes(spec.getTypes(), spec.getRequests());

            generateCommands(requests, typeMap, docSpec);
            generateClient(requests, docSpec);
            generateModelParsers(typeMap);
            generateAllTypes(typeMap);
        } catch (final TemplateException e) {
            e.printStackTrace();
        }
    }

    /**
     * Generates the .net code for the type/model parsers
     */
    private void generateModelParsers(final ImmutableMap<String, Ds3Type> typeMap)
            throws IOException, TemplateException {
        if (isEmpty(typeMap)) {
            LOG.info("Not generating model parsers: no types.");
            return;
        }
        final Template tmpl = config.getTemplate("parsers/typeset/all_type_parsers.ftl");
        final TypeParserSetGenerator<?> generator = new BaseTypeParserSetGenerator();
        final BaseTypeParserSet parser = generator.generate(typeMap);
        final Path path = destDir.resolve(BASE_PROJECT_PATH
                .resolve(Paths.get(MODEL_PARSER_NAMESPACE.replace(".", "/") + "/ModelParsers.cs")));

        LOG.info("Getting OutputStream for file:" + path.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(path);
                final Writer writer = new OutputStreamWriter(outStream)) {
            tmpl.process(parser, writer);
        }
    }

    /**
     * Generates the .net code for the types
     */
    private void generateAllTypes(final ImmutableMap<String, Ds3Type> typeMap)
            throws IOException, TemplateException {
        if (isEmpty(typeMap)) {
            LOG.info("There were no types to generate");
            return;
        }
        for (final Ds3Type ds3Type : typeMap.values()) {
            generateType(ds3Type, typeMap);
        }
    }

    /**
     * Generates the .net code for the specified Ds3Type
     */
    private void generateType(final Ds3Type ds3Type, final ImmutableMap<String, Ds3Type> typeMap)
            throws IOException, TemplateException {
        final Template tmpl = getTypeTemplate(ds3Type);
        final TypeModelGenerator<?> modelGenerator = getTypeGenerator(ds3Type);
        final BaseType type = modelGenerator.generate(ds3Type, typeMap);
        final Path requestPath = destDir.resolve(BASE_PROJECT_PATH
                .resolve(Paths.get(TYPES_NAMESPACE.replace(".", "/") + "/" + type.getName() + ".cs")));

        LOG.info("Getting OutputStream for file:" + requestPath.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(requestPath);
                final Writer writer = new OutputStreamWriter(outStream)) {
            tmpl.process(type, writer);
        }
    }

    /**
     * Gets the associated Type Generator for the specified Ds3Type
     */
    private TypeModelGenerator getTypeGenerator(final Ds3Type ds3Type) {
        if (isChecksumType(ds3Type)) {
            return new NoneEnumGenerator();
        }
        if (isObjectsType(ds3Type)) {
            return new ObjectsGenerator();
        }
        return new BaseTypeGenerator();
    }

    /**
     * Gets the template used to generate the .net code for the specified Ds3Type
     */
    private Template getTypeTemplate(final Ds3Type ds3Type) throws IOException {
        if (isChecksumType(ds3Type)) {
            return config.getTemplate("types/checksum_type.ftl");
        }
        if (hasContent(ds3Type.getEnumConstants())) {
            return config.getTemplate("types/enum_type.ftl");
        }
        if (hasContent(ds3Type.getElements())) {
            return config.getTemplate("types/type_template.ftl");
        }
        throw new IllegalArgumentException("Type must have Elements and/or EnumConstants");
    }

    /**
     * Generates the .net code for the client
     */
    private void generateClient(final ImmutableList<Ds3Request> requests, final Ds3DocSpec docSpec)
            throws IOException, TemplateException {
        if (isEmpty(requests)) {
            LOG.info("Not generating client: no requests.");
            return;
        }
        final Template clientTmpl = config.getTemplate("client/ds3_client.ftl");
        final ClientModelGenerator<?> clientGenerator = new BaseClientGenerator();
        final BaseClient client = clientGenerator.generate(requests, docSpec);
        final Path clientPath = toClientPath("Ds3Client.cs");

        LOG.info("Getting OutputStream for file:" + clientPath.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(clientPath);
                final Writer writer = new OutputStreamWriter(outStream)) {
            clientTmpl.process(client, writer);
        }

        final Template ids3ClientTmpl = config.getTemplate("client/ids3_client.ftl");
        final Path ids3ClientPath = toClientPath("IDs3Client.cs");

        LOG.info("Getting OutputStream for file:" + ids3ClientPath.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(ids3ClientPath);
                final Writer writer = new OutputStreamWriter(outStream)) {
            ids3ClientTmpl.process(client, writer);
        }
    }

    /**
     * Converts a file name into the path containing said file within the client path
     */
    private Path toClientPath(final String fileName) {
        return destDir
                .resolve(BASE_PROJECT_PATH.resolve(Paths.get(CLIENT_NAMESPACE.replace(".", "/") + "/" + fileName)));
    }

    /**
     * Generates all code associated with the Ds3ApiSpec
     */
    private void generateCommands(final ImmutableList<Ds3Request> requests,
            final ImmutableMap<String, Ds3Type> typeMap, final Ds3DocSpec docSpec)
            throws TemplateException, IOException {
        if (isEmpty(requests)) {
            LOG.info("There were no requests to generate");
            return;
        }
        for (final Ds3Request request : requests) {
            generateRequest(request, typeMap, docSpec);
            generateResponseAndParser(request);
        }
    }

    /**
     * Generates the .net code for the response handler and parser described in the Ds3Request
     */
    private void generateResponseAndParser(final Ds3Request ds3Request) throws IOException, TemplateException {
        if (!ResponsePayloadUtil.hasResponsePayload(ds3Request.getDs3ResponseCodes())) {
            //Check if the request is an exception for generating response and parser files
            if (hasResponseHandlerAndParser(ds3Request)) {
                generateResponse(ds3Request, null);
                generateResponseParser(ds3Request, null);
            }
            //There is no payload for this Ds3Request, so do not generate any response handling code
            return;
        }
        final String responsePayloadType = getResponsePayloadType(ds3Request.getDs3ResponseCodes());
        if ((isEmpty(responsePayloadType) || !responsePayloadType.equalsIgnoreCase("java.lang.String"))
                && isEmpty(spec.getTypes())) {
            LOG.error("Cannot generate response because type map is empty");
            return;
        }
        if (responsePayloadType == null) {
            throw new IllegalArgumentException(
                    "Cannot generate a response because there are no non-error payloads: " + ds3Request.getName());
        }

        generateResponse(ds3Request, responsePayloadType);

        if (responsePayloadType.equalsIgnoreCase("java.lang.String")) {
            generateResponseParser(ds3Request, null);
        } else {
            final Ds3Type ds3TypePayload = spec.getTypes().get(responsePayloadType);
            generateResponseParser(ds3Request, ds3TypePayload);
        }
    }

    /**
     * Generates the .net code for the response parser
     */
    private void generateResponseParser(final Ds3Request ds3Request, final Ds3Type responsePayload)
            throws IOException, TemplateException {
        final Template tmpl = getResponseParserTemplate(ds3Request);
        final ResponseParserModelGenerator<?> parserGenerator = getResponseParserGenerator(responsePayload);

        final BaseParser parser = generateBaseParser(ds3Request, responsePayload, parserGenerator);
        final Path parserPath = destDir.resolve(BASE_PROJECT_PATH
                .resolve(Paths.get(PARSER_NAMESPACE.replace(".", "/") + "/" + parser.getName() + ".cs")));

        LOG.info("Getting OutputStream for file:" + parserPath.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(parserPath);
                final Writer writer = new OutputStreamWriter(outStream)) {
            tmpl.process(parser, writer);
        }
    }

    /**
     * Generates the BaseParser for use in response parser generation. This is used to handle a null Ds3Type due to
     * a string payload
     */
    private static BaseParser generateBaseParser(final Ds3Request ds3Request, final Ds3Type ds3Type,
            final ResponseParserModelGenerator<?> generator) {
        if (ds3Type == null) {
            return generator.generate(ds3Request, "java.lang.String", null);
        }
        return generator.generate(ds3Request, ds3Type.getName(), ds3Type.getNameToMarshal());
    }

    /**
     * Retrieves the response parser generator for the specified Ds3Request
     */
    private ResponseParserModelGenerator getResponseParserGenerator(final Ds3Type responsePayload) {
        if (isJobsApiBean(responsePayload)) {
            return new JobListPayloadParserGenerator();
        }
        return new BaseResponseParserGenerator();
    }

    /**
     * Retrieves the response parser template for the specified Ds3Request
     */
    private Template getResponseParserTemplate(final Ds3Request ds3Request) throws IOException {
        if (supportsPaginationRequest(ds3Request)) {
            return config.getTemplate("parsers/response/pagination_headers_response_parser.ftl");
        }
        if (isAllocateJobChunkRequest(ds3Request)) {
            return config.getTemplate("parsers/response/allocate_job_chunk_parser.ftl");
        }
        if (isGetJobChunksReadyForClientProcessingRequest(ds3Request)) {
            return config.getTemplate("parsers/response/get_job_chunks_parser.ftl");
        }
        if (isGetObjectAmazonS3Request(ds3Request)) {
            return config.getTemplate("parsers/response/get_object_parser.ftl");
        }
        if (isHeadBucketRequest(ds3Request)) {
            return config.getTemplate("parsers/response/head_bucket_parser.ftl");
        }
        if (isHeadObjectRequest(ds3Request)) {
            return config.getTemplate("parsers/response/head_object_parser.ftl");
        }
        //Perform this check last so that individual special cased requests take precedence
        if (hasSpecifiedPayload(ds3Request, "MasterObjectList")) {
            return config.getTemplate("parsers/response/master_object_list_parser.ftl");
        }
        if (hasSpecifiedPayload(ds3Request, "String")) {
            return config.getTemplate("parsers/response/string_response_parser.ftl");
        }
        return config.getTemplate("parsers/response/parser_template.ftl");
    }

    /**
     * Generates the .net code for the response handler
     */
    private void generateResponse(final Ds3Request ds3Request, final String responsePayload)
            throws IOException, TemplateException {
        final Template tmpl = getResponseTemplate(ds3Request);
        final ResponseModelGenerator<?> responseGenerator = getResponseGenerator();
        final BaseResponse response = responseGenerator.generate(ds3Request, responsePayload);
        final Path responsePath = destDir.resolve(BASE_PROJECT_PATH
                .resolve(Paths.get(COMMANDS_NAMESPACE.replace(".", "/") + "/" + response.getName() + ".cs")));

        LOG.info("Getting OutputStream for file:" + responsePath.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(responsePath);
                final Writer writer = new OutputStreamWriter(outStream)) {
            tmpl.process(response, writer);
        }
    }

    /**
     * Retrieves the non-error response type from within the response codes.  If no non-error
     * response type is found, then null is returned.
     */
    private String getResponsePayloadType(final ImmutableList<Ds3ResponseCode> responseCodes) {
        if (isEmpty(responseCodes)) {
            LOG.error("There are no response codes to generate the response");
            return null;
        }
        for (final Ds3ResponseCode responseCode : responseCodes) {
            final String responseType = ResponsePayloadUtil.getResponseType(responseCode.getDs3ResponseTypes());
            if (ResponsePayloadUtil.isNonErrorCode(responseCode.getCode()) && !responseType.equals("null")) {
                return responseType;
            }
        }
        return null;
    }

    /**
     * Retrieves the associated .net response generator for the specified Ds3Request
     */
    private ResponseModelGenerator getResponseGenerator() {
        return new BaseResponseGenerator();
    }

    /**
     * Retrieves the appropriate template that will generate the .net response handler
     * code for this Ds3Request
     */
    private Template getResponseTemplate(final Ds3Request ds3Request) throws IOException {
        if (supportsPaginationRequest(ds3Request)) {
            return config.getTemplate("response/pagination_headers_response.ftl");
        }
        if (isAllocateJobChunkRequest(ds3Request)) {
            return config.getTemplate("response/allocate_job_chunk_response.ftl");
        }
        if (isGetJobChunksReadyForClientProcessingRequest(ds3Request)) {
            return config.getTemplate("response/get_job_chunks_response.ftl");
        }
        if (isGetObjectAmazonS3Request(ds3Request)) {
            return config.getTemplate("response/get_object_response.ftl");
        }
        if (isHeadBucketRequest(ds3Request)) {
            return config.getTemplate("response/head_bucket_response.ftl");
        }
        if (isHeadObjectRequest(ds3Request)) {
            return config.getTemplate("response/head_object_response.ftl");
        }
        return config.getTemplate("response/response_template.ftl");
    }

    /**
     * Generates the .net code for the request handler described in the Ds3Request
     */
    private void generateRequest(final Ds3Request ds3Request, final ImmutableMap<String, Ds3Type> typeMap,
            final Ds3DocSpec docSpec) throws IOException, TemplateException {
        final Template tmpl = getRequestTemplate(ds3Request);
        final RequestModelGenerator<?> modelGenerator = getTemplateModelGenerator(ds3Request);
        final BaseRequest request = modelGenerator.generate(ds3Request, typeMap, docSpec);
        final Path requestPath = destDir.resolve(BASE_PROJECT_PATH
                .resolve(Paths.get(COMMANDS_NAMESPACE.replace(".", "/") + "/" + request.getName() + ".cs")));

        LOG.info("Getting OutputStream for file:" + requestPath.toString());

        try (final OutputStream outStream = fileUtils.getOutputFile(requestPath);
                final Writer writer = new OutputStreamWriter(outStream)) {
            tmpl.process(request, writer);
        }
    }

    /**
     * Retrieves the associated .net request generator for the specified Ds3Request
     */
    private RequestModelGenerator<?> getTemplateModelGenerator(final Ds3Request ds3Request) {
        if (isGetObjectRequest(ds3Request)) {
            return new GetObjectRequestGenerator();
        }
        if (isBulkPutRequest(ds3Request)) {
            return new BulkPutRequestGenerator();
        }
        if (isBulkGetRequest(ds3Request)) {
            return new BulkGetRequestGenerator();
        }
        if (isCreateObjectRequest(ds3Request)) {
            return new PutObjectRequestGenerator();
        }
        if (isCreateMultiPartUploadPartRequest(ds3Request)) {
            return new StreamRequestPayloadGenerator();
        }
        if (isEjectStorageDomainBlobsRequest(ds3Request) || isPhysicalPlacementRequest(ds3Request)
                || isMultiFileDeleteRequest(ds3Request)) {
            return new ObjectsRequestPayloadGenerator();
        }
        if (hasStringRequestPayload(ds3Request)) {
            return new StringRequestPayloadGenerator();
        }
        return new BaseRequestGenerator();
    }

    /**
     * Retrieves the appropriate template that will generate the .net request handler
     * code for this Ds3Request
     */
    private Template getRequestTemplate(final Ds3Request ds3Request) throws IOException {
        if (isGetObjectRequest(ds3Request)) {
            return config.getTemplate("request/get_object_request.ftl");
        }
        if (isBulkPutRequest(ds3Request)) {
            return config.getTemplate("request/bulk_put_request.ftl");
        }
        if (isBulkGetRequest(ds3Request)) {
            return config.getTemplate("request/bulk_get_request.ftl");
        }
        if (isCreateObjectRequest(ds3Request)) {
            return config.getTemplate("request/put_object_request.ftl");
        }
        if (isCreateMultiPartUploadPartRequest(ds3Request)) {
            return config.getTemplate("request/stream_request_payload.ftl");
        }
        if (isEjectStorageDomainBlobsRequest(ds3Request)) {
            return config.getTemplate("request/eject_storage_domain_blobs.ftl");
        }
        if (isPhysicalPlacementRequest(ds3Request)) {
            return config.getTemplate("request/objects_request_payload.ftl");
        }
        if (isMultiFileDeleteRequest(ds3Request)) {
            return config.getTemplate("request/multi_file_delete_request.ftl");
        }
        if (hasStringRequestPayload(ds3Request)) {
            return config.getTemplate("request/string_request_payload.ftl");
        }
        return config.getTemplate("request/request_template.ftl");
    }
}