import java.io.*;
import java.math.BigInteger;
import java.util.*;
import tl.TL;
//import org.json.simple.*;
public class Main {
public static class TLField {
public String name;
public String type;
public String javaType;
public String namespace;
public String note;
public boolean consistent;
public TLField(String name, String type, String namespace, String note) {
this.name = name;
this.type = type;
this.javaType = getJavaType(type, namespace, note);
this.namespace = namespace;
this.note = note;
this.consistent = true;
}
}
public static class TLType {
public String name;
public String className;
public String fullName;
public String namespace;
public HashMap<String, TLField> fields;
public TLType(String name, String namespace) {
this.name = name;
this.className = "T" + prettifyClassName(name);
this.fullName = "tl." + (namespace.length() > 0 ? namespace + "." : "") + className;
this.namespace = namespace;
this.fields = new HashMap<String, TLField>();
}
}
public static class TLConstructor {
public String id;
public String name;
public String className;
public String fullName;
public String namespace;
public ArrayList<TLField> fields;
public TLType type;
public boolean isFunction;
public TLConstructor(String id, String name, String namespace, TLType type, boolean isFunction) {
this.id = id;
this.name = name;
this.className = prettifyClassName(name);
this.fullName = "tl." + (namespace.length() > 0 ? namespace + "." : "") + className;
this.namespace = namespace;
this.fields = new ArrayList<TLField>();
this.type = type;
this.isFunction = isFunction;
}
}
public static void main(String[] args) throws Exception {
HashMap<String, TLType> types = new HashMap<String, TLType>();
HashMap<String, TLConstructor> constrs = new HashMap<String, TLConstructor>();
BufferedReader typeTemplateFile = new BufferedReader(new FileReader("TypeTemplate.java"));
String typeTemplate = "";
while (typeTemplateFile.ready()) {
typeTemplate += typeTemplateFile.readLine() + "\n";
}
typeTemplateFile.close();
BufferedReader constructorTemplateFile = new BufferedReader(new FileReader("ConstructorTemplate.java"));
String constructorTemplate = "";
while (constructorTemplateFile.ready()) {
constructorTemplate += constructorTemplateFile.readLine() + "\n";
}
constructorTemplateFile.close();
(new File("src/tl/")).mkdirs();
//FileWriter switchFile = new FileWriter("out/<switch>.java");
PrintStream switchFile = System.out;
BufferedReader cin = new BufferedReader(new FileReader("types.txt"));
boolean functions = false;
while (cin.ready()) {
String line = cin.readLine();
if (line.trim().length() == 0) continue;
if (line.equals("---functions---")) {
functions = true;
continue;
}
if (line.endsWith(";")) line = line.substring(0, line.length() - 1).trim();
String[] parts = line.split("=");
// TYPE
String type_name = parts[1].trim();
String type_namespace = "";
if (type_name.indexOf(".") > 0) {
String[] tmp = type_name.split("\\.");
type_namespace = tmp[0];
type_name = tmp[1];
(new File("src/tl/" + type_namespace)).mkdirs();
}
TLType type = new TLType(type_name, type_namespace);
if (!functions) {
if (types.containsKey(type.fullName)) {
type = types.get(type.fullName);
} else {
types.put(type.fullName, type);
}
}
// CONSTRUCTOR
StringTokenizer tok = new StringTokenizer(parts[0]);
parts = tok.nextToken().split("#");
String constr_name = parts[0];
String hexCode = parts[1];
String namespace = "";
if (constr_name.indexOf(".") > 0) {
parts = constr_name.split("\\.");
namespace = parts[0];
constr_name = parts[1];
(new File("src/tl/" + namespace)).mkdirs();
}
TLConstructor constr = new TLConstructor(hexCode, constr_name, namespace, type, functions);
constrs.put(constr.fullName, constr);
if (!hexCode.equals("0")) {
switchFile.print("case 0x" + hexCode + ": return new " + constr.fullName + "(buffer);\n");
}
// ARGUMENTS
while (tok.hasMoreTokens()) {
parts = tok.nextToken().split(":");
String arg_name, arg_type, arg_note;
if (parts.length == 1) {
arg_name = "arg" + (constr.fields.size() + 1);
arg_type = parts[0];
} else {
arg_name = parts[0];
arg_type = parts[1];
}
if (arg_type.toLowerCase().equals("vector")) { // plus one arg
arg_type += " " + tok.nextToken();
}
if (arg_type.endsWith(")")) {
arg_note = arg_type.substring(arg_type.indexOf("(") + 1, arg_type.length() - 1);
arg_type = arg_type.substring(0, arg_type.indexOf("("));
} else {
arg_note = "";
}
String arg_namespace = "";
if (!(arg_type.toLowerCase().startsWith("vector ") || arg_type.toLowerCase().startsWith("vector<")) && arg_type.indexOf(".") > -1) {
parts = arg_type.split("\\.");
arg_namespace = parts[0];
arg_type = parts[1];
}
TLField field = new TLField(arg_name, arg_type, arg_namespace, arg_note);
if (type.fullName.equals("tl.TLObject") || type.fullName.equals("tl.TLFunction")) {
field.consistent = false;
}
if (type.fields.containsKey(arg_name)) {
TLField afield = type.fields.get(arg_name);
if (!afield.javaType.equals(field.javaType)) {
afield.consistent = false;
}
constr.fields.add(afield);
} else {
type.fields.put(arg_name, field);
constr.fields.add(field);
}
}
}
cin.close();
for (TLType type : types.values()) {
if (type.className.equals("TLObject")) continue;
FileWriter typeFile = new FileWriter("src/tl/" + (type.namespace.length() == 0 ? "" : (type.namespace + "/")) + type.className + ".java");
String fields = "";
for (TLField field : type.fields.values()) {
if (field.consistent) { // Only fields that have same type (or missing) in all constructors are declared in the parent (type) class
if (fields.length() > 0) {
fields += "\n";
}
fields += " public " + field.javaType + " " + field.name + ";";
}
}
String typeDeclaration = typeTemplate
.replaceAll("\\/\\*\\*namespace\\*\\*\\/", type.namespace.length() == 0 ? "" : ("." + type.namespace))
.replaceAll("\\/\\*\\*fields\\*\\*\\/", fields)
.replaceAll("\\/\\*\\*type\\*\\*\\/", type.className);
typeFile.write(typeDeclaration);
typeFile.close();
}
for (TLConstructor constr : constrs.values()) {
String fields = "";
String params = "";
String length = "";
String string = "";
String parse_body = "";
String init_body = "";
String write_body = "";
write_body += " if (boxed) {\n" +
" buffer.putInt(0x" + constr.id + ");\n" +
" }\n";
int extraSize = 0;
boolean requireBigInt = false;
for (TLField field : constr.fields) {
if (fields.length() > 0 && (constr.isFunction || !field.consistent)) {
fields += "\n";
}
if (params.length() > 0) {
params += ", ";
parse_body += "\n";
init_body += "\n";
write_body += "\n";
}
if (field.note.equals("num") || field.note.equals("unum")) requireBigInt = true;
if (field.type.equals("int128") || field.type.equals("int256")) requireBigInt = true;
if (constr.isFunction || !field.consistent) {
fields += " public " + field.javaType + " " + field.name + ";";
}
params += field.javaType + " " + field.name;
extraSize += getExtraSize(field.type);
String sz = getSize(field.type, field.name, field.note);
if (sz != null) {
if (length.length() > 0) length += " + ";
length += sz;
}
if (string.length() > 0) {
string += " + \" " + field.name + ":\" + ";
} else {
string += " " + field.name + ":\" + ";
}
// TODO: make proper toString generation
string += getString(field.type, field.name, field.note);
parse_body += " " + getParsing(field.type, field.namespace, field.name, field.note) + ";";
init_body += " this." + field.name + " = " + field.name + ";";
write_body += " " + getWriting(field.type, field.name, field.note) + ";";
}
if (extraSize > 0 && length.length() > 0) {
length = extraSize + " + " + length;
} else
if (length.length() == 0) {
length = extraSize + "";
}
string = "\"(" + (constr.namespace.length() > 0 ? constr.namespace + "." : "") + constr.name + (string.length() == 0 ? ")\"" : (string + " + \")\""));
FileWriter constructorFile = new FileWriter("src/tl/" + (constr.namespace.length() == 0 ? "" : (constr.namespace + "/")) + constr.className + ".java");
String constructorDeclaration = constructorTemplate
.replaceAll("\\/\\*\\*type\\*\\*\\/", constr.isFunction ? "tl.TLFunction" : constr.type.fullName)
.replaceAll("\\/\\*\\*constructor\\*\\*\\/", constr.className)
.replaceAll("\\/\\*\\*namespace\\*\\*\\/", constr.namespace.length() == 0 ? "" : ("." + constr.namespace))
.replaceAll("\\/\\*\\*import\\*\\*\\/",
(constr.namespace.length() == 0 ? "" : "\nimport tl.TL;")/* + (requireBigInt ? "\nimport java.math.BigInteger;" : "")*/)
.replaceAll("\\/\\*\\*fields\\*\\*\\/", fields)
.replaceAll("\\/\\*\\*params\\*\\*\\/", params)
.replaceAll("\\/\\*\\*length\\*\\*\\/", length)
.replaceAll("\\/\\*\\*string\\*\\*\\/", string)
.replaceAll("\\/\\*\\*parse_body\\*\\*\\/", parse_body)
.replaceAll("\\/\\*\\*init_body\\*\\*\\/", init_body)
.replaceAll("\\/\\*\\*write_body\\*\\*\\/", write_body)
;
constructorFile.write(constructorDeclaration);
constructorFile.close();
}
//JSONObject desc = (JSONObject) (JSONValue.parse(new FileReader("types.json")));
switchFile.close();
// vector#1cb5c415 {t:Type} # [ t ] = Vector t;
}
static String prettifyClassName(String name) {
if (name.equals("Object") || name.equals("!X") || name.equals("X")) return "LObject";
String[] parts = name.split("_");
String res = "";
for (int i = 0; i < parts.length; i++) {
res += parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1);
}
return res;
}
static String getJavaType(String tlType, String namespace, String note) {
if (tlType.toLowerCase().startsWith("vector ") || tlType.toLowerCase().startsWith("vector<")) {
String subtype = tlType.replaceAll("<", " ").replaceAll(">", "").split(" ")[1].trim();
String sub_namespace = "";
if (subtype.indexOf(".") > -1) {
String[] parts = subtype.split("\\.");
sub_namespace = parts[0];
subtype = parts[1];
}
return getJavaType(subtype, sub_namespace, note) + "[]";
}
if (tlType.equals("int") || tlType.equals("long") || tlType.equals("double")) {
return tlType;
} else
if (tlType.equals("int128") || tlType.equals("int256") || tlType.equals("bytes")) {
return "byte[]";
} else
if (tlType.equals("string")) {
if (note.equals("num") || note.equals("unum")) {
return "java.math.BigInteger";
} else {
return "String";
}
} else
if (tlType.equals("Bool")) {
return "boolean";
} else {
return "tl." + (namespace.length() > 0 ? namespace + "." : "") + "T" + prettifyClassName(tlType);
}
}
static int getExtraSize(String tlType) {
if (tlType.startsWith("Vector ") || tlType.startsWith("Vector<")) {
return 8;
} else
if (tlType.startsWith("vector ") || tlType.startsWith("vector<")) {
return 4;
}
if (tlType.equals("int")) {
return 4;
} else
if (tlType.equals("long") || tlType.equals("double")) {
return 8;
} else
if (tlType.equals("int128")) {
return 16;
} else
if (tlType.equals("int256")) {
return 32;
} else
if (tlType.equals("string") || tlType.equals("bytes")) {
return 0;
} else {
return 4;
}
}
static String stringBin(String note) {
if (note.equals("num") || note.equals("unum")) {
return ".toByteArray()";
}
return ".getBytes(\"UTF8\")";
}
static String getSize(String tlType, String var, String note) {
if (tlType.toLowerCase().startsWith("vector ") || tlType.toLowerCase().startsWith("vector<")) {
String subtype = tlType.toLowerCase().replaceAll("<", " ").replaceAll(">", "").split(" ")[1].trim();
if (subtype.equals("int")) {
return var + ".length * 4";
} else
if (subtype.equals("long") || subtype.equals("double")) {
return var + ".length * 8";
} else
if (subtype.equals("int128")) {
return var + ".length * 16";
} else
if (subtype.equals("int256")) {
return var + ".length * 8";
} else
if (subtype.equals("string")) {
return "TL.length(" + var/*+ stringBin(note)*/ + ")";
} else {
return "TL.length(" + var + ")";
}
}
if (tlType.equals("int") || tlType.equals("long") || tlType.equals("double") || tlType.equals("int128") || tlType.equals("int256") || tlType.equals("Bool")) {
return null;
} else
if (tlType.equals("bytes")) {
return "TL.length(" + var + ")";
} else
if (tlType.equals("string")) {
return "TL.length(" + var + stringBin(note) + ")";
} else {
return var + ".length()";
}
}
static String getParsing(String tlType, String namespace, String var, String note) {
if (tlType.toLowerCase().startsWith("vector ") || tlType.toLowerCase().startsWith("vector<")) {
String subtype = tlType.replaceAll("<", " ").replaceAll(">", "").split(" ")[1].trim();
String sub_namespace = "";
if (subtype.indexOf(".") > -1) {
String[] parts = subtype.split("\\.");
sub_namespace = parts[0];
subtype = parts[1];
}
if (subtype.equals("int") || subtype.equals("long") || subtype.equals("double") || subtype.equals("int128") || subtype.equals("int256") || subtype.equals("string") || subtype.equals("bytes")) {
return var + " = TL.readVector" + subtype.substring(0, 1).toUpperCase() + subtype.substring(1) + "(buffer, " + (tlType.startsWith("Vector") ? "true" : "false") + ")";
} else {
if (tlType.equals("vector transport_message")) {
return var + " = TL.readVectorMessage(buffer, false);";
}
return var + " = TL.readVector(buffer, " + (tlType.startsWith("Vector") ? "true" : "false") + ", new " + getJavaType(subtype, sub_namespace, note) + "[0])";
}
}
if (tlType.equals("int")) {
return var + " = buffer.getInt()";
} else
if (tlType.equals("long")) {
return var + " = buffer.getLong()";
} else
if (tlType.equals("double")) {
return var + " = buffer.getDouble()";
} else
if (tlType.equals("int128")) {
return var + " = TL.readInt128(buffer)";
} else
if (tlType.equals("int256")) {
return var + " = TL.readInt256(buffer)";
} else
if (tlType.equals("bytes")) {
return var + " = TL.readString(buffer)";
} else
if (tlType.equals("string")) {
if (note.equals("num") || note.equals("unum")) {
return var + " = new java.math.BigInteger(" + (note.equals("unum") ? "1, " : "") + "TL.readString(buffer))";
} else {
return var + " = new String(TL.readString(buffer), \"UTF8\")";
}
} else
if (tlType.equals("Bool")) {
return var + " = (buffer.getInt() == 0x997275b5)";
} else
if (tlType.substring(0, 1).toUpperCase().equals(tlType.substring(0, 1))) {
return var + " = (" + getJavaType(tlType, namespace, note) + ") TL.read(buffer)";
} else {
return var + " = new " + getJavaType(tlType, namespace, note) + "(buffer)";
}
}
static String getWriting(String tlType, String var, String note) {
if (tlType.toLowerCase().startsWith("vector ") || tlType.toLowerCase().startsWith("vector<")) {
String subtype = tlType.replaceAll("<", " ").replaceAll(">", "").split(" ")[1].trim();
String sub_namespace = "";
if (subtype.indexOf(".") > -1) {
String[] parts = subtype.split("\\.");
sub_namespace = parts[0];
subtype = parts[1];
}
return "TL.writeVector(buffer, " + var + ", " + (tlType.startsWith("Vector") ? "true" : "false") + ", " + (subtype.substring(0, 1).toUpperCase().equals(subtype.substring(0, 1)) ? "true" : "false") + ")";
}
if (tlType.equals("int")) {
return "buffer.putInt(" + var + ")";
} else
if (tlType.equals("long")) {
return "buffer.putLong(" + var + ")";
} else
if (tlType.equals("double")) {
return "buffer.putDouble(" + var + ")";
} else
if (tlType.equals("int128") || tlType.equals("int256")) {
return "buffer.put(" + var + ")";
} else
if (tlType.toLowerCase().equals("bytes")) {
return "TL.writeString(buffer, " + var + ", " + (tlType.substring(0, 1).toUpperCase().equals(tlType.substring(0, 1)) ? "true" : "false") + ")";
} else
if (tlType.toLowerCase().equals("string")) {
return "TL.writeString(buffer, " + var + stringBin(note) + ", " + (tlType.substring(0, 1).toUpperCase().equals(tlType.substring(0, 1)) ? "true" : "false") + ")";
} else
if (tlType.equals("Bool")) {
return "buffer.putInt(" + var + " ? 0x997275b5 : 0xbc799737)";
} else {
return var + ".writeTo(buffer, " + (tlType.substring(0, 1).toUpperCase().equals(tlType.substring(0, 1)) ? "true" : "false") + ")";
}
}
static String getString(String tlType, String var, String note) {
if (tlType.toLowerCase().startsWith("vector ") || tlType.toLowerCase().startsWith("vector<")) {
return "TL.toString(" + var + ")";
}
if (tlType.equals("long")) {
return "String.format(\"0x%016x\", " + var + ")";
} else
if (tlType.equals("int128") || tlType.equals("int256")) {
return "new java.math.BigInteger(" + var + ")";
} else
if (tlType.toLowerCase().equals("bytes")) {
return "TL.toString(" + var + ")";
} else
if (tlType.equals("Bool")) {
return "(" + var + " ? \"true\" : \"false\")";
} else
if (tlType.toLowerCase().equals("string")) {
return var;
} else {
return var;
}
}
}