blob: 900ce02cb4f65581ec2de9ebced655662f3fb079 [file] [log] [blame]
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gwtorm.schema.java;
import com.google.gwtorm.client.Column;
import com.google.gwtorm.schema.ColumnModel;
import com.google.gwtorm.schema.RelationModel;
import com.google.gwtorm.schema.Util;
import org.objectweb.asm.Type;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
class ProtoFileGenerator {
private static final Comparator<ColumnModel> COLUMN_COMPARATOR =
new Comparator<ColumnModel>() {
@Override
public int compare(ColumnModel o1, ColumnModel o2) {
return o1.getColumnID() - o2.getColumnID();
}
};
private static final Comparator<RelationModel> RELATION_COMPARATOR =
new Comparator<RelationModel>() {
@Override
public int compare(RelationModel o1, RelationModel o2) {
return o1.getRelationID() - o2.getRelationID();
}
};
private final Collection<RelationModel> rels;
private final String schemaName;
private final HashSet<String> seen;
private final HashSet<String> collisions;
ProtoFileGenerator(String schemaName, Collection<RelationModel> relations) {
this.schemaName = schemaName;
this.rels = relations;
this.seen = new HashSet<>();
this.collisions = new HashSet<>();
}
void print(PrintWriter out) {
seen.clear();
collisions.clear();
for (RelationModel r : rels) {
for (ColumnModel c : r.getColumns()) {
if (c.isNested()) {
String type = getShortClassName(c);
if (seen.contains(type)) {
collisions.add(type);
} else {
seen.add(type);
}
}
}
}
seen.clear();
for (RelationModel r : rels) {
generateMessage(r.getPrimaryKey().getField(), out, true);
}
for (RelationModel r : rels) {
generateMessage(r, out);
}
out.print("message Any" + schemaName + "PrimaryKey {\n");
for (RelationModel r : sortRelations(rels)) {
ColumnModel pk = r.getPrimaryKey().getField();
out.print("\toptional " + getType(pk) + " "
+ r.getRelationName().toLowerCase() + " = " + r.getRelationID()
+ ";\n");
}
out.print("}\n");
out.print("\n");
out.print("message Any" + schemaName + " {\n");
for (RelationModel r : sortRelations(rels)) {
out.print("\toptional " + getMessageName(r) + " "
+ r.getRelationName().toLowerCase() + " = " + r.getRelationID()
+ ";\n");
}
out.print("}\n");
}
private void generateMessage(RelationModel rel, PrintWriter out) {
List<ColumnModel> cols = sortColumns(rel.getFields());
for (ColumnModel c : cols) {
generateMessage(c, out, false);
}
ColumnModel pk = rel.getPrimaryKey().getField();
out.print("message " + getMessageName(rel) + " {\n");
for (ColumnModel c : cols) {
out.append("\t");
out.append(pk.equals(c) ? "required" : "optional");
out.append(" ").append(getType(c)).append(" ");
out.append(getName(c));
out.append(" = ").append(Integer.toString(c.getColumnID()));
out.append(";\n");
}
out.print("}\n\n");
}
private void generateMessage(ColumnModel parent, PrintWriter out,
boolean required) {
// Handle base cases
if (!parent.isNested()) {
return;
} else if (seen.contains(parent.getNestedClassName())) {
return;
}
List<ColumnModel> children = sortColumns(parent.getNestedColumns());
for (ColumnModel child : children) {
generateMessage(child, out, required);
}
out.print("message " + getType(parent) + " {\n");
for (ColumnModel child : children) {
out.append("\t");
out.append(required ? "required" : "optional");
out.append(" ").append(getType(child)).append(" ");
out.append(Util.makeSqlFriendly(child.getFieldName()));
out.append(" = ").append(Integer.toString(child.getColumnID()));
out.append(";\n");
}
out.print("}\n\n");
seen.add(parent.getNestedClassName());
}
private String getType(ColumnModel cm) {
if (cm.isNested()) {
String type = getShortClassName(cm);
if (collisions.contains(type)) {
return cm.getNestedClassName().replace('.', '_').replace('$', '_');
} else {
return type;
}
} else {
return toProtoType(cm.getPrimitiveType());
}
}
private static String getName(ColumnModel cm) {
if (cm.getColumnName().equals(Column.NONE)) {
return cm.getFieldName();
} else {
return cm.getColumnName();
}
}
private static String getShortClassName(ColumnModel cm) {
String tmp = cm.getNestedClassName();
return tmp.substring(tmp.lastIndexOf('.') + 1).replace('$', '_');
}
private static String getMessageName(RelationModel r) {
String typeName = r.getEntityTypeClassName();
return typeName.substring(typeName.lastIndexOf('.') + 1);
}
private static List<ColumnModel> sortColumns(Collection<ColumnModel> cols) {
ArrayList<ColumnModel> list = new ArrayList<>(cols);
Collections.sort(list, COLUMN_COMPARATOR);
return list;
}
private static List<RelationModel> sortRelations(
Collection<RelationModel> rels) {
ArrayList<RelationModel> list = new ArrayList<>(rels);
Collections.sort(list, RELATION_COMPARATOR);
return list;
}
private static String toProtoType(Class<?> clazz) {
switch (Type.getType(clazz).getSort()) {
case Type.BOOLEAN:
return "bool";
case Type.CHAR:
return "uint32";
case Type.BYTE:
case Type.SHORT:
case Type.INT:
return "int32";
case Type.FLOAT:
return "float";
case Type.DOUBLE:
return "double";
case Type.LONG:
return "int64";
case Type.ARRAY:
case Type.OBJECT: {
if (clazz == byte[].class) {
return "bytes";
} else if (clazz == String.class) {
return "string";
} else if (clazz == java.sql.Timestamp.class) {
return "fixed64";
} else {
throw new RuntimeException("Type " + clazz
+ " not supported on protobuf!");
}
}
default:
throw new RuntimeException("Type " + clazz
+ " not supported on protobuf!");
}
}
}