|
//------------------------------------------------------------------------------
// <copyright file="CodeTypeReference.cs" company="Microsoft">
//
// <OWNER>Microsoft</OWNER>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.CodeDom {
using System.Diagnostics;
using System;
using Microsoft.Win32;
using System.Collections;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Globalization;
[
ComVisible(true),
Serializable,
FlagsAttribute
]
public enum CodeTypeReferenceOptions {
GlobalReference = 0x00000001,
GenericTypeParameter = 0x00000002
}
/// <devdoc>
/// <para>
/// Represents a Type
/// </para>
/// </devdoc>
[
ClassInterface(ClassInterfaceType.AutoDispatch),
ComVisible(true),
Serializable,
]
public class CodeTypeReference : CodeObject {
private string baseType;
[OptionalField]
private bool isInterface;
private int arrayRank;
private CodeTypeReference arrayElementType;
[OptionalField]
private CodeTypeReferenceCollection typeArguments;
[OptionalField]
private CodeTypeReferenceOptions referenceOptions;
[OptionalField]
private bool needsFixup = false;
public CodeTypeReference() {
baseType = string.Empty;
this.arrayRank = 0;
this.arrayElementType = null;
}
public CodeTypeReference(Type type) {
if (type == null)
throw new ArgumentNullException("type");
if (type.IsArray) {
this.arrayRank = type.GetArrayRank();
this.arrayElementType = new CodeTypeReference(type.GetElementType());
this.baseType = null;
} else {
InitializeFromType(type);
this.arrayRank = 0;
this.arrayElementType = null;
}
this.isInterface = type.IsInterface;
}
public CodeTypeReference (Type type, CodeTypeReferenceOptions codeTypeReferenceOption) : this(type) {
referenceOptions = codeTypeReferenceOption;
}
public CodeTypeReference (String typeName, CodeTypeReferenceOptions codeTypeReferenceOption) {
Initialize(typeName, codeTypeReferenceOption);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
// We support the reflection format for generice type name.
// The format is like:
//
public CodeTypeReference(string typeName) {
Initialize(typeName);
}
private void InitializeFromType(Type type) {
baseType = type.Name;
if (!type.IsGenericParameter) {
Type currentType = type;
while (currentType.IsNested) {
currentType = currentType.DeclaringType;
baseType = currentType.Name + "+" + baseType;
}
if (!String.IsNullOrEmpty(type.Namespace))
baseType = type.Namespace + "." + baseType;
}
// pick up the type arguments from an instantiated generic type but not an open one
if (type.IsGenericType && !type.ContainsGenericParameters) {
Type[] genericArgs = type.GetGenericArguments();
for (int i = 0; i < genericArgs.Length; i++) {
TypeArguments.Add(new CodeTypeReference(genericArgs[i]));
}
}
else if (!type.IsGenericTypeDefinition)
{
// if the user handed us a non-generic type, but later
// appends generic type arguments, we'll pretend
// it's a generic type for their sake - this is good for
// them if they pass in System.Nullable class when they
// meant the System.Nullable<T> value type.
needsFixup = true;
}
}
private void Initialize(string typeName) {
Initialize(typeName, this.referenceOptions);
}
private void Initialize(string typeName, CodeTypeReferenceOptions options)
{
Options = options;
if (typeName == null || typeName.Length == 0) {
typeName = typeof(void).FullName;
this.baseType = typeName;
this.arrayRank = 0;
this.arrayElementType = null;
return;
}
typeName = RipOffAssemblyInformationFromTypeName(typeName);
int end = typeName.Length -1;
int current = end;
needsFixup = true; // default to true, and if we find arity or generic type args, we'll clear the flag.
// Scan the entire string for valid array tails and store ranks for array tails
// we found in a queue.
Queue q = new Queue();
while(current >= 0) {
int rank = 1;
if( typeName[current--] == ']') {
while(current >=0 && typeName[current] == ',') {
rank++;
current--;
}
if( current>=0 && typeName[current] == '[') { // found a valid array tail
q.Enqueue(rank);
current--;
end = current;
continue;
}
}
break;
}
// Try find generic type arguments
current = end;
ArrayList typeArgumentList = new ArrayList();
Stack subTypeNames = new Stack();
if( current > 0 && typeName[current--] == ']') {
needsFixup = false;
int unmatchedRightBrackets = 1;
int subTypeNameEndIndex = end;
// Try find the matching '[', if we can't find it, we will not try to parse the string
while(current >= 0) {
if( typeName[current] == '[' ) {
// break if we found matched brackets
if( --unmatchedRightBrackets == 0) break;
}
else if( typeName[current] == ']' ) {
++unmatchedRightBrackets;
}
else if( typeName[current] == ',' && unmatchedRightBrackets == 1) {
//
// Type name can contain nested generic types. Following is an example:
// System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],
// [System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
// mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
//
// Spliltting by ',' won't work. We need to do first-level split by ','.
//
if( current + 1 < subTypeNameEndIndex) {
subTypeNames.Push(typeName.Substring(current+1 , subTypeNameEndIndex - current - 1));
}
subTypeNameEndIndex = current;
}
--current;
}
if( current > 0 && (end - current - 1) > 0) {
// push the last generic type argument name if there is any
if( current + 1 < subTypeNameEndIndex) {
subTypeNames.Push(typeName.Substring(current+1 , subTypeNameEndIndex - current - 1));
}
// we found matched brackets and the brackets contains some characters.
while( subTypeNames.Count > 0) {
String name = RipOffAssemblyInformationFromTypeName((string)subTypeNames.Pop());
typeArgumentList.Add(new CodeTypeReference(name));
}
end = current - 1;
}
}
if( end < 0) { // this can happen if we have some string like "[...]"
this.baseType = typeName;
return;
}
if (q.Count > 0 ) {
CodeTypeReference type = new CodeTypeReference(typeName.Substring(0, end + 1), Options);
for(int i = 0; i < typeArgumentList.Count; i++) {
type.TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
}
while( q.Count > 1) {
type = new CodeTypeReference( type, (int)q.Dequeue());
}
// we don't need to create a new CodeTypeReference for the last one.
Debug.Assert(q.Count == 1 , "We should have one and only one in the rank queue.");
this.baseType = null;
this.arrayRank = (int)q.Dequeue();
this.arrayElementType = type;
}
else if( typeArgumentList.Count > 0 ) {
for( int i = 0; i < typeArgumentList.Count; i++) {
TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
}
this.baseType = typeName.Substring(0, end + 1);
}
else{
this.baseType = typeName;
}
// Now see if we have some arity. baseType could be null if this is an array type.
if (baseType != null && baseType.IndexOf('`') != -1)
needsFixup = false;
}
public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments) : this(typeName){
if( typeArguments != null && typeArguments.Length > 0) {
TypeArguments.AddRange(typeArguments);
}
}
public CodeTypeReference(CodeTypeParameter typeParameter):
this( (typeParameter == null) ? (string)null : typeParameter.Name) {
referenceOptions = CodeTypeReferenceOptions.GenericTypeParameter;
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public CodeTypeReference(string baseType, int rank) {
this.baseType = null;
this.arrayRank = rank;
this.arrayElementType = new CodeTypeReference(baseType);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public CodeTypeReference(CodeTypeReference arrayType, int rank) {
this.baseType = null;
this.arrayRank = rank;
this.arrayElementType = arrayType;
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public CodeTypeReference ArrayElementType {
get {
return arrayElementType;
}
set {
arrayElementType = value;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public int ArrayRank {
get {
return arrayRank;
}
set {
arrayRank = value;
}
}
internal int NestedArrayDepth {
get {
if (arrayElementType == null)
return 0;
return 1 + arrayElementType.NestedArrayDepth;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public string BaseType {
get {
if (arrayRank > 0 && arrayElementType != null) {
return arrayElementType.BaseType;
}
if (String.IsNullOrEmpty(baseType))
return string.Empty;
string returnType = baseType;
if (needsFixup && TypeArguments.Count > 0)
returnType = returnType + '`' + TypeArguments.Count.ToString(CultureInfo.InvariantCulture);
return returnType;
}
set {
baseType = value;
Initialize(baseType);
}
}
[System.Runtime.InteropServices.ComVisible(false)]
public CodeTypeReferenceOptions Options {
get { return referenceOptions;}
set { referenceOptions = value;}
}
[System.Runtime.InteropServices.ComVisible(false)]
public CodeTypeReferenceCollection TypeArguments{
get {
if (arrayRank > 0 && arrayElementType != null) {
return arrayElementType.TypeArguments;
}
if( typeArguments == null) {
typeArguments = new CodeTypeReferenceCollection();
}
return typeArguments;
}
}
internal bool IsInterface {
get {
// Note that this only works correctly if the Type ctor was used. Otherwise, it's always false.
return this.isInterface;
}
}
//
// The string for generic type argument might contain assembly information and square bracket pair.
// There might be leading spaces in front the type name.
// Following function will rip off assembly information and brackets
// Following is an example:
// " [System.Collections.Generic.List[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral,
// PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]"
//
private string RipOffAssemblyInformationFromTypeName(string typeName) {
int start = 0;
int end = typeName.Length - 1;
string result = typeName;
// skip white space in the beginning
while( start < typeName.Length && Char.IsWhiteSpace(typeName[start])) start++;
while( end >= 0 && Char.IsWhiteSpace(typeName[end])) end--;
if(start < end) {
if (typeName[start] =='[' && typeName[end] == ']') {
start++;
end--;
}
// if we still have a ] at the end, there's no assembly info.
if (typeName[end] != ']') {
int commaCount = 0;
for(int index = end; index >= start; index--) {
if( typeName[index] == ',') {
commaCount++;
if( commaCount == 4) {
result = typeName.Substring( start, index - start);
break;
}
}
}
}
}
return result;
}
}
}
|