/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.infinispan.hotrod;
import static org.teiid.language.SQLConstants.Tokens.*;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import org.teiid.infinispan.api.InfinispanConnection;
import org.teiid.infinispan.api.ProtobufDataManager;
import org.teiid.infinispan.api.ProtobufResource;
import org.teiid.metadata.BaseColumn.NullType;
import org.teiid.metadata.Column;
import org.teiid.metadata.ForeignKey;
import org.teiid.metadata.KeyRecord;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.Schema;
import org.teiid.metadata.Table;
import org.teiid.translator.TranslatorException;
public class SchemaToProtobufProcessor {
private static final String NL = "\n";//$NON-NLS-1$
private static final String OPEN_CURLY = "{";//$NON-NLS-1$
private static final String CLOSE_CURLY = "}";//$NON-NLS-1$
private static final String TAB = " "; //$NON-NLS-1$
private StringBuilder buffer = new StringBuilder();
private TreeMap<String, List<Column>> processLater = new TreeMap<>();
private Schema schema;
private boolean indexMessages = false;
public ProtobufResource process(MetadataFactory metadataFactory, InfinispanConnection connection)
throws TranslatorException {
this.schema = metadataFactory.getSchema();
buffer.append("package ").append(schema.getName()).append(";");
buffer.append(NL);
buffer.append(NL);
for (Table table : schema.getTables().values()) {
visit(table);
buffer.append(NL);
buffer.append(NL);
}
for (String name : this.processLater.keySet()) {
visitTable(name, this.processLater.get(name));
}
return new ProtobufResource(schema.getName() + ".proto", buffer.toString());
}
private void addTab() {
buffer.append(TAB);
}
private void visit(Table table) {
if (table.getAnnotation() != null) {
buffer.append("/* ").append(table.getAnnotation()).append(" */").append(NL);
} else if (isIndexMessages()) {
buffer.append("/* @Indexed */").append(NL);
}
buffer.append("message ").append(table.getName()).append(SPACE).append(OPEN_CURLY).append(NL);
for (Column column : table.getColumns()) {
visit(column);
}
processFKTable(table);
buffer.append(CLOSE_CURLY);
}
private void visitTable(String name, List<Column> columns) {
if (isIndexMessages()) {
buffer.append("/* @Indexed */").append(NL);
}
buffer.append("message ").append(name).append(SPACE).append(OPEN_CURLY).append(NL);
for (Column column : columns) {
visitColumn(column);
}
buffer.append(CLOSE_CURLY);
}
private void visit(Column column) {
String messageName = ProtobufMetadataProcessor.getMessageName(column);
if (messageName != null) {
if (this.processLater.get(messageName) == null) {
addTab();
int tag = ProtobufMetadataProcessor.getParentTag(column);
String name = ProtobufMetadataProcessor.getParentColumnName(column);
buffer.append("optional ");
buffer.append(messageName.substring(messageName.lastIndexOf('.') + 1)).append(SPACE).append(name);
buffer.append(" = ").append(tag).append(SEMICOLON).append(NL);
}
processLater(column);
return;
}
visitColumn(column);
}
private void visitColumn(Column column) {
addTab();
if (column.getAnnotation() != null) {
buffer.append("/* ").append(column.getAnnotation().replace('\n', ' ')).append(" */").append(NL);
addTab();
} else {
if (isPartOfPrimaryKey(column) || isPartOfUniqueKey(column)) {
buffer.append("/* @Id */").append(NL);
addTab();
}
}
boolean array = column.getJavaType().isArray();
if (column.getNullType().equals(NullType.No_Nulls)) {
buffer.append("required ");
} else if (array) {
buffer.append("repeated ");
} else {
buffer.append("optional ");
}
if (column.getNativeType() != null) {
buffer.append(column.getNativeType()).append(SPACE);
} else {
Class<?> clazz = column.getJavaType();
if (array) {
clazz = clazz.getComponentType();
}
String type = ProtobufDataManager.getCompatibleProtobufType(clazz).toString().toLowerCase();
buffer.append(type).append(SPACE);
}
if (column.getNameInSource() != null) {
buffer.append(column.getNameInSource()).append(SPACE);
} else {
buffer.append(column.getName()).append(SPACE);
}
int tag = ProtobufMetadataProcessor.getTag(column);
if (tag == -1) {
tag = column.getPosition();
}
buffer.append("= ").append(tag).append(SEMICOLON).append(NL);
}
private void processLater(Column column) {
String messageName = ProtobufMetadataProcessor.getMessageName(column);
List<Column> columns = this.processLater.get(messageName);
if (columns == null) {
columns = new ArrayList<>();
this.processLater.put(messageName, columns);
}
columns.add(column);
}
public boolean isPartOfPrimaryKey(Column column) {
KeyRecord pk = ((Table) column.getParent()).getPrimaryKey();
if (pk != null) {
for (Column c : pk.getColumns()) {
if (c.getName().equals(column.getName())) {
return true;
}
}
}
return false;
}
public boolean isPartOfUniqueKey(Column column) {
List<KeyRecord> list = ((Table) column.getParent()).getUniqueKeys();
for (KeyRecord uk : list) {
for (Column c : uk.getColumns()) {
if (c.getName().equals(column.getName())) {
return true;
}
}
}
return false;
}
// Find FK to this table, and add the column
private void processFKTable(Table table) {
int increment = 1;
for (Table t : schema.getTables().values()) {
if (table == t) {
continue;
}
if (!t.getForeignKeys().isEmpty()) {
List<ForeignKey> fks = t.getForeignKeys();
for (ForeignKey fk : fks) {
if (fk.getReferenceTableName().equals(table.getName())) {
addTab();
String messageName = ProtobufMetadataProcessor.getMessageName(t);
if (messageName == null) {
messageName = table.getName();
} else {
messageName = messageName.substring(messageName.lastIndexOf('.') + 1);
}
String columnName = ProtobufMetadataProcessor.getParentColumnName(t);
if (columnName == null) {
columnName = t.getName().toLowerCase();
}
int tag = ProtobufMetadataProcessor.getParentTag(t);
if (tag == -1) {
tag = table.getColumns().size()+increment;
increment++;
}
buffer.append("repeated ");
buffer.append(messageName).append(SPACE).append(columnName);
buffer.append(" = ").append(tag).append(SEMICOLON).append(NL);
}
}
}
}
}
boolean isIndexMessages() {
return indexMessages;
}
void setIndexMessages(boolean indexMessages) {
this.indexMessages = indexMessages;
}
}