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

org.glassfish.jersey.message.internal.NounInflector Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.7
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2012 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 org.glassfish.jersey.message.internal;

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 {@code 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 {@code addIrregular()}, {@code addPlural()}, {@code addSingular()}, * and {@code addUncountable()} methods to add additional rules ot the default * ones.

* *

IMPLEMENTATION NOTE - The default implementation is * intended to be functionally compatible with the {@code Inflector::inflections} * class in Ruby on Rails. The {@code gsub()} method on Ruby strings * matches regular expressions anywhere in the input. However, nearly all of * the actual patterns used in this component use {@code $} 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 */ final class NounInflector { // ------------------------------------------------------------ Constructors /** *

Private constructor to avoid instantiation.

*/ private NounInflector() { 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 NounInflector 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 NounInflector} instance that can be used * for performing transformations.

*/ public static NounInflector getInstance() { if (instance == null) { instance = new NounInflector(); } 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; } StringBuilder sb = new StringBuilder(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); } StringBuilder sb = new StringBuilder(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) { StringBuilder sb = new StringBuilder(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) { StringBuilder sb = new StringBuilder(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 static 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()) { StringBuilder sb = new StringBuilder(); 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