1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 ////////////////////////////////////////////////////////////////////////////
8 // Purpose: This Class defines behaviors specific to a writing system.
9 // A writing system is the collection of scripts and
10 // orthographic rules required to represent a language as text.
13 ////////////////////////////////////////////////////////////////////////////
15 using System.Security;
17 namespace System.Globalization {
20 using System.Threading;
22 using System.Runtime.InteropServices;
23 using System.Runtime.CompilerServices;
24 using System.Runtime.Serialization;
25 using System.Runtime.Versioning;
26 using System.Security.Permissions;
27 using System.Diagnostics.Contracts;
31 [System.Runtime.InteropServices.ComVisible(true)]
32 public partial class TextInfo : ICloneable, IDeserializationCallback
34 //--------------------------------------------------------------------//
35 // Internal Information //
36 //--------------------------------------------------------------------//
43 [OptionalField(VersionAdded = 2)]
44 private String m_listSeparator;
46 [OptionalField(VersionAdded = 2)]
47 private bool m_isReadOnly = false;
50 // In Whidbey we had several names:
51 // m_win32LangID is the name of the culture, but only used for (de)serialization.
52 // customCultureName is the name of the creating custom culture (if custom) In combination with m_win32LangID
53 // this is authoratative, ie when deserializing.
54 // m_cultureTableRecord was the data record of the creating culture. (could have different name if custom)
55 // m_textInfoID is the LCID of the textinfo itself (no longer used)
56 // m_name is the culture name (from cultureinfo.name)
58 // In Silverlight/Arrowhead this is slightly different:
59 // m_cultureName is the name of the creating culture. Note that we consider this authoratative,
60 // if the culture's textinfo changes when deserializing, then behavior may change.
61 // (ala Whidbey behavior). This is the only string Arrowhead needs to serialize.
62 // m_cultureData is the data that backs this class.
63 // m_textInfoName is the actual name of the textInfo (from cultureData.STEXTINFO)
64 // m_textInfoName can be the same as m_cultureName on Silverlight since the OS knows
65 // how to do the sorting. However in the desktop, when we call the sorting dll, it doesn't
66 // know how to resolve custom locle names to sort ids so we have to have alredy resolved this.
69 [OptionalField(VersionAdded = 3)]
70 private String m_cultureName; // Name of the culture that created this text info
71 [NonSerialized]private CultureData m_cultureData; // Data record for the culture that made us, not for this textinfo
72 [NonSerialized]private String m_textInfoName; // Name of the text info we're using (ie: m_cultureData.STEXTINFO)
73 [NonSerialized]private IntPtr m_dataHandle; // Sort handle
74 [NonSerialized]private IntPtr m_handleOrigin;
75 [NonSerialized]private bool? m_IsAsciiCasingSameAsInvariant;
78 // Invariant text info
79 internal static TextInfo Invariant
83 if (s_Invariant == null)
84 s_Invariant = new TextInfo(CultureData.Invariant);
88 internal volatile static TextInfo s_Invariant;
90 ////////////////////////////////////////////////////////////////////////
92 // TextInfo Constructors
94 // Implements CultureInfo.TextInfo.
96 ////////////////////////////////////////////////////////////////////////
97 internal TextInfo(CultureData cultureData)
99 // This is our primary data source, we don't need most of the rest of this
100 this.m_cultureData = cultureData;
101 this.m_cultureName = this.m_cultureData.CultureName;
102 this.m_textInfoName = this.m_cultureData.STEXTINFO;
105 this.m_dataHandle = CompareInfo.InternalInitSortHandle(m_textInfoName, out handleOrigin);
106 this.m_handleOrigin = handleOrigin;
110 ////////////////////////////////////////////////////////////////////////
112 // Serialization / Deserialization
114 // Note that we have to respect the Whidbey behavior for serialization compatibility
116 ////////////////////////////////////////////////////////////////////////
118 #region Serialization
119 // the following fields are defined to keep the compatibility with Whidbey.
120 // don't change/remove the names/types of these fields.
121 [OptionalField(VersionAdded = 2)]
122 private string customCultureName;
124 // the following fields are defined to keep compatibility with Everett.
125 // don't change/remove the names/types of these fields.
126 [OptionalField(VersionAdded = 1)]
127 internal int m_nDataItem;
128 [OptionalField(VersionAdded = 1)]
129 internal bool m_useUserOverride;
130 [OptionalField(VersionAdded = 1)]
131 internal int m_win32LangID;
135 private void OnDeserializing(StreamingContext ctx)
137 // Clear these so we can check if we've fixed them yet
138 this.m_cultureData = null;
139 this.m_cultureName = null;
142 private void OnDeserialized()
144 // this method will be called twice because of the support of IDeserializationCallback
145 if (this.m_cultureData == null)
147 if (this.m_cultureName == null)
149 // This is whidbey data, get it from customCultureName/win32langid
150 if (this.customCultureName != null)
152 // They gave a custom cultuer name, so use that
153 this.m_cultureName = this.customCultureName;
158 if (m_win32LangID == 0)
160 // m_cultureName and m_win32LangID are nulls which means we got uninitialized textinfo serialization stream.
161 // To be compatible with v2/3/3.5 we need to return ar-SA TextInfo in this case.
162 m_cultureName = "ar-SA";
166 // No custom culture, use the name from the LCID
167 m_cultureName = CultureInfo.GetCultureInfo(m_win32LangID).m_cultureData.CultureName;
173 // Get the text info name belonging to that culture
174 this.m_cultureData = CultureInfo.GetCultureInfo(m_cultureName).m_cultureData;
175 this.m_textInfoName = this.m_cultureData.STEXTINFO;
178 this.m_dataHandle = CompareInfo.InternalInitSortHandle(m_textInfoName, out handleOrigin);
179 this.m_handleOrigin = handleOrigin;
186 private void OnDeserialized(StreamingContext ctx)
192 private void OnSerializing(StreamingContext ctx)
195 // Initialize the fields Whidbey expects:
196 // Whidbey expected this, so set it, but the value doesn't matter much
197 this.m_useUserOverride = false;
198 #endif // FEATURE_CORECLR
200 // Relabel our name since Whidbey expects it to be called customCultureName
201 this.customCultureName = this.m_cultureName;
204 // Ignore the m_win32LangId because whidbey'll just get it by name if we make it the LOCALE_CUSTOM_UNSPECIFIED.
205 this.m_win32LangID = (CultureInfo.GetCultureInfo(m_cultureName)).LCID;
209 #endregion Serialization
212 // Internal ordinal comparison functions
214 internal static int GetHashCodeOrdinalIgnoreCase(String s)
216 return GetHashCodeOrdinalIgnoreCase(s, false, 0);
219 internal static int GetHashCodeOrdinalIgnoreCase(String s, bool forceRandomizedHashing, long additionalEntropy)
221 // This is the same as an case insensitive hash for Invariant
222 // (not necessarily true for sorting, but OK for casing & then we apply normal hash code rules)
223 return (Invariant.GetCaseInsensitiveHashCode(s, forceRandomizedHashing, additionalEntropy));
226 [System.Security.SecuritySafeCritical]
227 internal static unsafe bool TryFastFindStringOrdinalIgnoreCase(int searchFlags, String source, int startIndex, String value, int count, ref int foundIndex)
229 return InternalTryFindStringOrdinalIgnoreCase(searchFlags, source, count, startIndex, value, value.Length, ref foundIndex);
232 // This function doesn't check arguments. Please do check in the caller.
233 // The underlying unmanaged code will assert the sanity of arguments.
234 [System.Security.SecuritySafeCritical] // auto-generated
235 internal static unsafe int CompareOrdinalIgnoreCase(String str1, String str2)
237 // Compare the whole string and ignore case.
238 return InternalCompareStringOrdinalIgnoreCase(str1, 0, str2, 0, str1.Length, str2.Length);
241 // This function doesn't check arguments. Please do check in the caller.
242 // The underlying unmanaged code will assert the sanity of arguments.
243 [System.Security.SecuritySafeCritical] // auto-generated
244 internal static unsafe int CompareOrdinalIgnoreCaseEx(String strA, int indexA, String strB, int indexB, int lengthA, int lengthB )
246 Contract.Assert(strA.Length >= indexA + lengthA, "[TextInfo.CompareOrdinalIgnoreCaseEx] Caller should've validated strA.Length >= indexA + lengthA");
247 Contract.Assert(strB.Length >= indexB + lengthB, "[TextInfo.CompareOrdinalIgnoreCaseEx] Caller should've validated strB.Length >= indexB + lengthB");
248 return InternalCompareStringOrdinalIgnoreCase(strA, indexA, strB, indexB, lengthA, lengthB);
251 internal static int IndexOfStringOrdinalIgnoreCase(String source, String value, int startIndex, int count)
253 Contract.Assert(source != null, "[TextInfo.IndexOfStringOrdinalIgnoreCase] Caller should've validated source != null");
254 Contract.Assert(value != null, "[TextInfo.IndexOfStringOrdinalIgnoreCase] Caller should've validated value != null");
255 Contract.Assert(startIndex + count <= source.Length, "[TextInfo.IndexOfStringOrdinalIgnoreCase] Caller should've validated startIndex + count <= source.Length");
257 // We return 0 if both inputs are empty strings
258 if (source.Length == 0 && value.Length == 0)
265 if (TryFastFindStringOrdinalIgnoreCase(Microsoft.Win32.Win32Native.FIND_FROMSTART, source, startIndex, value, count, ref ret))
268 // the search space within [source] starts at offset [startIndex] inclusive and includes
269 // [count] characters (thus the last included character is at index [startIndex + count -1]
270 // [end] is the index of the next character after the search space
271 // (it points past the end of the search space)
272 int end = startIndex + count;
274 // maxStartIndex is the index beyond which we never *start* searching, inclusive; in other words;
275 // a search could include characters beyond maxStartIndex, but we'd never begin a search at an
276 // index strictly greater than maxStartIndex.
277 int maxStartIndex = end - value.Length;
279 for (; startIndex <= maxStartIndex; startIndex++)
281 // We should always have the same or more characters left to search than our actual pattern
282 Contract.Assert(end - startIndex >= value.Length);
283 // since this is an ordinal comparison, we can assume that the lengths must match
284 if (CompareOrdinalIgnoreCaseEx(source, startIndex, value, 0, value.Length, value.Length) == 0)
294 internal static int LastIndexOfStringOrdinalIgnoreCase(String source, String value, int startIndex, int count)
296 Contract.Assert(source != null, "[TextInfo.LastIndexOfStringOrdinalIgnoreCase] Caller should've validated source != null");
297 Contract.Assert(value != null, "[TextInfo.LastIndexOfStringOrdinalIgnoreCase] Caller should've validated value != null");
298 Contract.Assert(startIndex - count+1 >= 0, "[TextInfo.LastIndexOfStringOrdinalIgnoreCase] Caller should've validated startIndex - count+1 >= 0");
299 Contract.Assert(startIndex <= source.Length, "[TextInfo.LastIndexOfStringOrdinalIgnoreCase] Caller should've validated startIndex <= source.Length");
301 // If value is Empty, the return value is startIndex
302 if (value.Length == 0)
309 if (TryFastFindStringOrdinalIgnoreCase(Microsoft.Win32.Win32Native.FIND_FROMEND, source, startIndex, value, count, ref ret))
312 // the search space within [source] ends at offset [startIndex] inclusive
313 // and includes [count] characters
314 // minIndex is the first included character and is at index [startIndex - count + 1]
315 int minIndex = startIndex - count + 1;
317 // First place we can find it is start index - (value.length -1)
318 if (value.Length > 0)
320 startIndex -= (value.Length - 1);
323 for (; startIndex >= minIndex; startIndex--)
325 if (CompareOrdinalIgnoreCaseEx(source, startIndex, value, 0, value.Length, value.Length) == 0)
336 ////////////////////////////////////////////////////////////////////////
340 // Returns the number of the code page used by this writing system.
341 // The type parameter can be any of the following values:
346 ////////////////////////////////////////////////////////////////////////
350 public virtual int ANSICodePage {
352 return (this.m_cultureData.IDEFAULTANSICODEPAGE);
357 public virtual int OEMCodePage {
359 return (this.m_cultureData.IDEFAULTOEMCODEPAGE);
364 public virtual int MacCodePage {
366 return (this.m_cultureData.IDEFAULTMACCODEPAGE);
371 public virtual int EBCDICCodePage {
373 return (this.m_cultureData.IDEFAULTEBCDICCODEPAGE);
379 ////////////////////////////////////////////////////////////////////////
383 // We need a way to get an LCID from outside of the BCL. This prop is the way.
384 // NOTE: neutral cultures will cause GPS incorrect LCIDS from this
386 ////////////////////////////////////////////////////////////////////////
389 [System.Runtime.InteropServices.ComVisible(false)]
394 // Just use the LCID from our text info name
395 return CultureInfo.GetCultureInfo(this.m_textInfoName).LCID;
399 ////////////////////////////////////////////////////////////////////////
403 // The name of the culture associated with the current TextInfo.
405 ////////////////////////////////////////////////////////////////////////
406 [System.Runtime.InteropServices.ComVisible(false)]
407 public string CultureName
411 return(this.m_textInfoName);
415 ////////////////////////////////////////////////////////////////////////
419 // Detect if the object is readonly.
421 ////////////////////////////////////////////////////////////////////////
422 [System.Runtime.InteropServices.ComVisible(false)]
423 public bool IsReadOnly
425 get { return (m_isReadOnly); }
428 ////////////////////////////////////////////////////////////////////////
432 // Is the implementation of ICloneable.
434 ////////////////////////////////////////////////////////////////////////
435 [System.Runtime.InteropServices.ComVisible(false)]
436 public virtual Object Clone()
438 object o = MemberwiseClone();
439 ((TextInfo) o).SetReadOnlyState(false);
443 ////////////////////////////////////////////////////////////////////////
447 // Create a cloned readonly instance or return the input one if it is
450 ////////////////////////////////////////////////////////////////////////
451 [System.Runtime.InteropServices.ComVisible(false)]
452 public static TextInfo ReadOnly(TextInfo textInfo)
454 if (textInfo == null) { throw new ArgumentNullException("textInfo"); }
455 Contract.EndContractBlock();
456 if (textInfo.IsReadOnly) { return (textInfo); }
458 TextInfo clonedTextInfo = (TextInfo)(textInfo.MemberwiseClone());
459 clonedTextInfo.SetReadOnlyState(true);
461 return (clonedTextInfo);
464 private void VerifyWritable()
468 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
470 Contract.EndContractBlock();
473 internal void SetReadOnlyState(bool readOnly)
475 m_isReadOnly = readOnly;
479 ////////////////////////////////////////////////////////////////////////
483 // Returns the string used to separate items in a list.
485 ////////////////////////////////////////////////////////////////////////
488 public virtual String ListSeparator
490 [System.Security.SecuritySafeCritical] // auto-generated
493 if (m_listSeparator == null) {
494 m_listSeparator = this.m_cultureData.SLIST;
496 return (m_listSeparator);
499 [System.Runtime.InteropServices.ComVisible(false)]
504 throw new ArgumentNullException("value", Environment.GetResourceString("ArgumentNull_String"));
506 Contract.EndContractBlock();
508 m_listSeparator = value;
512 ////////////////////////////////////////////////////////////////////////
516 // Converts the character or string to lower case. Certain locales
517 // have different casing semantics from the file systems in Win32.
519 ////////////////////////////////////////////////////////////////////////
521 [System.Security.SecuritySafeCritical] // auto-generated
522 public unsafe virtual char ToLower(char c)
524 if(IsAscii(c) && IsAsciiCasingSameAsInvariant)
526 return ToLowerAsciiInvariant(c);
528 return (InternalChangeCaseChar(this.m_dataHandle, this.m_handleOrigin, this.m_textInfoName, c, false));
531 [System.Security.SecuritySafeCritical] // auto-generated
532 public unsafe virtual String ToLower(String str)
534 if (str == null) { throw new ArgumentNullException("str"); }
535 Contract.EndContractBlock();
537 return InternalChangeCaseString(this.m_dataHandle, this.m_handleOrigin, this.m_textInfoName, str, false);
541 static private Char ToLowerAsciiInvariant(Char c)
543 if ('A' <= c && c <= 'Z')
545 c = (Char)(c | 0x20);
550 ////////////////////////////////////////////////////////////////////////
554 // Converts the character or string to upper case. Certain locales
555 // have different casing semantics from the file systems in Win32.
557 ////////////////////////////////////////////////////////////////////////
559 [System.Security.SecuritySafeCritical] // auto-generated
560 public unsafe virtual char ToUpper(char c)
562 if (IsAscii(c) && IsAsciiCasingSameAsInvariant)
564 return ToUpperAsciiInvariant(c);
566 return (InternalChangeCaseChar(this.m_dataHandle, this.m_handleOrigin, this.m_textInfoName, c, true));
570 [System.Security.SecuritySafeCritical] // auto-generated
571 public unsafe virtual String ToUpper(String str)
573 if (str == null) { throw new ArgumentNullException("str"); }
574 Contract.EndContractBlock();
575 return InternalChangeCaseString(this.m_dataHandle, this.m_handleOrigin, this.m_textInfoName, str, true);
578 static private Char ToUpperAsciiInvariant(Char c)
580 if ('a' <= c && c <= 'z')
582 c = (Char)(c & ~0x20);
587 static private bool IsAscii(Char c)
592 private bool IsAsciiCasingSameAsInvariant
596 if (m_IsAsciiCasingSameAsInvariant == null)
598 m_IsAsciiCasingSameAsInvariant =
599 CultureInfo.GetCultureInfo(m_textInfoName).CompareInfo.Compare("abcdefghijklmnopqrstuvwxyz",
600 "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
601 CompareOptions.IgnoreCase) == 0;
603 return (bool)m_IsAsciiCasingSameAsInvariant;
607 ////////////////////////////////////////////////////////////////////////
611 // Implements Object.Equals(). Returns a boolean indicating whether
612 // or not object refers to the same CultureInfo as the current instance.
614 ////////////////////////////////////////////////////////////////////////
617 public override bool Equals(Object obj)
619 TextInfo that = obj as TextInfo;
623 return this.CultureName.Equals(that.CultureName);
630 ////////////////////////////////////////////////////////////////////////
634 // Implements Object.GetHashCode(). Returns the hash code for the
635 // CultureInfo. The hash code is guaranteed to be the same for CultureInfo A
636 // and B where A.Equals(B) is true.
638 ////////////////////////////////////////////////////////////////////////
641 public override int GetHashCode()
643 return (this.CultureName.GetHashCode());
647 ////////////////////////////////////////////////////////////////////////
651 // Implements Object.ToString(). Returns a string describing the
654 ////////////////////////////////////////////////////////////////////////
657 public override String ToString()
659 return ("TextInfo - " + this.m_cultureData.CultureName);
666 // Titlecasing refers to a casing practice wherein the first letter of a word is an uppercase letter
667 // and the rest of the letters are lowercase. The choice of which words to titlecase in headings
668 // and titles is dependent on language and local conventions. For example, "The Merry Wives of Windor"
669 // is the appropriate titlecasing of that play's name in English, with the word "of" not titlecased.
670 // In German, however, the title is "Die lustigen Weiber von Windsor," and both "lustigen" and "von"
671 // are not titlecased. In French even fewer words are titlecased: "Les joyeuses commeres de Windsor."
673 // Moreover, the determination of what actually constitutes a word is language dependent, and this can
674 // influence which letter or letters of a "word" are uppercased when titlecasing strings. For example
675 // "l'arbre" is considered two words in French, whereas "can't" is considered one word in English.
678 // Differences between UNICODE 5.0 and the .NET Framework:
679 // -------------------------------------------------------------------------------------
680 // The .NET Framework previously shipped a naive titlecasing implementation. Every word is titlecased
681 // regardless of language or orthographic practice. Furthermore, apostrophe is always considered to be
682 // a word joiner as used in English. The longterm vision is to depend on the operating system for
683 // titlecasing. Windows 7 is expected to be the first release with this feature. On the Macintosh side,
684 // titlecasing is not available as of version 10.5 of the operating system.
687 public unsafe String ToTitleCase(String str) {
689 throw new ArgumentNullException("str");
691 Contract.EndContractBlock();
692 if (str.Length == 0) {
696 StringBuilder result = new StringBuilder();
697 String lowercaseData = null;
699 for (int i = 0; i < str.Length; i++) {
700 UnicodeCategory charType;
703 charType = CharUnicodeInfo.InternalGetUnicodeCategory(str, i, out charLen);
704 if (Char.CheckLetter(charType)) {
705 // Do the titlecasing for the first character of the word.
706 i = AddTitlecaseLetter(ref result, ref str, i, charLen) + 1;
709 // Convert the characters until the end of the this word
712 int lowercaseStart = i;
715 // Use hasLowerCase flag to prevent from lowercasing acronyms (like "URT", "USA", etc)
716 // This is in line with Word 2000 behavior of titlecasing.
718 bool hasLowerCase = (charType == UnicodeCategory.LowercaseLetter);
719 // Use a loop to find all of the other letters following this letter.
720 while (i < str.Length) {
721 charType = CharUnicodeInfo.InternalGetUnicodeCategory(str, i, out charLen);
722 if (IsLetterCategory(charType)) {
723 if (charType == UnicodeCategory.LowercaseLetter) {
727 } else if (str[i] == '\'') {
730 if (lowercaseData==null) {
731 lowercaseData = this.ToLower(str);
733 result.Append(lowercaseData, lowercaseStart, i - lowercaseStart);
735 result.Append(str, lowercaseStart, i - lowercaseStart);
739 } else if (!IsWordSeparator(charType)) {
740 // This category is considered to be part of the word.
741 // This is any category that is marked as false in wordSeprator array.
744 // A word separator. Break out of the loop.
749 int count = i - lowercaseStart;
753 if (lowercaseData==null) {
754 lowercaseData = this.ToLower(str);
756 result.Append(lowercaseData, lowercaseStart, count);
758 result.Append(str, lowercaseStart, count);
762 if (i < str.Length) {
763 // not a letter, just append it
764 i = AddNonLetter(ref result, ref str, i, charLen);
768 // not a letter, just append it
769 i = AddNonLetter(ref result, ref str, i, charLen);
772 return (result.ToString());
775 private static int AddNonLetter(ref StringBuilder result, ref String input, int inputIndex, int charLen) {
776 Contract.Assert(charLen == 1 || charLen == 2, "[TextInfo.AddNonLetter] CharUnicodeInfo.InternalGetUnicodeCategory returned an unexpected charLen!");
779 result.Append(input[inputIndex++]);
780 result.Append(input[inputIndex]);
783 result.Append(input[inputIndex]);
789 private int AddTitlecaseLetter(ref StringBuilder result, ref String input, int inputIndex, int charLen) {
790 Contract.Assert(charLen == 1 || charLen == 2, "[TextInfo.AddTitlecaseLetter] CharUnicodeInfo.InternalGetUnicodeCategory returned an unexpected charLen!");
792 // for surrogate pairs do a simple ToUpper operation on the substring
795 result.Append( this.ToUpper(input.Substring(inputIndex, charLen)) );
799 switch (input[inputIndex]) {
801 // For AppCompat, the Titlecase Case Mapping data from NDP 2.0 is used below.
802 case (char)0x01C4: // DZ with Caron -> Dz with Caron
803 case (char)0x01C5: // Dz with Caron -> Dz with Caron
804 case (char)0x01C6: // dz with Caron -> Dz with Caron
805 result.Append( (char)0x01C5 );
807 case (char)0x01C7: // LJ -> Lj
808 case (char)0x01C8: // Lj -> Lj
809 case (char)0x01C9: // lj -> Lj
810 result.Append( (char)0x01C8 );
812 case (char)0x01CA: // NJ -> Nj
813 case (char)0x01CB: // Nj -> Nj
814 case (char)0x01CC: // nj -> Nj
815 result.Append( (char)0x01CB );
817 case (char)0x01F1: // DZ -> Dz
818 case (char)0x01F2: // Dz -> Dz
819 case (char)0x01F3: // dz -> Dz
820 result.Append( (char)0x01F2 );
823 result.Append( this.ToUpper(input[inputIndex]) );
832 // Used in ToTitleCase():
833 // When we find a starting letter, the following array decides if a category should be
834 // considered as word seprator or not.
836 private const int wordSeparatorMask =
837 /* false */ (0 << 0) | // UppercaseLetter = 0,
838 /* false */ (0 << 1) | // LowercaseLetter = 1,
839 /* false */ (0 << 2) | // TitlecaseLetter = 2,
840 /* false */ (0 << 3) | // ModifierLetter = 3,
841 /* false */ (0 << 4) | // OtherLetter = 4,
842 /* false */ (0 << 5) | // NonSpacingMark = 5,
843 /* false */ (0 << 6) | // SpacingCombiningMark = 6,
844 /* false */ (0 << 7) | // EnclosingMark = 7,
845 /* false */ (0 << 8) | // DecimalDigitNumber = 8,
846 /* false */ (0 << 9) | // LetterNumber = 9,
847 /* false */ (0 << 10) | // OtherNumber = 10,
848 /* true */ (1 << 11) | // SpaceSeparator = 11,
849 /* true */ (1 << 12) | // LineSeparator = 12,
850 /* true */ (1 << 13) | // ParagraphSeparator = 13,
851 /* true */ (1 << 14) | // Control = 14,
852 /* true */ (1 << 15) | // Format = 15,
853 /* false */ (0 << 16) | // Surrogate = 16,
854 /* false */ (0 << 17) | // PrivateUse = 17,
855 /* true */ (1 << 18) | // ConnectorPunctuation = 18,
856 /* true */ (1 << 19) | // DashPunctuation = 19,
857 /* true */ (1 << 20) | // OpenPunctuation = 20,
858 /* true */ (1 << 21) | // ClosePunctuation = 21,
859 /* true */ (1 << 22) | // InitialQuotePunctuation = 22,
860 /* true */ (1 << 23) | // FinalQuotePunctuation = 23,
861 /* true */ (1 << 24) | // OtherPunctuation = 24,
862 /* true */ (1 << 25) | // MathSymbol = 25,
863 /* true */ (1 << 26) | // CurrencySymbol = 26,
864 /* true */ (1 << 27) | // ModifierSymbol = 27,
865 /* true */ (1 << 28) | // OtherSymbol = 28,
866 /* false */ (0 << 29); // OtherNotAssigned = 29;
868 private static bool IsWordSeparator(UnicodeCategory category) {
869 return (wordSeparatorMask & (1 << (int)category)) != 0;
872 private static bool IsLetterCategory(UnicodeCategory uc) {
873 return (uc == UnicodeCategory.UppercaseLetter
874 || uc == UnicodeCategory.LowercaseLetter
875 || uc == UnicodeCategory.TitlecaseLetter
876 || uc == UnicodeCategory.ModifierLetter
877 || uc == UnicodeCategory.OtherLetter);
884 // Returns true if the dominant direction of text and UI such as the relative position of buttons and scroll bars
886 [System.Runtime.InteropServices.ComVisible(false)]
887 public bool IsRightToLeft
891 return this.m_cultureData.IsRightToLeft;
896 void IDeserializationCallback.OnDeserialization(Object sender)
902 // Get case-insensitive hash code for the specified string.
904 // NOTENOTE: this is an internal function. The caller should verify the string
905 // is not null before calling this. Currenlty, CaseInsensitiveHashCodeProvider
908 [System.Security.SecuritySafeCritical] // auto-generated
909 internal unsafe int GetCaseInsensitiveHashCode(String str)
911 return GetCaseInsensitiveHashCode(str, false, 0);
914 [System.Security.SecuritySafeCritical] // auto-generated
915 internal unsafe int GetCaseInsensitiveHashCode(String str, bool forceRandomizedHashing, long additionalEntropy)
920 throw new ArgumentNullException("str");
922 Contract.EndContractBlock();
925 return (InternalGetCaseInsHash(this.m_dataHandle, this.m_handleOrigin, this.m_textInfoName, str, forceRandomizedHashing, additionalEntropy));
928 // Change case (ToUpper/ToLower) -- COMNlsInfo::InternalChangeCaseChar
929 [System.Security.SecurityCritical] // auto-generated
930 [MethodImplAttribute(MethodImplOptions.InternalCall)]
931 private static unsafe extern char InternalChangeCaseChar(IntPtr handle, IntPtr handleOrigin, String localeName, char ch, bool isToUpper);
933 // Change case (ToUpper/ToLower) -- COMNlsInfo::InternalChangeCaseString
934 [System.Security.SecurityCritical] // auto-generated
935 [MethodImplAttribute(MethodImplOptions.InternalCall)]
936 private static unsafe extern String InternalChangeCaseString(IntPtr handle, IntPtr handleOrigin, String localeName, String str, bool isToUpper);
938 // Get case insensitive hash -- ComNlsInfo::InternalGetCaseInsHash
939 [System.Security.SecurityCritical] // auto-generated
940 [MethodImplAttribute(MethodImplOptions.InternalCall)]
941 private static unsafe extern int InternalGetCaseInsHash(IntPtr handle, IntPtr handleOrigin, String localeName, String str, bool forceRandomizedHashing, long additionalEntropy);
943 // Call ::CompareStringOrdinal -- ComNlsInfo::InternalCompareStringOrdinalIgnoreCase
944 // Start at indexes and compare for length characters (or remainder of string if length == -1)
945 [System.Security.SecurityCritical] // auto-generated
946 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
947 [SuppressUnmanagedCodeSecurity]
948 private static unsafe extern int InternalCompareStringOrdinalIgnoreCase(String string1, int index1, String string2, int index2, int length1, int length2);
950 // ComNlsInfo::InternalTryFindStringOrdinalIgnoreCase attempts a faster IndexOf/LastIndexOf OrdinalIgnoreCase using a kernel function.
951 // Returns true if FindStringOrdinal was handled, with foundIndex set to the target's index into the source
952 // Returns false when FindStringOrdinal wasn't handled
953 [System.Security.SecurityCritical] // auto-generated
954 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
955 [SuppressUnmanagedCodeSecurity]
956 [return: MarshalAs(UnmanagedType.Bool)]
957 private static unsafe extern bool InternalTryFindStringOrdinalIgnoreCase(int searchFlags, String source, int sourceCount, int startIndex, String target, int targetCount, ref int foundIndex);