Merge pull request #1659 from alexanderkyte/stringbuilder-referencesource
[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)
230                 {
231                         sb.Append (Namespace);
232                         if (DescKind == Kind.Namespace)
233                                 return;
234
235                         sb.Append ('.');
236                         sb.Append (TypeName);
237                         if (GenericTypeArguments != null && GenericTypeArgumentsIsNumeric) {
238                                 sb.AppendFormat ("`{0}", GenericTypeArgumentsCount);
239                         } else if (GenericTypeArguments != null) {
240                                 sb.Append ('<');
241                                 int i=0;
242                                 foreach (var t in GenericTypeArguments) {
243                                         if (i > 0) {
244                                                 sb.Append (",");
245                                         }
246                                         t.ConstructCRef (sb);
247
248                                         i++;
249                                 }
250                                 sb.Append ('>');
251                         }
252                         if (NestedType != null) {
253                                 sb.Append ('+');
254                                 NestedType.ConstructCRef (sb);
255                         }
256                         if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
257                                 for (int i = 0; i < ArrayDimensions.Count; i++) {
258                                         sb.Append ('[');
259                                         sb.Append (new string (',', ArrayDimensions[i] - 1));
260                                         sb.Append (']');
261                                 }
262                         }
263                         if (DescKind == Kind.Type)
264                                 return;
265
266                         sb.Append (".");
267                         sb.Append (MemberName);
268
269                         if (MemberArguments != null && MemberArgumentsCount > 0) {
270                                 sb.Append ("(");
271                                 int i=0;
272                                 foreach (var a in MemberArguments) {
273                                         if (i > 0) {
274                                                 sb.Append(",");
275                                         }
276                                         a.ConstructCRef (sb);
277                                         i++;
278                                 }
279                                 sb.Append (")");
280                         }
281                 }
282
283                 public override string ToString ()
284                 {
285                         return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
286                                               Namespace,
287                                               TypeName,
288                                               FormatGenericArgsFull (GenericTypeArguments),
289                                               NestedType != null ? "+" + NestedType.ToString () : string.Empty,
290                                               MemberName ?? string.Empty,
291                                               FormatGenericArgsFull (GenericMemberArguments),
292                                               MemberArguments != null ? "(" + string.Join (",", MemberArguments.Select (m => m.ToString ())) + ")" : string.Empty,
293                                               ArrayDimensions != null && ArrayDimensions.Count > 0 ? ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat) : string.Empty,
294                                               DescKind.ToString ()[0],
295                                               Etc != 0 ? '(' + Etc.ToString () + ')' : string.Empty,
296                                               ExplicitImplMember != null ? "$" + ExplicitImplMember.ToString () : string.Empty);
297                                               
298                 }
299
300                 public override bool Equals (object other)
301                 {
302                         var otherDesc = other as EcmaDesc;
303                         return otherDesc != null && Equals (otherDesc);
304                 }
305
306                 public bool Equals (EcmaDesc other)
307                 {
308                         if (other == null)
309                                 return false;
310
311                         if (NestedType == null ^ other.NestedType == null
312                             || ArrayDimensions == null ^ other.ArrayDimensions == null
313                             || GenericTypeArguments == null ^ other.GenericTypeArguments == null
314                             || GenericMemberArguments == null ^ other.GenericMemberArguments == null
315                             || MemberArguments == null ^ other.MemberArguments == null
316                             || ExplicitImplMember == null ^ other.ExplicitImplMember == null)
317                                 return false;
318
319                         return other != null
320                                 && DescKind == other.DescKind
321                                 && TypeName == other.TypeName
322                                 && Namespace == other.Namespace
323                                 && MemberName == other.MemberName
324                                 && (NestedType == null || NestedType.Equals (other.NestedType))
325                                 && (ArrayDimensions == null || ArrayDimensions.SequenceEqual (other.ArrayDimensions))
326                                 && (GenericTypeArguments == null || GenericTypeArguments.SequenceEqual (other.GenericTypeArguments))
327                                 && (GenericMemberArguments == null || GenericMemberArguments.SequenceEqual (other.GenericMemberArguments))
328                                 && (MemberArguments == null || MemberArguments.SequenceEqual (other.MemberArguments))
329                                 && Etc == other.Etc
330                                 && EtcFilter == other.EtcFilter
331                                 && (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
332                 }
333
334                 public override int GetHashCode ()
335                 {
336                         return DescKind.GetHashCode ()
337                                 ^ TypeName.GetHashCode ()
338                                 ^ Namespace.GetHashCode ()
339                                 ^ MemberName.GetHashCode ();
340                 }
341
342                 bool What (bool input)
343                 {
344                         if (!input)
345                                 throw new Exception ("Not equal");
346                         return input;
347                 }
348
349                 bool WhatT (bool input)
350                 {
351                         if (input)
352                                 throw new Exception ("Not equal");
353                         return input;
354                 }
355
356                 string FormatNamespace (EcmaDesc desc)
357                 {
358                         return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
359                 }
360
361                 string FormatGenericArgs (IEnumerable<EcmaDesc> args)
362                 {
363                         if (args == null || !args.Any ())
364                                 return string.Empty;
365                         // If we only have the number of generic arguments, use ` notation
366                         if (args.First () == null)
367                                 return "`" + args.Count ();
368
369                         IEnumerable<string> argsList = args.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ());
370
371                         return "<" + string.Join (",", argsList) + ">";
372                 }
373
374                 string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
375                 {
376                         return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
377                 }
378
379                 string ModToString (EcmaDesc desc)
380                 {
381                         switch (desc.DescModifier) {
382                         case Mod.Pointer:
383                                 return "*";
384                         case Mod.Ref:
385                                 return "&";
386                         case Mod.Out:
387                                 return "@";
388                         default:
389                                 return string.Empty;
390                         }
391                 }
392
393                 string FormattedNamespace {
394                         get {
395                                 return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;
396                         }
397                 }
398         }
399 }