Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Workflow.Activities / Common / TypeSystemHelpers.cs
1 // Copyright (c) Microsoft Corporation. All rights reserved. 
2 //  
3 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
4 // WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
5 // WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. 
6 // THE ENTIRE RISK OF USE OR RESULTS IN CONNECTION WITH THE USE OF THIS CODE 
7 // AND INFORMATION REMAINS WITH THE USER. 
8 //  
9
10 /*********************************************************************
11  * NOTE: A copy of this file exists at: WF\Common\Shared
12  * The two files must be kept in sync.  Any change made here must also
13  * be made to WF\Common\Shared\TypeSystemHelpers.cs
14 *********************************************************************/
15 namespace System.Workflow.Activities.Common
16 {
17     using System;
18     using System.Collections;
19     using System.Collections.Specialized;
20     using System.Collections.Generic;
21     using System.Globalization;
22     using System.Reflection;
23     using System.CodeDom;
24     using System.Text.RegularExpressions;
25     using System.Diagnostics.CodeAnalysis;
26     using System.Workflow.ComponentModel.Compiler;
27
28     internal static class ParseHelpers
29     {
30         private static readonly Version emptyVersion = new Version(0, 0, 0, 0);
31         private const string VersionTag = "version";
32         private const string CultureTag = "culture";
33         private const string PublicKeyTokenTag = "publickeytoken";
34
35         private static readonly ArrayList VBKeywords = new ArrayList(new string[] { "Integer", "String", "Boolean", "Object", "Void", "Single", "Double", "Char", "DateTime", "Long", "Byte", "Short", "Single", "Double", "Decimal", "UInteger", "ULong", "SByte", "UShort" });
36         private static readonly ArrayList CSKeywords = new ArrayList(new string[] { "int", "string", "bool", "object", "void", "float", "double", "char", "Date", "long", "byte", "short", "Single", "double", "decimal", "uint", "ulong", "sbyte", "ushort" });
37         private static readonly string[] DotNetKeywords = new string[] { "System.Int32", "System.String", "System.Boolean", "System.Object", "System.Void", "System.Single", "System.Double", "System.Char", "System.DateTime", "System.Int64", "System.Byte", "System.Int16", "System.Single", "System.Double", "System.Decimal", "System.UInt32", "System.UInt64", "System.SByte", "System.UInt16" };
38
39         internal enum ParseTypeNameLanguage
40         {
41             VB,
42             CSharp,
43             NetFramework
44         }
45
46         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
47         internal static bool ParseTypeName(string inputTypeName, ParseTypeNameLanguage parseTypeNameLanguage, out string typeName, out string[] parameters, out string elemantDecorator)
48         {
49             typeName = string.Empty;
50             parameters = null;
51             elemantDecorator = string.Empty;
52
53             // replace all language specific array\generic chars with the net-framework's representation
54             if (parseTypeNameLanguage == ParseTypeNameLanguage.VB)
55                 inputTypeName = inputTypeName.Replace('(', '[').Replace(')', ']');
56             else if (parseTypeNameLanguage == ParseTypeNameLanguage.CSharp)
57                 inputTypeName = inputTypeName.Replace('<', '[').Replace('>', ']');
58
59             int endIndex = inputTypeName.LastIndexOfAny(new char[] { ']', '&', '*' });
60             if (endIndex == -1)
61             {
62                 // "simple" type
63                 typeName = inputTypeName;
64             }
65             else if (inputTypeName[endIndex] == ']') //array or generic
66             {
67                 int startIndex = endIndex;
68                 int nestLevel = 1;
69                 while ((startIndex > 0) && (nestLevel > 0))
70                 {
71                     startIndex--;
72                     if (inputTypeName[startIndex] == ']')
73                         nestLevel++;
74                     else if (inputTypeName[startIndex] == '[')
75                         nestLevel--;
76                 }
77                 if (nestLevel != 0)
78                     return false;
79
80                 typeName = inputTypeName.Substring(0, startIndex) + inputTypeName.Substring(endIndex + 1);
81
82                 string bracketContent = inputTypeName.Substring(startIndex + 1, endIndex - startIndex - 1).Trim();
83                 if ((bracketContent == String.Empty) || (bracketContent.TrimStart()[0] == ','))
84                 {
85                     // array
86                     elemantDecorator = "[" + bracketContent + "]";
87                 }
88                 else
89                 {
90                     // Isolate the parameters (looking for commas alone will not cover cases
91                     // when parameters are multi-dim array or generics...
92                     int nestingLevel = 0;
93                     char[] genericParamChars = bracketContent.ToCharArray();
94                     for (int loop = 0; loop < genericParamChars.Length; loop++)
95                     {
96                         if (genericParamChars[loop] == '[')
97                             nestingLevel++;
98                         else if (genericParamChars[loop] == ']')
99                             nestingLevel--;
100                         else if ((genericParamChars[loop] == ',') && (nestingLevel == 0))
101                             genericParamChars[loop] = '$';
102                     }
103                     // split to get the list of generic arguments
104                     parameters = new string(genericParamChars).Split(new char[] { '$' });
105
106                     // clean the parameters
107                     for (int loop = 0; loop < parameters.Length; loop++)
108                     {
109                         parameters[loop] = parameters[loop].Trim();
110
111                         // remove extra brackects if exist
112                         if (parameters[loop][0] == '[')
113                             parameters[loop] = parameters[loop].Substring(1, parameters[loop].Length - 2);
114
115                         // remove the "Of " keyword form VB parameters
116                         if ((parseTypeNameLanguage == ParseTypeNameLanguage.VB) && (parameters[loop].StartsWith("Of ", StringComparison.OrdinalIgnoreCase)))
117                             parameters[loop] = parameters[loop].Substring(3).TrimStart();
118                     }
119                 }
120             }
121             else // byref, pointer
122             {
123                 typeName = inputTypeName.Substring(0, endIndex) + inputTypeName.Substring(endIndex + 1);
124                 elemantDecorator = inputTypeName.Substring(endIndex, 1);
125             }
126
127             //Work around: we need to account for these langugue keywords and provide the correct type for them.
128             //      A tighter way to achieve this should be found.
129             if ((parseTypeNameLanguage == ParseTypeNameLanguage.CSharp) && CSKeywords.Contains(typeName))
130                 typeName = DotNetKeywords[CSKeywords.IndexOf(typeName)];
131             else if ((parseTypeNameLanguage == ParseTypeNameLanguage.VB) && VBKeywords.Contains(typeName))
132                 typeName = DotNetKeywords[VBKeywords.IndexOf(typeName)];
133
134             return true;
135         }
136
137         internal static bool AssemblyNameEquals(AssemblyName thisName, AssemblyName thatName)
138         {
139             // Simplest check -- the assembly name must match.
140             if (thisName.Name == null || thatName.Name == null)
141                 return false;
142
143             if (!thatName.Name.Equals(thisName.Name))
144                 return false;
145
146             // Next, version checks.  We are comparing AGAINST thatName,
147             // so if thatName has a version defined, we must match.
148             Version thatVersion = thatName.Version;
149             if (thatVersion != null && thatVersion != emptyVersion && thatVersion != thisName.Version)
150                 return false;
151
152             // Same story for culture
153             CultureInfo thatCulture = thatName.CultureInfo;
154             if (thatCulture != null && !thatCulture.Equals(CultureInfo.InvariantCulture))
155             {
156                 CultureInfo thisCulture = thisName.CultureInfo;
157                 if (thisCulture == null)
158                     return false;
159
160                 // the requested culture must either equal, or be a parent of
161                 // our culture.
162                 do
163                 {
164                     if (thatCulture.Equals(thisCulture))
165                         break;
166                     thisCulture = thisCulture.Parent;
167                     if (thisCulture.Equals(CultureInfo.InvariantCulture))
168                         return false;
169                 } while (true);
170             }
171
172             // And the same thing for the public token
173             byte[] thatToken = thatName.GetPublicKeyToken();
174             if (thatToken != null && thatToken.Length != 0)
175             {
176                 byte[] thisToken = thisName.GetPublicKeyToken();
177                 if (thisToken == null)
178                     return false;
179                 if (thatToken.Length != thisToken.Length)
180                     return false;
181                 for (int i = 0; i < thatToken.Length; i++)
182                 {
183                     if (thatToken[i] != thisToken[i])
184                         return false;
185                 }
186             }
187             return true;
188         }
189
190         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
191         internal static bool AssemblyNameEquals(AssemblyName thisName, string thatName)
192         {
193             if (thisName == null || string.IsNullOrEmpty(thisName.Name))
194                 return false;
195             if (string.IsNullOrEmpty(thatName))
196                 return false;
197
198             string[] parts = thatName.Split(',');
199             if (parts.Length == 0)
200                 return false;
201
202             string thatAssemblyName = parts[0].Trim();
203             if (!thatAssemblyName.Equals(thisName.Name))
204                 return false;
205
206             if (parts.Length == 1)
207                 return true;
208
209             Version thatVersion = null;
210             CultureInfo thatCulture = null;
211             byte[] thatToken = null;
212             for (int index = 1; index < parts.Length; index++)
213             {
214                 int indexOfEquals = parts[index].IndexOf('=');
215                 if (indexOfEquals != -1)
216                 {
217                     string partName = parts[index].Substring(0, indexOfEquals).Trim().ToLowerInvariant();
218                     string partValue = parts[index].Substring(indexOfEquals + 1).Trim().ToLowerInvariant();
219                     if (string.IsNullOrEmpty(partValue))
220                         continue;
221
222                     switch (partName)
223                     {
224                         case ParseHelpers.VersionTag:
225                             thatVersion = new Version(partValue);
226                             break;
227                         case ParseHelpers.CultureTag:
228                             if (!string.Equals(partValue, "neutral", StringComparison.OrdinalIgnoreCase))
229                                 thatCulture = new CultureInfo(partValue);
230                             break;
231                         case ParseHelpers.PublicKeyTokenTag:
232                             if (!string.Equals(partValue, "null", StringComparison.OrdinalIgnoreCase))
233                             {
234                                 thatToken = new byte[partValue.Length / 2];
235                                 for (int i = 0; i < thatToken.Length; i++)
236                                     thatToken[i] = Byte.Parse(partValue.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
237                             }
238                             break;
239                         default:
240                             break;
241                     }
242                 }
243             }
244
245             if (thatVersion != null && thatVersion != emptyVersion && thatVersion != thisName.Version)
246                 return false;
247
248             if (thatCulture != null && !thatCulture.Equals(CultureInfo.InvariantCulture))
249             {
250                 CultureInfo thisCulture = thisName.CultureInfo;
251                 if (thisCulture == null)
252                     return false;
253
254                 // the requested culture must either equal, or be a parent of
255                 // our culture.
256                 do
257                 {
258                     if (thatCulture.Equals(thisCulture))
259                         break;
260                     thisCulture = thisCulture.Parent;
261                     if (thisCulture.Equals(CultureInfo.InvariantCulture))
262                         return false;
263                 } while (true);
264             }
265
266             if (thatToken != null && thatToken.Length != 0)
267             {
268                 byte[] thisToken = thisName.GetPublicKeyToken();
269                 if (thisToken == null)
270                     return false;
271                 if (thatToken.Length != thisToken.Length)
272                     return false;
273                 for (int i = 0; i < thatToken.Length; i++)
274                 {
275                     if (thatToken[i] != thisToken[i])
276                         return false;
277                 }
278             }
279
280             return true;
281         }
282
283         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
284         internal static string FormatType(Type type, SupportedLanguages language)
285         {
286             string typeName = string.Empty;
287             if (type.IsArray)
288             {
289                 typeName = FormatType(type.GetElementType(), language);
290                 if (language == SupportedLanguages.CSharp)
291                     typeName += '[';
292                 else
293                     typeName += '(';
294
295                 typeName += new string(',', type.GetArrayRank() - 1);
296                 if (language == SupportedLanguages.CSharp)
297                     typeName += ']';
298                 else
299                     typeName += ')';
300             }
301             else
302             {
303                 typeName = type.FullName;
304                 int indexOfSpecialChar = typeName.IndexOf('`');
305                 if (indexOfSpecialChar != -1)
306                     typeName = typeName.Substring(0, indexOfSpecialChar);
307                 typeName = typeName.Replace('+', '.');
308
309                 if (type.ContainsGenericParameters || type.IsGenericType)
310                 {
311                     Type[] genericArguments = type.GetGenericArguments();
312                     if (language == SupportedLanguages.CSharp)
313                         typeName += '<';
314                     else
315                         typeName += '(';
316
317                     bool first = true;
318                     foreach (Type genericArgument in genericArguments)
319                     {
320                         if (!first)
321                             typeName += ", ";
322                         else
323                         {
324                             if (language == SupportedLanguages.VB)
325                                 typeName += "Of ";
326                             first = false;
327                         }
328                         typeName += FormatType(genericArgument, language);
329                     }
330
331                     if (language == SupportedLanguages.CSharp)
332                         typeName += '>';
333                     else
334                         typeName += ')';
335                 }
336             }
337             return typeName;
338         }
339
340         // Helper method to format a type name (language invariant) to a specific language
341         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
342         internal static string FormatType(string type, SupportedLanguages language)
343         {
344             string formattedType = string.Empty;
345             string[] genericParamTypeNames = null;
346             string baseTypeName = string.Empty;
347             string elementDecorators = string.Empty;
348             if (ParseHelpers.ParseTypeName(type, ParseHelpers.ParseTypeNameLanguage.NetFramework, out baseTypeName, out genericParamTypeNames, out elementDecorators))
349             {
350                 if (elementDecorators.Length > 0)
351                 {
352                     // VB uses '()' for arrays
353                     if (language == SupportedLanguages.VB)
354                         elementDecorators = elementDecorators.Replace('[', '(').Replace(']', ')');
355
356                     formattedType = FormatType(baseTypeName, language) + elementDecorators;
357                 }
358                 else if (genericParamTypeNames != null && genericParamTypeNames.Length > 0)
359                 {
360                     // add generic type
361                     formattedType = FormatType(baseTypeName, language);
362
363                     // add generic arguments
364                     if (language == SupportedLanguages.CSharp)
365                         formattedType += '<';
366                     else
367                         formattedType += '(';
368
369                     bool first = true;
370                     foreach (string genericArgument in genericParamTypeNames)
371                     {
372                         if (!first)
373                             formattedType += ", ";
374                         else
375                         {
376                             if (language == SupportedLanguages.VB)
377                                 formattedType += "Of ";
378                             first = false;
379                         }
380
381                         formattedType += FormatType(genericArgument, language);
382                     }
383
384                     if (language == SupportedLanguages.CSharp)
385                         formattedType += '>';
386                     else
387                         formattedType += ')';
388                 }
389                 else
390                 {
391                     // non generic, non decorated type - simple cleanup
392                     formattedType = baseTypeName.Replace('+', '.');
393
394                     int indexOfSpecialChar = formattedType.IndexOf('`');
395                     if (indexOfSpecialChar != -1)
396                         formattedType = formattedType.Substring(0, indexOfSpecialChar);
397
398                     indexOfSpecialChar = formattedType.IndexOf(',');
399                     if (indexOfSpecialChar != -1)
400                         formattedType = formattedType.Substring(0, indexOfSpecialChar);
401
402                 }
403             }
404
405             return formattedType;
406         }
407
408         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
409         internal static Type ParseTypeName(ITypeProvider typeProvider, SupportedLanguages language, string typeName)
410         {
411             Type returnType = null;
412
413             returnType = typeProvider.GetType(typeName, false);
414             if (returnType == null)
415             {
416                 string simpleTypeName = String.Empty;
417                 string decoratorString = String.Empty;
418                 string[] parameters = null;
419                 if (ParseTypeName(typeName, language == SupportedLanguages.CSharp ? ParseTypeNameLanguage.CSharp : ParseTypeNameLanguage.VB, out simpleTypeName, out parameters, out decoratorString))
420                 {
421                     returnType = typeProvider.GetType(simpleTypeName + decoratorString, false);
422                 }
423             }
424
425             return returnType;
426         }
427     }
428 }