// 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 Mono.Cecil;
using Nova.Parsing;
using Nova.Rendering;
using Nova.Resolving;
namespace Nova.CodeDOM
{
/// <summary>
/// Represents an alias to a <see cref="Namespace"/> or a type (<see cref="TypeDecl"/>, or <see cref="TypeDefinition"/>/<see cref="Type"/>).
/// </summary>
/// <remarks>
/// An alias can only be defined at the <see cref="CodeUnit"/> or <see cref="NamespaceDecl"/> level, and it can only
/// be used within that scope. The syntax is "using aliasname = expression;" where the expression evaluates to either
/// a <see cref="NamespaceRef"/> or a <see cref="TypeRef"/>.
/// </remarks>
public class Alias : Statement, INamedCodeObject, ITypeDecl, INamespace
{
#region /* FIELDS */
protected string _name;
/// <summary>
/// The expression should be a <see cref="NamespaceRef"/>, <see cref="TypeRef"/>, or a <see cref="Dot"/> operator
/// whose right-most operand evaluates to a <see cref="NamespaceRef"/> or <see cref="TypeRef"/>. It can also be
/// an <see cref="UnresolvedRef"/>.
/// </summary>
protected Expression _expression;
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create an <see cref="Alias"/> with the specified name to the specified <see cref="Expression"/>.
/// </summary>
public Alias(string name, Expression expression)
{
_name = name;
Expression = expression;
}
#endregion
#region /* PROPERTIES */
/// <summary>
/// The name of the <see cref="Alias"/>.
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
/// The descriptive category of the code object.
/// </summary>
public string Category
{
get
{
Expression expression = _expression.SkipPrefixes();
string category;
if (expression is NamespaceRef)
category = "namespace";
else if (expression is TypeRef)
category = "type";
else
category = "unknown";
return category + " alias";
}
}
/// <summary>
/// The aliased <see cref="Expression"/>.
/// </summary>
public Expression Expression
{
get { return _expression; }
set { SetField(ref _expression, value, true); }
}
/// <summary>
/// The keyword associated with the <see cref="Statement"/>.
/// </summary>
public override string Keyword
{
get { return ParseToken; }
}
/// <summary>
/// True if this is a namespace alias.
/// </summary>
public bool IsNamespace
{
get { return _expression.SkipPrefixes() is NamespaceRef; }
}
/// <summary>
/// The namespace that this alias refers to (null if not a namespace alias).
/// </summary>
public NamespaceRef Namespace
{
get { return _expression.SkipPrefixes() as NamespaceRef; }
}
/// <summary>
/// True if this is a type alias.
/// </summary>
public bool IsType
{
get { return _expression.SkipPrefixes() is TypeRef; }
}
/// <summary>
/// The type that this alias refers to (null if not a type alias).
/// </summary>
public TypeRef Type
{
get { return _expression.EvaluateType() as TypeRef; }
}
#region /* ITYPEDECL PROPERTIES */
/// <summary>
/// Get the number of <see cref="TypeParameter"/>s in the aliased type (if any).
/// </summary>
public int TypeParameterCount
{
get
{
TypeRef typeRefBase = Type;
return (typeRefBase != null ? typeRefBase.TypeArgumentCount : 0);
}
}
/// <summary>
/// True if the aliased type is abstract.
/// </summary>
public bool IsAbstract
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsAbstract);
}
}
/// <summary>
/// True if the aliased type is a class.
/// </summary>
public bool IsClass
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsClass);
}
}
/// <summary>
/// True if the aliased type is a delegate type.
/// </summary>
public bool IsDelegateType
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsDelegateType);
}
}
/// <summary>
/// True if the aliased type is an enum.
/// </summary>
public bool IsEnum
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsEnum);
}
}
/// <summary>
/// Always <c>false</c>.
/// </summary>
public bool IsGenericParameter
{
get { return false; }
}
/// <summary>
/// True if the aliased type is a generic type.
/// </summary>
public bool IsGenericType
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsGenericType);
}
}
/// <summary>
/// True if the aliased type is an interface.
/// </summary>
public bool IsInterface
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsInterface);
}
}
/// <summary>
/// True if the aliased type is a nested type.
/// </summary>
public bool IsNested
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsNested);
}
}
/// <summary>
/// True if the aliased type is a nullable type.
/// </summary>
public bool IsNullableType
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsNullableType);
}
}
/// <summary>
/// True if the aliased type is a partial type.
/// </summary>
public bool IsPartial
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsPartial);
}
}
/// <summary>
/// True if the aliased type is a struct.
/// </summary>
public bool IsStruct
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsUserStruct);
}
}
/// <summary>
/// True if the aliased type is a value type.
/// </summary>
public bool IsValueType
{
get
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsValueType);
}
}
#endregion
#region /* INAMESPACE PROPERTIES */
/// <summary>
/// The full name of the referenced <see cref="Namespace"/>, including any parent namespaces.
/// </summary>
public string FullName
{
get
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null ? namespaceRef.FullName : null);
}
}
/// <summary>
/// The dictionary of child objects in the referenced <see cref="Namespace"/>.
/// </summary>
public NamespaceTypeDictionary Children
{
get
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null ? namespaceRef.Children : null);
}
}
/// <summary>
/// Determines if the referenced <see cref="Namespace"/> is root-level (global or extern alias).
/// </summary>
public bool IsRootLevel
{
get
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null && namespaceRef.IsRootLevel);
}
}
/// <summary>
/// Determines if the referenced <see cref="Namespace"/> is the project-global namespace.
/// </summary>
public bool IsGlobal
{
get
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null && namespaceRef.IsGlobal);
}
}
/// <summary>
/// True if the referenced <see cref="Namespace"/> has <see cref="NamespaceDecl"/> declarations in the current
/// project, otherwise false (meaning items in the namespace exist only in imported assemblies and projects).
/// </summary>
public bool HasDeclarationsInProject
{
get
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null && namespaceRef.HasDeclarationsInProject);
}
}
#endregion
#endregion
#region /* METHODS */
/// <summary>
/// Create a reference to the <see cref="Alias"/>.
/// </summary>
/// <param name="isFirstOnLine">True if the reference should be displayed on a new line.</param>
/// <returns>An <see cref="AliasRef"/>.</returns>
public override SymbolicRef CreateRef(bool isFirstOnLine)
{
return new AliasRef(this, isFirstOnLine);
}
/// <summary>
/// Create a reference to the <see cref="Alias"/>.
/// </summary>
/// <returns>An <see cref="AliasRef"/>.</returns>
public TypeRef CreateRef(bool isFirstOnLine, ChildList<Expression> typeArguments, List<int> arrayRanks)
{
// Ignore any type arguments - an AliasRef can't have any
return new AliasRef(this, isFirstOnLine, arrayRanks);
}
/// <summary>
/// Create a reference to the <see cref="Alias"/>.
/// </summary>
/// <returns>An <see cref="AliasRef"/>.</returns>
public TypeRef CreateRef(bool isFirstOnLine, ChildList<Expression> typeArguments)
{
// Ignore any type arguments - an AliasRef can't have any
return new AliasRef(this, isFirstOnLine);
}
/// <summary>
/// Create a reference to the <see cref="Alias"/>.
/// </summary>
/// <returns>An <see cref="AliasRef"/>.</returns>
public TypeRef CreateRef(ChildList<Expression> typeArguments, List<int> arrayRanks)
{
// Ignore any type arguments - an AliasRef can't have any
return new AliasRef(this, false, arrayRanks);
}
/// <summary>
/// Create a reference to the <see cref="Alias"/>.
/// </summary>
/// <returns>An <see cref="AliasRef"/>.</returns>
public TypeRef CreateRef(ChildList<Expression> typeArguments)
{
// Ignore any type arguments - an AliasRef can't have any
return new AliasRef(this, false);
}
/// <summary>
/// Create an array reference to this <see cref="Alias"/>.
/// </summary>
/// <returns>An <see cref="AliasRef"/>.</returns>
public TypeRef CreateArrayRef(bool isFirstOnLine, params int[] ranks)
{
return new AliasRef(this, isFirstOnLine, ranks);
}
/// <summary>
/// Create an array reference to this <see cref="Alias"/>.
/// </summary>
/// <returns>An <see cref="AliasRef"/>.</returns>
public TypeRef CreateArrayRef(params int[] ranks)
{
return new AliasRef(this, false, ranks);
}
/// <summary>
/// Create a nullable reference to this <see cref="Alias"/>.
/// </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="Alias"/>.
/// </summary>
/// <returns>A <see cref="TypeRef"/>.</returns>
public TypeRef CreateNullableRef()
{
return TypeRef.CreateNullable(CreateRef());
}
#region /* ITYPEDECL METHODS */
/// <summary>
/// Get the base type of the aliased type.
/// </summary>
public TypeRef GetBaseType()
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetBaseType() as TypeRef : null);
}
/// <summary>
/// Get the non-static constructor of the aliased type with the specified parameters.
/// </summary>
public ConstructorRef GetConstructor(params TypeRefBase[] parameterTypes)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetConstructor(parameterTypes) : null);
}
/// <summary>
/// Get all non-static constructors of the aliased type.
/// </summary>
public NamedCodeObjectGroup GetConstructors(bool currentPartOnly)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetConstructors(currentPartOnly) : null);
}
/// <summary>
/// Get all non-static constructors of the aliased type.
/// </summary>
public NamedCodeObjectGroup GetConstructors()
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetConstructors(false) : null);
}
/// <summary>
/// Get the method of the aliased type with the specified name and parameter types.
/// </summary>
public MethodRef GetMethod(string name, params TypeRefBase[] parameterTypes)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetMethod(name, parameterTypes) : null);
}
/// <summary>
/// Get all methods of the aliased type with the specified name.
/// </summary>
public void GetMethods(string name, bool searchBaseClasses, NamedCodeObjectGroup results)
{
TypeRef typeRef = Type;
if (typeRef != null)
typeRef.GetMethods(name, searchBaseClasses, 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)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetMethods(name, searchBaseClasses) : null);
}
/// <summary>
/// Get all methods with the specified name.
/// </summary>
/// <param name="name">The method name.</param>
public List<MethodRef> GetMethods(string name)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetMethods(name, false) : null);
}
/// <summary>
/// Get the property of the aliased type with the specified name.
/// </summary>
public PropertyRef GetProperty(string name)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetProperty(name) : null);
}
/// <summary>
/// Get the field of the aliased type with the specified name.
/// </summary>
public FieldRef GetField(string name)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetField(name) : null);
}
/// <summary>
/// Get the nested type of the aliased type with the specified name.
/// </summary>
public TypeRef GetNestedType(string name)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetNestedType(name) : null);
}
/// <summary>
/// Get the delegate parameters (if any) of the aliased type.
/// </summary>
public ICollection GetDelegateParameters()
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetDelegateParameters() : null);
}
/// <summary>
/// Get the delegate return type (if any) of the aliased type.
/// </summary>
public TypeRefBase GetDelegateReturnType()
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.GetDelegateReturnType() : null);
}
/// <summary>
/// Determine if the aliased type is assignable from the specified type.
/// </summary>
public bool IsAssignableFrom(TypeRef fromTypeRef)
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsAssignableFrom(fromTypeRef));
}
/// <summary>
/// Determine if the aliased type is a subclass of the specified type.
/// </summary>
public bool IsSubclassOf(TypeRef classTypeRef)
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsSubclassOf(classTypeRef));
}
/// <summary>
/// Determine if the aliased type implements the specified interface type.
/// </summary>
public bool IsImplementationOf(TypeRef interfaceTypeRef)
{
TypeRef typeRef = Type;
return (typeRef != null && typeRef.IsImplementationOf(interfaceTypeRef));
}
#endregion
#region /* INAMESPACE METHODS */
/// <summary>
/// Add a child <see cref="Namespace"/> to the referenced <see cref="Namespace"/>.
/// </summary>
public void Add(Namespace @namespace)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Add(@namespace);
}
/// <summary>
/// Add a <see cref="TypeDecl"/> to the referenced <see cref="Namespace"/>.
/// </summary>
public void Add(TypeDecl typeDecl)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Add(typeDecl);
}
/// <summary>
/// Add a <see cref="TypeDefinition"/> to the referenced <see cref="Namespace"/>.
/// </summary>
public void Add(TypeDefinition typeDefinition)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Add(typeDefinition);
}
/// <summary>
/// Add a <see cref="Type"/> to the referenced <see cref="Namespace"/>.
/// </summary>
public void Add(Type type)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Add(type);
}
/// <summary>
/// Remove a child <see cref="Namespace"/> from the referenced <see cref="Namespace"/>.
/// </summary>
public void Remove(Namespace @namespace)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Remove(@namespace);
}
/// <summary>
/// Remove a <see cref="TypeDecl"/> from the referenced <see cref="Namespace"/>.
/// </summary>
public void Remove(TypeDecl typeDecl)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Remove(typeDecl);
}
/// <summary>
/// Remove a <see cref="TypeDefinition"/> from the referenced <see cref="Namespace"/>.
/// </summary>
public void Remove(TypeDefinition typeDefinition)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Remove(typeDefinition);
}
/// <summary>
/// Remove a <see cref="Type"/> from the referenced <see cref="Namespace"/>.
/// </summary>
public void Remove(Type type)
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.Remove(type);
}
/// <summary>
/// Remove all items from the referenced <see cref="Namespace"/>.
/// </summary>
public void RemoveAll()
{
NamespaceRef namespaceRef = Namespace;
if (namespaceRef != null)
namespaceRef.RemoveAll();
}
/// <summary>
/// Find or create a child <see cref="Namespace"/>, including any missing parent namespaces.
/// </summary>
public Namespace FindOrCreateChildNamespace(string namespaceName)
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null ? namespaceRef.FindOrCreateChildNamespace(namespaceName) : null);
}
/// <summary>
/// Parse the specified name into a child <see cref="NamespaceRef"/> or <see cref="TypeRef"/> on the referenced namespace,
/// or a <see cref="Dot"/> expression that evaluates to one.
/// </summary>
public Expression ParseName(string name)
{
NamespaceRef namespaceRef = Namespace;
return (namespaceRef != null ? namespaceRef.ParseName(name) : null);
}
#endregion
/// <summary>
/// Find a child <see cref="Namespace"/>, <see cref="TypeDecl"/>, or <see cref="TypeDefinition"/>/<see cref="Type"/> with
/// the specified name.
/// </summary>
/// <returns>The child object if found, otherwise null.</returns>
public object Find(string name)
{
if (IsNamespace)
return Namespace.Find(name);
if (IsType)
return Type.GetNestedType(name).Reference;
return null;
}
/// <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>
/// Deep-clone the code object.
/// </summary>
public override CodeObject Clone()
{
Alias clone = (Alias)base.Clone();
clone.CloneField(ref clone._expression, _expression);
return clone;
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
public string GetFullName(bool descriptive)
{
return _name;
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
public string GetFullName()
{
return _name;
}
#endregion
#region /* PARSING */
/// <summary>
/// The token used to parse the code object.
/// </summary>
public const string ParseToken = "using";
/// <summary>
/// The token used to parse between the alias name and expression.
/// </summary>
public const string ParseTokenSeparator = "=";
internal static void AddParsePoints()
{
// Use a parse-priority of 0 (UsingDirective uses 100, Using uses 200)
Parser.AddParsePoint(ParseToken, Parse, typeof(NamespaceDecl));
}
/// <summary>
/// Parse an <see cref="Alias"/>.
/// </summary>
public static Alias Parse(Parser parser, CodeObject parent, ParseFlags flags)
{
// Continue only if it looks like an alias assignment instead of a UsingDirective
if (parser.PeekNextTokenText() == ParseTokenSeparator || parser.PeekNextTokenText() == ParseTokenSeparator)
return new Alias(parser, parent);
return null;
}
protected Alias(Parser parser, CodeObject parent)
: base(parser, parent)
{
parser.NextToken(); // Move past 'using'
_name = parser.GetIdentifierText(); // Parse the name
parser.NextToken(); // Move past '='
SetField(ref _expression, Expression.Parse(parser, this, true), false);
ParseTerminator(parser);
}
#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)
{
_expression = (Expression)_expression.Resolve(ResolveCategory.NamespaceOrType, flags);
return this;
}
/// <summary>
/// Resolve child code objects that match the specified name.
/// </summary>
public void ResolveRef(string name, Resolver resolver)
{
Expression expression = _expression.SkipPrefixes();
if (expression is NamespaceRef)
((NamespaceRef)expression).Namespace.ResolveRef(name, resolver, false);
else if (expression is TypeRef)
((TypeRef)expression).ResolveRef(name, resolver);
}
/// <summary>
/// Resolve indexers.
/// </summary>
public void ResolveIndexerRef(Resolver resolver)
{
TypeRef typeRef = Type;
if (typeRef != null)
typeRef.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 (_expression != null && _expression.HasUnresolvedRef())
return true;
return base.HasUnresolvedRef();
}
/// <summary>
/// Find a type argument in a base class for the specified type parameter.
/// </summary>
public TypeRefBase FindTypeArgumentInBase(TypeParameterRef typeParameterRef)
{
TypeRef typeRef = Type;
return (typeRef != null ? typeRef.FindTypeArgumentInBase(typeParameterRef) : null);
}
#endregion
#region /* FORMATTING */
/// <summary>
/// True if the <see cref="Statement"/> has parens around its argument.
/// </summary>
public override bool HasArgumentParens
{
get { return false; }
}
/// <summary>
/// True if the <see cref="Statement"/> has a terminator character by default.
/// </summary>
public override bool HasTerminatorDefault
{
get { return true; }
}
/// <summary>
/// Determine a default of 1 or 2 newlines when adding items to a <see cref="Block"/>.
/// </summary>
public override int DefaultNewLines(CodeObject previous)
{
// Default to a preceeding blank line if the object has first-on-line annotations
if (HasFirstOnLineAnnotations)
return 2;
// Default to a preceeding blank line if the previous object was another alias directive
// with a different root namespace, otherwise use a single newline.
if (previous is Alias)
{
SymbolicRef symbolicRef = ((Alias)previous).Expression.FirstPrefix() as SymbolicRef;
if (symbolicRef != null && !symbolicRef.IsSameRef(Expression.FirstPrefix() as SymbolicRef))
return 2;
return 1;
}
// Default to no preceeding blank line if the previous object was a using directive with
// the same root namespace, otherwise use a preceeding blank line.
if (previous is UsingDirective)
{
SymbolicRef symbolicRef = ((UsingDirective)previous).Namespace.FirstPrefix() as SymbolicRef;
if (symbolicRef != null && symbolicRef.IsSameRef(Expression.FirstPrefix() as SymbolicRef))
return 1;
return 2;
}
// Default to a preceeding blank line if the object is a different type than the previous one
if (previous.GetType() != GetType())
return 2;
return 1;
}
/// <summary>
/// Determines if the code object only requires a single line for display.
/// </summary>
public override bool IsSingleLine
{
get { return (base.IsSingleLine && (_expression == null || (!_expression.IsFirstOnLine && _expression.IsSingleLine))); }
set
{
base.IsSingleLine = value;
if (value && _expression != null)
{
_expression.IsFirstOnLine = false;
_expression.IsSingleLine = true;
}
}
}
#endregion
#region /* RENDERING */
protected override void AsTextArgument(CodeWriter writer, RenderFlags flags)
{
writer.WriteIdentifier(_name, flags);
writer.Write(" " + ParseTokenSeparator);
if (_expression != null)
_expression.AsText(writer, flags | RenderFlags.PrefixSpace);
}
#endregion
}
}