2009-07-30 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / corlib / System.Globalization / StringInfo.cs
1 //
2 // System.Globalization.StringInfo.cs
3 //
4 // Author:
5 //      Dick Porter (dick@ximian.com)
6 //
7 // (C) 2002 Ximian, Inc.
8 // (C) 2004 Novell, Inc.
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections;
35 using System.Runtime.InteropServices;
36
37 namespace System.Globalization {
38
39         [Serializable]
40 #if NET_2_0
41         [ComVisible(true)]
42 #endif
43         public class StringInfo {
44                 public StringInfo()
45                 {
46                 }
47
48 #if NET_2_0
49                 string s;
50                 int length;
51
52                 public StringInfo (string value)
53                 {
54                         // Argument check in property
55                         String = value;
56                 }
57
58                 [ComVisible (false)]
59                 public override bool Equals (object value)
60                 {
61                         StringInfo other = value as StringInfo;
62                         return other != null && s == other.s;
63                 }
64
65                 [ComVisible (false)]
66                 public override int GetHashCode ()
67                 {
68                         return s.GetHashCode ();
69                 }
70
71                 public int LengthInTextElements {
72                         get {
73                                 if (length < 0) {
74                                         length = 0;
75                                         for (int idx = 0; idx < s.Length; length++)
76                                                 idx += GetNextTextElementLength (s, idx);
77                                 }
78                                 return length;
79                         }
80                 }
81
82                 public string String {
83                         get { return s; }
84                         set {
85                                 if (value == null)
86                                         throw new ArgumentNullException ("value");
87                                 length = -1;
88                                 s = value;
89                         }
90                 }
91
92                 public string SubstringByTextElements (int startingTextElement)
93                 {
94                         if (startingTextElement < 0 || s.Length == 0)
95                                 throw new ArgumentOutOfRangeException ("startingTextElement");
96                         int idx = 0;
97                         for (int i = 0; i < startingTextElement; i++) {
98                                 if (idx >= s.Length)
99                                         throw new ArgumentOutOfRangeException ("startingTextElement");
100                                 idx += GetNextTextElementLength (s, idx);
101                         }
102                         return s.Substring (idx);
103                 }
104
105                 public string SubstringByTextElements (int startingTextElement, int lengthInTextElements)
106                 {
107                         if (startingTextElement < 0 || s.Length == 0)
108                                 throw new ArgumentOutOfRangeException ("startingTextElement");
109                         if (lengthInTextElements < 0)
110                                 throw new ArgumentOutOfRangeException ("lengthInTextElements");
111                         int idx = 0;
112                         for (int i = 0; i < startingTextElement; i++) {
113                                 if (idx >= s.Length)
114                                         throw new ArgumentOutOfRangeException ("startingTextElement");
115                                 idx += GetNextTextElementLength (s, idx);
116                         }
117                         int start = idx;
118                         for (int i = 0; i < lengthInTextElements; i++) {
119                                 if (idx >= s.Length)
120                                         throw new ArgumentOutOfRangeException ("lengthInTextElements");
121                                 idx += GetNextTextElementLength (s, idx);
122                         }
123                         return s.Substring (start, idx - start);
124                 }
125 #endif
126
127                 public static string GetNextTextElement(string str)
128                 {
129                         if(str == null || str.Length == 0) {
130                                 throw new ArgumentNullException("string is null");
131                         }
132                         return(GetNextTextElement (str, 0));
133                 }
134
135                 public static string GetNextTextElement(string str, int index)
136                 {
137                         int len = GetNextTextElementLength (str, index);
138                         return len != 1 ? str.Substring (index, len) : new string (str [index], 1);
139                 }
140                 
141                 static int GetNextTextElementLength(string str, int index)
142                 {
143                         if(str == null) {
144                                 throw new ArgumentNullException("string is null");
145                         }
146
147 #if NET_2_0
148                         if(index >= str.Length)
149                                 return 0;
150                         if(index < 0)
151 #else
152                         if(index < 0 || index >= str.Length)
153 #endif
154                                 throw new ArgumentOutOfRangeException ("Index is not valid");
155
156                         /* Find the next base character, surrogate
157                          * pair or combining character sequence
158                          */
159
160                         char ch = str[index];
161                         UnicodeCategory cat = char.GetUnicodeCategory (ch);
162
163                         if (cat == UnicodeCategory.Surrogate) {
164                                 /* Check that it's a high surrogate
165                                  * followed by a low surrogate
166                                  */
167                                 if (ch >= 0xD800 && ch <= 0xDBFF) {
168                                         if ((index + 1) < str.Length &&
169                                             str[index + 1] >= 0xDC00 &&
170                                             str[index + 1] <= 0xDFFF) {
171                                                 /* A valid surrogate pair */
172                                                 return 2;
173                                         } else {
174                                                 /* High surrogate on its own */
175                                                 return 1;
176                                         }
177                                 } else {
178                                         /* Low surrogate on its own */
179                                         return 1;
180                                 }
181                         } else {
182                                 /* Look for a base character, which
183                                  * may or may not be followed by a
184                                  * series of combining characters
185                                  */
186
187                                 if (cat == UnicodeCategory.NonSpacingMark ||
188                                     cat == UnicodeCategory.SpacingCombiningMark ||
189                                     cat == UnicodeCategory.EnclosingMark) {
190                                         /* Not a base character */
191                                         return 1;
192                                 }
193                                 
194                                 int count = 1;
195
196                                 while (index + count < str.Length) {
197                                         cat = char.GetUnicodeCategory (str[index + count]);
198                                         if (cat != UnicodeCategory.NonSpacingMark &&
199                                             cat != UnicodeCategory.SpacingCombiningMark &&
200                                             cat != UnicodeCategory.EnclosingMark) {
201                                                 /* Finished the sequence */
202                                                 break;
203                                         }
204                                         count++;
205                                 }
206
207                                 return count;
208                         }
209                 }
210
211                 public static TextElementEnumerator GetTextElementEnumerator(string str)
212                 {
213                         if(str == null || str.Length == 0) {
214                                 throw new ArgumentNullException("string is null");
215                         }
216                         return(new TextElementEnumerator (str, 0));
217                 }
218
219                 public static TextElementEnumerator GetTextElementEnumerator(string str, int index)
220                 {
221                         if(str == null) {
222                                 throw new ArgumentNullException("string is null");
223                         }
224
225                         if(index < 0 || index >= str.Length) {
226                                 throw new ArgumentOutOfRangeException ("Index is not valid");
227                         }
228                         
229                         return(new TextElementEnumerator (str, index));
230                 }
231                 
232                 public static int[] ParseCombiningCharacters(string str)
233                 {
234                         if(str == null) {
235                                 throw new ArgumentNullException("string is null");
236                         }
237
238                         ArrayList indices = new ArrayList (str.Length);
239                         TextElementEnumerator tee = GetTextElementEnumerator (str);
240
241                         tee.Reset ();
242                         while(tee.MoveNext ()) {
243                                 indices.Add (tee.ElementIndex);
244                         }
245
246                         return((int[])indices.ToArray (typeof (int)));
247                 }
248         }
249 }