Click here to Skip to main content
15,889,838 members
Articles / Programming Languages / C#

Resolving Symbolic References in a CodeDOM (Part 7)

Rate me:
Please Sign up or sign in to vote.
4.75/5 (6 votes)
2 Dec 2012CDDL12 min read 19.4K   509   14  
Resolving symbolic references in 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;
using System.Collections.Generic;
using System.Reflection;
using Mono.Cecil;
using Mono.Collections.Generic;

using Nova.CodeDOM;
using Nova.Utilities;

namespace Nova.UI
{
    /// <summary>
    /// The view model for a <see cref="CodeDOM.MethodRef"/>.
    /// </summary>
    public class MethodRefVM : TypeRefBaseVM
    {
        #region /* STATICS */

        internal static void AddViewModelMapping()
        {
            CreateViewModel.Add(typeof(MethodRef),
                delegate(CodeObject codeObject, bool isDescription, Dictionary<CodeObject, CodeObjectVM> dictionary) { return new MethodRefVM((MethodRef)codeObject, dictionary); });
        }

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create a view model instance for the specified <see cref="CodeDOM.MethodRef"/>.
        /// </summary>
        public MethodRefVM(MethodRef methodRef, Dictionary<CodeObject, CodeObjectVM> dictionary)
            : base(methodRef, true, dictionary)
        { }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The underlying <see cref="CodeDOM.MethodRef"/> model.
        /// </summary>
        public MethodRef MethodRef
        {
            get { return (MethodRef)CodeObject; }
        }

        #endregion

        #region /* METHODS */

        #endregion

        #region /* RENDERING */

        public override void RenderExpression(CodeRenderer renderer, RenderFlags flags)
        {
            MethodRef methodRef = MethodRef;

            // If we have no dot prefix, and the ShowParentTypes flag is set, then render all parent types
            // (this shouldn't occur in display of code, but only when displaying an evaluated type reference,
            // such as in a tooltip).  Generic methods won't include type arguments for enclosing types, so
            // we don't have to worry about them.
            if (!flags.HasFlag(RenderFlags.HasDotPrefix) && flags.HasFlag(RenderFlags.ShowParentTypes))
            {
                TypeRefBase typeRef = methodRef.GetDeclaringType();
                if (typeRef != null)
                {
                    CreateVM(typeRef).Render(renderer, flags);
                    renderer.RenderText(Dot.ParseToken, PUNC_BRUSH, this);
                    flags |= RenderFlags.HasDotPrefix;
                }
            }

            object reference = methodRef.Reference;
            if (reference is MethodDeclBase)
                renderer.RenderText(((MethodDeclBase)reference).Name, IDENTIFIER_BRUSH, this);
            else if (reference is MethodDefinition)
                renderer.RenderText(((MethodDefinition)reference).Name, IDENTIFIER_BRUSH, this);
            else if (reference is MethodInfo)
                renderer.RenderText(((MethodInfo)reference).Name, IDENTIFIER_BRUSH, this);

            if (!methodRef.HasInferredTypeArguments || flags.HasFlag(RenderFlags.Description))
                RenderTypeArguments(renderer, _typeArgumentVMs, flags, this);
        }

        public static void RenderMethodDefinition(CodeRenderer renderer, MethodDefinition methodDefinition, RenderFlags flags, CodeObjectVM tag)
        {
            RenderFlags passFlags = flags & ~RenderFlags.Description;
            bool hasBorder = flags.HasFlag(RenderFlags.ForceBorder);
            if (hasBorder)
                renderer.CreateBorder(MethodDeclBaseVM.StaticBorderBrush, MethodDeclBaseVM.StaticBackgroundBrush, tag);

            if (!flags.HasFlag(RenderFlags.NoPreAnnotations))
            {
                AttributeVM.RenderAttributes(renderer, methodDefinition, tag);
                AttributeVM.RenderAttributes(renderer, methodDefinition.MethodReturnType, tag, AttributeTarget.Return);
            }
            Modifiers modifiers = MethodRef.GetMethodModifiers(methodDefinition);
            renderer.RenderText(ModifiersHelpers.AsString(modifiers), KEYWORD_BRUSH, tag);
            bool isConversionOperator = (modifiers.HasFlag(Modifiers.Explicit) || modifiers.HasFlag(Modifiers.Implicit));
            if (!methodDefinition.IsConstructor && !isConversionOperator)
            {
                TypeReference returnType = methodDefinition.ReturnType;
                RenderTypeReferenceAsTypeRefVM(renderer, returnType, passFlags, tag);  // Render as TypeRefVM for nested tooltips
                renderer.RenderText(" ", PUNC_BRUSH, tag);
            }
            TypeReference declaringType = methodDefinition.DeclaringType;
            RenderTypeReferenceAsTypeRefVM(renderer, declaringType, passFlags, tag);  // Render as TypeRefVM for nested tooltips
            DotVM.RenderDot(renderer, tag);

            if (methodDefinition.IsConstructor)
                renderer.RenderName(declaringType.HasGenericParameters ? TypeDefinitionUtil.NonGenericName(declaringType) : declaringType.Name, TYPE_BRUSH, tag, passFlags);
            else if (methodDefinition.HasGenericParameters)
                RenderGenericMember(renderer, methodDefinition.Name, (IEnumerable<TypeReference>)methodDefinition.GenericParameters, passFlags, IDENTIFIER_BRUSH, tag);
            else if (methodDefinition.Name.StartsWith(Operator.NamePrefix))
            {
                // Convert the internal name into the appropriate symbol
                renderer.RenderText(OperatorDecl.ParseToken, KEYWORD_BRUSH, tag);
                renderer.RenderText(" ", PUNC_BRUSH, tag);
                if (isConversionOperator)
                    RenderTypeReferenceAsTypeRefVM(renderer, methodDefinition.ReturnType, passFlags, tag);  // Render as TypeRefVM for nested tooltips
                else
                    renderer.RenderText(OperatorDecl.GetOperatorSymbol(methodDefinition.Name), IDENTIFIER_BRUSH, tag);
            }
            else
                renderer.RenderText(methodDefinition.Name, IDENTIFIER_BRUSH, tag);

            RenderMethodParameters(renderer, methodDefinition, passFlags, tag);

            // Render type constraints (if any)
            if (methodDefinition.HasGenericParameters)
                RenderConstraints(renderer, methodDefinition.GenericParameters, passFlags, tag);

            // If we have a border, return to the previous one
            if (hasBorder)
                renderer.ReturnToBorderParent(tag);
        }

        public static void RenderMethodParameters(CodeRenderer renderer, MethodDefinition methodDefinition, RenderFlags flags, CodeObjectVM tag)
        {
            renderer.RenderText(MethodDeclBase.ParseTokenStart, PUNC_BRUSH, tag);
            ICollection parameters = MethodRef.GetMethodParameters(methodDefinition);
            if (parameters is ChildList<ParameterDecl>)
            {
                ChildListVM<ParameterDeclVM> parameterVMs = CreateListVM<ParameterDecl, ParameterDeclVM>((ChildList<ParameterDecl>)parameters, tag);
                renderer.RenderList(parameterVMs, flags, null);
            }
            else if (parameters is Collection<ParameterDefinition>)
                RenderParameters(renderer, (Collection<ParameterDefinition>)parameters, flags, tag);
            else //if (parameters is ParameterInfo[])
                RenderParameters(renderer, (ParameterInfo[])parameters, flags, tag);
            renderer.RenderText(MethodDeclBase.ParseTokenEnd, PUNC_BRUSH, tag);
        }

        public static void RenderParameters(CodeRenderer renderer, Collection<ParameterDefinition> parameters, RenderFlags flags, CodeObjectVM tag)
        {
            for (int i = 0; i < parameters.Count; ++i)
            {
                ParameterDefinition parameterDefinition = parameters[i];
                if (i > 0)
                    renderer.RenderText(ParameterDecl.ParseTokenSeparator + " ", PUNC_BRUSH, tag);

                bool hasBorder = !flags.HasFlag(RenderFlags.NoBorder);
                if (hasBorder)
                    renderer.CreateBorder(VariableDeclVM.StaticBorderBrush, VariableDeclVM.StaticBackgroundBrush, tag);

                ParameterRefVM.RenderParameterDefinition(renderer, parameterDefinition, flags, tag);

                // If we have a border, return to the previous one);
                if (hasBorder)
                    renderer.ReturnToBorderParent(tag);
            }
        }

        /// <summary>
        /// Render a MethodDefinition as a MethodRefVM for nested tooltips.
        /// </summary>
        public static void RenderMethodDefinitionAsMethodRefVM(CodeRenderer renderer, MethodDefinition methodDefinition, RenderFlags flags, CodeObjectVM parentVM)
        {
            // Use parentVM to render if possible, but it can be null if we're inside a tooltip
            CodeObjectVM codeObjectVM;
            MethodRef methodRef = MethodRef.Create(methodDefinition);
            if (parentVM != null)
                codeObjectVM = parentVM.CreateVM(methodRef, true);
            else
                codeObjectVM = CreateVM(methodRef, null, true);
            codeObjectVM.Render(renderer, flags | RenderFlags.NoBorder);
        }

        public static void RenderMethodInfo(CodeRenderer renderer, MethodInfo methodInfo, RenderFlags flags, CodeObjectVM tag)
        {
            RenderFlags passFlags = flags & ~RenderFlags.Description;
            bool hasBorder = flags.HasFlag(RenderFlags.ForceBorder);
            if (hasBorder)
                renderer.CreateBorder(MethodDeclBaseVM.StaticBorderBrush, MethodDeclBaseVM.StaticBackgroundBrush, tag);

            if (!flags.HasFlag(RenderFlags.NoPreAnnotations))
            {
                AttributeVM.RenderAttributes(renderer, methodInfo, tag);
                AttributeVM.RenderAttributes(renderer, methodInfo.ReturnParameter, tag, AttributeTarget.Return);
            }
            Modifiers modifiers = MethodRef.GetMethodModifiers(methodInfo);
            renderer.RenderText(ModifiersHelpers.AsString(modifiers), KEYWORD_BRUSH, tag);
            bool isConversionOperator = (modifiers.HasFlag(Modifiers.Explicit) || modifiers.HasFlag(Modifiers.Implicit));
            if (!isConversionOperator)
            {
                Type returnType = methodInfo.ReturnType;
                RenderTypeAsTypeRefVM(renderer, returnType, passFlags, tag);  // Render as TypeRefVM for nested tooltips
                renderer.RenderText(" ", PUNC_BRUSH, tag);
            }
            Type declaringType = methodInfo.DeclaringType;
            RenderTypeAsTypeRefVM(renderer, declaringType, passFlags, tag);  // Render as TypeRefVM for nested tooltips
            DotVM.RenderDot(renderer, tag);

            if (methodInfo.IsGenericMethod)
                RenderGenericMember(renderer, methodInfo.Name, methodInfo.GetGenericArguments(), passFlags, IDENTIFIER_BRUSH, tag);
            else if (methodInfo.Name.StartsWith(Operator.NamePrefix))
            {
                // Convert the internal name into the appropriate symbol
                renderer.RenderText(OperatorDecl.ParseToken, KEYWORD_BRUSH, tag);
                renderer.RenderText(" ", PUNC_BRUSH, tag);
                if (isConversionOperator)
                    RenderTypeAsTypeRefVM(renderer, methodInfo.ReturnType, passFlags, tag);  // Render as TypeRefVM for nested tooltips
                else
                    renderer.RenderText(OperatorDecl.GetOperatorSymbol(methodInfo.Name), IDENTIFIER_BRUSH, tag);
            }
            else
                renderer.RenderText(methodInfo.Name, IDENTIFIER_BRUSH, tag);

            RenderMethodParameters(renderer, methodInfo, passFlags, tag);

            // Render type constraints (if any)
            if (methodInfo.IsGenericMethod)
                RenderConstraints(renderer, methodInfo.GetGenericMethodDefinition().GetGenericArguments(), passFlags, tag);

            // If we have a border, return to the previous one
            if (hasBorder)
                renderer.ReturnToBorderParent(tag);
        }

        public static void RenderMethodParameters(CodeRenderer renderer, MethodBase methodBase, RenderFlags flags, CodeObjectVM tag)
        {
            renderer.RenderText(MethodDeclBase.ParseTokenStart, PUNC_BRUSH, tag);
            ICollection parameters = MethodRef.GetMethodParameters(methodBase);
            if (parameters is ParameterInfo[])
                RenderParameters(renderer, (ParameterInfo[])parameters, flags, tag);
            else
            {
                ChildListVM<ParameterDeclVM> parameterVMs = CreateListVM<ParameterDecl, ParameterDeclVM>((ChildList<ParameterDecl>)parameters, tag);
                renderer.RenderList(parameterVMs, flags, null);
            }
            renderer.RenderText(MethodDeclBase.ParseTokenEnd, PUNC_BRUSH, tag);
        }

        public static void RenderParameters(CodeRenderer renderer, ParameterInfo[] parameters, RenderFlags flags, CodeObjectVM tag)
        {
            for (int i = 0; i < parameters.Length; ++i)
            {
                ParameterInfo parameterInfo = parameters[i];
                if (i > 0)
                    renderer.RenderText(ParameterDecl.ParseTokenSeparator + " ", PUNC_BRUSH, tag);

                bool hasBorder = !flags.HasFlag(RenderFlags.NoBorder);
                if (hasBorder)
                    renderer.CreateBorder(VariableDeclVM.StaticBorderBrush, VariableDeclVM.StaticBackgroundBrush, tag);

                ParameterRefVM.RenderParameterInfo(renderer, parameterInfo, flags, tag);

                // If we have a border, return to the previous one);
                if (hasBorder)
                    renderer.ReturnToBorderParent(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