1 // GLib.Type.cs - GLib GType class implementation
2 //
3 // Author: Mike Kestner <mkestner@speakeasy.net>
4 //
5 // Copyright (c) 2003 Mike Kestner
6 // Copyright (c) 2003 Novell, Inc.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of version 2 of the Lesser GNU General
10 // Public License as published by the Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // Lesser General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this program; if not, write to the
19 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 // Boston, MA 02111-1307, USA.
21 
22 
23 namespace GLib {
24 
25 	using System;
26 	using System.Collections;
27 	using System.Collections.Generic;
28 	using System.IO;
29 	using System.Reflection;
30 	using System.Runtime.InteropServices;
31 
32 	[StructLayout(LayoutKind.Sequential)]
33 	public struct GType : IEquatable<GType> {
34 
35 		IntPtr val;
36 
GTypeGLib.GType37 		public GType (IntPtr val)
38 		{
39 			this.val = val;
40 		}
41 
FromNameGLib.GType42 		public static GType FromName (string native_name)
43 		{
44 			return new GType (g_type_from_name (native_name));
45 		}
46 
47 		public static readonly GType Invalid = new GType ((IntPtr) TypeFundamentals.TypeInvalid);
48 		public static readonly GType None = new GType ((IntPtr) TypeFundamentals.TypeNone);
49 		public static readonly GType Interface = new GType ((IntPtr) TypeFundamentals.TypeInterface);
50 		public static readonly GType Char = new GType ((IntPtr) TypeFundamentals.TypeChar);
51 		public static readonly GType UChar = new GType ((IntPtr) TypeFundamentals.TypeUChar);
52 		public static readonly GType Boolean = new GType ((IntPtr) TypeFundamentals.TypeBoolean);
53 		public static readonly GType Int = new GType ((IntPtr) TypeFundamentals.TypeInt);
54 		public static readonly GType UInt = new GType ((IntPtr) TypeFundamentals.TypeUInt);
55 		public static readonly GType Long = new GType ((IntPtr) TypeFundamentals.TypeLong);
56 		public static readonly GType ULong = new GType ((IntPtr) TypeFundamentals.TypeULong);
57 		public static readonly GType Int64 = new GType ((IntPtr) TypeFundamentals.TypeInt64);
58 		public static readonly GType UInt64 = new GType ((IntPtr) TypeFundamentals.TypeUInt64);
59 		public static readonly GType Enum = new GType ((IntPtr) TypeFundamentals.TypeEnum);
60 		public static readonly GType Flags = new GType ((IntPtr) TypeFundamentals.TypeFlags);
61 		public static readonly GType Float = new GType ((IntPtr) TypeFundamentals.TypeFloat);
62 		public static readonly GType Double = new GType ((IntPtr) TypeFundamentals.TypeDouble);
63 		public static readonly GType String = new GType ((IntPtr) TypeFundamentals.TypeString);
64 		public static readonly GType Pointer = new GType ((IntPtr) TypeFundamentals.TypePointer);
65 		public static readonly GType Boxed = new GType ((IntPtr) TypeFundamentals.TypeBoxed);
66 		public static readonly GType Param = new GType ((IntPtr) TypeFundamentals.TypeParam);
67 		public static readonly GType Object = new GType ((IntPtr) TypeFundamentals.TypeObject);
68 
69 		static Dictionary<IntPtr, Type> types = new Dictionary<IntPtr, Type> (IntPtrEqualityComparer.Instance);
70 		static Dictionary<Type, GType> gtypes = new Dictionary<Type, GType> ();
71 
RegisterGLib.GType72 		public static void Register (GType native_type, System.Type type)
73 		{
74 			if (native_type != GType.Pointer && native_type != GType.Boxed && native_type != ManagedValue.GType)
75 				types[native_type.Val] = type;
76 			if (type != null)
77 				gtypes[type] = native_type;
78 		}
79 
80 		[DllImport("libgobject-2.0-0.dll", CallingConvention=CallingConvention.Cdecl)]
g_type_initGLib.GType81 		static extern void g_type_init ();
82 
GTypeGLib.GType83 		static GType ()
84 		{
85 			if (!GLib.Thread.Supported)
86 				GLib.Thread.Init ();
87 
88 			g_type_init ();
89 
90 			Register (GType.Char, typeof (sbyte));
91 			Register (GType.UChar, typeof (byte));
92 			Register (GType.Boolean, typeof (bool));
93 			Register (GType.Int, typeof (int));
94 			Register (GType.UInt, typeof (uint));
95 			Register (GType.Int64, typeof (long));
96 			Register (GType.UInt64, typeof (ulong));
97 			Register (GType.Float, typeof (float));
98 			Register (GType.Double, typeof (double));
99 			Register (GType.String, typeof (string));
100 			Register (GType.Pointer, typeof (IntPtr));
101 			Register (GType.Object, typeof (GLib.Object));
102 
103 			// One-way mapping
104 			gtypes[typeof (char)] = GType.UInt;
105 		}
106 
operator GTypeGLib.GType107 		public static explicit operator GType (System.Type type)
108 		{
109 			GType gtype;
110 
111 			if (gtypes.TryGetValue (type, out gtype))
112 				return gtype;
113 
114 			if (type.IsSubclassOf (typeof (GLib.Object))) {
115 				gtype = GLib.Object.LookupGType (type);
116 				Register (gtype, type);
117 				return gtype;
118 			}
119 
120 			if (type.IsEnum) {
121 				GTypeTypeAttribute geattr;
122 				GTypeAttribute gattr;
123 				if ((geattr = (GTypeTypeAttribute)Attribute.GetCustomAttribute (type, typeof (GTypeTypeAttribute), false)) != null) {
124 					gtype = geattr.Type;
125 				} else if ((gattr = (GTypeAttribute)Attribute.GetCustomAttribute (type, typeof (GTypeAttribute), false)) != null) {
126 					// This should never happen for generated code, keep it in place for other users of the API.
127 					var pi = gattr.WrapperType.GetProperty ("GType", BindingFlags.Public | BindingFlags.Static);
128 					gtype = (GType)pi.GetValue (null, null);
129 				} else
130 					gtype = ManagedValue.GType;
131 			} else {
132 				GTypeTypeAttribute geattr;
133 				PropertyInfo pi;
134 				if ((geattr = (GTypeTypeAttribute)Attribute.GetCustomAttribute (type, typeof (GTypeTypeAttribute), false)) != null) {
135 					gtype = geattr.Type;
136 				} else if ((pi = type.GetProperty ("GType", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy)) != null) {
137 					gtype = (GType)pi.GetValue (null, null);
138 				} else if (type.IsSubclassOf (typeof (GLib.Opaque)))
139 					gtype = GType.Pointer;
140 				else
141 					gtype = ManagedValue.GType;
142 			}
143 
144 
145 			Register (gtype, type);
146 			return gtype;
147 		}
148 
GetQualifiedNameGLib.GType149 		static string GetQualifiedName (string cname)
150 		{
151 			for (int i = 1; i < cname.Length; i++) {
152 				if (System.Char.IsUpper (cname[i])) {
153 					if (i == 1 && cname [0] == 'G')
154 						return "GLib." + cname.Substring (1);
155 					else
156 						return cname.Substring (0, i) + "." + cname.Substring (i);
157 				}
158 			}
159 
160 			throw new ArgumentException ("cname is not in NamespaceType format. GType.Register should be called directly for " + cname);
161 		}
162 
operator TypeGLib.GType163 		public static explicit operator Type (GType gtype)
164 		{
165 			return LookupType (gtype.Val);
166 		}
167 
InitGLib.GType168 		public static void Init ()
169 		{
170 			// cctor already calls g_type_init.
171 		}
172 
LookupTypeGLib.GType173 		public static Type LookupType (IntPtr typeid)
174 		{
175 			Type result;
176 			if (types.TryGetValue (typeid, out result))
177 				return result;
178 
179 			string native_name = Marshaller.Utf8PtrToString (g_type_name (typeid));
180 			string type_name = GetQualifiedName (native_name);
181 			Assembly[] assemblies = (Assembly[]) AppDomain.CurrentDomain.GetAssemblies ().Clone ();
182 			foreach (Assembly asm in assemblies) {
183 				result = asm.GetType (type_name);
184 				if (result != null)
185 					break;
186 			}
187 
188 			if (result == null) {
189 				// Because of lazy loading of references, it's possible the type's assembly
190 				// needs to be loaded.  We will look for it by name in the references of
191 				// the currently loaded assemblies.  Hopefully a recursive traversal is
192 				// not needed. We avoid one for now because of problems experienced
193 				// in a patch from bug #400595, and a desire to keep memory usage low
194 				// by avoiding a complete loading of all dependent assemblies.
195 				string ns = type_name.Substring (0, type_name.LastIndexOf ('.'));
196 				string asm_name = ns.ToLower ().Replace ('.', '-') + "-sharp";
197 				foreach (Assembly asm in assemblies) {
198 					foreach (AssemblyName ref_name in asm.GetReferencedAssemblies ()) {
199 						if (ref_name.Name != asm_name)
200 							continue;
201 						try {
202 							string asm_dir = Path.GetDirectoryName (asm.Location);
203 							Assembly ref_asm;
204 							if (File.Exists (Path.Combine (asm_dir, ref_name.Name + ".dll")))
205 								ref_asm = Assembly.LoadFrom (Path.Combine (asm_dir, ref_name.Name + ".dll"));
206 							else
207 								ref_asm = Assembly.Load (ref_name);
208 							result = ref_asm.GetType (type_name);
209 							if (result != null)
210 								break;
211 						} catch (Exception) {
212 							/* Failure to load a referenced assembly is not an error */
213 						}
214 					}
215 					if (result != null)
216 						break;
217 				}
218 			}
219 
220 			Register (new GType (typeid), result);
221 			return result;
222 		}
223 
224 		public IntPtr Val {
225 			get {
226 				return val;
227 			}
228 		}
229 
EqualsGLib.GType230 		public override bool Equals (object o)
231 		{
232 			if (!(o is GType))
233 				return false;
234 
235 			return ((GType) o) == this;
236 		}
237 
EqualsGLib.GType238 		public bool Equals (GType other)
239 		{
240 			return this == other;
241 		}
242 
operator ==GLib.GType243 		public static bool operator == (GType a, GType b)
244 		{
245 			return a.Val == b.Val;
246 		}
247 
operator !=GLib.GType248 		public static bool operator != (GType a, GType b)
249 		{
250 			return a.Val != b.Val;
251 		}
252 
GetHashCodeGLib.GType253 		public override int GetHashCode ()
254 		{
255 			return val.GetHashCode ();
256 		}
257 
258 		[DllImport("libgobject-2.0-0.dll", CallingConvention=CallingConvention.Cdecl)]
g_type_nameGLib.GType259 		static extern IntPtr g_type_name (IntPtr raw);
260 
261 		[DllImport("libgobject-2.0-0.dll", CallingConvention=CallingConvention.Cdecl)]
g_type_from_nameGLib.GType262 		static extern IntPtr g_type_from_name (string name);
263 
ToStringGLib.GType264 		public override string ToString ()
265 		{
266 			return Marshaller.Utf8PtrToString (g_type_name (val));
267 		}
268 	}
269 }
270