Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Resolving Symbolic References in a CodeDOM (Part 7)

, 2 Dec 2012
Resolving symbolic references in a CodeDOM.
Nova.0.6.exe.zip
Nova.0.6.zip
Nova.CLI
Properties
Nova.CodeDOM
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Interfaces
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Assemblies
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Parsing
Base
Properties
Rendering
Resolving
Utilities
Mono.Cecil
Reflection
Nova.Examples
Properties
Nova.Studio
Images
About.png
Configuration.png
EditCopy.png
EditCut.png
EditDelete.png
EditPaste.png
EditRedo.png
EditUndo.png
Error.png
Exit.png
FileNew.png
FileOpen.png
FileSave.png
FileSaveAll.png
FileSaveAs.png
Find.png
Help.png
Info.png
Logo.png
Options.png
Print.png
PrintPreview.png
Properties.png
Todo.png
Warning.png
Objects.ico
Properties
Settings.settings
Nova.Test
Properties
Nova.UI
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Properties
Resolving
Utilties
// 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)

Share

About the Author

KenBeckett
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).

| Advertise | Privacy | Mobile
Web02 | 2.8.140814.1 | Last Updated 2 Dec 2012
Article Copyright 2012 by KenBeckett
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid