From dfd27132e5c6619c1304bc94332ff69f442fd7b5 Mon Sep 17 00:00:00 2001 From: Ron Hopper Date: Tue, 15 Sep 2009 19:45:37 -0400 Subject: [PATCH] Added util.Inflector to handle pluralization and singularization. --- spec/util/index.cfm | 1 + spec/util/inflectorSpec.cfm | 108 +++++++++++++++++++++++++++++++ util/Inflector.cfc | 126 ++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 spec/util/index.cfm create mode 100644 spec/util/inflectorSpec.cfm create mode 100644 util/Inflector.cfc diff --git a/spec/util/index.cfm b/spec/util/index.cfm new file mode 100644 index 0000000..f7237b5 --- /dev/null +++ b/spec/util/index.cfm @@ -0,0 +1 @@ + diff --git a/spec/util/inflectorSpec.cfm b/spec/util/inflectorSpec.cfm new file mode 100644 index 0000000..dd97a53 --- /dev/null +++ b/spec/util/inflectorSpec.cfm @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util/Inflector.cfc b/util/Inflector.cfc new file mode 100644 index 0000000..2547040 --- /dev/null +++ b/util/Inflector.cfc @@ -0,0 +1,126 @@ + + + function init() { + _plurals = []; + _singulars = []; + _uncountables = ""; + + _loadDefaultInflections(); + + return this; + } + + function pluralize(word) { + var local = {}; + + if (trim(word) eq "") return word; + if (isUncountable(word)) return word; + + for (local.i = 1; local.i <= arrayLen(_plurals); local.i++) { + local.pair = _plurals[local.i]; + if (reFindNoCase(local.pair.rule, word)) { + return reReplaceNoCase(word, local.pair.rule, local.pair.replacement); + } + } + + return word; + } + + function singularize(word) { + var local = {}; + + if (trim(word) eq "") return word; + if (isUncountable(word)) return word; + + for (local.i = 1; local.i <= arrayLen(_singulars); local.i++) { + local.pair = _singulars[local.i]; + if (reFindNoCase(local.pair.rule, word)) { + return reReplaceNoCase(word, local.pair.rule, local.pair.replacement); + } + } + + return word; + } + + function isUncountable(word) { + return listFindNoCase(_uncountables, trim(word)); + } + + function plural(rule, replacement) { + arrayPrepend(_plurals, arguments); + } + + function singular(rule, replacement) { + arrayPrepend(_singulars, arguments); + } + + function irregular(singularWord, pluralWord) { + var local = {}; + + local.rule = reReplace(singularWord, "^(.)(.*)$", "(\1)\2$"); + local.replacement = reReplace(pluralWord, "^(.)(.*)$", "\\1\2"); + plural(local.rule, local.replacement); + + local.rule = reReplace(pluralWord, "^(.)(.*)$", "(\1)\2$"); + local.replacement = reReplace(singularWord, "^(.)(.*)$", "\\1\2"); + singular(local.rule, local.replacement); + } + + function uncountable(words) { + _uncountables = listAppend(_uncountables, words); + } + + function _loadDefaultInflections() { + plural("$", "s"); + plural("s$", "s"); + plural("(ax|test)is$", "\1es"); + plural("(octop|vir)us$", "\1i"); + plural("(alias|status)$", "\1es"); + plural("(bu)s$", "\1ses"); + plural("(buffal|tomat)o$", "\1oes"); + plural("([ti])um$", "\1a"); + plural("sis$", "ses"); + plural("(?:([^f])fe|([lr])f)$", "\1\2ves"); + plural("(hive)$", "\1s"); + plural("([^aeiouy]|qu)y$", "\1ies"); + plural("(x|ch|ss|sh)$", "\1es"); + plural("(matr|vert|ind)ix|ex$", "\1ices"); + plural("([m|l])ouse$", "\1ice"); + plural("^(ox)$", "\1en"); + plural("(quiz)$", "\1zes"); + + singular("s$", ""); + singular("(n)ews$", "\1ews"); + singular("([ti])a$", "\1um"); + singular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "\1\2sis"); + singular("(^analy)ses$", "\1sis"); + singular("([^f])ves$", "\1fe"); + singular("(hive)s$", "\1"); + singular("(tive)s$", "\1"); + singular("([lr])ves$", "\1f"); + singular("([^aeiouy]|qu)ies$", "\1y"); + singular("(s)eries$", "\1eries"); + singular("(m)ovies$", "\1ovie"); + singular("(x|ch|ss|sh)es$", "\1"); + singular("([m|l])ice$", "\1ouse"); + singular("(bus)es$", "\1"); + singular("(o)es$", "\1"); + singular("(shoe)s$", "\1"); + singular("(cris|ax|test)es$", "\1is"); + singular("(octop|vir)i$", "\1us"); + singular("(alias|status)es$", "\1"); + singular("^(ox)en", "\1"); + singular("(vert|ind)ices$", "\1ex"); + singular("(matr)ices$", "\1ix"); + singular("(quiz)zes$", "\1"); + + irregular("person", "people"); + irregular("man", "men"); + irregular("child", "children"); + irregular("sex", "sexes"); + irregular("move", "moves"); + + uncountable("equipment,information,rice,money,species,series,fish,sheep"); + } + +