|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CodeDom
{
using System.Diagnostics;
using System;
using Microsoft.Win32;
using System.Collections;
using System.Reflection;
using System.Runtime.InteropServices;
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] // Not available in DNX (NetCore)
private bool _isInterface;
private int _arrayRank;
private CodeTypeReference _arrayElementType;
// [OptionalField] // Not available in DNX (NetCore)
private CodeTypeReferenceCollection _typeArguments;
// [OptionalField] // Not available in DNX (NetCore)
private CodeTypeReferenceOptions _referenceOptions;
// [OptionalField] // Not available in DNX (NetCore)
private bool _needsFixup = false;
public CodeTypeReference()
{
_baseType = string.Empty;
_arrayRank = 0;
_arrayElementType = null;
}
public CodeTypeReference(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsArray)
{
_arrayRank = type.GetArrayRank();
_arrayElementType = new CodeTypeReference(type.GetElementType());
_baseType = null;
}
else
{
InitializeFromType(type);
_arrayRank = 0;
_arrayElementType = null;
}
_isInterface = type.GetTypeInfo().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;
}
TypeInfo info = type.GetTypeInfo();
// pick up the type arguments from an instantiated generic type but not an open one
if (info.IsGenericType && !info.ContainsGenericParameters)
{
Type[] genericArgs = type.GetGenericArguments();
for (int i = 0; i < genericArgs.Length; i++)
{
TypeArguments.Add(new CodeTypeReference(genericArgs[i]));
}
}
else if (!info.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, _referenceOptions);
}
private void Initialize(string typeName, CodeTypeReferenceOptions options)
{
Options = options;
if (typeName == null || typeName.Length == 0)
{
typeName = typeof(void).FullName;
_baseType = typeName;
_arrayRank = 0;
_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 "[...]"
_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.");
_baseType = null;
_arrayRank = (int)q.Dequeue();
_arrayElementType = type;
}
else if (typeArgumentList.Count > 0)
{
for (int i = 0; i < typeArgumentList.Count; i++)
{
TypeArguments.Add((CodeTypeReference)typeArgumentList[i]);
}
_baseType = typeName.Substring(0, end + 1);
}
else
{
_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)
{
_baseType = null;
_arrayRank = rank;
_arrayElementType = new CodeTypeReference(baseType);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public CodeTypeReference(CodeTypeReference arrayType, int rank)
{
_baseType = null;
_arrayRank = rank;
_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();
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 _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;
}
}
}
|