Click here to Skip to main content
15,886,647 members
Articles / Programming Languages / C#

Creating a CodeDOM: Modeling the Semantics of Code (Part 2)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
9 Nov 2012CDDL24 min read 41.1K   756   33  
Creating a CodeDOM for C#
// 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.Collections;
using System.Collections.Generic;

using Nova.Rendering;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Represents a type parameter of a generic type or method declaration.
    /// </summary>
    public class TypeParameter : CodeObject, ITypeDecl
    {
        #region /* FIELDS */

        protected string _name;

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create a <see cref="TypeParameter"/> with the specified name.
        /// </summary>
        public TypeParameter(string name)
        {
            _name = name;
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The name of the <see cref="TypeParameter"/>.
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// The descriptive category of the code object.
        /// </summary>
        public string Category
        {
            get { return "type parameter"; }
        }

        /// <summary>
        /// Always <c>0</c>.
        /// </summary>
        public int TypeParameterCount
        {
            get { return 0; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsAbstract
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsClass
        {
            get { return false; }
        }

        /// <summary>
        /// True if the <see cref="TypeParameter"/> has a delegate type.
        /// </summary>
        public bool IsDelegateType
        {
            get { return GetTypeConstraint().IsDelegateType; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsEnum
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>true</c>.
        /// </summary>
        public bool IsGenericParameter
        {
            get { return true; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsGenericType
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsInterface
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>true</c>.
        /// </summary>
        public bool IsNested
        {
            // TypeParameters are considered nested types (including in .NET reflection)
            get { return true; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsNullableType
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsPartial
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsStruct
        {
            get { return false; }
        }

        /// <summary>
        /// Always <c>false</c>.
        /// </summary>
        public bool IsValueType
        {
            get { return false; }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Create a reference to the <see cref="TypeParameter"/>.
        /// </summary>
        /// <param name="isFirstOnLine">True if the reference should be displayed on a new line.</param>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public override SymbolicRef CreateRef(bool isFirstOnLine)
        {
            return new TypeParameterRef(this, isFirstOnLine);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public TypeRef CreateRef(bool isFirstOnLine, ChildList<Expression> typeArguments, List<int> arrayRanks)
        {
            // Ignore any type arguments - a TypeParameterRef can't have any
            return new TypeParameterRef(this, isFirstOnLine, arrayRanks);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public TypeRef CreateRef(bool isFirstOnLine, ChildList<Expression> typeArguments)
        {
            // Ignore any type arguments - a TypeParameterRef can't have any
            return new TypeParameterRef(this, isFirstOnLine);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public TypeRef CreateRef(ChildList<Expression> typeArguments, List<int> arrayRanks)
        {
            // Ignore any type arguments - a TypeParameterRef can't have any
            return new TypeParameterRef(this, false, arrayRanks);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public TypeRef CreateRef(ChildList<Expression> typeArguments)
        {
            // Ignore any type arguments - a TypeParameterRef can't have any
            return new TypeParameterRef(this, false);
        }

        /// <summary>
        /// Create an array reference to this <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public TypeRef CreateArrayRef(bool isFirstOnLine, params int[] ranks)
        {
            return new TypeParameterRef(this, isFirstOnLine, ranks);
        }

        /// <summary>
        /// Create an array reference to this <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeParameterRef"/>.</returns>
        public TypeRef CreateArrayRef(params int[] ranks)
        {
            return new TypeParameterRef(this, false, ranks);
        }

        /// <summary>
        /// Create a nullable reference to this <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateNullableRef(bool isFirstOnLine)
        {
            return TypeRef.CreateNullable(CreateRef(), isFirstOnLine);
        }

        /// <summary>
        /// Create a nullable reference to this <see cref="TypeParameter"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateNullableRef()
        {
            return TypeRef.CreateNullable(CreateRef());
        }

        /// <summary>
        /// Get any constraints for this <see cref="TypeParameter"/> from the parent type or generic method.
        /// </summary>
        public List<TypeParameterConstraint> GetConstraints()
        {
            return ((ITypeParameters)_parent).GetTypeParameterConstraints(this);
        }

        /// <summary>
        /// Get the main constraining type (if any - not including interfaces).
        /// </summary>
        public TypeRef GetTypeConstraint()
        {
            List<TypeParameterConstraint> constraints = GetConstraints();
            if (constraints != null)
            {
                foreach (TypeParameterConstraint constraint in constraints)
                {
                    if (constraint is TypeConstraint)
                    {
                        // Ignore if an UnresolvedRef, because we can't distinguish class vs interface
                        TypeRef typeRef = ((TypeConstraint)constraint).Type.SkipPrefixes() as TypeRef;
                        if (typeRef != null && typeRef.IsClass)
                            return typeRef;
                    }
                    else if (constraint is ClassConstraint)
                        return TypeRef.ObjectRef;
                    else if (constraint is StructConstraint)
                        return TypeRef.ValueTypeRef;
                }
            }
            return TypeRef.ObjectRef;
        }

        /// <summary>
        /// Get the base type - always a <see cref="TypeRef"/> to the 'object' type.
        /// </summary>
        public TypeRef GetBaseType()
        {
            return TypeRef.ObjectRef;
        }

        /// <summary>
        /// Always returns <c>null</c>.
        /// </summary>
        public ConstructorRef GetConstructor(params TypeRefBase[] parameterTypes)
        {
            return null;
        }

        /// <summary>
        /// Get all non-static constructors for this type.
        /// </summary>
        public NamedCodeObjectGroup GetConstructors(bool currentPartOnly)
        {
            return null;
        }

        /// <summary>
        /// Get all non-static constructors for this type.
        /// </summary>
        public NamedCodeObjectGroup GetConstructors()
        {
            return null;
        }

        /// <summary>
        /// Always returns <c>null</c>.
        /// </summary>
        public MethodRef GetMethod(string name, params TypeRefBase[] parameterTypes)
        {
            return null;
        }

        /// <summary>
        /// Does nothing.
        /// </summary>
        public void GetMethods(string name, bool searchBaseClasses, NamedCodeObjectGroup results)
        { }

        /// <summary>
        /// Always returns <c>null</c> for this type.
        /// </summary>
        /// <param name="name">The method name.</param>
        /// <param name="searchBaseClasses">Pass <c>false</c> to NOT search base classes.</param>
        public List<MethodRef> GetMethods(string name, bool searchBaseClasses)
        {
            return null;
        }

        /// <summary>
        /// Always returns <c>null</c> for this type.
        /// </summary>
        /// <param name="name">The method name.</param>
        public List<MethodRef> GetMethods(string name)
        {
            return null;
        }

        /// <summary>
        /// Always returns <c>null</c>.
        /// </summary>
        public PropertyRef GetProperty(string name)
        {
            return null;
        }

        /// <summary>
        /// Always returns <c>null</c>.
        /// </summary>
        public FieldRef GetField(string name)
        {
            return null;
        }

        /// <summary>
        /// Always returns <c>null</c>.
        /// </summary>
        public TypeRef GetNestedType(string name)
        {
            return null;
        }

        /// <summary>
        /// Get the delegate parameters of the constraining type (if any).
        /// </summary>
        public ICollection GetDelegateParameters()
        {
            return GetTypeConstraint().GetDelegateParameters();
        }

        /// <summary>
        /// Get the delegate return type of the constraining type (if any).
        /// </summary>
        public TypeRefBase GetDelegateReturnType()
        {
            return GetTypeConstraint().GetDelegateReturnType();
        }

        /// <summary>
        /// Determine if the constraining type is assignable from the specified type.
        /// </summary>
        public bool IsAssignableFrom(TypeRef typeRef)
        {
            return GetTypeConstraint().IsAssignableFrom(typeRef);
        }

        /// <summary>
        /// Determine if the constraining type is a subclass of the specified type.
        /// </summary>
        public bool IsSubclassOf(TypeRef classTypeRef)
        {
            return GetTypeConstraint().IsSubclassOf(classTypeRef);
        }

        /// <summary>
        /// Determine if the constraining type implements the specified interface type.
        /// </summary>
        public bool IsImplementationOf(TypeRef interfaceTypeRef)
        {
            return GetTypeConstraint().IsImplementationOf(interfaceTypeRef);
        }

        /// <summary>
        /// Add the <see cref="CodeObject"/> to the specified dictionary.
        /// </summary>
        public virtual void AddToDictionary(NamedCodeObjectDictionary dictionary)
        {
            dictionary.Add(Name, this);
        }

        /// <summary>
        /// Remove the <see cref="CodeObject"/> from the specified dictionary.
        /// </summary>
        public virtual void RemoveFromDictionary(NamedCodeObjectDictionary dictionary)
        {
            dictionary.Remove(Name, this);
        }

        /// <summary>
        /// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
        /// </summary>
        /// <param name="descriptive">True to display type parameters and method parameters, otherwise false.</param>
        public string GetFullName(bool descriptive)
        {
            if (Parent is TypeDecl)
                return ((TypeDecl)Parent).GetFullName(descriptive) + "." + _name;
            if (Parent is GenericMethodDecl)
                return ((GenericMethodDecl)Parent).GetFullName(descriptive) + "." + _name;
            return _name;
        }

        /// <summary>
        /// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
        /// </summary>
        public string GetFullName()
        {
            return GetFullName(false);
        }

        #endregion

        #region /* FORMATTING */

        /// <summary>
        /// True if the code object defaults to starting on a new line.
        /// </summary>
        public override bool IsFirstOnLineDefault
        {
            get { return false; }
        }

        /// <summary>
        /// The number of newlines preceeding the object (0 to N).
        /// </summary>
        public override int NewLines
        {
            get { return base.NewLines; }
            set
            {
                // If we're changing to zero, also change all prefix attributes to zero
                bool isFirstOnLine = (value != 0);
                if (_annotations != null && !isFirstOnLine && IsFirstOnLine)
                {
                    foreach (Annotation annotation in _annotations)
                    {
                        if (annotation is Attribute)
                            annotation.IsFirstOnLine = false;
                    }
                }

                base.NewLines = value;
            }
        }

        #endregion

        #region /* RENDERING */

        public override void AsText(CodeWriter writer, RenderFlags flags)
        {
            RenderFlags passFlags = (flags & RenderFlags.PassMask);
            if (flags.HasFlag(RenderFlags.PrefixSpace))
                writer.Write(" ");
            AsTextAnnotations(writer, passFlags);
            UpdateLineCol(writer, flags);
            writer.WriteIdentifier(_name, flags);
        }

        public static void AsTextTypeParameters(CodeWriter writer, ChildList<TypeParameter> typeParameters, RenderFlags flags)
        {
            RenderFlags passFlags = (flags & RenderFlags.PassMask);
            // Render the angle brackets as braces if we're inside a documentation comment
            writer.Write(flags.HasFlag(RenderFlags.InDocComment) ? "{" : "<");
            writer.WriteList(typeParameters, passFlags, typeParameters.Parent);
            writer.Write(flags.HasFlag(RenderFlags.InDocComment) ? "}" : ">");
        }

        #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