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;
35 internal class ArraySpec
40 internal ArraySpec (int dimensions, bool bound)
42 this.dimensions = dimensions;
46 internal Type Resolve (Type type)
49 return type.MakeArrayType (1);
50 else if (dimensions == 1)
51 return type.MakeArrayType ();
52 return type.MakeArrayType (dimensions);
56 public override string ToString ()
61 for (int i = 1; i < dimensions; ++i)
68 internal class TypeSpec
70 string name, assembly_name;
72 List<TypeSpec> generic_params;
73 List<ArraySpec> array_spec;
78 get { return array_spec != null; }
82 public override string ToString () {
85 foreach (var n in nested)
89 if (generic_params != null) {
91 for (int i = 0; i < generic_params.Count; ++i) {
94 if (generic_params [i].assembly_name != null)
95 str += "[" + generic_params [i] + "]";
97 str += generic_params [i];
102 if (array_spec != null) {
103 foreach (var ar in array_spec)
107 for (int i = 0; i < pointer_level; ++i)
113 if (assembly_name != null)
114 str += ", " + assembly_name;
120 internal static TypeSpec Parse (string typeName)
123 if (typeName == null)
124 throw new ArgumentNullException ("typeName");
126 TypeSpec res = Parse (typeName, ref pos, false, false);
127 if (pos < typeName.Length)
128 throw new ArgumentException ("Count not parse the whole type name", "typeName");
132 internal Type Resolve (Func<AssemblyName,Assembly> assemblyResolver, Func<Assembly,string,bool,Type> typeResolver, bool throwOnError, bool ignoreCase)
135 if (assemblyResolver == null && typeResolver == null)
136 return Type.GetType (name, throwOnError, ignoreCase);
138 if (assembly_name != null) {
139 if (assemblyResolver != null)
140 asm = assemblyResolver (new AssemblyName (assembly_name));
142 asm = Assembly.Load (assembly_name);
146 throw new FileNotFoundException ("Could not resolve assembly '" + assembly_name + "'");
152 if (typeResolver != null)
153 type = typeResolver (asm, name, ignoreCase);
155 type = asm.GetType (name, false, ignoreCase);
158 throw new TypeLoadException ("Could not resolve type '" + name + "'");
162 if (nested != null) {
163 foreach (var n in nested) {
164 var tmp = type.GetNestedType (n, BindingFlags.Public | BindingFlags.NonPublic);
167 throw new TypeLoadException ("Could not resolve type '" + n + "'");
174 if (generic_params != null) {
175 Type[] args = new Type [generic_params.Count];
176 for (int i = 0; i < args.Length; ++i) {
177 var tmp = generic_params [i].Resolve (assemblyResolver, typeResolver, throwOnError, ignoreCase);
180 throw new TypeLoadException ("Could not resolve type '" + generic_params [i].name + "'");
185 type = type.MakeGenericType (args);
188 if (array_spec != null) {
189 foreach (var arr in array_spec)
190 type = arr.Resolve (type);
193 for (int i = 0; i < pointer_level; ++i)
194 type = type.MakePointerType ();
197 type = type.MakeByRefType ();
202 void AddName (string type_name)
208 nested = new List <string> ();
209 nested.Add (type_name);
213 void AddArray (ArraySpec array)
215 if (array_spec == null)
216 array_spec = new List<ArraySpec> ();
217 array_spec.Add (array);
220 static void SkipSpace (string name, ref int pos)
223 while (p < name.Length && Char.IsWhiteSpace (name [p]))
228 static TypeSpec Parse (string name, ref int p, bool is_recurse, bool allow_aqn)
232 bool in_modifiers = false;
233 TypeSpec data = new TypeSpec ();
235 SkipSpace (name, ref pos);
239 for (; pos < name.Length; ++pos) {
240 switch (name [pos]) {
242 data.AddName (name.Substring (name_start, pos - name_start));
243 name_start = pos + 1;
247 data.AddName (name.Substring (name_start, pos - name_start));
248 name_start = pos + 1;
250 if (is_recurse && !allow_aqn) {
258 if (name [pos] != '[' && is_recurse)
259 throw new ArgumentException ("Generic argument can't be byref or pointer type", "typeName");
260 data.AddName (name.Substring (name_start, pos - name_start));
261 name_start = pos + 1;
269 if (name_start < pos)
270 data.AddName (name.Substring (name_start, pos - name_start));
273 for (; pos < name.Length; ++pos) {
275 switch (name [pos]) {
278 throw new ArgumentException ("Can't have a byref of a byref", "typeName");
280 data.is_byref = true;
284 throw new ArgumentException ("Can't have a pointer to a byref type", "typeName");
285 ++data.pointer_level;
290 while (end < name.Length && name [end] != ']')
292 if (end >= name.Length)
293 throw new ArgumentException ("Unmatched ']' while parsing generic argument assembly name");
294 data.assembly_name = name.Substring (pos + 1, end - pos - 1).Trim ();
298 data.assembly_name = name.Substring (pos + 1).Trim ();
303 throw new ArgumentException ("Byref qualifier must be the last one of a type", "typeName");
305 if (pos >= name.Length)
306 throw new ArgumentException ("Invalid array/generic spec", "typeName");
307 SkipSpace (name, ref pos);
309 if (name [pos] != ',' && name [pos] != '*' && name [pos] != ']') {//generic args
310 List<TypeSpec> args = new List <TypeSpec> ();
312 throw new ArgumentException ("generic args after array spec", "typeName");
314 while (pos < name.Length) {
315 SkipSpace (name, ref pos);
316 bool aqn = name [pos] == '[';
318 ++pos; //skip '[' to the start of the type
319 args.Add (Parse (name, ref pos, true, aqn));
320 if (pos >= name.Length)
321 throw new ArgumentException ("Invalid generic arguments spec", "typeName");
323 if (name [pos] == ']')
325 if (name [pos] == ',')
326 ++pos; // skip ',' to the start of the next arg
328 throw new ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName");
331 if (pos >= name.Length || name [pos] != ']')
332 throw new ArgumentException ("Error parsing generic params spec", "typeName");
333 data.generic_params = args;
334 } else { //array spec
337 while (pos < name.Length && name [pos] != ']') {
338 if (name [pos] == '*') {
340 throw new ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName");
343 else if (name [pos] != ',')
344 throw new ArgumentException ("Invalid character in array spec " + name [pos], "typeName");
349 SkipSpace (name, ref pos);
351 if (name [pos] != ']')
352 throw new ArgumentException ("Error parsing array spec", "typeName");
353 if (dimensions > 1 && bound)
354 throw new ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName");
355 data.AddArray (new ArraySpec (dimensions, bound));
364 throw new ArgumentException ("Unmatched ']'", "typeName");
366 throw new ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName");