536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
420 lines
11 KiB
C#
420 lines
11 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Monodoc.Ecma
|
|
{
|
|
/* Some properties might not be filled/meaningful depending on kind
|
|
* like a namespace EcmaUrl won't have a valid TypeName
|
|
*/
|
|
public class EcmaDesc : IEquatable<EcmaDesc>
|
|
{
|
|
public enum Kind
|
|
{
|
|
Type,
|
|
Constructor,
|
|
Method,
|
|
Namespace,
|
|
Field,
|
|
Property,
|
|
Event,
|
|
Operator
|
|
}
|
|
|
|
public enum Mod
|
|
{
|
|
Normal,
|
|
Pointer,
|
|
Ref,
|
|
Out
|
|
}
|
|
|
|
public enum Format
|
|
{
|
|
WithArgs,
|
|
WithoutArgs
|
|
}
|
|
|
|
public Kind DescKind {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public Mod DescModifier {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string Namespace {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string TypeName {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string MemberName {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public EcmaDesc NestedType {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/* A list of the array dimensions attached to this type.
|
|
* The list count corresponds to the number of recursive
|
|
* array definition (jagged arrays) the value of the
|
|
* corresponding list item is the number of dimension
|
|
* attached to that array definition instance
|
|
*/
|
|
public IList<int> ArrayDimensions {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/* Depending on the form of the url, we might not have the type
|
|
* of the argument but only how many the type/member has i.e.
|
|
* when such number is specified with a backtick
|
|
*/
|
|
public IList<EcmaDesc> GenericTypeArguments {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/* The GenericTypeArguments list may be null, in which case, this
|
|
* is an easier/safer way to check the count.
|
|
*/
|
|
public int GenericTypeArgumentsCount {
|
|
get { return GenericTypeArguments != null ? GenericTypeArguments.Count : 0; }
|
|
}
|
|
|
|
/* This property tells if the above collections only correct value
|
|
* is the number of item in it to represent generic arguments
|
|
*/
|
|
public bool GenericTypeArgumentsIsNumeric {
|
|
get {
|
|
return GenericTypeArguments != null && GenericTypeArguments.FirstOrDefault () == null;
|
|
}
|
|
}
|
|
|
|
public IList<EcmaDesc> GenericMemberArguments {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/* The GenericMemberArguments list may be null, in which case, this
|
|
* is an easier/safer way to check the count.
|
|
*/
|
|
public int GenericMemberArgumentsCount {
|
|
get { return GenericMemberArguments != null ? GenericMemberArguments.Count : 0; }
|
|
}
|
|
|
|
public bool GenericMemberArgumentsIsNumeric {
|
|
get {
|
|
return GenericMemberArguments != null && GenericMemberArguments.FirstOrDefault () == null;
|
|
}
|
|
}
|
|
|
|
public IList<EcmaDesc> MemberArguments {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/* The GenericTypeArguments list may be null, in which case, this
|
|
* is an easier/safer way to check the count.
|
|
*/
|
|
public int MemberArgumentsCount {
|
|
get { return MemberArguments != null ? MemberArguments.Count : 0; }
|
|
}
|
|
|
|
/* This indicates that we actually want an inner part of the ecmadesc
|
|
* i.e. in case of T: we could want the members (*), ctor (C), methods (M), ...
|
|
*/
|
|
public char Etc {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool IsEtc {
|
|
get {
|
|
return Etc != (char)0;
|
|
}
|
|
}
|
|
|
|
/* EtcFilter is only valid in some case of IsEtc when the inner part needs
|
|
* to be further filtered e.g. in case we want a listing of the type overloads
|
|
* Equals
|
|
*/
|
|
public string EtcFilter {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/* When a member is an explicit implementation of an interface member, we register
|
|
* the member EcmaDesc with its interface parent here
|
|
*/
|
|
public EcmaDesc ExplicitImplMember {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
// Returns the TypeName and the generic/inner type information if existing
|
|
public string ToCompleteTypeName (char innerTypeSeparator = '.')
|
|
{
|
|
var result = TypeName;
|
|
if (GenericTypeArguments != null)
|
|
result += FormatGenericArgs (GenericTypeArguments);
|
|
if (NestedType != null)
|
|
result += innerTypeSeparator + NestedType.ToCompleteTypeName ();
|
|
if (ArrayDimensions != null && ArrayDimensions.Count > 0)
|
|
result += ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Returns the member name with its generic types if existing
|
|
public string ToCompleteMemberName (Format format)
|
|
{
|
|
/* We special process two cases:
|
|
* - Explicit member implementation which append a full type specification
|
|
* - Conversion operator which are exposed as normal method but have specific captioning in the end
|
|
*/
|
|
if (ExplicitImplMember != null) {
|
|
var impl = ExplicitImplMember;
|
|
return impl.FormattedNamespace + impl.ToCompleteTypeName () + "." + impl.ToCompleteMemberName (format);
|
|
} else if (format == Format.WithArgs && DescKind == Kind.Operator && MemberName.EndsWith ("Conversion")) {
|
|
var type1 = MemberArguments[0].FormattedNamespace + MemberArguments[0].ToCompleteTypeName () + ModToString (MemberArguments[0]);
|
|
var type2 = MemberArguments[1].FormattedNamespace + MemberArguments[1].ToCompleteTypeName () + ModToString (MemberArguments[1]);
|
|
return type1 + " to " + type2;
|
|
}
|
|
|
|
var result = IsEtc && !string.IsNullOrEmpty (EtcFilter) ? EtcFilter : MemberName;
|
|
|
|
// Temporary hack for monodoc produced inner type ctor
|
|
//if (DescKind == Kind.Constructor && NestedType != null)
|
|
//result = ToCompleteTypeName ();
|
|
|
|
if (GenericMemberArguments != null)
|
|
result += FormatGenericArgs (GenericMemberArguments);
|
|
|
|
if (format == Format.WithArgs) {
|
|
result += '(';
|
|
if (MemberArguments != null && MemberArguments.Count > 0) {
|
|
var args = MemberArguments.Select (a => FormatNamespace (a) + a.ToCompleteTypeName ('+') + ModToString (a));
|
|
result += string.Join (",", args);
|
|
}
|
|
result += ')';
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public string ToEcmaCref ()
|
|
{
|
|
var sb = new StringBuilder ();
|
|
// Cref type
|
|
sb.Append (DescKind.ToString ()[0]);
|
|
sb.Append (":");
|
|
// Create the rest
|
|
ConstructCRef (sb);
|
|
|
|
return sb.ToString ();
|
|
}
|
|
|
|
void ConstructCRef (StringBuilder sb, bool skipLeadingDot = false)
|
|
{
|
|
if (string.IsNullOrEmpty (Namespace))
|
|
skipLeadingDot = true;
|
|
|
|
sb.Append (Namespace);
|
|
if (DescKind == Kind.Namespace)
|
|
return;
|
|
|
|
if (!skipLeadingDot)
|
|
sb.Append ('.');
|
|
|
|
sb.Append (TypeName);
|
|
AppendGenericArguments (sb, GenericTypeArguments, GenericTypeArgumentsIsNumeric, GenericTypeArgumentsCount);
|
|
|
|
if (NestedType != null) {
|
|
sb.Append ('+');
|
|
NestedType.ConstructCRef (sb, skipLeadingDot: true);
|
|
}
|
|
if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
|
|
for (int i = 0; i < ArrayDimensions.Count; i++) {
|
|
sb.Append ('[');
|
|
sb.Append (new string (',', ArrayDimensions[i] - 1));
|
|
sb.Append (']');
|
|
}
|
|
}
|
|
if (DescKind == Kind.Type)
|
|
return;
|
|
|
|
if (ExplicitImplMember != null) {
|
|
sb.Append ('$');
|
|
ExplicitImplMember.DescKind = this.DescKind;
|
|
ExplicitImplMember.ConstructCRef (sb, skipLeadingDot: false);
|
|
return;
|
|
}
|
|
|
|
sb.Append (".");
|
|
sb.Append (MemberName);
|
|
|
|
AppendGenericArguments (sb, GenericMemberArguments, GenericMemberArgumentsIsNumeric, GenericMemberArgumentsCount);
|
|
|
|
if (MemberArguments != null && MemberArgumentsCount > 0) {
|
|
sb.Append ("(");
|
|
int i=0;
|
|
foreach (var a in MemberArguments) {
|
|
if (i > 0) {
|
|
sb.Append(",");
|
|
}
|
|
a.ConstructCRef (sb);
|
|
i++;
|
|
}
|
|
sb.Append (")");
|
|
}
|
|
}
|
|
|
|
void AppendGenericArguments (StringBuilder sb, IEnumerable<EcmaDesc> arguments, bool isNumeric, int argumentsCount)
|
|
{
|
|
if (arguments != null && isNumeric) {
|
|
sb.AppendFormat ("`{0}", argumentsCount);
|
|
} else if (arguments != null) {
|
|
sb.Append ('<');
|
|
int i=0;
|
|
foreach (var t in arguments) {
|
|
if (i > 0) {
|
|
sb.Append (",");
|
|
}
|
|
t.ConstructCRef (sb);
|
|
|
|
i++;
|
|
}
|
|
sb.Append ('>');
|
|
}
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
|
|
Namespace,
|
|
TypeName,
|
|
FormatGenericArgsFull (GenericTypeArguments),
|
|
NestedType != null ? "+" + NestedType.ToString () : string.Empty,
|
|
MemberName ?? string.Empty,
|
|
FormatGenericArgsFull (GenericMemberArguments),
|
|
MemberArguments != null ? "(" + string.Join (",", MemberArguments.Select (m => m.ToString ())) + ")" : string.Empty,
|
|
ArrayDimensions != null && ArrayDimensions.Count > 0 ? ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat) : string.Empty,
|
|
DescKind.ToString ()[0],
|
|
Etc != 0 ? '(' + Etc.ToString () + ')' : string.Empty,
|
|
ExplicitImplMember != null ? "$" + ExplicitImplMember.ToString () : string.Empty);
|
|
|
|
}
|
|
|
|
public override bool Equals (object other)
|
|
{
|
|
var otherDesc = other as EcmaDesc;
|
|
return otherDesc != null && Equals (otherDesc);
|
|
}
|
|
|
|
public bool Equals (EcmaDesc other)
|
|
{
|
|
if (other == null)
|
|
return false;
|
|
|
|
if (NestedType == null ^ other.NestedType == null
|
|
|| ArrayDimensions == null ^ other.ArrayDimensions == null
|
|
|| GenericTypeArguments == null ^ other.GenericTypeArguments == null
|
|
|| GenericMemberArguments == null ^ other.GenericMemberArguments == null
|
|
|| MemberArguments == null ^ other.MemberArguments == null
|
|
|| ExplicitImplMember == null ^ other.ExplicitImplMember == null)
|
|
return false;
|
|
|
|
return other != null
|
|
&& DescKind == other.DescKind
|
|
&& TypeName == other.TypeName
|
|
&& Namespace == other.Namespace
|
|
&& MemberName == other.MemberName
|
|
&& (NestedType == null || NestedType.Equals (other.NestedType))
|
|
&& (ArrayDimensions == null || ArrayDimensions.SequenceEqual (other.ArrayDimensions))
|
|
&& (GenericTypeArguments == null || GenericTypeArguments.SequenceEqual (other.GenericTypeArguments))
|
|
&& (GenericMemberArguments == null || GenericMemberArguments.SequenceEqual (other.GenericMemberArguments))
|
|
&& (MemberArguments == null || MemberArguments.SequenceEqual (other.MemberArguments))
|
|
&& Etc == other.Etc
|
|
&& EtcFilter == other.EtcFilter
|
|
&& (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
|
|
}
|
|
|
|
public override int GetHashCode ()
|
|
{
|
|
return DescKind.GetHashCode ()
|
|
^ TypeName.GetHashCode ()
|
|
^ Namespace.GetHashCode ()
|
|
^ MemberName.GetHashCode ();
|
|
}
|
|
|
|
bool What (bool input)
|
|
{
|
|
if (!input)
|
|
throw new Exception ("Not equal");
|
|
return input;
|
|
}
|
|
|
|
bool WhatT (bool input)
|
|
{
|
|
if (input)
|
|
throw new Exception ("Not equal");
|
|
return input;
|
|
}
|
|
|
|
string FormatNamespace (EcmaDesc desc)
|
|
{
|
|
return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
|
|
}
|
|
|
|
string FormatGenericArgs (IEnumerable<EcmaDesc> args)
|
|
{
|
|
if (args == null || !args.Any ())
|
|
return string.Empty;
|
|
// If we only have the number of generic arguments, use ` notation
|
|
if (args.First () == null)
|
|
return "`" + args.Count ();
|
|
|
|
IEnumerable<string> argsList = args.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ());
|
|
|
|
return "<" + string.Join (",", argsList) + ">";
|
|
}
|
|
|
|
string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
|
|
{
|
|
return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
|
|
}
|
|
|
|
string ModToString (EcmaDesc desc)
|
|
{
|
|
switch (desc.DescModifier) {
|
|
case Mod.Pointer:
|
|
return "*";
|
|
case Mod.Ref:
|
|
return "&";
|
|
case Mod.Out:
|
|
return "@";
|
|
default:
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
string FormattedNamespace {
|
|
get {
|
|
return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;
|
|
}
|
|
}
|
|
}
|
|
}
|