using Vala;
using Gee;
/**
* Take a .vapi
file and output a set of Java interfaces
* corresponding to the types described.
*/
public class vapi2java : Object {
const string VAPI_DIR = "/usr/local/share/vala/vapi";
/**
* Take VAPI names from { link #VAPI_DIR} and output corresponding Java
* interfaces.
*/
public static void main(string[] args) {
new Converter().convert(args);
}
}
/**
* Convert a VAPI into a series of Java interfaces.
*/
public class Converter : Object {
public void convert(string[] args) {
CodeContext context = new CodeContext();
for (int i = 1; i < args.length; i++) {
context.add_package(args[i]);
context.add_source_file(new SourceFile(context, "/usr/local/share/vala/vapi/" + args[i] + ".vapi", true));
}
//context.add_package("glib-2.0");
//context.add_source_file(new SourceFile(context, "/usr/local/share/vala/vapi/glib-2.0.vapi", true));
new Parser().parse(context);
new SymbolResolver().resolve(context);
output("", context.root);
}
/**
* Output a namespace, and all its contents, recursively.
*/
public void output(string prefix, Namespace ns) {
string packageName = prefix + javaNameForNamespace(ns);
stdout.printf("pkg = [%s]\n", packageName);
if (packageName != "")
DirUtils.create(tidy(packageName), 0755);
// -- Classes...
//
stdout.printf("classes... \n");
foreach (Class clazz in ns.get_classes()) {
convertClass(packageName, clazz, clazz.get_base_types(), clazz.get_type_parameters());
}
// -- Interfaces...
//
stdout.printf("interfaces... \n");
foreach (Interface iface in ns.get_interfaces())
convertClass(packageName, iface, iface.get_prerequisites(), iface.get_type_parameters());
// -- Enumerations...
//
stdout.printf("enums... \n");
foreach (Enum e in ns.get_enums()) {
string enumname = javaNameForClass(e);
string filename = tidy(packageName) + "/" + enumname + ".java";
string content =
"public enum " + enumname + " {\n";
if (packageName != "")
content = "package " + packageName + ";\n\n" + content;
bool doneOne = false;
foreach (Vala.EnumValue v in e.get_values()) {
if (doneOne) content += ",\n";
content += " " + v.name;
doneOne = true;
}
content += ";\n}\n";
FileUtils.set_contents(filename, content);
}
// -- Structs...
//
stdout.printf("structs... \n");
foreach (Struct s in ns.get_structs()) {
string structname = javaNameForClass(s);
if (structname.down(1) == structname.substring(0,1))
continue;
string filename = tidy(packageName) + "/" + structname + ".java";
string content =
"import com.sun.jna.Structure;\n\n" +
"public class " + structname + " extends Structure {\n";
if (packageName != "")
content = "package " + packageName + ";\n\n" + content;
foreach (Field f in s.get_fields()) {
content += " public " + javaType(f.field_type) + " " + javaName(f.name) + ";\n";
}
content += ";\n}\n";
FileUtils.set_contents(filename, content);
}
// -- Recursively process child namespaces...
//
stdout.printf("namespaces... \n");
foreach (Namespace child in ns.get_namespaces())
if (packageName == "") {
output(packageName, child);
} else {
output(packageName + ".", child);
}
}
protected void convertClass(string packageName, ObjectTypeSymbol clazz, Gee.List parents, Gee.List typeParameters) {
// -- Identify any generic parms...
//
string generics = "";
bool doneOne = false;
foreach (TypeParameter arg in typeParameters) {
if (doneOne) generics += ", ";
generics += arg.name;
doneOne = true;
}
if (generics != "")
generics = "<" + generics + ">";
// -- Build up methods, and identify constructor...
//
string methods = "";
string constructor = "";
foreach (Method m in clazz.get_methods()) {
string new_method = "";
if (m.name.has_prefix(".new")) {
if (constructor == "" && (m.get_parameters().size == 0))
constructor = "@ConstructorName(\"" + m.get_cname() + "\")\n";
new_method += " // TODO Handle constructors? @CName(\"" + m.get_cname() + "\")\n //";
} else {
new_method += " @CName(\"" + m.get_cname() + "\")\n";
}
new_method += " public " + javaType(m.return_type) + " " + javaName(m.name) + "(";
bool doneOne = false;
foreach (FormalParameter p in m.get_parameters()) {
if (doneOne)
new_method += ", ";
// If the type wasn't found, don't output this method
var jType = javaType(p.parameter_type);
if (jType == null) {
new_method = "";
break;
}
new_method += jType;
if (p.ellipsis)
new_method += "...";
new_method += " " + javaName(p.name);
doneOne = true;
}
if (new_method != "")
methods += new_method + ");\n\n";
}
// -- Java source file...
//
string ifname = javaNameForClass(clazz);
string filename = tidy(packageName) + "/" + ifname + ".java";
string content =
"import lib.*;\n\n" + constructor +
"public interface " + ifname + generics;
if (packageName != "")
content = "package " + packageName + ";\n\n" + content;
if (parents.size > 0) {
bool doneOne = false;
foreach (DataType parent in parents) {
if (parent.to_qualified_string() == "GLib.Object") continue;
content += doneOne ? "," : " extends";
content += " " + javaType(parent);
doneOne = true;
}
}
content += " {\n" + methods;
// -- Property getters/setters...
//
foreach (Property p in clazz.get_properties()) {
var pname = p.name;
var jtype = javaType(p.property_type);
if (jtype == null)
continue;
if (p.get_accessor != null)
content += " @CName(\"" + clazz.get_upper_case_cname().down() + "_get_" + p.name + "\")\n" +
" public " + javaType(p.property_type) + " get" + javaName(pname, true) + "();\n\n";
if (p.set_accessor != null)
content += " @CName(\"" + clazz.get_upper_case_cname().down() + "_set_" + p.name + "\")\n" +
" public void set" + javaName(pname, true) + "(" + jtype + " " + javaName(p.name) + ");\n\n";
}
content += "}\n";
stdout.printf("Writing to %s\n", filename);
FileUtils.set_contents(filename, content);
}
/**
* Convert a name of the form `foo_bar' to the Java-esque `fooBar'
*/
public string javaName(string? name, bool property = false) {
if (name == null)
return "NAME_ERROR";
string result = "";
bool doneOne = false;
foreach (string bit in name.split("_")) {
if (doneOne)
result += ucfirst(bit);
else
result += bit;
doneOne = true;
}
// -- Avoid reserved words...
//
if (result == "assert") {
result = "assertion";
} else if (result == "new") {
result = "create";
}
// -- Properties are capitalised...
//
if (property)
result = ucfirst(result);
return result;
}
/**
* Return value with the first character capitalised.
*/
private string ucfirst(string arg) {
if (arg == null || arg.len() == 0)
return "UCFIRST_ERROR";
return arg.up(1) + arg.substring(1, arg.len() - 1);
}
/**
* Return the Java package name for a namespace.
*/
public string javaNameForNamespace(Namespace ns) {
if (ns.name == null)
return "";
return javaName(ns.name).down();
}
/**
* Return the Java package name for a namespace.
*/
public string javaNameForClass(TypeSymbol clazz) {
return javaName(clazz.name);
}
/**
* For the given data type, return a string representing th
* Java type.
*/
public string javaType(DataType? type) {
if (type == null)
return "TYPE_ERROR";
string[] bits = type.to_qualified_string().split(".", 2);
if (type.is_reference_type_or_type_parameter() && (type.to_string().size() <= 2))
return type.to_string().substring(0, 1);
int count = 0;
foreach (string bit in bits)
count++;
if (bits[count - 1] != "void" && !bits[count - 1].has_suffix("*") && !type.is_array() && !type.is_type_argument)
bits[count - 1] = type.data_type.name;
// -- Special cases...
//
if (bits[0] == "char*") {
return "String";
} else if (bits[0] == "unichar") {
return "char";
} else if (bits[0] == "size_t") {
return "long";
} else if (bits[0] == "bool") {
return "boolean";
} else if (bits[0] == "bool[]") {
return "boolean[]";
} else if (bits[0] == "string") {
return "String";
} else if (bits[0] == "string[]") {
return "String[]";
} else if (bits[0] == "uint") {
return "long";
} else if (bits[0] == "constpointer") {
return "long";
}
// -- Handle "normal" cases...
//
string generics = "";
bool doneOne = false;
foreach (DataType arg in type.get_type_arguments()) {
if (doneOne) generics += ", ";
var type = javaType(arg);
if (type == "int") {
type = "Integer";
}
generics += type;
doneOne = true;
}
if (generics != "")
bits[count - 1] = bits[count - 1] + "<" + generics + ">";
if (count > 1 && bits[1] == null) {
return null;
} else if (count > 1) {
return bits[0].down() + "." + javaName(bits[1]);
} else {
return bits[0];
}
}
private string tidy(string input) {
string output = ".";
foreach (string bit in input.split(".")) {
output = output + "/" + bit;
}
return output;
}
}