/*
* Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.jaxb.internal;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
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 component 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
/**
* <p>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.</p>
*
* <p>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.</p>
*
* <p><strong>IMPLEMENTATION NOTE</strong> - 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.</p>
*
* @author Florian Rosenberg
*/
final class NounInflector {
// ------------------------------------------------------------ Constructors
Private constructor to avoid instantiation.
/**
* <p>Private constructor to avoid instantiation.</p>
*/
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.
/**
* <p>The singleton instance returned by the default <code>getInstance()</code>
* method.</p>
*/
private static transient NounInflector instance = null;
List of Replacer
s for performing replacement operations
on matches for plural words.
/**
* <p>List of <code>Replacer</code>s for performing replacement operations
* on matches for plural words.</p>
*/
private final List<Replacer> plurals = new LinkedList<Replacer>();
List of Replacer
s for performing replacement operations
on matches for addSingular words.
/**
* <p>List of <code>Replacer</code>s for performing replacement operations
* on matches for addSingular words.</p>
*/
private final List<Replacer> singulars = new ArrayList<Replacer>();
List of words that represent addUncountable concepts that cannot be
pluralized or singularized.
/**
* <p>List of words that represent addUncountable concepts that cannot be
* pluralized or singularized.</p>
*/
private final List<String> uncountables = new LinkedList<String>();
// ------------------------------------------------------ Instance Variables
// ---------------------------------------------------------- Static Methods
Return a fully configured NounInflector
instance that can be used for performing transformations.
/**
* <p>Return a fully configured {@link NounInflector} instance that can be used
* for performing transformations.</p>
*/
public static NounInflector getInstance() {
if (instance == null) {
instance = new NounInflector();
}
return instance;
}
// ---------------------------------------------------------- Public Methods
Convert strings to EmbeddedCamelCase
. Embedded
underscores will be removed.
Params: - word – Word to be converted
/**
* <p>Convert strings to <code>EmbeddedCamelCase</code>. Embedded
* underscores will be removed.</p>
*
* @param word Word to be converted
*/
public String camelize(final 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.
Input
Output
"foo_bar", false
"FooBar"
"foo_bar", true
"fooBar"
"foo_bar/baz", false
"FooBar.Baz"
"foo_bar/baz", true
"fooBar.Baz"
Params: - word – Word to be converted
- flag – Flag indicating that the initial character should
be lower cased instead of upper cased
/**
* <p>Convert word strings consisting of lower case letters and
* underscore characters between words into <code>embeddedCamelCase</code>
* or <code>EmbeddedCamelCase</code>, depending on the <code>lower</code>
* 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.</p>
*
* <p><strong>IMPLEMENTATION DIFFERENCE</strong> - The Rails version of this
* method also converts '/' characters to '::' because that reflects
* the normal syntax for fully qualified names in Ruby.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"foo_bar", false</td>
* <td>"FooBar"</td>
* </tr>
* <tr>
* <td>"foo_bar", true</td>
* <td>"fooBar"</td>
* </tr>
* <tr>
* <td>"foo_bar/baz", false</td>
* <td>"FooBar.Baz"</td>
* </tr>
* <tr>
* <td>"foo_bar/baz", true</td>
* <td>"fooBar.Baz"</td>
* </tr>
* </table>
*
* @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(final String word, final boolean flag) {
if (word.length() == 0) {
return word;
}
final 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++) {
final 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.
Input
Output
"foo_bars"
"FooBar"
"baz"
"Baz"
Params: - tableName – Table name to be converted
/**
* <p>Create and return a simple class name that corresponds to a
* addPlural table name. Any leading schema name will be trimmed.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"foo_bars"</td>
* <td>"FooBar"</td>
* </tr>
* <tr>
* <td>"baz"</td>
* <td>"Baz"</td>
* </tr>
* </table>
*
* @param tableName Table name to be converted
*/
public String classify(String tableName) {
final int period = tableName.lastIndexOf('.');
if (period >= 0) {
tableName = tableName.substring(period + 1);
}
return camelize(singularize(tableName));
}
Replace underscores in the specified word with dashes.
Input
Output
"foo_bar"
"foo-bar"
"baz"
"baz"
Params: - word – Word to be converted
/**
* <p>Replace underscores in the specified word with dashes.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"foo_bar"</td>
* <td>"foo-bar"</td>
* </tr>
* <tr>
* <td>"baz"</td>
* <td>"baz"</td>
* </tr>
* </table>
*
* @param word Word to be converted
*/
public String dasherize(final String word) {
return word.replace('_', '-');
}
Remove any package name from a fully qualified class name,
returning only the simple classname.
Input
Output
"java.util.Map"
"Map"
"String"
"String"
Params: - className – Fully qualified class name to be converted
/**
* <p>Remove any package name from a fully qualified class name,
* returning only the simple classname.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"java.util.Map"</td>
* <td>"Map"</td>
* </tr>
* <tr>
* <td>"String"</td>
* <td>"String"</td>
* </tr>
* </table>
*
* @param className Fully qualified class name to be converted
*/
public String demodulize(final String className) {
final 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.
/**
* <p>Create and return a foreign key name from a class name,
* separating the "id" suffix with an underscore.</p>
*/
public String foreignKey(final 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.
Input
Output
"com.mymodel.Order", false
"orderid"
"com.mymodel.Order", true
"order_id"
"Message", false
"messageid"
"Message", true
"message_id"
Params: - className – Class name for which to create a foreign key
- underscore – Flag indicating whether an underscore should
be emitted between the class name and the "id" suffix
/**
* <p>Create and return a foreign key name from a class name,
* optionally inserting an underscore before the "id" portion.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"com.mymodel.Order", false</td>
* <td>"orderid"</td>
* </tr>
* <tr>
* <td>"com.mymodel.Order", true</td>
* <td>"order_id"</td>
* </tr>
* <tr>
* <td>"Message", false</td>
* <td>"messageid"</td>
* </tr>
* <tr>
* <td>"Message", true</td>
* <td>"message_id"</td>
* </tr>
* </table>
*
* @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(final String className, final 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.
Input
Output
"employee_salary"
"Employee salary"
"author_id"
"Author"
Params: - words – Word string to be converted
/**
* <p>Capitalize the first word in a lower cased and underscored string,
* turn underscores into spaces, and string any trailing "_id". Like
* <code>titleize()</code>, this is meant for creating pretty output,
* and is not intended for code generation.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"employee_salary"</td>
* <td>"Employee salary"</td>
* </tr>
* <tr>
* <td>"author_id"</td>
* <td>"Author"</td>
* </tr>
* </table>
*
* @param words Word string to be converted
*/
public String humanize(String words) {
if (words.endsWith("_id")) {
words = words.substring(0, words.length() - 3);
}
final StringBuilder sb = new StringBuilder(words.length());
sb.append(Character.toUpperCase(words.charAt(0)));
for (int i = 1; i < words.length(); i++) {
final 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.
Input
Output
1
"1st"
2
"2nd"
3
"3rd"
4
"rth"
1002
"1002nd"
2012
"2012th"
Params: - number – Number to be converted
/**
* <p>Turn a number into a corresponding ordinal string used to
* denote the position in an ordered sequence.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>1</td>
* <td>"1st"</td>
* </tr>
* <tr>
* <td>2</td>
* <td>"2nd"</td>
* </tr>
* <tr>
* <td>3</td>
* <td>"3rd"</td>
* </tr>
* <tr>
* <td>4</td>
* <td>"rth"</td>
* </tr>
* <tr>
* <td>1002</td>
* <td>"1002nd"</td>
* </tr>
* <tr>
* <td>2012</td>
* <td>"2012th"</td>
* </tr>
* </table>
*
* @param number Number to be converted
*/
public String ordinalize(final int number) {
final 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.
Params: - word – Singular word to be converted
/**
* <p>Return a addPlural version of the specified (addSingular) word.</p>
*
*
* @param word Singular word to be converted
*/
public String pluralize(final String word) {
// Scan uncountables and leave alone
for (final String uncountable : uncountables) {
if (uncountable.equals(word)) {
return word;
}
}
// Scan our patterns for a match and return the correct replacement
for (final Replacer plural : plurals) {
final String replacement = plural.replacement(word);
if (replacement != null) {
return replacement;
}
}
// Return the original string unchanged
return word;
}
Return a addSingular version of the specified (addPlural) word.
Params: - word – Plural word to be converted
/**
* <p>Return a addSingular version of the specified (addPlural) word.</p>
*
*
* @param word Plural word to be converted
*/
public String singularize(final String word) {
// Scan uncountables and leave alone
for (final String uncountable : uncountables) {
if (uncountable.equals(word)) {
return word;
}
}
// Scan our patterns for a match and return the correct replacement
for (final Replacer singular : singulars) {
final String replacement = singular.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.
Input
Output
"RawScaledScorer"
"raw_scaled_scorers"
"fancyCategory"
"fancy_categories"
Params: - className – Class name to be converted
/**
* <p>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.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"RawScaledScorer"</td>
* <td>"raw_scaled_scorers"</td>
* </tr>
* <tr>
* <td>"fancyCategory"</td>
* <td>"fancy_categories"</td>
* </tr>
* </table>
*
* @param className Class name to be converted
*/
public String tableize(final 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.
Input
Output
"the honeymooners"
"The Honeymooners"
"x-men: the last stand"
"X Men: The Last Stand"
Params: - words – Word string to be converted
/**
* <p>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.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"the honeymooners"</td>
* <td>"The Honeymooners"</td>
* </tr>
* <tr>
* <td>"x-men: the last stand"</td>
* <td>"X Men: The Last Stand"</td>
* </tr>
* </table>
*
* @param words Word string to be converted
*/
public String titleize(final String words) {
final StringBuilder sb = new StringBuilder(words.length());
boolean capitalize = true; // To get the first character right
for (int i = 0; i < words.length(); i++) {
final 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(final String word) {
// do nothing if null or empty
if ((word == null) || (word.length() < 1)) {
return word;
}
// or if already decapitalized
final char first = word.charAt(0);
if (Character.isLowerCase(first)) {
return word;
}
// otherwise turn the first character to lower case and attach the rest
final 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.
Input
Output
"FooBar"
"foo_bar"
"fooBar"
"foo_bar"
"FooBar.Baz"
"foo_bar/baz"
"FooBar.Baz"
"foo_bar/baz"
Params: - word – Camel cased word to be converted
/**
* <p>The reverse of <code>camelize()</code>, makes an underscored form
* from the expression in the string. Changes "." to "/" to convert
* fully qualified class names into paths.</p>
*
* <table border="1" width="100%">
* <tr>
* <th>Input</th>
* <th>Output</th>
* </tr>
* <tr>
* <td>"FooBar"</td>
* <td>"foo_bar"</td>
* </tr>
* <tr>
* <td>"fooBar"</td>
* <td>"foo_bar"</td>
* </tr>
* <tr>
* <td>"FooBar.Baz"</td>
* <td>"foo_bar/baz"</td>
* </tr>
* <tr>
* <td>"FooBar.Baz"</td>
* <td>"foo_bar/baz"</td>
* </tr>
* </table>
*
* @param word Camel cased word to be converted
*/
public String underscore(final String word) {
final StringBuilder sb = new StringBuilder(word.length() + 5);
boolean uncapitalize = false;
for (int i = 0; i < word.length(); i++) {
final 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.
Params: - singular – Singular form of the word
- plural – Plural form of the word
/**
* <p>Add the addSingular and addPlural forms of words that cannot be
* converted using the normal rules.</p>
*
*
* @param singular Singular form of the word
* @param plural Plural form of the word
*/
public void addIrregular(final String singular, final 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.
Params: - match – Match pattern regular expression
- rule – Replacement rule
/**
* <p>Add a match pattern and replacement rule for converting addPlural
* forms to addSingular forms. By default, matches will be case
* insensitive.</p>
*
*
* @param match Match pattern regular expression
* @param rule Replacement rule
*/
public void addPlural(final String match, final String rule) {
addPlural(match, rule, true);
}
Add a match pattern and replacement rule for converting addPlural
forms to addSingular forms.
Params: - match – Match pattern regular expression
- rule – Replacement rule
- insensitive – Flag indicating this match should be case insensitive
/**
* <p>Add a match pattern and replacement rule for converting addPlural
* forms to addSingular forms.</p>
*
*
* @param match Match pattern regular expression
* @param rule Replacement rule
* @param insensitive Flag indicating this match should be case insensitive
*/
public void addPlural(final String match, final String rule, final 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.
Params: - match – Match pattern regular expression
- rule – Replacement rule
/**
* <p>Add a match pattern and replacement rule for converting addSingular
* forms to addPlural forms. By default, matches will be case insensitive.</p>
*
*
* @param match Match pattern regular expression
* @param rule Replacement rule
*/
public void addSingular(final String match, final String rule) {
addSingular(match, rule, true);
}
Add a match pattern and replacement rule for converting addSingular
forms to addPlural forms.
Params: - match – Match pattern regular expression
- rule – Replacement rule
- insensitive – Flag indicating this match should be case insensitive
/**
* <p>Add a match pattern and replacement rule for converting addSingular
* forms to addPlural forms.</p>
*
*
* @param match Match pattern regular expression
* @param rule Replacement rule
* @param insensitive Flag indicating this match should be case insensitive
*/
public void addSingular(final String match, final String rule, final boolean insensitive) {
singulars.add(0, new Replacer(match, rule, insensitive));
}
Add a word that cannot be converted between addSingular and addPlural.
Params: - word – Word to be added
/**
* <p>Add a word that cannot be converted between addSingular and addPlural.</p>
*
*
* @param word Word to be added
*/
public void addUncountable(final String word) {
uncountables.add(0, word.toLowerCase(Locale.ROOT));
}
// --------------------------------------------------------- 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.
/**
* <p>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.</p>
*/
private static class Replacer {
// --------------------------------------------------------- Constructor
public Replacer(final String match, final String rule, final 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.
Params: - input – the input string.
Returns: the replacement, if the input matches, otherwise null.
/**
* 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(final String input) {
final Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
final StringBuilder sb = new StringBuilder();
boolean group = false;
for (int i = 0; i < rule.length(); i++) {
final 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;
}
}
}
}