All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jersey.core.impl.provider.entity.Inflector Maven / Gradle / Ivy

There is a newer version: 1.19.4
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved.
 *
 * 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://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  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 packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * 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.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * 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 don't 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.
 */
package com.sun.jersey.core.impl.provider.entity;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 

API for performing inflections (pluralization, singularization, and so on) * on various strings. These inflections will be useful in code generators that * convert things like database table names into Java class names.

* *

The getInstance() method returns a singleton instance of * this class with a default set of rules, which can then be customized. * Rules added during customization will take precedence over the standard ones. * Use the addIrregular(), addPlural(), addSingular(), * and addUncountable() methods to add additional rules ot the default * ones.

* *

IMPLEMENTATION NOTE - The default implementation is * intended to be functionally compatible with the Inflector::inflections * class in Ruby on Rails. The gsub() method on Ruby strings * matches regular expressions anywhere in the input. However, nearly all of * the actual patterns used in this module use $ at the end to * match the end of the input string (so that only the last word in a multiple * word phrase will be singularized or pluralized). Therefore, the Java versions * of the regular expressions have been modified to capture all text before the * interesting characters at the end, and emit them as part of the result, so * that the entire string can be matched against a pattern once.

* * @author Florian Rosenberg */ public class Inflector { // ------------------------------------------------------------ Constructors /** *

Private constructor to avoid instantiation.

*/ private Inflector() { addPlural("$", "s", false); addPlural("(.*)$", "\\1s"); addPlural("(.*)(ax|test)is$", "\\1\\2es"); addPlural("(.*)(octop|vir)us$", "\\1\\2i"); addPlural("(.*)(alias|status)$", "\\1\\2es"); addPlural("(.*)(bu)s$", "\\1\\2ses"); addPlural("(.*)(buffal|tomat)o$", "\\1\\2oes"); addPlural("(.*)([ti])um$", "\\1\\2a"); addPlural("(.*)sis$", "\\1ses"); addPlural("(.*)(?:([^f])fe|([lr])f)$", "\\1\\3ves"); addPlural("(.*)(hive)$", "\\1\\2s"); addPlural("(.*)(tive)$", "\\1\\2s"); // Added for consistency with singular rules addPlural("(.*)([^aeiouy]|qu)y$", "\\1\\2ies"); addPlural("(.*)(series)$", "\\1\\2"); // Added for consistency with singular rules addPlural("(.*)(movie)$", "\\1\\2s"); // Added for consistency with singular rules addPlural("(.*)(x|ch|ss|sh)$", "\\1\\2es"); addPlural("(.*)(matr|vert|ind)ix|ex$", "\\1\\2ices"); addPlural("(.*)(o)$", "\\1\\2es"); // Added for consistency with singular rules addPlural("(.*)(shoe)$", "\\1\\2s"); // Added for consistency with singular rules addPlural("(.*)([m|l])ouse$", "\\1\\2ice"); addPlural("^(ox)$", "\\1en"); addPlural("(.*)(vert|ind)ex$", "\\1\\2ices"); // Added for consistency with singular rules addPlural("(.*)(matr)ix$", "\\1\\2ices"); // Added for consistency with singular rules addPlural("(.*)(quiz)$", "\\1\\2zes"); addSingular("(.*)s$", "\\1"); addSingular("(.*)(n)ews$", "\\1\\2ews"); addSingular("(.*)([ti])a$", "\\1\\2um"); addSingular("(.*)((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "\\1\\2sis"); addSingular("(.*)(^analy)ses$", "\\1\\2sis"); addSingular("(.*)([^f])ves$", "\\1\\2fe"); addSingular("(.*)(hive)s$", "\\1\\2"); addSingular("(.*)(tive)s$", "\\1\\2"); addSingular("(.*)([lr])ves$", "\\1\\2f"); addSingular("(.*)([^aeiouy]|qu)ies$", "\\1\\2y"); addSingular("(.*)(s)eries$", "\\1\\2eries"); addSingular("(.*)(m)ovies$", "\\1\\2ovie"); addSingular("(.*)(x|ch|ss|sh)es$", "\\1\\2"); addSingular("(.*)([m|l])ice$", "\\1\\2ouse"); addSingular("(.*)(bus)es$", "\\1\\2"); addSingular("(.*)(o)es$", "\\1\\2"); addSingular("(.*)(shoe)s$", "\\1\\2"); addSingular("(.*)(cris|ax|test)es$", "\\1\\2is"); addSingular("(.*)(octop|vir)i$", "\\1\\2us"); addSingular("(.*)(alias|status)es$", "\\1\\2"); addSingular("^(ox)en", "\\1"); addSingular("(.*)(vert|ind)ices$", "\\1\\2ex"); addSingular("(.*)(matr)ices$", "\\1\\2ix"); addSingular("(.*)(quiz)zes$", "\\1\\2"); addIrregular("child", "children"); addIrregular("man", "men"); addIrregular("move", "moves"); addIrregular("person", "people"); addIrregular("sex", "sexes"); addUncountable("equipment"); addUncountable("fish"); addUncountable("information"); addUncountable("money"); addUncountable("rice"); addUncountable("series"); addUncountable("sheep"); addUncountable("species"); } // -------------------------------------------------------- Static Variables /** *

The singleton instance returned by the default getInstance() * method.

*/ private transient static Inflector instance = null; /** *

List of Replacers for performing replacement operations * on matches for plural words.

*/ private List plurals = new LinkedList(); /** *

List of Replacers for performing replacement operations * on matches for addSingular words.

*/ private List singulars = new ArrayList(); /** *

List of words that represent addUncountable concepts that cannot be * pluralized or singularized.

*/ private List uncountables = new LinkedList(); // ------------------------------------------------------ Instance Variables // ---------------------------------------------------------- Static Methods /** *

Return a fully configured {@link Inflector} instance that can be used * for performing transformations.

*/ public static Inflector getInstance() { if (instance == null) { instance = new Inflector(); } return instance; } // ---------------------------------------------------------- Public Methods /** *

Convert strings to EmbeddedCamelCase. Embedded * underscores will be removed.

* * @param word Word to be converted */ public String camelize(String word) { return camelize(word, false); } /** *

Convert word strings consisting of lower case letters and * underscore characters between words into embeddedCamelCase * or EmbeddedCamelCase, depending on the lower * flag. Embedded underscores will be removed. Embedded '/' * characters will be replaced by '.', making this method useful * in converting path-like names into fully qualified classnames.

* *

IMPLEMENTATION DIFFERENCE - The Rails version of this * method also converts '/' characters to '::' because that reflects * the normal syntax for fully qualified names in Ruby.

* * * * * * * * * * * * * * * * * * * * * * *
InputOutput
"foo_bar", false"FooBar"
"foo_bar", true"fooBar"
"foo_bar/baz", false"FooBar.Baz"
"foo_bar/baz", true"fooBar.Baz"
* * @param word Word to be converted * @param flag Flag indicating that the initial character should * be lower cased instead of upper cased */ public String camelize(String word, boolean flag) { if (word.length() == 0) { return word; } StringBuffer sb = new StringBuffer(word.length()); if (flag) { sb.append(Character.toLowerCase(word.charAt(0))); } else { sb.append(Character.toUpperCase(word.charAt(0))); } boolean capitalize = false; for (int i = 1; i < word.length(); i++) { char ch = word.charAt(i); if (capitalize) { sb.append(Character.toUpperCase(ch)); capitalize = false; } else if (ch == '_') { capitalize = true; } else if (ch == '/') { capitalize = true; sb.append('.'); } else { sb.append(ch); } } return sb.toString(); } /** *

Create and return a simple class name that corresponds to a * addPlural table name. Any leading schema name will be trimmed.

* * * * * * * * * * * * * * *
InputOutput
"foo_bars""FooBar"
"baz""Baz"
* * @param tableName Table name to be converted */ public String classify(String tableName) { int period = tableName.lastIndexOf('.'); if (period >= 0) { tableName = tableName.substring(period + 1); } return camelize(singularize(tableName)); } /** *

Replace underscores in the specified word with dashes.

* * * * * * * * * * * * * * *
InputOutput
"foo_bar""foo-bar"
"baz""baz"
* * @param word Word to be converted */ public String dasherize(String word) { return word.replace('_', '-'); } /** *

Remove any package name from a fully qualified class name, * returning only the simple classname.

* * * * * * * * * * * * * * *
InputOutput
"java.util.Map""Map"
"String""String"
* * @param className Fully qualified class name to be converted */ public String demodulize(String className) { int period = className.lastIndexOf('.'); if (period >= 0) { return className.substring(period + 1); } else { return className; } } /** *

Create and return a foreign key name from a class name, * separating the "id" suffix with an underscore.

*/ public String foreignKey(String className) { return foreignKey(className, true); } /** *

Create and return a foreign key name from a class name, * optionally inserting an underscore before the "id" portion.

* * * * * * * * * * * * * * * * * * * * * * *
InputOutput
"com.mymodel.Order", false"orderid"
"com.mymodel.Order", true"order_id"
"Message", false"messageid"
"Message", true"message_id"
* * @param className Class name for which to create a foreign key * @param underscore Flag indicating whether an underscore should * be emitted between the class name and the "id" suffix */ public String foreignKey(String className, boolean underscore) { return underscore(demodulize(className) + (underscore ? "_id" : "id")); } /** *

Capitalize the first word in a lower cased and underscored string, * turn underscores into spaces, and string any trailing "_id". Like * titleize(), this is meant for creating pretty output, * and is not intended for code generation.

* * * * * * * * * * * * * * *
InputOutput
"employee_salary""Employee salary"
"author_id""Author"
* * @param words Word string to be converted */ public String humanize(String words) { if (words.endsWith("_id")) { words = words.substring(0, words.length() - 3); } StringBuffer sb = new StringBuffer(words.length()); sb.append(Character.toUpperCase(words.charAt(0))); for (int i = 1; i < words.length(); i++) { char ch = words.charAt(i); if (ch == '_') { sb.append(' '); } else { sb.append(ch); } } return sb.toString(); } /** *

Turn a number into a corresponding ordinal string used to * denote the position in an ordered sequence.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
InputOutput
1"1st"
2"2nd"
3"3rd"
4"rth"
1002"1002nd"
2012"2012th"
* * @param number Number to be converted */ public String ordinalize(int number) { int modulo = number % 100; if ((modulo >= 11) && (modulo <= 13)) { return "" + number + "th"; } switch (number % 10) { case 1: return "" + number + "st"; case 2: return "" + number + "nd"; case 3: return "" + number + "rd"; default: return "" + number + "th"; } } /** *

Return a addPlural version of the specified (addSingular) word.

* * * @param word Singular word to be converted */ public String pluralize(String word) { // Scan uncountables and leave alone for (int i = 0; i < uncountables.size(); i++) { if (uncountables.get(i).equals(word)) { return word; } } // Scan our patterns for a match and return the correct replacement for (int i = 0; i < plurals.size(); i++) { String replacement = plurals.get(i).replacement(word); if (replacement != null) { return replacement; } } // Return the original string unchanged return word; } /** *

Return a addSingular version of the specified (addPlural) word.

* * * @param word Plural word to be converted */ public String singularize(String word) { // Scan uncountables and leave alone for (int i = 0; i < uncountables.size(); i++) { if (uncountables.get(i).equals(word)) { return word; } } // Scan our patterns for a match and return the correct replacement for (int i = 0; i < singulars.size(); i++) { String replacement = singulars.get(i).replacement(word); if (replacement != null) { return replacement; } } // Return the original string unchanged return word; } /** *

Convert the simple name of a model class into the corresponding * name of a database table, by uncamelizing, inserting underscores, * and pluralizing the last word.

* * * * * * * * * * * * * * *
InputOutput
"RawScaledScorer""raw_scaled_scorers"
"fancyCategory""fancy_categories"
* * @param className Class name to be converted */ public String tableize(String className) { return pluralize(underscore(className)); } /** *

Capitalize all the words, and replace some characters in the string * to create a nicer looking title. This is meant for creating pretty * output, and is not intended for code generation.

* * * * * * * * * * * * * * *
InputOutput
"the honeymooners""The Honeymooners"
"x-men: the last stand""X Men: The Last Stand"
* * @param words Word string to be converted */ public String titleize(String words) { StringBuffer sb = new StringBuffer(words.length()); boolean capitalize = true; // To get the first character right for (int i = 0; i < words.length(); i++) { char ch = words.charAt(i); if (Character.isWhitespace(ch)) { sb.append(' '); capitalize = true; } else if (ch == '-') { sb.append(' '); capitalize = true; } else if (capitalize) { sb.append(Character.toUpperCase(ch)); capitalize = false; } else { sb.append(ch); } } return sb.toString(); } public String decapitalize(String word) { // do nothing if null or empty if ((word == null) || (word.length() < 1)) { return word; } // or if already decapitalized char first = word.charAt(0); if (Character.isLowerCase(first)) { return word; } // otherwise turn the first character to lower case and attach the rest StringBuilder sb = new StringBuilder(word.length()); sb.append(Character.toLowerCase(first)); sb.append(word.substring(1)); return sb.toString(); } /** *

The reverse of camelize(), makes an underscored form * from the expression in the string. Changes "." to "/" to convert * fully qualified class names into paths.

* * * * * * * * * * * * * * * * * * * * * * *
InputOutput
"FooBar""foo_bar"
"fooBar""foo_bar"
"FooBar.Baz""foo_bar/baz"
"FooBar.Baz""foo_bar/baz"
* * @param word Camel cased word to be converted */ public String underscore(String word) { StringBuffer sb = new StringBuffer(word.length() + 5); boolean uncapitalize = false; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); if (uncapitalize) { sb.append(Character.toLowerCase(ch)); uncapitalize = false; } else if (ch == '.') { sb.append('/'); uncapitalize = true; } else if (Character.isUpperCase(ch)) { if (i > 0) { sb.append('_'); } sb.append(Character.toLowerCase(ch)); } else { sb.append(ch); } } return sb.toString(); } // --------------------------------------------------- Customization Methods /** *

Add the addSingular and addPlural forms of words that cannot be * converted using the normal rules.

* * * @param singular Singular form of the word * @param plural Plural form of the word */ public void addIrregular(String singular, String plural) { addPlural("(.*)(" + singular.substring(0, 1) + ")" + singular.substring(1) + "$", "\\1\\2" + plural.substring(1)); addSingular("(.*)(" + plural.substring(0, 1) + ")" + plural.substring(1) + "$", "\\1\\2" + singular.substring(1)); } /** *

Add a match pattern and replacement rule for converting addPlural * forms to addSingular forms. By default, matches will be case * insensitive.

* * * @param match Match pattern regular expression * @param rule Replacement rule */ public void addPlural(String match, String rule) { addPlural(match, rule, true); } /** *

Add a match pattern and replacement rule for converting addPlural * forms to addSingular forms.

* * * @param match Match pattern regular expression * @param rule Replacement rule * @param insensitive Flag indicating this match should be case insensitive */ public void addPlural(String match, String rule, boolean insensitive) { plurals.add(0, new Replacer(match, rule, insensitive)); } /** *

Add a match pattern and replacement rule for converting addSingular * forms to addPlural forms. By default, matches will be case insensitive.

* * * @param match Match pattern regular expression * @param rule Replacement rule */ public void addSingular(String match, String rule) { addSingular(match, rule, true); } /** *

Add a match pattern and replacement rule for converting addSingular * forms to addPlural forms.

* * * @param match Match pattern regular expression * @param rule Replacement rule * @param insensitive Flag indicating this match should be case insensitive */ public void addSingular(String match, String rule, boolean insensitive) { singulars.add(0, new Replacer(match, rule, insensitive)); } /** *

Add a word that cannot be converted between addSingular and addPlural.

* * * @param word Word to be added */ public void addUncountable(String word) { uncountables.add(0, word.toLowerCase()); } // --------------------------------------------------------- Private Classes /** *

Internal class that uses a regular expression matcher to both * match the specified regular expression to a specified word, and * (if successful) perform the appropriate substitutions.

*/ private class Replacer { // --------------------------------------------------------- Constructor public Replacer(String match, String rule, boolean insensitive) { pattern = Pattern.compile(match, insensitive ? Pattern.CASE_INSENSITIVE : 0); this.rule = rule; } // -------------------------------------------------- Instance Variables private Pattern pattern = null; private String rule = null; // ------------------------------------------------------ Public Methods /** * Replace the input if it matches the pattern. * * @param input the input string. * @return the replacement, if the input matches, otherwise null. */ public String replacement(String input) { Matcher matcher = pattern.matcher(input); if (matcher.matches()) { StringBuffer sb = new StringBuffer(); boolean group = false; for (int i = 0; i < rule.length(); i++) { char ch = rule.charAt(i); if (group) { sb.append(matcher.group(Character.digit(ch, 10))); group = false; } else if (ch == '\\') { group = true; } else { sb.append(ch); } } return sb.toString(); } else { return null; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy