GtkSharp/generator/ObjectBase.cs
Bertrand Lorentz 4d19e5ac2a generator: Properly handle boolean attributes when parsing the XML
In a lot of places, we were only checking the presence of boolean
attributes, like "array", automatically assuming they were true. This
meant that we didn't allow setting them explicitly to false, which
apparently is needed for some bindings.

For all boolean attributes, we now use the GetAttributeAsBoolean method
added in the previous commit, to correctly check the value of the
attribute. As before, if the attribute is not present, it is considered
to be false.

Thanks to Stephan Sundermann for noticing this issue.
2012-10-21 18:22:13 +02:00

325 lines
9.4 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.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 ArrayList class_fields = new ArrayList ();
// The default handlers of these signals need to be overridden with g_signal_override_class_closure
protected ArrayList virtual_methods = new ArrayList ();
// virtual methods that are generated as an IntPtr in the class struct
protected ArrayList hidden_vms = new ArrayList ();
protected ArrayList interface_vms = new ArrayList ();
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;
class_struct_name = class_elem.GetAttribute ("cname");
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;
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, methods [target_name] as Method, 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 (vm);
else
interface_vms.Add (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_fields_valid;
}
}
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 (VirtualMethod 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 (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;
}
}
}