using System; using System.Xml; using System.Xml.XPath; using System.IO; using System.Collections; public class EnumValue { public string name; public int value; } public class FlagValue { public string name; public uint value; } public class EnumInfo { public string name; public bool flag; public ArrayList values; } public class SignalParameter { public string name; public string type; } public class SignalInfo { public string name; public string managed_name; public string object_type; public string return_type; public ArrayList parameters; } public class PropertyInfo { public string name; public string managed_name; public string type; public bool readable, writeable; public EnumInfo enuminfo; } public class ElementInfo { public string name; public string class_name; public string gtype_name; public ArrayList hierarchy; public ArrayList interfaces; public ArrayList pads; public ArrayList properties; public ArrayList signals; public ArrayList actions; } public class ElementGen { static bool IsHidden (XmlNode node) { XmlElement elt = node as XmlElement; return (elt != null && elt.HasAttribute ("hidden") && (elt.GetAttribute ("hidden") == "1" || elt.GetAttribute ("hidden") == "true")); } static bool handlePads (XmlNode node, ElementInfo ei) { if (ei.pads == null) ei.pads = new ArrayList (); XmlElement elt = node as XmlElement; if (elt == null) return true; foreach (XmlElement pad in elt.ChildNodes) if (!IsHidden (pad) && !IsHidden (pad["name"])) ei.pads.Add (pad["name"].InnerText); return true; } static bool handleProperties (XmlNode node, ElementInfo ei) { if (ei.properties == null) ei.properties = new ArrayList (); XmlElement elt = node as XmlElement; if (elt == null) return true; foreach (XmlElement property in elt.ChildNodes) { if (IsHidden (property)) continue; PropertyInfo pi = new PropertyInfo (); pi.name = property["name"].InnerText; pi.managed_name = (property["managed_name"] != null) ? property["managed_name"].InnerText : null; pi.type = property["type"].InnerText; pi.readable = property["flags"].InnerText.IndexOf ('R') != -1; pi.writeable = property["flags"].InnerText.IndexOf ('W') != -1; if (property["enum-values"] != null) { pi.enuminfo = new EnumInfo (); pi.enuminfo.flag = false; pi.enuminfo.values = new ArrayList (); foreach (XmlNode val in property["enum-values"].ChildNodes) { EnumValue env = new EnumValue (); env.name = val.Attributes["nick"].InnerText; env.value = Int32.Parse (val.Attributes["value"].InnerText); pi.enuminfo.values.Add (env); } } else if (property["flags-values"] != null) { pi.enuminfo = new EnumInfo (); pi.enuminfo.flag = true; pi.enuminfo.values = new ArrayList (); foreach (XmlNode val in property["flags-values"].ChildNodes) { FlagValue env = new FlagValue (); env.name = val.Attributes["nick"].InnerText; env.value = UInt32.Parse (val.Attributes["value"].InnerText); pi.enuminfo.values.Add (env); } } ei.properties.Add (pi); } return true; } static bool handleSignals (XmlNode node, ElementInfo ei) { if (ei.signals == null) ei.signals = new ArrayList (); XmlElement elt = node as XmlElement; if (elt == null) return true; foreach (XmlElement signal in elt.ChildNodes) { if (IsHidden (signal)) continue; SignalInfo si = new SignalInfo (); si.name = signal["name"].InnerText; si.managed_name = (signal["managed_name"] != null) ? signal["managed_name"].InnerText : null; si.return_type = signal["return-type"].InnerText; si.object_type = signal["object-type"].InnerText; XmlElement parms = signal["params"]; if (parms != null) { si.parameters = new ArrayList (); foreach (XmlElement parm in parms.ChildNodes) { SignalParameter par = new SignalParameter (); par.type = parm.InnerText; if (parm.Attributes["name"] == null) throw new Exception ("All signal parameters need the 'name' attribute"); par.name = parm.Attributes["name"].InnerText; si.parameters.Add (par); } } ei.signals.Add (si); } return true; } static bool handleActions (XmlNode node, ElementInfo ei) { if (ei.actions == null) ei.actions = new ArrayList (); XmlElement elt = node as XmlElement; if (elt == null) return true; foreach (XmlElement signal in elt.ChildNodes) { if (IsHidden (signal)) continue; SignalInfo si = new SignalInfo (); si.name = signal["name"].InnerText; si.managed_name = (signal["managed_name"] != null) ? signal["managed_name"].InnerText : null; si.return_type = signal["return-type"].InnerText; si.object_type = signal["object-type"].InnerText; XmlElement parms = signal["params"]; if (parms != null) { si.parameters = new ArrayList (); foreach (XmlElement parm in parms.ChildNodes) { SignalParameter par = new SignalParameter (); par.type = parm.InnerText; if (parm.Attributes["name"] == null) throw new Exception ("All signal parameters need the 'name' attribute"); par.name = parm.Attributes["name"].InnerText; si.parameters.Add (par); } } ei.actions.Add (si); } return true; } public static string CTypeToManagedType (string ctype, XmlDocument api_doc) { switch (ctype) { case "GObject": return "GLib.Object"; case "gchararray": return "string"; case "gboolean": return "bool"; case "guint": return "uint"; case "gint": return "int"; case "gulong": case "guint64": return "ulong"; case "glong": case "gint64": return "long"; case "gfloat": return "float"; case "gdouble": return "double"; case "GValueArray": return "GLib.ValueArray"; } XPathNavigator api_nav = api_doc.CreateNavigator (); XPathNodeIterator type_iter = api_nav.Select ("/api/namespace/*[@cname='" + ctype + "']"); while (type_iter.MoveNext ()) { string ret = type_iter.Current.GetAttribute ("name", ""); if (ret != null && ret != String.Empty) { XPathNavigator parent = type_iter.Current.Clone (); parent.MoveToParent (); ret = parent.GetAttribute ("name", "") + "." + ret; return ret; } } return null; } public static void writeElement (TextWriter writer, ElementInfo ei, StreamReader custom_code, XmlDocument api_doc) { ArrayList enums = new ArrayList (); writer.WriteLine ("#region Autogenerated code"); writer.WriteLine ("\t[GTypeName (\"" + ei.gtype_name + "\")]"); string class_name = (ei.class_name != null) ? ei.class_name : ei.gtype_name.StartsWith ("Gst") ? ei.gtype_name.Substring (3) : ei.gtype_name; writer.Write ("\tpublic sealed class " + class_name + " : "); for (int i = 1; i < ei.hierarchy.Count; i++) { string parent_type = (string) ei.hierarchy[i]; string parent_managed_type = CTypeToManagedType (parent_type, api_doc); if (parent_managed_type != null) { writer.Write (parent_managed_type); break; } } foreach (string iface in ei.interfaces) writer.Write (", " + CTypeToManagedType (iface, api_doc)); writer.WriteLine (" {"); writer.WriteLine ("\t\tpublic " + class_name + " (IntPtr raw) : base (raw) { }\n"); writer.WriteLine ("\t\t[DllImport(\"libgstreamer-0.10.dll\") ]"); writer.WriteLine ("\t\tstatic extern IntPtr gst_element_factory_make (IntPtr element, IntPtr name);\n"); writer.WriteLine ("\t\tpublic " + class_name + " (string name) : base (IntPtr.Zero) {"); writer.WriteLine ("\t\t\tIntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name);"); writer.WriteLine ("\t\t\tIntPtr native_element = GLib.Marshaller.StringToPtrGStrdup (\"" + ei.name + "\");"); writer.WriteLine ("\t\t\tRaw = gst_element_factory_make (native_element, native_name);"); writer.WriteLine ("\t\t\tGLib.Marshaller.Free (native_name);"); writer.WriteLine ("\t\t\tGLib.Marshaller.Free (native_element);"); writer.WriteLine ("\t\t\tif (Raw == IntPtr.Zero)"); writer.WriteLine ("\t\t\t\tthrow new Exception (\"Failed to instantiate element \\\"" + ei.name + "\\\"\");"); writer.WriteLine ("\t\t}\n"); writer.WriteLine ("\t\tpublic " + class_name + " () : this ((string) null) { }\n"); writer.WriteLine ("\t\tpublic static " + class_name + " Make (string name) {"); writer.WriteLine ("\t\t\treturn Gst.ElementFactory.Make (\"" + ei.name + "\", name) as " + class_name + ";"); writer.WriteLine ("\t\t}\n"); writer.WriteLine ("\t\tpublic static " + class_name + " Make () { return Make (null); } \n"); foreach (PropertyInfo pinfo in ei.properties) { string managed_name = (pinfo.managed_name != null) ? pinfo.managed_name : PropToCamelCase (pinfo.name); string managed_type = CTypeToManagedType (pinfo.type, api_doc); if (managed_type == null && pinfo.enuminfo == null) { throw new Exception ("Can't get managed type mapping for type " + pinfo.type); } else if (managed_type == null) { pinfo.enuminfo.name = pinfo.type; enums.Add (pinfo.enuminfo); managed_type = pinfo.type.StartsWith (ei.gtype_name) ? pinfo.type.Substring (ei.gtype_name.Length) : pinfo.type.StartsWith ("Gst") ? pinfo.type.Substring (3) : pinfo.type; managed_type += "Type"; } writer.WriteLine ("\t\t[GLib.Property (\"" + pinfo.name + "\")]"); writer.WriteLine ("\t\tpublic " + managed_type + " " + managed_name + " {"); if (pinfo.readable) { writer.WriteLine ("\t\t\tget {"); writer.WriteLine ("\t\t\t\tGLib.Value val = GetProperty (\"" + pinfo.name + "\");"); writer.WriteLine ("\t\t\t\t" + managed_type + " ret = (" + managed_type + ") val.Val;"); writer.WriteLine ("\t\t\t\tval.Dispose ();"); writer.WriteLine ("\t\t\t\treturn ret;"); writer.WriteLine ("\t\t\t}"); } if (pinfo.writeable) { writer.WriteLine ("\t\t\tset {"); writer.WriteLine ("\t\t\t\tGLib.Value val = new GLib.Value (this, \"" + pinfo.name + "\");"); writer.WriteLine ("\t\t\t\tval.Val = value;"); writer.WriteLine ("\t\t\t\tSetProperty (\"" + pinfo.name + "\", val);"); writer.WriteLine ("\t\t\t\tval.Dispose ();"); writer.WriteLine ("\t\t\t}"); } writer.WriteLine ("\t\t}\n"); } writer.WriteLine (); if (ei.signals.Count > 0) { foreach (SignalInfo si in ei.signals) { string managed_name = (si.managed_name != null) ? si.managed_name : PropToCamelCase (si.name); writer.WriteLine ("\t\tpublic delegate void " + managed_name + "Handler (object o, " + managed_name + "Args args);\n"); writer.WriteLine ("\t\tpublic class " + managed_name + "Args : GLib.SignalArgs {"); for (int i = 0; i < si.parameters.Count; i++) { SignalParameter param = (SignalParameter) si.parameters[i]; string managed_type = CTypeToManagedType (param.type, api_doc); writer.WriteLine ("\t\t\tpublic " + managed_type + " " + param.name + " {"); writer.WriteLine ("\t\t\t\tget {"); writer.WriteLine ("\t\t\t\t\treturn (" + managed_type + ") Args[" + i + "];"); writer.WriteLine ("\t\t\t\t}"); writer.WriteLine ("\t\t\t}\n"); } writer.WriteLine ("\t\t}\n"); writer.WriteLine ("\t\tpublic event " + managed_name + "Handler " + managed_name + " {"); writer.WriteLine ("\t\t\tadd {"); writer.WriteLine ("\t\t\t\tDynamicSignal.Connect (this, \"" + si.name + "\", value);"); writer.WriteLine ("\t\t\t}\n"); writer.WriteLine ("\t\t\tremove {"); writer.WriteLine ("\t\t\t\tDynamicSignal.Disconnect (this, \"" + si.name + "\", value);"); writer.WriteLine ("\t\t\t}"); writer.WriteLine ("\t\t}"); } } if (ei.actions.Count > 0) { foreach (SignalInfo si in ei.actions) { string managed_name = (si.managed_name != null) ? si.managed_name : PropToCamelCase (si.name); string managed_return = CTypeToManagedType (si.return_type, api_doc); writer.Write ("\t\tpublic " + managed_return + " " + managed_name + " ("); for (int i = 0; i < si.parameters.Count; i++) { SignalParameter param = (SignalParameter) si.parameters[i]; string managed_type = CTypeToManagedType (param.type, api_doc); if (i == 0) writer.Write (managed_type + " " + param.name); else writer.Write (", " + managed_type + " " + param.name); } writer.WriteLine (") {"); writer.WriteLine ("\t\t\tobject[] parameters = new object[" + si.parameters.Count + "];"); for (int i = 0; i < si.parameters.Count; i++) { SignalParameter param = (SignalParameter) si.parameters[i]; writer.WriteLine ("\t\t\tparameters[" + i + "] = " + param.name + ";"); } writer.WriteLine ("\t\t\treturn (" + managed_return + ") Emit (\"" + si.name + "\", parameters);"); writer.WriteLine ("\t\t}\n"); } } if (ei.interfaces.Count > 0) { string path = Path.GetDirectoryName (System.Reflection.Assembly.GetCallingAssembly ().Location); foreach (string iface in ei.interfaces) { writer.WriteLine ("#endregion"); writer.WriteLine ("#region Customized code"); writer.WriteLine ("#line 1 \"" + iface + ".cs\""); StreamReader interface_code = System.IO.File.OpenText (path + "/interfaces/" + iface + ".cs"); string iface_code = interface_code.ReadToEnd (); writer.WriteLine (iface_code); } } if (enums.Count > 0) { foreach (EnumInfo eni in enums) { writer.WriteLine ("\t\t[GTypeName (\"" + eni.name + "\")]"); if (eni.flag) writer.WriteLine ("\t\t[Flags]"); string enum_name = eni.name.StartsWith (ei.gtype_name) ? eni.name.Substring (ei.gtype_name.Length) : eni.name.StartsWith ("Gst") ? eni.name.Substring (3) : eni.name; enum_name += "Type"; writer.WriteLine ("\t\tpublic enum " + enum_name + " {"); if (eni.flag) { foreach (FlagValue ev in eni.values) { writer.WriteLine ("\t\t\t" + PropToCamelCase (ev.name) + " = " + ev.value + ", "); } } else { foreach (EnumValue ev in eni.values) { writer.WriteLine ("\t\t\t" + PropToCamelCase (ev.name) + " = " + ev.value + ", "); } } writer.WriteLine ("\t\t}\n"); } } if (custom_code != null) { writer.WriteLine ("#endregion"); writer.WriteLine ("#region Customized code"); writer.WriteLine ("#line 1 \"" + ei.name + ".custom\""); string custom_code_string = custom_code.ReadToEnd (); writer.WriteLine (custom_code_string); } writer.WriteLine ("#endregion"); writer.WriteLine ("\t}\n"); } public static string PropToCamelCase (string pname) { string ret = Char.ToUpper (pname[0]).ToString (); bool next_upper = false; for (int i = 1; i < pname.Length; i++) { if (pname[i] == '-') { next_upper = true; } else if (next_upper) { ret = ret + Char.ToUpper (pname[i]); next_upper = false; } else { ret = ret + pname[i]; } } return ret; } public static int Main (string[] args) { if (args.Length != 3) { Console.Error.WriteLine ("Usage: element-gen --namespace= --api= --input="); return -1; } string ns = null; XmlDocument api_doc = new XmlDocument (); XmlDocument introspect_doc = new XmlDocument (); string filename = null; foreach (string arg in args) { if (arg.StartsWith ("--input=")) { filename = arg.Substring (8); try { Stream stream = File.OpenRead (filename + ".xml"); introspect_doc.Load (stream); stream.Close (); } catch (Exception e) { Console.Error.WriteLine ("Failed to load introspection XML:\n" + e.ToString ()); return -2; } } else if (arg.StartsWith ("--api=")) { string api_filename = arg.Substring (6); try { Stream stream = File.OpenRead (api_filename); api_doc.Load (stream); stream.Close (); } catch (Exception e) { Console.Error.WriteLine ("Failed to load API XML:\n" + e.ToString ()); return 1; } } else if (arg.StartsWith ("--namespace=")) { ns = arg.Substring (12); } else { Console.Error.WriteLine ("Usage: element-gen --namespace: --api= --input:"); return 1; } } if (introspect_doc.DocumentElement.Name != "element") { Console.Error.WriteLine ("Invalid introspection XML"); return -3; } TextWriter writer; try { writer = Console.Out; } catch (Exception e) { Console.Error.WriteLine ("Failed to open output file:\n" + e.ToString ()); return -2; } StreamReader custom_code = null; try { custom_code = System.IO.File.OpenText (filename + ".custom"); } catch (Exception) {} // No custom file is OK if (IsHidden (introspect_doc.DocumentElement)) return 0; writer.WriteLine ("using System;"); writer.WriteLine ("using System.Collections;"); writer.WriteLine ("using System.Runtime.InteropServices;"); writer.WriteLine ("using GLib;"); writer.WriteLine ("using Gst;"); writer.WriteLine ("using Gst.Interfaces;"); writer.WriteLine (); writer.WriteLine (String.Format ("namespace {0} ", ns) + "{"); ElementInfo ei = new ElementInfo (); if (introspect_doc.DocumentElement.Attributes["name"] != null) ei.class_name = introspect_doc.DocumentElement.Attributes["name"].InnerText; foreach (XmlNode node in introspect_doc.DocumentElement) { if (IsHidden (node)) continue; switch (node.Name) { case "name": ei.name = node.InnerText; break; case "object": XmlElement elt = node as XmlElement; ei.gtype_name = elt.GetAttribute ("name"); ei.hierarchy = new ArrayList (); while (elt != null) { ei.hierarchy.Add (elt.GetAttribute ("name")); elt = elt.ChildNodes[0] as XmlElement; } break; case "interfaces": XmlElement elt2 = node as XmlElement; ei.interfaces = new ArrayList (); foreach (XmlElement iface in elt2.ChildNodes) ei.interfaces.Add (iface.GetAttribute ("name")); break; case "pads": if (!handlePads (node, ei)) return -4; break; case "element-properties": if (!handleProperties (node, ei)) return -5; break; case "element-signals": if (!handleSignals (node, ei)) return -6; break; case "element-actions": if (!handleActions (node, ei)) return -7; break; case "pad-templates": default: break; } } writeElement (writer, ei, custom_code, api_doc); writer.WriteLine ("}"); return 0; } }