5 // Rodrigo Kumpera <kumpera@gmail.com>
8 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
32 using System.Reflection;
36 internal class ArraySpec
41 internal ArraySpec (int dimensions, bool bound)
43 this.dimensions = dimensions;
47 internal Type Resolve (Type type)
50 return type.MakeArrayType (1);
51 else if (dimensions == 1)
52 return type.MakeArrayType ();
53 return type.MakeArrayType (dimensions);
57 public override string ToString ()
62 for (int i = 1; i < dimensions; ++i)
69 internal class TypeSpec
71 string name, assembly_name;
73 List<TypeSpec> generic_params;
74 List<ArraySpec> array_spec;
79 get { return array_spec != null; }
83 public override string ToString () {
86 foreach (var n in nested)
90 if (generic_params != null) {
92 for (int i = 0; i < generic_params.Count; ++i) {
95 if (generic_params [i].assembly_name != null)
96 str += "[" + generic_params [i] + "]";
98 str += generic_params [i];
103 if (array_spec != null) {
104 foreach (var ar in array_spec)
108 for (int i = 0; i < pointer_level; ++i)
114 if (assembly_name != null)
115 str += ", " + assembly_name;
121 internal static TypeSpec Parse (string typeName)
124 if (typeName == null)
125 throw new ArgumentNullException ("typeName");
127 TypeSpec res = Parse (typeName, ref pos, false, false);
128 if (pos < typeName.Length)
129 throw new ArgumentException ("Count not parse the whole type name", "typeName");
133 internal Type Resolve (Func<AssemblyName,Assembly> assemblyResolver, Func<Assembly,string,bool,Type> typeResolver, bool throwOnError, bool ignoreCase)
136 if (assemblyResolver == null && typeResolver == null)
137 return Type.GetType (name, throwOnError, ignoreCase);
139 if (assembly_name != null) {
140 if (assemblyResolver != null)
141 asm = assemblyResolver (new AssemblyName (assembly_name));
143 asm = Assembly.Load (assembly_name);
147 throw new FileNotFoundException ("Could not resolve assembly '" + assembly_name + "'");
153 if (typeResolver != null)
154 type = typeResolver (asm, name, ignoreCase);
156 type = asm.GetType (name, false, ignoreCase);
159 throw new TypeLoadException ("Could not resolve type '" + name + "'");
163 if (nested != null) {
164 foreach (var n in nested) {
165 var tmp = type.GetNestedType (n, BindingFlags.Public | BindingFlags.NonPublic);
168 throw new TypeLoadException ("Could not resolve type '" + n + "'");
175 if (generic_params != null) {
176 Type[] args = new Type [generic_params.Count];
177 for (int i = 0; i < args.Length; ++i) {
178 var tmp = generic_params [i].Resolve (assemblyResolver, typeResolver, throwOnError, ignoreCase);
181 throw new TypeLoadException ("Could not resolve type '" + generic_params [i].name + "'");
186 type = type.MakeGenericType (args);
189 if (array_spec != null) {
190 foreach (var arr in array_spec)
191 type = arr.Resolve (type);
194 for (int i = 0; i < pointer_level; ++i)
195 type = type.MakePointerType ();
198 type = type.MakeByRefType ();
203 void AddName (string type_name)
209 nested = new List <string> ();
210 nested.Add (type_name);
214 void AddArray (ArraySpec array)
216 if (array_spec == null)
217 array_spec = new List<ArraySpec> ();
218 array_spec.Add (array);
221 static void SkipSpace (string name, ref int pos)
224 while (p < name.Length && Char.IsWhiteSpace (name [p]))
229 static TypeSpec Parse (string name, ref int p, bool is_recurse, bool allow_aqn)
233 bool in_modifiers = false;
234 TypeSpec data = new TypeSpec ();
236 SkipSpace (name, ref pos);
240 for (; pos < name.Length; ++pos) {
241 switch (name [pos]) {
243 data.AddName (name.Substring (name_start, pos - name_start));
244 name_start = pos + 1;
248 data.AddName (name.Substring (name_start, pos - name_start));
249 name_start = pos + 1;
251 if (is_recurse && !allow_aqn) {
259 if (name [pos] != '[' && is_recurse)
260 throw new ArgumentException ("Generic argument can't be byref or pointer type", "typeName");
261 data.AddName (name.Substring (name_start, pos - name_start));
262 name_start = pos + 1;
270 if (name_start < pos)
271 data.AddName (name.Substring (name_start, pos - name_start));
274 for (; pos < name.Length; ++pos) {
276 switch (name [pos]) {
279 throw new ArgumentException ("Can't have a byref of a byref", "typeName");
281 data.is_byref = true;
285 throw new ArgumentException ("Can't have a pointer to a byref type", "typeName");
286 ++data.pointer_level;
291 while (end < name.Length && name [end] != ']')
293 if (end >= name.Length)
294 throw new ArgumentException ("Unmatched ']' while parsing generic argument assembly name");
295 data.assembly_name = name.Substring (pos + 1, end - pos - 1).Trim ();
299 data.assembly_name = name.Substring (pos + 1).Trim ();
304 throw new ArgumentException ("Byref qualifier must be the last one of a type", "typeName");
306 if (pos >= name.Length)
307 throw new ArgumentException ("Invalid array/generic spec", "typeName");
308 SkipSpace (name, ref pos);
310 if (name [pos] != ',' && name [pos] != '*' && name [pos] != ']') {//generic args
311 List<TypeSpec> args = new List <TypeSpec> ();
313 throw new ArgumentException ("generic args after array spec", "typeName");
315 while (pos < name.Length) {
316 SkipSpace (name, ref pos);
317 bool aqn = name [pos] == '[';
319 ++pos; //skip '[' to the start of the type
320 args.Add (Parse (name, ref pos, true, aqn));
321 if (pos >= name.Length)
322 throw new ArgumentException ("Invalid generic arguments spec", "typeName");
324 if (name [pos] == ']')
326 if (name [pos] == ',')
327 ++pos; // skip ',' to the start of the next arg
329 throw new ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName");
332 if (pos >= name.Length || name [pos] != ']')
333 throw new ArgumentException ("Error parsing generic params spec", "typeName");
334 data.generic_params = args;
335 } else { //array spec
338 while (pos < name.Length && name [pos] != ']') {
339 if (name [pos] == '*') {
341 throw new ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName");
344 else if (name [pos] != ',')
345 throw new ArgumentException ("Invalid character in array spec " + name [pos], "typeName");
350 SkipSpace (name, ref pos);
352 if (name [pos] != ']')
353 throw new ArgumentException ("Error parsing array spec", "typeName");
354 if (dimensions > 1 && bound)
355 throw new ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName");
356 data.AddArray (new ArraySpec (dimensions, bound));
365 throw new ArgumentException ("Unmatched ']'", "typeName");
367 throw new ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName");