[monodoc] Add utility property Generic*IsNumeric to distinguish between types using...
[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                 /* This property tells if the above collections only correct value
90                  * is the number of item in it to represent generic arguments
91                  */
92                 public bool GenericTypeArgumentsIsNumeric {
93                         get {
94                                 return GenericTypeArguments != null && GenericTypeArguments.FirstOrDefault () == null;
95                         }
96                 }
97
98                 public IList<EcmaDesc> GenericMemberArguments {
99                         get;
100                         set;
101                 }
102
103                 public bool GenericMemberArgumentsIsNumeric {
104                         get {
105                                 return GenericMemberArguments != null && GenericMemberArguments.FirstOrDefault () == null;
106                         }
107                 }
108
109                 public IList<EcmaDesc> MemberArguments {
110                         get;
111                         set;
112                 }
113
114                 /* This indicates that we actually want an inner part of the ecmadesc
115                  * i.e. in case of T: we could want the members (*), ctor (C), methods (M), ...
116                  */
117                 public char Etc {
118                         get;
119                         set;
120                 }
121
122                 public bool IsEtc {
123                         get {
124                                 return Etc != (char)0;
125                         }
126                 }
127
128                 /* EtcFilter is only valid in some case of IsEtc when the inner part needs
129                  * to be further filtered e.g. in case we want a listing of the type overloads
130                  * Equals
131                  */
132                 public string EtcFilter {
133                         get;
134                         set;
135                 }
136
137                 /* When a member is an explicit implementation of an interface member, we register
138                  * the member EcmaDesc with its interface parent here
139                  */
140                 public EcmaDesc ExplicitImplMember {
141                         get;
142                         set;
143                 }
144
145                 // Returns the TypeName and the generic/inner type information if existing
146                 public string ToCompleteTypeName (char innerTypeSeparator = '.')
147                 {
148                         var result = TypeName;
149                         if (GenericTypeArguments != null)
150                                 result += FormatGenericArgs (GenericTypeArguments);
151                         if (NestedType != null)
152                                 result += innerTypeSeparator + NestedType.ToCompleteTypeName ();
153                         if (ArrayDimensions != null && ArrayDimensions.Count > 0)
154                                 result += ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat);
155
156                         return result;
157                 }
158
159                 // Returns the member name with its generic types if existing
160                 public string ToCompleteMemberName (Format format)
161                 {
162                         /* We special process two cases:
163                          *   - Explicit member implementation which append a full type specification
164                          *   - Conversion operator which are exposed as normal method but have specific captioning in the end
165                          */
166                         if (ExplicitImplMember != null) {
167                                 var impl = ExplicitImplMember;
168                                 return impl.FormattedNamespace + impl.ToCompleteTypeName () + "." + impl.ToCompleteMemberName (format);
169                         } else if (format == Format.WithArgs && DescKind == Kind.Operator && MemberName.EndsWith ("Conversion")) {
170                                 var type1 = MemberArguments[0].FormattedNamespace + MemberArguments[0].ToCompleteTypeName () + ModToString (MemberArguments[0]);
171                                 var type2 = MemberArguments[1].FormattedNamespace + MemberArguments[1].ToCompleteTypeName () + ModToString (MemberArguments[1]);
172                                 return type1 + " to " + type2;
173                         }
174
175                         var result = IsEtc && !string.IsNullOrEmpty (EtcFilter) ? EtcFilter : MemberName;
176
177                         // Temporary hack for monodoc produced inner type ctor
178                         //if (DescKind == Kind.Constructor && NestedType != null)
179                                 //result = ToCompleteTypeName ();
180
181                         if (GenericMemberArguments != null)
182                                 result += FormatGenericArgs (GenericMemberArguments);
183
184                         if (format == Format.WithArgs) {
185                                 result += '(';
186                                 if (MemberArguments != null && MemberArguments.Count > 0) {
187                                         var args = MemberArguments.Select (a => FormatNamespace (a) + a.ToCompleteTypeName ('+') + ModToString (a));
188                                         result += string.Join (",", args);
189                                 }
190                                 result += ')';
191                         }
192
193                         return result;
194                 }
195
196                 public string ToEcmaCref ()
197                 {
198                         var sb = new StringBuilder ();
199                         // Cref type
200                         sb.Append (DescKind.ToString ()[0]);
201                         // Create the rest
202                         ConstructCRef (sb);
203
204                         return sb.ToString ();
205                 }
206
207                 void ConstructCRef (StringBuilder sb)
208                 {
209                         sb.Append (Namespace);
210                         if (DescKind == Kind.Namespace)
211                                 return;
212
213                         sb.Append ('.');
214                         sb.Append (TypeName);
215                         if (GenericTypeArguments != null) {
216                                 sb.Append ('<');
217                                 foreach (var t in GenericTypeArguments)
218                                         t.ConstructCRef (sb);
219                                 sb.Append ('>');
220                         }
221                         if (NestedType != null) {
222                                 sb.Append ('+');
223                                 NestedType.ConstructCRef (sb);
224                         }
225                         if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
226                                 for (int i = 0; i < ArrayDimensions.Count; i++) {
227                                         sb.Append ('[');
228                                         sb.Append (new string (',', ArrayDimensions[i] - 1));
229                                         sb.Append (']');
230                                 }
231                         }
232                         if (DescKind == Kind.Type)
233                                 return;
234
235                         if (MemberArguments != null) {
236                                 
237                         }
238                 }
239
240                 public override string ToString ()
241                 {
242                         return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
243                                               Namespace,
244                                               TypeName,
245                                               FormatGenericArgsFull (GenericTypeArguments),
246                                               NestedType != null ? "+" + NestedType.ToString () : string.Empty,
247                                               MemberName ?? string.Empty,
248                                               FormatGenericArgsFull (GenericMemberArguments),
249                                               MemberArguments != null ? "(" + string.Join (",", MemberArguments.Select (m => m.ToString ())) + ")" : string.Empty,
250                                               ArrayDimensions != null && ArrayDimensions.Count > 0 ? ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat) : string.Empty,
251                                               DescKind.ToString ()[0],
252                                               Etc != 0 ? '(' + Etc.ToString () + ')' : string.Empty,
253                                               ExplicitImplMember != null ? "$" + ExplicitImplMember.ToString () : string.Empty);
254                                               
255                 }
256
257                 public override bool Equals (object other)
258                 {
259                         var otherDesc = other as EcmaDesc;
260                         return otherDesc != null && Equals (otherDesc);
261                 }
262
263                 public bool Equals (EcmaDesc other)
264                 {
265                         if (other == null)
266                                 return false;
267
268                         if (NestedType == null ^ other.NestedType == null
269                             || ArrayDimensions == null ^ other.ArrayDimensions == null
270                             || GenericTypeArguments == null ^ other.GenericTypeArguments == null
271                             || GenericMemberArguments == null ^ other.GenericMemberArguments == null
272                             || MemberArguments == null ^ other.MemberArguments == null
273                             || ExplicitImplMember == null ^ other.ExplicitImplMember == null)
274                                 return false;
275
276                         return other != null
277                                 && DescKind == other.DescKind
278                                 && TypeName == other.TypeName
279                                 && Namespace == other.Namespace
280                                 && MemberName == other.MemberName
281                                 && (NestedType == null || NestedType.Equals (other.NestedType))
282                                 && (ArrayDimensions == null || ArrayDimensions.SequenceEqual (other.ArrayDimensions))
283                                 && (GenericTypeArguments == null || GenericTypeArguments.SequenceEqual (other.GenericTypeArguments))
284                                 && (GenericMemberArguments == null || GenericMemberArguments.SequenceEqual (other.GenericMemberArguments))
285                                 && (MemberArguments == null || MemberArguments.SequenceEqual (other.MemberArguments))
286                                 && Etc == other.Etc
287                                 && EtcFilter == other.EtcFilter
288                                 && (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
289                 }
290
291                 public override int GetHashCode ()
292                 {
293                         return DescKind.GetHashCode ()
294                                 ^ TypeName.GetHashCode ()
295                                 ^ Namespace.GetHashCode ()
296                                 ^ MemberName.GetHashCode ();
297                 }
298
299                 bool What (bool input)
300                 {
301                         if (!input)
302                                 throw new Exception ("Not equal");
303                         return input;
304                 }
305
306                 bool WhatT (bool input)
307                 {
308                         if (input)
309                                 throw new Exception ("Not equal");
310                         return input;
311                 }
312
313                 string FormatNamespace (EcmaDesc desc)
314                 {
315                         return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
316                 }
317
318                 string FormatGenericArgs (IEnumerable<EcmaDesc> args)
319                 {
320                         if (args == null || !args.Any ())
321                                 return string.Empty;
322
323                         IEnumerable<string> argsList = null;
324
325                         // HACK: If we don't have fully specified EcmaDesc for generic arguments
326                         // we use the most common names for the argument length configuration
327                         if (args.Any (a => a == null)) {
328                                 var argCount = args.Count ();
329                                 switch (argCount) {
330                                 case 1:
331                                         argsList = new string[] { "T" };
332                                         break;
333                                 case 2:
334                                         argsList = new string[] { "TKey", "TValue" };
335                                         break;
336                                 default:
337                                         argsList = Enumerable.Range (1, argCount).Select (i => "T" + i);
338                                         break;
339                                 }
340                         } else {
341                                 argsList = args.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ());
342                         }
343
344                         return "<" + string.Join (",", argsList) + ">";
345                 }
346
347                 string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
348                 {
349                         return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
350                 }
351
352                 string ModToString (EcmaDesc desc)
353                 {
354                         switch (desc.DescModifier) {
355                         case Mod.Pointer:
356                                 return "*";
357                         case Mod.Ref:
358                                 return "&";
359                         case Mod.Out:
360                                 return "@";
361                         default:
362                                 return string.Empty;
363                         }
364                 }
365
366                 string FormattedNamespace {
367                         get {
368                                 return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;
369                         }
370                 }
371         }
372 }