Package com.foundationdb.protobuf

Source Code of com.foundationdb.protobuf.ProtobufDecompiler

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* 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/>.
*/

package com.foundationdb.protobuf;

import com.google.protobuf.DescriptorProtos.DescriptorProto;
import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Label;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Type;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.Descriptors.FieldDescriptor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class ProtobufDecompiler
{
    protected static final Map<Label,String> LABELS = new HashMap<>();
    protected static final Map<Type,String> TYPES = new HashMap<>();
    static {
        LABELS.put(Label.LABEL_OPTIONAL, "optional");
        LABELS.put(Label.LABEL_REPEATED, "repeated");
        LABELS.put(Label.LABEL_REQUIRED, "required");

        // Simple types only.
        TYPES.put(Type.TYPE_BOOL, "bool");
        TYPES.put(Type.TYPE_BYTES, "bytes");
        TYPES.put(Type.TYPE_DOUBLE, "double");
        TYPES.put(Type.TYPE_FIXED32, "fixed32");
        TYPES.put(Type.TYPE_FIXED64, "fixed64");
        TYPES.put(Type.TYPE_FLOAT, "float");
        TYPES.put(Type.TYPE_INT32, "int32");
        TYPES.put(Type.TYPE_INT64, "int64");
        TYPES.put(Type.TYPE_SFIXED32, "sfixed32");
        TYPES.put(Type.TYPE_SFIXED64, "sfixed64");
        TYPES.put(Type.TYPE_SINT32, "sint32");
        TYPES.put(Type.TYPE_SINT64, "sint64");
        TYPES.put(Type.TYPE_STRING, "string");
        TYPES.put(Type.TYPE_UINT32, "uint32");
        TYPES.put(Type.TYPE_UINT64, "uint64");
    }
    protected final Appendable output;
    protected String newline = System.lineSeparator();
    protected int indent = 0;
    protected int tabWidth = 4;
    protected boolean indentWithTabs = false;
    protected String absolutePackage = null;

    public ProtobufDecompiler(Appendable output) {
        this.output = output;
    }

    public ProtobufDecompiler(OutputStream output) {
        this.output = new BufferedWriter(new OutputStreamWriter(output));
    }

    public static void main(String[] args) throws IOException {
        ProtobufDecompiler decompiler = new ProtobufDecompiler((Appendable)System.out);
        FileDescriptorSet set;
        try (FileInputStream istr = new FileInputStream(args[0])) {
            set = FileDescriptorSet.parseFrom(istr);
        }
        decompiler.decompile(set);
    }

    public void decompile(FileDescriptorSet setDescriptor) throws IOException {
        for (FileDescriptorProto fileDescriptor : setDescriptor.getFileList()) {
            newline();
            format("===== %s =====", fileDescriptor.getName());
            newline();
            decompile(fileDescriptor);
        }
    }

    public void decompile(FileDescriptorProto fileDescriptor) throws IOException {
        if (fileDescriptor.hasPackage()) {
            indentedFormat("package %s;", fileDescriptor.getPackage());
            absolutePackage = "." + fileDescriptor.getPackage() + ".";
        }
        for (String dependency : fileDescriptor.getDependencyList()) {
            indentedFormat("import \"%s\";", dependency);
        }
        if (fileDescriptor.hasOptions()) {
            decompileOptions(fileDescriptor.getOptions());
        }
        decompileMembers(fileDescriptor.getEnumTypeList(),
                         fileDescriptor.getMessageTypeList(),
                         Collections.<FieldDescriptorProto>emptyList(),
                         Collections.<DescriptorProto.ExtensionRange>emptyList(),
                         fileDescriptor.getExtensionList());
        for (ServiceDescriptorProto serviceDescriptor : fileDescriptor.getServiceList()) {
            decompile(serviceDescriptor);
        }
        newline();
        flush();
    }

    protected void decompile(EnumDescriptorProto enumDescriptor) throws IOException {
        indentedFormat("enum %s {", enumDescriptor.getName());
        indent++;
        if (enumDescriptor.hasOptions()) {
            decompileOptions(enumDescriptor.getOptions());
        }
        for (EnumValueDescriptorProto value : enumDescriptor.getValueList()) {
            indentedFormat("%s = %d%s;", value.getName(), value.getNumber(),
                           value.hasOptions() ? bracketedOptions(value.getOptions()) : "");
        }
        indent--;
        indentedFormat("}");
    }

    protected void decompile(DescriptorProto messageDescriptor) throws IOException {
        indentedFormat("message %s {", messageDescriptor.getName());
        decompileMessageBody(messageDescriptor);
    }

    protected void decompile(ServiceDescriptorProto serviceDescriptor) throws IOException {
        indentedFormat("service %s {", serviceDescriptor.getName());
        indent++;
        if (serviceDescriptor.hasOptions()) {
            decompileOptions(serviceDescriptor.getOptions());
        }
        for (MethodDescriptorProto methodDescriptor : serviceDescriptor.getMethodList()) {
            indentedFormat("rpc %s (%s) returns (%s)",
                           methodDescriptor.getName(), methodDescriptor.getInputType(), methodDescriptor.getOutputType());
            if (methodDescriptor.hasOptions()) {
                write("{ ");
                indent++;
                decompileOptions(methodDescriptor.getOptions());
                indent--;
                indentedFormat("}");
            }
            else {
                write(";");
            }
        }
        indent--;
        indentedFormat("}");
    }

    protected void decompileMessageBody(DescriptorProto messageDescriptor) throws IOException {
        indent++;
        if (messageDescriptor.hasOptions()) {
            decompileOptions(messageDescriptor.getOptions());
        }
        decompileMembers(messageDescriptor.getEnumTypeList(),
                         messageDescriptor.getNestedTypeList(),
                         messageDescriptor.getFieldList(),
                         messageDescriptor.getExtensionRangeList(),
                         messageDescriptor.getExtensionList());
        indent--;
        indentedFormat("}");
    }

    protected void decompileMembers(List<EnumDescriptorProto> enumDescriptors,
                                    List<DescriptorProto> messageDescriptors,
                                    List<FieldDescriptorProto> fieldDescriptors,
                                    List<DescriptorProto.ExtensionRange> extensionRanges,
                                    List<FieldDescriptorProto> extensions)
            throws IOException {
        for (EnumDescriptorProto enumDescriptor : enumDescriptors) {
            decompile(enumDescriptor);
        }
        Map<String,DescriptorProto> groups = new HashMap<>();
        findGroups(fieldDescriptors, groups);
        findGroups(extensions, groups);
        for (DescriptorProto nestedDescriptor : messageDescriptors) {
            if (groups.containsKey(nestedDescriptor.getName())) {
                groups.put(nestedDescriptor.getName(), nestedDescriptor);
            }
            else {
                decompile(nestedDescriptor);
            }
        }
        decompileFields(fieldDescriptors, groups);
        for (DescriptorProto.ExtensionRange extensionRange : extensionRanges) {
            indentedFormat("extensions %s to %s;", extensionRange.getStart(),
                           (extensionRange.getEnd() == 0x20000000) ? "max" : extensionRange.getEnd());
        }
        if (!extensions.isEmpty()) {
            Map<String,List<FieldDescriptorProto>> extensionsByExtendee = new TreeMap<>();
            for (FieldDescriptorProto extension : extensions) {
                List<FieldDescriptorProto> entry = extensionsByExtendee.get(extension.getExtendee());
                if (entry == null) {
                    entry = new ArrayList<>();
                    extensionsByExtendee.put(extension.getExtendee(), entry);
                }
                entry.add(extension);
            }
            for (Map.Entry<String,List<FieldDescriptorProto>> entry : extensionsByExtendee.entrySet()) {
                indentedFormat("extend %s {", entry.getKey());
                indent++;
                decompileFields(entry.getValue(), groups);
                indent--;
                indentedFormat("}");
            }
        }
    }

    protected void findGroups(List<FieldDescriptorProto> fieldDescriptors,
                              Map<String,DescriptorProto> groups) {
        for (FieldDescriptorProto fieldDescriptor : fieldDescriptors) {
            if (fieldDescriptor.getType() == Type.TYPE_GROUP) {
                groups.put(fieldDescriptor.getTypeName(), null);
            }
        }
    }

    protected void decompileFields(List<FieldDescriptorProto> fieldDescriptors,
                                   Map<String,DescriptorProto> groups)
            throws IOException {
        for (FieldDescriptorProto fieldDescriptor : fieldDescriptors) {
            String label = LABELS.get(fieldDescriptor.getLabel());
            String type = TYPES.get(fieldDescriptor.getType());
            String name = fieldDescriptor.getName();
            if (fieldDescriptor.hasTypeName()) {
                type = fieldDescriptor.getTypeName();
                if ((absolutePackage != null) && type.startsWith(absolutePackage)) {
                    type = type.substring(absolutePackage.length());
                }
            }
            DescriptorProto groupDescriptor = null;
            if (fieldDescriptor.getType() == Type.TYPE_GROUP) {
                groupDescriptor = groups.get(type);
                if (groupDescriptor != null) {
                    name = type;
                    type = "group";
                }
            }
            indentedFormat("%s %s %s = %d",
                           label, type, name, fieldDescriptor.getNumber());
            if (fieldDescriptor.hasOptions() || fieldDescriptor.hasDefaultValue()) {
                write(defaultAndOptions(fieldDescriptor.hasOptions() ? fieldDescriptor.getOptions() : null,
                                        fieldDescriptor.hasDefaultValue() ? fieldDescriptor.getDefaultValue() : null));
            }
            if (groupDescriptor == null) {
                write(";");
            }
            else {
                decompileMessageBody(groupDescriptor);
            }
        }
    }

    protected void decompileOptions(MessageOrBuilder options) throws IOException {
        for (Map.Entry<FieldDescriptor,Object> entry : options.getAllFields().entrySet()) {
            FieldDescriptor field = entry.getKey();
            Object value = entry.getValue();
            String fieldName = field.getName();
            if (field.isExtension()) {
                fieldName = "(" + fieldName + ")";
            }
            if (field.getType() == FieldDescriptor.Type.MESSAGE) {
                for (Map.Entry<FieldDescriptor,Object> subentry : ((MessageOrBuilder)value).getAllFields().entrySet()) {
                    FieldDescriptor subfield = subentry.getKey();
                    Object subvalue = subentry.getValue();
                    indentedFormat("option %s.%s = %s;", fieldName, subfield.getName(), literal(subvalue, subfield.getType()));
                }
            }
            else {
                indentedFormat("option %s = %s;", fieldName, literal(value, field.getType()));
            }
        }
    }

    protected String bracketedOptions(MessageOrBuilder options) {
        return defaultAndOptions(options, null);
    }

    protected String defaultAndOptions(MessageOrBuilder options, String defaultValue) {
        StringBuilder str = new StringBuilder();
        boolean first = true;
        if (defaultValue != null) {
            str.append(" [default = ");
            str.append(defaultValue); // TODO: quote
            first = false;
        }
        if (options != null) {
            for (Map.Entry<FieldDescriptor,Object> entry : options.getAllFields().entrySet()) {
                FieldDescriptor field = entry.getKey();
                Object value = entry.getValue();
                String fieldName = field.getName();
                if (field.isExtension()) {
                    fieldName = "(" + fieldName + ")";
                }
                if (field.getType() == FieldDescriptor.Type.MESSAGE) {
                    for (Map.Entry<FieldDescriptor,Object> subentry : ((MessageOrBuilder)value).getAllFields().entrySet()) {
                        FieldDescriptor subfield = subentry.getKey();
                        Object subvalue = subentry.getValue();
                        if (first) {
                            str.append(" [");
                            first = false;
                        }
                        else {
                            str.append(", ");
                        }
                        str.append(fieldName).append(".").append(subfield.getName()).append(" = ").append(literal(subvalue, subfield.getType()));
                    }
                }
                else {
                    if (first) {
                        str.append(" [");
                        first = false;
                    }
                    else {
                        str.append(", ");
                    }
                    str.append(fieldName).append(" = ").append(literal(value, field.getType()));
                }
            }
        }
        if (!first) {
            str.append("]");
        }
        return str.toString();
    }

    protected String literal(Object value, FieldDescriptor.Type type) {
        switch (type) {
        case STRING:
            return quotedString((String)value);
        default:
            return value.toString();
        }
    }

    protected String quotedString(String str) {
        return "\"" + str.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }

    public String getNewline() {
        return newline;
    }
    public void setNewline(String newline) {
        this.newline = newline;
    }

    public int getTabWidth() {
        return tabWidth;
    }
    public void setTabWidth(int tabWidth) {
        this.tabWidth = tabWidth;
    }
   
    public boolean isIndentWithTabs() {
        return indentWithTabs;
    }
    public void setIndentWithTabs(boolean indentWithTabs) {
        this.indentWithTabs = indentWithTabs;
    }

    protected void indentedFormat(String fmt, Object... args) throws IOException {
        indentedLine();
        format(fmt, args);
    }

    protected void format(String fmt, Object... args) throws IOException {
        write(String.format(fmt, args));
    }

    protected void write(String str) throws IOException {
        output.append(str);
    }

    protected void newline() throws IOException {
        write(newline);
    }

    protected void indentedLine() throws IOException {
        newline();
        for (int i = 0; i < indent; i++) {
            if (indentWithTabs) {
                write("\t");
            }
            else {
                for (int j = 0; j < tabWidth; j++) {
                    write(" ");
                }
            }
        }
    }

    protected void flush() throws IOException {
        if (output instanceof Flushable) {
            ((Flushable)output).flush();
        }
    }
}
TOP

Related Classes of com.foundationdb.protobuf.ProtobufDecompiler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.