Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / Entity / Design / PluralizationService / EnglishPluralizationService.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EnglishPluralizationService.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections.Generic;
11 using System.Linq;
12 using System.Text;
13 using System.Globalization;
14 using System.Data.Entity.Design.Common;
15 using System.Text.RegularExpressions;
16
17 namespace System.Data.Entity.Design.PluralizationServices
18 {
19     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pluralization")]
20     internal class EnglishPluralizationService : PluralizationService, ICustomPluralizationMapping
21     {
22         private BidirectionalDictionary<string, string> _userDictionary;
23         private StringBidirectionalDictionary _irregularPluralsPluralizationService;
24         private StringBidirectionalDictionary _assimilatedClassicalInflectionPluralizationService;
25         private StringBidirectionalDictionary _oSuffixPluralizationService;
26         private StringBidirectionalDictionary _classicalInflectionPluralizationService;
27         private StringBidirectionalDictionary _irregularVerbPluralizationService;
28         private StringBidirectionalDictionary _wordsEndingWithSePluralizationService;
29         private StringBidirectionalDictionary _wordsEndingWithSisPluralizationService;
30         private StringBidirectionalDictionary _wordsEndingWithSusPluralizationService;
31         private StringBidirectionalDictionary _wordsEndingWithInxAnxYnxPluralizationService;
32
33         private List<string> _knownSingluarWords;
34         private List<string> _knownPluralWords;
35
36         private string[] _uninflectiveSuffixList =
37             new string[] { "fish", "ois", "sheep", "deer", "pos", "itis", "ism" };
38
39         private string[] _uninflectiveWordList =
40             new string[] { 
41                 "bison", "flounder", "pliers", "bream", "gallows", "proceedings", 
42                 "breeches", "graffiti", "rabies", "britches", "headquarters", "salmon", 
43                 "carp", "----", "scissors", "ch----is", "high-jinks", "sea-bass", 
44                 "clippers", "homework", "series", "cod", "innings", "shears", "contretemps", 
45                 "jackanapes", "species", "corps", "mackerel", "swine", "debris", "measles", 
46                 "trout", "diabetes", "mews", "tuna", "djinn", "mumps", "whiting", "eland", 
47                 "news", "wildebeest", "elk", "pincers", "police", "hair", "ice", "chaos",
48                 "milk", "cotton", "pneumonoultramicroscopicsilicovolcanoconiosis",
49                 "information", "aircraft", "scabies", "traffic", "corn", "millet", "rice", 
50                                 "hay", "----", "tobacco", "cabbage", "okra", "broccoli", "asparagus", 
51                                 "lettuce", "beef", "pork", "venison", "mutton",  "cattle", "offspring", 
52                                 "molasses", "shambles", "shingles"};
53
54         private Dictionary<string, string> _irregularVerbList =
55             new Dictionary<string, string>()
56             {
57                 {"am", "are"}, {"are", "are"}, {"is", "are"}, {"was", "were"}, {"were", "were"}, 
58                 {"has", "have"}, {"have", "have"}
59             };
60
61         private List<string> _pronounList =
62             new List<string>()
63                 {
64                     "I", "we", "you", "he", "she", "they", "it",
65                     "me", "us", "him", "her", "them", 
66                     "myself", "ourselves", "yourself", "himself", "herself", "itself",
67                     "oneself", "oneselves", 
68                     "my", "our", "your", "his", "their", "its",
69                     "mine", "yours", "hers", "theirs", "this", "that", "these", "those", 
70                     "all", "another", "any", "anybody", "anyone", "anything", "both", "each", 
71                     "other", "either", "everyone", "everybody", "everything", "most", "much", "nothing",
72                     "nobody", "none", "one", "others", "some", "somebody", "someone", "something",
73                     "what", "whatever", "which", "whichever", "who", "whoever", "whom", "whomever",
74                     "whose", 
75                 };
76
77         private Dictionary<string, string> _irregularPluralsDictionary =
78             new Dictionary<string, string>()
79                 {
80                     {"brother", "brothers"}, {"child", "children"},
81                     {"cow", "cows"}, {"ephemeris", "ephemerides"}, {"genie", "genies"}, 
82                     {"money", "moneys"}, {"mongoose", "mongooses"}, {"mythos", "mythoi"}, 
83                     {"octopus", "octopuses"}, {"ox", "oxen"}, {"soliloquy", "soliloquies"}, 
84                     {"trilby", "trilbys"}, {"crisis", "crises"}, {"synopsis","synopses"},  
85                     {"rose", "roses"}, {"gas","gases"}, {"bus", "buses"},
86                     {"axis", "axes"},{"memo", "memos"}, {"casino","casinos"},
87                     {"silo", "silos"},{"stereo", "stereos"}, {"studio","studios"},
88                     {"lens", "lenses"}, {"alias","aliases"},
89                     {"pie","pies"}, {"corpus","corpora"},
90                     {"viscus", "viscera"},{"hippopotamus", "hippopotami"}, {"trace", "traces"},
91                     {"person", "people"}, {"chili", "chilies"}, {"analysis", "analyses"}, 
92                     {"basis", "bases"}, {"neurosis", "neuroses"}, {"oasis", "oases"}, 
93                     {"synthesis", "syntheses"}, {"thesis", "theses"}, {"change", "changes"}, 
94                     {"lie", "lies"}, {"calorie", "calories"}, {"freebie", "freebies"}, {"case", "cases"},
95                     {"house", "houses"}, {"valve", "valves"}, {"cloth", "clothes"}, {"tie", "ties"}, 
96                     {"movie", "movies"}, {"bonus", "bonuses"}, {"specimen", "specimens"}
97                 };
98
99         Dictionary<string, string> _assimilatedClassicalInflectionDictionary =
100                     new Dictionary<string, string>()
101                     {
102                         {"alumna", "alumnae"}, {"alga", "algae"}, {"vertebra", "vertebrae"}, 
103                         {"codex", "codices"}, 
104                         {"murex", "murices"}, {"silex", "silices"}, {"aphelion", "aphelia"}, 
105                         {"hyperbaton", "hyperbata"}, {"perihelion", "perihelia"}, 
106                         {"asyndeton", "asyndeta"}, {"noumenon", "noumena"}, 
107                         {"phenomenon", "phenomena"}, {"criterion", "criteria"}, {"organon", "organa"}, 
108                         {"prolegomenon", "prolegomena"}, {"agendum", "agenda"}, {"datum", "data"}, 
109                         {"extremum", "extrema"}, {"bacterium", "bacteria"}, {"desideratum", "desiderata"}, 
110                         {"stratum", "strata"}, {"candelabrum", "candelabra"}, {"erratum", "errata"}, 
111                         {"ovum", "ova"}, {"forum", "fora"}, {"addendum", "addenda"},  {"stadium", "stadia"}, 
112                         {"automaton", "automata"}, {"polyhedron", "polyhedra"}, 
113                     };
114
115         Dictionary<string, string> _oSuffixDictionary =
116                     new Dictionary<string, string>()
117                     {
118                         {"albino", "albinos"}, {"generalissimo", "generalissimos"}, 
119                         {"manifesto", "manifestos"}, {"archipelago", "archipelagos"}, 
120                         {"ghetto", "ghettos"}, {"medico", "medicos"}, {"armadillo", "armadillos"}, 
121                         {"guano", "guanos"}, {"octavo", "octavos"}, {"commando", "commandos"}, 
122                         {"inferno", "infernos"}, {"photo", "photos"}, {"ditto", "dittos"}, 
123                         {"jumbo", "jumbos"}, {"pro", "pros"}, {"dynamo", "dynamos"}, 
124                         {"lingo", "lingos"}, {"quarto", "quartos"}, {"embryo", "embryos"}, 
125                         {"lumbago", "lumbagos"}, {"rhino", "rhinos"}, {"fiasco", "fiascos"}, 
126                         {"magneto", "magnetos"}, {"stylo", "stylos"}
127                     };
128
129         Dictionary<string, string> _classicalInflectionDictionary =
130                     new Dictionary<string, string>()
131                     {
132                         {"stamen", "stamina"}, {"foramen", "foramina"}, {"lumen", "lumina"}, 
133                         {"anathema", "anathemata"}, {"----", "----ta"}, {"oedema", "oedemata"}, 
134                         {"bema", "bemata"}, {"enigma", "enigmata"}, {"sarcoma", "sarcomata"}, 
135                         {"carcinoma", "carcinomata"}, {"gumma", "gummata"}, {"schema", "schemata"}, 
136                         {"charisma", "charismata"}, {"lemma", "lemmata"}, {"soma", "somata"}, 
137                         {"diploma", "diplomata"}, {"lymphoma", "lymphomata"}, {"stigma", "stigmata"}, 
138                         {"dogma", "dogmata"}, {"magma", "magmata"}, {"stoma", "stomata"}, 
139                         {"drama", "dramata"}, {"melisma", "melismata"}, {"trauma", "traumata"}, 
140                         {"edema", "edemata"}, {"miasma", "miasmata"}, {"abscissa", "abscissae"}, 
141                         {"formula", "formulae"}, {"medusa", "medusae"}, {"amoeba", "amoebae"}, 
142                         {"hydra", "hydrae"}, {"nebula", "nebulae"}, {"antenna", "antennae"}, 
143                         {"hyperbola", "hyperbolae"}, {"nova", "novae"}, {"aurora", "aurorae"}, 
144                         {"lacuna", "lacunae"}, {"parabola", "parabolae"}, {"apex", "apices"}, 
145                         {"latex", "latices"}, {"vertex", "vertices"}, {"cortex", "cortices"}, 
146                         {"pontifex", "pontifices"}, {"vortex", "vortices"}, {"index", "indices"}, 
147                         {"simplex", "simplices"}, {"iris", "irides"}, {"----oris", "----orides"}, 
148                         {"alto", "alti"}, {"contralto", "contralti"}, {"soprano", "soprani"}, 
149                         {"b----o", "b----i"}, {"crescendo", "crescendi"}, {"tempo", "tempi"}, 
150                         {"canto", "canti"}, {"solo", "soli"}, {"aquarium", "aquaria"}, 
151                         {"interregnum", "interregna"}, {"quantum", "quanta"}, 
152                         {"compendium", "compendia"}, {"lustrum", "lustra"}, {"rostrum", "rostra"}, 
153                         {"consortium", "consortia"}, {"maximum", "maxima"}, {"spectrum", "spectra"}, 
154                         {"cranium", "crania"}, {"medium", "media"}, {"speculum", "specula"}, 
155                         {"curriculum", "curricula"}, {"memorandum", "memoranda"}, {"stadium", "stadia"}, 
156                         {"dictum", "dicta"}, {"millenium", "millenia"}, {"t----zium", "t----zia"}, 
157                         {"emporium", "emporia"}, {"minimum", "minima"}, {"ultimatum", "ultimata"}, 
158                         {"enconium", "enconia"}, {"momentum", "momenta"}, {"vacuum", "vacua"}, 
159                         {"gymnasium", "gymnasia"}, {"optimum", "optima"}, {"velum", "vela"}, 
160                         {"honorarium", "honoraria"}, {"phylum", "phyla"}, {"focus", "foci"}, 
161                         {"nimbus", "nimbi"}, {"succubus", "succubi"}, {"fungus", "fungi"}, 
162                         {"nucleolus", "nucleoli"}, {"torus", "tori"}, {"genius", "genii"}, 
163                         {"radius", "radii"}, {"umbilicus", "umbilici"}, {"incubus", "incubi"}, 
164                         {"stylus", "styli"}, {"uterus", "uteri"}, {"stimulus", "stimuli"}, {"apparatus", "apparatus"}, 
165                         {"impetus", "impetus"}, {"prospectus", "prospectus"}, {"cantus", "cantus"}, 
166                         {"nexus", "nexus"}, {"sinus", "sinus"}, {"coitus", "coitus"}, {"plexus", "plexus"}, 
167                         {"status", "status"}, {"hiatus", "hiatus"}, {"afreet", "afreeti"}, 
168                         {"afrit", "afriti"}, {"efreet", "efreeti"}, {"cherub", "cherubim"}, 
169                         {"goy", "goyim"}, {"seraph", "seraphim"}, {"alumnus", "alumni"}
170                     };
171
172         // this list contains all the plural words that being treated as singluar form, for example, "they" -> "they"
173         private List<string> _knownConflictingPluralList =
174             new List<string>()
175             {
176                 "they", "them", "their", "have", "were", "yourself", "are"
177             };
178
179         // this list contains the words ending with "se" and we special case these words since 
180         // we need to add a rule for "ses" singularize to "s"
181         private Dictionary<string, string> _wordsEndingWithSeDictionary =
182             new Dictionary<string, string>()
183             {
184                 {"house", "houses"}, {"case", "cases"}, {"enterprise", "enterprises"}, 
185                 {"purchase", "purchases"}, {"surprise", "surprises"}, {"release", "releases"}, 
186                 {"disease", "diseases"}, {"promise", "promises"}, {"refuse", "refuses"}, 
187                 {"whose", "whoses"}, {"phase", "phases"}, {"noise", "noises"}, 
188                 {"nurse", "nurses"}, {"rose", "roses"}, {"franchise", "franchises"}, 
189                 {"supervise", "supervises"}, {"farmhouse", "farmhouses"}, 
190                 {"suitcase", "suitcases"}, {"recourse", "recourses"}, {"impulse", "impulses"}, 
191                 {"license", "licenses"}, {"diocese", "dioceses"}, {"excise", "excises"}, 
192                 {"demise", "demises"}, {"blouse", "blouses"}, 
193                 {"bruise", "bruises"}, {"misuse", "misuses"}, {"curse", "curses"}, 
194                 {"prose", "proses"}, {"purse", "purses"}, {"goose", "gooses"}, 
195                 {"tease", "teases"}, {"poise", "poises"}, {"vase", "vases"}, 
196                 {"fuse", "fuses"}, {"muse", "muses"}, 
197                 {"slaughterhouse", "slaughterhouses"}, {"clearinghouse", "clearinghouses"}, 
198                 {"endonuclease", "endonucleases"}, {"steeplechase", "steeplechases"}, 
199                 {"metamorphose", "metamorphoses"}, {"----", "----s"}, 
200                 {"commonsense", "commonsenses"}, {"intersperse", "intersperses"}, 
201                 {"merchandise", "merchandises"}, {"phosphatase", "phosphatases"}, 
202                 {"summerhouse", "summerhouses"}, {"watercourse", "watercourses"}, 
203                 {"catchphrase", "catchphrases"}, {"compromise", "compromises"}, 
204                 {"greenhouse", "greenhouses"}, {"lighthouse", "lighthouses"}, 
205                 {"paraphrase", "paraphrases"}, {"mayonnaise", "mayonnaises"}, 
206                 {"----course", "----courses"}, {"apocalypse", "apocalypses"}, 
207                 {"courthouse", "courthouses"}, {"powerhouse", "powerhouses"}, 
208                 {"storehouse", "storehouses"}, {"glasshouse", "glasshouses"}, 
209                 {"hypotenuse", "hypotenuses"}, {"peroxidase", "peroxidases"}, 
210                 {"pillowcase", "pillowcases"}, {"roundhouse", "roundhouses"},
211                 {"streetwise", "streetwises"}, {"expertise", "expertises"}, 
212                 {"discourse", "discourses"}, {"warehouse", "warehouses"}, 
213                 {"staircase", "staircases"}, {"workhouse", "workhouses"}, 
214                 {"briefcase", "briefcases"}, {"clubhouse", "clubhouses"}, 
215                 {"clockwise", "clockwises"}, {"concourse", "concourses"}, 
216                 {"playhouse", "playhouses"}, {"turquoise", "turquoises"}, 
217                 {"boathouse", "boathouses"}, {"cellulose", "celluloses"}, 
218                 {"epitomise", "epitomises"}, {"gatehouse", "gatehouses"}, 
219                 {"grandiose", "grandioses"}, {"menopause", "menopauses"}, 
220                 {"penthouse", "penthouses"}, {"----horse", "----horses"}, 
221                 {"transpose", "transposes"}, {"almshouse", "almshouses"}, 
222                 {"customise", "customises"}, {"footloose", "footlooses"}, 
223                 {"galvanise", "galvanises"}, {"princesse", "princesses"}, 
224                 {"universe", "universes"}, {"workhorse", "workhorses"}
225             };
226
227         private Dictionary<string, string> _wordsEndingWithSisDictionary =
228             new Dictionary<string, string>()
229             {
230                 {"analysis", "analyses"}, {"crisis", "crises"}, {"basis", "bases"}, 
231                 {"atherosclerosis", "atheroscleroses"}, {"electrophoresis", "electrophoreses"}, 
232                 {"psychoanalysis", "psychoanalyses"}, {"photosynthesis", "photosyntheses"}, 
233                 {"amniocentesis", "amniocenteses"}, {"metamorphosis", "metamorphoses"}, 
234                 {"toxoplasmosis", "toxoplasmoses"}, {"endometriosis", "endometrioses"}, 
235                 {"tuberculosis", "tuberculoses"}, {"pathogenesis", "pathogeneses"}, 
236                 {"osteoporosis", "osteoporoses"}, {"parenthesis", "parentheses"}, 
237                 {"anastomosis", "anastomoses"}, {"peristalsis", "peristalses"}, 
238                 {"hypothesis", "hypotheses"}, {"antithesis", "antitheses"}, 
239                 {"apotheosis", "apotheoses"}, {"thrombosis", "thromboses"}, 
240                 {"diagnosis", "diagnoses"}, {"synthesis", "syntheses"}, 
241                 {"paralysis", "paralyses"}, {"prognosis", "prognoses"}, 
242                 {"cirrhosis", "cirrhoses"}, {"sclerosis", "scleroses"}, 
243                 {"psychosis", "psychoses"}, {"apoptosis", "apoptoses"}, {"symbiosis", "symbioses"}
244             };
245
246         private Dictionary<string, string> _wordsEndingWithSusDictionary =
247             new Dictionary<string, string>()
248             {
249                 {"consensus","consensuses"}, {"census", "censuses"}
250             };
251
252         private Dictionary<string, string> _wordsEndingWithInxAnxYnxDictionary =
253             new Dictionary<string, string>()
254             {
255                 {"sphinx", "sphinxes"},
256                 {"larynx", "larynges"}, {"lynx", "lynxes"}, {"pharynx", "pharynxes"},
257                 {"phalanx", "phalanxes"}
258             };
259
260         internal EnglishPluralizationService()
261         {
262             this.Culture = new CultureInfo("en");
263
264             this._userDictionary = new BidirectionalDictionary<string, string>();
265
266             this._irregularPluralsPluralizationService =
267                 new StringBidirectionalDictionary(this._irregularPluralsDictionary);
268             this._assimilatedClassicalInflectionPluralizationService =
269                 new StringBidirectionalDictionary(this._assimilatedClassicalInflectionDictionary);
270             this._oSuffixPluralizationService =
271                 new StringBidirectionalDictionary(this._oSuffixDictionary);
272             this._classicalInflectionPluralizationService =
273                 new StringBidirectionalDictionary(this._classicalInflectionDictionary);
274             this._wordsEndingWithSePluralizationService =
275                 new StringBidirectionalDictionary(this._wordsEndingWithSeDictionary);
276             this._wordsEndingWithSisPluralizationService =
277                 new StringBidirectionalDictionary(this._wordsEndingWithSisDictionary);
278             this._wordsEndingWithSusPluralizationService =
279                 new StringBidirectionalDictionary(this._wordsEndingWithSusDictionary);
280             this._wordsEndingWithInxAnxYnxPluralizationService =
281                 new StringBidirectionalDictionary(this._wordsEndingWithInxAnxYnxDictionary);
282
283             // verb
284             this._irregularVerbPluralizationService =
285                 new StringBidirectionalDictionary(this._irregularVerbList);
286
287             this._knownSingluarWords = new List<string>(
288                 _irregularPluralsDictionary.Keys
289                 .Concat(_assimilatedClassicalInflectionDictionary.Keys)
290                 .Concat(_oSuffixDictionary.Keys)
291                 .Concat(_classicalInflectionDictionary.Keys)
292                 .Concat(_irregularVerbList.Keys)
293                 .Concat(_irregularPluralsDictionary.Keys)
294                 .Concat(_wordsEndingWithSeDictionary.Keys)
295                 .Concat(_wordsEndingWithSisDictionary.Keys)
296                 .Concat(_wordsEndingWithSusDictionary.Keys)
297                 .Concat(_wordsEndingWithInxAnxYnxDictionary.Keys)
298                 .Concat(_uninflectiveWordList)
299                 .Except(this._knownConflictingPluralList)); // see the _knowConflictingPluralList comment above
300
301             this._knownPluralWords = new List<string>(
302                 _irregularPluralsDictionary.Values
303                 .Concat(_assimilatedClassicalInflectionDictionary.Values)
304                 .Concat(_oSuffixDictionary.Values)
305                 .Concat(_classicalInflectionDictionary.Values)
306                 .Concat(_irregularVerbList.Values)
307                 .Concat(_irregularPluralsDictionary.Values)
308                 .Concat(_wordsEndingWithSeDictionary.Values)
309                 .Concat(_wordsEndingWithSisDictionary.Values)
310                 .Concat(_wordsEndingWithSusDictionary.Values)
311                 .Concat(_wordsEndingWithInxAnxYnxDictionary.Values)
312                 .Concat(_uninflectiveWordList));
313         }
314
315         public override bool IsPlural(string word)
316         {
317             EDesignUtil.CheckArgumentNull<string>(word, "word");
318
319             if (this._userDictionary.ExistsInSecond(word))
320             {
321                 return true;
322             }
323             if (this._userDictionary.ExistsInFirst(word))
324             {
325                 return false;
326             }
327
328             if (this.IsUninflective(word) || this._knownPluralWords.Contains(word.ToLower(this.Culture)))
329             {
330                 return true;
331             }
332             else if (this.Singularize(word).Equals(word))
333             {
334                 return false;
335             }
336             else
337             {
338                 return true;
339             }
340         }
341
342         public override bool IsSingular(string word)
343         {
344             EDesignUtil.CheckArgumentNull<string>(word, "word");
345
346             if (this._userDictionary.ExistsInFirst(word))
347             {
348                 return true;
349             }
350             if (this._userDictionary.ExistsInSecond(word))
351             {
352                 return false;
353             }
354
355             if (this.IsUninflective(word) || this._knownSingluarWords.Contains(word.ToLower(this.Culture)))
356             {
357                 return true;
358             }
359             else if (!IsNoOpWord(word) && this.Singularize(word).Equals(word))
360             {
361                 return true;
362             }
363             else
364             {
365                 return false;
366             }
367         }
368
369         // 
370         public override string Pluralize(string word)
371         {
372             EDesignUtil.CheckArgumentNull<string>(word, "word");
373
374             return Capitalize(word, InternalPluralize);
375         }
376
377         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
378         private string InternalPluralize(string word)
379         {
380             // words that we know of
381             if (this._userDictionary.ExistsInFirst(word))
382             {
383                 return this._userDictionary.GetSecondValue(word);
384             }
385
386             if (IsNoOpWord(word))
387             {
388                 return word;
389             }
390
391             string prefixWord;
392             string suffixWord = GetSuffixWord(word, out prefixWord);
393
394             // by me -> by me
395             if (IsNoOpWord(suffixWord))
396             {
397                 return prefixWord + suffixWord;
398             }
399
400             // handle the word that do not inflect in the plural form
401             if (this.IsUninflective(suffixWord))
402             {
403                 return prefixWord + suffixWord;
404             }
405
406             // if word is one of the known plural forms, then just return
407             if (this._knownPluralWords.Contains(suffixWord.ToLowerInvariant()) || this.IsPlural(suffixWord))
408             {
409                 return prefixWord + suffixWord;
410             }
411
412             // handle irregular plurals, e.g. "ox" -> "oxen"
413             if (this._irregularPluralsPluralizationService.ExistsInFirst(suffixWord))
414             {
415                 return prefixWord + this._irregularPluralsPluralizationService.GetSecondValue(suffixWord);
416             }
417
418             string newSuffixWord;
419             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
420                 new List<string>() { "man" },
421                 (s) => s.Remove(s.Length - 2, 2) + "en", this.Culture, out newSuffixWord))
422             {
423                 return prefixWord + newSuffixWord;
424             }
425
426             // handle irregular inflections for common suffixes, e.g. "mouse" -> "mice"
427             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
428                 new List<string>() { "louse", "mouse" },
429                 (s) => s.Remove(s.Length - 4, 4) + "ice", this.Culture, out newSuffixWord))
430             {
431                 return prefixWord + newSuffixWord;
432             }
433
434             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
435                 new List<string>() { "tooth" },
436                 (s) => s.Remove(s.Length - 4, 4) + "eeth", this.Culture, out newSuffixWord))
437             {
438                 return prefixWord + newSuffixWord;
439             }
440             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
441                 new List<string>() { "goose" },
442                 (s) => s.Remove(s.Length - 4, 4) + "eese", this.Culture, out newSuffixWord))
443             {
444                 return prefixWord + newSuffixWord;
445             }
446             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
447                 new List<string>() { "foot" },
448                 (s) => s.Remove(s.Length - 3, 3) + "eet", this.Culture, out newSuffixWord))
449             {
450                 return prefixWord + newSuffixWord;
451             }
452             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
453                 new List<string>() { "zoon" },
454                 (s) => s.Remove(s.Length - 3, 3) + "oa", this.Culture, out newSuffixWord))
455             {
456                 return prefixWord + newSuffixWord;
457             }
458             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
459                 new List<string>() { "cis", "sis", "xis" },
460                 (s) => s.Remove(s.Length - 2, 2) + "es", this.Culture, out newSuffixWord))
461             {
462                 return prefixWord + newSuffixWord;
463             }
464
465             // handle assimilated classical inflections, e.g. vertebra -> vertebrae
466             if (this._assimilatedClassicalInflectionPluralizationService.ExistsInFirst(suffixWord))
467             {
468                 return prefixWord + this._assimilatedClassicalInflectionPluralizationService.GetSecondValue(suffixWord);
469             }
470
471             // Handle the classical variants of modern inflections
472             // 
473             if (this._classicalInflectionPluralizationService.ExistsInFirst(suffixWord))
474             {
475                 return prefixWord + this._classicalInflectionPluralizationService.GetSecondValue(suffixWord);
476             }
477
478             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
479                 new List<string>() { "trix" },
480                 (s) => s.Remove(s.Length - 1, 1) + "ces", this.Culture, out newSuffixWord))
481             {
482                 return prefixWord + newSuffixWord;
483             }
484
485             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
486                 new List<string>() { "eau", "ieu" },
487                 (s) => s + "x", this.Culture, out newSuffixWord))
488             {
489                 return prefixWord + newSuffixWord;
490             }
491
492             if (this._wordsEndingWithInxAnxYnxPluralizationService.ExistsInFirst(suffixWord))
493             {
494                 return prefixWord + this._wordsEndingWithInxAnxYnxPluralizationService.GetSecondValue(suffixWord);
495             }
496
497             // [cs]h and ss that take es as plural form
498             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord, new List<string>() { "ch", "sh", "ss" }, (s) => s + "es", this.Culture, out newSuffixWord))
499             {
500                 return prefixWord + newSuffixWord;
501             }
502
503             // f, fe that take ves as plural form
504             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
505                 new List<string>() { "alf", "elf", "olf", "eaf", "arf" },
506                 (s) => s.EndsWith("deaf", true, this.Culture) ? s : s.Remove(s.Length - 1, 1) + "ves", this.Culture, out newSuffixWord))
507             {
508                 return prefixWord + newSuffixWord;
509             }
510
511             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
512                 new List<string>() { "nife", "life", "wife" },
513                 (s) => s.Remove(s.Length - 2, 2) + "ves", this.Culture, out newSuffixWord))
514             {
515                 return prefixWord + newSuffixWord;
516             }
517
518             // y takes ys as plural form if preceded by a vowel, but ies if preceded by a consonant, e.g. stays, skies
519             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
520                 new List<string>() { "ay", "ey", "iy", "oy", "uy" },
521                 (s) => s + "s", this.Culture, out newSuffixWord))
522             {
523                 return prefixWord + newSuffixWord;
524             }
525
526             // 
527
528             if (suffixWord.EndsWith("y", true, this.Culture))
529             {
530                 return prefixWord + suffixWord.Remove(suffixWord.Length - 1, 1) + "ies";
531             }
532
533             // handle some of the words o -> os, and [vowel]o -> os, and the rest are o->oes
534             if (this._oSuffixPluralizationService.ExistsInFirst(suffixWord))
535             {
536                 return prefixWord + this._oSuffixPluralizationService.GetSecondValue(suffixWord);
537             }
538
539             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
540                 new List<string>() { "ao", "eo", "io", "oo", "uo" },
541                 (s) => s + "s", this.Culture, out newSuffixWord))
542             {
543                 return prefixWord + newSuffixWord;
544             }
545
546             if (suffixWord.EndsWith("o", true, this.Culture) || suffixWord.EndsWith("s", true, this.Culture))
547             {
548                 return prefixWord + suffixWord + "es";
549             }
550
551             if (suffixWord.EndsWith("x", true, this.Culture))
552             {
553                 return prefixWord + suffixWord + "es";
554             }
555
556             // cats, bags, hats, speakers
557             return prefixWord + suffixWord + "s";
558         }
559
560         public override string Singularize(string word)
561         {
562             EDesignUtil.CheckArgumentNull<string>(word, "word");
563
564             return Capitalize(word, InternalSingularize);
565         }
566
567         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
568         private string InternalSingularize(string word)
569         {
570             // words that we know of
571             if (this._userDictionary.ExistsInSecond(word))
572             {
573                 return this._userDictionary.GetFirstValue(word);
574             }
575
576             if (IsNoOpWord(word))
577             {
578                 return word;
579             }
580
581             string prefixWord;
582             string suffixWord = GetSuffixWord(word, out prefixWord);
583
584             if (IsNoOpWord(suffixWord))
585             {
586                 return prefixWord + suffixWord;
587             }
588
589             // handle the word that is the same as the plural form
590             if (this.IsUninflective(suffixWord))
591             {
592                 return prefixWord + suffixWord;
593             }
594
595             // if word is one of the known singular words, then just return
596
597             if (this._knownSingluarWords.Contains(suffixWord.ToLowerInvariant()))
598             {
599                 return prefixWord + suffixWord;
600             }
601
602             // handle simple irregular verbs, e.g. was -> were
603             if (this._irregularVerbPluralizationService.ExistsInSecond(suffixWord))
604             {
605                 return prefixWord + this._irregularVerbPluralizationService.GetFirstValue(suffixWord);
606             }
607
608             // handle irregular plurals, e.g. "ox" -> "oxen"
609             if (this._irregularPluralsPluralizationService.ExistsInSecond(suffixWord))
610             {
611                 return prefixWord + this._irregularPluralsPluralizationService.GetFirstValue(suffixWord);
612             }
613
614             // handle singluarization for words ending with sis and pluralized to ses, 
615             // e.g. "ses" -> "sis"
616             if (this._wordsEndingWithSisPluralizationService.ExistsInSecond(suffixWord))
617             {
618                 return prefixWord + this._wordsEndingWithSisPluralizationService.GetFirstValue(suffixWord);
619             }
620
621             // handle words ending with se, e.g. "ses" -> "se"
622             if (this._wordsEndingWithSePluralizationService.ExistsInSecond(suffixWord))
623             {
624                 return prefixWord + this._wordsEndingWithSePluralizationService.GetFirstValue(suffixWord);
625             }
626
627             // handle words ending with sus, e.g. "suses" -> "sus"
628             if (this._wordsEndingWithSusPluralizationService.ExistsInSecond(suffixWord))
629             {
630                 return prefixWord + this._wordsEndingWithSusPluralizationService.GetFirstValue(suffixWord);
631             }
632
633             string newSuffixWord;
634             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
635                 new List<string>() { "men" },
636                 (s) => s.Remove(s.Length - 2, 2) + "an", this.Culture, out newSuffixWord))
637             {
638                 return prefixWord + newSuffixWord;
639             }
640
641             // handle irregular inflections for common suffixes, e.g. "mouse" -> "mice"
642             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
643                 new List<string>() { "lice", "mice" },
644                 (s) => s.Remove(s.Length - 3, 3) + "ouse", this.Culture, out newSuffixWord))
645             {
646                 return prefixWord + newSuffixWord;
647             }
648
649             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
650                 new List<string>() { "teeth" },
651                 (s) => s.Remove(s.Length - 4, 4) + "ooth", this.Culture, out newSuffixWord))
652             {
653                 return prefixWord + newSuffixWord;
654             }
655             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
656                 new List<string>() { "geese" },
657                 (s) => s.Remove(s.Length - 4, 4) + "oose", this.Culture, out newSuffixWord))
658             {
659                 return prefixWord + newSuffixWord;
660             }
661             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
662                 new List<string>() { "feet" },
663                 (s) => s.Remove(s.Length - 3, 3) + "oot", this.Culture, out newSuffixWord))
664             {
665                 return prefixWord + newSuffixWord;
666             }
667             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
668                 new List<string>() { "zoa" },
669                 (s) => s.Remove(s.Length - 2, 2) + "oon", this.Culture, out newSuffixWord))
670             {
671                 return prefixWord + newSuffixWord;
672             }
673
674             // [cs]h and ss that take es as plural form, this is being moved up since the sses will be override by the ses
675             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
676                 new List<string>() { "ches", "shes", "sses" },
677                 (s) => s.Remove(s.Length - 2, 2), this.Culture, out newSuffixWord))
678             {
679                 return prefixWord + newSuffixWord;
680             }
681
682
683             // handle assimilated classical inflections, e.g. vertebra -> vertebrae
684             if (this._assimilatedClassicalInflectionPluralizationService.ExistsInSecond(suffixWord))
685             {
686                 return prefixWord + this._assimilatedClassicalInflectionPluralizationService.GetFirstValue(suffixWord);
687             }
688
689             // Handle the classical variants of modern inflections
690             // 
691             if (this._classicalInflectionPluralizationService.ExistsInSecond(suffixWord))
692             {
693                 return prefixWord + this._classicalInflectionPluralizationService.GetFirstValue(suffixWord);
694             }
695
696             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
697                 new List<string>() { "trices" },
698                 (s) => s.Remove(s.Length - 3, 3) + "x", this.Culture, out newSuffixWord))
699             {
700                 return prefixWord + newSuffixWord;
701             }
702
703             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
704                 new List<string>() { "eaux", "ieux" },
705                 (s) => s.Remove(s.Length - 1, 1), this.Culture, out newSuffixWord))
706             {
707                 return prefixWord + newSuffixWord;
708             }
709
710             if (this._wordsEndingWithInxAnxYnxPluralizationService.ExistsInSecond(suffixWord))
711             {
712                 return prefixWord + this._wordsEndingWithInxAnxYnxPluralizationService.GetFirstValue(suffixWord);
713             }
714
715             // f, fe that take ves as plural form
716             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
717                 new List<string>() { "alves", "elves", "olves", "eaves", "arves" },
718                 (s) => s.Remove(s.Length - 3, 3) + "f", this.Culture, out newSuffixWord))
719             {
720                 return prefixWord + newSuffixWord;
721             }
722
723             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
724                 new List<string>() { "nives", "lives", "wives" },
725                 (s) => s.Remove(s.Length - 3, 3) + "fe", this.Culture, out newSuffixWord))
726             {
727                 return prefixWord + newSuffixWord;
728             }
729
730             // y takes ys as plural form if preceded by a vowel, but ies if preceded by a consonant, e.g. stays, skies
731             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
732                 new List<string>() { "ays", "eys", "iys", "oys", "uys" },
733                 (s) => s.Remove(s.Length - 1, 1), this.Culture, out newSuffixWord))
734             {
735                 return prefixWord + newSuffixWord;
736             }
737
738             // 
739
740             if (suffixWord.EndsWith("ies", true, this.Culture))
741             {
742                 return prefixWord + suffixWord.Remove(suffixWord.Length - 3, 3) + "y";
743             }
744
745             // handle some of the words o -> os, and [vowel]o -> os, and the rest are o->oes
746             if (this._oSuffixPluralizationService.ExistsInSecond(suffixWord))
747             {
748                 return prefixWord + this._oSuffixPluralizationService.GetFirstValue(suffixWord);
749             }
750
751             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
752                 new List<string>() { "aos", "eos", "ios", "oos", "uos" },
753                 (s) => suffixWord.Remove(suffixWord.Length - 1, 1), this.Culture, out newSuffixWord))
754             {
755                 return prefixWord + newSuffixWord;
756             }
757
758             // 
759
760
761
762
763             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
764                 new List<string>() { "ces" },
765                 (s) => s.Remove(s.Length - 1, 1), this.Culture, out newSuffixWord))
766             {
767                 return prefixWord + newSuffixWord;
768             }
769
770             if (PluralizationServiceUtil.TryInflectOnSuffixInWord(suffixWord,
771                 new List<string>() { "ces", "ses", "xes" },
772                 (s) => s.Remove(s.Length - 2, 2), this.Culture, out newSuffixWord))
773             {
774                 return prefixWord + newSuffixWord;
775             }
776
777             if (suffixWord.EndsWith("oes", true, this.Culture))
778             {
779                 return prefixWord + suffixWord.Remove(suffixWord.Length - 2, 2);
780             }
781
782             if (suffixWord.EndsWith("ss", true, this.Culture))
783             {
784                 return prefixWord + suffixWord;
785             }
786
787             if (suffixWord.EndsWith("s", true, this.Culture))
788             {
789                 return prefixWord + suffixWord.Remove(suffixWord.Length - 1, 1);
790             }
791
792             // word is a singlar
793             return prefixWord + suffixWord;
794         }
795
796         #region Utils
797         /// <summary>
798         /// captalize the return word if the parameter is capitalized
799         /// if word is "Table", then return "Tables"
800         /// </summary>
801         /// <param name="word"></param>
802         /// <param name="action"></param>
803         /// <returns></returns>
804         private string Capitalize(string word, Func<string, string> action)
805         {
806             string result = action(word);
807
808             if (IsCapitalized(word))
809             {
810                 if (result.Length == 0)
811                     return result;
812
813                 StringBuilder sb = new StringBuilder(result.Length);
814
815                 sb.Append(char.ToUpperInvariant(result[0]));
816                 sb.Append(result.Substring(1));
817                 return sb.ToString();
818             }
819             else
820             {
821                 return result;
822             }
823         }
824
825         /// <summary>
826         /// separate one combine word in to two parts, prefix word and the last word(suffix word)
827         /// </summary>
828         /// <param name="word"></param>
829         /// <param name="prefixWord"></param>
830         /// <returns></returns>
831         private string GetSuffixWord(string word, out string prefixWord)
832         {
833             // use the last space to separate the words
834             int lastSpaceIndex = word.LastIndexOf(' ');
835             prefixWord = word.Substring(0, lastSpaceIndex + 1);
836             return word.Substring(lastSpaceIndex + 1);
837
838             // 
839         }
840
841         private bool IsCapitalized(string word)
842         {
843             return string.IsNullOrEmpty(word) ? false : char.IsUpper(word, 0);
844         }
845
846         private bool IsAlphabets(string word)
847         {
848             // return false when the word is "[\s]*" or leading or tailing with spaces
849             // or contains non alphabetical characters
850             if (string.IsNullOrEmpty(word.Trim()) || !word.Equals(word.Trim()) ||
851                 Regex.IsMatch(word, "[^a-zA-Z\\s]"))
852             {
853                 return false;
854             }
855             else
856             {
857                 return true;
858             }
859         }
860
861         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
862         private bool IsUninflective(string word)
863         {
864             EDesignUtil.CheckArgumentNull<string>(word, "word");
865             if (PluralizationServiceUtil.DoesWordContainSuffix(word, _uninflectiveSuffixList, this.Culture)
866                            || (!word.ToLower(this.Culture).Equals(word) && word.EndsWith("ese", false, this.Culture))
867                            || this._uninflectiveWordList.Contains(word.ToLowerInvariant()))
868             {
869                 return true;
870             }
871             else
872             {
873                 return false;
874             }
875         }
876
877         /// <summary>
878         /// return true when the word is "[\s]*" or leading or tailing with spaces
879         /// or contains non alphabetical characters
880         /// </summary>
881         /// <param name="word"></param>
882         /// <returns></returns>
883         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
884         private bool IsNoOpWord(string word)
885         {
886
887             if (!IsAlphabets(word) ||
888                 word.Length <= 1 ||
889                 _pronounList.Contains(word.ToLowerInvariant()))
890             {
891                 return true;
892             }
893             else
894             {
895                 return false;
896             }
897         }
898
899         #endregion
900
901         #region ICustomPluralizationMapping Members
902
903         /// <summary>
904         /// This method allow you to add word to internal PluralizationService of English.
905         /// If the singluar or the plural value was already added by this method, then an ArgumentException will be thrown.
906         /// </summary>
907         /// <param name="singular"></param>
908         /// <param name="plural"></param>
909         public void AddWord(string singular, string plural)
910         {
911             EDesignUtil.CheckArgumentNull<string>(singular, "singular");
912             EDesignUtil.CheckArgumentNull<string>(plural, "plural");
913
914             if (this._userDictionary.ExistsInSecond(plural))
915             {
916                 throw new ArgumentException(Strings.DuplicateEntryInUserDictionary("plural", plural), "plural");
917             }
918             else if (this._userDictionary.ExistsInFirst(singular))
919             {
920                 throw new ArgumentException(Strings.DuplicateEntryInUserDictionary("singular", singular), "singular");
921             }
922             else
923             {
924                 this._userDictionary.AddValue(singular, plural);
925             }
926         }
927
928         #endregion
929     }
930 }