1 // This is a revised source code from CodeDom.
3 //------------------------------------------------------------------------------
4 // <copyright file="CodeTypeReference.cs" company="Microsoft">
6 // <OWNER>[....]</OWNER>
7 // Copyright (c) Microsoft Corporation. All rights reserved.
9 //------------------------------------------------------------------------------
11 namespace System.Runtime.Serialization {
13 using System.Diagnostics;
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;
22 enum CodeTypeReferenceOptions {
23 GlobalReference = 0x00000001,
24 GenericTypeParameter = 0x00000002
27 class CodeTypeReference {
28 private string baseType;
30 private bool isInterface;
31 private int arrayRank;
32 private CodeTypeReference arrayElementType;
34 private List<CodeTypeReference> typeArguments;
36 private CodeTypeReferenceOptions referenceOptions;
38 private bool needsFixup = false;
40 public CodeTypeReference() {
41 baseType = string.Empty;
43 this.arrayElementType = null;
46 public CodeTypeReference(Type type) {
48 throw new ArgumentNullException("type");
51 this.arrayRank = type.GetArrayRank();
52 this.arrayElementType = new CodeTypeReference(type.GetElementType());
55 InitializeFromType(type);
57 this.arrayElementType = null;
60 this.isInterface = type.IsInterface;
63 public CodeTypeReference (Type type, CodeTypeReferenceOptions codeTypeReferenceOption) : this(type) {
64 referenceOptions = codeTypeReferenceOption;
67 public CodeTypeReference (String typeName, CodeTypeReferenceOptions codeTypeReferenceOption) {
68 Initialize(typeName, codeTypeReferenceOption);
72 /// <para>[To be supplied.]</para>
75 // We support the reflection format for generice type name.
76 // The format is like:
78 public CodeTypeReference(string typeName) {
82 private void InitializeFromType(Type type) {
84 if (!type.IsGenericParameter) {
85 Type currentType = type;
86 while (currentType.IsNested) {
87 currentType = currentType.DeclaringType;
88 baseType = currentType.Name + "+" + baseType;
90 if (!String.IsNullOrEmpty(type.Namespace))
91 baseType = type.Namespace + "." + baseType;
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]));
101 else if (!type.IsGenericTypeDefinition)
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.
112 private void Initialize(string typeName) {
113 Initialize(typeName, this.referenceOptions);
116 private void Initialize(string typeName, CodeTypeReferenceOptions options)
119 if (typeName == null || typeName.Length == 0) {
120 typeName = typeof(void).FullName;
121 this.baseType = typeName;
123 this.arrayElementType = null;
127 typeName = RipOffAssemblyInformationFromTypeName(typeName);
129 int end = typeName.Length -1;
131 needsFixup = true; // default to true, and if we find arity or generic type args, we'll clear the flag.
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) {
138 if( typeName[current--] == ']') {
139 while(current >=0 && typeName[current] == ',') {
144 if( current>=0 && typeName[current] == '[') { // found a valid array tail
154 // Try find generic type arguments
156 ArrayList typeArgumentList = new ArrayList();
157 Stack subTypeNames = new Stack();
158 if( current > 0 && typeName[current--] == ']') {
160 int unmatchedRightBrackets = 1;
161 int subTypeNameEndIndex = end;
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;
169 else if( typeName[current] == ']' ) {
170 ++unmatchedRightBrackets;
172 else if( typeName[current] == ',' && unmatchedRightBrackets == 1) {
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]]
179 // Spliltting by ',' won't work. We need to do first-level split by ','.
181 if( current + 1 < subTypeNameEndIndex) {
182 subTypeNames.Push(typeName.Substring(current+1 , subTypeNameEndIndex - current - 1));
185 subTypeNameEndIndex = current;
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));
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));
206 if( end < 0) { // this can happen if we have some string like "[...]"
207 this.baseType = typeName;
213 CodeTypeReference type = new CodeTypeReference(typeName.Substring(0, end + 1), Options);
215 for(int i = 0; i < typeArgumentList.Count; i++) {
216 type.TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
219 while( q.Count > 1) {
220 type = new CodeTypeReference( type, (int)q.Dequeue());
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;
229 else if( typeArgumentList.Count > 0 ) {
230 for( int i = 0; i < typeArgumentList.Count; i++) {
231 TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
234 this.baseType = typeName.Substring(0, end + 1);
237 this.baseType = typeName;
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)
246 public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments) : this(typeName){
247 if( typeArguments != null && typeArguments.Length > 0) {
248 TypeArguments.AddRange(typeArguments);
253 /// <para>[To be supplied.]</para>
255 public CodeTypeReference(string baseType, int rank) {
256 this.baseType = null;
257 this.arrayRank = rank;
258 this.arrayElementType = new CodeTypeReference(baseType);
262 /// <para>[To be supplied.]</para>
264 public CodeTypeReference(CodeTypeReference arrayType, int rank) {
265 this.baseType = null;
266 this.arrayRank = rank;
267 this.arrayElementType = arrayType;
271 /// <para>[To be supplied.]</para>
273 public CodeTypeReference ArrayElementType {
275 return arrayElementType;
278 arrayElementType = value;
283 /// <para>[To be supplied.]</para>
285 public int ArrayRank {
294 internal int NestedArrayDepth {
297 if (arrayElementType == null)
300 return 1 + arrayElementType.NestedArrayDepth;
305 /// <para>[To be supplied.]</para>
307 public string BaseType {
309 if (arrayRank > 0 && arrayElementType != null) {
310 return arrayElementType.BaseType;
312 if (String.IsNullOrEmpty(baseType))
315 string returnType = baseType;
316 if (needsFixup && TypeArguments.Count > 0)
317 returnType = returnType + '`' + TypeArguments.Count.ToString(CultureInfo.InvariantCulture);
324 Initialize(baseType);
328 [System.Runtime.InteropServices.ComVisible(false)]
329 public CodeTypeReferenceOptions Options {
330 get { return referenceOptions;}
331 set { referenceOptions = value;}
334 [System.Runtime.InteropServices.ComVisible(false)]
335 public List<CodeTypeReference> TypeArguments{
337 if (arrayRank > 0 && arrayElementType != null) {
338 return arrayElementType.TypeArguments;
341 if( typeArguments == null) {
342 typeArguments = new List<CodeTypeReference>();
344 return typeArguments;
348 internal bool IsInterface {
350 // Note that this only works correctly if the Type ctor was used. Otherwise, it's always false.
351 return this.isInterface;
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]"
363 private string RipOffAssemblyInformationFromTypeName(string typeName) {
365 int end = typeName.Length - 1;
366 string result = typeName;
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--;
373 if (typeName[start] =='[' && typeName[end] == ']') {
378 // if we still have a ] at the end, there's no assembly info.
379 if (typeName[end] != ']') {
381 for(int index = end; index >= start; index--) {
382 if( typeName[index] == ',') {
384 if( commaCount == 4) {
385 result = typeName.Substring( start, index - start);