// ListBase.cs - List base class implementation // // Authors: Mike Kestner // // Copyright (c) 2002 Mike Kestner // Copyright (c) 2005 Novell, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the Lesser 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser 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 GLib { using System; using System.Collections; using System.Runtime.InteropServices; public abstract class ListBase : IDisposable, ICollection, GLib.IWrapper, ICloneable { private IntPtr list_ptr = IntPtr.Zero; private int length = -1; private bool managed = false; internal bool elements_owned = false; protected System.Type element_type = null; abstract internal IntPtr NthData (uint index); abstract internal int Length (IntPtr list); abstract internal void Free (IntPtr list); abstract internal IntPtr Append (IntPtr current, IntPtr raw); abstract internal IntPtr Prepend (IntPtr current, IntPtr raw); internal ListBase (IntPtr list, System.Type element_type, bool owned, bool elements_owned) { list_ptr = list; this.element_type = element_type; managed = owned; this.elements_owned = elements_owned; } ~ListBase () { Dispose (false); } [Obsolete ("Replaced by owned parameter on ctor.")] public bool Managed { set { managed = value; } } public IntPtr Handle { get { return list_ptr; } } public void Append (IntPtr raw) { list_ptr = Append (list_ptr, raw); } public void Append (string item) { this.Append (Marshaller.StringToPtrGStrdup (item)); } public void Append (object item) { this.Append (AllocNativeElement (item)); } public void Prepend (IntPtr raw) { list_ptr = Prepend (list_ptr, raw); } // ICollection public int Count { get { if (length == -1) length = Length (list_ptr); return length; } } public object this [int index] { get { IntPtr data = NthData ((uint) index); object ret = null; ret = DataMarshal (data); return ret; } } // Synchronization could be tricky here. Hmm. public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return null; } } public void CopyTo (Array array, int index) { object[] orig = new object[Count]; int i = 0; foreach (object o in this) orig [i++] = o; orig.CopyTo (array, index); } public class FilenameString { private FilenameString () {} } IntPtr AllocNativeElement (object element) { if (element_type == null) { if (element is IWrapper) return (element as IWrapper).Handle; else return (IntPtr) GCHandle.Alloc (element); } else { if (element_type == typeof (string)) return Marshaller.StringToPtrGStrdup (element as string); else if (element_type == typeof (FilenameString)) return Marshaller.StringToFilenamePtr (element as string); else if (element_type == typeof (IntPtr)) return (IntPtr) GCHandle.Alloc (element); else if (typeof (IWrapper).IsAssignableFrom (element_type)) return (element as IWrapper).Handle; else if (element_type == typeof (int)) return new IntPtr ((int) element); else if (element_type.IsValueType) return Marshaller.StructureToPtrAlloc (element); } return IntPtr.Zero; } internal object DataMarshal (IntPtr data) { object ret = null; if (element_type != null) { if (element_type == typeof (string)) ret = Marshaller.Utf8PtrToString (data); else if (element_type == typeof (FilenameString)) ret = Marshaller.FilenamePtrToString (data); else if (element_type == typeof (IntPtr)) ret = data; else if (element_type.IsSubclassOf (typeof (GLib.Object))) ret = GLib.Object.GetObject (data, false); else if (element_type.IsSubclassOf (typeof (GLib.Opaque))) ret = GLib.Opaque.GetOpaque (data, element_type, elements_owned); else if (element_type == typeof (int)) ret = (int) data; else if (element_type.IsValueType) ret = Marshal.PtrToStructure (data, element_type); else if (element_type.IsInterface) { Type adapter_type = element_type.Assembly.GetType (InterfaceToAdapterTypeName (element_type)); System.Reflection.MethodInfo method = adapter_type.GetMethod ("GetObject", new Type[] {typeof(IntPtr), typeof(bool)}); ret = method.Invoke (null, new object[] {data, false}); } else ret = Activator.CreateInstance (element_type, new object[] {data}); } else if (Object.IsObject (data)) ret = GLib.Object.GetObject (data, false); return ret; } static string InterfaceToAdapterTypeName (Type type) { string fullname = type.Namespace; if (!String.IsNullOrEmpty (fullname)) { fullname += "."; } fullname += type.Name.Substring (1); // IActivatable -> Activatable return fullname + "Adapter"; } delegate void d_g_object_unref(IntPtr item); static d_g_object_unref g_object_unref = FuncLoader.LoadFunction(FuncLoader.GetProcAddress(GLibrary.Load(Library.GObject), "g_object_unref")); public void Empty () { if (elements_owned) { for (uint i = 0; i < Count; i++) { if (typeof (GLib.Opaque).IsAssignableFrom (element_type)) { GLib.Opaque.GetOpaque (NthData (i), element_type, true).Dispose (); } else if (typeof (GLib.IWrapper).IsAssignableFrom (element_type)) { g_object_unref (NthData (i)); } else { Marshaller.Free (NthData (i)); } } } if (managed) FreeList (); } IntPtr GetData (IntPtr current) { // data field is at offset 0 for GList and GSList return Marshal.ReadIntPtr (current); } IntPtr Next (IntPtr current) { // next field follows gpointer data field for GList and GSList return Marshal.ReadIntPtr (current, IntPtr.Size); } private class ListEnumerator : IEnumerator { private IntPtr current = IntPtr.Zero; private ListBase list; public ListEnumerator (ListBase list) { this.list = list; } public object Current { get { IntPtr data = list.GetData (current); object ret = null; ret = list.DataMarshal (data); return ret; } } public bool MoveNext () { if (current == IntPtr.Zero) current = list.list_ptr; else current = list.Next (current); return (current != IntPtr.Zero); } public void Reset () { current = IntPtr.Zero; } } // IEnumerable public IEnumerator GetEnumerator () { return new ListEnumerator (this); } // IDisposable public void Dispose () { Dispose (true); GC.SuppressFinalize (this); } protected virtual void Dispose (bool disposing) { Empty (); } void FreeList () { if (list_ptr != IntPtr.Zero) Free (list_ptr); list_ptr = IntPtr.Zero; length = -1; } // ICloneable abstract public object Clone (); } }