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; } }