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

CodeDOM Classes for Solution and Project Files (Part 5)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (12 votes)
30 Nov 2012CDDL7 min read 29.9K   1.2K   18  
CodeDOM objects for VS Solution and Project files.
// 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 Nova.Parsing;
using Nova.Rendering;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Represents a symbolic reference that hasn't been resolved to a direct reference (includes references that
    /// are unresolved because they are ambiguous, or because the type arguments or method arguments don't match
    /// in number or type).
    /// </summary>
    public class UnresolvedRef : TypeRefBase
    {
        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        public UnresolvedRef(string name, bool isFirstOnLine)
            : base(name, isFirstOnLine)
        { }

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        public UnresolvedRef(string name)
            : base(name, false)
        { }

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        public UnresolvedRef(string name, int lineNumber, int column)
            : this(name, false)
        {
            _lineNumber = lineNumber;
            _columnNumber = (ushort)column;
        }

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        public UnresolvedRef(SymbolicRef symbolicRef)
            : this(symbolicRef.Name, symbolicRef.IsFirstOnLine)
        {
            Parent = symbolicRef.Parent;
            SetLineCol(symbolicRef);
        }

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        public UnresolvedRef(TypeRefBase typeRefBase, bool copyTypeArguments)
            : this(typeRefBase.Name, typeRefBase.IsFirstOnLine)
        {
            Parent = typeRefBase.Parent;
            SetLineCol(typeRefBase);
            if (copyTypeArguments)
                _typeArguments = typeRefBase.TypeArguments;
            _arrayRanks = typeRefBase.ArrayRanks;
        }

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        protected internal UnresolvedRef(Token token)
            : this(token.NonVerbatimText, token.IsFirstOnLine)
        {
            NewLines = token.NewLines;
            SetLineCol(token);
        }

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/>.
        /// </summary>
        protected internal UnresolvedRef(string name, Token token)
            : this(token)
        {
            // Initialize with token first, but then override to the specified name
            _reference = name;
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The name of the <see cref="UnresolvedRef"/>.
        /// </summary>
        public override string Name
        {
            get { return (string)_reference; }
        }

        /// <summary>
        /// The descriptive category of the <see cref="SymbolicRef"/>.
        /// </summary>
        public override string Category
        {
            get { return "unresolved"; }
        }

        /// <summary>
        /// True if this <see cref="UnresolvedRef"/> is the target of an assignment.
        /// </summary>
        public bool IsTargetOfAssignment
        {
            get
            {
                // Handle "expression.UnresolvedRef = ..."
                if (_parent is Dot && _parent.Parent is Assignment && ((Assignment)_parent.Parent).Left == _parent && ((Dot)_parent).Right == this)
                    return true;
                // Handle "expression[...] = ..." (where the hidden IndexerRef is the UnresolvedRef
                if (_parent is Index && _parent.Parent is Assignment && ((Assignment)_parent.Parent).Left == _parent && _parent.HiddenRef == this)
                    return true;
                // Handle "UnresolvedRef = ..."
                if (_parent is Assignment && ((Assignment)_parent).Left == this)
                {
                    // Don't treat as the target of an assignment in the special case of an "embedded collection" initialization.
                    // This is identified by the both the parent AND the right side of the assignment being an Initializer.  In such
                    // a case, the property being assigned should be a collection, and the getter will be used instead of the setter
                    // and the items in the Initializer will be added to the existing collection.
                    Assignment assignment = (Assignment)_parent;
                    return !(assignment.Parent is Initializer && assignment.Right is Initializer);
                }
                return false;
            }
        }

        /// <summary>
        /// Returns true if the UnresolvedRef represents an explicit interface implementation.
        /// </summary>
        public bool IsExplicitInterfaceImplementation
        {
            get
            {
                if (Parent is Dot)
                {
                    if (Parent.Parent is MethodDeclBase)
                        return (((MethodDeclBase)Parent.Parent).ExplicitInterfaceExpression == Parent);
                    if (Parent.Parent is PropertyDeclBase)
                        return (((PropertyDeclBase)Parent.Parent).ExplicitInterfaceExpression == Parent);
                }
                return false;
            }
        }

        #endregion

        #region /* STATIC METHODS */

        /// <summary>
        /// Create an <see cref="UnresolvedRef"/> from the specified <see cref="Token"/>.
        /// </summary>
        public static UnresolvedRef Create(Token identifier)
        {
            return (identifier != null ? new UnresolvedRef(identifier) : null);
        }

        #endregion

        #region /* METHODS */

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

        /// <summary>
        /// Determine if the current reference refers to the same code object as the specified reference.
        /// </summary>
        public override bool IsSameRef(SymbolicRef symbolicRef)
        {
            UnresolvedRef unresolvedRef = (symbolicRef is AliasRef ? ((AliasRef)symbolicRef).Alias.Expression.SkipPrefixes() : symbolicRef) as UnresolvedRef;
            if (unresolvedRef == null || (string)Reference != (string)unresolvedRef.Reference)
                return false;

            // The strings of the UnresolvedRefs match, but we have to also verify that any Dot prefixes
            // match - if either side has one, they must match, otherwise neither side can have one.
            Dot parentDot = _parent as Dot;
            Dot parentDot2 = symbolicRef.Parent as Dot;
            SymbolicRef dotPrefix = (parentDot != null && parentDot.Right == this ? parentDot.Left as SymbolicRef : null);
            SymbolicRef dotPrefix2 = (parentDot2 != null && parentDot2.Right == this ? parentDot2.Left as SymbolicRef : null);
            return (dotPrefix == null || dotPrefix2 == null || dotPrefix.IsSameRef(dotPrefix2));
        }

        /// <summary>
        /// Calculate a hash code for the referenced object which is the same for all references where IsSameRef() is true.
        /// </summary>
        public override int GetIsSameRefHashCode()
        {
            // Make the hash codes as unique as possible while still ensuring that they are identical
            // for any objects for which IsSameRef() returns true.
            int hashCode = base.GetIsSameRefHashCode();
            if (_parent is Dot && ((Dot)_parent).Right == this && ((Dot)_parent).Left is SymbolicRef)
                hashCode ^= ((SymbolicRef)((Dot)_parent).Left).GetIsSameRefHashCode();
            return hashCode;
        }

        /// <summary>
        /// Determine if the specified TypeRefBase refers to the same generic type, regardless of actual type arguments.
        /// </summary>
        public override bool IsSameGenericType(TypeRefBase typeRefBase)
        {
            return (typeRefBase is UnresolvedRef && (_typeArguments != null ? _typeArguments.Count : 0) == typeRefBase.TypeArgumentCount && Name == typeRefBase.Name);
        }

        /// <summary>
        /// Dispose the <see cref="UnresolvedRef"/>.
        /// </summary>
        public override void Dispose()
        {
            _parent = null;
        }

        /// <summary>
        /// Get the full name of the object, including the namespace name.
        /// </summary>
        public override string GetFullName()
        {
            return Reference as string;
        }

        #endregion

        #region /* PARSING */

        internal static new void AddParsePoints()
        {
            // Parse generic type and method references and arrays here in UnresolvedRef, because they may
            // or may not parse as resolved.  Built-in types and '?' nullable types are parsed in TypeRef,
            // because they will parse as resolved.

            // Use a parse-priority of 100 (IndexerDecl uses 0, Index uses 200, Attribute uses 300)
            Parser.AddParsePoint(ParseTokenArrayStart, 100, ParseArrayRanks);

            // Use a parse-priority of 100 (GenericMethodDecl uses 0, LessThan uses 200)
            Parser.AddParsePoint(ParseTokenArgumentStart, 100, ParseTypeArguments);
            // Support alternate symbols for doc comments:
            // Use a parse-priority of 100 (GenericMethodDecl uses 0, PropertyDeclBase uses 200, BlockDecl uses 300, Initializer uses 400)
            Parser.AddParsePoint(ParseTokenAltArgumentStart, 100, ParseAltTypeArguments);
        }

        /// <summary>
        /// Parse array ranks.
        /// </summary>
        public static Expression ParseArrayRanks(Parser parser, CodeObject parent, ParseFlags flags)
        {
            // Verify that we seem to match an array rank pattern
            // (otherwise abort so it can be parsed as an array index or Attribute)
            if (parser.HasUnusedIdentifier && PeekArrayRanks(parser))
                return new UnresolvedRef(parser, parent, true, false);
            return null;
        }

        /// <summary>
        /// Parse type arguments.
        /// </summary>
        public static Expression ParseTypeArguments(Parser parser, CodeObject parent, ParseFlags flags)
        {
            // Verify that we seem to match a type argument list pattern
            // (otherwise abort so that the LessThan operator will get a chance to parse it)
            if (parser.HasUnusedIdentifier && PeekTypeArguments(parser, ParseTokenArgumentEnd, flags))
                return new UnresolvedRef(parser, parent, false, true);
            return null;
        }

        /// <summary>
        /// Parse type arguments using the alternate delimiters.
        /// </summary>
        public static Expression ParseAltTypeArguments(Parser parser, CodeObject parent, ParseFlags flags)
        {
            // Verify that we seem to match a type argument list pattern
            // (otherwise abort so that PropertyDeclBase will get a chance to parse it)
            // Only supported inside documentation comments - subroutines will look for the
            // appropriate delimiters according to the parser state.
            if (parser.InDocComment && parser.HasUnusedIdentifier && PeekTypeArguments(parser, ParseTokenAltArgumentEnd, flags))
                return new UnresolvedRef(parser, parent, false, true);
            return null;
        }

        /// <summary>
        /// Construct an unresolved reference to an array type, or generic type.
        /// </summary>
        protected UnresolvedRef(Parser parser, CodeObject parent, bool isArray, bool isGeneric)
            : base(parser, parent)
        {
            Token token = parser.RemoveLastUnusedToken();
            _reference = token.NonVerbatimText;  // Get the type name
            NewLines = token.NewLines;
            SetLineCol(token);
            
            if (isArray)
            {
                MoveCommentsAsPost(token);   // Get any comments after the identifier
                ParseArrayRanks(parser);     // Parse the array ranks
            }
            else if (isGeneric)
            {
                MoveCommentsAsPost(token);   // Get any comments after the identifier
                _typeArguments = ParseTypeArgumentList(parser, this);  // Parse the type arguments

                // Check for array ranks on the generic type
                if (parser.TokenText == ParseTokenArrayStart && PeekArrayRanks(parser))
                    ParseArrayRanks(parser);
            }
        }

        #endregion

        #region /* RENDERING */

        public override void AsTextExpression(CodeWriter writer, RenderFlags flags)
        {
            UpdateLineCol(writer, flags);
            writer.Write((string)_reference);
            AsTextTypeArguments(writer, _typeArguments, flags);
            AsTextArrayRanks(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)


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