GtkSharp/generator/ObjectBase.cs
Bertrand Lorentz 82a957bc9d generator: Switch a lot of collections to their generic counterpart
The additional type information makes the code a bit more safe
and readable.

At least one bug is fixed by this: in ObjectGen, an invalid child
property could still be generated, as it was not removed from the hash
table.

This should cause no real change to the generated code, except maybe in
the order of some members.
2012-11-04 16:58:49 +01:00

327 lines
9.5 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 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;
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 ((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_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 (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 (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;
}
}
}