4 using System.Collections.Generic;
8 /* Some properties might not be filled/meaningful depending on kind
9 * like a namespace EcmaUrl won't have a valid TypeName
11 public class EcmaDesc : IEquatable<EcmaDesc>
39 public Kind DescKind {
44 public Mod DescModifier {
49 public string Namespace {
54 public string TypeName {
59 public string MemberName {
64 public EcmaDesc NestedType {
69 /* A list of the array dimensions attached to this type.
70 * The list count corresponds to the number of recursive
71 * array definition (jagged arrays) the value of the
72 * corresponding list item is the number of dimension
73 * attached to that array definition instance
75 public IList<int> ArrayDimensions {
80 /* Depending on the form of the url, we might not have the type
81 * of the argument but only how many the type/member has i.e.
82 * when such number is specified with a backtick
84 public IList<EcmaDesc> GenericTypeArguments {
89 /* The GenericTypeArguments list may be null, in which case, this
90 * is an easier/safer way to check the count.
92 public int GenericTypeArgumentsCount {
93 get { return GenericTypeArguments != null ? GenericTypeArguments.Count : 0; }
96 /* This property tells if the above collections only correct value
97 * is the number of item in it to represent generic arguments
99 public bool GenericTypeArgumentsIsNumeric {
101 return GenericTypeArguments != null && GenericTypeArguments.FirstOrDefault () == null;
105 public IList<EcmaDesc> GenericMemberArguments {
110 /* The GenericMemberArguments list may be null, in which case, this
111 * is an easier/safer way to check the count.
113 public int GenericMemberArgumentsCount {
114 get { return GenericMemberArguments != null ? GenericMemberArguments.Count : 0; }
117 public bool GenericMemberArgumentsIsNumeric {
119 return GenericMemberArguments != null && GenericMemberArguments.FirstOrDefault () == null;
123 public IList<EcmaDesc> MemberArguments {
128 /* The GenericTypeArguments list may be null, in which case, this
129 * is an easier/safer way to check the count.
131 public int MemberArgumentsCount {
132 get { return MemberArguments != null ? MemberArguments.Count : 0; }
135 /* This indicates that we actually want an inner part of the ecmadesc
136 * i.e. in case of T: we could want the members (*), ctor (C), methods (M), ...
145 return Etc != (char)0;
149 /* EtcFilter is only valid in some case of IsEtc when the inner part needs
150 * to be further filtered e.g. in case we want a listing of the type overloads
153 public string EtcFilter {
158 /* When a member is an explicit implementation of an interface member, we register
159 * the member EcmaDesc with its interface parent here
161 public EcmaDesc ExplicitImplMember {
166 // Returns the TypeName and the generic/inner type information if existing
167 public string ToCompleteTypeName (char innerTypeSeparator = '.')
169 var result = TypeName;
170 if (GenericTypeArguments != null)
171 result += FormatGenericArgs (GenericTypeArguments);
172 if (NestedType != null)
173 result += innerTypeSeparator + NestedType.ToCompleteTypeName ();
174 if (ArrayDimensions != null && ArrayDimensions.Count > 0)
175 result += ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat);
180 // Returns the member name with its generic types if existing
181 public string ToCompleteMemberName (Format format)
183 /* We special process two cases:
184 * - Explicit member implementation which append a full type specification
185 * - Conversion operator which are exposed as normal method but have specific captioning in the end
187 if (ExplicitImplMember != null) {
188 var impl = ExplicitImplMember;
189 return impl.FormattedNamespace + impl.ToCompleteTypeName () + "." + impl.ToCompleteMemberName (format);
190 } else if (format == Format.WithArgs && DescKind == Kind.Operator && MemberName.EndsWith ("Conversion")) {
191 var type1 = MemberArguments[0].FormattedNamespace + MemberArguments[0].ToCompleteTypeName () + ModToString (MemberArguments[0]);
192 var type2 = MemberArguments[1].FormattedNamespace + MemberArguments[1].ToCompleteTypeName () + ModToString (MemberArguments[1]);
193 return type1 + " to " + type2;
196 var result = IsEtc && !string.IsNullOrEmpty (EtcFilter) ? EtcFilter : MemberName;
198 // Temporary hack for monodoc produced inner type ctor
199 //if (DescKind == Kind.Constructor && NestedType != null)
200 //result = ToCompleteTypeName ();
202 if (GenericMemberArguments != null)
203 result += FormatGenericArgs (GenericMemberArguments);
205 if (format == Format.WithArgs) {
207 if (MemberArguments != null && MemberArguments.Count > 0) {
208 var args = MemberArguments.Select (a => FormatNamespace (a) + a.ToCompleteTypeName ('+') + ModToString (a));
209 result += string.Join (",", args);
217 public string ToEcmaCref ()
219 var sb = new StringBuilder ();
221 sb.Append (DescKind.ToString ()[0]);
226 return sb.ToString ();
229 void ConstructCRef (StringBuilder sb, bool skipLeadingDot = false)
231 if (string.IsNullOrEmpty (Namespace))
232 skipLeadingDot = true;
234 sb.Append (Namespace);
235 if (DescKind == Kind.Namespace)
241 sb.Append (TypeName);
242 AppendGenericArguments (sb, GenericTypeArguments, GenericTypeArgumentsIsNumeric, GenericTypeArgumentsCount);
244 if (NestedType != null) {
246 NestedType.ConstructCRef (sb, skipLeadingDot: true);
248 if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
249 for (int i = 0; i < ArrayDimensions.Count; i++) {
251 sb.Append (new string (',', ArrayDimensions[i] - 1));
255 if (DescKind == Kind.Type)
258 if (ExplicitImplMember != null) {
260 ExplicitImplMember.DescKind = this.DescKind;
261 ExplicitImplMember.ConstructCRef (sb, skipLeadingDot: false);
266 sb.Append (MemberName);
268 AppendGenericArguments (sb, GenericMemberArguments, GenericMemberArgumentsIsNumeric, GenericMemberArgumentsCount);
270 if (MemberArguments != null && MemberArgumentsCount > 0) {
273 foreach (var a in MemberArguments) {
277 a.ConstructCRef (sb);
284 void AppendGenericArguments (StringBuilder sb, IEnumerable<EcmaDesc> arguments, bool isNumeric, int argumentsCount)
286 if (arguments != null && isNumeric) {
287 sb.AppendFormat ("`{0}", argumentsCount);
288 } else if (arguments != null) {
291 foreach (var t in arguments) {
295 t.ConstructCRef (sb);
303 public override string ToString ()
305 return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
308 FormatGenericArgsFull (GenericTypeArguments),
309 NestedType != null ? "+" + NestedType.ToString () : string.Empty,
310 MemberName ?? string.Empty,
311 FormatGenericArgsFull (GenericMemberArguments),
312 MemberArguments != null ? "(" + string.Join (",", MemberArguments.Select (m => m.ToString ())) + ")" : string.Empty,
313 ArrayDimensions != null && ArrayDimensions.Count > 0 ? ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat) : string.Empty,
314 DescKind.ToString ()[0],
315 Etc != 0 ? '(' + Etc.ToString () + ')' : string.Empty,
316 ExplicitImplMember != null ? "$" + ExplicitImplMember.ToString () : string.Empty);
320 public override bool Equals (object other)
322 var otherDesc = other as EcmaDesc;
323 return otherDesc != null && Equals (otherDesc);
326 public bool Equals (EcmaDesc other)
331 if (NestedType == null ^ other.NestedType == null
332 || ArrayDimensions == null ^ other.ArrayDimensions == null
333 || GenericTypeArguments == null ^ other.GenericTypeArguments == null
334 || GenericMemberArguments == null ^ other.GenericMemberArguments == null
335 || MemberArguments == null ^ other.MemberArguments == null
336 || ExplicitImplMember == null ^ other.ExplicitImplMember == null)
340 && DescKind == other.DescKind
341 && TypeName == other.TypeName
342 && Namespace == other.Namespace
343 && MemberName == other.MemberName
344 && (NestedType == null || NestedType.Equals (other.NestedType))
345 && (ArrayDimensions == null || ArrayDimensions.SequenceEqual (other.ArrayDimensions))
346 && (GenericTypeArguments == null || GenericTypeArguments.SequenceEqual (other.GenericTypeArguments))
347 && (GenericMemberArguments == null || GenericMemberArguments.SequenceEqual (other.GenericMemberArguments))
348 && (MemberArguments == null || MemberArguments.SequenceEqual (other.MemberArguments))
350 && EtcFilter == other.EtcFilter
351 && (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
354 public override int GetHashCode ()
356 return DescKind.GetHashCode ()
357 ^ TypeName.GetHashCode ()
358 ^ Namespace.GetHashCode ()
359 ^ MemberName.GetHashCode ();
362 bool What (bool input)
365 throw new Exception ("Not equal");
369 bool WhatT (bool input)
372 throw new Exception ("Not equal");
376 string FormatNamespace (EcmaDesc desc)
378 return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
381 string FormatGenericArgs (IEnumerable<EcmaDesc> args)
383 if (args == null || !args.Any ())
385 // If we only have the number of generic arguments, use ` notation
386 if (args.First () == null)
387 return "`" + args.Count ();
389 IEnumerable<string> argsList = args.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ());
391 return "<" + string.Join (",", argsList) + ">";
394 string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
396 return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
399 string ModToString (EcmaDesc desc)
401 switch (desc.DescModifier) {
413 string FormattedNamespace {
415 return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;