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

org.lealone.sql.ddl.CreateService Maven / Gradle / Ivy

/*
 * Copyright Lealone Database Group.
 * Licensed under the Server Side Public License, v 1.
 * Initial Developer: zhh
 */
package org.lealone.sql.ddl;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;

import org.lealone.common.exceptions.DbException;
import org.lealone.common.util.CamelCaseHelper;
import org.lealone.common.util.CaseInsensitiveMap;
import org.lealone.common.util.StatementBuilder;
import org.lealone.common.util.StringUtils;
import org.lealone.db.Database;
import org.lealone.db.DbObjectType;
import org.lealone.db.PluginManager;
import org.lealone.db.api.ErrorCode;
import org.lealone.db.lock.DbObjectLock;
import org.lealone.db.schema.Schema;
import org.lealone.db.service.JavaServiceExecutorFactory;
import org.lealone.db.service.Service;
import org.lealone.db.service.ServiceExecutor;
import org.lealone.db.service.ServiceExecutorFactory;
import org.lealone.db.service.ServiceMethod;
import org.lealone.db.service.ServiceSetting;
import org.lealone.db.session.ServerSession;
import org.lealone.db.table.Column;
import org.lealone.db.table.Column.ListColumn;
import org.lealone.db.table.Column.MapColumn;
import org.lealone.db.table.Column.SetColumn;
import org.lealone.db.table.CreateTableData;
import org.lealone.db.value.DataType;
import org.lealone.db.value.Value;
import org.lealone.db.value.ValueUuid;
import org.lealone.sql.SQLStatement;

/**
 * This class represents the statement
 * CREATE SERVICE
 * 
 * @author zhh
 */
public class CreateService extends SchemaStatement {

    private final ArrayList serviceMethods = new ArrayList<>();
    private String serviceName;
    private boolean ifNotExists;
    private String comment;
    private String language = "java";
    private String packageName;
    private String implementBy;
    private boolean genCode;
    private String codePath;
    private CaseInsensitiveMap serviceParameters;

    public CreateService(ServerSession session, Schema schema) {
        super(session, schema);
    }

    @Override
    public int getType() {
        return SQLStatement.CREATE_SERVICE;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public void addServiceMethod(CreateTable serviceMethod) {
        serviceMethods.add(serviceMethod);
    }

    public void setIfNotExists(boolean ifNotExists) {
        this.ifNotExists = ifNotExists;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public void setImplementBy(String implementBy) {
        this.implementBy = implementBy;
    }

    public void setGenCode(boolean genCode) {
        this.genCode = genCode;
    }

    public void setCodePath(String codePath) {
        this.codePath = codePath;
    }

    public void setServiceParameters(CaseInsensitiveMap serviceParameters) {
        this.serviceParameters = serviceParameters;
    }

    @Override
    public int update() {
        session.getUser().checkAdmin();
        DbObjectLock lock = schema.tryExclusiveLock(DbObjectType.SERVICE, session);
        if (lock == null)
            return -1;

        if (schema.findService(session, serviceName) != null) {
            if (ifNotExists) {
                return 0;
            }
            throw DbException.get(ErrorCode.SERVICE_ALREADY_EXISTS_1, serviceName);
        }
        int id = getObjectId();
        // 替换变量,重建SQL
        if (sql.indexOf('@') >= 0) {
            sql = getCreateSQL();
        }
        int methodSize = this.serviceMethods.size();
        ArrayList serviceMethods = new ArrayList<>(methodSize);
        for (int methodIndex = 0; methodIndex < methodSize; methodIndex++) {
            CreateTableData data = this.serviceMethods.get(methodIndex).data;
            ServiceMethod m = new ServiceMethod();
            m.setMethodName(data.tableName);
            int size = data.columns.size() - 1;
            ArrayList parameters = new ArrayList<>(size);
            m.setParameters(parameters);
            m.setReturnType(data.columns.get(size)); // 最后一列是返回类型
            for (int i = 0; i < size; i++) {
                parameters.add(data.columns.get(i));
            }
            serviceMethods.add(m);
        }

        boolean isJava = "java".equalsIgnoreCase(language);
        if (isJava && implementBy != null && packageName == null) {
            int pos = implementBy.lastIndexOf('.');
            if (pos > 0)
                packageName = implementBy.substring(0, pos);
            else
                packageName = "";
        }

        // 非启动阶段,如果java服务实现类不存在时自动生成一个
        if (!session.getDatabase().isStarting() && isJava && implementBy != null) {
            try {
                Class.forName(implementBy);
            } catch (Exception e) {
                if (codePath == null)
                    codePath = "./src/main/java";
                genServiceImplementClassCode();
            }
        }

        Service service = new Service(schema, id, serviceName, sql, getExecutorFullName(),
                serviceMethods);
        service.setLanguage(language);
        service.setImplementBy(implementBy);
        service.setPackageName(packageName);
        service.setComment(comment);
        schema.add(session, service, lock);

        ServiceExecutorFactory factory = PluginManager.getPlugin(ServiceExecutorFactory.class, language);
        if (factory == null)
            factory = new JavaServiceExecutorFactory();

        // 数据库在启动阶段执行CREATE SERVICE语句时不用再生成代码
        if (!session.getDatabase().isStarting()) {
            if (factory.supportsGenCode()) { // 支持其他语言插件生成自己的代码
                factory.genCode(service);
            } else {
                if (genCode) {
                    genServiceInterfaceCode();
                    genServiceExecutorCode(true);
                }
            }
        }
        // 最后才创建执行器,此时implementBy肯定存在了
        // 如果没有提前生成ServiceExecutor才去使用JavaServiceExecutor
        if (!genCode) {
            if (isJava) {
                service.setExecutorCode(genServiceExecutorCode(false));
            } else {
                ServiceExecutor executor = factory.createServiceExecutor(service);
                service.setExecutor(executor);
            }
        }
        return 0;
    }

    private String getCreateSQL() {
        StatementBuilder buff = new StatementBuilder("CREATE SERVICE ");
        buff.append(session.getDatabase().quoteIdentifier(serviceName));
        if (comment != null) {
            buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
        }
        buff.append(" (\n");
        int methodSize = serviceMethods.size();
        for (int methodIndex = 0; methodIndex < methodSize; methodIndex++) {
            CreateTableData data = serviceMethods.get(methodIndex).data;
            buff.append("    ").append(session.getDatabase().quoteIdentifier(data.tableName))
                    .append("(");
            // 最后一列是返回类型所以要减一
            for (int i = 0, size = data.columns.size() - 1; i < size; i++) {
                Column column = data.columns.get(i);
                if (i != 0) {
                    buff.append(", ");
                }
                buff.append(column.getCreateSQL());
            }
            // 返回类型不需要生成列名
            buff.append(") ").append(data.columns.get(data.columns.size() - 1).getCreateSQL(true));
            if (methodIndex != methodSize - 1) {
                buff.append(",");
            }
            buff.append("\n");
        }
        buff.append(")\n");
        if (language != null) {
            buff.append("LANGUAGE '").append(language).append("'\n");
        }
        if (packageName != null) {
            buff.append("PACKAGE '").append(packageName).append("'\n");
        }
        if (implementBy != null) {
            buff.append("IMPLEMENT BY '").append(implementBy).append("'\n");
        }
        if (genCode && codePath != null) {
            buff.append("GENERATE CODE '").append(codePath).append("'\n");
        }
        if (codePath != null) {
            buff.append("CODE PATH '").append(codePath).append("'\n");
        }
        if (serviceParameters != null && !serviceParameters.isEmpty()) {
            buff.append(" PARAMETERS");
            Database.appendMap(buff, serviceParameters);
        }
        return buff.toString();
    }

    public static String toClassName(String n) {
        n = CamelCaseHelper.toCamelFromUnderscore(n);
        return Character.toUpperCase(n.charAt(0)) + n.substring(1);
    }

    private static String toMethodName(String n) {
        return CamelCaseHelper.toCamelFromUnderscore(n);
    }

    private static String toFieldName(String n) {
        return CamelCaseHelper.toCamelFromUnderscore(n);
    }

    private void genServiceInterfaceCode() {
        // PreparedStatement字段声明
        StringBuilder psBuff = new StringBuilder();
        // PreparedStatement字段初始化
        StringBuilder psInitBuff = new StringBuilder();

        TreeSet importSet = new TreeSet<>();
        importSet.add("org.lealone.client.ClientServiceProxy");
        importSet.add("java.sql.*");

        // 生成方法签名和方法体的代码
        int methodSize = serviceMethods.size();
        ArrayList methodSignatureList = new ArrayList<>(methodSize);
        ArrayList proxyMethodBodyList = new ArrayList<>(methodSize);

        for (int methodIndex = 0; methodIndex < methodSize; methodIndex++) {
            StringBuilder methodSignatureBuff = new StringBuilder();
            StringBuilder proxyMethodBodyBuff = new StringBuilder();
            String psVarName = "ps" + (methodIndex + 1);
            CreateTableData data = serviceMethods.get(methodIndex).data;

            psBuff.append("        private final PreparedStatement ").append(psVarName).append(";\r\n");
            psInitBuff.append("            ").append(psVarName)
                    .append(" = ClientServiceProxy.prepareStatement(url, \"EXECUTE SERVICE ")
                    .append(serviceName).append(" ").append(data.tableName).append("(");

            // 最后一列是返回类型所以要减一
            for (int i = 0, size = data.columns.size() - 1; i < size; i++) {
                if (i != 0) {
                    psInitBuff.append(", ");
                }
                psInitBuff.append("?");
            }
            psInitBuff.append(")\");\r\n");

            Column returnColumn = data.columns.get(data.columns.size() - 1);
            String returnType = getTypeName(returnColumn, importSet);
            String methodName = toMethodName(data.tableName);
            methodSignatureBuff.append(returnType).append(" ").append(methodName).append("(");

            proxyMethodBodyBuff.append("            try {\r\n");

            for (int i = 0, size = data.columns.size() - 1; i < size; i++) {
                if (i != 0) {
                    methodSignatureBuff.append(", ");
                }
                Column c = data.columns.get(i);
                String cType = getTypeName(c, importSet);
                String cName = toFieldName(c.getName());
                methodSignatureBuff.append(cType).append(" ").append(cName);
                proxyMethodBodyBuff.append("                ").append(psVarName).append(".");
                if (c.getTable() != null) {
                    proxyMethodBodyBuff.append("setString(").append(i + 1).append(", ").append(cName)
                            .append(".encode());\r\n");
                } else {
                    proxyMethodBodyBuff.append(getPreparedStatementSetterMethodName(cType)).append("(")
                            .append(i + 1).append(", ");
                    if (cType.toUpperCase().equals("UUID")) {
                        importSet.add(UUID.class.getName());
                        importSet.add(ValueUuid.class.getName());
                        proxyMethodBodyBuff.append("ValueUuid.get(").append(cName)
                                .append(").getBytes()");
                    } else {
                        proxyMethodBodyBuff.append(cName);
                    }
                    proxyMethodBodyBuff.append(");\r\n");
                }
            }
            methodSignatureBuff.append(")");
            methodSignatureList.add(methodSignatureBuff);

            if (returnType.equals("void")) {
                proxyMethodBodyBuff.append("                ").append(psVarName)
                        .append(".executeUpdate();\r\n");
            } else {
                proxyMethodBodyBuff.append("                ResultSet rs = ").append(psVarName)
                        .append(".executeQuery();\r\n");
                proxyMethodBodyBuff.append("                rs.next();\r\n");

                if (returnColumn.isCollectionType()) {
                    proxyMethodBodyBuff.append("                @SuppressWarnings(\"unchecked\")\r\n");
                    proxyMethodBodyBuff.append("                ").append(returnType).append(" ret = (")
                            .append(returnType).append(")rs.getObject(1);\r\n");
                    proxyMethodBodyBuff.append("                rs.close();\r\n");
                    proxyMethodBodyBuff.append("                return ret;\r\n");
                } else if (returnColumn.getTable() != null) {
                    proxyMethodBodyBuff.append("                String ret = rs.getString(1);\r\n");
                    proxyMethodBodyBuff.append("                rs.close();\r\n");
                    proxyMethodBodyBuff.append("                return ").append(returnType)
                            .append(".decode(ret);\r\n");
                } else {
                    proxyMethodBodyBuff.append("                ").append(returnType).append(" ret = ");
                    if (returnType.toUpperCase().equals("UUID")) {
                        importSet.add(UUID.class.getName());
                        importSet.add(ValueUuid.class.getName());
                        proxyMethodBodyBuff.append("ValueUuid.get(rs.")
                                .append(getResultSetReturnMethodName(returnType))
                                .append("(1)).getUuid();\r\n");
                    } else {
                        proxyMethodBodyBuff.append("rs.")
                                .append(getResultSetReturnMethodName(returnType)).append("(1);\r\n");
                    }
                    proxyMethodBodyBuff.append("                rs.close();\r\n");
                    proxyMethodBodyBuff.append("                return ret;\r\n");
                }
            }
            proxyMethodBodyBuff.append("            } catch (Throwable e) {\r\n");
            proxyMethodBodyBuff.append("                throw ClientServiceProxy.failed(\"")
                    .append(serviceName).append('.').append(data.tableName).append("\", e);\r\n");
            proxyMethodBodyBuff.append("            }\r\n");

            proxyMethodBodyList.add(proxyMethodBodyBuff);
        }

        // 生成package和import代码
        StringBuilder buff = new StringBuilder();
        buff.append("package ").append(packageName).append(";\r\n");
        buff.append("\r\n");
        for (String i : importSet) {
            buff.append("import ").append(i).append(";\r\n");
        }
        buff.append("\r\n");

        // 接口注释
        buff.append("/**\r\n");
        buff.append(" * Service interface for '").append(serviceName.toLowerCase()).append("'.\r\n");
        buff.append(" *\r\n");
        buff.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.\r\n");
        buff.append(" */\r\n");

        // 服务接口
        String serviceInterfaceName = toClassName(serviceName);
        buff.append("public interface ").append(serviceInterfaceName).append(" {\r\n");
        buff.append("\r\n");

        // 生成服务接口方法
        for (StringBuilder m : methodSignatureList) {
            buff.append("    ").append(m).append(";\r\n");
            buff.append("\r\n");
        }

        String createMethodName = getParameterValue(ServiceSetting.CREATE_METHOD_NAME, "create");

        // 生成两个static create方法
        buff.append("    static ").append(serviceInterfaceName).append(" ").append(createMethodName)
                .append("() {\r\n");
        buff.append("        return ").append(createMethodName).append("(null);\r\n");
        buff.append("    }\r\n");
        buff.append("\r\n");
        buff.append("    static ").append(serviceInterfaceName).append(" ").append(createMethodName)
                .append("(String url) {\r\n");
        buff.append("        if (url == null)\r\n");
        buff.append("            url = ClientServiceProxy.getUrl();\r\n");
        buff.append("\r\n");
        buff.append("        if (ClientServiceProxy.isEmbedded(url))\r\n");
        buff.append("            return new ").append(implementBy).append("();\r\n");
        buff.append("        else\r\n");
        buff.append("            return new ServiceProxy(url);\r\n");
        buff.append("    }\r\n");

        // 生成Service Proxy类
        buff.append("\r\n");
        buff.append("    static class ServiceProxy implements ").append(serviceInterfaceName)
                .append(" {\r\n");
        buff.append("\r\n");
        buff.append(psBuff);
        buff.append("\r\n");
        buff.append("        private ServiceProxy(String url) {\r\n");
        buff.append(psInitBuff);
        buff.append("        }\r\n");
        for (int i = 0; i < methodSize; i++) {
            buff.append("\r\n");
            buff.append("        @Override\r\n");
            buff.append("        public ").append(methodSignatureList.get(i)).append(" {\r\n");
            buff.append(proxyMethodBodyList.get(i));
            buff.append("        }\r\n");
        }
        buff.append("    }\r\n");
        buff.append("}\r\n");

        writeFile(codePath, packageName, serviceInterfaceName, buff);
    }

    private boolean isServiceImplementClassExists() {
        String path = codePath;
        path = path.replace('/', File.separatorChar);
        if (!path.endsWith(File.separator))
            path = path + File.separator;
        String srcFile = path + implementBy.replace('.', File.separatorChar) + ".java";
        return new File(srcFile).exists();
    }

    private String getParameterValue(ServiceSetting key, String defaultValue) {
        String v = serviceParameters == null ? null : serviceParameters.get(key.name());
        return v == null ? defaultValue : v.trim();
    }

    private void genServiceImplementClassCode() {
        if (isServiceImplementClassExists()) {
            return;
        }
        TreeSet importSet = new TreeSet<>();

        // 生成方法签名和方法体的代码
        int methodSize = serviceMethods.size();
        ArrayList methodSignatureList = new ArrayList<>(methodSize);
        ArrayList methodReturnTypeList = new ArrayList<>(methodSize);

        for (int methodIndex = 0; methodIndex < methodSize; methodIndex++) {
            StringBuilder methodSignatureBuff = new StringBuilder();
            CreateTableData data = serviceMethods.get(methodIndex).data;

            Column returnColumn = data.columns.get(data.columns.size() - 1);
            String returnType = getTypeName(returnColumn, importSet);
            String methodName = toMethodName(data.tableName);
            methodSignatureBuff.append(returnType).append(" ").append(methodName).append("(");
            methodReturnTypeList.add(returnType);

            for (int i = 0, size = data.columns.size() - 1; i < size; i++) {
                if (i != 0) {
                    methodSignatureBuff.append(", ");
                }
                Column c = data.columns.get(i);
                String cType = getTypeName(c, importSet);
                String cName = toFieldName(c.getName());
                methodSignatureBuff.append(cType).append(" ").append(cName);
            }
            methodSignatureBuff.append(")");
            methodSignatureList.add(methodSignatureBuff);
        }

        String serviceInterfaceName = toClassName(serviceName);
        int pos = implementBy.lastIndexOf('.');
        String implementPackageName = implementBy.substring(0, pos);
        String implementClassName = implementBy.substring(pos + 1);
        if (!implementPackageName.equals(packageName)) {
            importSet.add(packageName + "." + serviceInterfaceName);
        }
        // 生成package和import代码
        StringBuilder buff = new StringBuilder();
        buff.append("package ").append(implementPackageName).append(";\r\n");
        if (importSet.size() > 0) {
            buff.append("\r\n");
            for (String i : importSet) {
                buff.append("import ").append(i).append(";\r\n");
            }
        }
        buff.append("\r\n");

        // 生成Service实现类的骨架代码
        buff.append("public class ").append(implementClassName);
        if (genCode)
            buff.append(" implements ").append(serviceInterfaceName);
        buff.append(" {\r\n");
        for (int i = 0; i < methodSize; i++) {
            buff.append("\r\n");
            if (genCode)
                buff.append("    @Override\r\n");
            buff.append("    public ").append(methodSignatureList.get(i)).append(" {\r\n");
            if (!methodReturnTypeList.get(i).equals("void"))
                buff.append("        return null;\r\n");
            buff.append("    }\r\n");
        }
        buff.append("}\r\n");

        writeFile(codePath, implementPackageName, implementClassName, buff);
    }

    private abstract class ServiceExecutorMethodGenerator {

        public StringBuilder genCode(TreeSet importSet, String method) {
            return genCode(importSet, method, "");
        }

        public StringBuilder genCode(TreeSet importSet, String method, String varInit) {
            StringBuilder buff = new StringBuilder();
            buff.append("\r\n");
            buff.append("    @Override\r\n");
            buff.append("    public ").append(method).append(" {\r\n");
            buff.append(varInit);
            buff.append("        switch (methodName) {\r\n");

            int index = 0;
            for (CreateTable m : serviceMethods) {
                index++;
                // switch语句不同case代码块的本地变量名不能相同
                String resultVarName = "result" + index;
                CreateTableData data = m.data;

                Column returnColumn = data.columns.get(data.columns.size() - 1);
                String returnType = getTypeName(returnColumn, importSet);
                StringBuilder argsBuff = new StringBuilder();
                String methodName = toMethodName(data.tableName);
                buff.append("        case \"").append(data.tableName).append("\":\r\n");
                // 有参数,参数放在一个json数组中
                int size = data.columns.size() - 1;
                if (size > 0) {
                    if (varInit.length() > 0)
                        buff.append("            ja = new JsonArray(json);\r\n");
                    for (int i = 0; i < size; i++) {
                        if (i != 0) {
                            argsBuff.append(", ");
                        }
                        Column c = data.columns.get(i);
                        String cType = getTypeName(c, importSet);
                        String cName = "p_" + toFieldName(c.getName()) + "_" + index;

                        buff.append("            ").append(cType).append(" ").append(cName)
                                .append(" = ");
                        genVarInitCode(buff, importSet, c, cType, i);
                        argsBuff.append(cName);
                    }
                }
                boolean isVoid = returnType.equals("void");
                buff.append("            ");
                if (!isVoid) {
                    if (createResultVar())
                        buff.append(returnType).append(" ").append(resultVarName).append(" = ");
                    else
                        buff.append("return ");
                }
                buff.append("si.").append(methodName).append("(").append(argsBuff).append(");\r\n");
                if (!isVoid) {
                    if (createResultVar())
                        genReturnCode(buff, importSet, returnColumn, returnType, resultVarName);
                } else {
                    buff.append("            return ").append(getReturnType()).append(";\r\n");
                }
            }
            buff.append("        default:\r\n");
            buff.append("            throw noMethodException(methodName);\r\n");
            buff.append("        }\r\n");
            buff.append("    }\r\n");
            return buff;
        }

        protected abstract void genVarInitCode(StringBuilder buff, TreeSet importSet, Column c,
                String cType, int cIndex);

        protected void genReturnCode(StringBuilder buff, TreeSet importSet, Column returnColumn,
                String returnType, String resultVarName) {
            buff.append("            return ").append(resultVarName).append(";\r\n");
        }

        protected String getReturnType() {
            return "NO_RETURN_VALUE";
        }

        protected boolean createResultVar() {
            return false;
        }
    }

    private class ValueServiceExecutorMethodGenerator extends ServiceExecutorMethodGenerator {
        @Override
        protected void genVarInitCode(StringBuilder buff, TreeSet importSet, Column c,
                String cType, int cIndex) {
            if (c.isCollectionType()) {
                buff.append("methodArgs[").append(cIndex).append("].getCollection();\r\n");
            } else if (c.getTable() != null) {
                buff.append(cType).append(".decode(").append("methodArgs[").append(cIndex)
                        .append("].getString());\r\n");
            } else {
                buff.append("methodArgs[").append(cIndex).append("].").append(getValueMethodName(cType))
                        .append("();\r\n");
            }
        }

        @Override
        protected void genReturnCode(StringBuilder buff, TreeSet importSet, Column returnColumn,
                String returnType, String resultVarName) {
            buff.append("            if (").append(resultVarName).append(" == null)\r\n");
            buff.append("                return ValueNull.INSTANCE;\r\n");
            if (returnColumn.getTable() != null) {
                buff.append("            return ValueString.get(").append(resultVarName)
                        .append(".encode());\r\n");
            } else {
                Column c = returnColumn;
                if (c instanceof ListColumn) {
                    ListColumn lc = (ListColumn) c;
                    buff.append("            return ValueList.get(")
                            .append(getTypeName(lc.element, importSet)).append(".class, ")
                            .append(resultVarName).append(");\r\n");
                } else if (c instanceof SetColumn) {
                    SetColumn sc = (SetColumn) c;
                    buff.append("            return ValueSet.get(")
                            .append(getTypeName(sc.element, importSet)).append(".class, ")
                            .append(resultVarName).append(");\r\n");
                } else if (c instanceof MapColumn) {
                    MapColumn mc = (MapColumn) c;
                    buff.append("            return ValueMap.get(")
                            .append(getTypeName(mc.key, importSet)).append(".class, ")
                            .append(getTypeName(mc.value, importSet)).append(".class, ")
                            .append(resultVarName).append(");\r\n");
                } else {
                    buff.append("            return ").append(getReturnMethodName(returnType))
                            .append("(").append(resultVarName).append(")").append(";\r\n");
                }
            }
        }

        @Override
        protected String getReturnType() {
            return "ValueNull.INSTANCE";
        }

        @Override
        protected boolean createResultVar() {
            return true;
        }
    }

    private class MapServiceExecutorMethodGenerator extends ServiceExecutorMethodGenerator {
        @Override
        protected void genVarInitCode(StringBuilder buff, TreeSet importSet, Column c,
                String cType, int cIndex) {
            String cName = c.getName();
            String methodName;
            if (c.getTable() != null) {
                buff.append(cType).append(".decode(").append("toString(\"").append(cName)
                        .append("\", methodArgs));\r\n");
            } else {
                if (c instanceof ListColumn) {
                    methodName = "toList";
                } else if (c instanceof SetColumn) {
                    methodName = "toSet";
                } else if (c instanceof MapColumn) {
                    methodName = "toMap";
                } else {
                    methodName = getMapMethodName(cType);
                }
                buff.append(methodName).append("(\"").append(cName).append("\", methodArgs);\r\n");
            }
        }
    }

    private class JsonServiceExecutorMethodGenerator extends ServiceExecutorMethodGenerator {
        @Override
        protected void genVarInitCode(StringBuilder buff, TreeSet importSet, Column c,
                String cType, int cIndex) {
            buff.append(getJsonArrayMethodName(c, cType, cIndex, importSet)).append(";\r\n");
        }
    }

    private StringBuilder genServiceExecutorCode(boolean writeFile) {
        TreeSet importSet = new TreeSet<>();
        importSet.add(ServiceExecutor.class.getName());

        // 生成public Value executeService(String methodName, Value[] methodArgs)方法
        importSet.add("org.lealone.db.value.*");
        StringBuilder buffValueMethod = new ValueServiceExecutorMethodGenerator().genCode(importSet,
                "Value executeService(String methodName, Value[] methodArgs)");

        boolean generateMapExecutorMethod = Boolean
                .parseBoolean(getParameterValue(ServiceSetting.GENERATE_MAP_EXECUTOR_METHOD, "true"));
        boolean generateJsonExecutorMethod = Boolean
                .parseBoolean(getParameterValue(ServiceSetting.GENERATE_JSON_EXECUTOR_METHOD, "true"));
        StringBuilder buffMapMethod = null;
        StringBuilder buffJsonMethod = null;

        // 生成public String executeService(String methodName, Map methodArgs)方法
        if (generateMapExecutorMethod) {
            importSet.add(Map.class.getName());
            buffMapMethod = new MapServiceExecutorMethodGenerator().genCode(importSet,
                    "Object executeService(String methodName, Map methodArgs)");
        }

        // 生成public String executeService(String methodName, String json)方法
        if (generateJsonExecutorMethod) {
            // 提前看一下是否用到JsonArray
            String varInit = "";
            for (CreateTable m : serviceMethods) {
                if (m.data.columns.size() - 1 > 0) {
                    importSet.add("org.lealone.plugins.orm.json.JsonArray");
                    varInit = "        JsonArray ja = null;\r\n";
                    break;
                }
            }
            buffJsonMethod = new JsonServiceExecutorMethodGenerator().genCode(importSet,
                    "Object executeService(String methodName, String json)", varInit);
        }

        String executorPackageName = getExecutorPackageName();

        // 生成package和import代码
        String serviceImplementClassName = implementBy;
        if (implementBy != null) {
            if (implementBy.startsWith(executorPackageName)) {
                serviceImplementClassName = implementBy.substring(executorPackageName.length() + 1);
            } else {
                int lastDotPos = implementBy.lastIndexOf('.');
                if (lastDotPos > 0) {
                    serviceImplementClassName = implementBy.substring(lastDotPos + 1);
                    importSet.add(implementBy);
                }
            }
        }
        StringBuilder buff = new StringBuilder();
        buff.append("package ").append(executorPackageName).append(";\r\n");
        buff.append("\r\n");
        for (String i : importSet) {
            buff.append("import ").append(i).append(";\r\n");
        }
        buff.append("\r\n");

        // Service executor注释
        buff.append("/**\r\n");
        buff.append(" * Service executor for '").append(serviceName.toLowerCase()).append("'.\r\n");
        buff.append(" *\r\n");
        buff.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.\r\n");
        buff.append(" */\r\n");

        // 生成Service executor类的代码
        String className = getExecutorSimpleName();
        buff.append("public class ").append(className).append(" implements ServiceExecutor {\r\n");
        buff.append("\r\n");
        buff.append("    private final ").append(serviceImplementClassName).append(" si = new ")
                .append(serviceImplementClassName).append("();\r\n");

        // 生成默认构造函数
        // buff.append("\r\n");
        // buff.append(" public ").append(className).append("() {\r\n");
        // buff.append(" }\r\n");
        // buff.append("\r\n");

        buff.append(buffValueMethod);
        if (generateMapExecutorMethod)
            buff.append(buffMapMethod);
        if (generateJsonExecutorMethod)
            buff.append(buffJsonMethod);

        buff.append("}\r\n");
        if (writeFile)
            writeFile(codePath, executorPackageName, className, buff);
        return buff;
    }

    private String getExecutorPackageName() {
        return packageName + ".executor";
    }

    private String getExecutorFullName() {
        return getExecutorPackageName() + "." + getExecutorSimpleName();
    }

    private String getExecutorSimpleName() {
        return toClassName(serviceName) + "Executor";
    }

    public static void writeFile(String codePath, String packageName, String className,
            StringBuilder... buffArray) {
        String path = codePath;
        if (!path.endsWith(File.separator))
            path = path + File.separator;
        path = path.replace('/', File.separatorChar);
        path = path + packageName.replace('.', File.separatorChar) + File.separatorChar;
        try {
            if (!new File(path).exists()) {
                new File(path).mkdirs();
            }
            Charset utf8 = Charset.forName("UTF-8");
            BufferedOutputStream file = new BufferedOutputStream(
                    new FileOutputStream(path + className + ".java"));
            for (StringBuilder buff : buffArray) {
                file.write(buff.toString().getBytes(utf8));
            }
            file.close();
        } catch (IOException e) {
            throw DbException.convertIOException(e, "Failed to genJavaCode, path = " + path);
        }
    }

    static String getTypeName(Column c, TreeSet importSet) {
        String cType;
        if (c instanceof ListColumn) {
            importSet.add("java.util.List");
            ListColumn lc = (ListColumn) c;
            cType = "List<" + getTypeName(lc.element, importSet) + ">";
            return cType;
        } else if (c instanceof SetColumn) {
            importSet.add("java.util.Set");
            SetColumn sc = (SetColumn) c;
            cType = "Set<" + getTypeName(sc.element, importSet) + ">";
            return cType;
        } else if (c instanceof MapColumn) {
            importSet.add("java.util.Map");
            MapColumn mc = (MapColumn) c;
            cType = "Map<" + getTypeName(mc.key, importSet) + ", " + getTypeName(mc.value, importSet)
                    + ">";
            return cType;
        }
        if (c.getTable() != null) {
            cType = c.getTable().getName();
            cType = toClassName(cType);
            String packageName = c.getTable().getPackageName();
            if (packageName != null)
                cType = packageName + "." + cType;
        } else {
            switch (c.getType()) {
            case Value.BYTES:
                cType = "byte[]";
                break;
            case Value.UUID:
                cType = UUID.class.getName();
                break;
            default:
                cType = DataType.getTypeClassName(c.getType());
            }
        }
        int lastDotPos = cType.lastIndexOf('.');
        if (lastDotPos > 0) {
            if (cType.startsWith("java.lang.")) {
                cType = cType.substring(10);
            } else {
                importSet.add(cType);
                cType = cType.substring(lastDotPos + 1);
            }
        }
        // 把java.lang.Void转成void,这样就不用加return语句
        if (cType.equalsIgnoreCase("void")) {
            cType = "void";
        }
        return cType;
    }

    private static String getReturnMethodName(String type) {
        type = type.toUpperCase();
        switch (type) {
        case "BOOLEAN":
            return "ValueBoolean.get";
        case "BYTE":
            return "ValueByte.get";
        case "SHORT":
            return "ValueShort.get";
        case "INTEGER":
            return "ValueInt.get";
        case "LONG":
            return "ValueLong.get";
        case "BIGDECIMAL":
            return "ValueDecimal.get";
        case "TIME":
            return "ValueTime.get";
        case "DATE":
            return "ValueDate.get";
        case "TIMESTAMP":
            return "ValueTimestamp.get";
        case "BYTE[]":
            return "ValueBytes.get";
        case "UUID":
            return "ValueUuid.get";
        case "STRING":
        case "STRING_IGNORECASE":
        case "STRING_FIXED":
            return "ValueString.get";
        case "DOUBLE":
            return "ValueDouble.get";
        case "FLOAT":
            return "ValueFloat.get";
        case "NULL":
            return "ValueNull.INSTANCE";
        case "UNKNOWN": // anything
        case "OBJECT":
            return "ValueShort.get";
        case "BLOB":
            return "ValueShort.get";
        case "CLOB":
            return "ValueShort.get";
        case "ARRAY":
            return "ValueArray.get";
        case "RESULT_SET":
            return "ValueResultSet.get";
        }
        return "ValueString.get";
    }

    private static String m(String str, int i) {
        return str + "(ja.getValue(" + i + ").toString())";
    }

    // 根据具体类型调用合适的JsonArray方法
    private static String getJsonArrayMethodName(Column c, String type0, int i,
            TreeSet importSet) {
        String type = type0.toUpperCase();
        switch (type) {
        case "BOOLEAN":
            return m("Boolean.valueOf", i);
        case "BYTE":
            return m("Byte.valueOf", i);
        case "SHORT":
            return m("Short.valueOf", i);
        case "INTEGER":
            return m("Integer.valueOf", i);
        case "LONG":
            return m("Long.valueOf", i);
        case "BIGDECIMAL":
            return m("new java.math.BigDecimal", i);
        case "TIME":
            return m("java.sql.Time.valueOf", i);
        case "DATE":
            return m("java.sql.Date.valueOf", i);
        case "TIMESTAMP":
            return m("java.sql.Timestamp.valueOf", i);
        case "BYTE[]":
            return "ja.getString(" + i + ").getBytes()";
        case "UUID":
            return m("java.util.UUID.fromString", i);
        case "STRING":
        case "STRING_IGNORECASE":
        case "STRING_FIXED":
            return "ja.getString(" + i + ")";
        case "DOUBLE":
            return m("Double.valueOf", i);
        case "FLOAT":
            return m("Float.valueOf", i);
        case "NULL":
            return null;
        case "UNKNOWN": // anything
        case "OBJECT":
            return "ja.getValue(" + i + ")";
        case "BLOB":
            return "new org.lealone.db.value.ReadonlyBlob(ja.getString(" + i + "))";
        case "CLOB":
            return "new org.lealone.db.value.ReadonlyClob(ja.getString(" + i + "))";
        case "ARRAY":
            return "new org.lealone.db.value.ReadonlyArray(ja.getString(" + i + "))";
        case "RESULT_SET":
            return "ja.getValue(" + i + ")";
        }
        if (c instanceof ListColumn)
            return "ja.getList(" + i + ")";
        if (c instanceof SetColumn)
            return "ja.getSet(" + i + ")";
        if (c instanceof MapColumn) {
            MapColumn mc = (MapColumn) c;
            return "ja.getMap(" + i + ", " + getTypeName(mc.key, importSet) + ".class)";
        }
        return type0 + ".decode(ja.getString(" + i + "))";
    }

    // 根据具体类型调用合适的Map方法
    private static String getMapMethodName(String type0) {
        String type = type0.toUpperCase();
        switch (type) {
        case "BOOLEAN":
            return "toBoolean";
        case "BYTE":
            return "toByte";
        case "SHORT":
            return "toShort";
        case "INTEGER":
            return "toInt";
        case "LONG":
            return "toLong";
        case "BIGDECIMAL":
            return "toBigDecimal";
        case "TIME":
            return "toTime";
        case "DATE":
            return "toDate";
        case "TIMESTAMP":
            return "toTimestamp";
        case "BYTE[]":
            return "toBytes";
        case "UUID":
            return "toUUID";
        case "STRING":
        case "STRING_IGNORECASE":
        case "STRING_FIXED":
            return "toString";
        case "DOUBLE":
            return "toDouble";
        case "FLOAT":
            return "toFloat";
        case "BLOB":
            return "toBlob";
        case "CLOB":
            return "toClob";
        case "ARRAY":
            return "toArray";
        case "NULL":
        case "UNKNOWN": // anything
        case "OBJECT":
            return "toObject";
        case "RESULT_SET":
            return "(java.sql.ResultSet) toObject";
        }
        return "toObject";
    }

    // 根据具体类型调用合适的Value方法
    private static String getValueMethodName(String type) {
        type = type.toUpperCase();
        switch (type) {
        case "BOOLEAN":
            return "getBoolean";
        case "BYTE":
            return "getByte";
        case "SHORT":
            return "getShort";
        case "INTEGER":
            return "getInt";
        case "LONG":
            return "getLong";
        case "BIGDECIMAL":
            return "getBigDecimal";
        case "TIME":
            return "getTime";
        case "DATE":
            return "getDate";
        case "TIMESTAMP":
            return "getTimestamp";
        case "BYTE[]":
            return "getBytes";
        case "UUID":
            return "getUuid";
        case "STRING":
        case "STRING_IGNORECASE":
        case "STRING_FIXED":
            return "getString";
        case "DOUBLE":
            return "getDouble";
        case "FLOAT":
            return "getFloat";
        case "NULL":
            return null;
        case "UNKNOWN": // anything
        case "OBJECT":
            return "getObject";
        case "BLOB":
            return "getBlob";
        case "CLOB":
            return "getClob";
        case "ARRAY":
            return "getArray";
        case "RESULT_SET":
            return "getResultSet";
        }
        return "getObject";
    }

    private static String getResultSetReturnMethodName(String type) {
        type = type.toUpperCase();
        switch (type) {
        case "BOOLEAN":
            return "getBoolean";
        case "BYTE":
            return "getByte";
        case "SHORT":
            return "getShort";
        case "INTEGER":
            return "getInt";
        case "LONG":
            return "getLong";
        case "BIGDECIMAL":
            return "getBigDecimal";
        case "TIME":
            return "getTime";
        case "DATE":
            return "getDate";
        case "TIMESTAMP":
            return "getTimestamp";
        case "BYTE[]":
            return "getBytes";
        case "UUID":
            return "getBytes";
        case "STRING":
        case "STRING_IGNORECASE":
        case "STRING_FIXED":
            return "getString";
        case "DOUBLE":
            return "getDouble";
        case "FLOAT":
            return "getFloat";
        case "NULL":
            throw DbException.getInternalError();
        case "UNKNOWN": // anything
        case "OBJECT":
            return "getObject";
        case "BLOB":
            return "getBlob";
        case "CLOB":
            return "getClob";
        case "ARRAY":
            return "getArray";
        case "RESULT_SET":
            throw DbException.getInternalError();
        }
        return "getObject";
    }

    private static String getPreparedStatementSetterMethodName(String type) {
        type = type.toUpperCase();
        switch (type) {
        case "BOOLEAN":
            return "setBoolean";
        case "BYTE":
            return "setByte";
        case "SHORT":
            return "setShort";
        case "INTEGER":
            return "setInt";
        case "LONG":
            return "setLong";
        case "BIGDECIMAL":
            return "setBigDecimal";
        case "TIME":
            return "setTime";
        case "DATE":
            return "setDate";
        case "TIMESTAMP":
            return "setTimestamp";
        case "BYTE[]":
            return "setBytes";
        case "UUID":
            return "setBytes";
        case "STRING":
        case "STRING_IGNORECASE":
        case "STRING_FIXED":
            return "setString";
        case "DOUBLE":
            return "setDouble";
        case "FLOAT":
            return "setFloat";
        case "NULL":
            throw DbException.getInternalError();
        case "UNKNOWN": // anything
        case "OBJECT":
            return "setObject";
        case "BLOB":
            return "setBlob";
        case "CLOB":
            return "setClob";
        case "ARRAY":
            return "setArray";
        case "RESULT_SET":
            throw DbException.getInternalError();
        }
        return "setObject";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy