net.sf.saxon.expr.number.Numberer_en Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.expr.number;
/**
* Numberer class for the English language.
*/
public class Numberer_en extends AbstractNumberer {
// Separator between tens and units. Allows customisation: "twenty five", "twenty-five", "twentyfive",
// or "thirty second", "thirty-second", "thirtysecond".
private String tensUnitsSeparatorCardinal = " ";
// Separator between tens and units. Allows customisation: "thirty second", "thirty-second", "thirtysecond".
private String tensUnitsSeparatorOrdinal = "-";
/**
* Set the separator to be used between tens and units for cardinal numbers. This allows customization
* of the output, for example "thirty six", "thirty-six", or "thirtysix". Default is a single space.
* Currently the only way of calling this is from a subclass, which can be nominated by setting
* a {@link net.sf.saxon.lib.LocalizerFactory} on the {@link net.sf.saxon.Configuration}
*
* @param separator the separator to be used between tens and units when cardinal numbers are written
* as words.
*/
public void setTensUnitsSeparatorCardinal(String separator) {
tensUnitsSeparatorCardinal = separator;
}
/**
* Set the separator to be used between tens and units for ordinal numbers. This allows customization
* of the output, for example "thirty sixth", "thirty-sixth", or "thirtysixth". Default is a hyphen.
* Currently the only way of calling this is from a subclass, which can be nominated by setting
* a {@link net.sf.saxon.lib.LocalizerFactory} on the {@link net.sf.saxon.Configuration}
*
* @param separator the separator to be used between tens and units when ordinal numbers are written
* as words.
*/
public void setTensUnitsSeparatorOrdinal(String separator) {
tensUnitsSeparatorOrdinal = separator;
}
/**
* Set the language used by this numberer. Useful because it can potentially handle variants of English
* (and subclasses can handle other languages)
*
* @param language the requested language. Note that "en-x-hyphen" is recognized as a request to hyphenate
* numbers in the range 21-99.
*/
@Override
public void setLanguage(String language) {
super.setLanguage(language);
if (language.endsWith("-x-hyphen")) {
setTensUnitsSeparatorOrdinal("-");
setTensUnitsSeparatorCardinal("-");
} else if (language.endsWith("-x-nohyphen")) {
setTensUnitsSeparatorOrdinal(" ");
setTensUnitsSeparatorCardinal(" ");
}
}
/**
* Construct the ordinal suffix for a number, for example "st", "nd", "rd"
*
* @param ordinalParam the value of the ordinal attribute (used in non-English
* language implementations)
* @param number the number being formatted
* @return the ordinal suffix to be appended to the formatted number
*/
@Override
protected String ordinalSuffix(String ordinalParam, long number) {
int penult = (int) (number % 100) / 10;
int ult = (int) (number % 10);
if (penult == 1) {
// e.g. 11th, 12th, 13th
return "th";
} else {
if (ult == 1) {
return "st";
} else if (ult == 2) {
return "nd";
} else if (ult == 3) {
return "rd";
} else {
return "th";
}
}
}
/**
* Show the number as words in title case. (We choose title case because
* the result can then be converted algorithmically to lower case or upper case).
*
* @param cardinal
* @param number the number to be formatted
* @return the number formatted as English words
*/
@Override
public String toWords(String cardinal, long number) {
if (number >= 1000000000) {
long rem = number % 1000000000;
return toWords(cardinal, number / 1000000000) + " Billion" +
(rem == 0 ? "" : (rem < 100 ? " and " : " ") + toWords(cardinal, rem));
} else if (number >= 1000000) {
long rem = number % 1000000;
return toWords(cardinal, number / 1000000) + " Million" +
(rem == 0 ? "" : (rem < 100 ? " and " : " ") + toWords(cardinal, rem));
} else if (number >= 1000) {
long rem = number % 1000;
return toWords(cardinal, number / 1000) + " Thousand" +
(rem == 0 ? "" : (rem < 100 ? " and " : " ") + toWords(cardinal, rem));
} else if (number >= 100) {
long rem = number % 100;
return toWords(cardinal, number / 100) + " Hundred" +
(rem == 0 ? "" : " and " + toWords(cardinal, rem));
} else {
if (number < 20) {
return englishUnits[(int) number];
}
int rem = (int) (number % 10);
return englishTens[(int) number / 10] +
(rem == 0 ? "" : tensUnitsSeparatorCardinal + englishUnits[rem]);
}
}
/**
* Show an ordinal number as English words in a requested case (for example, Twentyfirst)
*
* @param ordinalParam the value of the "ordinal" attribute as supplied by the user
* @param number the number to be formatted
* @param wordCase the required case for example {@link #UPPER_CASE},
* {@link #LOWER_CASE}, {@link #TITLE_CASE}
* @return the formatted number
*/
@Override
public String toOrdinalWords(String ordinalParam, long number, int wordCase) {
String s;
if (number >= 1000000000) {
long rem = number % 1000000000;
s = toWords(ordinalParam, number / 1000000000) + " Billion" +
(rem == 0 ? "th" : (rem < 100 ? " and " : " ") +
toOrdinalWords(ordinalParam, rem, wordCase));
} else if (number >= 1000000) {
long rem = number % 1000000;
s = toWords(ordinalParam, number / 1000000) + " Million" +
(rem == 0 ? "th" : (rem < 100 ? " and " : " ") +
toOrdinalWords(ordinalParam, rem, wordCase));
} else if (number >= 1000) {
long rem = number % 1000;
s = toWords(ordinalParam, number / 1000) + " Thousand" +
(rem == 0 ? "th" : (rem < 100 ? " and " : " ") +
toOrdinalWords(ordinalParam, rem, wordCase));
} else if (number >= 100) {
long rem = number % 100;
s = toWords(ordinalParam, number / 100) + " Hundred" +
(rem == 0 ? "th" : " and " +
toOrdinalWords(ordinalParam, rem, wordCase));
} else {
if (number < 20) {
s = englishOrdinalUnits[(int) number];
} else {
int rem = (int) (number % 10);
if (rem == 0) {
s = englishOrdinalTens[(int) number / 10];
} else {
s = englishTens[(int) number / 10] + tensUnitsSeparatorOrdinal + englishOrdinalUnits[rem];
}
}
}
if (wordCase == UPPER_CASE) {
return s.toUpperCase();
} else if (wordCase == LOWER_CASE) {
return s.toLowerCase();
} else {
return s;
}
}
private static final String[] englishUnits = {
"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine",
"Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen",
"Seventeen", "Eighteen", "Nineteen"};
private static final String[] englishTens = {
"", "Ten", "Twenty", "Thirty", "Forty", "Fifty",
"Sixty", "Seventy", "Eighty", "Ninety"};
private static final String[] englishOrdinalUnits = {
"Zeroth", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth", "Ninth",
"Tenth", "Eleventh", "Twelfth", "Thirteenth", "Fourteenth", "Fifteenth", "Sixteenth",
"Seventeenth", "Eighteenth", "Nineteenth"};
private static final String[] englishOrdinalTens = {
"", "Tenth", "Twentieth", "Thirtieth", "Fortieth", "Fiftieth",
"Sixtieth", "Seventieth", "Eightieth", "Ninetieth"};
/**
* Get a month name or abbreviation
*
* @param month The month number (1=January, 12=December)
* @param minWidth The minimum number of characters
* @param maxWidth The maximum number of characters
*/
@Override
public String monthName(int month, int minWidth, int maxWidth) {
String name = englishMonths[month - 1];
if (maxWidth < 3) {
maxWidth = 3;
}
if (name.length() > maxWidth) {
name = name.substring(0, maxWidth);
}
StringBuilder nameBuilder = new StringBuilder(name);
while (nameBuilder.length() < minWidth) {
nameBuilder.append(' ');
}
name = nameBuilder.toString();
return name;
}
private static final String[] englishMonths = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
/**
* Get a day name or abbreviation
*
* @param day The day of the week (1=Monday, 7=Sunday)
* @param minWidth The minimum number of characters
* @param maxWidth The maximum number of characters
*/
@Override
public String dayName(int day, int minWidth, int maxWidth) {
String name = englishDays[day - 1];
if (maxWidth < 2) {
maxWidth = 2;
}
if (name.length() > maxWidth) {
name = englishDayAbbreviations[day - 1];
if (name.length() > maxWidth) {
name = name.substring(0, maxWidth);
}
}
StringBuilder nameBuilder = new StringBuilder(name);
while (nameBuilder.length() < minWidth) {
nameBuilder.append(' ');
}
name = nameBuilder.toString();
if (minWidth == 1 && maxWidth == 2) {
// special case
name = name.substring(0, minUniqueDayLength[day - 1]);
}
return name;
}
private static final String[] englishDays = {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
};
private static final String[] englishDayAbbreviations = {
"Mon", "Tues", "Weds", "Thurs", "Fri", "Sat", "Sun"
};
/*@NotNull*/ private static final int[] minUniqueDayLength = {
1, 2, 1, 2, 1, 2, 2
};
}