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