// 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.Windows.Media;
using Mono.Cecil;
using Nova.CodeDOM;
namespace Nova.UI
{
/// <summary>
/// The view model for a <see cref="CodeDOM.TypeRef"/>.
/// </summary>
public class TypeRefVM : TypeRefBaseVM
{
#region /* STATICS */
internal static void AddViewModelMapping()
{
CreateViewModel.Add(typeof(TypeRef),
delegate(CodeObject codeObject, bool isDescription, Dictionary<CodeObject, CodeObjectVM> dictionary) { return new TypeRefVM((TypeRef)codeObject, true, dictionary); });
}
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create a view model instance for the specified <see cref="CodeDOM.TypeRef"/>.
/// </summary>
public TypeRefVM(TypeRef typeRef, bool createTypeArgumentVMs, Dictionary<CodeObject, CodeObjectVM> dictionary)
: base(typeRef, createTypeArgumentVMs, dictionary)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// The underlying <see cref="CodeDOM.TypeRef"/> model.
/// </summary>
public TypeRef TypeRef
{
get { return (TypeRef)CodeObject; }
}
#endregion
#region /* METHODS */
#endregion
#region /* RENDERING */
protected override Brush RenderBrush(RenderFlags flags)
{
return TYPE_BRUSH;
}
public override void RenderExpression(CodeRenderer renderer, RenderFlags flags)
{
RenderType(renderer, _typeArgumentVMs, flags, this);
}
public void RenderType(CodeRenderer renderer, List<ExpressionVM> typeArgumentVMs, RenderFlags flags, CodeObjectVM tag)
{
// If it's a nested type, and we have no dot prefix, and the ShowParentTypes flag is set, then
// render all parent types with appropriate type arguments (this shouldn't occur in display of
// code, but only when displaying an evaluated type reference, such as in a tooltip).
// Nova will include all type arguments for any parent types in a reference to a nested type
// (as .NET Reflection also does). Such parent type arguments are ignored for display purposes
// if the TypeRef is displayed in code that includes parent type prefixes or is within the scope
// of the parent generic type.
// Also do this for GenericParameters (which are treated as 'nested' by reflection, but not Mono Cecil).
if (TypeRef.IsNested || TypeRef.IsGenericParameter)
{
TypeRefBase typeRefBase = TypeRef.GetDeclaringType();
if (typeRefBase != null)
{
// Recursively render the parent type minus the type arguments that belong to the current type
List<ExpressionVM> parentTypeArgumentVMs = null;
if (typeArgumentVMs != null)
{
int localCount = TypeRef.GetLocalTypeArgumentCount();
int parentCount = typeArgumentVMs.Count - localCount;
if (parentCount > 0)
{
if (localCount == 0)
{
parentTypeArgumentVMs = typeArgumentVMs;
typeArgumentVMs = null;
}
else
{
parentTypeArgumentVMs = typeArgumentVMs.GetRange(0, parentCount);
typeArgumentVMs = typeArgumentVMs.GetRange(parentCount, localCount);
}
}
}
// We must always extract the parent type arguments, but only render the parent types if appropriate
if (!flags.HasFlag(RenderFlags.HasDotPrefix) && flags.HasFlag(RenderFlags.ShowParentTypes) && typeRefBase is TypeRef)
{
// If we're about to render an enclosing type without type parameters, then use the declared
// ones by default. This is necessary for nested Enums, since they are never considered to
// be generic types, and won't have any type arguments even if nested inside generic types.
// Since an Enum can't be instantiated, the declared type arguments are always appropriate.
if (parentTypeArgumentVMs == null || parentTypeArgumentVMs.Count == 0)
parentTypeArgumentVMs = CreateListVM<Expression, ExpressionVM>(((TypeRef)typeRefBase).GetTypeParametersAsArguments());
TypeRefVM typeRefVM = (TypeRefVM)CreateVM(typeRefBase);
typeRefVM.RenderType(renderer, parentTypeArgumentVMs, flags, typeRefVM);
renderer.RenderText(Dot.ParseToken, PUNC_BRUSH, null);
flags |= RenderFlags.HasDotPrefix;
}
}
}
RenderFlags passFlags = flags & ~RenderFlags.Description;
object reference = TypeRef.GetReferencedType();
// Handle references to Types
if (reference is TypeDefinition)
{
// Handle references to TypeDefinitions and GenericParameters
TypeReference typeReference = (TypeReference)reference;
//// If we have array ranks, or were requested to suppress them (by NewArray for jagged
//// arrays), then suppress them in the Type rendering by getting the innermost type.
//if (TypeRef.HasArrayRanks || flags.HasFlag(RenderFlags.SuppressBrackets))
//{
// while (typeDefinition.IsArray)
// typeDefinition = typeDefinition.GetElementType();
//}
// If we have type arguments, override any in the Type itself
if (typeArgumentVMs != null)
{
// Render "Nullable<Type>" as "Type?" if optimizations is on *and* there's no "System." prefix
if (TypeRef.IsNullableType && !flags.HasFlag(RenderFlags.HasDotPrefix))
{
// Render the Nullable type argument
ExpressionVM typeArgumentVM = typeArgumentVMs[0];
typeArgumentVM.Render(renderer, passFlags & ~RenderFlags.ShowParentTypes);
// Render the '?' in the appropriate color
typeArgumentVM = typeArgumentVM.SkipPrefixes();
Brush brush = TYPE_BRUSH;
if (typeArgumentVM is TypeParameterRefVM)
brush = IDENTIFIER_BRUSH;
else if (typeArgumentVM is TypeRefVM)
{
if (((TypeRefVM)typeArgumentVM).TypeRef.IsBuiltInType)
brush = KEYWORD_BRUSH;
}
else if (typeArgumentVM is UnresolvedRefVM)
brush = ERROR_BRUSH;
// Render with a null object tag for now (no special tooltip handling)
renderer.RenderText(TypeRefBase.ParseTokenNullable, brush, null);
}
else
{
RenderTypeReference(renderer, typeReference, passFlags | RenderFlags.SuppressTypeArgs, tag);
RenderTypeArguments(renderer, typeArgumentVMs, flags, tag);
}
}
else
{
// If the TypeRef has no type arguments, we still want to suppress any on the
// type itself - this is valid for bad (incomplete) code.
RenderTypeReference(renderer, typeReference, passFlags | RenderFlags.SuppressTypeArgs, tag);
}
}
else if (reference is Type)
{
Type type = (Type)reference;
// If we have array ranks, or were requested to suppress them (by NewArray for jagged
// arrays), then suppress them in the Type rendering by getting the innermost type.
if (TypeRef.HasArrayRanks || flags.HasFlag(RenderFlags.SuppressBrackets))
{
while (type.IsArray)
type = type.GetElementType();
}
// If we have type arguments, override any in the Type itself
if (typeArgumentVMs != null)
{
// Render "Nullable<Type>" as "Type?" if optimizations is on *and* there's no "System." prefix
if (TypeRef.IsNullableType && !flags.HasFlag(RenderFlags.HasDotPrefix))
{
// Render the Nullable type argument
ExpressionVM typeArgumentVM = typeArgumentVMs[0];
typeArgumentVM.Render(renderer, passFlags & ~RenderFlags.ShowParentTypes);
// Render the '?' in the appropriate color
typeArgumentVM = typeArgumentVM.SkipPrefixes();
Brush brush = TYPE_BRUSH;
if (typeArgumentVM is TypeParameterRefVM)
brush = IDENTIFIER_BRUSH;
else if (typeArgumentVM is TypeRefVM)
{
if (((TypeRefVM)typeArgumentVM).TypeRef.IsBuiltInType)
brush = KEYWORD_BRUSH;
}
else if (typeArgumentVM is UnresolvedRefVM)
brush = ERROR_BRUSH;
// Render with a null object tag for now (no special tooltip handling)
renderer.RenderText(TypeRefBase.ParseTokenNullable, brush, null);
}
else
{
RenderType(renderer, type, passFlags | RenderFlags.SuppressTypeArgs, tag);
RenderTypeArguments(renderer, typeArgumentVMs, flags, tag);
}
}
else
{
// If the TypeRef has no type arguments, we still want to suppress any on the
// type itself - this is valid for bad (incomplete) code.
RenderType(renderer, type, passFlags | RenderFlags.SuppressTypeArgs, tag);
}
}
else // Handle references to TypeDecls, TypeParameters
{
renderer.RenderName(TypeRef.Name, RenderBrush(flags), tag, flags);
RenderTypeArguments(renderer, typeArgumentVMs, flags, tag);
}
// Render any array ranks last
RenderArrayRanks(renderer, passFlags);
// In description mode, also display any constant value (used for evaluated types of constant expressions)
if (flags.HasFlag(RenderFlags.Description) && TypeRef.IsConst)
{
object constantValue = TypeRef.GetConstantValue();
bool isHex = (flags.HasFlag(RenderFlags.FormatAsHex) || (constantValue is EnumConstant && ((EnumConstant)constantValue).IsBitFlags));
LiteralVM.RenderConstantValue(renderer, passFlags, constantValue, isHex, tag);
}
}
#endregion
}
}