package org.radrails.rails.core; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public abstract class Inflector { private static class Replacement { private Pattern rule; private String replacement; Replacement(Pattern rule, String replacement) { this.rule = rule; this.replacement = replacement; } } private static List<Replacement> plurals = new ArrayList<Replacement>(); private static List<Replacement> singulars = new ArrayList<Replacement>(); private static List<String> uncountables = new ArrayList<String>(); public static String singularize(String plural) { if (uncountables.contains(plural.toLowerCase())) { return plural; } for (Replacement replacement : singulars) { Matcher matcher = replacement.rule.matcher(plural); if (matcher.find()) { return matcher.replaceFirst(replacement.replacement); } } return plural; } public static String pluralize(String singular) { if (uncountables.contains(singular.toLowerCase())) { return singular; } for (Replacement replacement : plurals) { Matcher matcher = replacement.rule.matcher(singular); if (matcher.find()) { return matcher.replaceFirst(replacement.replacement); } } return singular; } private static void plural(Pattern rule, String replacement) { plurals.add(0, new Replacement(rule, replacement)); } private static void singular(Pattern rule, String replacement) { singulars.add(0, new Replacement(rule, replacement)); } private static void irregular(String singular, String plural) { plural(Pattern.compile("(" + singular.charAt(0) + ")" + singular.substring(1) + "$", Pattern.CASE_INSENSITIVE), "$1" + plural.substring(1)); singular(Pattern.compile("(" + plural.charAt(0) + ")" + plural.substring(1) + "$", Pattern.CASE_INSENSITIVE), "$1" + singular.substring(1)); } private static void uncountable(String[] words) { for (int i = 0; i < words.length; i++) { uncountables.add(words[i]); } } static { Inflector.plural(Pattern.compile("$"), "s"); Inflector.plural(Pattern.compile("s$", Pattern.CASE_INSENSITIVE), "s"); Inflector.plural(Pattern.compile("(ax|test)is$", Pattern.CASE_INSENSITIVE), "$1es"); Inflector.plural(Pattern.compile("(octop|vir)us$", Pattern.CASE_INSENSITIVE), "$1i"); Inflector.plural(Pattern.compile("(alias|status)$", Pattern.CASE_INSENSITIVE), "$1es"); Inflector.plural(Pattern.compile("(bu)s$", Pattern.CASE_INSENSITIVE), "$1ses"); Inflector.plural(Pattern.compile("(buffal|tomat)o$", Pattern.CASE_INSENSITIVE), "$1oes"); Inflector.plural(Pattern.compile("([ti])um$", Pattern.CASE_INSENSITIVE), "$1a"); Inflector.plural(Pattern.compile("sis$", Pattern.CASE_INSENSITIVE), "ses"); Inflector.plural(Pattern.compile("(?:([^f])fe|([lr])f)$", Pattern.CASE_INSENSITIVE), "$1$2ves"); Inflector.plural(Pattern.compile("(hive)$", Pattern.CASE_INSENSITIVE), "$1s"); Inflector.plural(Pattern.compile("([^aeiouy]|qu)y$", Pattern.CASE_INSENSITIVE), "$1ies"); Inflector.plural(Pattern.compile("(x|ch|ss|sh)$", Pattern.CASE_INSENSITIVE), "$1es"); Inflector.plural(Pattern.compile("(matr|vert|ind)ix|ex$", Pattern.CASE_INSENSITIVE), "$1ices"); Inflector.plural(Pattern.compile("([m|l])ouse$", Pattern.CASE_INSENSITIVE), "$1ice"); Inflector.plural(Pattern.compile("^(ox)$", Pattern.CASE_INSENSITIVE), "$1en"); Inflector.plural(Pattern.compile("(quiz)$", Pattern.CASE_INSENSITIVE), "$1zes"); Inflector.singular(Pattern.compile("s$", Pattern.CASE_INSENSITIVE), ""); Inflector.singular(Pattern.compile("(n)ews$", Pattern.CASE_INSENSITIVE), "$1ews"); Inflector.singular(Pattern.compile("([ti])a$", Pattern.CASE_INSENSITIVE), "$1um"); Inflector.singular(Pattern.compile("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", Pattern.CASE_INSENSITIVE), "$1$2sis"); Inflector.singular(Pattern.compile("(^analy)ses$", Pattern.CASE_INSENSITIVE), "$1sis"); Inflector.singular(Pattern.compile("([^f])ves$", Pattern.CASE_INSENSITIVE), "$1fe"); Inflector.singular(Pattern.compile("(hive)s$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("(tive)s$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("([lr])ves$", Pattern.CASE_INSENSITIVE), "$1f"); Inflector.singular(Pattern.compile("([^aeiouy]|qu)ies$", Pattern.CASE_INSENSITIVE), "$1y"); Inflector.singular(Pattern.compile("(s)eries$", Pattern.CASE_INSENSITIVE), "$1eries"); Inflector.singular(Pattern.compile("(m)ovies$", Pattern.CASE_INSENSITIVE), "$1ovie"); Inflector.singular(Pattern.compile("(x|ch|ss|sh)es$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("([m|l])ice$", Pattern.CASE_INSENSITIVE), "$1ouse"); Inflector.singular(Pattern.compile("(bus)es$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("(o)es$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("(shoe)s$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("(cris|ax|test)es$", Pattern.CASE_INSENSITIVE), "$1is"); Inflector.singular(Pattern.compile("(octop|vir)i$", Pattern.CASE_INSENSITIVE), "$1us"); Inflector.singular(Pattern.compile("(alias|status)es$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("^(ox)en", Pattern.CASE_INSENSITIVE), "$1"); Inflector.singular(Pattern.compile("(vert|ind)ices$", Pattern.CASE_INSENSITIVE), "$1ex"); Inflector.singular(Pattern.compile("(matr)ices$", Pattern.CASE_INSENSITIVE), "$1ix"); Inflector.singular(Pattern.compile("(quiz)zes$", Pattern.CASE_INSENSITIVE), "$1"); Inflector.irregular("person", "people"); Inflector.irregular("man", "men"); Inflector.irregular("child", "children"); Inflector.irregular("sex", "sexes"); Inflector.irregular("move", "moves"); Inflector.uncountable(new String[] { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep" }); } }