/**
* Copyright (c) 2008-2011 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions.
*
* This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General
* Public License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License Version 3
* for more details.
*
* You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see
* http://www.gnu.org/licenses.
*
* Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of
* Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation.
* All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.util;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Transforms words (from singular to plural, from camelCase to under_score, etc.). I got bored of doing Real Work...
*
* Copied from as-is: http://snippets.dzone.com/posts/show/4110
*
* @author chuyeow
*/
public class Inflector
{
// Pfft, can't think of a better name, but this is needed to avoid the price of initializing the pattern on each call.
private static final Pattern UNDERSCORE_PATTERN_1 = Pattern.compile( "([A-Z]+)([A-Z][a-z])" );
private static final Pattern UNDERSCORE_PATTERN_2 = Pattern.compile( "([a-z\\d])([A-Z])" );
private static List<RuleAndReplacement> plurals = new ArrayList<RuleAndReplacement>();
private static List<RuleAndReplacement> singulars = new ArrayList<RuleAndReplacement>();
private static List<String> uncountables = new ArrayList<String>();
private static Inflector instance; // (Pseudo-)Singleton instance.
private Inflector()
{
// Woo, you can't touch me.
initialize();
}
private void initialize()
{
plural( "$", "s" );
plural( "s$", "s" );
plural( "(ax|test)is$", "$1es" );
plural( "(octop|vir)us$", "$1i" );
plural( "(alias|status)$", "$1es" );
plural( "(bu)s$", "$1es" );
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( "([^aeiouy]|qu)ies$", "$1y" );
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( new String[] { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep" } );
}
public static Inflector getInstance()
{
if ( instance == null )
{
instance = new Inflector();
}
return instance;
}
public String underscore( String camelCasedWord )
{
// Regexes in Java are fucking stupid...
String underscoredWord = UNDERSCORE_PATTERN_1.matcher( camelCasedWord ).replaceAll( "$1_$2" );
underscoredWord = UNDERSCORE_PATTERN_2.matcher( underscoredWord ).replaceAll( "$1_$2" );
underscoredWord = underscoredWord.replace( '-', '_' ).toLowerCase();
return underscoredWord;
}
public String pluralize( String word )
{
if ( uncountables.contains( word.toLowerCase() ) )
{
return word;
}
return replaceWithFirstRule( word, plurals );
}
public String singularize( String word )
{
if ( uncountables.contains( word.toLowerCase() ) )
{
return word;
}
return replaceWithFirstRule( word, singulars );
}
private String replaceWithFirstRule( String word, List<RuleAndReplacement> ruleAndReplacements )
{
for ( RuleAndReplacement rar : ruleAndReplacements )
{
String rule = rar.getRule();
String replacement = rar.getReplacement();
// Return if we find a match.
Matcher matcher = Pattern.compile( rule, Pattern.CASE_INSENSITIVE ).matcher( word );
if ( matcher.find() )
{
return matcher.replaceAll( replacement );
}
}
return word;
}
public String tableize( String className )
{
return pluralize( underscore( className ) );
}
public String tableize( Class klass )
{
// Strip away package name - we only want the 'base' class name.
String className = klass.getName().replace( klass.getPackage().getName() + ".", "" );
return tableize( className );
}
public static void plural( String rule, String replacement )
{
plurals.add( 0, new RuleAndReplacement( rule, replacement ) );
}
public static void singular( String rule, String replacement )
{
singulars.add( 0, new RuleAndReplacement( rule, replacement ) );
}
public static void irregular( String singular, String plural )
{
plural( singular, plural );
singular( plural, singular );
}
public static void uncountable( String... words )
{
for ( String word : words )
{
uncountables.add( word );
}
}
}
// Ugh, no open structs in Java (not-natively at least).
class RuleAndReplacement
{
private String rule;
private String replacement;
public RuleAndReplacement( String rule, String replacement )
{
this.rule = rule;
this.replacement = replacement;
}
public String getReplacement()
{
return replacement;
}
public void setReplacement( String replacement )
{
this.replacement = replacement;
}
public String getRule()
{
return rule;
}
public void setRule( String rule )
{
this.rule = rule;
}
}