GtkSharp/Source/Tools/GapiCodegen/ObjectBase.cs

372 lines
11 KiB
C#

// ObjectBase.cs - Base class for Object types
//
// Authors: Mike Kestner <mkestner@novell.com>
//
// Copyright (c) 2005 Novell, Inc.
// Copyright (c) 2009 Christian Hoff
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
namespace GtkSharp.Generation {
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;
public abstract class ObjectBase : HandleBase {
bool is_interface;
protected string class_struct_name = null;
bool class_fields_valid; // false if the class structure contains a bitfield or fields of unknown types
ArrayList class_members = new ArrayList ();
protected List<StructABIField> abi_class_members = new List<StructABIField> ();
bool class_abi_valid = true;
protected IList<ClassField> class_fields = new List<ClassField> ();
// The default handlers of these signals need to be overridden with g_signal_override_class_closure
protected IList<GObjectVM> virtual_methods = new List<GObjectVM> ();
// virtual methods that are generated as an IntPtr in the class struct
protected IList<VirtualMethod> hidden_vms = new List<VirtualMethod> ();
protected IList<InterfaceVM> interface_vms = new List<InterfaceVM> ();
protected Hashtable sigs = new Hashtable();
protected ObjectBase (XmlElement ns, XmlElement elem, bool is_interface) : base (ns, elem)
{
this.is_interface = is_interface;
XmlElement class_elem = null;
Hashtable vms = new Hashtable ();
Hashtable signal_vms = new Hashtable ();
if (this.ParserVersion == 1)
class_struct_name = this.CName + (is_interface ? "Iface" : "Class");
foreach (XmlNode node in elem.ChildNodes) {
if (!(node is XmlElement)) continue;
XmlElement member = node as XmlElement;
switch (node.Name) {
case "virtual_method":
if (this.ParserVersion == 1) {
if (is_interface) // Generating non-signal GObject virtual methods is not supported in compatibility mode
AddVM (member, false, is_interface);
} else
vms.Add (member.GetAttribute ("cname"), member);
break;
case "signal":
if (this.ParserVersion == 1 || member.GetAttribute ("field_name") == "")
AddVM (member, true, is_interface);
else
signal_vms.Add (member.GetAttribute ("field_name"), member);
if (!member.GetAttributeAsBoolean ("hidden")) {
string name = member.GetAttribute("name");
while (sigs.ContainsKey(name))
name += "mangled";
sigs.Add (name, new Signal (member, this));
}
break;
case "class_struct":
class_elem = member;
break;
}
}
if (class_elem == null) return;
if (class_elem.GetAttributeAsBoolean("private")) {
class_abi_valid = false;
return;
}
class_struct_name = class_elem.GetAttribute ("cname");
int num_abi_fields = 0;
for (int node_idx = 0; node_idx < class_elem.ChildNodes.Count; node_idx++) {
XmlNode node = class_elem.ChildNodes [node_idx];
if (!(node is XmlElement)) continue;
XmlElement member = (XmlElement) node;
// Make sure ABI fields are taken into account, even when hidden.
if (node.Name == "field") {
num_abi_fields += 1;
if (num_abi_fields != 1) { // Skip instance parent struct
abi_class_members.Add (new StructABIField (member, this, "class_abi"));
}
} else if (node.Name == "union") {
abi_class_members.Add (new UnionABIField (member, this, "class_abi"));
} else if (node.Name == "method") {
abi_class_members.Add (new MethodABIField (member, this, "class_abi"));
}
switch (member.Name) {
case "method":
string vm_name;
XmlElement vm_elem;
bool is_signal_vm = member.HasAttribute ("signal_vm");
if (is_signal_vm) {
vm_name = member.GetAttribute ("signal_vm");
vm_elem = signal_vms [vm_name] as XmlElement;
} else {
vm_name = member.GetAttribute ("vm");
vm_elem = vms [vm_name] as XmlElement;
}
AddVM (vm_elem, is_signal_vm, is_interface);
break;
case "field":
if (node_idx == 0) continue; // Parent class
ClassField field = new ClassField (member, this);
class_fields.Add (field);
class_members.Add (field);
break;
default:
Console.WriteLine ("Unexpected node " + member.Name + " in " + class_elem.GetAttribute ("cname"));
break;
}
}
}
VirtualMethod AddVM (XmlElement vm_elem, bool is_signal_vm, bool is_interface)
{
VirtualMethod vm;
if (is_signal_vm)
vm = new DefaultSignalHandler (vm_elem, this);
else if (is_interface) {
string target_name = vm_elem.HasAttribute ("target_method") ? vm_elem.GetAttribute ("target_method") : vm_elem.GetAttribute ("name");
vm = new InterfaceVM (vm_elem, GetMethod (target_name), this);
} else
vm = new GObjectVM (vm_elem, this);
if (vm_elem.GetAttributeAsBoolean ("padding") || vm_elem.GetAttributeAsBoolean ("hidden"))
hidden_vms.Add (vm);
else {
if (vm is GObjectVM) {
virtual_methods.Add ((GObjectVM)vm);
} else {
interface_vms.Add ((InterfaceVM)vm);
}
}
if (vm.CName != "")
class_members.Add (vm);
return vm;
}
protected override bool IsNodeNameHandled (string name)
{
switch (name) {
case "virtual_method":
case "signal":
case "class_struct":
return true;
default:
return base.IsNodeNameHandled (name);
}
}
public override string CallByName (string var)
{
return CallByName (var, false);
}
public abstract string CallByName (string var, bool owned);
public override string FromNative (string var, bool owned)
{
return "GLib.Object.GetObject(" + var + (owned ? ", true" : "") + ") as " + QualifiedName;
}
public string ClassStructName {
get {
return class_struct_name;
}
}
public bool CanGenerateClassStruct {
get {
/* Generation of interface class structs was already supported by version 2.12 of the GAPI parser. Their layout was determined by the order
* in which the signal and virtual_method elements appeared in the XML. However, we cannot use that approach for old GObject class structs
* as they may contain class fields which don't appear in the old (version 1) API files. There are also cases in which the order of the
* <signal> and <virtual_method> elements do not match the struct layout.
*/
return (is_interface || this.ParserVersion >= 2) && (class_abi_valid || class_struct_name == "GtkWidgetClass");
}
}
public override bool CanGenerateABIStruct(LogWriter log) {
if (!abi_fields_valid) {
log.Info(CName + " has invalid fields");
return false;
}
// No instance structure for interfaces
if (is_interface) {
log.Info(CName + " Is interface");
return false;
}
return class_struct_name != null;
}
protected void GenerateClassStruct (GenerationInfo gen_info)
{
if (class_struct_name == null || !CanGenerateClassStruct) return;
StreamWriter sw = gen_info.Writer;
sw.WriteLine ("\t\t[StructLayout (LayoutKind.Sequential)]");
sw.WriteLine ("\t\tstruct " + class_struct_name + " {");
foreach (object member in class_members) {
if (member is VirtualMethod) {
VirtualMethod vm = member as VirtualMethod;
if (hidden_vms.Contains (vm) || (is_interface && vm is DefaultSignalHandler))
sw.WriteLine ("\t\t\tIntPtr {0};", vm.Name);
else
sw.WriteLine ("\t\t\tpublic {0}NativeDelegate {0};", vm.Name);
} else if (member is ClassField) {
ClassField field = member as ClassField;
field.Generate (gen_info, "\t\t\t");
}
}
sw.WriteLine ("\t\t}");
sw.WriteLine ();
}
public Hashtable Signals {
get {
return sigs;
}
}
public Signal GetSignal (string name)
{
return sigs[name] as Signal;
}
public Signal GetSignalRecursively (string name)
{
return GetSignalRecursively (name, false);
}
public virtual Signal GetSignalRecursively (string name, bool check_self)
{
Signal p = null;
if (check_self)
p = GetSignal (name);
if (p == null && Parent != null)
p = (Parent as ObjectBase).GetSignalRecursively (name, true);
if (check_self && p == null) {
foreach (string iface in interfaces) {
InterfaceGen igen = SymbolTable.Table.GetClassGen (iface) as InterfaceGen;
if (igen == null)
continue;
p = igen.GetSignalRecursively (name, true);
if (p != null)
break;
}
}
return p;
}
public void GenSignals (GenerationInfo gen_info, ObjectBase implementor)
{
foreach (Signal sig in sigs.Values)
sig.Generate (gen_info, implementor);
}
public void GenVirtualMethods (GenerationInfo gen_info, ObjectBase implementor)
{
foreach (GObjectVM vm in virtual_methods)
vm.Generate (gen_info, implementor);
}
public override bool Validate ()
{
if (Parent != null && !(Parent as ObjectBase).ValidateForSubclass ())
return false;
LogWriter log = new LogWriter (QualifiedName);
ArrayList invalids = new ArrayList ();
foreach (GObjectVM vm in virtual_methods)
if (!vm.Validate (log))
invalids.Add (vm);
foreach (GObjectVM invalid_vm in invalids) {
virtual_methods.Remove (invalid_vm);
hidden_vms.Add (invalid_vm);
}
invalids.Clear ();
class_fields_valid = true;
foreach (ClassField field in class_fields)
if (!field.Validate (log))
class_fields_valid = false;
foreach (StructABIField field in abi_class_members)
if (!field.Validate (log))
class_abi_valid = false;
foreach (InterfaceVM vm in interface_vms)
if (!vm.Validate (log))
invalids.Add (vm);
foreach (InterfaceVM invalid_vm in invalids) {
interface_vms.Remove (invalid_vm);
hidden_vms.Add (invalid_vm);
}
invalids.Clear ();
foreach (Signal sig in sigs.Values) {
if (!sig.Validate (log))
invalids.Add (sig);
}
foreach (Signal sig in invalids)
sigs.Remove (sig.Name);
return base.Validate ();
}
public virtual bool ValidateForSubclass ()
{
LogWriter log = new LogWriter (QualifiedName);
ArrayList invalids = new ArrayList ();
foreach (Signal sig in sigs.Values) {
if (!sig.Validate (log)) {
invalids.Add (sig);
}
}
foreach (Signal sig in invalids)
sigs.Remove (sig.Name);
invalids.Clear ();
return true;
}
public override string GenerateGetSizeOf () {
return NS + "." + Name + ".abi_info.Size";
}
}
}