// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Collections.Generic;
namespace Nova.CodeDOM
{
/// <summary>
/// Represents a reference to a <see cref="TypeParameter"/> (or <see cref="GenericParameter"/>/<see cref="Type"/>) from <b>within</b>
/// the generic type or method declaration that declares it.
/// </summary>
/// <remarks>
/// In contrast, an <see cref="OpenTypeParameterRef"/> represents a reference to a <see cref="TypeParameter"/> (or <see cref="GenericParameter"/>
/// /<see cref="Type"/>) from <b>outside</b> the generic type or method declaration that declares it, and is a temporary placeholder that should
/// be resolved to either a concrete type or a <see cref="TypeParameterRef"/>.
/// Like a <see cref="TypeRef"/>, a <see cref="TypeParameterRef"/> can include array ranks. It subclasses <see cref="TypeRef"/> in order to
/// provide this functionality, although it doesn't support type arguments.
/// </remarks>
public class TypeParameterRef : TypeRef
{
#region /* CONSTRUCTORS */
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="TypeParameter"/>.
/// </summary>
public TypeParameterRef(TypeParameter declaration, bool isFirstOnLine, List<int> arrayRanks)
: base(declaration, isFirstOnLine, null, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="TypeParameter"/>.
/// </summary>
public TypeParameterRef(TypeParameter declaration, bool isFirstOnLine)
: base(declaration, isFirstOnLine)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="TypeParameter"/>.
/// </summary>
public TypeParameterRef(TypeParameter declaration)
: base(declaration, false)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="TypeParameter"/>.
/// </summary>
public TypeParameterRef(TypeParameter declaration, List<int> arrayRanks)
: base(declaration, false, null, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="TypeParameter"/>.
/// </summary>
public TypeParameterRef(TypeParameter declaration, bool isFirstOnLine, params int[] arrayRanks)
: base(declaration, isFirstOnLine, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="TypeParameter"/>.
/// </summary>
public TypeParameterRef(TypeParameter declaration, params int[] arrayRanks)
: base(declaration, false, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="GenericParameter"/>.
/// </summary>
public TypeParameterRef(GenericParameter genericParameter, bool isFirstOnLine, List<int> arrayRanks)
: base(genericParameter, isFirstOnLine, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="GenericParameter"/>.
/// </summary>
public TypeParameterRef(GenericParameter genericParameter, bool isFirstOnLine)
: base(genericParameter, isFirstOnLine)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="GenericParameter"/>.
/// </summary>
public TypeParameterRef(GenericParameter genericParameter)
: base(genericParameter, false)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="GenericParameter"/>.
/// </summary>
public TypeParameterRef(GenericParameter genericParameter, List<int> arrayRanks)
: base(genericParameter, false, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="GenericParameter"/>.
/// </summary>
public TypeParameterRef(GenericParameter genericParameter, bool isFirstOnLine, params int[] arrayRanks)
: base(genericParameter, isFirstOnLine, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="GenericParameter"/>.
/// </summary>
public TypeParameterRef(GenericParameter genericParameter, params int[] arrayRanks)
: base(genericParameter, false, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="Type"/> (which must be a generic parameter).
/// </summary>
public TypeParameterRef(Type type, bool isFirstOnLine, List<int> arrayRanks)
: base(type, isFirstOnLine, null, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="Type"/> (which must be a generic parameter).
/// </summary>
public TypeParameterRef(Type type, bool isFirstOnLine)
: base(type, isFirstOnLine)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="Type"/> (which must be a generic parameter).
/// </summary>
public TypeParameterRef(Type type)
: base(type, false)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="Type"/> (which must be a generic parameter).
/// </summary>
public TypeParameterRef(Type type, List<int> arrayRanks)
: base(type, false, null, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="Type"/> (which must be a generic parameter).
/// </summary>
public TypeParameterRef(Type type, bool isFirstOnLine, params int[] arrayRanks)
: base(type, isFirstOnLine, arrayRanks)
{ }
/// <summary>
/// Construct a <see cref="TypeParameterRef"/> from a <see cref="Type"/> (which must be a generic parameter).
/// </summary>
public TypeParameterRef(Type type, params int[] arrayRanks)
: base(type, false, arrayRanks)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// The name of the <see cref="SymbolicRef"/>.
/// </summary>
public override string Name
{
get
{
if (_reference is TypeParameter)
return ((TypeParameter)_reference).Name;
if (_reference is GenericParameter)
return ((GenericParameter)_reference).Name;
return ((Type)_reference).Name;
}
}
/// <summary>
/// Always <c>false</c>.
/// </summary>
public override bool IsDelegateType
{
get { return false; }
}
/// <summary>
/// Always <c>true</c>.
/// </summary>
public override bool IsPossibleDelegateType
{
get { return true; }
}
/// <summary>
/// Always <c>true</c>.
/// </summary>
public override bool IsGenericParameter
{
get { return true; }
}
/// <summary>
/// True if the referenced type is a value type.
/// </summary>
public override bool IsValueType
{
get
{
// Determine value vs reference type based upon any constraints
if (_reference is TypeParameter)
{
List<TypeParameterConstraint> constraints = ((TypeParameter)_reference).GetConstraints();
if (constraints != null && constraints.Count > 0)
{
foreach (TypeParameterConstraint constraint in constraints)
{
if (constraint is ClassConstraint)
return false;
if (constraint is StructConstraint)
return true;
if (constraint is TypeConstraint)
{
TypeRef typeRef = ((TypeConstraint)constraint).Type.SkipPrefixes() as TypeRef;
if (typeRef != null && typeRef.IsValueType)
return true;
}
}
}
}
else if (_reference is GenericParameter)
{
GenericParameterAttributes attributes = ((GenericParameter)_reference).Attributes;
if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) // class constraint
return false;
if (attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) // struct constraint
return true;
Collection<TypeReference> constraintTypes = ((GenericParameter)_reference).Constraints;
if (constraintTypes != null && constraintTypes.Count > 0)
return Enumerable.Any(constraintTypes, delegate(TypeReference constraintType) { return constraintType.IsValueType; });
}
else //if (_reference is Type)
{
System.Reflection.GenericParameterAttributes attributes = ((Type)_reference).GenericParameterAttributes;
if (attributes.HasFlag(System.Reflection.GenericParameterAttributes.ReferenceTypeConstraint)) // class constraint
return false;
if (attributes.HasFlag(System.Reflection.GenericParameterAttributes.NotNullableValueTypeConstraint)) // struct constraint
return true;
Type[] constraints = ((Type)_reference).GetGenericParameterConstraints();
if (constraints.Length > 0)
return Enumerable.Any(constraints, delegate(Type constraintType) { return constraintType.IsValueType; });
}
return false;
}
}
/// <summary>
/// Always <c>false</c>.
/// </summary>
public override bool IsConst
{
get { return false; }
}
/// <summary>
/// The type argument <see cref="Expression"/>s of the reference (if any).
/// </summary>
public override ChildList<Expression> TypeArguments
{
get { return null; }
set
{
if (value != null)
throw new Exception("Can't set type arguments on a TypeParameterRef!");
}
}
#endregion
#region /* METHODS */
/// <summary>
/// Determine if the current reference refers to the same code object as the specified reference.
/// </summary>
public override bool IsSameRef(SymbolicRef symbolicRef)
{
if (!(symbolicRef is TypeParameterRef))
return false;
TypeParameterRef typeParameterRef = (TypeParameterRef)symbolicRef;
object reference = GetReferencedType();
object typeParameterRefReference = typeParameterRef.GetReferencedType();
if (reference != typeParameterRefReference)
{
// We also have to consider the references the same if the Name and NamespaceNames match.
// This can occur when types are present both in an assembly reference, and also as CodeDOM
// objects (either in the current project, or a referenced project). This can occur if one
// or both types are in a project with assembly references instead of project references to
// a type that is defined in the current solution, which isn't uncommon - it will occur in
// "master" solutions that include projects that are also used in other solutions, and also
// if a project in the solution uses a non-supported language and so is referenced by its
// assembly.
if (Name != typeParameterRef.Name || NamespaceName != typeParameterRef.NamespaceName)
return false;
// With type parameters, the declaring (parent) type names must also match for the type
// parameters to be considered the same.
string declaringTypeName = GetDeclaringTypeOrMethodName(reference);
string typeParameterRefDeclaringTypeName = typeParameterRef.GetDeclaringTypeOrMethodName();
if (declaringTypeName == null || typeParameterRefDeclaringTypeName == null
|| declaringTypeName != typeParameterRefDeclaringTypeName)
return false;
}
return HasSameArrayRanks(typeParameterRef);
}
/// <summary>
/// Calculate a hash code for the referenced object which is the same for all references where IsSameRef() is true.
/// </summary>
public override int GetIsSameRefHashCode()
{
// Make the hash codes as unique as possible while still ensuring that they are identical
// for any objects for which IsSameRef() returns true.
int hashCode = Name.GetHashCode();
string namespaceName = NamespaceName;
if (namespaceName != null) hashCode ^= namespaceName.GetHashCode();
if (_arrayRanks != null)
{
foreach (int rank in _arrayRanks)
hashCode = (hashCode << 1) ^ rank;
}
return hashCode;
}
/// <summary>
/// Get the actual type reference (TypeParameter or Type).
/// </summary>
/// <returns>The TypeParameter or Type.</returns>
public override object GetReferencedType()
{
return Reference;
}
/// <summary>
/// Get the declaring generic type or method.
/// </summary>
public TypeRefBase GetDeclaringTypeOrMethod()
{
object reference = GetReferencedType();
if (reference is TypeParameter)
{
CodeObject parent = ((TypeParameter)reference).Parent;
if (parent is GenericMethodDecl)
{
GenericMethodDecl methodDecl = (GenericMethodDecl)parent;
return methodDecl.CreateRef(ChildList<Expression>.CreateListOfNulls(methodDecl.TypeParameterCount));
}
if (parent is ITypeDecl)
{
ITypeDecl typeDecl = (ITypeDecl)parent;
return typeDecl.CreateRef(ChildList<Expression>.CreateListOfNulls(typeDecl.TypeParameterCount));
}
}
else if (reference is GenericParameter)
{
GenericParameter genericParameter = (GenericParameter)reference;
IGenericParameterProvider owner = genericParameter.Owner;
if (owner is MethodReference)
return MethodRef.Create((MethodReference)owner);
return Create((TypeReference)owner);
}
else if (reference is Type)
{
Type typeParameter = (Type)reference;
if (typeParameter.DeclaringMethod != null)
return MethodRef.Create(typeParameter.DeclaringMethod);
Type declaringType = ((Type)reference).DeclaringType;
return (declaringType != null ? Create(declaringType) : null);
}
return null;
}
/// <summary>
/// Get the declaring generic type (returns null if the parent is a method).
/// </summary>
public override TypeRefBase GetDeclaringType()
{
return GetDeclaringTypeOrMethod() as TypeRef;
}
/// <summary>
/// Get the name of the declaring generic type or method.
/// </summary>
public string GetDeclaringTypeOrMethodName()
{
return GetDeclaringTypeOrMethodName(GetReferencedType());
}
/// <summary>
/// Get the name of the declaring generic type or method for the specified type parameter
/// (<see cref="TypeParameter"/> or <see cref="Type"/>).
/// </summary>
protected static string GetDeclaringTypeOrMethodName(object reference)
{
if (reference is TypeParameter)
{
CodeObject parent = ((TypeParameter)reference).Parent;
if (parent is INamedCodeObject)
return ((INamedCodeObject)parent).Name;
}
else if (reference is GenericParameter)
{
GenericParameter genericParameter = (GenericParameter)reference;
IGenericParameterProvider owner = genericParameter.Owner;
if (owner is MethodReference)
return ((MethodReference)owner).Name;
return ((TypeReference)owner).Name;
}
else if (reference is Type)
{
Type typeParameter = (Type)reference;
if (typeParameter.DeclaringMethod != null)
return typeParameter.DeclaringMethod.Name;
if (typeParameter.DeclaringType != null)
return typeParameter.DeclaringType.Name;
}
return null;
}
/// <summary>
/// Get the type constraints (if any) for this type parameter.
/// </summary>
public List<TypeRefBase> GetTypeConstraints()
{
List<TypeRefBase> constraintTypeRefs = null;
if (_reference is TypeParameter)
{
List<TypeParameterConstraint> constraints = ((TypeParameter)_reference).GetConstraints();
if (constraints != null)
{
foreach (TypeParameterConstraint constraint in constraints)
{
if (constraint is TypeConstraint)
{
if (constraintTypeRefs == null)
constraintTypeRefs = new List<TypeRefBase>();
constraintTypeRefs.Add(((TypeConstraint)constraint).Type.SkipPrefixes() as TypeRefBase);
}
}
}
}
else if (_reference is GenericParameter)
{
Collection<TypeReference> constraintTypes = ((GenericParameter)_reference).Constraints;
if (constraintTypes != null && constraintTypes.Count > 0)
{
constraintTypeRefs = new List<TypeRefBase>();
foreach (TypeReference typeConstraint in constraintTypes)
constraintTypeRefs.Add(Create(typeConstraint));
}
}
else //if (_reference is Type)
{
Type[] constraintTypes = ((Type)_reference).GetGenericParameterConstraints();
if (constraintTypes.Length > 0)
{
constraintTypeRefs = new List<TypeRefBase>();
foreach (Type typeConstraint in constraintTypes)
constraintTypeRefs.Add(Create(typeConstraint));
}
}
return constraintTypeRefs;
}
#endregion
}
}