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.Collections;
using System.Collections.Generic;

using Nova.Parsing;
using Nova.Rendering;
using Nova.Resolving;

namespace Nova.CodeDOM
{
    /// <summary>
    /// The common base class of all user-defined type declarations (<see cref="ClassDecl"/>, <see cref="StructDecl"/>,
    /// <see cref="InterfaceDecl"/>, <see cref="EnumDecl"/>, and <see cref="DelegateDecl"/>).
    /// </summary>
    public abstract class TypeDecl : BlockStatement, ITypeDecl, ITypeParameters, IModifiers
    {
        #region /* FIELDS */

        protected Modifiers _modifiers;
        protected string _name;
        protected ChildList<TypeParameter> _typeParameters;        // Not used for EnumDecls
        protected ChildList<ConstraintClause> _constraintClauses;  // Not used for EnumDecls

        /// <summary>
        /// The parent Project of the TypeDecl, used for performance when importing types.
        /// </summary>
        protected Project _parentProject;

        #endregion

        #region /* CONSTRUCTORS */

        protected TypeDecl(string name, Modifiers modifiers, CodeObject body)
            : base(body, false)
        {
            _name = name;
            _modifiers = modifiers;
        }

        protected TypeDecl(string name, Modifiers modifiers)
        {
            _name = name;
            _modifiers = modifiers;
        }

        protected TypeDecl(string name)
        {
            _name = name;
        }

        protected TypeDecl(string name, Modifiers modifiers, params TypeParameter[] typeParameters)
            : this(name, modifiers)
        {
            CreateTypeParameters().AddRange(typeParameters);
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The name of the <see cref="TypeDecl"/>.
        /// </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"; }
        }

        /// <summary>
        /// Optional <see cref="Modifiers"/> for the type.
        /// </summary>
        public Modifiers Modifiers
        {
            get { return _modifiers; }
            set { _modifiers = value; }
        }

        /// <summary>
        /// The parent <see cref="CodeObject"/>.
        /// </summary>
        public override CodeObject Parent
        {
            set
            {
                base.Parent = value;

                // Cache the parent project for performance when importing types from other projects
                _parentProject = FindParent<Project>();
            }
        }

        /// <summary>
        /// True if the type is abstract.
        /// </summary>
        public bool IsAbstract
        {
            get { return _modifiers.HasFlag(Modifiers.Abstract); }
            set { _modifiers = (value ? _modifiers | Modifiers.Abstract : _modifiers & ~Modifiers.Abstract); }
        }

        /// <summary>
        /// True if the type is a partial type.
        /// </summary>
        public bool IsPartial
        {
            get { return _modifiers.HasFlag(Modifiers.Partial); }
            set { _modifiers = (value ? _modifiers | Modifiers.Partial : _modifiers & ~Modifiers.Partial); }
        }

        /// <summary>
        /// True if the type has public access.
        /// </summary>
        public bool IsPublic
        {
            get { return _modifiers.HasFlag(Modifiers.Public); }
            // Force other flags off if setting to Public
            set { _modifiers = (value ? _modifiers & ~(Modifiers.Private | Modifiers.Protected | Modifiers.Internal) | Modifiers.Public : _modifiers & ~Modifiers.Public); }
        }

        /// <summary>
        /// True if the type has private access.
        /// </summary>
        public bool IsPrivate
        {
            // Should only be true for nested types, and defaults to true if nested and nothing else is set
            get { return (_modifiers.HasFlag(Modifiers.Private) || (IsNested && (_modifiers & (Modifiers.Protected | Modifiers.Internal | Modifiers.Public)) == 0)); }
            // Force other flags off if setting to Private
            set { _modifiers = (value ? _modifiers & ~(Modifiers.Protected | Modifiers.Internal | Modifiers.Public) | Modifiers.Private : _modifiers & ~Modifiers.Private); }
        }

        /// <summary>
        /// True if the type has protected access.
        /// </summary>
        public bool IsProtected
        {
            // Should only be true for nested types
            get { return _modifiers.HasFlag(Modifiers.Protected); }
            // Force certain other flags off if setting to Protected
            set { _modifiers = (value ? _modifiers & ~(Modifiers.Private | Modifiers.Public) | Modifiers.Protected : _modifiers & ~Modifiers.Protected); }
        }

        /// <summary>
        /// True if the type has internal access.
        /// </summary>
        public bool IsInternal
        {
            // Defaults to true if nothing else is set
            get { return (_modifiers.HasFlag(Modifiers.Internal) || ((_modifiers & (Modifiers.Private | Modifiers.Protected | Modifiers.Public)) == 0)); }
            // Force certain other flags off if setting to Protected
            set { _modifiers = (value ? _modifiers & ~(Modifiers.Private | Modifiers.Public) | Modifiers.Internal : _modifiers & ~Modifiers.Internal); }
        }

        /// <summary>
        /// True if the type is a nested type.
        /// </summary>
        public bool IsNested
        {
            get { return (_parent is TypeDecl); }
        }

        /// <summary>
        /// True if the type is a nullable type.
        /// </summary>
        public bool IsNullableType
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is static.
        /// </summary>
        public virtual bool IsStatic
        {
            get { return _modifiers.HasFlag(Modifiers.Static); }
            set { _modifiers = (value ? _modifiers | Modifiers.Static : _modifiers & ~Modifiers.Static); }
        }

        /// <summary>
        /// A collection of optional <see cref="TypeParameter"/>s (for generic types).
        /// </summary>
        public ChildList<TypeParameter> TypeParameters
        {
            get { return _typeParameters; }
        }

        /// <summary>
        /// True if the type has <see cref="TypeParameter"/>s.
        /// </summary>
        public bool HasTypeParameters
        {
            get { return (_typeParameters != null && _typeParameters.Count > 0); }
        }

        /// <summary>
        /// The number of <see cref="TypeParameter"/>s the type has.
        /// </summary>
        public int TypeParameterCount
        {
            get { return (_typeParameters != null ? _typeParameters.Count : 0); }
        }

        /// <summary>
        /// A collection of optional <see cref="ConstraintClause"/>s (for generic types).
        /// </summary>
        public ChildList<ConstraintClause> ConstraintClauses
        {
            get { return _constraintClauses; }
        }

        /// <summary>
        /// True if there are any <see cref="ConstraintClause"/>s.
        /// </summary>
        public bool HasConstraintClauses
        {
            get { return (_constraintClauses != null && _constraintClauses.Count > 0); }
        }

        /// <summary>
        /// True if the type is a class.
        /// </summary>
        public virtual bool IsClass
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is a delegate type.
        /// </summary>
        public virtual bool IsDelegateType
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is an enum.
        /// </summary>
        public virtual bool IsEnum
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is a generic parameter.
        /// </summary>
        public bool IsGenericParameter
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is a generic type (meaning that either it or an enclosing type has type parameters,
        /// and it's not an enum).
        /// </summary>
        public virtual bool IsGenericType
        {
            get { return (HasTypeParameters || (_parent is TypeDecl && ((TypeDecl)_parent).HasTypeParameters)); }
        }

        /// <summary>
        /// True if the type is an interface.
        /// </summary>
        public virtual bool IsInterface
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is a struct.
        /// </summary>
        public virtual bool IsStruct
        {
            get { return false; }
        }

        /// <summary>
        /// True if the type is a value type.
        /// </summary>
        public virtual bool IsValueType
        {
            get { return false; }
        }

        /// <summary>
        /// Get the declaring <see cref="TypeDecl"/>.
        /// </summary>
        public TypeDecl DeclaringType
        {
            get { return (_parent as TypeDecl); }
        }

        /// <summary>
        /// The parent <see cref="Project"/> of the <see cref="TypeDecl"/>.
        /// </summary>
        public Project ParentProject
        {
            get { return _parentProject; }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Create the list of <see cref="TypeParameter"/>s, or return the existing one.
        /// </summary>
        public ChildList<TypeParameter> CreateTypeParameters()
        {
            if (_typeParameters == null)
                _typeParameters = new ChildList<TypeParameter>(this);
            return _typeParameters;
        }

        /// <summary>
        /// Add one or more <see cref="TypeParameter"/>s.
        /// </summary>
        public void AddTypeParameters(params TypeParameter[] typeParameters)
        {
            CreateTypeParameters().AddRange(typeParameters);
        }

        /// <summary>
        /// Create the list of <see cref="ConstraintClause"/>s, or return the existing one.
        /// </summary>
        public ChildList<ConstraintClause> CreateConstraintClauses()
        {
            if (_constraintClauses == null)
                _constraintClauses = new ChildList<ConstraintClause>(this);
            return _constraintClauses;
        }

        /// <summary>
        /// Add one or more <see cref="ConstraintClause"/>s.
        /// </summary>
        public void AddConstraintClauses(params ConstraintClause[] constraintClauses)
        {
            CreateConstraintClauses().AddRange(constraintClauses);
        }

        /// <summary>
        /// Get any constraints for the specified <see cref="TypeParameter"/> on this type.
        /// </summary>
        public List<TypeParameterConstraint> GetTypeParameterConstraints(TypeParameter typeParameter)
        {
            if (_constraintClauses != null)
            {
                foreach (ConstraintClause constraintClause in _constraintClauses)
                {
                    if (constraintClause.TypeParameter.Reference == typeParameter)
                        return constraintClause.Constraints;
                }
            }
            return null;
        }

        /// <summary>
        /// Get the base type.
        /// </summary>
        public virtual TypeRef GetBaseType()
        {
            return null;
        }

        /// <summary>
        /// Deep-clone the code object.
        /// </summary>
        public override CodeObject Clone()
        {
            TypeDecl clone = (TypeDecl)base.Clone();
            clone._typeParameters = ChildListHelpers.Clone(_typeParameters, clone);
            clone._constraintClauses = ChildListHelpers.Clone(_constraintClauses, clone);
            return clone;
        }

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

        /// <summary>
        /// Create a reference to the <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateRef(bool isFirstOnLine, ChildList<Expression> typeArguments, List<int> arrayRanks)
        {
            return new TypeRef(this, isFirstOnLine, typeArguments, arrayRanks);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateRef(bool isFirstOnLine, ChildList<Expression> typeArguments)
        {
            return new TypeRef(this, isFirstOnLine, typeArguments, null);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateRef(ChildList<Expression> typeArguments, List<int> arrayRanks)
        {
            return new TypeRef(this, false, typeArguments, arrayRanks);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateRef(ChildList<Expression> typeArguments)
        {
            return new TypeRef(this, false, typeArguments, null);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateRef(bool isFirstOnLine, params Expression[] typeArguments)
        {
            return new TypeRef(this, isFirstOnLine, typeArguments);
        }

        /// <summary>
        /// Create a reference to the <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateRef(params Expression[] typeArguments)
        {
            return new TypeRef(this, false, typeArguments);
        }

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

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

        /// <summary>
        /// Create a nullable reference to this <see cref="TypeDecl"/>.
        /// </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="TypeDecl"/>.
        /// </summary>
        /// <returns>A <see cref="TypeRef"/>.</returns>
        public TypeRef CreateNullableRef()
        {
            return TypeRef.CreateNullable(CreateRef(), false);
        }

        /// <summary>
        /// Get all member declarations of the <see cref="TypeDecl"/> (methods, properties, indexers, events, fields, and nested types).
        /// </summary>
        /// <param name="currentPartOnly">True to get members from current part only if the TypeDecl is partial.</param>
        public IEnumerable<INamedCodeObject> GetMemberDecls(bool currentPartOnly)
        {
            if (_body != null)
            {
                foreach (CodeObject codeObject in _body)
                {
                    if (codeObject is IMultiVariableDecl)
                    {
                        foreach (VariableDecl variableDecl in ((IMultiVariableDecl)codeObject))
                            yield return variableDecl;
                    }
                    else if (codeObject is INamedCodeObject && !codeObject.IsGenerated)
                        yield return (INamedCodeObject)codeObject;
                }
            }
            if (IsPartial && !currentPartOnly)
            {
                foreach (TypeDecl otherPart in GetOtherParts())
                {
                    if (otherPart.Body != null)
                    {
                        foreach (CodeObject codeObject in otherPart.Body)
                        {
                            if (codeObject is IMultiVariableDecl)
                            {
                                foreach (VariableDecl variableDecl in ((IMultiVariableDecl)codeObject))
                                    yield return variableDecl;
                            }
                            else if (codeObject is INamedCodeObject && !codeObject.IsGenerated)
                                yield return (INamedCodeObject)codeObject;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Get all member declarations of the <see cref="TypeDecl"/> (methods, properties, indexers, events, fields, and nested types).
        /// </summary>
        public IEnumerable<INamedCodeObject> GetMemberDecls()
        {
            return GetMemberDecls(false);
        }

        /// <summary>
        /// Get all nested type declarations of the <see cref="TypeDecl"/>.
        /// </summary>
        /// <param name="recursive">True to recursively return all nested type declarations, otherwise false.</param>
        /// <param name="currentPartOnly">True to get nested types from current part only if the TypeDecl is partial, otherwise false.</param>
        public IEnumerable<TypeDecl> GetNestedTypeDecls(bool recursive, bool currentPartOnly)
        {
            if (_body != null)
            {
                foreach (CodeObject codeObject in _body)
                {
                    if (codeObject is TypeDecl)
                    {
                        TypeDecl typeDecl = (TypeDecl)codeObject;
                        yield return typeDecl;

                        if (recursive)
                        {
                            foreach (TypeDecl nestedType in typeDecl.GetNestedTypeDecls(true, currentPartOnly))
                                yield return nestedType;
                        }
                    }
                }
            }
            if (IsPartial && !currentPartOnly)
            {
                foreach (TypeDecl otherPart in GetOtherParts())
                {
                    if (otherPart.Body != null)
                    {
                        foreach (CodeObject codeObject in otherPart.Body)
                        {
                            if (codeObject is TypeDecl)
                            {
                                TypeDecl typeDecl = (TypeDecl)codeObject;
                                yield return typeDecl;

                                if (recursive)
                                {
                                    foreach (TypeDecl nestedType in typeDecl.GetNestedTypeDecls(true))
                                        yield return nestedType;
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Get all nested type declarations of the <see cref="TypeDecl"/>.
        /// </summary>
        /// <param name="recursive">True to recursively return all nested type declarations, otherwise false.</param>
        public IEnumerable<TypeDecl> GetNestedTypeDecls(bool recursive)
        {
            return GetNestedTypeDecls(recursive, false);
        }

        /// <summary>
        /// Get all nested type declarations of the <see cref="TypeDecl"/>.
        /// </summary>
        public IEnumerable<TypeDecl> GetNestedTypeDecls()
        {
            return GetNestedTypeDecls(false, false);
        }

        /// <summary>
        /// Get the non-static constructor with the specified parameters.
        /// </summary>
        public virtual ConstructorRef GetConstructor(params TypeRefBase[] parameterTypes)
        {
            ConstructorDecl found = GetMethod<ConstructorDecl>(_name, parameterTypes);
            if (found != null)
                return (ConstructorRef)found.CreateRef();
            TypeRef baseRef = GetBaseType();
            return (baseRef != null ? baseRef.GetConstructor(parameterTypes) : null);
        }

        /// <summary>
        /// Get all non-static constructors for this type.
        /// </summary>
        public virtual NamedCodeObjectGroup GetConstructors(bool currentPartOnly)
        {
            NamedCodeObjectGroup constructors = new NamedCodeObjectGroup();
            NamedCodeObjectGroup found = new NamedCodeObjectGroup();
            if (currentPartOnly && _body != null)
                _body.FindChildren<ConstructorDecl>(_name, found);
            else
                FindInAllParts<ConstructorDecl>(_name, found);
            foreach (INamedCodeObject namedCodeObject in found)
            {
                if (!((ConstructorDecl)namedCodeObject).IsStatic)
                    constructors.Add(namedCodeObject);
            }
            return constructors;
        }

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

        /// <summary>
        /// Get the method with the specified name and parameter types.
        /// </summary>
        public virtual MethodRef GetMethod(string name, params TypeRefBase[] parameterTypes)
        {
            MethodDeclBase found = GetMethod<MethodDeclBase>(name, parameterTypes);
            if (found != null)
                return (MethodRef)found.CreateRef();
            TypeRef baseRef = GetBaseType();
            return (baseRef != null ? baseRef.GetMethod(name, parameterTypes) : null);
        }

        /// <summary>
        /// Get all methods with the specified name, adding them to the provided NamedCodeObjectGroup.
        /// </summary>
        public virtual void GetMethods(string name, bool searchBaseClasses, NamedCodeObjectGroup results)
        {
            FindInAllParts<MethodDeclBase>(name, results);
            if (searchBaseClasses)
            {
                TypeRef baseRef = GetBaseType();
                if (baseRef != null)
                    baseRef.GetMethods(name, true, results);
            }
        }

        /// <summary>
        /// Get all methods with the specified name.
        /// </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)
        {
            NamedCodeObjectGroup results = new NamedCodeObjectGroup();
            GetMethods(name, searchBaseClasses, results);
            return MethodRef.MethodRefsFromGroup(results);
        }

        /// <summary>
        /// Get all methods with the specified name.
        /// </summary>
        /// <param name="name">The method name.</param>
        public List<MethodRef> GetMethods(string name)
        {
            return GetMethods(name, false);
        }

        /// <summary>
        /// Get the property with the specified name.
        /// </summary>
        public virtual PropertyRef GetProperty(string name)
        {
            NamedCodeObjectGroup found = new NamedCodeObjectGroup();
            FindInAllParts<PropertyDecl>(name, found);
            if (found.Count > 0)
                return (PropertyRef)((PropertyDecl)found[0]).CreateRef();
            TypeRef baseRef = GetBaseType();
            return (baseRef != null ? baseRef.GetProperty(name) : null);
        }

        /// <summary>
        /// Get the field with the specified name.
        /// </summary>
        public virtual FieldRef GetField(string name)
        {
            NamedCodeObjectGroup found = new NamedCodeObjectGroup();
            FindInAllParts<FieldDecl>(name, found);
            if (found.Count > 0)
                return (FieldRef)((FieldDecl)found[0]).CreateRef();
            TypeRef baseRef = GetBaseType();
            return (baseRef != null ? baseRef.GetField(name) : null);
        }

        /// <summary>
        /// Get the nested type with the specified name.
        /// </summary>
        public TypeRef GetNestedType(string name)
        {
            NamedCodeObjectGroup found = new NamedCodeObjectGroup();
            FindInAllParts<TypeDecl>(name, found);
            if (found.Count > 0)
                return ((TypeDecl)found[0]).CreateRef();
            TypeRef baseRef = GetBaseType();
            return (baseRef != null ? baseRef.GetNestedType(name) : null);
        }

        /// <summary>
        /// Get the method with the specified name and parameters, and of type T.
        /// </summary>
        public T GetMethod<T>(string name, params TypeRefBase[] parameterTypes) where T : MethodDeclBase
        {
            NamedCodeObjectGroup found = new NamedCodeObjectGroup();
            FindInAllParts<T>(name, found);
            foreach (T methodDecl in found)
            {
                if (methodDecl.MatchParameters(parameterTypes))
                    return methodDecl;
            }
            return null;
        }

        /// <summary>
        /// Find all members of the type with the specified name and of type T, including in other parts of partial types.
        /// </summary>
        protected void FindInAllParts<T>(string name, NamedCodeObjectGroup results) where T : CodeObject
        {
            if (_body != null)
                _body.FindChildren<T>(name, results);
            if (IsPartial)
            {
                foreach (TypeDecl otherPart in GetOtherParts())
                {
                    if (otherPart.Body != null)
                        otherPart.Body.FindChildren<T>(name, results);
                }
            }
        }

        /// <summary>
        /// Find the index of the specified <see cref="TypeParameter"/> in the declaration of the <see cref="TypeDecl"/> or
        /// an enclosing <see cref="TypeDecl"/> if this one is nested.  Also handles partial types.
        /// </summary>
        /// <returns>The index of the <see cref="TypeParameter"/>, or -1 if not found.</returns>
        public int FindTypeParameterIndex(TypeParameter typeParameter)
        {
            int index;
            if (FindTypeParameterIndex(typeParameter, out index))
                return index;
            return -1;
        }

        /// <summary>
        /// Find the index of the specified <see cref="TypeParameter"/> in the declaration of the <see cref="TypeDecl"/> or
        /// an enclosing <see cref="TypeDecl"/> if this one is nested.  Also handles partial types.
        /// </summary>
        /// <returns>Returns true if the index of the TypeParameter was found, otherwise false.</returns>
        protected bool FindTypeParameterIndex(TypeParameter typeParameter, out int index)
        {
            // First, recursively check any parent types
            if (_parent is TypeDecl)
            {
                if (((TypeDecl)_parent).FindTypeParameterIndex(typeParameter, out index))
                    return true;
            }
            else
                index = 0;

            // Then, check the type parameters on the current type
            int startingIndex = index;
            if (FindTypeParameterIndexLocal(typeParameter, ref index))
                return true;

            // Finally, also check the type parameters of any partial types
            if (IsPartial)
            {
                int endingIndex = index;
                foreach (TypeDecl otherPart in GetOtherParts())
                {
                    index = startingIndex;
                    if (otherPart.FindTypeParameterIndexLocal(typeParameter, ref index))
                        return true;
                }
                index = endingIndex;
            }

            return false;
        }

        private bool FindTypeParameterIndexLocal(TypeParameter typeParameter, ref int index)
        {
            if (_typeParameters != null)
            {
                foreach (TypeParameter localTypeParameter in _typeParameters)
                {
                    if (localTypeParameter == typeParameter)
                        return true;
                    ++index;
                }
            }
            return false;
        }

        /// <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 IsPrivate access right for the specified usage, and if not private then also get the IsProtected and IsInternal rights.
        /// </summary>
        /// <param name="isTargetOfAssignment">Usage - true if the target of an assignment ('lvalue'), otherwise false.</param>
        /// <param name="isPrivate">True if the access is private.</param>
        /// <param name="isProtected">True if the access is protected.</param>
        /// <param name="isInternal">True if the access is internal.</param>
        public void GetAccessRights(bool isTargetOfAssignment, out bool isPrivate, out bool isProtected, out bool isInternal)
        {
            // The isTargetOfAssignment flag is needed only for properties/indexers/events, not types
            isPrivate = IsPrivate;
            if (!isPrivate)
            {
                isProtected = IsProtected;
                isInternal = IsInternal;
            }
            else
                isProtected = isInternal = false;
        }

        #region /* PARTIAL TYPES */

        /// <summary>
        /// Get any other parts of this <see cref="TypeDecl"/>.
        /// </summary>
        /// <returns></returns>
        public List<TypeDecl> GetOtherParts()
        {
            List<TypeDecl> otherParts = new List<TypeDecl>();
            GetOtherParts(otherParts, null);
            return otherParts;
        }

        protected void GetOtherParts(List<TypeDecl> otherParts, List<string> parentTypes)
        {
            // Find any other parts of the type
            CodeObject parent = _parent;
            if (parent is NamespaceDecl)
                GetOtherParts(((NamespaceDecl)parent).Namespace.Find(_name), otherParts, parentTypes);
            else if (parent is TypeDecl)
            {
                // Look for other parts of a nested type in the parent type
                TypeDecl parentTypeDecl = (TypeDecl)parent;
                GetOtherParts(parentTypeDecl.Body.FindChildren(_name), otherParts, parentTypes);

                // If the parent type is also partial, then look for other parts of the parent
                // type, and look in them for additional parts of the nested type.
                if (parentTypeDecl.IsPartial)
                {
                    if (parentTypes == null)
                        parentTypes = new List<string>();
                    parentTypes.Insert(0, _name);
                    parentTypeDecl.GetOtherParts(otherParts, parentTypes);
                }
            }
        }

        private void GetOtherParts(object obj, List<TypeDecl> otherParts, List<string> parentTypes)
        {
            if (obj is TypeDecl)
                GetOtherParts((TypeDecl)obj, otherParts, parentTypes);
            else if (obj is NamespaceTypeGroup)
            {
                NamespaceTypeGroup group = (NamespaceTypeGroup)obj;
                // Lock the NamespaceTypeGroup while iterating it to prevent changes while parsing on other threads
                lock (group.SyncRoot)
                {
                    foreach (object @object in group)
                    {
                        if (@object is TypeDecl)
                            GetOtherParts((TypeDecl)@object, otherParts, parentTypes);
                    }
                }
            }
        }

        private void GetOtherParts(TypeDecl typeDecl, List<TypeDecl> otherParts, List<string> parentTypes)
        {
            if (typeDecl != this)
            {
                if (parentTypes == null)
                {
                    if (typeDecl.TypeParameterCount == TypeParameterCount)
                        otherParts.Add(typeDecl);
                }
                else
                {
                    List<string> newList = new List<string>(parentTypes);
                    string parentType = parentTypes[0];
                    newList.RemoveAt(0);
                    GetOtherParts(typeDecl.Body.FindChildren(parentType), otherParts, newList.Count > 0 ? newList : null);
                }
            }
        }

        #endregion

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

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

        /// <summary>
        /// Determine if the type is assignable from the specified type.
        /// </summary>
        public virtual bool IsAssignableFrom(TypeRef typeRef)
        {
            if (typeRef == null)
                return false;

            TypeRef thisTypeRef = CreateRef();
            return (typeRef.IsSameRef(thisTypeRef) || typeRef.IsSubclassOf(thisTypeRef));
        }

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

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

        /// <summary>
        /// Check if the specified TypeDecl is identical to OR has the same name/namespace as the current one (could be different parts).
        /// </summary>
        public bool IsSameAs(TypeDecl typeDecl)
        {
            return (this == typeDecl || (_name == typeDecl.Name && (_typeParameters != null ? _typeParameters.Count : 0) == typeDecl.TypeParameterCount && GetNamespace() == typeDecl.GetNamespace()));
        }

        /// <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)
        {
            string name = _name;
            if (_typeParameters != null && _typeParameters.Count > 0)
            {
                if (descriptive)
                    name += GetTypeParametersAsString(_typeParameters);
                else
                    name += "`" + TypeParameterCount;
            }
            if (Parent is TypeDecl)
                return ((TypeDecl)Parent).GetFullName(descriptive) + (descriptive ? "." : "+") + name;
            Namespace @namespace = GetNamespace();
            return (@namespace != null && !@namespace.IsGlobal ? @namespace.FullName + "." : "") + name;
        }

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

        /// <summary>
        /// Get the specified type parameters as a descriptive string.
        /// </summary>
        internal static string GetTypeParametersAsString(ChildList<TypeParameter> typeParameters)
        {
            string result = TypeParameter.ParseTokenStart;
            bool isFirst = true;
            foreach (TypeParameter typeParameter in typeParameters)
            {
                result += (isFirst ? "" : ", ") + typeParameter.Name;
                isFirst = false;
            }
            result += TypeParameter.ParseTokenEnd;
            return result;
        }

        #endregion

        #region /* PARSING */

        protected TypeDecl(Parser parser, CodeObject parent)
            : base(parser, parent)
        {
            // Force all type declarations to start on a new line by default
            IsFirstOnLine = true;
        }

        protected void ParseModifiersAndAnnotations(Parser parser)
        {
            _modifiers = ModifiersHelpers.Parse(parser, this);  // Parse any modifiers in reverse from the Unused list
            ParseUnusedAnnotations(parser, this, false);        // Parse attributes and/or doc comments from the Unused list
        }

        protected void ParseNameTypeParameters(Parser parser)
        {
            MoveComments(parser.LastToken);
            _name = parser.GetIdentifierText();                       // Parse the name
            MoveEOLComment(parser.LastToken);                         // Associate any skipped EOL comment
            _typeParameters = TypeParameter.ParseList(parser, this);  // Parse any type parameters
            MoveEOLComment(parser.LastToken);                         // Associate any skipped EOL comment
        }

        protected void ParseConstraintClauses(Parser parser)
        {
            _constraintClauses = ConstraintClause.ParseList(parser, this);  // Parse any constraint clauses
        }

        /// <summary>
        /// Determine if the specified comment should be associated with the current code object during parsing.
        /// </summary>
        public override bool AssociateCommentWhenParsing(CommentBase comment)
        {
            return true;
        }

        #endregion

        #region /* RESOLVING */

        /// <summary>
        /// Resolve all child symbolic references, using the specified <see cref="ResolveCategory"/> and <see cref="ResolveFlags"/>.
        /// </summary>
        public override CodeObject Resolve(ResolveCategory resolveCategory, ResolveFlags flags)
        {
            if ((flags & ResolveFlags.Phase1) == 0)
            {
                if ((flags & ResolveFlags.Phase3) == 0)
                {
                    ChildListHelpers.Resolve(_typeParameters, ResolveCategory.CodeObject, flags);  // Resolve any attributes on the TypeParameters
                    ChildListHelpers.Resolve(_constraintClauses, ResolveCategory.CodeObject, flags);
                }
                if ((flags & ResolveFlags.Phase2) == 0)
                    ResolveAttributes(flags);
                if (_body != null)
                    _body.Resolve(ResolveCategory.CodeObject, flags);
                if ((flags & ResolveFlags.Phase2) == 0)
                    ResolveDocComments(flags);
            }
            return this;
        }

        /// <summary>
        /// Resolve child code objects that match the specified name, moving up the tree until a complete match is found.
        /// </summary>
        public override void ResolveRefUp(string name, Resolver resolver)
        {
            // Abort if we reach this level and we're looking for a category that can't be found this "high"
            if (resolver.IsMethodLevelCategory())
                return;

            if (resolver.ResolveCategory == ResolveCategory.LocalTypeParameter)
                ChildListHelpers.ResolveRef(_typeParameters, name, resolver);
            else
            {
                if (_body != null)
                    _body.ResolveRef(name, resolver);
                if (IsPartial)
                    ResolveOtherParts(name, resolver, null);
                if (resolver.HasCompleteMatch) return;  // Abort if we found a match
                ChildListHelpers.ResolveRef(_typeParameters, name, resolver);
                if (resolver.HasCompleteMatch) return;  // Abort if we found a match
                ResolveRefInBase(name, resolver);
                if (_parent != null && !resolver.HasCompleteMatch)
                    _parent.ResolveRefUp(name, resolver);
            }
        }

        /// <summary>
        /// Resolve child code objects that match the specified name.
        /// </summary>
        public override void ResolveRef(string name, Resolver resolver)
        {
            if (_body != null)
                _body.ResolveRef(name, resolver);
            if (IsPartial)
                ResolveOtherParts(name, resolver, null);
            if (resolver.HasCompleteMatch) return;  // Abort if we found a match
            ResolveRefInBase(name, resolver);
        }

        /// <summary>
        /// Resolve child code objects in the base class that match the specified name.
        /// </summary>
        protected virtual void ResolveRefInBase(string name, Resolver resolver)
        {
            TypeRef baseRef = GetBaseType();
            if (baseRef != null)
            {
                TypeRefBase typeRefBase = baseRef.EvaluateType();
                if (typeRefBase != null)
                    typeRefBase.ResolveRef(name, resolver);
            }
        }

        /// <summary>
        /// Resolve indexers.
        /// </summary>
        public override void ResolveIndexerRef(Resolver resolver)
        {
            if (_body != null)
                _body.ResolveIndexerRef(resolver);
            if (IsPartial)
                ResolveIndexerOtherParts(resolver, null);
            if (resolver.HasCompleteMatch) return;  // Abort if we found a match
            ResolveIndexerRefInBase(resolver);
        }

        /// <summary>
        /// Resolve indexers in the base class.
        /// </summary>
        protected virtual void ResolveIndexerRefInBase(Resolver resolver)
        {
            TypeRef baseRef = GetBaseType();
            if (baseRef != null)
            {
                TypeRefBase typeRefBase = baseRef.EvaluateType();
                if (typeRefBase != null)
                    typeRefBase.ResolveIndexerRef(resolver);
            }
        }

        /// <summary>
        /// Returns true if the code object is an <see cref="UnresolvedRef"/> or has any <see cref="UnresolvedRef"/> children.
        /// </summary>
        public override bool HasUnresolvedRef()
        {
            if (ChildListHelpers.HasUnresolvedRef(_typeParameters))
                return true;
            if (ChildListHelpers.HasUnresolvedRef(_constraintClauses))
                return true;
            return base.HasUnresolvedRef();
        }

        #region /* RESOLVE OTHER PARTS */

        private void ResolveOtherParts(string name, Resolver resolver, List<string> parentTypes)
        {
            // Find any other parts of the type, and try resolving with them
            CodeObject parent = _parent;
            if (parent is NamespaceDecl)
                ResolveOtherParts(((NamespaceDecl)parent).Namespace.Find(_name), name, resolver, parentTypes);
            else if (parent is TypeDecl)
            {
                // Look for other parts of a nested type in the parent type
                TypeDecl parentTypeDecl = (TypeDecl)parent;
                ResolveOtherParts(parentTypeDecl.Body.FindChildren(_name), name, resolver, parentTypes);

                // If the parent type is also partial, then look for other parts of the parent
                // type, and look in them for additional parts of the nested type.
                if (parentTypeDecl.IsPartial)
                {
                    if (parentTypes == null)
                        parentTypes = new List<string>();
                    parentTypes.Insert(0, _name);
                    parentTypeDecl.ResolveOtherParts(name, resolver, parentTypes);
                }
            }
        }

        private void ResolveOtherParts(object obj, string name, Resolver resolver, List<string> parentTypes)
        {
            if (obj is TypeDecl && obj != this && ((TypeDecl)obj).TypeParameterCount == TypeParameterCount)
                ResolveOtherPart((TypeDecl)obj, name, resolver, parentTypes);
            else if (obj is NamespaceTypeGroup)
            {
                foreach (object @object in (NamespaceTypeGroup)obj)
                {
                    if (@object is TypeDecl && @object != this && ((TypeDecl)@object).TypeParameterCount == TypeParameterCount)
                        ResolveOtherPart((TypeDecl)@object, name, resolver, parentTypes);
                }
            }
        }

        private void ResolveOtherPart(TypeDecl typeDecl, string name, Resolver resolver, List<string> parentTypes)
        {
            if (parentTypes == null)
                typeDecl.ResolveOtherPart(name, resolver);
            else
            {
                List<string> newList = new List<string>(parentTypes);
                string parentType = parentTypes[0];
                newList.RemoveAt(0);
                ResolveOtherParts(typeDecl.Body.FindChildren(parentType), name, resolver, newList.Count > 0 ? newList : null);
            }
        }

        private void ResolveOtherPart(string name, Resolver resolver)
        {
            if (IsPartial && _body != null)
                _body.ResolveRef(name, resolver);
        }

        private void ResolveIndexerOtherParts(Resolver resolver, List<string> parentTypes)
        {
            // Find any other parts of the type, and try resolving with them
            CodeObject parent = _parent;
            if (parent is NamespaceDecl)
                ResolveIndexerOtherParts(((NamespaceDecl)parent).Namespace.Find(_name), resolver, parentTypes);
            else if (parent is TypeDecl)
            {
                // Look for other parts of a nested type in the parent type
                TypeDecl parentTypeDecl = (TypeDecl)parent;
                ResolveIndexerOtherParts(parentTypeDecl.Body.FindChildren(_name), resolver, parentTypes);

                // If the parent type is also partial, then look for other parts of the parent
                // type, and look in them for additional parts of the nested type.
                if (parentTypeDecl.IsPartial)
                {
                    if (parentTypes == null)
                        parentTypes = new List<string>();
                    parentTypes.Insert(0, _name);
                    parentTypeDecl.ResolveIndexerOtherParts(resolver, parentTypes);
                }
            }
        }

        private void ResolveIndexerOtherParts(object obj, Resolver resolver, List<string> parentTypes)
        {
            if (obj is TypeDecl && obj != this)
                ResolveIndexerOtherPart((TypeDecl)obj, resolver, parentTypes);
            else if (obj is NamespaceTypeGroup)
            {
                foreach (object @object in (NamespaceTypeGroup)obj)
                {
                    if (@object is TypeDecl && @object != this)
                        ResolveIndexerOtherPart((TypeDecl)@object, resolver, parentTypes);
                }
            }
        }

        private void ResolveIndexerOtherPart(TypeDecl typeDecl, Resolver resolver, List<string> parentTypes)
        {
            if (parentTypes == null)
                typeDecl.ResolveIndexerOtherPart(resolver);
            else
            {
                List<string> newList = new List<string>(parentTypes);
                string parentType = parentTypes[0];
                newList.RemoveAt(0);
                ResolveIndexerOtherParts(typeDecl.Body.FindChildren(parentType), resolver, newList.Count > 0 ? newList : null);
            }
        }

        private void ResolveIndexerOtherPart(Resolver resolver)
        {
            if (IsPartial && _body != null)
                _body.ResolveIndexerRef(resolver);
        }

        #endregion

        /// <summary>
        /// Find a type argument in a base class for the specified type parameter.
        /// </summary>
        public virtual TypeRefBase FindTypeArgumentInBase(TypeParameterRef typeParameterRef)
        {
            return null;
        }

        /// <summary>
        /// Determine if the type argument counts match those in the specified <see cref="UnresolvedRef"/>.
        /// </summary>
        public bool DoTypeArgumentCountsMatch(UnresolvedRef unresolvedRef)
        {
            return (TypeParameterCount == unresolvedRef.TypeArgumentCount);
        }

        #endregion

        #region /* FORMATTING */

        /// <summary>
        /// True if the <see cref="Statement"/> has an argument.
        /// </summary>
        public override bool HasArgument
        {
            get { return true; }
        }

        /// <summary>
        /// True if the <see cref="Statement"/> has parens around its argument.
        /// </summary>
        public override bool HasArgumentParens
        {
            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 or from zero, also change any prefix attributes
                bool isFirstOnLine = (value != 0);
                if (_annotations != null && ((!isFirstOnLine && IsFirstOnLine) || (isFirstOnLine && !IsFirstOnLine)))
                {
                    foreach (Annotation annotation in _annotations)
                    {
                        if (annotation is Attribute)
                            annotation.IsFirstOnLine = isFirstOnLine;
                    }
                }

                base.NewLines = value;
            }
        }

        /// <summary>
        /// Determines if the code object only requires a single line for display.
        /// </summary>
        public override bool IsSingleLine
        {
            get
            {
                return (base.IsSingleLine && (_typeParameters == null || _typeParameters.Count == 0 || (!_typeParameters[0].IsFirstOnLine && _typeParameters.IsSingleLine))
                    && (_constraintClauses == null || _constraintClauses.Count == 0 || (!_constraintClauses[0].IsFirstOnLine && _constraintClauses.IsSingleLine)));
            }
            set
            {
                base.IsSingleLine = value;
                if (value)
                {
                    if (_typeParameters != null && _typeParameters.Count > 0)
                    {
                        _typeParameters[0].IsFirstOnLine = false;
                        _typeParameters.IsSingleLine = true;
                    }
                    if (_constraintClauses != null && _constraintClauses.Count > 0)
                    {
                        _constraintClauses[0].IsFirstOnLine = false;
                        _constraintClauses.IsSingleLine = true;
                    }
                }
            }
        }

        #endregion

        #region /* RENDERING */

        protected override void AsTextPrefix(CodeWriter writer, RenderFlags flags)
        {
            ModifiersHelpers.AsText(_modifiers, writer);
        }

        protected override void AsTextArgument(CodeWriter writer, RenderFlags flags)
        {
            AsTextName(writer, flags);
        }

        public void AsTextName(CodeWriter writer, RenderFlags flags)
        {
            if (flags.HasFlag(RenderFlags.Description) && _parent is TypeDecl)
            {
                ((TypeDecl)_parent).AsTextName(writer, flags);
                Dot.AsTextDot(writer);
            }

            writer.WriteIdentifier(_name, flags);
            if (HasTypeParameters)
                TypeParameter.AsTextTypeParameters(writer, _typeParameters, flags);
        }

        protected override void AsTextSuffix(CodeWriter writer, RenderFlags flags)
        {
            if (!HasConstraintClauses)
                base.AsTextSuffix(writer, flags);
        }

        protected override void AsTextAfter(CodeWriter writer, RenderFlags flags)
        {
            ConstraintClause.AsTextConstraints(writer, _constraintClauses, flags | RenderFlags.HasTerminator);
            base.AsTextAfter(writer, flags);
        }

        #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)

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
Web03 | 2.8.140721.1 | Last Updated 2 Dec 2012
Article Copyright 2012 by KenBeckett
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid