// 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.Generic;
using System.Reflection;
using Nova.Parsing;
using Nova.Rendering;
using Nova.Utilities;
namespace Nova.CodeDOM
{
/// <summary>
/// The common base class of all symbolic references, such as <see cref="NamespaceRef"/>, <see cref="TypeRefBase"/>
/// (common base of <see cref="TypeRef"/>, <see cref="MethodRef"/>, <see cref="UnresolvedRef"/>), <see cref="VariableRef"/>,
/// <see cref="SelfRef"/>, <see cref="GotoTargetRef"/>, <see cref="ExternAliasRef"/>, and <see cref="DirectiveSymbolRef"/>.
/// </summary>
/// <remarks>
/// A symbolic reference can consist of a string (unresolved reference), or a reference to a <see cref="Namespace"/>
/// or <see cref="Type"/> (derived from <see cref="MemberInfo"/>), or a reference to a Decl code object (when the code is in the
/// same solution) or a <see cref="MemberInfo"/> object (when the code is in a referenced assembly).
/// </remarks>
public abstract class SymbolicRef : Expression
{
#region /* FIELDS */
// Reference can be a string (unresolved), INamedCodeObject, AnonymousMethod, or MemberInfo object.
// It can also be null: for ThisRef, BaseRef, and VarTypeRef.
protected object _reference;
#endregion
#region /* CONSTRUCTORS */
protected SymbolicRef(string name, bool isFirstOnLine)
{
_reference = name;
IsFirstOnLine = isFirstOnLine;
}
protected SymbolicRef(INamedCodeObject namedCodeObject, bool isFirstOnLine)
{
_reference = namedCodeObject;
IsFirstOnLine = isFirstOnLine;
}
protected SymbolicRef(AnonymousMethod anonymousMethod, bool isFirstOnLine)
{
_reference = anonymousMethod;
IsFirstOnLine = isFirstOnLine;
}
protected SymbolicRef(MemberInfo memberInfo, bool isFirstOnLine)
{
_reference = memberInfo;
IsFirstOnLine = isFirstOnLine;
}
protected SymbolicRef(ParameterInfo parameterInfo, bool isFirstOnLine)
{
_reference = parameterInfo;
IsFirstOnLine = isFirstOnLine;
}
protected SymbolicRef(object obj)
{
_reference = obj;
}
#endregion
#region /* PROPERTIES */
/// <summary>
/// The name of the <see cref="SymbolicRef"/>.
/// </summary>
public virtual string Name
{
get
{
if (_reference is INamedCodeObject)
return ((INamedCodeObject)_reference).Name;
if (_reference is MemberInfo)
return ((MemberInfo)_reference).Name;
return (_reference != null ? _reference.ToString() : null);
}
}
/// <summary>
/// The code object to which the <see cref="SymbolicRef"/> refers.
/// </summary>
public virtual object Reference
{
get { return _reference; }
}
/// <summary>
/// The descriptive category of the <see cref="SymbolicRef"/>.
/// </summary>
public virtual string Category
{
get
{
object reference = Reference;
if (reference is INamedCodeObject)
return ((INamedCodeObject)reference).Category;
if (reference is MemberInfo)
return MemberInfoUtil.GetCategory((MemberInfo)reference);
if (reference is ParameterInfo)
return ParameterInfoUtil.GetCategory((ParameterInfo)reference);
return null;
}
}
#endregion
#region /* STATIC METHODS */
/// <summary>
/// Implicit conversion of a <see cref="Namespace"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="NamespaceRef"/>).
/// </summary>
/// <remarks>This allows <see cref="Namespace"/>s to be passed directly to any method expecting a <see cref="SymbolicRef"/> type
/// without having to create a reference first.</remarks>
/// <param name="namespace">The <see cref="Namespace"/> to be converted.</param>
/// <returns>A generated <see cref="NamespaceRef"/> to the specified <see cref="Namespace"/>.</returns>
public static implicit operator SymbolicRef(Namespace @namespace)
{
return @namespace.CreateRef();
}
/// <summary>
/// Implicit conversion of a <see cref="Type"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="TypeRef"/>).
/// </summary>
/// <remarks>This allows Types such as <c>typeof(int)</c> to be passed directly to any method
/// expecting a <see cref="SymbolicRef"/> type without having to create a reference first.</remarks>
/// <param name="type">The <see cref="Type"/> to be converted.</param>
/// <returns>A generated <see cref="TypeRef"/> to the specified <see cref="Type"/>.</returns>
public static implicit operator SymbolicRef(Type type)
{
return TypeRef.Create(type);
}
/// <summary>
/// Implicit conversion of a <see cref="MethodBase"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="MethodRef"/> or <see cref="ConstructorRef"/>).
/// </summary>
/// <remarks>This allows <see cref="MethodBase"/>s (<see cref="MethodInfo"/>s or <see cref="ConstructorInfo"/>s) to be passed directly
/// to any method expecting a <see cref="SymbolicRef"/> type without having to create a reference first.</remarks>
/// <param name="methodBase">The <see cref="MethodBase"/> to be converted.</param>
/// <returns>A generated <see cref="MethodRef"/> to the specified <see cref="MethodBase"/>.</returns>
public static implicit operator SymbolicRef(MethodBase methodBase)
{
return MethodRef.Create(methodBase);
}
/// <summary>
/// Implicit conversion of a <see cref="PropertyInfo"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="PropertyRef"/>).
/// </summary>
/// <remarks>This allows <see cref="PropertyInfo"/>s to be passed directly to any method expecting a <see cref="SymbolicRef"/> type without
/// having to create a reference first.</remarks>
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> to be converted.</param>
/// <returns>A generated <see cref="PropertyRef"/> to the specified <see cref="PropertyInfo"/>.</returns>
public static implicit operator SymbolicRef(PropertyInfo propertyInfo)
{
return new PropertyRef(propertyInfo);
}
/// <summary>
/// Implicit conversion of a <see cref="EventInfo"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="EventRef"/>).
/// </summary>
/// <remarks>This allows <see cref="EventInfo"/>s to be passed directly to any method expecting a <see cref="SymbolicRef"/> type without
/// having to create a reference first.</remarks>
/// <param name="eventInfo">The <see cref="EventInfo"/> to be converted.</param>
/// <returns>A generated <see cref="EventRef"/> to the specified <see cref="EventInfo"/>.</returns>
public static implicit operator SymbolicRef(EventInfo eventInfo)
{
return new EventRef(eventInfo);
}
/// <summary>
/// Implicit conversion of a <see cref="FieldInfo"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="FieldRef"/>).
/// </summary>
/// <remarks>This allows <see cref="FieldInfo"/>s to be passed directly to any method expecting a <see cref="SymbolicRef"/> type without
/// having to create a reference first.</remarks>
/// <param name="fieldInfo">The <see cref="FieldInfo"/> to be converted.</param>
/// <returns>A generated <see cref="FieldRef"/> to the specified <see cref="FieldInfo"/>.</returns>
public static implicit operator SymbolicRef(FieldInfo fieldInfo)
{
return new FieldRef(fieldInfo);
}
/// <summary>
/// Implicit conversion of a <see cref="TypeParameter"/> to a <see cref="SymbolicRef"/> (actually, a <see cref="TypeParameterRef"/>).
/// </summary>
/// <remarks>This allows <see cref="TypeParameter"/>s to be passed directly to any method expecting a <see cref="SymbolicRef"/> type
/// without having to create a reference first.</remarks>
/// <param name="typeParameter">The <see cref="TypeParameter"/> to be converted.</param>
/// <returns>A generated <see cref="TypeParameterRef"/> to the specified <see cref="TypeParameter"/>.</returns>
public static implicit operator SymbolicRef(TypeParameter typeParameter)
{
return typeParameter.CreateRef();
}
/// <summary>
/// Implicit conversion of a <see cref="Statement"/> to a <see cref="SymbolicRef"/>.
/// </summary>
/// <remarks>This allows declarations to be passed directly to any method expecting a <see cref="SymbolicRef"/>
/// type without having to create a reference first.</remarks>
/// <param name="statement">The <see cref="Statement"/> to be converted.</param>
/// <returns>A generated <see cref="SymbolicRef"/> to the specified <see cref="Statement"/>.</returns>
public static implicit operator SymbolicRef(Statement statement)
{
return statement.CreateRef();
}
#endregion
#region /* METHODS */
/// <summary>
/// Get the declaring type of the referenced object (returns null if none).
/// </summary>
/// <remarks>
/// References which have declaring types include: <see cref="MethodRef"/> (and <see cref="ConstructorRef"/>, <see cref="OperatorRef"/>),
/// <see cref="PropertyRef"/> (and <see cref="IndexerRef"/>), <see cref="EventRef"/>, <see cref="FieldRef"/>, <see cref="EnumMemberRef"/>.
/// </remarks>
public virtual TypeRefBase GetDeclaringType()
{
return null;
}
/// <summary>
/// Returns the <see cref="DocSummary"/> documentation comment, or null if none exists.
/// </summary>
public override DocSummary GetDocSummary()
{
DocSummary docSummary = null;
object reference = Reference;
if (reference is CodeObject)
docSummary = ((CodeObject)reference).GetDocSummary();
return docSummary;
}
#region /* EQUALITY RELATED */
/// <summary>
/// Determine if the current reference refers to the same code object as the specified reference.
/// </summary>
public virtual bool IsSameRef(SymbolicRef symbolicRef)
{
return (symbolicRef != null && Reference == symbolicRef.Reference);
}
/// <summary>
/// Calculate a hash code for the referenced object which is the same for all references where IsSameRef() is true.
/// </summary>
/// <remarks>
/// We don't want to override GetHashCode(), because we want all TypeRefs to have unique hashes so they can be
/// used as dictionary keys. However, we also sometimes want hashes to be the same if IsSameRef() is true - this
/// method allows for that.
/// </remarks>
public virtual int GetIsSameRefHashCode()
{
// In order to keep the hash code as unique as possible while still identical when necessary, this method
// should be overloaded by any derived classes that also overload IsSameRef(), incorporating any fields
// compared by IsSameRef() into the hash code.
return (Reference != null ? Reference.GetHashCode() : base.GetHashCode());
}
#endregion
#endregion
#region /* PARSING */
protected SymbolicRef(Parser parser, CodeObject parent)
: base(parser, parent)
{ }
#endregion
#region /* RENDERING */
public override void AsTextExpression(CodeWriter writer, RenderFlags flags)
{
UpdateLineCol(writer, flags);
writer.WriteIdentifier(Name, flags);
}
protected static void AsTextDescription(CodeWriter writer, MemberInfo memberInfo)
{
const RenderFlags flags = RenderFlags.ShowParentTypes | RenderFlags.NoPreAnnotations;
switch (memberInfo.MemberType)
{
case MemberTypes.TypeInfo:
case MemberTypes.NestedType:
TypeRefBase.AsTextType(writer, (Type)memberInfo, flags | RenderFlags.Description);
break;
case MemberTypes.Constructor:
ConstructorRef.AsTextConstructorInfo(writer, (ConstructorInfo)memberInfo, flags);
break;
case MemberTypes.Method:
MethodRef.AsTextMethodInfo(writer, (MethodInfo)memberInfo, flags);
break;
case MemberTypes.Property:
PropertyRef.AsTextPropertyInfo(writer, (PropertyInfo)memberInfo, flags);
break;
case MemberTypes.Field:
FieldRef.AsTextFieldInfo(writer, (FieldInfo)memberInfo, flags);
break;
case MemberTypes.Event:
EventRef.AsTextEventInfo(writer, (EventInfo)memberInfo, flags);
break;
default:
writer.Write(memberInfo.ToString());
break;
}
}
/// <summary>
/// Get a short text description of the specified <see cref="MemberInfo"/>.
/// This is generally the shortest text representation that uniquely identifies objects, even if
/// they have the same name, for example: type or return type, name, type parameters, parameters.
/// </summary>
public static string GetDescription(MemberInfo memberInfo)
{
using (CodeWriter writer = new CodeWriter())
{
try
{
AsTextDescription(writer, memberInfo);
}
catch
{
writer.Write(memberInfo.Name);
}
return writer.ToString();
}
}
/// <summary>
/// Get the description of an object which is a <see cref="CodeObject"/> or <see cref="MemberInfo"/> (or a <c>string</c>).
/// </summary>
/// <param name="object">The object to be described.</param>
/// <returns>The string description of the object.</returns>
public static string GetDescription(object @object)
{
string description;
if (@object is CodeObject)
description = ((CodeObject)@object).GetDescription();
else if (@object is MemberInfo)
description = GetDescription((MemberInfo)@object);
else
description = @object.ToString();
return description;
}
#endregion
#region /* IsSameRefComparer */
/// <summary>
/// Determines if one <see cref="SymbolicRef"/> is equivalent to another one, meaning they both refer
/// to the same code object or type.
/// </summary>
public class IsSameRefComparer : IEqualityComparer<SymbolicRef>
{
/// <summary>
/// Determines if one <see cref="SymbolicRef"/> is equivalent to another one.
/// </summary>
public bool Equals(SymbolicRef x, SymbolicRef y) // For IEqualityComparer<SymbolicRef>
{
return x.IsSameRef(y);
}
/// <summary>
/// Calculate the hash code for the specified <see cref="SymbolicRef"/>.
/// </summary>
public int GetHashCode(SymbolicRef obj) // For IEqualityComparer<SymbolicRef>
{
return obj.GetIsSameRefHashCode();
}
}
#endregion
}
}