3ba435d53a6a9e53c9416c560877e0941e60fc64
[mono.git] / mcs / class / referencesource / System / compmod / system / codedom / CodeTypeReference.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="CodeTypeReference.cs" company="Microsoft">
3 // 
4 // <OWNER>Microsoft</OWNER>
5 //     Copyright (c) Microsoft Corporation.  All rights reserved.
6 // </copyright>                                                                
7 //------------------------------------------------------------------------------
8
9 namespace System.CodeDom {
10
11     using System.Diagnostics;
12     using System;
13     using Microsoft.Win32;
14     using System.Collections;
15     using System.Runtime.InteropServices;
16     using System.Runtime.Serialization;
17     using System.Globalization;
18     
19     [ 
20         ComVisible(true), 
21         Serializable,
22         FlagsAttribute
23     ]
24     public enum CodeTypeReferenceOptions {
25         GlobalReference =  0x00000001,
26         GenericTypeParameter = 0x00000002
27     }
28
29     /// <devdoc>
30     ///    <para>
31     ///       Represents a Type
32     ///    </para>
33     /// </devdoc>
34     [
35         ClassInterface(ClassInterfaceType.AutoDispatch),
36         ComVisible(true),
37         Serializable,
38     ]
39     public class CodeTypeReference : CodeObject {
40         private string baseType;
41         [OptionalField] 
42         private bool isInterface;
43         private int arrayRank;
44         private CodeTypeReference arrayElementType;
45         [OptionalField] 
46         private CodeTypeReferenceCollection typeArguments;
47         [OptionalField]
48         private CodeTypeReferenceOptions referenceOptions;
49         [OptionalField]
50         private bool needsFixup = false;
51         
52         public CodeTypeReference() {
53             baseType = string.Empty;
54             this.arrayRank = 0;
55             this.arrayElementType = null;            
56         }
57
58         public CodeTypeReference(Type type) {
59             if (type == null)
60                 throw new ArgumentNullException("type");
61             
62             if (type.IsArray) {
63                 this.arrayRank = type.GetArrayRank();
64                 this.arrayElementType = new CodeTypeReference(type.GetElementType());
65                 this.baseType = null;
66             } else {
67                 InitializeFromType(type);
68                 this.arrayRank = 0;
69                 this.arrayElementType = null;
70             }
71
72             this.isInterface = type.IsInterface;
73         }
74
75         public CodeTypeReference (Type type, CodeTypeReferenceOptions codeTypeReferenceOption) : this(type) {
76             referenceOptions = codeTypeReferenceOption;
77         }
78         
79         public CodeTypeReference (String typeName, CodeTypeReferenceOptions codeTypeReferenceOption) {
80             Initialize(typeName, codeTypeReferenceOption);
81         }
82
83         /// <devdoc>
84         ///    <para>[To be supplied.]</para>
85         /// </devdoc>
86
87         // We support the reflection format for generice type name.
88         // The format is like:
89         //
90         public CodeTypeReference(string typeName) {
91             Initialize(typeName);
92         }
93
94         private void InitializeFromType(Type type) {
95             baseType = type.Name;
96             if (!type.IsGenericParameter) {
97                 Type currentType = type;
98                 while (currentType.IsNested) {
99                     currentType = currentType.DeclaringType;
100                     baseType = currentType.Name + "+" + baseType;
101                 }
102                 if (!String.IsNullOrEmpty(type.Namespace))
103                     baseType = type.Namespace + "." + baseType;
104             }
105
106             // pick up the type arguments from an instantiated generic type but not an open one    
107             if (type.IsGenericType && !type.ContainsGenericParameters) {
108                 Type[] genericArgs = type.GetGenericArguments();
109                 for (int i = 0; i < genericArgs.Length; i++) {
110                     TypeArguments.Add(new CodeTypeReference(genericArgs[i]));
111                 }
112             }
113             else if (!type.IsGenericTypeDefinition) 
114             {
115                 // if the user handed us a non-generic type, but later
116                 // appends generic type arguments, we'll pretend
117                 // it's a generic type for their sake - this is good for
118                 // them if they pass in System.Nullable class when they
119                 // meant the System.Nullable<T> value type.
120                 needsFixup = true;
121             }
122         }
123
124         private void Initialize(string typeName) {
125             Initialize(typeName, this.referenceOptions);
126         }
127
128         private void Initialize(string typeName, CodeTypeReferenceOptions options)
129         {
130             Options = options;
131             if (typeName == null || typeName.Length == 0) {
132                 typeName = typeof(void).FullName;            
133                 this.baseType = typeName;
134                 this.arrayRank = 0;
135                 this.arrayElementType = null;
136                 return;                
137             }
138
139             typeName = RipOffAssemblyInformationFromTypeName(typeName);
140             
141             int end = typeName.Length -1;
142             int current = end;
143             needsFixup = true;      // default to true, and if we find arity or generic type args, we'll clear the flag.
144             
145             // Scan the entire string for valid array tails and store ranks for array tails
146             // we found in a queue.
147             Queue q = new Queue();
148             while(current >= 0) {
149                 int rank = 1;
150                 if( typeName[current--] == ']') {
151                     while(current >=0 && typeName[current] == ',') {
152                         rank++;
153                         current--;
154                     }
155
156                     if( current>=0 && typeName[current] == '[') { // found a valid array tail
157                         q.Enqueue(rank); 
158                         current--;   
159                         end = current; 
160                         continue;
161                     }
162                 }
163                 break;
164             }
165             
166             // Try find generic type arguments
167             current = end;
168             ArrayList typeArgumentList = new ArrayList();
169             Stack subTypeNames = new Stack();
170             if( current > 0 && typeName[current--] == ']') {
171                 needsFixup = false;
172                 int unmatchedRightBrackets = 1;
173                 int subTypeNameEndIndex = end;
174                 
175                 // Try find the matching '[', if we can't find it, we will not try to parse the string
176                 while(current >= 0) {
177                     if( typeName[current] == '[' ) {      
178                         // break if we found matched brackets
179                         if( --unmatchedRightBrackets == 0) break;
180                     }
181                     else if( typeName[current] == ']' ) {                   
182                         ++unmatchedRightBrackets;
183                     }
184                     else if( typeName[current] == ',' && unmatchedRightBrackets == 1) {
185                         //
186                         // Type name can contain nested generic types. Following is an example:
187                         // System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089], 
188                         //          [System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], 
189                         //           mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
190                         // 
191                         // Spliltting by ',' won't work. We need to do first-level split by ','. 
192                         //
193                         if( current + 1 < subTypeNameEndIndex) {
194                             subTypeNames.Push(typeName.Substring(current+1 , subTypeNameEndIndex - current - 1));                            
195                         }
196
197                         subTypeNameEndIndex = current;
198                         
199                     }    
200                     --current;
201                 }
202             
203                 if( current > 0 && (end - current - 1) > 0) { 
204                     // push the last generic type argument name if there is any
205                     if( current + 1 < subTypeNameEndIndex) {
206                         subTypeNames.Push(typeName.Substring(current+1 , subTypeNameEndIndex - current - 1));                                                    
207                     }
208                         
209                     // we found matched brackets and the brackets contains some characters.                    
210                     while( subTypeNames.Count > 0) {
211                         String name = RipOffAssemblyInformationFromTypeName((string)subTypeNames.Pop());                         
212                         typeArgumentList.Add(new CodeTypeReference(name));
213                     }
214                     end = current - 1;
215                 }
216             }
217
218             if( end < 0) {  // this can happen if we have some string like "[...]"
219                 this.baseType = typeName;
220                 return;
221             }
222
223             if (q.Count > 0 ) {             
224                 
225                 CodeTypeReference type = new CodeTypeReference(typeName.Substring(0, end + 1), Options);
226
227                 for(int i = 0; i < typeArgumentList.Count; i++) {
228                     type.TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
229                 }
230
231                 while( q.Count > 1) {
232                     type = new CodeTypeReference( type, (int)q.Dequeue());  
233                 } 
234                 
235                 // we don't need to create a new CodeTypeReference for the last one.
236                 Debug.Assert(q.Count == 1 , "We should have one and only one in the rank queue.");                                
237                 this.baseType = null;
238                 this.arrayRank = (int)q.Dequeue();
239                 this.arrayElementType = type;
240             }
241             else if( typeArgumentList.Count > 0 ) {
242                 for( int i = 0; i < typeArgumentList.Count; i++) {
243                     TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
244                 }
245
246                 this.baseType = typeName.Substring(0, end + 1);
247             }
248             else{
249                 this.baseType = typeName;
250             }
251
252             // Now see if we have some arity.  baseType could be null if this is an array type. 
253             if (baseType != null && baseType.IndexOf('`') != -1)
254                 needsFixup = false;
255             
256         }
257
258         public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments) : this(typeName){
259             if( typeArguments != null && typeArguments.Length > 0) {
260                 TypeArguments.AddRange(typeArguments);
261             }
262         }
263
264         public CodeTypeReference(CodeTypeParameter typeParameter): 
265                 this( (typeParameter == null) ? (string)null : typeParameter.Name) {  
266             referenceOptions = CodeTypeReferenceOptions.GenericTypeParameter; 
267         }
268
269         /// <devdoc>
270         ///    <para>[To be supplied.]</para>
271         /// </devdoc>
272         public CodeTypeReference(string baseType, int rank) {
273             this.baseType = null;
274             this.arrayRank = rank;
275             this.arrayElementType = new CodeTypeReference(baseType);
276         }
277
278         /// <devdoc>
279         ///    <para>[To be supplied.]</para>
280         /// </devdoc>
281         public CodeTypeReference(CodeTypeReference arrayType, int rank) {
282             this.baseType = null;
283             this.arrayRank = rank;
284             this.arrayElementType = arrayType;
285         }
286  
287         /// <devdoc>
288         ///    <para>[To be supplied.]</para>
289         /// </devdoc>
290         public CodeTypeReference ArrayElementType {
291             get {
292                 return arrayElementType;
293             }
294             set {
295                 arrayElementType = value;
296             }
297         }
298
299         /// <devdoc>
300         ///    <para>[To be supplied.]</para>
301         /// </devdoc>
302         public int ArrayRank {
303             get {
304                 return arrayRank;
305             }
306             set {
307                 arrayRank = value;
308             }
309         }
310
311         internal int NestedArrayDepth {
312             get {
313                 
314                 if (arrayElementType == null)
315                     return 0;
316
317                 return 1 + arrayElementType.NestedArrayDepth;
318             }
319         }
320
321         /// <devdoc>
322         ///    <para>[To be supplied.]</para>
323         /// </devdoc>
324         public string BaseType {
325             get {
326                 if (arrayRank > 0 && arrayElementType != null) {
327                     return arrayElementType.BaseType;
328                 }
329                 if (String.IsNullOrEmpty(baseType)) 
330                     return string.Empty;
331                 
332                 string returnType = baseType;
333                 if (needsFixup && TypeArguments.Count > 0)
334                     returnType = returnType + '`' + TypeArguments.Count.ToString(CultureInfo.InvariantCulture);
335
336                 return returnType;
337                 
338             }
339             set {
340                 baseType = value;
341                 Initialize(baseType);
342             }
343         }
344
345         [System.Runtime.InteropServices.ComVisible(false)]
346         public CodeTypeReferenceOptions Options {
347             get { return referenceOptions;}
348             set { referenceOptions = value;}            
349         }
350
351         [System.Runtime.InteropServices.ComVisible(false)]        
352         public CodeTypeReferenceCollection TypeArguments{ 
353             get {
354                 if (arrayRank > 0 && arrayElementType != null) {
355                     return arrayElementType.TypeArguments;
356                 }
357
358                 if( typeArguments == null) {
359                     typeArguments = new CodeTypeReferenceCollection();
360                 }
361                 return typeArguments;
362             }
363         }
364
365         internal bool IsInterface {
366             get {
367                 // Note that this only works correctly if the Type ctor was used. Otherwise, it's always false.
368                 return this.isInterface;
369             }
370         }
371
372         //
373         // The string for generic type argument might contain assembly information and square bracket pair.
374         // There might be leading spaces in front the type name.
375         // Following function will rip off assembly information and brackets 
376         // Following is an example:
377         // " [System.Collections.Generic.List[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral,
378         //   PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]"
379         //
380         private string RipOffAssemblyInformationFromTypeName(string typeName) {
381             int start = 0;
382             int end = typeName.Length - 1;
383             string result = typeName;
384             
385             // skip white space in the beginning
386             while( start < typeName.Length && Char.IsWhiteSpace(typeName[start])) start++;
387             while( end >= 0 && Char.IsWhiteSpace(typeName[end])) end--;
388                     
389             if(start < end) {
390                 if (typeName[start] =='[' && typeName[end] == ']') {  
391                     start++;
392                     end--;
393                 }
394
395                 // if we still have a ] at the end, there's no assembly info. 
396                 if (typeName[end] != ']') {
397                     int commaCount = 0;                            
398                     for(int index = end; index >= start; index--) {
399                         if( typeName[index] == ',') {
400                             commaCount++;
401                             if( commaCount == 4) {
402                                 result = typeName.Substring( start, index - start); 
403                                 break;
404                             }
405                         }
406                     }
407                 }
408             }
409
410             return result;
411         }
412     }
413 }
414