dubbo AbstractGenerator 源码

  • 2022-10-20
  • 浏览 (302)

dubbo AbstractGenerator 代码

文件路径:/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.dubbo.gen;

import com.google.common.base.Strings;
import com.google.common.html.HtmlEscapers;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileOptions;
import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location;
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.Feature;
import com.google.protobuf.compiler.PluginProtos;
import com.salesforce.jprotoc.Generator;
import com.salesforce.jprotoc.GeneratorException;
import com.salesforce.jprotoc.ProtoTypeMap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public abstract class AbstractGenerator extends Generator {

    private static final int SERVICE_NUMBER_OF_PATHS = 2;
    private static final int METHOD_NUMBER_OF_PATHS = 4;

    protected abstract String getClassPrefix();

    protected abstract String getClassSuffix();

    protected String getSingleTemplateFileName() {
        return getTemplateFileName();
    }

    protected String getTemplateFileName() {
        return getClassPrefix() + getClassSuffix() + "Stub.mustache";
    }

    protected String getInterfaceTemplateFileName() {
        return getClassPrefix() + getClassSuffix() + "InterfaceStub.mustache";
    }

    @Override
    protected List<Feature> supportedFeatures() {
        return Collections.singletonList(Feature.FEATURE_PROTO3_OPTIONAL);
    }

    private String getServiceJavaDocPrefix() {
        return "    ";
    }

    private String getMethodJavaDocPrefix() {
        return "        ";
    }

    @Override
    public List<PluginProtos.CodeGeneratorResponse.File> generateFiles(
        PluginProtos.CodeGeneratorRequest request) throws GeneratorException {
        final ProtoTypeMap typeMap = ProtoTypeMap.of(request.getProtoFileList());

        List<FileDescriptorProto> protosToGenerate = request.getProtoFileList().stream()
            .filter(protoFile -> request.getFileToGenerateList().contains(protoFile.getName()))
            .collect(Collectors.toList());

        List<ServiceContext> services = findServices(protosToGenerate, typeMap);
        return generateFiles(services);
    }

    private List<ServiceContext> findServices(List<FileDescriptorProto> protos,
        ProtoTypeMap typeMap) {
        List<ServiceContext> contexts = new ArrayList<>();

        protos.forEach(fileProto -> {
            for (int serviceNumber = 0; serviceNumber < fileProto.getServiceCount();
                serviceNumber++) {
                ServiceContext serviceContext = buildServiceContext(
                    fileProto.getService(serviceNumber),
                    typeMap,
                    fileProto.getSourceCodeInfo().getLocationList(),
                    serviceNumber
                );
                serviceContext.protoName = fileProto.getName();
                serviceContext.packageName = extractPackageName(fileProto);
                if (!Strings.isNullOrEmpty(fileProto.getOptions().getJavaOuterClassname())) {
                    serviceContext.outerClassName = fileProto.getOptions().getJavaOuterClassname();
                }
                serviceContext.commonPackageName = extractCommonPackageName(fileProto);
                serviceContext.multipleFiles =
                    fileProto.getOptions() != null && fileProto.getOptions().getJavaMultipleFiles();
                contexts.add(serviceContext);
            }
        });

        return contexts;
    }

    private String extractPackageName(FileDescriptorProto proto) {
        FileOptions options = proto.getOptions();
        if (options != null) {
            String javaPackage = options.getJavaPackage();
            if (!Strings.isNullOrEmpty(javaPackage)) {
                return javaPackage;
            }
        }

        return Strings.nullToEmpty(proto.getPackage());
    }

    private String extractCommonPackageName(FileDescriptorProto proto) {
        return Strings.nullToEmpty(proto.getPackage());
    }

    private ServiceContext buildServiceContext(ServiceDescriptorProto serviceProto,
        ProtoTypeMap typeMap, List<Location> locations, int serviceNumber) {
        ServiceContext serviceContext = new ServiceContext();
        serviceContext.fileName =
            getClassPrefix() + serviceProto.getName() + getClassSuffix() + ".java";
        serviceContext.className = getClassPrefix() + serviceProto.getName() + getClassSuffix();
        serviceContext.outerClassName = serviceProto.getName() + "OuterClass";
        serviceContext.interfaceFileName = serviceProto.getName() + ".java";
        serviceContext.interfaceClassName = serviceProto.getName();
        serviceContext.serviceName = serviceProto.getName();
        serviceContext.deprecated =
            serviceProto.getOptions() != null && serviceProto.getOptions().getDeprecated();

        List<Location> allLocationsForService = locations.stream()
            .filter(location ->
                location.getPathCount() >= 2 &&
                    location.getPath(0) == FileDescriptorProto.SERVICE_FIELD_NUMBER &&
                    location.getPath(1) == serviceNumber
            )
            .collect(Collectors.toList());

        Location serviceLocation = allLocationsForService.stream()
            .filter(location -> location.getPathCount() == SERVICE_NUMBER_OF_PATHS)
            .findFirst()
            .orElseGet(Location::getDefaultInstance);
        serviceContext.javaDoc = getJavaDoc(getComments(serviceLocation),
            getServiceJavaDocPrefix());

        for (int methodNumber = 0; methodNumber < serviceProto.getMethodCount(); methodNumber++) {
            MethodContext methodContext = buildMethodContext(
                serviceProto.getMethod(methodNumber),
                typeMap,
                locations,
                methodNumber
            );

            serviceContext.methods.add(methodContext);
            serviceContext.methodTypes.add(methodContext.inputType);
            serviceContext.methodTypes.add(methodContext.outputType);
        }
        return serviceContext;
    }

    private MethodContext buildMethodContext(MethodDescriptorProto methodProto,
        ProtoTypeMap typeMap, List<Location> locations, int methodNumber) {
        MethodContext methodContext = new MethodContext();
        methodContext.originMethodName = methodProto.getName();
        methodContext.methodName = lowerCaseFirst(methodProto.getName());
        methodContext.inputType = typeMap.toJavaTypeName(methodProto.getInputType());
        methodContext.outputType = typeMap.toJavaTypeName(methodProto.getOutputType());
        methodContext.deprecated =
            methodProto.getOptions() != null && methodProto.getOptions().getDeprecated();
        methodContext.isManyInput = methodProto.getClientStreaming();
        methodContext.isManyOutput = methodProto.getServerStreaming();
        methodContext.methodNumber = methodNumber;

        Location methodLocation = locations.stream()
            .filter(location ->
                location.getPathCount() == METHOD_NUMBER_OF_PATHS &&
                    location.getPath(METHOD_NUMBER_OF_PATHS - 1) == methodNumber
            )
            .findFirst()
            .orElseGet(Location::getDefaultInstance);
        methodContext.javaDoc = getJavaDoc(getComments(methodLocation), getMethodJavaDocPrefix());

        if (!methodProto.getClientStreaming() && !methodProto.getServerStreaming()) {
            methodContext.reactiveCallsMethodName = "oneToOne";
            methodContext.grpcCallsMethodName = "asyncUnaryCall";
        }
        if (!methodProto.getClientStreaming() && methodProto.getServerStreaming()) {
            methodContext.reactiveCallsMethodName = "oneToMany";
            methodContext.grpcCallsMethodName = "asyncServerStreamingCall";
        }
        if (methodProto.getClientStreaming() && !methodProto.getServerStreaming()) {
            methodContext.reactiveCallsMethodName = "manyToOne";
            methodContext.grpcCallsMethodName = "asyncClientStreamingCall";
        }
        if (methodProto.getClientStreaming() && methodProto.getServerStreaming()) {
            methodContext.reactiveCallsMethodName = "manyToMany";
            methodContext.grpcCallsMethodName = "asyncBidiStreamingCall";
        }
        return methodContext;
    }

    private String lowerCaseFirst(String s) {
        return Character.toLowerCase(s.charAt(0)) + s.substring(1);
    }

    private List<PluginProtos.CodeGeneratorResponse.File> generateFiles(
        List<ServiceContext> services) {
        List<PluginProtos.CodeGeneratorResponse.File> allServiceFiles = new ArrayList<>();
        for (ServiceContext context : services) {
            List<PluginProtos.CodeGeneratorResponse.File> files = buildFile(context);
            allServiceFiles.addAll(files);
        }
        return allServiceFiles;
    }

    protected boolean enableMultipleTemplateFiles() {
        return false;
    }

    private List<PluginProtos.CodeGeneratorResponse.File> buildFile(ServiceContext context) {
        List<PluginProtos.CodeGeneratorResponse.File> files = new ArrayList<>();

        if (context.multipleFiles && enableMultipleTemplateFiles()) {
            String content = applyTemplate(getTemplateFileName(), context);
            String dir = absoluteDir(context);

            files.add(PluginProtos.CodeGeneratorResponse.File
                .newBuilder()
                .setName(getFileName(dir, context.fileName))
                .setContent(content)
                .build());

            content = applyTemplate(getInterfaceTemplateFileName(), context);
            files.add(PluginProtos.CodeGeneratorResponse.File
                .newBuilder()
                .setName(getFileName(dir, context.interfaceFileName))
                .setContent(content)
                .build());
        } else {
            String content = applyTemplate(getSingleTemplateFileName(), context);
            String dir = absoluteDir(context);

            files.add(PluginProtos.CodeGeneratorResponse.File
                .newBuilder()
                .setName(getFileName(dir, context.fileName))
                .setContent(content)
                .build());
        }

        return files;
    }

    private String absoluteDir(ServiceContext ctx) {
        return ctx.packageName.replace('.', '/');
//        if (Strings.isNullOrEmpty(dir)) {
//            return ctx.fileName;
//        } else {
//            return dir + "/" + ctx.fileName;
//        }if ()
    }

    private String getFileName(String dir, String fileName) {
        if (Strings.isNullOrEmpty(dir)) {
            return fileName;
        }
        return dir + "/" + fileName;
    }

    private String getComments(Location location) {
        return location.getLeadingComments().isEmpty() ? location.getTrailingComments()
            : location.getLeadingComments();
    }

    private String getJavaDoc(String comments, String prefix) {
        if (!comments.isEmpty()) {
            StringBuilder builder = new StringBuilder("/**\n")
                .append(prefix).append(" * <pre>\n");
            Arrays.stream(HtmlEscapers.htmlEscaper().escape(comments).split("\n"))
                .map(line -> line.replace("*/", "&#42;&#47;").replace("*", "&#42;"))
                .forEach(line -> builder.append(prefix).append(" * ").append(line).append("\n"));
            builder
                .append(prefix).append(" * </pre>\n")
                .append(prefix).append(" */");
            return builder.toString();
        }
        return null;
    }

    /**
     * Template class for proto Service objects.
     */
    private class ServiceContext {

        // CHECKSTYLE DISABLE VisibilityModifier FOR 8 LINES
        public String fileName;
        public String interfaceFileName;
        public String protoName;
        public String packageName;
        public String commonPackageName;
        public String className;
        public String interfaceClassName;
        public String serviceName;
        public boolean deprecated;
        public String javaDoc;
        public boolean multipleFiles;

        public String outerClassName;
        public List<MethodContext> methods = new ArrayList<>();

        public Set<String> methodTypes = new HashSet<>();

        public List<MethodContext> unaryRequestMethods() {
            return methods.stream().filter(m -> !m.isManyInput).collect(Collectors.toList());
        }

        public List<MethodContext> unaryMethods() {
            return methods.stream().filter(m -> (!m.isManyInput && !m.isManyOutput))
                .collect(Collectors.toList());
        }

        public List<MethodContext> serverStreamingMethods() {
            return methods.stream().filter(m -> !m.isManyInput && m.isManyOutput)
                .collect(Collectors.toList());
        }

        public List<MethodContext> biStreamingMethods() {
            return methods.stream().filter(m -> m.isManyInput).collect(Collectors.toList());
        }

        public List<MethodContext> biStreamingWithoutClientStreamMethods() {
            return methods.stream().filter(m -> m.isManyInput && m.isManyOutput)
                .collect(Collectors.toList());
        }

        public List<MethodContext> clientStreamingMethods() {
            return methods.stream().filter(m -> m.isManyInput && !m.isManyOutput)
                .collect(Collectors.toList());
        }

        public List<MethodContext> methods() {
            return methods;
        }

    }

    /**
     * Template class for proto RPC objects.
     */
    private class MethodContext {

        // CHECKSTYLE DISABLE VisibilityModifier FOR 10 LINES
        public String originMethodName;
        public String methodName;
        public String inputType;
        public String outputType;
        public boolean deprecated;
        public boolean isManyInput;
        public boolean isManyOutput;
        public String reactiveCallsMethodName;
        public String grpcCallsMethodName;
        public int methodNumber;
        public String javaDoc;

        // This method mimics the upper-casing method ogf gRPC to ensure compatibility
        // See https://github.com/grpc/grpc-java/blob/v1.8.0/compiler/src/java_plugin/cpp/java_generator.cpp#L58
        public String methodNameUpperUnderscore() {
            StringBuilder s = new StringBuilder();
            for (int i = 0; i < methodName.length(); i++) {
                char c = methodName.charAt(i);
                s.append(Character.toUpperCase(c));
                if ((i < methodName.length() - 1) && Character.isLowerCase(c)
                    && Character.isUpperCase(methodName.charAt(i + 1))) {
                    s.append('_');
                }
            }
            return s.toString();
        }

        public String methodNamePascalCase() {
            String mn = methodName.replace("_", "");
            return String.valueOf(Character.toUpperCase(mn.charAt(0))) + mn.substring(1);
        }

        public String methodNameCamelCase() {
            String mn = methodName.replace("_", "");
            return String.valueOf(Character.toLowerCase(mn.charAt(0))) + mn.substring(1);
        }
    }
}

相关信息

dubbo 源码目录

相关文章

dubbo AddressListener 源码

dubbo CacheableRouterFactory 源码

dubbo Cluster 源码

dubbo ClusterInvoker 源码

dubbo ClusterScopeModelInitializer 源码

dubbo Configurator 源码

dubbo ConfiguratorFactory 源码

dubbo Constants 源码

dubbo Directory 源码

dubbo LoadBalance 源码

0  赞