1 //------------------------------------------------------------------------------
2 // <copyright file="CodeTypeReference.cs" company="Microsoft">
4 // <OWNER>Microsoft</OWNER>
5 // Copyright (c) Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
9 namespace System.CodeDom {
11 using System.Diagnostics;
13 using Microsoft.Win32;
14 using System.Collections;
15 using System.Runtime.InteropServices;
16 using System.Runtime.Serialization;
17 using System.Globalization;
24 public enum CodeTypeReferenceOptions {
25 GlobalReference = 0x00000001,
26 GenericTypeParameter = 0x00000002
35 ClassInterface(ClassInterfaceType.AutoDispatch),
39 public class CodeTypeReference : CodeObject {
40 private string baseType;
42 private bool isInterface;
43 private int arrayRank;
44 private CodeTypeReference arrayElementType;
46 private CodeTypeReferenceCollection typeArguments;
48 private CodeTypeReferenceOptions referenceOptions;
50 private bool needsFixup = false;
52 public CodeTypeReference() {
53 baseType = string.Empty;
55 this.arrayElementType = null;
58 public CodeTypeReference(Type type) {
60 throw new ArgumentNullException("type");
63 this.arrayRank = type.GetArrayRank();
64 this.arrayElementType = new CodeTypeReference(type.GetElementType());
67 InitializeFromType(type);
69 this.arrayElementType = null;
72 this.isInterface = type.IsInterface;
75 public CodeTypeReference (Type type, CodeTypeReferenceOptions codeTypeReferenceOption) : this(type) {
76 referenceOptions = codeTypeReferenceOption;
79 public CodeTypeReference (String typeName, CodeTypeReferenceOptions codeTypeReferenceOption) {
80 Initialize(typeName, codeTypeReferenceOption);
84 /// <para>[To be supplied.]</para>
87 // We support the reflection format for generice type name.
88 // The format is like:
90 public CodeTypeReference(string typeName) {
94 private void InitializeFromType(Type type) {
96 if (!type.IsGenericParameter) {
97 Type currentType = type;
98 while (currentType.IsNested) {
99 currentType = currentType.DeclaringType;
100 baseType = currentType.Name + "+" + baseType;
102 if (!String.IsNullOrEmpty(type.Namespace))
103 baseType = type.Namespace + "." + baseType;
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]));
113 else if (!type.IsGenericTypeDefinition)
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.
124 private void Initialize(string typeName) {
125 Initialize(typeName, this.referenceOptions);
128 private void Initialize(string typeName, CodeTypeReferenceOptions options)
131 if (typeName == null || typeName.Length == 0) {
132 typeName = typeof(void).FullName;
133 this.baseType = typeName;
135 this.arrayElementType = null;
139 typeName = RipOffAssemblyInformationFromTypeName(typeName);
141 int end = typeName.Length -1;
143 needsFixup = true; // default to true, and if we find arity or generic type args, we'll clear the flag.
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) {
150 if( typeName[current--] == ']') {
151 while(current >=0 && typeName[current] == ',') {
156 if( current>=0 && typeName[current] == '[') { // found a valid array tail
166 // Try find generic type arguments
168 ArrayList typeArgumentList = new ArrayList();
169 Stack subTypeNames = new Stack();
170 if( current > 0 && typeName[current--] == ']') {
172 int unmatchedRightBrackets = 1;
173 int subTypeNameEndIndex = end;
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;
181 else if( typeName[current] == ']' ) {
182 ++unmatchedRightBrackets;
184 else if( typeName[current] == ',' && unmatchedRightBrackets == 1) {
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]]
191 // Spliltting by ',' won't work. We need to do first-level split by ','.
193 if( current + 1 < subTypeNameEndIndex) {
194 subTypeNames.Push(typeName.Substring(current+1 , subTypeNameEndIndex - current - 1));
197 subTypeNameEndIndex = current;
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));
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));
218 if( end < 0) { // this can happen if we have some string like "[...]"
219 this.baseType = typeName;
225 CodeTypeReference type = new CodeTypeReference(typeName.Substring(0, end + 1), Options);
227 for(int i = 0; i < typeArgumentList.Count; i++) {
228 type.TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
231 while( q.Count > 1) {
232 type = new CodeTypeReference( type, (int)q.Dequeue());
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;
241 else if( typeArgumentList.Count > 0 ) {
242 for( int i = 0; i < typeArgumentList.Count; i++) {
243 TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
246 this.baseType = typeName.Substring(0, end + 1);
249 this.baseType = typeName;
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)
258 public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments) : this(typeName){
259 if( typeArguments != null && typeArguments.Length > 0) {
260 TypeArguments.AddRange(typeArguments);
264 public CodeTypeReference(CodeTypeParameter typeParameter):
265 this( (typeParameter == null) ? (string)null : typeParameter.Name) {
266 referenceOptions = CodeTypeReferenceOptions.GenericTypeParameter;
270 /// <para>[To be supplied.]</para>
272 public CodeTypeReference(string baseType, int rank) {
273 this.baseType = null;
274 this.arrayRank = rank;
275 this.arrayElementType = new CodeTypeReference(baseType);
279 /// <para>[To be supplied.]</para>
281 public CodeTypeReference(CodeTypeReference arrayType, int rank) {
282 this.baseType = null;
283 this.arrayRank = rank;
284 this.arrayElementType = arrayType;
288 /// <para>[To be supplied.]</para>
290 public CodeTypeReference ArrayElementType {
292 return arrayElementType;
295 arrayElementType = value;
300 /// <para>[To be supplied.]</para>
302 public int ArrayRank {
311 internal int NestedArrayDepth {
314 if (arrayElementType == null)
317 return 1 + arrayElementType.NestedArrayDepth;
322 /// <para>[To be supplied.]</para>
324 public string BaseType {
326 if (arrayRank > 0 && arrayElementType != null) {
327 return arrayElementType.BaseType;
329 if (String.IsNullOrEmpty(baseType))
332 string returnType = baseType;
333 if (needsFixup && TypeArguments.Count > 0)
334 returnType = returnType + '`' + TypeArguments.Count.ToString(CultureInfo.InvariantCulture);
341 Initialize(baseType);
345 [System.Runtime.InteropServices.ComVisible(false)]
346 public CodeTypeReferenceOptions Options {
347 get { return referenceOptions;}
348 set { referenceOptions = value;}
351 [System.Runtime.InteropServices.ComVisible(false)]
352 public CodeTypeReferenceCollection TypeArguments{
354 if (arrayRank > 0 && arrayElementType != null) {
355 return arrayElementType.TypeArguments;
358 if( typeArguments == null) {
359 typeArguments = new CodeTypeReferenceCollection();
361 return typeArguments;
365 internal bool IsInterface {
367 // Note that this only works correctly if the Type ctor was used. Otherwise, it's always false.
368 return this.isInterface;
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]"
380 private string RipOffAssemblyInformationFromTypeName(string typeName) {
382 int end = typeName.Length - 1;
383 string result = typeName;
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--;
390 if (typeName[start] =='[' && typeName[end] == ']') {
395 // if we still have a ] at the end, there's no assembly info.
396 if (typeName[end] != ']') {
398 for(int index = end; index >= start; index--) {
399 if( typeName[index] == ',') {
401 if( commaCount == 4) {
402 result = typeName.Substring( start, index - start);