All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tencent.trpc.codegen.template.CodeFileGenerator Maven / Gradle / Ivy

/*
 * Tencent is pleased to support the open source community by making tRPC available.
 *
 * Copyright (C) 2023 THL A29 Limited, a Tencent company. 
 * All rights reserved.
 *
 * If you have downloaded a copy of the tRPC source code from Tencent,
 * please note that tRPC source code is licensed under the Apache 2.0 License,
 * A copy of the Apache 2.0 License can be found in the LICENSE file.
 */

package com.tencent.trpc.codegen.template;

import com.google.common.collect.ImmutableMap;
import com.tencent.trpc.codegen.Protocol;
import com.tencent.trpc.codegen.TRpcCodeGenerateException;
import com.tencent.trpc.codegen.protobuf.ProtoSourceInfo;
import com.tencent.trpc.codegen.protobuf.source.model.ProtoService;
import com.tencent.trpc.core.common.Version;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Templated code generator.
 * Use specified {@link CodeTemplate}s and {@link CodeTemplateEngine} to generate code files.
 *
 * @param  Type of the Template Object
 * @param  Type of the Template Context
 */
public class CodeFileGenerator {
    private static final Logger log = LoggerFactory.getLogger(CodeFileGenerator.class);
    /**
     * CodeTemplateEngine to use
     */
    private final CodeTemplateEngine codeTemplateEngine;
    /**
     * CodeTemplates to use
     */
    private final List> codeTemplates;
    /**
     * Provider of Template Context
     */
    private final TemplateContextProvider templateContextProvider;
    /**
     * Will not generate pom.xml if set to true
     */
    private final boolean noPom;

    private CodeFileGenerator(CodeTemplateEngine codeTemplateEngine,
                              List> codeTemplates,
                              TemplateContextProvider templateContextProvider,
                              boolean noPom) {
        this.codeTemplateEngine = codeTemplateEngine;
        this.codeTemplates = codeTemplates;
        this.templateContextProvider = templateContextProvider;
        this.noPom = noPom;
    }

    /**
     * Generate templated code files.
     * 

Input {@link ProtoSourceInfo} and {@link Protocol} will be used as Template Context.

*

Each {@code ProtoService} in {@code ProtoSourceInfo} will result in a set of code files.

* * @param protocol the specified transfer protocol * @param sourceInfo the specified {@code ProtoSourceInfo} * @param outputPath parent directory of the generated files * @param customContext custom variables to add to Template Context */ public void generateCodeFiles(Protocol protocol, ProtoSourceInfo sourceInfo, Path outputPath, Map customContext) { Map baseContext = ImmutableMap.of( "version", Version.version(), "protocol", protocol.toString(), "custom", customContext ); codeTemplates.forEach(template -> { if (noPom && template.getCodeType() == CodeType.POM_XML) { return; } if (template.getCodeScope() == CodeScope.GLOBAL) { writeCodeTo(outputPath.resolve(template.getCodeFilename()), generateCode(template, baseContext, ImmutableMap.of("sourceInfo", sourceInfo))); } else if (template.getCodeScope() == CodeScope.SERVICE) { sourceInfo.getServices().forEach(service -> { if (template.getCodeType() == CodeType.STREAM_API && !service.hasStreamingMethod()) { return; } writeCodeTo(getServiceCodeFilePath(service, template, outputPath), generateCode(template, baseContext, ImmutableMap.of("service", service))); }); } }); } private String generateCode(CodeTemplate template, Map baseContext, Map otherContext) { TemplateContext templateContext = templateContextProvider.createContext(); baseContext.forEach(templateContext::put); otherContext.forEach(templateContext::put); return codeTemplateEngine.process(template.getName(), template.getTemplate(), templateContext); } /** * Get the absolute path of the rpc service code file about to generate. * * @param service {@code ProtoService} related to the code file. * @param codeTemplate {@code CodeTemplate} used to generate code. * @param outputPath parent directory of the generated code files. * @return {@code Path} of the code file. */ private Path getServiceCodeFilePath(ProtoService service, CodeTemplate codeTemplate, Path outputPath) { Path filename = codeTemplate.getCodeFilename(service.getInterfaceNamePrefix()); Path out = outputPath; String packageName = service.getJavaPackage(); if (StringUtils.isEmpty(packageName)) { packageName = service.getPackageName(); } for (String s : packageName.split("\\.")) { out = out.resolve(s); } return out.resolve(filename); } private void writeCodeTo(Path outputPath, String code) { log.info("writing code {}", outputPath); try { Files.createDirectories(outputPath.getParent()); Files.write(outputPath, code.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new TRpcCodeGenerateException("failed to write file " + outputPath, e); } } public static CodeFileGeneratorBuilder builder() { return new CodeFileGeneratorBuilder<>(); } /** * create a default {@code CodeFileGenerator}, which use FreeMarker as TemplateEngine, * and load default CodeTemplates. see {@link DefaultCodeTemplates} * * @return the default CodeGenerator */ public static CodeFileGenerator> createDefault() { return CodeFileGenerator.>builder() .codeTemplateEngine(new FreeMarkerStringTemplateEngine()) .templateContextProvider(new FreeMarkerContextProvider()) .codeTemplates(DefaultCodeTemplates.getInstance().getCodeTemplates()) .build(); } public static final class CodeFileGeneratorBuilder { private CodeTemplateEngine codeTemplateEngine; private List> codeTemplates = Collections.emptyList(); private TemplateContextProvider templateContextProvider; private boolean noPom; private CodeFileGeneratorBuilder() { } /** * Set the CodeTemplateEngine */ public CodeFileGeneratorBuilder codeTemplateEngine(CodeTemplateEngine codeTemplateEngine) { this.codeTemplateEngine = codeTemplateEngine; return this; } /** * Set CodeTemplates */ public CodeFileGeneratorBuilder codeTemplates(List> codeTemplates) { this.codeTemplates = codeTemplates; return this; } /** * Set TemplateContextProvider */ public CodeFileGeneratorBuilder templateContextProvider( TemplateContextProvider templateContextProvider) { this.templateContextProvider = templateContextProvider; return this; } /** * Will not generate pom.xml if set to true */ public CodeFileGeneratorBuilder noPom(boolean noPom) { this.noPom = noPom; return this; } /** * Build {@link CodeFileGenerator} */ public CodeFileGenerator build() { if (codeTemplateEngine == null) { throw new IllegalArgumentException("codeTemplateEngine must not be null"); } if (templateContextProvider == null) { throw new IllegalArgumentException("templateContextProvider must not be null"); } return new CodeFileGenerator<>(codeTemplateEngine, codeTemplates, templateContextProvider, noPom); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy