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 interface ModifierSpec {
36 Type Resolve (Type type);
37 Text.StringBuilder Append (Text.StringBuilder sb);
39 internal class ArraySpec : ModifierSpec
41 // dimensions == 1 and bound, or dimensions > 1 and !bound
45 internal ArraySpec (int dimensions, bool bound)
47 this.dimensions = dimensions;
51 public Type Resolve (Type type)
54 return type.MakeArrayType (1);
55 else if (dimensions == 1)
56 return type.MakeArrayType ();
57 return type.MakeArrayType (dimensions);
60 public Text.StringBuilder Append (Text.StringBuilder sb)
63 return sb.Append ("[*]");
64 return sb.Append ('[')
65 .Append (',', dimensions - 1)
69 public override string ToString ()
71 return Append (new Text.StringBuilder ()).ToString ();
75 internal class PointerSpec : ModifierSpec
79 internal PointerSpec (int pointer_level) {
80 this.pointer_level = pointer_level;
83 public Type Resolve (Type type) {
84 for (int i = 0; i < pointer_level; ++i)
85 type = type.MakePointerType ();
89 public Text.StringBuilder Append (Text.StringBuilder sb)
91 return sb.Append ('*', pointer_level);
94 public override string ToString () {
95 return Append (new Text.StringBuilder ()).ToString ();
100 internal class TypeSpec
103 string assembly_name;
104 List<TypeIdentifier> nested;
105 List<TypeSpec> generic_params;
106 List<ModifierSpec> modifier_spec;
109 string display_fullname; // cache
111 internal bool HasModifiers {
112 get { return modifier_spec != null; }
115 internal bool IsNested {
116 get { return nested != null && nested.Count > 0; }
119 internal bool IsByRef {
120 get { return is_byref; }
123 internal TypeName Name {
127 internal IEnumerable<TypeName> Nested {
132 return EmptyArray<TypeName>.Value;
136 internal IEnumerable<ModifierSpec> Modifiers {
138 if (modifier_spec != null)
139 return modifier_spec;
141 return EmptyArray<ModifierSpec>.Value;
146 internal enum DisplayNameFormat {
152 public override string ToString () {
153 return GetDisplayFullName (DisplayNameFormat.WANT_ASSEMBLY);
157 string GetDisplayFullName (DisplayNameFormat flags)
159 bool wantAssembly = (flags & DisplayNameFormat.WANT_ASSEMBLY) != 0;
160 bool wantModifiers = (flags & DisplayNameFormat.NO_MODIFIERS) == 0;
161 var sb = new Text.StringBuilder(name.DisplayName);
162 if (nested != null) {
163 foreach (var n in nested)
164 sb.Append ('+').Append (n.DisplayName);
167 if (generic_params != null) {
169 for (int i = 0; i < generic_params.Count; ++i) {
172 if (generic_params [i].assembly_name != null)
173 sb.Append ('[').Append (generic_params [i].DisplayFullName).Append (']');
175 sb.Append (generic_params [i].DisplayFullName);
181 GetModifierString (sb);
183 if (assembly_name != null && wantAssembly)
184 sb.Append (", ").Append (assembly_name);
186 return sb.ToString();
189 internal string ModifierString ()
191 return GetModifierString (new Text.StringBuilder ()).ToString ();
194 private Text.StringBuilder GetModifierString (Text.StringBuilder sb)
196 if (modifier_spec != null) {
197 foreach (var md in modifier_spec)
207 internal string DisplayFullName {
209 if (display_fullname == null)
210 display_fullname = GetDisplayFullName (DisplayNameFormat.Default);
211 return display_fullname;
215 internal static TypeSpec Parse (string typeName)
218 if (typeName == null)
219 throw new ArgumentNullException ("typeName");
221 TypeSpec res = Parse (typeName, ref pos, false, true);
222 if (pos < typeName.Length)
223 throw new ArgumentException ("Count not parse the whole type name", "typeName");
227 internal static string EscapeDisplayName(string internalName)
229 // initial capacity = length of internalName.
230 // Maybe we won't have to escape anything.
231 var res = new Text.StringBuilder (internalName.Length);
232 foreach (char c in internalName)
242 res.Append ('\\').Append (c);
249 return res.ToString ();
252 internal static string UnescapeInternalName(string displayName)
254 var res = new Text.StringBuilder (displayName.Length);
255 for (int i = 0; i < displayName.Length; ++i)
257 char c = displayName[i];
259 if (++i < displayName.Length)
263 return res.ToString ();
266 internal static bool NeedsEscaping (string internalName)
268 foreach (char c in internalName)
286 internal Type Resolve (Func<AssemblyName,Assembly> assemblyResolver, Func<Assembly,string,bool,Type> typeResolver, bool throwOnError, bool ignoreCase)
289 if (assemblyResolver == null && typeResolver == null)
290 return Type.GetType (DisplayFullName, throwOnError, ignoreCase);
292 if (assembly_name != null) {
293 if (assemblyResolver != null)
294 asm = assemblyResolver (new AssemblyName (assembly_name));
296 asm = Assembly.Load (assembly_name);
300 throw new FileNotFoundException ("Could not resolve assembly '" + assembly_name + "'");
306 if (typeResolver != null)
307 type = typeResolver (asm, name.DisplayName, ignoreCase);
309 type = asm.GetType (name.DisplayName, false, ignoreCase);
312 throw new TypeLoadException ("Could not resolve type '" + name + "'");
316 if (nested != null) {
317 foreach (var n in nested) {
318 var tmp = type.GetNestedType (n.DisplayName, BindingFlags.Public | BindingFlags.NonPublic);
321 throw new TypeLoadException ("Could not resolve type '" + n + "'");
328 if (generic_params != null) {
329 Type[] args = new Type [generic_params.Count];
330 for (int i = 0; i < args.Length; ++i) {
331 var tmp = generic_params [i].Resolve (assemblyResolver, typeResolver, throwOnError, ignoreCase);
334 throw new TypeLoadException ("Could not resolve type '" + generic_params [i].name + "'");
339 type = type.MakeGenericType (args);
342 if (modifier_spec != null) {
343 foreach (var md in modifier_spec)
344 type = md.Resolve (type);
348 type = type.MakeByRefType ();
353 void AddName (string type_name)
356 name = ParsedTypeIdentifier(type_name);
359 nested = new List <TypeIdentifier> ();
360 nested.Add (ParsedTypeIdentifier(type_name));
364 void AddModifier (ModifierSpec md)
366 if (modifier_spec == null)
367 modifier_spec = new List<ModifierSpec> ();
368 modifier_spec.Add (md);
371 static void SkipSpace (string name, ref int pos)
374 while (p < name.Length && Char.IsWhiteSpace (name [p]))
379 static void BoundCheck (int idx, string s)
382 throw new ArgumentException ("Invalid generic arguments spec", "typeName");
385 static TypeIdentifier ParsedTypeIdentifier (string displayName)
387 return TypeIdentifiers.FromDisplay(displayName);
390 static TypeSpec Parse (string name, ref int p, bool is_recurse, bool allow_aqn)
393 // - On exit p, is updated to pos the current unconsumed character.
395 // - The callee peeks at but does not consume delimiters following
396 // recurisve parse (so for a recursive call like the args of "Foo[P,Q]"
397 // we'll return with p either on ',' or on ']'. If the name was aqn'd
398 // "Foo[[P,assmblystuff],Q]" on return p with be on the ']' just
399 // after the "assmblystuff")
401 // - If allow_aqn is True, assembly qualification is optional.
402 // If allow_aqn is False, assembly qualification is prohibited.
405 bool in_modifiers = false;
406 TypeSpec data = new TypeSpec ();
408 SkipSpace (name, ref pos);
412 for (; pos < name.Length; ++pos) {
413 switch (name [pos]) {
415 data.AddName (name.Substring (name_start, pos - name_start));
416 name_start = pos + 1;
420 data.AddName (name.Substring (name_start, pos - name_start));
421 name_start = pos + 1;
423 if (is_recurse && !allow_aqn) {
431 if (name [pos] != '[' && is_recurse)
432 throw new ArgumentException ("Generic argument can't be byref or pointer type", "typeName");
433 data.AddName (name.Substring (name_start, pos - name_start));
434 name_start = pos + 1;
445 if (name_start < pos)
446 data.AddName (name.Substring (name_start, pos - name_start));
449 for (; pos < name.Length; ++pos) {
451 switch (name [pos]) {
454 throw new ArgumentException ("Can't have a byref of a byref", "typeName");
456 data.is_byref = true;
460 throw new ArgumentException ("Can't have a pointer to a byref type", "typeName");
461 // take subsequent '*'s too
462 int pointer_level = 1;
463 while (pos+1 < name.Length && name[pos+1] == '*') {
467 data.AddModifier (new PointerSpec(pointer_level));
470 if (is_recurse && allow_aqn) {
472 while (end < name.Length && name [end] != ']')
474 if (end >= name.Length)
475 throw new ArgumentException ("Unmatched ']' while parsing generic argument assembly name");
476 data.assembly_name = name.Substring (pos + 1, end - pos - 1).Trim ();
485 data.assembly_name = name.Substring (pos + 1).Trim ();
491 throw new ArgumentException ("Byref qualifier must be the last one of a type", "typeName");
493 if (pos >= name.Length)
494 throw new ArgumentException ("Invalid array/generic spec", "typeName");
495 SkipSpace (name, ref pos);
497 if (name [pos] != ',' && name [pos] != '*' && name [pos] != ']') {//generic args
498 List<TypeSpec> args = new List <TypeSpec> ();
499 if (data.HasModifiers)
500 throw new ArgumentException ("generic args after array spec or pointer type", "typeName");
502 while (pos < name.Length) {
503 SkipSpace (name, ref pos);
504 bool aqn = name [pos] == '[';
506 ++pos; //skip '[' to the start of the type
507 args.Add (Parse (name, ref pos, true, aqn));
508 BoundCheck (pos, name);
510 if (name [pos] == ']')
513 throw new ArgumentException ("Unclosed assembly-qualified type name at " + name[pos], "typeName");
514 BoundCheck (pos, name);
517 if (name [pos] == ']')
519 if (name [pos] == ',')
520 ++pos; // skip ',' to the start of the next arg
522 throw new ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName");
525 if (pos >= name.Length || name [pos] != ']')
526 throw new ArgumentException ("Error parsing generic params spec", "typeName");
527 data.generic_params = args;
528 } else { //array spec
531 while (pos < name.Length && name [pos] != ']') {
532 if (name [pos] == '*') {
534 throw new ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName");
537 else if (name [pos] != ',')
538 throw new ArgumentException ("Invalid character in array spec " + name [pos], "typeName");
543 SkipSpace (name, ref pos);
545 if (pos >= name.Length || name [pos] != ']')
546 throw new ArgumentException ("Error parsing array spec", "typeName");
547 if (dimensions > 1 && bound)
548 throw new ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName");
549 data.AddModifier (new ArraySpec (dimensions, bound));
558 throw new ArgumentException ("Unmatched ']'", "typeName");
560 throw new ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName");
569 internal TypeName TypeNameWithoutModifiers ()
571 return new TypeSpecTypeName (this, false);
574 internal TypeName TypeName {
575 get { return new TypeSpecTypeName (this, true); }
578 private class TypeSpecTypeName : TypeNames.ATypeName, TypeName {
582 internal TypeSpecTypeName (TypeSpec ts, bool wantModifiers)
585 this.want_modifiers = wantModifiers;
588 public override string DisplayName {
591 return ts.DisplayFullName;
593 return ts.GetDisplayFullName (DisplayNameFormat.NO_MODIFIERS);
597 public override TypeName NestedName (TypeIdentifier innerName)
599 return TypeNames.FromDisplay(DisplayName + "+" + innerName.DisplayName);