/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2010 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. * * Contributor(s): * * Portions Copyrighted 2009 Sun Microsystems, Inc. */ package org.netbeans.modules.ruby; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Handles pluralizing/singularizing words Rails style * -- see <tt>activesupport/lib/active_support/inflections.rb</tt>. This * is basically a poor man's simplified Java port of the Rails equivalent. * <p/> * See <a href="http://api.rubyonrails.org/classes/Inflector.html">Rails Inflector</a> * for the original implementation that this class tries to mimic. * * @todo move also all the others related methods from RubyUtils to here (tableize etc..) * * @author Erno Mononen */ final class Inflector { private final Set<String> uncountables = new HashSet<String>(); private final Map<String, String> irregulars = new LinkedHashMap<String, String>(); private final Map<Pattern, String> plurals = new LinkedHashMap<Pattern, String>(); private final Map<Pattern, String> singulars = new LinkedHashMap<Pattern, String>(); private static final Inflector INSTANCE = new Inflector(); public static Inflector getDefault() { return INSTANCE; } private Inflector() { init(); } /** * Gets the plural form of the given word. * * @param word * @return */ public String pluralize(final String word) { if (isEmpty(word)) { return word; } String lowerCaseWord = word.toLowerCase(Locale.ENGLISH); if (uncountables.contains(lowerCaseWord)) { return word; } if (irregulars.containsKey(lowerCaseWord)) { return irregulars.get(lowerCaseWord); } for (Pattern p : plurals.keySet()) { Matcher m = p.matcher(word); if (m.find()) { return m.replaceAll(plurals.get(p)); } } return word; } /** * Gets the singular form of the given word. * * @param word * @return */ public String singularize(final String word) { if (isEmpty(word)) { return word; } String lowerCaseWord = word.toLowerCase(Locale.ENGLISH); if (uncountables.contains(lowerCaseWord)) { return word; } if (irregulars.containsValue(lowerCaseWord)) { for (Map.Entry<String, String> entry : irregulars.entrySet()){ if (entry.getValue().equals(lowerCaseWord)) { return entry.getKey(); } } } for (Pattern p : singulars.keySet()) { Matcher m = p.matcher(word); if (m.find()) { return m.replaceAll(singulars.get(p)); } } return word; } /** * Similar to Rails' Inflector tableize method: converts a name * to a corresponding table name: * * @param word * @return */ public String tableize(String word) { return pluralize(RubyUtils.camelToUnderlinedName(word)); } /** * Gets the class name part from the given <code>classNameInModule</code>, i.e. * removes the module part from it. E.g. for <code>"Foo::Bar::Baz"</code> returns * <code>"Baz"</code>. * * @param classNameInModule * @return */ public String demodulize(String classNameInModule) { int last = classNameInModule.lastIndexOf("::"); //NOI18N if (last == -1) { return classNameInModule; } return classNameInModule.substring(last + 2, classNameInModule.length()); } private boolean isEmpty(String str) { return str == null || "".equals(str.trim()); } private void plural(String regex, String replacement) { plurals.put(compile(regex), replacement); } private void singular(String regex, String replacement) { singulars.put(compile(regex), replacement); } private void irregular(String singular, String plural) { irregulars.put(singular, plural); } private Pattern compile(String regex) { // case insensitive return Pattern.compile("(?i)" + regex); } void uncountable(String... uncountable) { for (String each : uncountable) { uncountables.add(each); } } private void init() { 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("(matr|vert|ind)ix|ex$", "$1ices"); plural("(x|ch|ss|sh)$", "$1es"); plural("([m|l])ouse$", "$1ice"); plural("^(ox)$", "$1en"); plural("(quiz)$", "$1zes"); plural("(.*p)erson$", "$1eople"); plural("(.*c)riterion$", "$1riteria"); plural("(.*m)an$", "$1en"); plural("s$", "s"); plural("$", "s"); singular("(ax|test)es$", "$1is"); 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("(octop|vir)us$", "$1us"); singular("(alias|status)es$", "$1"); singular("^(ox)en", "$1"); singular("(vert|ind)ices$", "$1ex"); singular("(matr)ices$", "$1ix"); singular("(quiz)zes$", "$1"); singular("(database)s$", "$1"); singular("(.*p)eople$", "$1erson"); plural("(.*c)riteria$", "$1riterion"); singular("(.*m)en$", "$1an"); singular("s$", ""); irregular("person", "people"); irregular("man", "men"); irregular("child", "children"); irregular("sex", "sexes"); irregular("move", "moves"); irregular("cow", "kine"); irregular("criterion", "criteria"); uncountable("equipment", "information", "rice", "money", "species", "series", "fish", "sheep"); } }