Click here to Skip to main content
15,896,912 members
Articles / Programming Languages / C#

Calculating Metrics and Searching with a CodeDOM (Part 8)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
6 Mar 2013CDDL7 min read 22.1K   684   10  
Calculating metrics on and searching a CodeDOM.
// 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
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions