2 // System.Globalization.TextInfo.cs
5 // Dick Porter (dick@ximian.com)
6 // Duncan Mak (duncan@ximian.com)
7 // Atsushi Enomoto (atsushi@ximian.com)
8 // Sebastien Pouliot <sebastien@ximian.com>
10 // (C) 2002 Ximian, Inc.
11 // (C) 2005 Novell, Inc.
14 // Missing the various code page mappings.
15 // Missing the OnDeserialization implementation.
17 // Copyright (C) 2004, 2005 Novell, Inc (http://www.novell.com)
19 // Permission is hereby granted, free of charge, to any person obtaining
20 // a copy of this software and associated documentation files (the
21 // "Software"), to deal in the Software without restriction, including
22 // without limitation the rights to use, copy, modify, merge, publish,
23 // distribute, sublicense, and/or sell copies of the Software, and to
24 // permit persons to whom the Software is furnished to do so, subject to
25 // the following conditions:
27 // The above copyright notice and this permission notice shall be
28 // included in all copies or substantial portions of the Software.
30 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
34 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
35 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 using System.Runtime.Serialization;
40 using System.Runtime.InteropServices;
43 namespace System.Globalization {
47 [MonoTODO ("IDeserializationCallback isn't implemented.")]
48 public class TextInfo: IDeserializationCallback, ICloneable
50 [StructLayout (LayoutKind.Sequential)]
56 public bool right_to_left;
60 string m_listSeparator;
62 string customCultureName;
64 #pragma warning disable 169
67 bool m_useUserOverride;
68 #pragma warning restore 169
73 readonly CultureInfo ci;
76 readonly bool handleDotI;
81 internal unsafe TextInfo (CultureInfo ci, int lcid, void* data, bool read_only)
83 this.m_isReadOnly = read_only;
84 this.m_win32LangID = lcid;
87 this.data = *(Data*) data;
89 this.data = new Data ();
90 this.data.list_sep = (byte) ',';
94 while (tmp.Parent != null && tmp.Parent.LCID != 0x7F && tmp.Parent != tmp)
99 case 44: // Azeri (az)
100 case 31: // Turkish (tr)
107 private TextInfo (TextInfo textInfo)
109 m_win32LangID = textInfo.m_win32LangID;
110 m_nDataItem = textInfo.m_nDataItem;
111 m_useUserOverride = textInfo.m_useUserOverride;
112 m_listSeparator = textInfo.ListSeparator;
113 customCultureName = textInfo.CultureName;
115 handleDotI = textInfo.handleDotI;
116 data = textInfo.data;
119 public virtual int ANSICodePage
126 public virtual int EBCDICCodePage
135 get { return m_win32LangID; }
138 public virtual string ListSeparator {
140 if (m_listSeparator == null)
141 m_listSeparator = ((char) data.list_sep).ToString ();
142 return m_listSeparator;
145 set { m_listSeparator = value; }
148 public virtual int MacCodePage
155 public virtual int OEMCodePage
163 public string CultureName {
165 if (customCultureName == null)
166 customCultureName = ci == null ? String.Empty : ci.Name;
167 return customCultureName;
172 public bool IsReadOnly {
173 get { return m_isReadOnly; }
177 public bool IsRightToLeft {
179 return data.right_to_left;
183 public override bool Equals (object obj)
187 TextInfo other = obj as TextInfo;
190 if (other.m_win32LangID != m_win32LangID)
197 public override int GetHashCode()
199 return (m_win32LangID);
202 public override string ToString()
204 return "TextInfo - " + m_win32LangID;
207 public string ToTitleCase (string str)
210 throw new ArgumentNullException ("str");
212 StringBuilder sb = null;
215 while (i < str.Length) {
216 if (!Char.IsLetter (str [i++]))
219 char t = ToTitleCase (str [i]);
220 bool capitalize = true;
223 bool allTitle = true;
224 // if the word is all titlecase,
225 // then don't capitalize it.
227 while (++i < str.Length) {
228 if (Char.IsWhiteSpace (str [i]))
230 t = ToTitleCase (str [i]);
240 // still check if all remaining
241 // characters are lowercase,
242 // where we don't have to modify
244 while (++i < str.Length) {
245 if (Char.IsWhiteSpace (str [i]))
247 if (ToLower (str [i]) != str [i]) {
257 sb = new StringBuilder (str.Length);
258 sb.Append (str, start, i - start);
259 sb.Append (ToTitleCase (str [i]));
261 while (++i < str.Length) {
262 if (Char.IsWhiteSpace (str [i]))
264 sb.Append (ToLower (str [i]));
270 sb.Append (str, start, str.Length - start);
272 return sb != null ? sb.ToString () : str;
275 // Only Azeri and Turkish have their own special cases.
276 // Other than them, all languages have common special case
277 // (enumerable enough).
278 public virtual char ToLower (char c)
280 // quick ASCII range check
281 if (c < 0x40 || 0x60 < c && c < 128)
283 else if ('A' <= c && c <= 'Z' && (!handleDotI || c != 'I'))
284 return (char) (c + 0x20);
286 if (ci == null || ci.LCID == 0x7F)
287 return Char.ToLowerInvariant (c);
290 case '\u0049': // Latin uppercase I
292 return '\u0131'; // I becomes dotless i
294 case '\u0130': // I-dotted
295 return '\u0069'; // i
297 case '\u01c5': // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
299 // \u01c7 -> \u01c9 (LJ) : invariant
300 case '\u01c8': // LATIN CAPITAL LETTER L WITH SMALL LETTER J
302 // \u01ca -> \u01cc (NJ) : invariant
303 case '\u01cb': // LATIN CAPITAL LETTER N WITH SMALL LETTER J
305 // WITH CARON : invariant
306 // WITH DIAERESIS AND * : invariant
308 case '\u01f2': // LATIN CAPITAL LETTER D WITH SMALL LETTER Z
310 case '\u03d2': // ? it is not in ICU
312 case '\u03d3': // ? it is not in ICU
314 case '\u03d4': // ? it is not in ICU
317 return Char.ToLowerInvariant (c);
320 public virtual char ToUpper (char c)
322 // quick ASCII range check
325 else if ('a' <= c && c <= 'z' && (!handleDotI || c != 'i'))
326 return (char) (c - 0x20);
328 if (ci == null || ci.LCID == 0x7F)
329 return Char.ToUpperInvariant (c);
332 case '\u0069': // Latin lowercase i
334 return '\u0130'; // dotted capital I
336 case '\u0131': // dotless i
337 return '\u0049'; // I
339 case '\u01c5': // see ToLower()
341 case '\u01c8': // see ToLower()
343 case '\u01cb': // see ToLower()
345 case '\u01f2': // see ToLower()
347 case '\u0390': // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
348 return '\u03aa'; // it is not in ICU
349 case '\u03b0': // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
350 return '\u03ab'; // it is not in ICU
351 case '\u03d0': // GREEK BETA
353 case '\u03d1': // GREEK THETA
355 case '\u03d5': // GREEK PHI
357 case '\u03d6': // GREEK PI
359 case '\u03f0': // GREEK KAPPA
361 case '\u03f1': // GREEK RHO
363 // am not sure why miscellaneous GREEK symbols are
367 return Char.ToUpperInvariant (c);
370 private char ToTitleCase (char c)
372 // Handle some Latin characters.
391 if ('\u2170' <= c && c <= '\u217f' || // Roman numbers
392 '\u24d0' <= c && c <= '\u24e9')
397 public unsafe virtual string ToLower (string str)
399 // In ICU (3.2) there are a few cases that one single
400 // character results in multiple characters in e.g.
401 // tr-TR culture. So I tried brute force conversion
402 // test with single character as a string input, but
403 // there was no such conversion. So I think it just
404 // invokes ToLower(char).
406 throw new ArgumentNullException ("str");
411 string tmp = String.InternalAllocateStr (str.Length);
412 fixed (char* source = str, dest = tmp) {
414 char* destPtr = (char*)dest;
415 char* sourcePtr = (char*)source;
417 for (int n = 0; n < str.Length; n++) {
418 *destPtr = ToLower (*sourcePtr);
426 public unsafe virtual string ToUpper (string str)
428 // In ICU (3.2) there is a case that string
429 // is handled beyond per-character conversion, but
430 // it is only lt-LT culture where MS.NET does not
431 // handle any special transliteration. So I keep
432 // ToUpper() just as character conversion.
434 throw new ArgumentNullException ("str");
439 string tmp = String.InternalAllocateStr (str.Length);
440 fixed (char* source = str, dest = tmp) {
442 char* destPtr = (char*)dest;
443 char* sourcePtr = (char*)source;
445 for (int n = 0; n < str.Length; n++) {
446 *destPtr = ToUpper (*sourcePtr);
455 public static TextInfo ReadOnly (TextInfo textInfo)
457 if (textInfo == null)
458 throw new ArgumentNullException ("textInfo");
460 TextInfo ti = new TextInfo (textInfo);
461 ti.m_isReadOnly = true;
465 /* IDeserialization interface */
467 void IDeserializationCallback.OnDeserialization(object sender)
469 // FIXME: we need to re-create "data" in order to get most properties working
474 public virtual object Clone ()
476 return new TextInfo (this);