Package com.foundationdb.server.store.format.protobuf

Source Code of com.foundationdb.server.store.format.protobuf.AISToProtobuf

/**
* 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.server.store.format.protobuf;

import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.Group;
import com.foundationdb.ais.model.Join;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.protobuf.CommonProtobuf.ProtobufRowFormat;

import com.google.protobuf.DescriptorProtos.DescriptorProto;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Label;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Type;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
import com.google.protobuf.DescriptorProtos.FieldOptions;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import com.google.protobuf.DescriptorProtos.FileOptions;
import com.google.protobuf.DescriptorProtos.MessageOptions;

import com.foundationdb.server.store.format.protobuf.CustomOptions.GroupOptions;
import com.foundationdb.server.store.format.protobuf.CustomOptions.TableOptions;
import com.foundationdb.server.store.format.protobuf.CustomOptions.ColumnOptions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class AISToProtobuf
{
    private ProtobufRowFormat.Type formatType;
    private List<Table> tables = new ArrayList<>();
    private Map<Table,String> tableMessageNames = new HashMap<>();
    private FileDescriptorSet.Builder setBuilder;
    private FileDescriptorSet priorSet;
    private FileDescriptorProto.Builder fileBuilder;
    private FileDescriptorProto priorFile;
    private DescriptorProto.Builder messageBuilder;
    private DescriptorProto priorMessage;
    private FieldDescriptorProto.Builder fieldBuilder;
    private FieldDescriptorProto priorField;
    private Set<String> messageNames = new HashSet<>();
    private Set<String> fieldNames = new HashSet<>();
    private int nextField;

    public AISToProtobuf(ProtobufRowFormat.Type formatType) {
        this(formatType, null);
    }

    public AISToProtobuf(ProtobufRowFormat.Type formatType, FileDescriptorSet priorSet) {
        this.formatType = formatType;
        this.priorSet = priorSet;
        setBuilder = FileDescriptorSet.newBuilder();
    }

    public FileDescriptorSet build() {
        return setBuilder.build();
    }

    public void addGroup(Group group) {
        tables.clear();
        findTables(group.getRoot());
        Collections.sort(tables, new Comparator<Table>() {
                             @Override
                             public int compare(Table t1, Table t2) {
                                 return Integer.compare(t1.getOrdinal(),
                                                        t2.getOrdinal());
                             }
                         });
        fileBuilder = setBuilder.addFileBuilder();
        fileBuilder.setName(ident(group.getName().getTableName(), false) + ".proto");
        fileBuilder.setPackage(ident(group.getName().getSchemaName(), false));
        fileBuilder.addDependency("sql_custom_options.proto");
        FileOptions.Builder fileOptions = FileOptions.newBuilder();
        GroupOptions.Builder groupOptions = GroupOptions.newBuilder();
        groupOptions.setName(group.getName().getTableName());
        groupOptions.setSchema(group.getName().getSchemaName());
        // TODO: Would it be better to use the root's version and
        // arrange for that to always change on DDL?
        int sumVersions = 0;
        for (Table table : tables) {
            // Add one so that every table contributes something.
            sumVersions += table.getVersion() + 1;
        }
        groupOptions.setVersion(sumVersions);
        priorFile = null;
        if (priorSet != null) {
            String rootUuid = group.getRoot().getUuid().toString();
            for (FileDescriptorProto file : priorSet.getFileList()) {
                DescriptorProto firstMessage = file.getMessageType(0);
                MessageOptions options = firstMessage.getOptions();
                if ((options != null) &&
                    (options.hasExtension(TableOptions.fdbsql))) {
                    TableOptions tableOptions = options.getExtension(TableOptions.fdbsql);
                    if (tableOptions.getUuid().equals(rootUuid)) {
                        priorFile = file;
                        break;
                    }
                }
            }
        }
        messageNames.clear();
        for (Table table : tables) {
            tableMessageNames.put(table, uniqueIdent(ident(table.getName().getTableName(), true), messageNames));
        }
        for (Table table : tables) {
            addTable(table);
        }
        if (formatType == ProtobufRowFormat.Type.GROUP_MESSAGE) {
            addGroupMessage();
        }
        fileOptions.setExtension(GroupOptions.fdbsql, groupOptions.build());
        fileBuilder.setOptions(fileOptions);
    }

    protected void findTables(Table table) {
        tables.add(table);
        for (Join join : table.getChildJoins()) {
            findTables(join.getChild());
        }
    }

    protected void addTable(Table table) {
        messageBuilder = fileBuilder.addMessageTypeBuilder();
        messageBuilder.setName(tableMessageNames.get(table));
        MessageOptions.Builder messageOptions = MessageOptions.newBuilder();
        TableOptions.Builder tableOptions = TableOptions.newBuilder();
        tableOptions.setName(table.getName().getTableName());
        tableOptions.setSchema(table.getName().getSchemaName());
        tableOptions.setUuid(table.getUuid().toString());
        priorMessage = null;
        if (priorFile != null) {
            for (DescriptorProto message : priorFile.getMessageTypeList()) {
                MessageOptions options = message.getOptions();
                if ((options != null) &&
                    (options.hasExtension(TableOptions.fdbsql))) {
                    TableOptions toptions = options.getExtension(TableOptions.fdbsql);
                    if (toptions.getUuid().equals(tableOptions.getUuid())) {
                        priorMessage = message;
                        break;
                    }
                }
            }
        }
        nextField = 1;
        if (priorMessage != null) {
            TableOptions options = priorMessage.getOptions().getExtension(TableOptions.fdbsql);
            if (options.hasNextField()) {
                nextField = options.getNextField();
            }
            else {
                nextField = priorMessage.getField(priorMessage.getFieldCount() - 1)
                    .getNumber() + 1;
            }
        }
        fieldNames.clear();
        for (Column column : table.getColumnsIncludingInternal()) {
            addColumn(column);
        }
        for (Table child : tables) { // Continue to follow ordinal order.
            if (child.getParentTable() == table) {
                addChildTable(child);
            }
        }
        if (nextField != messageBuilder.getFieldOrBuilder(messageBuilder.getFieldCount() - 1).getNumber() + 1) {
            tableOptions.setNextField(nextField);
        }
        messageOptions.setExtension(TableOptions.fdbsql, tableOptions.build());
        messageBuilder.setOptions(messageOptions);
    }

    protected void addColumn(Column column) {
        String fieldName = uniqueIdent(ident(column.getName(), false), fieldNames);
        fieldBuilder = messageBuilder.addFieldBuilder();
        fieldBuilder.setName(fieldName);
        fieldBuilder.setLabel(Label.LABEL_OPTIONAL);
        FieldOptions.Builder fieldBuilderOptions = FieldOptions.newBuilder();
        ColumnOptions.Builder columnOptions = ColumnOptions.newBuilder();
        if (!fieldName.equals(column.getName())) {
            columnOptions.setName(column.getName());
        }
        columnOptions.setSqlType(column.getTypeDescription().toUpperCase());
        columnOptions.setUuid(column.getUuid().toString());
        priorField = null;
        if (priorMessage != null) {
            for (FieldDescriptorProto field : priorMessage.getFieldList()) {
                FieldOptions options = field.getOptions();
                if ((options != null) &&
                    (options.hasExtension(ColumnOptions.fdbsql))) {
                    ColumnOptions coptions = options.getExtension(ColumnOptions.fdbsql);
                    if (coptions.getUuid().equals(columnOptions.getUuid())) {
                        priorField = field;
                        break;
                    }
                }
            }
        }
        setColumnType(column, columnOptions);
        setFieldNumber();
        fieldBuilderOptions.setExtension(ColumnOptions.fdbsql, columnOptions.build());
        fieldBuilder.setOptions(fieldBuilderOptions);
        if (column.getNullable() &&
            ((column.getDefaultValue() != null) ||
             (column.getDefaultFunction() != null))) {
            addNullForField(column.getName(), fieldBuilder.getNumber());
        }
    }

    protected void setColumnType(Column column, ColumnOptions.Builder columnOptions) {
        ProtobufRowConversion conversion = ProtobufRowConversion.forTInstance(column.getType());
        assert (conversion != null) : column;
        Type type = conversion.getType();
        int decimalScale = conversion.getDecimalScale();
        fieldBuilder.setType(type);
        if (decimalScale >= 0) {
            columnOptions.setDecimalScale(decimalScale);
        }
        if ((priorField != null) &&
            ((priorField.getType() != type) ||
             (priorDecimalScale() != decimalScale))) {
            priorField = null;
        }
    }

    protected int priorDecimalScale() {
        if (priorField != null) {
            ColumnOptions columnOptions = priorField.getOptions().getExtension(ColumnOptions.fdbsql);
            if (columnOptions.hasDecimalScale()) {
                return columnOptions.getDecimalScale();
            }
        }
        return -1;
    }

    protected void setFieldNumber() {
        if (priorField != null) {
            fieldBuilder.setNumber(priorField.getNumber());
            if (fieldBuilder.getNumber() >= nextField) {
                nextField = fieldBuilder.getNumber() + 1;
            }
        }
        else {
            fieldBuilder.setNumber(nextField++);
        }
    }

    protected void addNullForField(String columnName, int forField) {
        String fieldName = uniqueIdent("_" + ident(columnName, false) + "_is_null", fieldNames);
        fieldBuilder = messageBuilder.addFieldBuilder();
        fieldBuilder.setName(fieldName);
        fieldBuilder.setType(Type.TYPE_BOOL);
        fieldBuilder.setLabel(Label.LABEL_OPTIONAL);
        FieldOptions.Builder fieldBuilderOptions = FieldOptions.newBuilder();
        ColumnOptions.Builder columnOptions = ColumnOptions.newBuilder();
        columnOptions.setNullForField(forField);
        priorField = null;
        if (priorMessage != null) {
            for (FieldDescriptorProto field : priorMessage.getFieldList()) {
                FieldOptions options = field.getOptions();
                if ((options != null) &&
                    (options.hasExtension(ColumnOptions.fdbsql))) {
                    ColumnOptions coptions = options.getExtension(ColumnOptions.fdbsql);
                    if (coptions.hasNullForField() &&
                        (coptions.getNullForField() == forField)) {
                        priorField = field;
                        break;
                    }
                }
            }
        }
        setFieldNumber();
        fieldBuilderOptions.setExtension(ColumnOptions.fdbsql, columnOptions.build());
        fieldBuilder.setOptions(fieldBuilderOptions);
    }

    protected void addChildTable(Table table) {
        String fieldName = uniqueIdent(ident(table.getName().getTableName(), false), fieldNames);
        fieldBuilder = messageBuilder.addFieldBuilder();
        fieldBuilder.setName(fieldName);
        fieldBuilder.setLabel(Label.LABEL_REPEATED);
        fieldBuilder.setType(Type.TYPE_MESSAGE);
        fieldBuilder.setTypeName(tableMessageNames.get(table));
        FieldOptions.Builder fieldBuilderOptions = FieldOptions.newBuilder();
        ColumnOptions.Builder columnOptions = ColumnOptions.newBuilder();
        columnOptions.setUuid(table.getUuid().toString());
        priorField = null;
        if (priorMessage != null) {
            for (FieldDescriptorProto field : priorMessage.getFieldList()) {
                FieldOptions options = field.getOptions();
                if ((options != null) &&
                    (options.hasExtension(ColumnOptions.fdbsql))) {
                    ColumnOptions coptions = options.getExtension(ColumnOptions.fdbsql);
                    if (coptions.getUuid().equals(columnOptions.getUuid())) {
                        priorField = field;
                        break;
                    }
                }
            }
        }
        setFieldNumber();
        fieldBuilderOptions.setExtension(ColumnOptions.fdbsql, columnOptions.build());
        fieldBuilder.setOptions(fieldBuilderOptions);
    }

    protected void addGroupMessage() {
        messageBuilder = fileBuilder.addMessageTypeBuilder();
        messageBuilder.setName(uniqueIdent("_Group", messageNames));
        MessageOptions.Builder messageOptions = MessageOptions.newBuilder();
        TableOptions.Builder tableOptions = TableOptions.newBuilder();
        tableOptions.setIsGroup(true);
        priorMessage = null;
        if (priorFile != null) {
            for (DescriptorProto message : priorFile.getMessageTypeList()) {
                MessageOptions options = message.getOptions();
                if ((options != null) &&
                    (options.hasExtension(TableOptions.fdbsql))) {
                    TableOptions toptions = options.getExtension(TableOptions.fdbsql);
                    if (toptions.getIsGroup()) {
                        priorMessage = message;
                        break;
                    }
                }
            }
        }
        nextField = 1;
        if (priorMessage != null) {
            TableOptions options = priorMessage.getOptions().getExtension(TableOptions.fdbsql);
            if (options.hasNextField()) {
                nextField = options.getNextField();
            }
            else {
                nextField = priorMessage.getField(priorMessage.getFieldCount() - 1)
                    .getNumber() + 1;
            }
        }
        fieldNames.clear();
        for (Table table : tables) {
            String fieldName = uniqueIdent(ident(table.getName().getTableName(), false), fieldNames);
            fieldBuilder = messageBuilder.addFieldBuilder();
            fieldBuilder.setName(fieldName);
            fieldBuilder.setLabel(Label.LABEL_OPTIONAL);
            fieldBuilder.setType(Type.TYPE_MESSAGE);
            fieldBuilder.setTypeName(tableMessageNames.get(table));
            FieldOptions.Builder fieldBuilderOptions = FieldOptions.newBuilder();
            ColumnOptions.Builder columnOptions = ColumnOptions.newBuilder();
            columnOptions.setUuid(table.getUuid().toString());
            priorField = null;
            if (priorMessage != null) {
                for (FieldDescriptorProto field : priorMessage.getFieldList()) {
                    FieldOptions options = field.getOptions();
                    if ((options != null) &&
                        (options.hasExtension(ColumnOptions.fdbsql))) {
                        ColumnOptions coptions = options.getExtension(ColumnOptions.fdbsql);
                        if (coptions.getUuid().equals(columnOptions.getUuid())) {
                            priorField = field;
                            break;
                        }
                    }
                }
            }
            setFieldNumber();
            fieldBuilderOptions.setExtension(ColumnOptions.fdbsql, columnOptions.build());
            fieldBuilder.setOptions(fieldBuilderOptions);
        }
        if (nextField != messageBuilder.getFieldOrBuilder(messageBuilder.getFieldCount() - 1).getNumber() + 1) {
            tableOptions.setNextField(nextField);
        }
        messageOptions.setExtension(TableOptions.fdbsql, tableOptions.build());
        messageBuilder.setOptions(messageOptions);
    }

    protected String ident(String base, boolean camelize) {
        String ident = base.toLowerCase();
        if (camelize) {
            StringBuilder str = new StringBuilder();
            boolean upper = true;
            for (int i = 0; i < ident.length(); i++) {
                char ch = ident.charAt(i);
                if (ch == '_') {
                    upper = true;
                }
                else if (upper) {
                    str.append(Character.toUpperCase(ch));
                    upper = false;
                }
                else {
                    str.append(ch);
                }
            }
            ident = str.toString();
        }
        return ident;
    }

    protected String uniqueIdent(String base, Set<String> existing) {
        for (int i = 0; ; i++) {
            String ident = (i == 0) ? base : base + "_" + i;
            if (existing.add(ident)) {
                return ident;
            }
        }
    }
}
TOP

Related Classes of com.foundationdb.server.store.format.protobuf.AISToProtobuf

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.