2f46c6cc0fddccb6aa562c3bc515ef73c0727b35
[mono.git] / mcs / class / monodoc / Monodoc.Ecma / EcmaDesc.cs
1 using System;
2 using System.Linq;
3 using System.Text;
4 using System.Collections.Generic;
5
6 namespace Monodoc.Ecma
7 {
8         /* Some properties might not be filled/meaningful depending on kind
9          * like a namespace EcmaUrl won't have a valid TypeName
10          */
11         public class EcmaDesc : IEquatable<EcmaDesc>
12         {
13                 public enum Kind
14                 {
15                         Type,
16                         Constructor,
17                         Method,
18                         Namespace,
19                         Field,
20                         Property,
21                         Event,
22                         Operator
23                 }
24
25                 public enum Mod
26                 {
27                         Normal,
28                         Pointer,
29                         Ref,
30                         Out
31                 }
32
33                 public enum Format
34                 {
35                         WithArgs,
36                         WithoutArgs
37                 }
38
39                 public Kind DescKind {
40                         get;
41                         set;
42                 }
43
44                 public Mod DescModifier {
45                         get;
46                         set;
47                 }
48
49                 public string Namespace {
50                         get;
51                         set;
52                 }
53
54                 public string TypeName {
55                         get;
56                         set;
57                 }
58
59                 public string MemberName {
60                         get;
61                         set;
62                 }
63
64                 public EcmaDesc NestedType {
65                         get;
66                         set;
67                 }
68
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
74                  */
75                 public IList<int> ArrayDimensions {
76                         get;
77                         set;
78                 }
79
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
83                  */
84                 public IList<EcmaDesc> GenericTypeArguments {
85                         get;
86                         set;
87                 }
88
89                 /* The GenericTypeArguments list may be null, in which case, this
90                  * is an easier/safer way to check the count.
91                  */
92                 public int GenericTypeArgumentsCount {
93                         get { return GenericTypeArguments != null ? GenericTypeArguments.Count : 0; }
94                 }
95
96                 /* This property tells if the above collections only correct value
97                  * is the number of item in it to represent generic arguments
98                  */
99                 public bool GenericTypeArgumentsIsNumeric {
100                         get {
101                                 return GenericTypeArguments != null && GenericTypeArguments.FirstOrDefault () == null;
102                         }
103                 }
104
105                 public IList<EcmaDesc> GenericMemberArguments {
106                         get;
107                         set;
108                 }
109
110                 /* The GenericMemberArguments list may be null, in which case, this
111                  * is an easier/safer way to check the count.
112                  */
113                 public int GenericMemberArgumentsCount {
114                         get { return GenericMemberArguments != null ? GenericMemberArguments.Count : 0; }
115                 }
116
117                 public bool GenericMemberArgumentsIsNumeric {
118                         get {
119                                 return GenericMemberArguments != null && GenericMemberArguments.FirstOrDefault () == null;
120                         }
121                 }
122
123                 public IList<EcmaDesc> MemberArguments {
124                         get;
125                         set;
126                 }
127
128                 /* The GenericTypeArguments list may be null, in which case, this
129                  * is an easier/safer way to check the count.
130                  */
131                 public int MemberArgumentsCount {
132                         get { return MemberArguments != null ? MemberArguments.Count : 0; }
133                 }
134
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), ...
137                  */
138                 public char Etc {
139                         get;
140                         set;
141                 }
142
143                 public bool IsEtc {
144                         get {
145                                 return Etc != (char)0;
146                         }
147                 }
148
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
151                  * Equals
152                  */
153                 public string EtcFilter {
154                         get;
155                         set;
156                 }
157
158                 /* When a member is an explicit implementation of an interface member, we register
159                  * the member EcmaDesc with its interface parent here
160                  */
161                 public EcmaDesc ExplicitImplMember {
162                         get;
163                         set;
164                 }
165
166                 // Returns the TypeName and the generic/inner type information if existing
167                 public string ToCompleteTypeName (char innerTypeSeparator = '.')
168                 {
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);
176
177                         return result;
178                 }
179
180                 // Returns the member name with its generic types if existing
181                 public string ToCompleteMemberName (Format format)
182                 {
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
186                          */
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;
194                         }
195
196                         var result = IsEtc && !string.IsNullOrEmpty (EtcFilter) ? EtcFilter : MemberName;
197
198                         // Temporary hack for monodoc produced inner type ctor
199                         //if (DescKind == Kind.Constructor && NestedType != null)
200                                 //result = ToCompleteTypeName ();
201
202                         if (GenericMemberArguments != null)
203                                 result += FormatGenericArgs (GenericMemberArguments);
204
205                         if (format == Format.WithArgs) {
206                                 result += '(';
207                                 if (MemberArguments != null && MemberArguments.Count > 0) {
208                                         var args = MemberArguments.Select (a => FormatNamespace (a) + a.ToCompleteTypeName ('+') + ModToString (a));
209                                         result += string.Join (",", args);
210                                 }
211                                 result += ')';
212                         }
213
214                         return result;
215                 }
216
217                 public string ToEcmaCref ()
218                 {
219                         var sb = new StringBuilder ();
220                         // Cref type
221                         sb.Append (DescKind.ToString ()[0]);
222                         sb.Append (":");
223                         // Create the rest
224                         ConstructCRef (sb);
225
226                         return sb.ToString ();
227                 }
228
229                 void ConstructCRef (StringBuilder sb, bool skipLeadingDot = false)
230                 {
231                         if (string.IsNullOrEmpty (Namespace))
232                                 skipLeadingDot = true;
233
234                         sb.Append (Namespace);
235                         if (DescKind == Kind.Namespace)
236                                 return;
237
238                         if (!skipLeadingDot)
239                                 sb.Append ('.');
240
241                         sb.Append (TypeName);
242                         AppendGenericArguments (sb, GenericTypeArguments, GenericTypeArgumentsIsNumeric, GenericTypeArgumentsCount);
243
244                         if (NestedType != null) {
245                                 sb.Append ('+');
246                                 NestedType.ConstructCRef (sb, skipLeadingDot: true);
247                         }
248                         if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
249                                 for (int i = 0; i < ArrayDimensions.Count; i++) {
250                                         sb.Append ('[');
251                                         sb.Append (new string (',', ArrayDimensions[i] - 1));
252                                         sb.Append (']');
253                                 }
254                         }
255                         if (DescKind == Kind.Type)
256                                 return;
257
258                         if (ExplicitImplMember != null) {
259                                 sb.Append ('$');
260                                 ExplicitImplMember.DescKind = this.DescKind;
261                                 ExplicitImplMember.ConstructCRef (sb, skipLeadingDot: false);
262                                 return;
263                         }
264
265                         sb.Append (".");
266                         sb.Append (MemberName);
267
268                         AppendGenericArguments (sb, GenericMemberArguments, GenericMemberArgumentsIsNumeric, GenericMemberArgumentsCount);
269
270                         if (MemberArguments != null && MemberArgumentsCount > 0) {
271                                 sb.Append ("(");
272                                 int i=0;
273                                 foreach (var a in MemberArguments) {
274                                         if (i > 0) {
275                                                 sb.Append(",");
276                                         }
277                                         a.ConstructCRef (sb);
278                                         i++;
279                                 }
280                                 sb.Append (")");
281                         }
282                 }
283
284                 void AppendGenericArguments (StringBuilder sb, IEnumerable<EcmaDesc> arguments, bool isNumeric, int argumentsCount)
285                 {
286                         if (arguments != null && isNumeric) {
287                                 sb.AppendFormat ("`{0}", argumentsCount);
288                         } else if (arguments != null) {
289                                 sb.Append ('<');
290                                 int i=0;
291                                 foreach (var t in arguments) {
292                                         if (i > 0) {
293                                                 sb.Append (",");
294                                         }
295                                         t.ConstructCRef (sb);
296
297                                         i++;
298                                 }
299                                 sb.Append ('>');
300                         }
301                 }
302
303                 public override string ToString ()
304                 {
305                         return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
306                                               Namespace,
307                                               TypeName,
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);
317                                               
318                 }
319
320                 public override bool Equals (object other)
321                 {
322                         var otherDesc = other as EcmaDesc;
323                         return otherDesc != null && Equals (otherDesc);
324                 }
325
326                 public bool Equals (EcmaDesc other)
327                 {
328                         if (other == null)
329                                 return false;
330
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)
337                                 return false;
338
339                         return other != 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))
349                                 && Etc == other.Etc
350                                 && EtcFilter == other.EtcFilter
351                                 && (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
352                 }
353
354                 public override int GetHashCode ()
355                 {
356                         return DescKind.GetHashCode ()
357                                 ^ TypeName.GetHashCode ()
358                                 ^ Namespace.GetHashCode ()
359                                 ^ MemberName.GetHashCode ();
360                 }
361
362                 bool What (bool input)
363                 {
364                         if (!input)
365                                 throw new Exception ("Not equal");
366                         return input;
367                 }
368
369                 bool WhatT (bool input)
370                 {
371                         if (input)
372                                 throw new Exception ("Not equal");
373                         return input;
374                 }
375
376                 string FormatNamespace (EcmaDesc desc)
377                 {
378                         return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
379                 }
380
381                 string FormatGenericArgs (IEnumerable<EcmaDesc> args)
382                 {
383                         if (args == null || !args.Any ())
384                                 return string.Empty;
385                         // If we only have the number of generic arguments, use ` notation
386                         if (args.First () == null)
387                                 return "`" + args.Count ();
388
389                         IEnumerable<string> argsList = args.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ());
390
391                         return "<" + string.Join (",", argsList) + ">";
392                 }
393
394                 string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
395                 {
396                         return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
397                 }
398
399                 string ModToString (EcmaDesc desc)
400                 {
401                         switch (desc.DescModifier) {
402                         case Mod.Pointer:
403                                 return "*";
404                         case Mod.Ref:
405                                 return "&";
406                         case Mod.Out:
407                                 return "@";
408                         default:
409                                 return string.Empty;
410                         }
411                 }
412
413                 string FormattedNamespace {
414                         get {
415                                 return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;
416                         }
417                 }
418         }
419 }