org.bimserver.tools.generators.ProtocolBuffersGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.bimserver.tools.generators.ProtocolBuffersGenerator.java

Source

package org.bimserver.tools.generators;

/******************************************************************************
 * Copyright (C) 2009-2012  BIMserver.org
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *****************************************************************************/

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.activation.DataHandler;

import org.apache.commons.io.FileUtils;
import org.bimserver.shared.meta.SClass;
import org.bimserver.shared.meta.SField;
import org.bimserver.shared.meta.SMethod;
import org.bimserver.shared.meta.SParameter;
import org.bimserver.shared.meta.SService;
import org.bimserver.utils.Licenser;
import org.bimserver.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ProtocolBuffersGenerator {
    private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolBuffersGenerator.class);
    private final Map<Class<?>, String> generatedClasses = new HashMap<Class<?>, String>();

    public void generate(Class<?> serviceInterfaceClass, File protoFile, File descFile,
            File reflectorImplementationFile, boolean createBaseMessages, SService service, String... imports) {
        generateProtoFile(serviceInterfaceClass, protoFile, createBaseMessages, service, imports);
        generateProtocolBuffersObjects(protoFile, descFile, true);
        generateServiceInterfaceImplementationForReflector(service, reflectorImplementationFile);
    }

    private void generateServiceInterfaceImplementationForReflector(SService service,
            File reflectorImplementationFile) {
        try {
            PrintWriter out = new PrintWriter(reflectorImplementationFile);
            out.println("package org.bimserver.pb;\n");
            out.println(Licenser.getCommentedLicenseText(new File("license.txt")));
            out.println("import org.bimserver.shared.pb.Reflector;\n");
            out.println("@SuppressWarnings(\"unchecked\")");
            out.println("public class " + service.getName() + "ReflectorImpl implements "
                    + service.getInstanceClass().getName() + " {\n");
            out.println("private Reflector reflector;\n");
            out.println("\tpublic " + service.getName()
                    + "ReflectorImpl (Reflector reflector) {this.reflector = reflector;}");

            for (SMethod method : service.getMethods()) {
                String returnType = method.getPrintableName();
                out.print("\tpublic " + returnType + " " + method.getName() + "(");
                StringBuilder sb1 = new StringBuilder();
                StringBuilder sb2 = new StringBuilder();
                for (SParameter parameter : method.getParameters()) {
                    sb1.append(parameter.getPrintableName() + " " + parameter.getName()
                            + (parameter.isLast() ? "" : ", "));
                    sb2.append(parameter.getName() + (parameter.isLast() ? "" : ", "));
                }
                out.println(sb1.toString()
                        + ") throws org.bimserver.shared.exceptions.UserException, org.bimserver.shared.exceptions.ServerException {");
                if (method.returnsVoid()) {
                    out.println("\t\treflector.callMethod(\"" + service.getName() + "\", \"" + method.getName()
                            + "\"" + ", " + method.getReturnType().getName() + ".class"
                            + (method.getParameters().size() > 0 ? ", " : "") + sb2.toString() + ");");
                } else {
                    out.println("\t\treturn (" + returnType + ") reflector.callMethod(\"" + service.getName()
                            + "\", \"" + method.getName() + "\", " + method.getReturnType().getName() + ".class"
                            + (method.getParameters().size() > 0 ? (", " + sb2.toString()) : "") + ");");
                }
                out.println("\t}");
            }
            out.println("}");
            out.close();
        } catch (Exception e) {
            LOGGER.error("", e);
        }
    }

    private void generateProtocolBuffersObjects(File protoFile, File protoDestFile, boolean javaOut) {
        File destDir = new File("../GeneratedProtocolBuffersClient/generated");
        File protoDir = new File("../Builds/build/pb");

        File execFile = null;
        String execFileName = "protoc";
        String path = System.getenv("PATH");
        String[] folders = path.split(File.pathSeparator);
        for (String folder : folders) {
            File file = new File(folder, execFileName);
            if (file.isFile()) {
                execFile = file;
                break;
            }
        }
        if (execFile == null) {
            execFile = new File("../Builds/build/pb/protoc.exe");
        }

        try {
            ProcessBuilder processBuilder = null;
            if (javaOut) {
                processBuilder = new ProcessBuilder(execFile.getAbsolutePath(), "-I=" + protoDir.getAbsolutePath(),
                        "--java_out=" + destDir.getAbsolutePath(),
                        "--descriptor_set_out=" + protoDestFile.getAbsolutePath(), protoFile.getAbsolutePath());
            } else {
                processBuilder = new ProcessBuilder(execFile.getAbsolutePath(), "-I=" + protoDir.getAbsolutePath(),
                        "--descriptor_set_out=" + protoDestFile.getAbsolutePath(), protoFile.getAbsolutePath());
            }
            final Process exec = processBuilder.start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    InputStream inputStream = exec.getInputStream();
                    byte[] buffer = new byte[1024];
                    int red;
                    try {
                        red = inputStream.read(buffer);
                        while (red != -1) {
                            System.out.print(new String(buffer, 0, red));
                            red = inputStream.read(buffer);
                        }
                    } catch (IOException e) {
                        LOGGER.error("", e);
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    InputStream inputStream = exec.getErrorStream();
                    byte[] buffer = new byte[1024];
                    int red;
                    try {
                        red = inputStream.read(buffer);
                        while (red != -1) {
                            System.err.print(new String(buffer, 0, red));
                            red = inputStream.read(buffer);
                        }
                    } catch (IOException e) {
                        LOGGER.error("", e);
                    }
                }
            }).start();
            exec.waitFor();
        } catch (IOException e) {
            LOGGER.error("", e);
        } catch (InterruptedException e) {
            LOGGER.error("", e);
        }
    }

    public void generateServiceInterfaceImplementation(String name, File file, SService sService) {
        try {
            PrintWriter out = new PrintWriter(file);
            out.println("package org.bimserver.pb;\n");
            out.println(Licenser.getCommentedLicenseText(new File("license.txt")));
            out.println("import java.util.*;");
            out.println("import com.google.protobuf.*;");
            out.println("import org.bimserver.utils.*;");
            out.println("import org.bimserver.pb.ProtocolBuffersService.*;");
            out.println("import com.google.protobuf.BlockingRpcChannel;");
            out.println("import org.bimserver.pb.ProtocolBuffersService.ServiceInterface.BlockingInterface;");
            out.println("import com.googlecode.protobuf.socketrpc.SocketRpcController;");
            out.println("import com.googlecode.protobuf.socketrpc.RpcChannels;");
            out.println("import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories;");
            out.println("import javax.mail.util.ByteArrayDataSource;");
            out.println("import javax.activation.DataHandler;");
            out.println();
            out.println("@SuppressWarnings(\"unused\")");
            out.println("public class " + name + " implements org.bimserver.shared.ServiceInterface {\n");
            out.println("\tprivate BlockingInterface service;\n");
            out.println("\tprivate SocketRpcController rpcController;\n");
            out.println("\tpublic ProtocolBuffersServiceInterfaceImplementation(String address, int port) {");
            out.println(
                    "\t\tBlockingRpcChannel rpcChannel = RpcChannels.newBlockingRpcChannel(SocketRpcConnectionFactories.createRpcConnectionFactory(address, port));");
            out.println("\t\trpcController = new SocketRpcController();");
            out.println("\t\tservice = ServiceInterface.newBlockingStub(rpcChannel);");
            out.println("\t}\n");
            for (SMethod sMethod : sService.getMethods()) {
                String fullResultType = "void";
                if (sMethod.returnsVoid()) {
                    out.print("\tpublic void " + sMethod.getName() + "(");
                } else {
                    fullResultType = sMethod.getReturnType().getName();
                    if (sMethod.isAggregateReturnType()) {
                        fullResultType = sMethod.getReturnType().getName() + "<"
                                + sMethod.getGenericReturnType().getName() + ">";
                    }
                    out.print("\tpublic " + fullResultType + " " + sMethod.getName() + "(");
                }
                for (SParameter parameter : sMethod.getParameters()) {
                    if (parameter.isAggregate()) {
                        out.print(parameter.getType().getName() + "<" + parameter.getGenericType().getName() + "> "
                                + parameter.getName() + (parameter.isLast() ? "" : ", "));
                    } else {
                        out.print(parameter.getType().getName() + " " + parameter.getName()
                                + (parameter.isLast() ? "" : ", "));
                    }
                }
                out.println(") {");
                out.println("\t\ttry {");
                String requestClassName = StringUtils.firstUpperCase(sMethod.getName()) + "Request";
                String responseClassName = StringUtils.firstUpperCase(sMethod.getName()) + "Response";
                out.println("\t\t\t" + requestClassName + ".Builder requestBuilder = " + requestClassName
                        + ".newBuilder();");
                for (SParameter parameter : sMethod.getParameters()) {
                    if (parameter.isAggregate()) {
                        out.println("\t\t\tfor (" + parameter.getGenericType() + " val : " + parameter.getName()
                                + ") {");
                        if (parameter.getType().isPrimitive()
                                || parameter.getType().getInstanceClass() == String.class) {
                            out.println("\t\t\t\t" + parameter.getGenericType().getInstanceClass().getSimpleName()
                                    + " v = val;");
                        } else {
                            out.println("\t\t\t\tProtocolBuffersService."
                                    + parameter.getGenericType().getInstanceClass().getSimpleName() + " v = null;");
                        }
                        out.println("\t\t\t\trequestBuilder.add" + StringUtils.firstUpperCase(parameter.getName())
                                + "(v);");
                        out.println("\t\t\t}");
                    } else if (parameter.getType().getInstanceClass().isAssignableFrom(DataHandler.class)) {
                        out.println("\t\t\tByteString bs = ByteString.copyFrom(BinUtils.readInputStream("
                                + parameter.getName() + ".getInputStream()));");
                        out.println("\t\t\trequestBuilder.set" + StringUtils.firstUpperCase(parameter.getName())
                                + "(bs);");
                    } else if (parameter.getType().getInstanceClass().isPrimitive()) {
                        out.println("\t\t\trequestBuilder.set" + StringUtils.firstUpperCase(parameter.getName())
                                + "(" + parameter.getName() + ");");
                    } else if (parameter.getType().getInstanceClass().isEnum()) {
                        out.println("\t\t\trequestBuilder.set" + StringUtils.firstUpperCase(parameter.getName())
                                + "(ProtocolBuffersService."
                                + parameter.getType().getInstanceClass().getSimpleName() + ".values()["
                                + parameter.getName() + ".ordinal()]);");
                    } else if (parameter.getType().getInstanceClass().isPrimitive()
                            || parameter.getType().getInstanceClass() == String.class) {
                        out.println("\t\t\trequestBuilder.set" + StringUtils.firstUpperCase(parameter.getName())
                                + "(" + parameter.getName() + ");");
                    } else {
                        out.println("\t\t\tProtocolBuffersService."
                                + parameter.getType().getInstanceClass().getSimpleName() + ".Builder newVal = "
                                + parameter.getType().getInstanceClass().getSimpleName() + ".newBuilder();");
                        genServiceInterfaceToProtocolBuffers(out, parameter.getName(), "newVal",
                                parameter.getType());
                        out.println("\t\t\trequestBuilder.set" + StringUtils.firstUpperCase(parameter.getName())
                                + "(newVal.build());");
                    }
                }
                out.println("\t\t\t" + requestClassName + " request = requestBuilder.build();");
                if (sMethod.returnsVoid()) {
                    out.println("\t\t\tservice." + sMethod.getName() + "(rpcController, request);");
                } else {
                    out.println("\t\t\t" + responseClassName + " response = service." + sMethod.getName()
                            + "(rpcController, request);");
                    if (sMethod.isListReturnType()) {
                        out.println("\t\t\t" + fullResultType + " realResult = new ArrayList<"
                                + sMethod.getGenericReturnType().getName() + ">();");
                        String fullTypeName = sMethod.getGenericReturnType().getName();
                        if (sMethod.getGenericReturnType().isPrimitive()
                                || sMethod.getGenericReturnType().getInstanceClass() == String.class) {
                        } else {
                            fullTypeName = "ProtocolBuffersService." + sMethod.getGenericReturnType().getName();
                        }
                        out.println("\t\t\tList<" + fullTypeName + "> originalList = response.getValueList();");
                        out.println("\t\t\tfor (" + fullTypeName + " val : originalList) {");
                        if (sMethod.getGenericReturnType().getInstanceClass() == String.class) {
                            out.println("\t\t\t\trealResult.add(val);");
                        } else if (sMethod.getGenericReturnType().isEnum()) {
                            out.println("\t\t\t\trealResult.add(" + sMethod.getGenericReturnType().getName()
                                    + ".values()[val.ordinal()]);");
                        } else {
                            out.println("\t\t\t\t" + sMethod.getGenericReturnType().getName() + " v = "
                                    + genInitializerCode(sMethod.getGenericReturnType().getInstanceClass()) + ";");
                            genProtocolBuffersToServiceInterface(out, "val", "v", sMethod.getGenericReturnType(),
                                    sMethod);
                            out.println("\t\t\t\trealResult.add(v);");
                        }
                        out.println("\t\t\t}");
                        out.println("\t\treturn realResult;");
                    } else if (sMethod.getGenericReturnType().isSet()) {
                        out.println("\t\t\t" + fullResultType + " realResult = new HashSet<"
                                + sMethod.getGenericReturnType().getName() + ">();");
                        String fullTypeName = "";
                        if (sMethod.getGenericReturnType().isPrimitive()
                                || sMethod.getGenericReturnType().isString()) {
                        } else {
                            fullTypeName = "ProtocolBuffersService."
                                    + sMethod.getGenericReturnType().getInstanceClass().getSimpleName();
                        }
                        out.println("\t\t\tList<" + fullTypeName + "> originalList = response.getValueList();");
                        out.println("\t\t\tfor (" + fullTypeName + " val : originalList) {");
                        if (sMethod.getGenericReturnType().isString()) {
                            out.println("\t\t\t\trealResult.add(val);");
                        } else if (sMethod.getGenericReturnType().isEnum()) {
                            out.println("\t\t\t\trealResult.add(" + sMethod.getGenericReturnType().getName()
                                    + ".values()[val.ordinal()]);");
                        } else {
                            out.println("\t\t\t\t" + sMethod.getGenericReturnType().getName() + " v = "
                                    + genInitializerCode(sMethod.getGenericReturnType().getInstanceClass()) + ";");
                            genProtocolBuffersToServiceInterface(out, "val", "v", sMethod.getGenericReturnType(),
                                    sMethod);
                            out.println("\t\t\t\trealResult.add(v);");
                        }
                        out.println("\t\t\t}");
                        out.println("\t\treturn realResult;");
                    } else if (sMethod.getReturnType().isPrimitive() || sMethod.getReturnType().isString()) {
                        out.println("\t\t\treturn response.getValue();");
                    } else if (sMethod.getReturnType().isDate()) {
                        out.println("\t\t\treturn new java.util.Date(response.getValue());");
                    } else if (sMethod.getGenericReturnType().isEnum()) {
                        out.println("\t\t\treturn null;");
                    } else {
                        out.println("\t\t\t" + sMethod.getGenericReturnType().getName() + " realResult = new "
                                + sMethod.getReturnType().getName() + "();");
                        out.println("\t\t\t" + "ProtocolBuffersService." + sMethod.getReturnType().getName()
                                + " val = response.getValue();");
                        genProtocolBuffersToServiceInterface(out, "val", "realResult", sMethod.getReturnType(),
                                sMethod);
                        out.println("\t\t\treturn realResult;");
                    }
                }
                out.println("\t\t} catch (Exception e) {}");
                if (!sMethod.returnsVoid()) {
                    out.println("\t\treturn "
                            + getDefaultLiteralCode(sMethod.getGenericReturnType().getInstanceClass()) + ";");
                }
                out.println("\t}\n");
            }
            out.println("}");
            out.close();
        } catch (FileNotFoundException e) {
            LOGGER.error("", e);
        }
    }

    private void genProtocolBuffersToServiceInterface(PrintWriter out, String sourceName, String targetName,
            SClass sType, SMethod method) {
        for (SField field : sType.getFields()) {
            if (sType.getInstanceClass().isAssignableFrom(List.class)) {
            } else if (sType.isDate()) {
                out.println("\t\t\t\t" + targetName + ".set" + field.getName() + "(new Date(" + sourceName + "."
                        + method.getName() + "()));");
            } else if (sType.isClass()) {
                out.println("\t\t\t\t" + targetName + ".set" + field.getName() + "(Class.forName(" + sourceName
                        + "." + method.getName() + "()));");
            } else if (sType.isDataHandler()) {
                out.println("\t\t\t\t" + targetName + ".setFile(new DataHandler(new ByteArrayDataSource("
                        + sourceName + ".getFile().toByteArray(), \"\")));");
            } else if (sType.isEnum()) {
                out.println("\t\t\t\t" + targetName + ".set" + field.getName() + "("
                        + method.getReturnType().getName().replace("$", ".") + ".values()[" + sourceName + "."
                        + method.getName() + "().ordinal()]);");
            } else {
                out.println("\t\t\t\t" + targetName + ".set" + field.getName() + "(" + sourceName + "."
                        + method.getName() + "());");
            }
        }
    }

    private void genServiceInterfaceToProtocolBuffers(PrintWriter out, String sourceName, String targetName,
            SClass parameterType) {
        if (parameterType instanceof SClass) {
            SClass sClass = (SClass) parameterType;
            for (SField field : sClass.getFields()) {
                SClass fieldType = field.getType();
                if (fieldType instanceof SClass) {
                    SClass fieldClass = (SClass) fieldType;
                    if (fieldClass.isList()) {
                        out.println("\t\t\tfor (" + fieldType.getName() + " o : " + sourceName + "."
                                + field.getName() + "()) {");
                        out.println("\t\t\t\t" + targetName + ".add" + field.getName() + "(o);");
                        out.println("\t\t\t}");
                    } else if (fieldClass.isDate()) {
                        out.println("\t\t\t" + targetName + ".set" + field.getName() + "(" + sourceName + "."
                                + field.getName() + "().getTime());");
                    } else if (fieldType.getInstanceClass() == byte[].class) {
                        out.println("\t\t\t" + targetName + ".set" + field.getName() + "(ByteString.copyFrom("
                                + sourceName + "." + field.getName() + "()));");
                    } else if (fieldClass.isEnum()) {
                        out.println("\t\t\t" + targetName + ".set" + field.getName() + "("
                                + fieldType.getInstanceClass().getSimpleName() + ".values()[" + sourceName + "."
                                + field.getName() + "().ordinal()]);");
                    } else {
                        out.println("\t\t\t" + targetName + ".set" + field.getName() + "(" + sourceName + "."
                                + field.getName() + "());");
                    }
                }
            }
        }
    }

    private String genInitializerCode(Class<?> type) {
        if (type.isPrimitive()) {
            return getDefaultLiteralCode(type);
        } else if (type == String.class) {
            return "\"\"";
        } else if (type.isEnum()) {
            return "null";
        } else {
            return "new " + type.getName() + "()";
        }
    }

    private String getDefaultLiteralCode(Class<?> type) {
        if (type == Boolean.class || type == boolean.class) {
            return "false";
        } else if (type == Integer.class || type == int.class) {
            return "0";
        } else if (type == Long.class || type == long.class) {
            return "0L";
        } else if (type.isEnum()) {
            return "null";
        } else {
            return "null";
        }
    }

    private void generateProtoFile(Class<?> serviceInterfaceClass, File protoFile, boolean createBaseMessages,
            SService service, String... imports) {
        PrintWriter out = null;
        try {
            out = new PrintWriter(protoFile);
        } catch (FileNotFoundException e) {
            LOGGER.error("", e);
        }
        try {
            out.println("package org.bimserver.pb;\n");
            out.println(Licenser.getCommentedLicenseText(new File("license.txt")));
            for (String importFile : imports) {
                out.println("import \"" + importFile + ".proto\";");
            }
            out.println("option java_generic_services = true;\n");
            out.println("option java_outer_classname = \"" + service.getName() + "Impl\";\n");
            out.println("option optimize_for = SPEED;\n");
            StringBuilder serviceBuilder = new StringBuilder();
            StringBuilder messageBuilder = new StringBuilder();
            serviceBuilder.append("service " + service.getName() + " {\n");
            if (createBaseMessages) {
                createVoidResponseMessage(messageBuilder);
                generateVoidMessage(messageBuilder);
            }
            for (SMethod method : service.getMethods()) {
                String inputObjectName = StringUtils.firstUpperCase(method.getName()) + "Request";
                String outputObjectName = StringUtils.firstUpperCase(method.getName()) + "Response";
                createRequestMessage(messageBuilder, method, inputObjectName);
                if (method.returnsVoid()) {
                    outputObjectName = "VoidResponse";
                } else {
                    createResponseMessage(messageBuilder, method, outputObjectName);
                }
                serviceBuilder.append("\trpc " + method.getName() + " (" + inputObjectName + ") returns ("
                        + outputObjectName + ");\n\n");
            }
            serviceBuilder.append("}\n\n");
            out.print(serviceBuilder.toString());
            out.print(messageBuilder.toString());
        } finally {
            if (out != null) {
                out.close();
            }
        }
        try {
            FileUtils.copyFile(protoFile, new File("../Builds/build/targets/shared/" + protoFile.getName()));
        } catch (IOException e) {
            LOGGER.error("", e);
        }
    }

    private void generateVoidMessage(StringBuilder builder) {
        builder.append("message Void {\n}");
    }

    private void createVoidResponseMessage(StringBuilder builder) {
        String messageName = "VoidResponse";
        StringBuilder messageBuilder = new StringBuilder();
        messageBuilder.append("message " + messageName + " {\n");
        messageBuilder.append("\toptional string errorMessage = 1;\n");
        messageBuilder.append("}\n\n");
        builder.append(messageBuilder);
    }

    private void createResponseMessage(StringBuilder builder, SMethod method, String messageName) {
        StringBuilder messageBuilder = new StringBuilder();
        messageBuilder.append("message " + messageName + " {\n");
        messageBuilder.append("\toptional string errorMessage = 1;\n");
        messageBuilder.append("\t");
        if (method.isAggregateReturnType()) {
            messageBuilder.append("repeated ");
        } else {
            messageBuilder.append("optional ");
        }
        messageBuilder.append(createMessage(builder,
                method.isAggregateReturnType() ? method.getGenericReturnType() : method.getReturnType())
                + " value = 2;\n");
        messageBuilder.append("}\n\n");
        builder.append(messageBuilder);
    }

    private String createMessage(StringBuilder sb, SClass sType) {
        if (sType == null) {
            return "VoidResponse";
        }
        Class<?> clazz = sType.getInstanceClass();
        if (generatedClasses.containsKey(clazz)) {
            return generatedClasses.get(clazz);
        }
        if (clazz == Class.class) {
            return "string";
        } else if (clazz == boolean.class || clazz == Boolean.class) {
            return "bool";
        } else if (clazz == Long.class || clazz == long.class) {
            return "int64";
        } else if (clazz == Date.class) {
            return "int64";
        } else if (clazz == DataHandler.class || clazz == byte[].class) {
            return "bytes";
        } else if (clazz == Integer.class | clazz == int.class) {
            return "int32";
        } else if (clazz == Float.class | clazz == float.class) {
            return "float";
        } else if (clazz == Double.class | clazz == double.class) {
            return "double";
        } else if (clazz.isEnum()) {
            return createEnum(sb, clazz);
        } else if (sType.isString()) {
            return "string";
        } else {
            StringBuilder messageBuilder = new StringBuilder();
            SClass sClass = (SClass) sType;
            generatedClasses.put(clazz, clazz.getSimpleName());
            messageBuilder.append("message " + clazz.getSimpleName() + " {\n");
            int counter = 1;
            if (!sClass.getSubClasses().isEmpty()) {
                messageBuilder.append("\trequired string __actual_type = " + (counter++) + ";\n");
                for (SClass subClass : sClass.getSubClasses()) {
                    messageBuilder.append("\t");
                    messageBuilder.append("optional ");
                    messageBuilder.append(createMessage(sb, subClass) + " __"
                            + subClass.getInstanceClass().getSimpleName() + " = " + (counter++) + ";\n");
                }
            }
            for (SField field : sClass.getFields()) {
                messageBuilder.append("\t");
                if (field.isAggregate()) {
                    messageBuilder.append("repeated ");
                } else {
                    messageBuilder.append("optional ");
                }
                SClass type = field.getType();
                if (field.isAggregate()) {
                    type = field.getGenericType();
                    // Type genericReturnType = method.getGenericReturnType();
                    // if (genericReturnType instanceof ParameterizedType) {
                    // ParameterizedType parameterizedTypeImpl =
                    // (ParameterizedType)genericReturnType;
                    // Type type2 =
                    // parameterizedTypeImpl.getActualTypeArguments()[0];
                    // parameterType = ((Class<?>)type2);
                    // }
                }
                messageBuilder
                        .append(createMessage(sb, type) + " " + field.getName() + " = " + (counter++) + ";\n");
            }
            messageBuilder.append("}\n\n");
            sb.append(messageBuilder);
            return clazz.getSimpleName();
        }
    }

    private String createEnum(StringBuilder sb, Class<?> clazz) {
        if (generatedClasses.containsKey(clazz)) {
            return generatedClasses.get(clazz);
        } else {
            generatedClasses.put(clazz, clazz.getSimpleName());
        }
        sb.append("enum " + clazz.getSimpleName() + "{\n");
        int counter = 0;
        for (Object o : clazz.getEnumConstants()) {
            sb.append("\t" + o.toString() + " = " + (counter++) + ";\n");
        }
        sb.append("}\n\n");
        return clazz.getSimpleName();
    }

    private void createRequestMessage(StringBuilder builder, SMethod method, String messageName) {
        StringBuilder messageBuilder = new StringBuilder();
        messageBuilder.append("message " + messageName + " {\n");
        int counter = 1;
        for (SParameter sParameter : method.getParameters()) {
            messageBuilder.append("\t");
            if (sParameter.isAggregate()) {
                messageBuilder.append("repeated ");
            } else {
                messageBuilder.append("optional ");
            }
            messageBuilder.append(createMessage(builder, sParameter.getBestType()) + " " + sParameter.getName()
                    + " = " + (counter++) + ";\n");
        }
        messageBuilder.append("}\n\n");
        builder.append(messageBuilder);
    }
}