Merge pull request #916 from akoeplinger/fix-gac-test
[mono.git] / mcs / class / corlib / System / Char.cs
1 //
2 // System.Char.cs
3 //
4 // Authors:
5 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
6 //   Miguel de Icaza (miguel@ximian.com)
7 //   Jackson Harper (jackson@ximian.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 // Note about the ToString()'s. ECMA says there's only a ToString() method, 
33 // BUT it is just a wrapper for ToString(null). However there is no other ToString
34 // in the docs. Turning to the NET framework sdk reveals that there is a 
35 // ToString(formatprovider) method, as well as a 'static ToString (char c)' method, 
36 // which appears to just be a Convert.ToString(char c) type method. ECMA also
37 // doesn't list this class as implementing IFormattable.
38
39 using System.Globalization;
40 using System.Runtime.CompilerServices;
41 using System.Runtime.InteropServices;
42
43 namespace System
44 {
45         [Serializable]
46         [ComVisible (true)]
47         public struct Char : IComparable, IConvertible, IComparable <char>, IEquatable <char>
48         {
49                 public const char MaxValue = (char) 0xffff;
50                 public const char MinValue = (char) 0;
51
52                 internal char m_value;
53
54                 static Char ()
55                 {
56                         unsafe {
57                                 GetDataTablePointers (CategoryDataVersion,
58                                         out category_data, out category_astral_index, out numeric_data,
59                                         out numeric_data_values, out to_lower_data_low, out to_lower_data_high,
60                                         out to_upper_data_low, out to_upper_data_high);
61                                 category_check_pair = category_astral_index != null
62                                         ? (byte)UnicodeCategory.Surrogate
63                                         : (byte)0xff;
64                         }
65                 }
66
67                 private readonly unsafe static byte *category_data;
68                 private readonly unsafe static ushort *category_astral_index;
69                 private readonly unsafe static byte *numeric_data;
70                 private readonly unsafe static double *numeric_data_values;
71                 private readonly unsafe static ushort *to_lower_data_low;
72                 private readonly unsafe static ushort *to_lower_data_high;
73                 private readonly unsafe static ushort *to_upper_data_low;
74                 private readonly unsafe static ushort *to_upper_data_high;
75
76                 // UnicodeCategory.Surrogate if astral plane
77                 // categories are available, 0xff otherwise.
78                 private readonly static byte category_check_pair;
79
80 #if NET_4_0
81                 private const int CategoryDataVersion = 4;
82 #else
83                 private const int CategoryDataVersion = 2;
84 #endif
85
86                 [MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
87                 private unsafe static extern void GetDataTablePointers (int category_data_version,
88                         out byte *category_data, out ushort *category_astral_index, out byte *numeric_data,
89                         out double *numeric_data_values, out ushort *to_lower_data_low, out ushort *to_lower_data_high,
90                         out ushort *to_upper_data_low, out ushort *to_upper_data_high);
91
92                 public int CompareTo (object value)
93                 {
94                         if (value == null)
95                                 return 1;
96                         
97                         if (!(value is System.Char))
98                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Char"));
99
100                         char xv = (char) value;
101                         if (m_value == xv)
102                                 return 0;
103
104                         if (m_value > xv)
105                                 return 1;
106                         else
107                                 return -1;
108                 }
109
110                 public override bool Equals (object obj)
111                 {
112                         if (!(obj is Char))
113                                 return false;
114
115                         return ((char) obj) == m_value;
116                 }
117
118                 public int CompareTo (char value)
119                 {
120                         if (m_value == value)
121                                 return 0;
122
123                         if (m_value > value)
124                                 return 1;
125                         else
126                                 return -1;
127                 }
128
129                 public static string ConvertFromUtf32 (int utf32)
130                 {
131                         if (utf32 < 0 || utf32 > 0x10FFFF)
132                                 throw new ArgumentOutOfRangeException ("utf32", "The argument must be from 0 to 0x10FFFF.");
133                         if (0xD800 <= utf32 && utf32 <= 0xDFFF)
134                                 throw new ArgumentOutOfRangeException ("utf32", "The argument must not be in surrogate pair range.");
135                         if (utf32 < 0x10000)
136                                 return new string ((char) utf32, 1);
137                         utf32 -= 0x10000;
138                         return new string (
139                                 new char [] {(char) ((utf32 >> 10) + 0xD800),
140                                 (char) (utf32 % 0x0400 + 0xDC00)});
141                 }
142
143                 public static int ConvertToUtf32 (char highSurrogate, char lowSurrogate)
144                 {
145                         if (highSurrogate < 0xD800 || 0xDBFF < highSurrogate)
146                                 throw new ArgumentOutOfRangeException ("highSurrogate");
147                         if (lowSurrogate < 0xDC00 || 0xDFFF < lowSurrogate)
148                                 throw new ArgumentOutOfRangeException ("lowSurrogate");
149
150                         return 0x10000 + ((highSurrogate - 0xD800) << 10) + (lowSurrogate - 0xDC00);
151                 }
152
153                 public static int ConvertToUtf32 (string s, int index)
154                 {
155                         CheckParameter (s, index);
156
157                         if (!Char.IsSurrogate (s [index]))
158                                 return s [index];
159                         if (!Char.IsHighSurrogate (s [index])
160                             || index == s.Length - 1
161                             || !Char.IsLowSurrogate (s [index + 1]))
162                                 throw new ArgumentException (String.Format ("The string contains invalid surrogate pair character at {0}", index));
163                         return ConvertToUtf32 (s [index], s [index + 1]);
164                 }
165
166                 public bool Equals (char obj)
167                 {
168                         return m_value == obj;
169                 }
170
171                 public static bool IsSurrogatePair (char highSurrogate, char lowSurrogate)
172                 {
173                         return '\uD800' <= highSurrogate && highSurrogate <= '\uDBFF'
174                                 && '\uDC00' <= lowSurrogate && lowSurrogate <= '\uDFFF';
175                 }
176
177                 public static bool IsSurrogatePair (string s, int index)
178                 {
179                         CheckParameter (s, index);
180                         return index + 1 < s.Length && IsSurrogatePair (s [index], s [index + 1]);
181                 }
182
183                 public override int GetHashCode ()
184                 {
185                         return m_value;
186                 }
187
188                 public static double GetNumericValue (char c)
189                 {
190                         if (c > (char)0x3289) {
191                                 if (c >= (char)0xFF10 && c <= (char)0xFF19)
192                                         return (c - 0xFF10); // Numbers 0-9
193
194                                 // Default not set data
195                                 return -1;
196                         }
197                         else {
198                                 unsafe {
199                                         return numeric_data_values [numeric_data [c]];
200                                 }
201                         }
202                         
203                 }
204
205                 public static double GetNumericValue (string s, int index)
206                 {
207                         CheckParameter (s, index);
208                         return GetNumericValue (s[index]);
209                 }
210
211                 public static UnicodeCategory GetUnicodeCategory (char c)
212                 {
213                         unsafe {
214                                 return (UnicodeCategory)(category_data [c]);
215                         }
216                 }
217
218                 public static UnicodeCategory GetUnicodeCategory (string s, int index)
219                 {
220                         CheckParameter (s, index);
221                         UnicodeCategory c = GetUnicodeCategory (s [index]);
222
223                         if ((byte)c == category_check_pair &&
224                             IsSurrogatePair (s, index)) {
225                                 int u = ConvertToUtf32 (s [index], s [index + 1]);
226                                 unsafe {
227                                         // ConvertToUtf32 guarantees 0x10000 <= u <= 0x10ffff
228                                         int x = (category_astral_index [(u - 0x10000) >> 8] << 8) + (u & 0xff);
229
230                                         c = (UnicodeCategory)category_data [x];
231                                 }
232                         }
233
234                         return c;
235                 }
236
237                 public static bool IsControl (char c)
238                 {
239                         unsafe {
240                                 return (category_data [c] == (byte)UnicodeCategory.Control);
241                         }
242                 }
243
244                 public static bool IsControl (string s, int index)
245                 {
246                         CheckParameter (s, index);
247                         return IsControl (s[index]);
248                 }       
249
250                 public static bool IsDigit (char c)
251                 {
252                         unsafe {
253                                 return (category_data [c] == (byte)UnicodeCategory.DecimalDigitNumber);
254                         }
255                 }
256
257                 public static bool IsDigit (string s, int index)
258                 {
259                         CheckParameter (s, index);
260                         return IsDigit (s[index]);
261                 }
262
263                 public static bool IsHighSurrogate (char c)
264                 {
265                         return c >= '\uD800' && c <= '\uDBFF';
266                 }
267
268                 public static bool IsHighSurrogate (string s, int index)
269                 {
270                         CheckParameter (s, index);
271                         return IsHighSurrogate (s [index]);
272                 }
273
274                 public static bool IsLetter (char c)
275                 {
276                         unsafe {
277                                 return category_data [c] <= ((byte)UnicodeCategory.OtherLetter);
278                         }
279                 }
280
281                 public static bool IsLetter (string s, int index)
282                 {
283                         CheckParameter (s, index);
284                         return IsLetter (s[index]);
285                 }
286
287                 public static bool IsLetterOrDigit (char c)
288                 {
289                         unsafe {
290                                 int category = category_data [c];
291                                 return (category <= ((int)UnicodeCategory.OtherLetter) ||
292                                         category == ((int)UnicodeCategory.DecimalDigitNumber));
293                         }
294                 }
295
296                 public static bool IsLetterOrDigit (string s, int index)
297                 {
298                         CheckParameter (s, index);
299                         return IsLetterOrDigit (s[index]);
300                 }
301
302                 public static bool IsLower (char c)
303                 {
304                         unsafe {
305                                 return (category_data [c] == (byte)UnicodeCategory.LowercaseLetter);
306                         }
307                 }
308
309                 public static bool IsLower (string s, int index)
310                 {
311                         CheckParameter (s, index);
312                         return IsLower (s[index]);
313                 }
314
315                 public static bool IsLowSurrogate (char c)
316                 {
317                         return c >= '\uDC00' && c <= '\uDFFF';
318                 }
319
320                 public static bool IsLowSurrogate (string s, int index)
321                 {
322                         CheckParameter (s, index);
323                         return IsLowSurrogate (s [index]);
324                 }
325
326                 public static bool IsNumber (char c)
327                 {
328                         unsafe {
329                                 int category = category_data [c];
330                                 return (category >= ((int)UnicodeCategory.DecimalDigitNumber) &&
331                                         category <= ((int)UnicodeCategory.OtherNumber));
332                         }
333                 }
334
335                 public static bool IsNumber (string s, int index)
336                 {
337                         CheckParameter (s, index);
338                         return IsNumber (s[index]);
339                 }
340
341                 public static bool IsPunctuation (char c)
342                 {
343                         unsafe {
344                                 int category = category_data [c];
345                                 return (category >= ((int)UnicodeCategory.ConnectorPunctuation) &&
346                                         category <= ((int)UnicodeCategory.OtherPunctuation));
347                         }
348                 }
349
350                 public static bool IsPunctuation (string s, int index)
351                 {
352                         CheckParameter (s, index);
353                         return IsPunctuation (s[index]);
354                 }
355
356                 public static bool IsSeparator (char c)
357                 {
358                         unsafe {
359                                 int category = category_data [c];
360                                 return (category >= ((int)UnicodeCategory.SpaceSeparator) &&
361                                         category <= ((int)UnicodeCategory.ParagraphSeparator));
362                         }
363                 }
364
365                 public static bool IsSeparator (string s, int index)
366                 {
367                         CheckParameter (s, index);
368                         return IsSeparator (s[index]);
369                 }
370
371                 public static bool IsSurrogate (char c)
372                 {
373                         unsafe {
374                                 return (category_data [c] == (byte)UnicodeCategory.Surrogate);
375                         }
376                 }
377
378                 public static bool IsSurrogate (string s, int index)
379                 {
380                         CheckParameter (s, index);
381                         return IsSurrogate (s[index]);
382                 }
383
384                 public static bool IsSymbol (char c)
385                 {
386                         unsafe {
387                                 int category = category_data [c];
388                                 return (category >= ((int)UnicodeCategory.MathSymbol) &&
389                                         category <= ((int)UnicodeCategory.OtherSymbol));
390                         }
391                 }
392
393                 public static bool IsSymbol (string s, int index)
394                 {
395                         CheckParameter (s, index);
396                         return IsSymbol (s[index]);
397                 }
398
399                 public static bool IsUpper (char c)
400                 {
401                         unsafe {
402                                 return (category_data [c] == (byte)UnicodeCategory.UppercaseLetter);
403                         }
404                 }
405
406                 public static bool IsUpper (string s, int index)
407                 {
408                         CheckParameter (s, index);
409                         return IsUpper (s[index]);
410                 }
411                 
412                 public static bool IsWhiteSpace (char c)
413                 {
414                         if (c < 0x1680)
415                                 return c == 0x20 || c >= 0x09 && c <= 0x0d || c == 0x85 || c == 0xA0;
416
417                         unsafe {
418                                 int category = category_data [c];
419                                 return category > (int) UnicodeCategory.OtherNumber && category <= (int) UnicodeCategory.ParagraphSeparator;
420                         }
421                 }
422
423                 public static bool IsWhiteSpace (string s, int index)
424                 {
425                         CheckParameter (s, index);
426                         return IsWhiteSpace (s[index]);
427                 }
428
429                 private static void CheckParameter (string s, int index)
430                 {
431                         if (s == null) 
432                                 throw new ArgumentNullException ("s");
433                         
434                         if (index < 0 || index >= s.Length) 
435                                 throw new ArgumentOutOfRangeException (
436                                         Locale.GetText ("The value of index is less than zero, or greater than or equal to the length of s."));
437                 }
438
439                 public static bool TryParse (string s, out char result)
440                 {
441                         if (s == null || s.Length != 1) {
442                                 result = (char) 0;
443                                 return false;
444                         }
445
446                         result = s [0];
447                         return true;
448                 }
449
450                 public static char Parse (string s)
451                 {
452                         if (s == null) 
453                                 throw new ArgumentNullException ("s");
454
455                         if (s.Length != 1)
456                                 throw new FormatException (Locale.GetText ("s contains more than one character."));
457                         
458                         return s [0];
459                 }
460
461                 public static char ToLower (char c)
462                 {
463                         // CurrentCulture is never null or Invariant
464                         return CultureInfo.CurrentCulture.TextInfo.ToLower (c);
465                 }
466
467                 public static char ToLowerInvariant (char c)
468                 {
469                         unsafe {
470                                 if (c <= ((char)0x24cf))
471                                         return (char) to_lower_data_low [c];
472                                 if (c >= ((char)0xff21))
473                                         return (char) to_lower_data_high[c - 0xff21];
474                         }
475                         return c;
476                 }
477
478                 public static char ToLower (char c, CultureInfo culture)
479                 {
480                         if (culture == null)
481                                 throw new ArgumentNullException ("culture");
482                         if (culture.LCID == 0x007F) // Invariant
483                                 return ToLowerInvariant (c);
484
485                         return culture.TextInfo.ToLower (c);
486                 }
487
488                 public static char ToUpper (char c)
489                 {
490                         // CurrentCulture is never null or Invariant
491                         return CultureInfo.CurrentCulture.TextInfo.ToUpper (c);
492                 }
493
494                 public static char ToUpperInvariant (char c)
495                 {
496                         unsafe {
497                                 if (c <= ((char)0x24e9))
498                                         return (char) to_upper_data_low [c];
499                                 if (c >= ((char)0xff21))
500                                         return (char) to_upper_data_high [c - 0xff21];
501                         }
502                         return c;
503                 }
504
505                 public static char ToUpper (char c, CultureInfo culture)
506                 {
507                         if (culture == null)
508                                 throw new ArgumentNullException ("culture");
509                         if (culture.LCID == 0x007F) // Invariant
510                                 return ToUpperInvariant (c);
511
512                         return culture.TextInfo.ToUpper (c);
513                 }
514
515                 public override string ToString ()
516                 {
517                         return new String (m_value, 1);
518                 }
519
520                 public static string ToString (char c)
521                 {
522                         return new String (c, 1);
523                 }
524
525                 public string ToString (IFormatProvider provider)
526                 {
527                         return new String (m_value, 1);
528                 }
529
530                 // =========== IConvertible Methods =========== //
531
532                 public TypeCode GetTypeCode ()
533                 {
534                         return TypeCode.Char;
535                 }
536
537                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
538                 {
539                         if (targetType == null)
540                                 throw new ArgumentNullException ("targetType");
541                         return System.Convert.ToType (m_value, targetType, provider, false);
542                 }
543
544                 bool IConvertible.ToBoolean (IFormatProvider provider)
545                 {
546                         throw new InvalidCastException ();
547                 }
548
549                 byte IConvertible.ToByte (IFormatProvider provider)
550                 {
551                         return System.Convert.ToByte (m_value);
552                 }
553
554                 char IConvertible.ToChar (IFormatProvider provider)
555                 {
556                         return m_value;
557                 }
558
559                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
560                 {
561                         throw new InvalidCastException ();
562                 }
563
564                 decimal IConvertible.ToDecimal (IFormatProvider provider)
565                 {
566                         throw new InvalidCastException ();
567                 }
568
569                 double IConvertible.ToDouble (IFormatProvider provider)
570                 {
571                         throw new InvalidCastException ();
572                 }
573
574                 short IConvertible.ToInt16 (IFormatProvider provider)
575                 {
576                         return System.Convert.ToInt16 (m_value);
577                 }
578
579                 int IConvertible.ToInt32 (IFormatProvider provider)
580                 {
581                         return System.Convert.ToInt32 (m_value);
582                 }
583
584                 long IConvertible.ToInt64 (IFormatProvider provider)
585                 {
586                         return System.Convert.ToInt64 (m_value);
587                 }
588
589                 sbyte IConvertible.ToSByte (IFormatProvider provider)
590                 {
591                         return System.Convert.ToSByte (m_value);
592                 }
593
594                 float IConvertible.ToSingle (IFormatProvider provider)
595                 {
596                         throw new InvalidCastException ();
597                 }
598
599                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
600                 {
601                         return System.Convert.ToUInt16 (m_value);
602                 }
603
604                 uint IConvertible.ToUInt32 (IFormatProvider provider)
605                 {
606                         return System.Convert.ToUInt32 (m_value);
607                 }
608
609                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
610                 {
611                         return System.Convert.ToUInt64 (m_value);
612                 }
613         }
614 }