The Labs \ Source Viewer \ SSCLI \ System.Text.RegularExpressions \ SingleRangeComparer

  1. //------------------------------------------------------------------------------
  2. // <copyright file="RegexCharClass.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. // This RegexCharClass class provides the "set of Unicode chars" functionality
  16. // used by the regexp engine.
  17. // The main function of RegexCharClass is as a builder to turn ranges, characters and
  18. // Unicode categories into a single string. This string is used as a black box
  19. // representation of a character class by the rest of Regex. The format is as follows.
  20. //
  21. // Char index Use
  22. // 0 Flags - currently this only holds the "negate" flag
  23. // 1 length of the string representing the "set" portion, eg [a-z0-9] only has a "set"
  24. // 2 length of the string representing the "category" portion, eg [\p{Lu}] only has a "category"
  25. // 3...m The set. These are a series of ranges which define the characters included in the set.
  26. // To determine if a given character is in the set, we binary search over this set of ranges
  27. // and see where the character should go. Based on whether the ending index is odd or even,
  28. // we know if the character is in the set.
  29. // m+1...n The categories. This is a list of UnicodeCategory enum values which describe categories
  30. // included in this class.
  31. namespace System.Text.RegularExpressions
  32. {
  33.    
  34.     using System.Collections;
  35.     using System.Globalization;
  36.     using System.Diagnostics;
  37.    
  38.     internal sealed class RegexCharClass
  39.     {
  40.         // instance data
  41.         private ArrayList _rangelist;
  42.         private StringBuilder _categories;
  43.         private bool _canonical;
  44.         private bool _negate;
  45.         private RegexCharClass _subtractor;
  46.        
  47.         // Constants
  48.         private const int FLAGS = 0;
  49.         private const int SETLENGTH = 1;
  50.         private const int CATEGORYLENGTH = 2;
  51.         private const int SETSTART = 3;
  52.        
  53.         private const char Nullchar = '\0';
  54.         private const char Lastchar = '￿';
  55.        
  56.         private const char GroupChar = (char)0;
  57.        
  58.        
  59.         private const short SpaceConst = 100;
  60.         private const short NotSpaceConst = -100;
  61.        
  62.         private static readonly string Space = "d";
  63.         private static readonly string NotSpace = NegateCategory(Space);
  64.         private static readonly string Word;
  65.         private static readonly string NotWord;
  66.        
  67.         static internal readonly string SpaceClass;
  68.         static internal readonly string NotSpaceClass;
  69.         static internal readonly string WordClass;
  70.         static internal readonly string NotWordClass;
  71.         static internal readonly string DigitClass;
  72.         static internal readonly string NotDigitClass;
  73.        
  74.         private const string ECMASpaceSet = "\t\u14 !";
  75.         private const string NotECMASpaceSet = "\0\t\u14 !";
  76.         private const string ECMAWordSet = "0:A[_`a{İı";
  77.         private const string NotECMAWordSet = "\00:A[_`a{İı";
  78.         private const string ECMADigitSet = "0:";
  79.         private const string NotECMADigitSet = "\00:";
  80.        
  81.         internal const string ECMASpaceClass = "\0\u4\0" + ECMASpaceSet;
  82.         internal const string NotECMASpaceClass = "\u1\u4\0" + ECMASpaceSet;
  83.         internal const string ECMAWordClass = "\0\n\0" + ECMAWordSet;
  84.         internal const string NotECMAWordClass = "\u1\n\0" + ECMAWordSet;
  85.         internal const string ECMADigitClass = "\0\u2\0" + ECMADigitSet;
  86.         internal const string NotECMADigitClass = "\u1\u2\0" + ECMADigitSet;
  87.        
  88.         internal const string AnyClass = "\0\u1\0\0";
  89.         internal const string EmptyClass = "\0\0\0";
  90.        
  91.         private static Hashtable _definedCategories;
  92.        
  93. /*
  94.         *  The property table contains all the block definitions defined in the
  95.         *  XML schema spec (http://www.w3.org/TR/2001/PR-xmlschema-2-20010316/#charcter-classes), Unicode 4.0 spec (www.unicode.org),
  96.         *  and Perl 5.6 (see Programming Perl, 3rd edition page 167).  Three blocks defined by Perl (and here) may
  97.         *  not be in the Unicode: IsHighPrivateUseSurrogates, IsHighSurrogates, and IsLowSurrogates. 
  98.         * 
  99.         **/       
  100.         // Has to be sorted by the first column
  101.         private static readonly string[,] _propTable = {{"IsAlphabeticPresentationForms", "ffﭐ"}, {"IsArabic", "؀܀"}, {"IsArabicPresentationForms-A", "ﭐ︀"}, {"IsArabicPresentationForms-B", "ﹰ＀"}, {"IsArmenian", "԰֐"}, {"IsArrows", "←∀"}, {"IsBasicLatin", "\0\u128"}, {"IsBengali", "ঀ਀"}, {"IsBlockElements", "▀■"}, {"IsBopomofo", "㄀㄰"},
  102.         {"IsBopomofoExtended", "ㆠ㇀"}, {"IsBoxDrawing", "─▀"}, {"IsBraillePatterns", "⠀⤀"}, {"IsBuhid", "ᝀᝠ"}, {"IsCJKCompatibility", "㌀㐀"}, {"IsCJKCompatibilityForms", "︰﹐"}, {"IsCJKCompatibilityIdeographs", "豈ff"}, {"IsCJKRadicalsSupplement", "⺀⼀"}, {"IsCJKSymbolsandPunctuation", " ぀"}, {"IsCJKUnifiedIdeographs", "一ꀀ"},
  103.         {"IsCJKUnifiedIdeographsExtensionA", "㐀䷀"}, {"IsCherokee", "Ꭰ᐀"}, {"IsCombiningDiacriticalMarks", "̀Ͱ"}, {"IsCombiningDiacriticalMarksforSymbols", "⃐℀"}, {"IsCombiningHalfMarks", "︠︰"}, {"IsCombiningMarksforSymbols", "⃐℀"}, {"IsControlPictures", "␀⑀"}, {"IsCurrencySymbols", "₠⃐"}, {"IsCyrillic", "ЀԀ"}, {"IsCyrillicSupplement", "Ԁ԰"},
  104.         {"IsDevanagari", "ऀঀ"}, {"IsDingbats", "✀⟀"}, {"IsEnclosedAlphanumerics", "①─"}, {"IsEnclosedCJKLettersandMonths", "㈀㌀"}, {"IsEthiopic", "ሀᎀ"}, {"IsGeneralPunctuation", " ⁰"}, {"IsGeometricShapes", "■☀"}, {"IsGeorgian", "Ⴀᄀ"}, {"IsGreek", "ͰЀ"}, {"IsGreekExtended", "ἀ "},
  105.         {"IsGreekandCoptic", "ͰЀ"}, {"IsGujarati", "઀଀"}, {"IsGurmukhi", "਀઀"}, {"IsHalfwidthandFullwidthForms", "＀￰"}, {"IsHangulCompatibilityJamo", "㄰㆐"}, {"IsHangulJamo", "ᄀሀ"}, {"IsHangulSyllables", "가ힰ"}, {"IsHanunoo", "ᜠᝀ"}, {"IsHebrew", "֐؀"}, {"IsHighPrivateUseSurrogates", "󰀀"},
  106.         {"IsHighSurrogates", "��"}, {"IsHiragana", "぀゠"}, {"IsIPAExtensions", "ɐʰ"}, {"IsIdeographicDescriptionCharacters", "⿰ "}, {"IsKanbun", "㆐ㆠ"}, {"IsKangxiRadicals", "⼀⿠"}, {"IsKannada", "ಀഀ"}, {"IsKatakana", "゠㄀"}, {"IsKatakanaPhoneticExtensions", "ㇰ㈀"}, {"IsKhmer", "ក᠀"},
  107.         {"IsKhmerSymbols", "᧠ᨀ"}, {"IsLao", "຀ༀ"}, {"IsLatin-1Supplement", "\u128Ā"}, {"IsLatinExtended-A", "Āƀ"}, {"IsLatinExtended-B", "ƀɐ"}, {"IsLatinExtendedAdditional", "Ḁἀ"}, {"IsLetterlikeSymbols", "℀⅐"}, {"IsLimbu", "ᤀᥐ"}, {"IsLowSurrogates", "�"}, {"IsMalayalam", "ഀ඀"},
  108.         {"IsMathematicalOperators", "∀⌀"}, {"IsMiscellaneousMathematicalSymbols-A", "⟀⟰"}, {"IsMiscellaneousMathematicalSymbols-B", "⦀⨀"}, {"IsMiscellaneousSymbols", "☀✀"}, {"IsMiscellaneousSymbolsandArrows", "⬀Ⰰ"}, {"IsMiscellaneousTechnical", "⌀␀"}, {"IsMongolian", "᠀ᢰ"}, {"IsMyanmar", "ကႠ"}, {"IsNumberForms", "⅐←"}, {"IsOgham", " ᚠ"},
  109.         {"IsOpticalCharacterRecognition", "⑀①"}, {"IsOriya", "଀஀"}, {"IsPhoneticExtensions", "ᴀᶀ"}, {"IsPrivateUse", "豈"}, {"IsPrivateUseArea", "豈"}, {"IsRunic", "ᚠᜀ"}, {"IsSinhala", "඀฀"}, {"IsSmallFormVariants", "﹐ﹰ"}, {"IsSpacingModifierLetters", "ʰ̀"}, {"IsSpecials", "￰"},
  110.         {"IsSuperscriptsandSubscripts", "⁰₠"}, {"IsSupplementalArrows-A", "⟰⠀"}, {"IsSupplementalArrows-B", "⤀⦀"}, {"IsSupplementalMathematicalOperators", "⨀⬀"}, {"IsSyriac", "܀ݐ"}, {"IsTagalog", "ᜀᜠ"}, {"IsTagbanwa", "ᝠក"}, {"IsTaiLe", "ᥐᦀ"}, {"IsTamil", "஀ఀ"}, {"IsTelugu", "ఀಀ"},
  111.         {"IsThaana", "ހ߀"}, {"IsThai", "฀຀"}, {"IsTibetan", "ༀက"}, {"IsUnifiedCanadianAboriginalSyllabics", "᐀ "}, {"IsVariationSelectors", "︀︐"}, {"IsYiRadicals", "꒐ꓐ"}, {"IsYiSyllables", "ꀀ꒐"}, {"IsYijingHexagramSymbols", "䷀一"}, {"_xmlC", "-/0;A[_`a{·¸À×Ø÷øIJĴĿŁʼnŊſƀDŽǍDZǴǶǺȘɐʩʻ˂ː˒̀͆͢͠Ά΋Ό΍Ύ΢ΣϏϐϗϚϛϜϝϞϟϠϡϢϴЁЍЎѐёѝў҂҃҇ҐӅӇӉӋӍӐӬӮӶӸӺԱ՗ՙ՚աևֺֻ֑֢֣־ֿ׀ׁ׃ׅׄא׫װ׳ءػـٓ٠٪ٰڸںڿۀۏې۔ە۩۪ۮ۰ۺँऄअऺ़ॎ॑ॕक़।०॰ঁ঄অ঍এ঑ও঩প঱ল঳শ঺়ঽা৅ে৉োৎৗ৘ড়" + "৞য়৤০৲ਂਃਅ਋ਏ਑ਓ਩ਪ਱ਲ਴ਵ਷ਸ਺਼਽ਾ੃ੇ੉ੋ੎ਖ਼੝ਫ਼੟੦ੵઁ઄અઌઍ઎એ઒ઓ઩પ઱લ઴વ઺઼૆ે૊ો૎ૠૡ૦૰ଁ଄ଅ଍ଏ଑ଓ଩ପ଱ଲ଴ଶ଺଼ୄେ୉ୋ୎ୖ୘ଡ଼୞ୟୢ୦୰ஂ஄அ஋எ஑ஒ஖ங஛ஜ஝ஞ஠ண஥ந஫மஶஷ஺ா௃ெ௉ொ௎ௗ௘௧௰ఁఄఅ఍ఎ఑ఒ఩పఴవ఺ా౅ె౉ొ౎ౕ౗ౠౢ౦౰ಂ಄ಅ಍ಎ಑ಒ಩ಪ಴ವ಺ಾ೅ೆ೉ೊ೎ೕ೗ೞ೟ೠೢ" + "೦೰ംഄഅ഍എ഑ഒഩപഺാൄെ൉ൊൎൗ൘ൠൢ൦൰กฯะ฻เ๏๐๚ກ຃ຄ຅ງຉຊ຋ຍຎດຘນຠມ຤ລ຦ວຨສຬອຯະ຺ົ຾ເ໅ໆ໇່໎໐໚༘༚༠༪༵༶༷༸༹༺༾཈ཉཪཱ྅྆ྌྐྖྗ྘ྙྮྱྸྐྵྺႠ჆აჷᄀᄁᄂᄄᄅᄈᄉᄊᄋᄍᄎᄓᄼᄽᄾᄿᅀᅁᅌᅍᅎᅏᅐᅑᅔᅖᅙᅚᅟᅢᅣᅤᅥᅦᅧᅨᅩᅪᅭᅯᅲᅴᅵᅶᆞᆟᆨᆩᆫᆬᆮᆰᆷᆹᆺᆻᆼᇃᇫᇬᇰᇱᇹᇺḀẜẠỺἀ" + "἖Ἐ἞ἠ὆Ὀ὎ὐ὘Ὑ὚Ὓ὜Ὕ὞Ὗ὾ᾀ᾵ᾶ᾽ι᾿ῂ῅ῆ῍ῐ῔ῖ῜ῠ῭ῲ῵ῶ´⃐⃝⃡⃢Ω℧Kℬ℮ℯↀↃ々〆〇〈〡〰〱〶ぁゕ゙゛ゝゟァ・ーヿㄅㄭ一龦가힤"}, {"_xmlD", "0:٠٪۰ۺ०॰০ৰ੦ੰ૦૰୦୰௧௰౦౰೦೰൦൰๐๚໐໚༠༪၀၊፩፲០៪᠐᠚0:"},
  112.             /* Name Char              */            /* Start Name Char      */        {"_xmlI", ":;A[_`a{À×Ø÷øIJĴĿŁʼnŊſƀDŽǍDZǴǶǺȘɐʩʻ˂Ά·Έ΋Ό΍Ύ΢ΣϏϐϗϚϛϜϝϞϟϠϡϢϴЁЍЎѐёѝў҂ҐӅӇӉӋӍӐӬӮӶӸӺԱ՗ՙ՚աևא׫װ׳ءػفًٱڸںڿۀۏې۔ەۖۥۧअऺऽाक़ॢঅ঍এ঑ও঩প঱ল঳শ঺ড়৞য়ৢৰ৲ਅ਋ਏ਑ਓ਩ਪ਱ਲ਴ਵ਷ਸ਺ਖ਼੝ਫ਼੟ੲੵઅઌઍ઎એ઒ઓ઩પ઱લ઴વ઺ઽાૠૡଅ଍ଏ" + "଑ଓ଩ପ଱ଲ଴ଶ଺ଽାଡ଼୞ୟୢஅ஋எ஑ஒ஖ங஛ஜ஝ஞ஠ண஥ந஫மஶஷ஺అ఍ఎ఑ఒ఩పఴవ఺ౠౢಅ಍ಎ಑ಒ಩ಪ಴ವ಺ೞ೟ೠೢഅ഍എ഑ഒഩപഺൠൢกฯะัาิเๆກ຃ຄ຅ງຉຊ຋ຍຎດຘນຠມ຤ລ຦ວຨສຬອຯະັາິຽ຾ເ໅ཀ཈ཉཪႠ჆აჷᄀᄁᄂᄄᄅᄈᄉᄊᄋᄍᄎᄓᄼᄽᄾᄿᅀᅁᅌᅍᅎᅏᅐᅑᅔᅖᅙᅚᅟᅢᅣᅤᅥᅦᅧᅨᅩᅪᅭᅯᅲᅴᅵᅶᆞᆟᆨᆩᆫᆬ" + "ᆮᆰᆷᆹᆺᆻᆼᇃᇫᇬᇰᇱᇹᇺḀẜẠỺἀ἖Ἐ἞ἠ὆Ὀ὎ὐ὘Ὑ὚Ὓ὜Ὕ὞Ὗ὾ᾀ᾵ᾶ᾽ι᾿ῂ῅ῆ῍ῐ῔ῖ῜ῠ῭ῲ῵ῶ´Ω℧Kℬ℮ℯↀↃ〇〈〡〪ぁゕァ・ㄅㄭ一龦가힤"}, {"_xmlW", "$%+,0:<?A[^_`{|}~\u127¢«¬­®·¸»¼¿ÀȡȢȴɐʮʰ˯̀͐͠ͰʹͶͺͻ΄·Έ΋Ό΍Ύ΢ΣϏϐϷЀ҇҈ӏӐӶӸӺԀԐԱ՗ՙ՚աֈֺֻ֑֢֣־ֿ׀ׁ׃ׅׄא׫װ׳ءػـٖ٠٪ٮ۔ە۝۞ۮ۰ۿܐܭܰ݋ހ޲ँऄअऺ़ॎॐॕक़।०॰ঁ঄অ঍এ঑ও঩প঱ল঳শ঺়ঽা৅ে৉োৎৗ৘ড়৞য়৤০৻ਂਃਅ਋ਏ਑ਓ਩ਪ਱ਲ਴ਵ" + "਷ਸ਺਼਽ਾ੃ੇ੉ੋ੎ਖ਼੝ਫ਼੟੦ੵઁ઄અઌઍ઎એ઒ઓ઩પ઱લ઴વ઺઼૆ે૊ો૎ૐ૑ૠૡ૦૰ଁ଄ଅ଍ଏ଑ଓ଩ପ଱ଲ଴ଶ଺଼ୄେ୉ୋ୎ୖ୘ଡ଼୞ୟୢ୦ୱஂ஄அ஋எ஑ஒ஖ங஛ஜ஝ஞ஠ண஥ந஫மஶஷ஺ா௃ெ௉ொ௎ௗ௘௧௳ఁఄఅ఍ఎ఑ఒ఩పఴవ఺ా౅ె౉ొ౎ౕ౗ౠౢ౦౰ಂ಄ಅ಍ಎ಑ಒ಩ಪ಴ವ಺ಾ೅ೆ೉ೊ೎ೕ೗ೞ೟ೠೢ೦೰ംഄഅ഍എ഑ഒഩപഺാൄെ൉" + "ൊൎൗ൘ൠൢ൦൰ං඄අ඗ක඲ඳ඼ල඾ව෇්෋ා෕ූ෗ෘ෠ෲ෴ก฻฿๏๐๚ກ຃ຄ຅ງຉຊ຋ຍຎດຘນຠມ຤ລ຦ວຨສຬອ຺ົ຾ເ໅ໆ໇່໎໐໚ໜໞༀ༄༓༺༾཈ཉཫཱ྅྆ྌྐ྘ྙ྽྾࿍࿏࿐ကဢဣဨဩါာဳံ်၀၊ၐၚႠ჆აჹᄀᅚᅟᆣᆨᇺሀሇለቇቈ቉ቊ቎ቐ቗ቘ቙ቚ቞በኇኈ኉ኊ኎ነኯኰ኱ኲ኶ኸ኿ዀ዁ዂ዆ወዏዐ዗ዘዯደጏጐ጑ጒ጖ጘጟጠፇፈ፛፩፽Ꭰ" + "Ᏽᐁ᙭ᙯᙷᚁ᚛ᚠ᛫ᛮᛱᜀᜍᜎ᜕ᜠ᜵ᝀ᝔ᝠ᝭ᝮ᝱ᝲ᝴ក។ៗ៘៛៝០៪᠋᠎᠐᠚ᠠᡸᢀᢪḀẜẠỺἀ἖Ἐ἞ἠ὆Ὀ὎ὐ὘Ὑ὚Ὓ὜Ὕ὞Ὗ὾ᾀ᾵ᾶ῅ῆ῔ῖ῜῝῰ῲ῵ῶ῿⁄⁅⁒⁓⁰⁲⁴⁽ⁿ₍₠₲⃫⃐℀℻ℽ⅌⅓ↄ←〈⌫⎴⎷⏏␀␧⑀⑋①⓿─☔☖☘☙♾⚀⚊✁✅✆✊✌✨✩❌❍❎❏❓❖❗❘❟❡❨❶➕➘➰➱➿⟐⟦⟰⦃⦙⧘⧜⧼⧾⬀⺀⺚⺛⻴⼀⿖⿰⿼〄〈〒〔〠〰〱〽〾぀" + "ぁ゗゙゠ァ・ー㄀ㄅㄭㄱ㆏㆐ㆸㇰ㈝㈠㉄㉑㉼㉿㋌㋐㋿㌀㍷㍻㏞㏠㏿㐀䶶一龦ꀀ꒍꒐꓇가힤豈郞侮恵ff﬇ﬓ﬘יִ﬷טּ﬽מּ﬿נּ﭂ףּ﭅צּ﮲ﯓ﴾ﵐ﶐ﶒ﷈ﷰ﷽︀︐︠︤﹢﹣﹤﹧﹩﹪ﹰ﹵ﹶ﻽$%+,0:<?A[^_`{|}~⦅ヲ﾿ᅡ￈ᅧ￐ᅭ￘ᅳ￝¢￧│￯￾"}};
  113.        
  114.        
  115. /**************************************************************************
  116.             Let U be the set of Unicode character values and let L be the lowercase
  117.             function, mapping from U to U. To perform case insensitive matching of
  118.             character sets, we need to be able to map an interval I in U, say
  119.    
  120.                 I = [chMin, chMax] = { ch : chMin <= ch <= chMax }
  121.    
  122.             to a set A such that A contains L(I) and A is contained in the union of
  123.             I and L(I).
  124.    
  125.             The table below partitions U into intervals on which L is non-decreasing.
  126.             Thus, for any interval J = [a, b] contained in one of these intervals,
  127.             L(J) is contained in [L(a), L(b)].
  128.    
  129.             It is also true that for any such J, [L(a), L(b)] is contained in the
  130.             union of J and L(J). This does not follow from L being non-decreasing on
  131.             these intervals. It follows from the nature of the L on each interval.
  132.             On each interval, L has one of the following forms:
  133.    
  134.                 (1) L(ch) = constant            (LowercaseSet)
  135.                 (2) L(ch) = ch + offset        (LowercaseAdd)
  136.                 (3) L(ch) = ch | 1              (LowercaseBor)
  137.                 (4) L(ch) = ch + (ch & 1)      (LowercaseBad)
  138.    
  139.             It is easy to verify that for any of these forms [L(a), L(b)] is
  140.             contained in the union of [a, b] and L([a, b]).
  141.         ***************************************************************************/       
  142.        
  143.         private const int LowercaseSet = 0;
  144.         // Set to arg.
  145.         private const int LowercaseAdd = 1;
  146.         // Add arg.
  147.         private const int LowercaseBor = 2;
  148.         // Bitwise or with 1.
  149.         private const int LowercaseBad = 3;
  150.         // Bitwise and with 1 and add original.
  151.         private static readonly LowerCaseMapping[] _lcTable = new LowerCaseMapping[] {new LowerCaseMapping('A', 'Z', LowercaseAdd, 32), new LowerCaseMapping('À', 'Þ', LowercaseAdd, 32), new LowerCaseMapping('Ā', 'Į', LowercaseBor, 0), new LowerCaseMapping('İ', 'İ', LowercaseSet, 105), new LowerCaseMapping('IJ', 'Ķ', LowercaseBor, 0), new LowerCaseMapping('Ĺ', 'Ň', LowercaseBad, 0), new LowerCaseMapping('Ŋ', 'Ŷ', LowercaseBor, 0), new LowerCaseMapping('Ÿ', 'Ÿ', LowercaseSet, 255), new LowerCaseMapping('Ź', 'Ž', LowercaseBad, 0), new LowerCaseMapping('Ɓ', 'Ɓ', LowercaseSet, 595),
  152.         new LowerCaseMapping('Ƃ', 'Ƅ', LowercaseBor, 0), new LowerCaseMapping('Ɔ', 'Ɔ', LowercaseSet, 596), new LowerCaseMapping('Ƈ', 'Ƈ', LowercaseSet, 392), new LowerCaseMapping('Ɖ', 'Ɗ', LowercaseAdd, 205), new LowerCaseMapping('Ƌ', 'Ƌ', LowercaseSet, 396), new LowerCaseMapping('Ǝ', 'Ǝ', LowercaseSet, 477), new LowerCaseMapping('Ə', 'Ə', LowercaseSet, 601), new LowerCaseMapping('Ɛ', 'Ɛ', LowercaseSet, 603), new LowerCaseMapping('Ƒ', 'Ƒ', LowercaseSet, 402), new LowerCaseMapping('Ɠ', 'Ɠ', LowercaseSet, 608),
  153.         new LowerCaseMapping('Ɣ', 'Ɣ', LowercaseSet, 611), new LowerCaseMapping('Ɩ', 'Ɩ', LowercaseSet, 617), new LowerCaseMapping('Ɨ', 'Ɨ', LowercaseSet, 616), new LowerCaseMapping('Ƙ', 'Ƙ', LowercaseSet, 409), new LowerCaseMapping('Ɯ', 'Ɯ', LowercaseSet, 623), new LowerCaseMapping('Ɲ', 'Ɲ', LowercaseSet, 626), new LowerCaseMapping('Ɵ', 'Ɵ', LowercaseSet, 629), new LowerCaseMapping('Ơ', 'Ƥ', LowercaseBor, 0), new LowerCaseMapping('Ƨ', 'Ƨ', LowercaseSet, 424), new LowerCaseMapping('Ʃ', 'Ʃ', LowercaseSet, 643),
  154.         new LowerCaseMapping('Ƭ', 'Ƭ', LowercaseSet, 429), new LowerCaseMapping('Ʈ', 'Ʈ', LowercaseSet, 648), new LowerCaseMapping('Ư', 'Ư', LowercaseSet, 432), new LowerCaseMapping('Ʊ', 'Ʋ', LowercaseAdd, 217), new LowerCaseMapping('Ƴ', 'Ƶ', LowercaseBad, 0), new LowerCaseMapping('Ʒ', 'Ʒ', LowercaseSet, 658), new LowerCaseMapping('Ƹ', 'Ƹ', LowercaseSet, 441), new LowerCaseMapping('Ƽ', 'Ƽ', LowercaseSet, 445), new LowerCaseMapping('DŽ', 'Dž', LowercaseSet, 454), new LowerCaseMapping('LJ', 'Lj', LowercaseSet, 457),
  155.         new LowerCaseMapping('NJ', 'Nj', LowercaseSet, 460), new LowerCaseMapping('Ǎ', 'Ǜ', LowercaseBad, 0), new LowerCaseMapping('Ǟ', 'Ǯ', LowercaseBor, 0), new LowerCaseMapping('DZ', 'Dz', LowercaseSet, 499), new LowerCaseMapping('Ǵ', 'Ǵ', LowercaseSet, 501), new LowerCaseMapping('Ǻ', 'Ȗ', LowercaseBor, 0), new LowerCaseMapping('Ά', 'Ά', LowercaseSet, 940), new LowerCaseMapping('Έ', 'Ί', LowercaseAdd, 37), new LowerCaseMapping('Ό', 'Ό', LowercaseSet, 972), new LowerCaseMapping('Ύ', 'Ώ', LowercaseAdd, 63),
  156.         new LowerCaseMapping('Α', 'Ϋ', LowercaseAdd, 32), new LowerCaseMapping('Ϣ', 'Ϯ', LowercaseBor, 0), new LowerCaseMapping('Ё', 'Џ', LowercaseAdd, 80), new LowerCaseMapping('А', 'Я', LowercaseAdd, 32), new LowerCaseMapping('Ѡ', 'Ҁ', LowercaseBor, 0), new LowerCaseMapping('Ґ', 'Ҿ', LowercaseBor, 0), new LowerCaseMapping('Ӂ', 'Ӄ', LowercaseBad, 0), new LowerCaseMapping('Ӈ', 'Ӈ', LowercaseSet, 1224), new LowerCaseMapping('Ӌ', 'Ӌ', LowercaseSet, 1228), new LowerCaseMapping('Ӑ', 'Ӫ', LowercaseBor, 0),
  157.         new LowerCaseMapping('Ӯ', 'Ӵ', LowercaseBor, 0), new LowerCaseMapping('Ӹ', 'Ӹ', LowercaseSet, 1273), new LowerCaseMapping('Ա', 'Ֆ', LowercaseAdd, 48), new LowerCaseMapping('Ⴀ', 'Ⴥ', LowercaseAdd, 48), new LowerCaseMapping('Ḁ', 'Ỹ', LowercaseBor, 0), new LowerCaseMapping('Ἀ', 'Ἇ', LowercaseAdd, -8), new LowerCaseMapping('Ἐ', '἟', LowercaseAdd, -8), new LowerCaseMapping('Ἠ', 'Ἧ', LowercaseAdd, -8), new LowerCaseMapping('Ἰ', 'Ἷ', LowercaseAdd, -8), new LowerCaseMapping('Ὀ', 'Ὅ', LowercaseAdd, -8),
  158.         new LowerCaseMapping('Ὑ', 'Ὑ', LowercaseSet, 8017), new LowerCaseMapping('Ὓ', 'Ὓ', LowercaseSet, 8019), new LowerCaseMapping('Ὕ', 'Ὕ', LowercaseSet, 8021), new LowerCaseMapping('Ὗ', 'Ὗ', LowercaseSet, 8023), new LowerCaseMapping('Ὠ', 'Ὧ', LowercaseAdd, -8), new LowerCaseMapping('ᾈ', 'ᾏ', LowercaseAdd, -8), new LowerCaseMapping('ᾘ', 'ᾟ', LowercaseAdd, -8), new LowerCaseMapping('ᾨ', 'ᾯ', LowercaseAdd, -8), new LowerCaseMapping('Ᾰ', 'Ᾱ', LowercaseAdd, -8), new LowerCaseMapping('Ὰ', 'Ά', LowercaseAdd, -74),
  159.         new LowerCaseMapping('ᾼ', 'ᾼ', LowercaseSet, 8115), new LowerCaseMapping('Ὲ', 'Ή', LowercaseAdd, -86), new LowerCaseMapping('ῌ', 'ῌ', LowercaseSet, 8131), new LowerCaseMapping('Ῐ', 'Ῑ', LowercaseAdd, -8), new LowerCaseMapping('Ὶ', 'Ί', LowercaseAdd, -100), new LowerCaseMapping('Ῠ', 'Ῡ', LowercaseAdd, -8), new LowerCaseMapping('Ὺ', 'Ύ', LowercaseAdd, -112), new LowerCaseMapping('Ῥ', 'Ῥ', LowercaseSet, 8165), new LowerCaseMapping('Ὸ', 'Ό', LowercaseAdd, -128), new LowerCaseMapping('Ὼ', 'Ώ', LowercaseAdd, -126),
  160.         new LowerCaseMapping('ῼ', 'ῼ', LowercaseSet, 8179), new LowerCaseMapping('Ⅰ', 'Ⅿ', LowercaseAdd, 16), new LowerCaseMapping('Ⓐ', 'ⓐ', LowercaseAdd, 26), new LowerCaseMapping('A', 'Z', LowercaseAdd, 32)};
  161.        
  162.         static RegexCharClass()
  163.         {
  164.             _definedCategories = new Hashtable(31);
  165.            
  166.             char[] groups = new char[9];
  167.             StringBuilder word = new StringBuilder(11);
  168.            
  169.             word.Append(GroupChar);
  170.             groups[0] = GroupChar;
  171.            
  172.             // We need the UnicodeCategory enum values as a char so we can put them in a string
  173.             // in the hashtable. In order to get there, we first must cast to an int,
  174.             // then cast to a char
  175.             // Also need to distinguish between positive and negative values. UnicodeCategory is zero
  176.             // based, so we add one to each value and subtract it off later
  177.            
  178.             // Others
  179.             groups[1] = (char)((int)UnicodeCategory.Control + 1);
  180.             _definedCategories["Cc"] = groups[1].ToString();
  181.             // Control
  182.             groups[2] = (char)((int)UnicodeCategory.Format + 1);
  183.             _definedCategories["Cf"] = groups[2].ToString();
  184.             // Format
  185.             groups[3] = (char)((int)UnicodeCategory.OtherNotAssigned + 1);
  186.             _definedCategories["Cn"] = groups[3].ToString();
  187.             // Not assigned
  188.             groups[4] = (char)((int)UnicodeCategory.PrivateUse + 1);
  189.             _definedCategories["Co"] = groups[4].ToString();
  190.             // Private use
  191.             groups[5] = (char)((int)UnicodeCategory.Surrogate + 1);
  192.             _definedCategories["Cs"] = groups[5].ToString();
  193.             // Surrogate
  194.             groups[6] = GroupChar;
  195.             _definedCategories["C"] = new string(groups, 0, 7);
  196.            
  197.             // Letters
  198.             groups[1] = (char)((int)UnicodeCategory.LowercaseLetter + 1);
  199.             _definedCategories["Ll"] = groups[1].ToString();
  200.             // Lowercase
  201.             groups[2] = (char)((int)UnicodeCategory.ModifierLetter + 1);
  202.             _definedCategories["Lm"] = groups[2].ToString();
  203.             // Modifier
  204.             groups[3] = (char)((int)UnicodeCategory.OtherLetter + 1);
  205.             _definedCategories["Lo"] = groups[3].ToString();
  206.             // Other
  207.             groups[4] = (char)((int)UnicodeCategory.TitlecaseLetter + 1);
  208.             _definedCategories["Lt"] = groups[4].ToString();
  209.             // Titlecase
  210.             groups[5] = (char)((int)UnicodeCategory.UppercaseLetter + 1);
  211.             _definedCategories["Lu"] = groups[5].ToString();
  212.             // Uppercase
  213.             //groups[6] = GroupChar;
  214.             _definedCategories["L"] = new string(groups, 0, 7);
  215.             word.Append(new string(groups, 1, 5));
  216.            
  217.             // Marks
  218.             groups[1] = (char)((int)UnicodeCategory.SpacingCombiningMark + 1);
  219.             _definedCategories["Mc"] = groups[1].ToString();
  220.             // Spacing combining
  221.             groups[2] = (char)((int)UnicodeCategory.EnclosingMark + 1);
  222.             _definedCategories["Me"] = groups[2].ToString();
  223.             // Enclosing
  224.             groups[3] = (char)((int)UnicodeCategory.NonSpacingMark + 1);
  225.             _definedCategories["Mn"] = groups[3].ToString();
  226.             // Non-spacing
  227.             groups[4] = GroupChar;
  228.             _definedCategories["M"] = new string(groups, 0, 5);
  229.             //word.Append(groups[1]);
  230.             //word.Append(groups[3]);
  231.            
  232.             // Numbers
  233.             groups[1] = (char)((int)UnicodeCategory.DecimalDigitNumber + 1);
  234.             _definedCategories["Nd"] = groups[1].ToString();
  235.             // Decimal digit
  236.             groups[2] = (char)((int)UnicodeCategory.LetterNumber + 1);
  237.             _definedCategories["Nl"] = groups[2].ToString();
  238.             // Letter
  239.             groups[3] = (char)((int)UnicodeCategory.OtherNumber + 1);
  240.             _definedCategories["No"] = groups[3].ToString();
  241.             // Other
  242.             //groups[4] = GroupChar;
  243.             _definedCategories["N"] = new string(groups, 0, 5);
  244.             word.Append(groups[1]);
  245.             //word.Append(new String(groups, 1, 3));
  246.            
  247.             // Punctuation
  248.             groups[1] = (char)((int)UnicodeCategory.ConnectorPunctuation + 1);
  249.             _definedCategories["Pc"] = groups[1].ToString();
  250.             // Connector
  251.             groups[2] = (char)((int)UnicodeCategory.DashPunctuation + 1);
  252.             _definedCategories["Pd"] = groups[2].ToString();
  253.             // Dash
  254.             groups[3] = (char)((int)UnicodeCategory.ClosePunctuation + 1);
  255.             _definedCategories["Pe"] = groups[3].ToString();
  256.             // Close
  257.             groups[4] = (char)((int)UnicodeCategory.OtherPunctuation + 1);
  258.             _definedCategories["Po"] = groups[4].ToString();
  259.             // Other
  260.             groups[5] = (char)((int)UnicodeCategory.OpenPunctuation + 1);
  261.             _definedCategories["Ps"] = groups[5].ToString();
  262.             // Open
  263.             groups[6] = (char)((int)UnicodeCategory.FinalQuotePunctuation + 1);
  264.             _definedCategories["Pf"] = groups[6].ToString();
  265.             // Inital quote
  266.             groups[7] = (char)((int)UnicodeCategory.InitialQuotePunctuation + 1);
  267.             _definedCategories["Pi"] = groups[7].ToString();
  268.             // Final quote
  269.             groups[8] = GroupChar;
  270.             _definedCategories["P"] = new string(groups, 0, 9);
  271.             word.Append(groups[1]);
  272.            
  273.             // Symbols
  274.             groups[1] = (char)((int)UnicodeCategory.CurrencySymbol + 1);
  275.             _definedCategories["Sc"] = groups[1].ToString();
  276.             // Currency
  277.             groups[2] = (char)((int)UnicodeCategory.ModifierSymbol + 1);
  278.             _definedCategories["Sk"] = groups[2].ToString();
  279.             // Modifier
  280.             groups[3] = (char)((int)UnicodeCategory.MathSymbol + 1);
  281.             _definedCategories["Sm"] = groups[3].ToString();
  282.             // Math
  283.             groups[4] = (char)((int)UnicodeCategory.OtherSymbol + 1);
  284.             _definedCategories["So"] = groups[4].ToString();
  285.             // Other
  286.             groups[5] = GroupChar;
  287.             _definedCategories["S"] = new string(groups, 0, 6);
  288.            
  289.             // Separators
  290.             groups[1] = (char)((int)UnicodeCategory.LineSeparator + 1);
  291.             _definedCategories["Zl"] = groups[1].ToString();
  292.             // Line
  293.             groups[2] = (char)((int)UnicodeCategory.ParagraphSeparator + 1);
  294.             _definedCategories["Zp"] = groups[2].ToString();
  295.             // Paragraph
  296.             groups[3] = (char)((int)UnicodeCategory.SpaceSeparator + 1);
  297.             _definedCategories["Zs"] = groups[3].ToString();
  298.             // Space
  299.             groups[4] = GroupChar;
  300.             _definedCategories["Z"] = new string(groups, 0, 5);
  301.            
  302.            
  303.             word.Append(GroupChar);
  304.             Word = word.ToString();
  305.             NotWord = NegateCategory(Word);
  306.            
  307.            
  308.             SpaceClass = "\0\0\u1" + Space;
  309.             NotSpaceClass = "\u1\0\u1" + Space;
  310.             WordClass = "\0\0" + (char)Word.Length + Word;
  311.             NotWordClass = "\u1\0" + (char)Word.Length + Word;
  312.             ;
  313.             DigitClass = "\0\0\u1" + (char)((int)UnicodeCategory.DecimalDigitNumber + 1);
  314.             NotDigitClass = "\0\0\u1" + unchecked((char)(-((int)UnicodeCategory.DecimalDigitNumber + 1)));
  315.            
  316.             #if DBG
  317.             // make sure the _propTable is correctly ordered
  318.             int len = _propTable.GetLength(0);
  319.             for (int i = 0; i < len - 1; i++)
  320.                 Debug.Assert(String.Compare(_propTable[i, 0], _propTable[i + 1, 0], StringComparison.Ordinal) < 0, "RegexCharClass _propTable is out of order at (" + _propTable[i, 0] + ", " + _propTable[i + 1, 0] + ")");
  321.             #endif
  322.         }
  323.        
  324. /*
  325.         * RegexCharClass()
  326.         *
  327.         * Creates an empty character class.
  328.         */       
  329.         internal RegexCharClass()
  330.         {
  331.             _rangelist = new ArrayList(6);
  332.             _canonical = true;
  333.             _categories = new StringBuilder();
  334.            
  335.         }
  336.        
  337.         private RegexCharClass(bool negate, ArrayList ranges, StringBuilder categories, RegexCharClass subtraction)
  338.         {
  339.             _rangelist = ranges;
  340.             _categories = categories;
  341.             _canonical = true;
  342.             _negate = negate;
  343.             _subtractor = subtraction;
  344.         }
  345.        
  346.         internal bool CanMerge {
  347.             get { return !_negate && _subtractor == null; }
  348.         }
  349.        
  350.         internal bool Negate {
  351.             set { _negate = value; }
  352.         }
  353.        
  354.         internal void AddChar(char c)
  355.         {
  356.             AddRange(c, c);
  357.         }
  358.        
  359. /*
  360.         * AddCharClass()
  361.         *
  362.         * Adds a regex char class
  363.         */       
  364.         internal void AddCharClass(RegexCharClass cc)
  365.         {
  366.             int i;
  367.            
  368.             Debug.Assert(cc.CanMerge && this.CanMerge, "Both character classes added together must be able to merge");
  369.            
  370.             if (!cc._canonical) {
  371.                 // if the new char class to add isn't canonical, we're not either.
  372.                 _canonical = false;
  373.             }
  374.             else if (_canonical && RangeCount() > 0 && cc.RangeCount() > 0 && cc.GetRangeAt(0)._first <= GetRangeAt(RangeCount() - 1)._last)
  375.                 _canonical = false;
  376.            
  377.             for (i = 0; i < cc.RangeCount(); i += 1) {
  378.                 _rangelist.Add(cc.GetRangeAt(i));
  379.             }
  380.            
  381.             _categories.Append(cc._categories.ToString());
  382.         }
  383.        
  384. /*
  385.         * AddSet()
  386.         *
  387.         * Adds a set (specified by its string represenation) to the class.
  388.         */       
  389.         private void AddSet(string set)
  390.         {
  391.             int i;
  392.            
  393.             if (_canonical && RangeCount() > 0 && set.Length > 0 && set[0] <= GetRangeAt(RangeCount() - 1)._last)
  394.                 _canonical = false;
  395.            
  396.             for (i = 0; i < set.Length - 1; i += 2) {
  397.                 _rangelist.Add(new SingleRange(set[i], (char)(set[i + 1] - 1)));
  398.             }
  399.            
  400.             if (i < set.Length) {
  401.                 _rangelist.Add(new SingleRange(set[i], Lastchar));
  402.             }
  403.         }
  404.        
  405.         internal void AddSubtraction(RegexCharClass sub)
  406.         {
  407.             Debug.Assert(_subtractor == null, "Can't add two subtractions to a char class. ");
  408.             _subtractor = sub;
  409.         }
  410.        
  411. /*
  412.         * AddRange()
  413.         *
  414.         * Adds a single range of characters to the class.
  415.         */       
  416.         internal void AddRange(char first, char last)
  417.         {
  418.             _rangelist.Add(new SingleRange(first, last));
  419.             if (_canonical && _rangelist.Count > 0 && first <= ((SingleRange)_rangelist[_rangelist.Count - 1])._last) {
  420.                 _canonical = false;
  421.             }
  422.         }
  423.        
  424.         internal void AddCategoryFromName(string categoryName, bool invert, bool caseInsensitive, string pattern)
  425.         {
  426.            
  427.             object cat = _definedCategories[categoryName];
  428.             if (cat != null) {
  429.                 string catstr = (string)cat;
  430.                
  431.                 if (caseInsensitive) {
  432.                     if (categoryName.Equals("Lu") || categoryName.Equals("Lt"))
  433.                         /*catstr +*/                        catstr = (string)_definedCategories["Ll"];
  434.                 }
  435.                
  436.                 if (invert)
  437.                     catstr = NegateCategory(catstr);
  438.                 // negate the category
  439.                 _categories.Append((string)catstr);
  440.             }
  441.             else
  442.                 AddSet(SetFromProperty(categoryName, invert, pattern));
  443.         }
  444.        
  445.         private void AddCategory(string category)
  446.         {
  447.             _categories.Append(category);
  448.         }
  449.        
  450. /*
  451.         * AddLowerCase()
  452.         *
  453.         * Adds to the class any lowercase versions of characters already
  454.         * in the class. Used for case-insensitivity.
  455.         */       
  456.         internal void AddLowercase(CultureInfo culture)
  457.         {
  458.             int i;
  459.             int origSize;
  460.             SingleRange range;
  461.            
  462.             _canonical = false;
  463.            
  464.             for (i = 0,origSize = _rangelist.Count; i < origSize; i++) {
  465.                 range = (SingleRange)_rangelist[i];
  466.                 if (range._first == range._last)
  467.                     range._first = range._last = Char.ToLower(range._first, culture);
  468.                 else
  469.                     AddLowercaseRange(range._first, range._last, culture);
  470.             }
  471.         }
  472.        
  473. /*
  474.         * AddLowercaseRange()
  475.         *
  476.         * For a single range that's in the set, adds any additional ranges
  477.         * necessary to ensure that lowercase equivalents are also included.
  478.         */       
  479.         private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
  480.         {
  481.             int i;
  482.             int iMax;
  483.             int iMid;
  484.             char chMinT;
  485.             char chMaxT;
  486.             LowerCaseMapping lc;
  487.            
  488.             for (i = 0,iMax = _lcTable.Length; i < iMax;) {
  489.                 iMid = (i + iMax) / 2;
  490.                 if (_lcTable[iMid]._chMax < chMin)
  491.                     i = iMid + 1;
  492.                 else
  493.                     iMax = iMid;
  494.             }
  495.            
  496.             if (i >= _lcTable.Length)
  497.                 return;
  498.            
  499.             for (; i < _lcTable.Length && (lc = _lcTable[i])._chMin <= chMax; i++) {
  500.                 if ((chMinT = lc._chMin) < chMin)
  501.                     chMinT = chMin;
  502.                
  503.                 if ((chMaxT = lc._chMax) > chMax)
  504.                     chMaxT = chMax;
  505.                
  506.                 switch (lc._lcOp) {
  507.                     case LowercaseSet:
  508.                         chMinT = (char)lc._data;
  509.                         chMaxT = (char)lc._data;
  510.                         break;
  511.                     case LowercaseAdd:
  512.                         chMinT += (char)lc._data;
  513.                         chMaxT += (char)lc._data;
  514.                         break;
  515.                     case LowercaseBor:
  516.                         chMinT |= (char)1;
  517.                         chMaxT |= (char)1;
  518.                         break;
  519.                     case LowercaseBad:
  520.                         chMinT += (char)(chMinT & 1);
  521.                         chMaxT += (char)(chMaxT & 1);
  522.                         break;
  523.                 }
  524.                
  525.                 if (chMinT < chMin || chMaxT > chMax)
  526.                     AddRange(chMinT, chMaxT);
  527.             }
  528.         }
  529.        
  530.         internal void AddWord(bool ecma, bool negate)
  531.         {
  532.             if (negate) {
  533.                 if (ecma)
  534.                     AddSet(RegexCharClass.NotECMAWordSet);
  535.                 else
  536.                     AddCategory(RegexCharClass.NotWord);
  537.             }
  538.             else {
  539.                 if (ecma)
  540.                     AddSet(RegexCharClass.ECMAWordSet);
  541.                 else
  542.                     AddCategory(RegexCharClass.Word);
  543.             }
  544.         }
  545.        
  546.         internal void AddSpace(bool ecma, bool negate)
  547.         {
  548.             if (negate) {
  549.                 if (ecma)
  550.                     AddSet(RegexCharClass.NotECMASpaceSet);
  551.                 else
  552.                     AddCategory(RegexCharClass.NotSpace);
  553.             }
  554.             else {
  555.                 if (ecma)
  556.                     AddSet(RegexCharClass.ECMASpaceSet);
  557.                 else
  558.                     AddCategory(RegexCharClass.Space);
  559.             }
  560.         }
  561.        
  562.         internal void AddDigit(bool ecma, bool negate, string pattern)
  563.         {
  564.             if (ecma) {
  565.                 if (negate)
  566.                     AddSet(RegexCharClass.NotECMADigitSet);
  567.                 else
  568.                     AddSet(RegexCharClass.ECMADigitSet);
  569.             }
  570.             else
  571.                 AddCategoryFromName("Nd", negate, false, pattern);
  572.         }
  573.        
  574.         static internal string ConvertOldStringsToClass(string set, string category)
  575.         {
  576.             StringBuilder sb = new StringBuilder(set.Length + category.Length + 3);
  577.            
  578.             if (set.Length >= 2 && set[0] == '\0' && set[1] == '\0') {
  579.                 sb.Append((char)1);
  580.                 sb.Append((char)(set.Length - 2));
  581.                 sb.Append((char)category.Length);
  582.                 sb.Append(set.Substring(2));
  583.             }
  584.             else {
  585.                 sb.Append((char)0);
  586.                 sb.Append((char)set.Length);
  587.                 sb.Append((char)category.Length);
  588.                 sb.Append(set);
  589.             }
  590.             sb.Append(category);
  591.            
  592.             return sb.ToString();
  593.         }
  594.        
  595. /*
  596.         * SingletonChar()
  597.         *
  598.         * Returns the char
  599.         */       
  600.         static internal char SingletonChar(string set)
  601.         {
  602.             Debug.Assert(IsSingleton(set) || IsSingletonInverse(set), "Tried to get the singleton char out of a non singleton character class");
  603.             return set[SETSTART];
  604.         }
  605.        
  606.         static internal bool IsMergeable(string charClass)
  607.         {
  608.             return (!IsNegated(charClass) && !IsSubtraction(charClass));
  609.         }
  610.        
  611.         static internal bool IsEmpty(string charClass)
  612.         {
  613.             if (charClass[CATEGORYLENGTH] == 0 && charClass[FLAGS] == 0 && charClass[SETLENGTH] == 0 && !IsSubtraction(charClass))
  614.                 return true;
  615.             else
  616.                 return false;
  617.         }
  618.        
  619. /*
  620.         * IsSingleton()
  621.         *
  622.         * True if the set contains a single character only
  623.         */       
  624.         static internal bool IsSingleton(string set)
  625.         {
  626.             if (set[FLAGS] == 0 && set[CATEGORYLENGTH] == 0 && set[SETLENGTH] == 2 && !IsSubtraction(set) && (set[SETSTART] == Lastchar || set[SETSTART] + 1 == set[SETSTART + 1]))
  627.                 return true;
  628.             else
  629.                 return false;
  630.         }
  631.        
  632.         static internal bool IsSingletonInverse(string set)
  633.         {
  634.             if (set[FLAGS] == 1 && set[CATEGORYLENGTH] == 0 && set[SETLENGTH] == 2 && !IsSubtraction(set) && (set[SETSTART] == Lastchar || set[SETSTART] + 1 == set[SETSTART + 1]))
  635.                 return true;
  636.             else
  637.                 return false;
  638.         }
  639.        
  640.         private static bool IsSubtraction(string charClass)
  641.         {
  642.             return (charClass.Length > SETSTART + charClass[SETLENGTH] + charClass[CATEGORYLENGTH]);
  643.         }
  644.        
  645.         static internal bool IsNegated(string set)
  646.         {
  647.             return (set != null && set[FLAGS] == 1);
  648.         }
  649.        
  650.         static internal bool IsECMAWordChar(char ch)
  651.         {
  652.             return CharInClass(ch, ECMAWordClass);
  653.         }
  654.        
  655.         static internal bool IsWordChar(char ch)
  656.         {
  657.             return CharInClass(ch, WordClass);
  658.         }
  659.        
  660.         static internal bool CharInClass(char ch, string set)
  661.         {
  662.             return CharInClassRecursive(ch, set, 0);
  663.         }
  664.        
  665.        
  666.         static internal bool CharInClassRecursive(char ch, string set, int start)
  667.         {
  668.             int mySetLength = set[start + SETLENGTH];
  669.             int myCategoryLength = set[start + CATEGORYLENGTH];
  670.             int myEndPosition = start + SETSTART + mySetLength + myCategoryLength;
  671.            
  672.             bool subtracted = false;
  673.            
  674.             if (set.Length > myEndPosition) {
  675.                 subtracted = CharInClassRecursive(ch, set, myEndPosition);
  676.             }
  677.            
  678.             bool b = CharInClassInternal(ch, set, start, mySetLength, myCategoryLength);
  679.            
  680.             // Note that we apply the negation *before* performing the subtraction. This is because
  681.             // the negation only applies to the first char class, not the entire subtraction.
  682.             if (set[start + FLAGS] == 1)
  683.                 b = !b;
  684.            
  685.             return b && !subtracted;
  686.         }
  687.        
  688. /*
  689.         * CharInClass()
  690.         *
  691.         * Determines a character's membership in a character class (via the
  692.         * string representation of the class).
  693.         */       
  694.         private static bool CharInClassInternal(char ch, string set, int start, int mySetLength, int myCategoryLength)
  695.         {
  696.             int min;
  697.             int max;
  698.             int mid;
  699.             min = start + SETSTART;
  700.             max = min + mySetLength;
  701.            
  702.             while (min != max) {
  703.                 mid = (min + max) / 2;
  704.                 if (ch < set[mid])
  705.                     max = mid;
  706.                 else
  707.                     min = mid + 1;
  708.             }
  709.            
  710.             // The starting position of the set within the character class determines
  711.             // whether what an odd or even ending position means. If the start is odd,
  712.             // an *even* ending position means the character was in the set. With recursive
  713.             // subtractions in the mix, the starting position = start+SETSTART. Since we know that
  714.             // SETSTART is odd, we can simplify it out of the equation. But if it changes we need to
  715.             // reverse this check.
  716.             Debug.Assert((SETSTART & 1) == 1, "If SETSTART is not odd, the calculation below this will be reversed");
  717.             if ((min & 1) == (start & 1))
  718.                 return true;
  719.             else {
  720.                 if (myCategoryLength == 0)
  721.                     return false;
  722.                
  723.                 return CharInCategory(ch, set, start, mySetLength, myCategoryLength);
  724.             }
  725.         }
  726.        
  727.         private static bool CharInCategory(char ch, string set, int start, int mySetLength, int myCategoryLength)
  728.         {
  729.             UnicodeCategory chcategory = char.GetUnicodeCategory(ch);
  730.            
  731.             int i = start + SETSTART + mySetLength;
  732.             int end = i + myCategoryLength;
  733.             while (i < end) {
  734.                 int curcat = (short)set[i];
  735.                
  736.                 if (curcat == 0) {
  737.                     // zero is our marker for a group of categories - treated as a unit
  738.                     if (CharInCategoryGroup(ch, chcategory, set, ref i))
  739.                         return true;
  740.                 }
  741.                 else if (curcat > 0) {
  742.                     // greater than zero is a positive case
  743.                    
  744.                     if (curcat == SpaceConst) {
  745.                         if (Char.IsWhiteSpace(ch))
  746.                             return true;
  747.                         else {
  748.                             i++;
  749.                             continue;
  750.                         }
  751.                     }
  752.                     --curcat;
  753.                    
  754.                     if (chcategory == (UnicodeCategory)curcat)
  755.                         return true;
  756.                 }
  757.                 else {
  758.                     // less than zero is a negative case
  759.                     if (curcat == NotSpaceConst) {
  760.                         if (!Char.IsWhiteSpace(ch))
  761.                             return true;
  762.                         else {
  763.                             i++;
  764.                             continue;
  765.                         }
  766.                     }
  767.                    
  768.                     //curcat = -curcat;
  769.                     //--curcat;
  770.                     curcat = -1 - curcat;
  771.                    
  772.                     if (chcategory != (UnicodeCategory)curcat)
  773.                         return true;
  774.                 }
  775.                 i++;
  776.             }
  777.             return false;
  778.         }
  779.        
  780. /*
  781.         *  CharInCategoryGroup
  782.         *  This is used for categories which are composed of other categories - L, N, Z, W...
  783.         *  These groups need special treatment when they are negated
  784.         */       
  785.         private static bool CharInCategoryGroup(char ch, UnicodeCategory chcategory, string category, ref int i)
  786.         {
  787.             i++;
  788.            
  789.             int curcat = (short)category[i];
  790.             if (curcat > 0) {
  791.                 // positive case - the character must be in ANY of the categories in the group
  792.                 bool answer = false;
  793.                
  794.                 while (curcat != 0) {
  795.                     if (!answer) {
  796.                         --curcat;
  797.                         if (chcategory == (UnicodeCategory)curcat)
  798.                             answer = true;
  799.                     }
  800.                     i++;
  801.                     curcat = (short)category[i];
  802.                 }
  803.                 return answer;
  804.             }
  805.             else {
  806.                
  807.                 // negative case - the character must be in NONE of the categories in the group
  808.                 bool answer = true;
  809.                
  810.                 while (curcat != 0) {
  811.                     if (answer) {
  812.                         //curcat = -curcat;
  813.                         //--curcat;
  814.                         curcat = -1 - curcat;
  815.                         if (chcategory == (UnicodeCategory)curcat)
  816.                             answer = false;
  817.                     }
  818.                     i++;
  819.                     curcat = (short)category[i];
  820.                 }
  821.                 return answer;
  822.             }
  823.         }
  824.        
  825.         private static string NegateCategory(string category)
  826.         {
  827.             if (category == null)
  828.                 return null;
  829.            
  830.             StringBuilder sb = new StringBuilder(category.Length);
  831.            
  832.             for (int i = 0; i < category.Length; i++) {
  833.                 short ch = (short)category[i];
  834.                 sb.Append((char)-ch);
  835.             }
  836.             return sb.ToString();
  837.         }
  838.        
  839.         static internal RegexCharClass Parse(string charClass)
  840.         {
  841.             return ParseRecursive(charClass, 0);
  842.         }
  843.        
  844.         private static RegexCharClass ParseRecursive(string charClass, int start)
  845.         {
  846.             int mySetLength = charClass[start + SETLENGTH];
  847.             int myCategoryLength = charClass[start + CATEGORYLENGTH];
  848.             int myEndPosition = start + SETSTART + mySetLength + myCategoryLength;
  849.            
  850.             ArrayList ranges = new ArrayList(mySetLength);
  851.             int i = start + SETSTART;
  852.             int end = i + mySetLength;
  853.             while (i < end) {
  854.                 char first = charClass[i];
  855.                 i++;
  856.                
  857.                 char last;
  858.                 if (i < end)
  859.                     last = (char)(charClass[i] - 1);
  860.                 else
  861.                     last = Lastchar;
  862.                 i++;
  863.                 ranges.Add(new SingleRange(first, last));
  864.             }
  865.            
  866.             RegexCharClass sub = null;
  867.             if (charClass.Length > myEndPosition)
  868.                 sub = ParseRecursive(charClass, myEndPosition);
  869.            
  870.             return new RegexCharClass(charClass[start + FLAGS] == 1, ranges, new StringBuilder(charClass.Substring(end, myCategoryLength)), sub);
  871.         }
  872.        
  873. /*
  874.         * RangeCount()
  875.         *
  876.         * The number of single ranges that have been accumulated so far.
  877.         */       
  878.         private int RangeCount()
  879.         {
  880.             return _rangelist.Count;
  881.         }
  882.        
  883. /*
  884.         * ToString()
  885.         *
  886.         * Constructs the string representation of the class.
  887.         */       
  888.         internal string ToStringClass()
  889.         {
  890.             if (!_canonical)
  891.                 Canonicalize();
  892.            
  893.             // make a guess about the length of the ranges. We'll update this at the end.
  894.             // This is important because if the last range ends in LastChar, we won't append
  895.             // LastChar to the list.
  896.             int rangeLen = _rangelist.Count * 2;
  897.             StringBuilder sb = new StringBuilder(rangeLen + _categories.Length + 3);
  898.            
  899.             int flags;
  900.             if (_negate)
  901.                 flags = 1;
  902.             else
  903.                 flags = 0;
  904.            
  905.             sb.Append((char)flags);
  906.             sb.Append((char)rangeLen);
  907.             sb.Append((char)_categories.Length);
  908.            
  909.             for (int i = 0; i < _rangelist.Count; i++) {
  910.                 SingleRange currentRange = (SingleRange)_rangelist[i];
  911.                 sb.Append(currentRange._first);
  912.                
  913.                 if (currentRange._last != Lastchar)
  914.                     sb.Append((char)(currentRange._last + 1));
  915.             }
  916.            
  917.             sb[SETLENGTH] = (char)(sb.Length - SETSTART);
  918.            
  919.             sb.Append(_categories);
  920.            
  921.             if (_subtractor != null)
  922.                 sb.Append(_subtractor.ToStringClass());
  923.            
  924.             return sb.ToString();
  925.         }
  926.        
  927. /*
  928.         * GetRangeAt(int i)
  929.         *
  930.         * The ith range.
  931.         */       
  932.         private SingleRange GetRangeAt(int i)
  933.         {
  934.             return (SingleRange)_rangelist[i];
  935.         }
  936.        
  937. /*
  938.         * Canonicalize()
  939.         *
  940.         * Logic to reduce a character class to a unique, sorted form.
  941.         */       
  942.         private void Canonicalize()
  943.         {
  944.             SingleRange CurrentRange;
  945.             int i;
  946.             int j;
  947.             char last;
  948.             bool Done;
  949.            
  950.             _canonical = true;
  951.             _rangelist.Sort(0, _rangelist.Count, new SingleRangeComparer());
  952.            
  953.             //
  954.             // Find and eliminate overlapping or abutting ranges
  955.             //
  956.            
  957.             if (_rangelist.Count > 1) {
  958.                 Done = false;
  959.                
  960.                 for (i = 1,j = 0;; i++) {
  961.                     for (last = ((SingleRange)_rangelist[j])._last;; i++) {
  962.                         if (i == _rangelist.Count || last == Lastchar) {
  963.                             Done = true;
  964.                             break;
  965.                         }
  966.                        
  967.                         if ((CurrentRange = (SingleRange)_rangelist[i])._first > last + 1)
  968.                             break;
  969.                        
  970.                         if (last < CurrentRange._last)
  971.                             last = CurrentRange._last;
  972.                     }
  973.                    
  974.                     ((SingleRange)_rangelist[j])._last = last;
  975.                    
  976.                     j++;
  977.                    
  978.                     if (Done)
  979.                         break;
  980.                    
  981.                     if (j < i)
  982.                         _rangelist[j] = _rangelist[i];
  983.                 }
  984.                 _rangelist.RemoveRange(j, _rangelist.Count - j);
  985.             }
  986.         }
  987.        
  988.         private static string SetFromProperty(string capname, bool invert, string pattern)
  989.         {
  990.             int min = 0;
  991.             int max = _propTable.GetLength(0);
  992.             while (min != max) {
  993.                 int mid = (min + max) / 2;
  994.                 int res = String.Compare(capname, _propTable[mid, 0], StringComparison.Ordinal);
  995.                 if (res < 0)
  996.                     max = mid;
  997.                 else if (res > 0)
  998.                     min = mid + 1;
  999.                 else {
  1000.                     string set = _propTable[mid, 1];
  1001.                     Debug.Assert(!String.IsNullOrEmpty(set), "Found a null/empty element in RegexCharClass prop table");
  1002.                     if (invert) {
  1003.                         if (set[0] == Nullchar) {
  1004.                             return set.Substring(1);
  1005.                         }
  1006.                         return Nullchar + set;
  1007.                     }
  1008.                     else {
  1009.                         return set;
  1010.                     }
  1011.                 }
  1012.             }
  1013.             throw new ArgumentException(SR.GetString(SR.MakeException, pattern, SR.GetString(SR.UnknownProperty, capname)));
  1014.         }
  1015.        
  1016.         #if DBG
  1017.        
  1018. /*
  1019.         * SetDescription()
  1020.         *
  1021.         * Produces a human-readable description for a set string.
  1022.         */       
  1023.         static internal string SetDescription(string set)
  1024.         {
  1025.             int mySetLength = set[SETLENGTH];
  1026.             int myCategoryLength = set[CATEGORYLENGTH];
  1027.             int myEndPosition = SETSTART + mySetLength + myCategoryLength;
  1028.            
  1029.             StringBuilder desc = new StringBuilder("[");
  1030.            
  1031.             int index = SETSTART;
  1032.             char ch1;
  1033.             char ch2;
  1034.            
  1035.             if (IsNegated(set))
  1036.                 desc.Append('^');
  1037.            
  1038.             while (index < SETSTART + set[SETLENGTH]) {
  1039.                 ch1 = set[index];
  1040.                 if (index + 1 < set.Length)
  1041.                     ch2 = (char)(set[index + 1] - 1);
  1042.                 else
  1043.                     ch2 = Lastchar;
  1044.                
  1045.                 desc.Append(CharDescription(ch1));
  1046.                
  1047.                 if (ch2 != ch1) {
  1048.                     if (ch1 + 1 != ch2)
  1049.                         desc.Append('-');
  1050.                     desc.Append(CharDescription(ch2));
  1051.                 }
  1052.                 index += 2;
  1053.             }
  1054.            
  1055.             while (index < SETSTART + set[SETLENGTH] + set[CATEGORYLENGTH]) {
  1056.                 ch1 = set[index];
  1057.                 if (ch1 == 0) {
  1058.                     bool found = false;
  1059.                    
  1060.                     int lastindex = set.IndexOf(GroupChar, index + 1);
  1061.                     string group = set.Substring(index, lastindex - index + 1);
  1062.                    
  1063.                     IDictionaryEnumerator en = _definedCategories.GetEnumerator();
  1064.                     while (en.MoveNext()) {
  1065.                         if (group.Equals(en.Value)) {
  1066.                             if ((short)set[index + 1] > 0)
  1067.                                 desc.Append("\\p{" + en.Key + "}");
  1068.                             else
  1069.                                 desc.Append("\\P{" + en.Key + "}");
  1070.                            
  1071.                             found = true;
  1072.                             break;
  1073.                         }
  1074.                     }
  1075.                    
  1076.                     if (!found) {
  1077.                         if (group.Equals(Word))
  1078.                             desc.Append("\\w");
  1079.                         else if (group.Equals(NotWord))
  1080.                             desc.Append("\\W");
  1081.                         else
  1082.                             Debug.Assert(false, "Couldn't find a goup to match '" + group + "'");
  1083.                     }
  1084.                    
  1085.                     index = lastindex;
  1086.                 }
  1087.                 else {
  1088.                     desc.Append(CategoryDescription(ch1));
  1089.                 }
  1090.                
  1091.                 index++;
  1092.             }
  1093.            
  1094.             if (set.Length > myEndPosition) {
  1095.                 desc.Append('-');
  1096.                 desc.Append(SetDescription(set.Substring(myEndPosition)));
  1097.             }
  1098.            
  1099.             desc.Append(']');
  1100.            
  1101.             return desc.ToString();
  1102.         }
  1103.        
  1104.         static internal readonly char[] Hex = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  1105.         'a', 'b', 'c', 'd', 'e', 'f'};
  1106.         static internal readonly string[] Categories = new string[] {"Lu", "Ll", "Lt", "Lm", "Lo", "Mn", "Mc", "Me", "Nd", "Nl",
  1107.         "No", "Zs", "Zl", "Zp", "Cc", "Cf", "Cs", "Co", "Pc", "Pd",
  1108.         "Ps", "Pe", "Pi", "Pf", "Po", "Sm", "Sc", "Sk", "So", "Cn"
  1109.         };
  1110.        
  1111. /*
  1112.         * CharDescription()
  1113.         *
  1114.         * Produces a human-readable description for a single character.
  1115.         */       
  1116.         static internal string CharDescription(char ch)
  1117.         {
  1118.             StringBuilder sb = new StringBuilder();
  1119.             int shift;
  1120.            
  1121.             if (ch == '\\')
  1122.                 return "\\\\";
  1123.            
  1124.             if (ch >= ' ' && ch <= '~') {
  1125.                 sb.Append(ch);
  1126.                 return sb.ToString();
  1127.             }
  1128.            
  1129.             if (ch < 256) {
  1130.                 sb.Append("\\x");
  1131.                 shift = 8;
  1132.             }
  1133.             else {
  1134.                 sb.Append("\\u");
  1135.                 shift = 16;
  1136.             }
  1137.            
  1138.             while (shift > 0) {
  1139.                 shift -= 4;
  1140.                 sb.Append(Hex[(ch >> shift) & 15]);
  1141.             }
  1142.            
  1143.             return sb.ToString();
  1144.         }
  1145.        
  1146.         private static string CategoryDescription(char ch)
  1147.         {
  1148.             if (ch == SpaceConst)
  1149.                 return "\\s";
  1150.             else if ((short)ch == NotSpaceConst)
  1151.                 return "\\S";
  1152.             else if ((short)ch < 0) {
  1153.                 return "\\P{" + Categories[(-((short)ch) - 1)] + "}";
  1154.             }
  1155.             else {
  1156.                 return "\\p{" + Categories[(ch - 1)] + "}";
  1157.             }
  1158.         }
  1159.        
  1160.         #endif
  1161.        
  1162.         // Lower case mapping descriptor.
  1163.         private struct LowerCaseMapping
  1164.         {
  1165.             internal LowerCaseMapping(char chMin, char chMax, int lcOp, int data)
  1166.             {
  1167.                 _chMin = chMin;
  1168.                 _chMax = chMax;
  1169.                 _lcOp = lcOp;
  1170.                 _data = data;
  1171.             }
  1172.            
  1173.             internal char _chMin;
  1174.             internal char _chMax;
  1175.             internal int _lcOp;
  1176.             internal int _data;
  1177.         }
  1178.        
  1179. /*
  1180.         * SingleRangeComparer
  1181.         *
  1182.         * For sorting ranges; compare based on the first char in the range.
  1183.         */       
  1184.         private sealed class SingleRangeComparer : IComparer
  1185.         {
  1186.             public int Compare(object x, object y)
  1187.             {
  1188.                 return (((SingleRange)x)._first < ((SingleRange)y)._first ? -1 : (((SingleRange)x)._first > ((SingleRange)y)._first ? 1 : 0));
  1189.             }
  1190.         }
  1191.        
  1192. /*
  1193.         * SingleRange
  1194.         *
  1195.         * A first/last pair representing a single range of characters.
  1196.         */       
  1197.         private sealed class SingleRange
  1198.         {
  1199.             internal SingleRange(char first, char last)
  1200.             {
  1201.                 _first = first;
  1202.                 _last = last;
  1203.             }
  1204.            
  1205.             internal char _first;
  1206.             internal char _last;
  1207.         }
  1208.     }
  1209.    
  1210. }

Developer Fusion