/* * SubSonic - http://subsonicproject.com * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. */ using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using SubSonic.Sugar; using SubSonic.Utilities; namespace SubSonic { /// /// Summary for the Inflector class /// public static class Inflector { private static readonly List _plurals = new List(); private static readonly List _singulars = new List(); private static readonly List _uncountables = new List(); /// /// Initializes the class. /// static Inflector() { AddPluralRule("$", "s"); AddPluralRule("s$", "s"); AddPluralRule("(ax|test)is$", "$1es"); AddPluralRule("(octop|vir)us$", "$1i"); AddPluralRule("(alias|status)$", "$1es"); AddPluralRule("(bu)s$", "$1ses"); AddPluralRule("(buffal|tomat)o$", "$1oes"); AddPluralRule("([ti])um$", "$1a"); AddPluralRule("sis$", "ses"); AddPluralRule("(?:([^f])fe|([lr])f)$", "$1$2ves"); AddPluralRule("(hive)$", "$1s"); AddPluralRule("([^aeiouy]|qu)y$", "$1ies"); AddPluralRule("(x|ch|ss|sh)$", "$1es"); AddPluralRule("(matr|vert|ind)ix|ex$", "$1ices"); AddPluralRule("([m|l])ouse$", "$1ice"); AddPluralRule("^(ox)$", "$1en"); AddPluralRule("(quiz)$", "$1zes"); AddSingularRule("s$", String.Empty); AddSingularRule("ss$", "ss"); AddSingularRule("(n)ews$", "$1ews"); AddSingularRule("([ti])a$", "$1um"); AddSingularRule("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis"); AddSingularRule("(^analy)ses$", "$1sis"); AddSingularRule("([^f])ves$", "$1fe"); AddSingularRule("(hive)s$", "$1"); AddSingularRule("(tive)s$", "$1"); AddSingularRule("([lr])ves$", "$1f"); AddSingularRule("([^aeiouy]|qu)ies$", "$1y"); AddSingularRule("(s)eries$", "$1eries"); AddSingularRule("(m)ovies$", "$1ovie"); AddSingularRule("(x|ch|ss|sh)es$", "$1"); AddSingularRule("([m|l])ice$", "$1ouse"); AddSingularRule("(bus)es$", "$1"); AddSingularRule("(o)es$", "$1"); AddSingularRule("(shoe)s$", "$1"); AddSingularRule("(cris|ax|test)es$", "$1is"); AddSingularRule("(octop|vir)i$", "$1us"); AddSingularRule("(alias|status)$", "$1"); AddSingularRule("(alias|status)es$", "$1"); AddSingularRule("^(ox)en", "$1"); AddSingularRule("(vert|ind)ices$", "$1ex"); AddSingularRule("(matr)ices$", "$1ix"); AddSingularRule("(quiz)zes$", "$1"); AddIrregularRule("person", "people"); AddIrregularRule("man", "men"); AddIrregularRule("child", "children"); AddIrregularRule("sex", "sexes"); AddIrregularRule("tax", "taxes"); AddIrregularRule("move", "moves"); AddUnknownCountRule("equipment"); AddUnknownCountRule("information"); AddUnknownCountRule("rice"); AddUnknownCountRule("money"); AddUnknownCountRule("species"); AddUnknownCountRule("series"); AddUnknownCountRule("fish"); AddUnknownCountRule("sheep"); } /// /// Adds the irregular rule. /// /// The singular. /// The plural. private static void AddIrregularRule(string singular, string plural) { AddPluralRule(String.Concat("(", singular[0], ")", singular.Substring(1), "$"), String.Concat("$1", plural.Substring(1))); AddSingularRule(String.Concat("(", plural[0], ")", plural.Substring(1), "$"), String.Concat("$1", singular.Substring(1))); } /// /// Adds the unknown count rule. /// /// The word. private static void AddUnknownCountRule(string word) { _uncountables.Add(word.ToLower()); } /// /// Adds the plural rule. /// /// The rule. /// The replacement. private static void AddPluralRule(string rule, string replacement) { _plurals.Add(new InflectorRule(rule, replacement)); } /// /// Adds the singular rule. /// /// The rule. /// The replacement. private static void AddSingularRule(string rule, string replacement) { _singulars.Add(new InflectorRule(rule, replacement)); } /// /// Makes the plural. /// /// The word. /// public static string MakePlural(string word) { return ApplyRules(_plurals, word); } /// /// Makes the singular. /// /// The word. /// public static string MakeSingular(string word) { return ApplyRules(_singulars, word); } /// /// Applies the rules. /// /// The rules. /// The word. /// private static string ApplyRules(IList rules, string word) { string result = word; if(!_uncountables.Contains(word.ToLower())) { for(int i = rules.Count - 1; i >= 0; i--) { string currentPass = rules[i].Apply(word); if(currentPass != null) { result = currentPass; break; } } } return result; } /// /// Converts the string to title case. /// /// The word. /// public static string ToTitleCase(string word) { return Regex.Replace(ToHumanCase(AddUnderscores(word)), @"\b([a-z])", delegate(Match match) { return match.Captures[0].Value.ToUpper(); }); } /// /// Converts the string to human case. /// /// The lowercase and underscored word. /// public static string ToHumanCase(string lowercaseAndUnderscoredWord) { return MakeInitialCaps(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " ")); } /// /// Converts the string to pascal case. /// /// The lowercase and underscored word. /// public static string ToPascalCase(string lowercaseAndUnderscoredWord) { return ToPascalCase(lowercaseAndUnderscoredWord, true); } /// /// Converts text to pascal case... /// /// The text. /// if set to true [remove underscores]. /// public static string ToPascalCase(string text, bool removeUnderscores) { if(String.IsNullOrEmpty(text)) return text; text = text.Replace("_", " "); string joinString = removeUnderscores ? String.Empty : "_"; string[] words = text.Split(' '); if(words.Length > 1 || Validation.IsUpperCase(words[0])) { for(int i = 0; i < words.Length; i++) { if(words[i].Length > 0) { string word = words[i]; string restOfWord = word.Substring(1); if(Validation.IsUpperCase(restOfWord)) restOfWord = restOfWord.ToLower(CultureInfo.CurrentUICulture); char firstChar = char.ToUpper(word[0], CultureInfo.CurrentUICulture); words[i] = String.Concat(firstChar, restOfWord); } } return String.Join(joinString, words); } return String.Concat(words[0].Substring(0, 1).ToUpper(CultureInfo.CurrentUICulture), words[0].Substring(1)); } /// /// Converts the string to camel case. /// /// The lowercase and underscored word. /// public static string ToCamelCase(string lowercaseAndUnderscoredWord) { return MakeInitialLowerCase(ToPascalCase(lowercaseAndUnderscoredWord)); } /// /// Adds the underscores. /// /// The pascal cased word. /// public static string AddUnderscores(string pascalCasedWord) { return Regex.Replace(Regex.Replace(Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-z\d])([A-Z])", "$1_$2"), @"[-\s]", "_").ToLower(); } /// /// Makes the initial caps. /// /// The word. /// public static string MakeInitialCaps(string word) { return String.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower()); } /// /// Makes the initial lower case. /// /// The word. /// public static string MakeInitialLowerCase(string word) { return String.Concat(word.Substring(0, 1).ToLower(), word.Substring(1)); } /// /// Adds the ordinal suffix. /// /// The number. /// public static string AddOrdinalSuffix(string number) { if(Utility.IsStringNumeric(number)) { int n = int.Parse(number); int nMod100 = n % 100; if(nMod100 >= 11 && nMod100 <= 13) return String.Concat(number, "th"); switch(n % 10) { case 1: return String.Concat(number, "st"); case 2: return String.Concat(number, "nd"); case 3: return String.Concat(number, "rd"); default: return String.Concat(number, "th"); } } return number; } /// /// Converts the underscores to dashes. /// /// The underscored word. /// public static string ConvertUnderscoresToDashes(string underscoredWord) { return underscoredWord.Replace('_', '-'); } #region Nested type: InflectorRule /// /// Summary for the InflectorRule class /// private class InflectorRule { /// /// /// public readonly Regex regex; /// /// /// public readonly string replacement; /// /// Initializes a new instance of the class. /// /// The regex pattern. /// The replacement text. public InflectorRule(string regexPattern, string replacementText) { regex = new Regex(regexPattern, RegexOptions.IgnoreCase); replacement = replacementText; } /// /// Applies the specified word. /// /// The word. /// public string Apply(string word) { if(!regex.IsMatch(word)) return null; string replace = regex.Replace(word, replacement); if(word == word.ToUpper()) replace = replace.ToUpper(); return replace; } } #endregion } }