12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.complexscripts.util;
-
- import java.util.ArrayList;
- import java.util.List;
-
- // CSOFF: LineLengthCheck
-
- /**
- * <p>Implementation of Number to String Conversion algorithm specified by
- * XSL Transformations (XSLT) Version 2.0, W3C Recommendation, 23 January 2007.</p>
- *
- * <p>This algorithm differs from that specified in XSLT 1.0 in the following
- * ways:</p>
- * <ul>
- * <li>input numbers are greater than or equal to zero rather than greater than zero;</li>
- * <li>introduces format tokens { w, W, Ww };</li>
- * <li>introduces ordinal parameter to generate ordinal numbers;</li>
- * </ul>
- *
- * <p>Implementation Defaults and Limitations</p>
- * <ul>
- * <li>If language parameter is unspecified (null or empty string), then the value
- * of DEFAULT_LANGUAGE is used, which is defined below as "eng" (English).</li>
- * <li>Only English, French, and Spanish word numerals are supported, and only if less than one trillion (1,000,000,000,000).</li>
- * <li>Ordinal word numerals are supported for French and Spanish only when less than or equal to ten (10).</li>
- * </ul>
- *
- * <p>Implementation Notes</p>
- * <ul>
- * <li>In order to handle format tokens outside the Unicode BMP, all processing is
- * done in Unicode Scalar Values represented with Integer and Integer[]
- * types. Without affecting behavior, this may be subsequently optimized to
- * use int and int[] types.</li>
- * <li>In order to communicate various sub-parameters, including ordinalization, a <em>features</em>
- * is employed, which consists of comma separated name and optional value tokens, where name and value
- * are separated by an equals '=' sign.</li>
- * <li>Ordinal numbers are selected by specifying a word based format token in combination with a 'ordinal' feature with no value, in which case
- * the features 'male' and 'female' may be used to specify gender for gender sensitive languages. For example, the feature string "ordinal,female"
- * selects female ordinals.</li>
- * </ul>
- *
- * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
- */
- public class NumberConverter {
-
- /** alphabetical */
- public static final int LETTER_VALUE_ALPHABETIC = 1;
- /** traditional */
- public static final int LETTER_VALUE_TRADITIONAL = 2;
-
- /** no token type */
- private static final int TOKEN_NONE = 0;
- /** alhphanumeric token type */
- private static final int TOKEN_ALPHANUMERIC = 1;
- /** nonalphanumeric token type */
- private static final int TOKEN_NONALPHANUMERIC = 2;
- /** default token */
- private static final Integer[] DEFAULT_TOKEN = new Integer[] { (int) '1' };
- /** default separator */
- private static final Integer[] DEFAULT_SEPARATOR = new Integer[] { (int) '.' };
- /** default language */
- private static final String DEFAULT_LANGUAGE = "eng";
-
- /** prefix token */
- private Integer[] prefix;
- /** suffix token */
- private Integer[] suffix;
- /** sequence of tokens, as parsed from format */
- private Integer[][] tokens;
- /** sequence of separators, as parsed from format */
- private Integer[][] separators;
- /** grouping separator */
- private int groupingSeparator;
- /** grouping size */
- private int groupingSize;
- /** letter value */
- private int letterValue;
- /** letter value system */
- private String features;
- /** language */
- private String language;
- /** country */
- private String country;
-
- /**
- * Construct parameterized number converter.
- * @param format format for the page number (may be null or empty, which is treated as null)
- * @param groupingSeparator grouping separator (if zero, then no grouping separator applies)
- * @param groupingSize grouping size (if zero or negative, then no grouping size applies)
- * @param letterValue letter value (must be one of the above letter value enumeration values)
- * @param features features (feature sub-parameters)
- * @param language (may be null or empty, which is treated as null)
- * @param country (may be null or empty, which is treated as null)
- * @throws IllegalArgumentException if format is not a valid UTF-16 string (e.g., has unpaired surrogate)
- */
- public NumberConverter(String format, int groupingSeparator, int groupingSize, int letterValue, String features, String language, String country)
- throws IllegalArgumentException {
- this.groupingSeparator = groupingSeparator;
- this.groupingSize = groupingSize;
- this.letterValue = letterValue;
- this.features = features;
- this.language = (language != null) ? language.toLowerCase() : null;
- this.country = (country != null) ? country.toLowerCase() : null;
- parseFormatTokens(format);
- }
-
- /**
- * Convert a number to string according to conversion parameters.
- * @param number number to conver
- * @return string representing converted number
- */
- public String convert(long number) {
- List<Long> numbers = new ArrayList<Long>();
- numbers.add(number);
- return convert(numbers);
- }
-
- /**
- * Convert list of numbers to string according to conversion parameters.
- * @param numbers list of numbers to convert
- * @return string representing converted list of numbers
- */
- public String convert(List<Long> numbers) {
- List<Integer> scalars = new ArrayList<Integer>();
- if (prefix != null) {
- appendScalars(scalars, prefix);
- }
- convertNumbers(scalars, numbers);
- if (suffix != null) {
- appendScalars(scalars, suffix);
- }
- return scalarsToString(scalars);
- }
-
- private void parseFormatTokens(String format) throws IllegalArgumentException {
- List<Integer[]> tokens = new ArrayList<Integer[]>();
- List<Integer[]> separators = new ArrayList<Integer[]>();
- if ((format == null) || (format.length() == 0)) {
- format = "1";
- }
- int tokenType = TOKEN_NONE;
- List<Integer> token = new ArrayList<Integer>();
- Integer[] ca = UTF32.toUTF32(format, 0, true);
- for (int i = 0, n = ca.length; i < n; i++) {
- int c = ca[i];
- int tokenTypeNew = isAlphaNumeric(c) ? TOKEN_ALPHANUMERIC : TOKEN_NONALPHANUMERIC;
- if (tokenTypeNew != tokenType) {
- if (token.size() > 0) {
- if (tokenType == TOKEN_ALPHANUMERIC) {
- tokens.add(token.toArray(new Integer [ token.size() ]));
- } else {
- separators.add(token.toArray(new Integer [ token.size() ]));
- }
- token.clear();
- }
- tokenType = tokenTypeNew;
- }
- token.add(c);
- }
- if (token.size() > 0) {
- if (tokenType == TOKEN_ALPHANUMERIC) {
- tokens.add(token.toArray(new Integer [ token.size() ]));
- } else {
- separators.add(token.toArray(new Integer [ token.size() ]));
- }
- }
- if (!separators.isEmpty()) {
- this.prefix = separators.remove(0);
- }
- if (!separators.isEmpty()) {
- this.suffix = separators.remove(separators.size() - 1);
- }
- this.separators = separators.toArray(new Integer [ separators.size() ] []);
- this.tokens = tokens.toArray(new Integer [ tokens.size() ] []);
- }
-
- private static boolean isAlphaNumeric(int c) {
- switch (Character.getType(c)) {
- case Character.DECIMAL_DIGIT_NUMBER: // Nd
- case Character.LETTER_NUMBER: // Nl
- case Character.OTHER_NUMBER: // No
- case Character.UPPERCASE_LETTER: // Lu
- case Character.LOWERCASE_LETTER: // Ll
- case Character.TITLECASE_LETTER: // Lt
- case Character.MODIFIER_LETTER: // Lm
- case Character.OTHER_LETTER: // Lo
- return true;
- default:
- return false;
- }
- }
-
- private void convertNumbers(List<Integer> scalars, List<Long> numbers) {
- Integer[] tknLast = DEFAULT_TOKEN;
- int tknIndex = 0;
- int tknCount = tokens.length;
- int sepIndex = 0;
- int sepCount = separators.length;
- int numIndex = 0;
- for (Long number : numbers) {
- Integer[] sep = null;
- Integer[] tkn;
- if (tknIndex < tknCount) {
- if (numIndex > 0) {
- if (sepIndex < sepCount) {
- sep = separators [ sepIndex++ ];
- } else {
- sep = DEFAULT_SEPARATOR;
- }
- }
- tkn = tokens [ tknIndex++ ];
- } else {
- tkn = tknLast;
- }
- appendScalars(scalars, convertNumber(number, sep, tkn));
- tknLast = tkn;
- numIndex++;
- }
- }
-
- private Integer[] convertNumber(long number, Integer[] separator, Integer[] token) {
- List<Integer> sl = new ArrayList<Integer>();
- if (separator != null) {
- appendScalars(sl, separator);
- }
- if (token != null) {
- appendScalars(sl, formatNumber(number, token));
- }
- return sl.toArray(new Integer [ sl.size() ]);
- }
-
- private Integer[] formatNumber(long number, Integer[] token) {
- Integer[] fn = null;
- assert token.length > 0;
- if (number < 0) {
- throw new IllegalArgumentException("number must be non-negative");
- } else if (token.length == 1) {
- int s = token[0].intValue();
- switch (s) {
- case (int) '1':
- fn = formatNumberAsDecimal(number, (int) '1', 1);
- break;
- case (int) 'W':
- case (int) 'w':
- fn = formatNumberAsWord(number, (s == (int) 'W') ? Character.UPPERCASE_LETTER : Character.LOWERCASE_LETTER);
- break;
- case (int) 'A': // handled as numeric sequence
- case (int) 'a': // handled as numeric sequence
- case (int) 'I': // handled as numeric special
- case (int) 'i': // handled as numeric special
- default:
- if (isStartOfDecimalSequence(s)) {
- fn = formatNumberAsDecimal(number, s, 1);
- } else if (isStartOfAlphabeticSequence(s)) {
- fn = formatNumberAsSequence(number, s, getSequenceBase(s), null);
- } else if (isStartOfNumericSpecial(s)) {
- fn = formatNumberAsSpecial(number, s);
- } else {
- fn = null;
- }
- break;
- }
- } else if ((token.length == 2) && (token[0] == (int) 'W') && (token[1] == (int) 'w')) {
- fn = formatNumberAsWord(number, Character.TITLECASE_LETTER);
- } else if (isPaddedOne(token)) {
- int s = token [ token.length - 1 ].intValue();
- fn = formatNumberAsDecimal(number, s, token.length);
- } else {
- throw new IllegalArgumentException("invalid format token: \"" + UTF32.fromUTF32(token) + "\"");
- }
- if (fn == null) {
- fn = formatNumber(number, DEFAULT_TOKEN);
- }
- assert fn != null;
- return fn;
- }
-
- /**
- * Format NUMBER as decimal using characters denoting digits that start at ONE,
- * adding one or more (zero) padding characters as needed to fill out field WIDTH.
- * @param number to be formatted
- * @param one unicode scalar value denoting numeric value 1
- * @param width non-negative integer denoting field width of number, possible including padding
- * @return formatted number as array of unicode scalars
- */
- private Integer[] formatNumberAsDecimal(long number, int one, int width) {
- assert Character.getNumericValue(one) == 1;
- assert Character.getNumericValue(one - 1) == 0;
- assert Character.getNumericValue(one + 8) == 9;
- List<Integer> sl = new ArrayList<Integer>();
- int zero = one - 1;
- while (number > 0) {
- long digit = number % 10;
- sl.add(0, zero + (int) digit);
- number = number / 10;
- }
- while (width > sl.size()) {
- sl.add(0, zero);
- }
- if ((groupingSize != 0) && (groupingSeparator != 0)) {
- sl = performGrouping(sl, groupingSize, groupingSeparator);
- }
- return sl.toArray(new Integer [ sl.size() ]);
- }
-
- private static List<Integer> performGrouping(List<Integer> sl, int groupingSize, int groupingSeparator) {
- assert groupingSize > 0;
- assert groupingSeparator != 0;
- if (sl.size() > groupingSize) {
- List<Integer> gl = new ArrayList<Integer>();
- for (int i = 0, n = sl.size(), g = 0; i < n; i++) {
- int k = n - i - 1;
- if (g == groupingSize) {
- gl.add(0, groupingSeparator);
- g = 1;
- } else {
- g++;
- }
- gl.add(0, sl.get(k));
- }
- return gl;
- } else {
- return sl;
- }
- }
-
-
- /**
- * Format NUMBER as using sequence of characters that start at ONE, and
- * having BASE radix.
- * @param number to be formatted
- * @param one unicode scalar value denoting start of sequence (numeric value 1)
- * @param base number of elements in sequence
- * @param map if non-null, then maps sequences indices to unicode scalars
- * @return formatted number as array of unicode scalars
- */
- private Integer[] formatNumberAsSequence(long number, int one, int base, int[] map) {
- assert base > 1;
- assert (map == null) || (map.length >= base);
- List<Integer> sl = new ArrayList<Integer>();
- if (number == 0) {
- return null;
- } else {
- long n = number;
- while (n > 0) {
- int d = (int) ((n - 1) % (long) base);
- int s = (map != null) ? map [ d ] : (one + d);
- sl.add(0, s);
- n = (n - 1) / base;
- }
- return sl.toArray(new Integer [ sl.size() ]);
- }
- }
-
- /**
- * Format NUMBER as using special system that starts at ONE.
- * @param number to be formatted
- * @param one unicode scalar value denoting start of system (numeric value 1)
- * @return formatted number as array of unicode scalars
- */
- private Integer[] formatNumberAsSpecial(long number, int one) {
- SpecialNumberFormatter f = getSpecialFormatter(one, letterValue, features, language, country);
- if (f != null) {
- return f.format(number, one, letterValue, features, language, country);
- } else {
- return null;
- }
- }
-
- /**
- * Format NUMBER as word according to TYPE, which must be either
- * Character.UPPERCASE_LETTER, Character.LOWERCASE_LETTER, or
- * Character.TITLECASE_LETTER. Makes use of this.language to
- * determine language of word.
- * @param number to be formatted
- * @param caseType unicode character type for case conversion
- * @return formatted number as array of unicode scalars
- */
- private Integer[] formatNumberAsWord(long number, int caseType) {
- SpecialNumberFormatter f = null;
- if (isLanguage("eng")) {
- f = new EnglishNumberAsWordFormatter(caseType);
- } else if (isLanguage("spa")) {
- f = new SpanishNumberAsWordFormatter(caseType);
- } else if (isLanguage("fra")) {
- f = new FrenchNumberAsWordFormatter(caseType);
- } else {
- f = new EnglishNumberAsWordFormatter(caseType);
- }
- return f.format(number, 0, letterValue, features, language, country);
- }
-
- private boolean isLanguage(String iso3Code) {
- if (language == null) {
- return false;
- } else if (language.equals(iso3Code)) {
- return true;
- } else {
- return isSameLanguage(iso3Code, language);
- }
- }
-
- private static String[][] equivalentLanguages = {
- { "eng", "en" },
- { "fra", "fre", "fr" },
- { "spa", "es" },
- };
-
- private static boolean isSameLanguage(String i3c, String lc) {
- for (String[] el : equivalentLanguages) {
- assert el.length >= 2;
- if (el[0].equals(i3c)) {
- for (int i = 0, n = el.length; i < n; i++) {
- if (el[i].equals(lc)) {
- return true;
- }
- }
- return false;
- }
- }
- return false;
- }
-
- private static boolean hasFeature(String features, String feature) {
- if (features != null) {
- assert feature != null;
- assert feature.length() != 0;
- String[] fa = features.split(",");
- for (String f : fa) {
- String[] fp = f.split("=");
- assert fp.length > 0;
- String fn = fp[0];
- String fv = (fp.length > 1) ? fp[1] : "";
- if (fn.equals(feature)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /* not yet used
- private static String getFeatureValue ( String features, String feature ) {
- if ( features != null ) {
- assert feature != null;
- assert feature.length() != 0;
- String[] fa = features.split(",");
- for ( String f : fa ) {
- String[] fp = f.split("=");
- assert fp.length > 0;
- String fn = fp[0];
- String fv = ( fp.length > 1 ) ? fp[1] : "";
- if ( fn.equals ( feature ) ) {
- return fv;
- }
- }
- }
- return "";
- }
- */
-
- private static void appendScalars(List<Integer> scalars, Integer[] sa) {
- for (Integer s : sa) {
- scalars.add(s);
- }
- }
-
- private static String scalarsToString(List<Integer> scalars) {
- Integer[] sa = scalars.toArray(new Integer [ scalars.size() ]);
- return UTF32.fromUTF32(sa);
- }
-
- private static boolean isPaddedOne(Integer[] token) {
- if (getDecimalValue(token [ token.length - 1 ]) != 1) {
- return false;
- } else {
- for (int i = 0, n = token.length - 1; i < n; i++) {
- if (getDecimalValue(token [ i ]) != 0) {
- return false;
- }
- }
- return true;
- }
- }
-
- private static int getDecimalValue(Integer scalar) {
- int s = scalar.intValue();
- if (Character.getType(s) == Character.DECIMAL_DIGIT_NUMBER) {
- return Character.getNumericValue(s);
- } else {
- return -1;
- }
- }
-
- private static boolean isStartOfDecimalSequence(int s) {
- return (Character.getNumericValue(s) == 1)
- && (Character.getNumericValue(s - 1) == 0)
- && (Character.getNumericValue(s + 8) == 9);
- }
-
- private static int[][] supportedAlphabeticSequences = {
- { 'A', 26 }, // A...Z
- { 'a', 26 }, // a...z
- };
-
- private static boolean isStartOfAlphabeticSequence(int s) {
- for (int[] ss : supportedAlphabeticSequences) {
- assert ss.length >= 2;
- if (ss[0] == s) {
- return true;
- }
- }
- return false;
- }
-
- private static int getSequenceBase(int s) {
- for (int[] ss : supportedAlphabeticSequences) {
- assert ss.length >= 2;
- if (ss[0] == s) {
- return ss[1];
- }
- }
- return 0;
- }
-
- private static int[][] supportedSpecials = {
- { 'I' }, // latin - uppercase roman numerals
- { 'i' }, // latin - lowercase roman numerals
- { '\u0391' }, // greek - uppercase isopsephry numerals
- { '\u03B1' }, // greek - lowercase isopsephry numerals
- { '\u05D0' }, // hebrew - gematria numerals
- { '\u0623' }, // arabic - abjadi numberals
- { '\u0627' }, // arabic - either abjadi or hijai alphabetic sequence
- { '\u0E01' }, // thai - default alphabetic sequence
- { '\u3042' }, // kana - hiragana (gojuon) - default alphabetic sequence
- { '\u3044' }, // kana - hiragana (iroha)
- { '\u30A2' }, // kana - katakana (gojuon) - default alphabetic sequence
- { '\u30A4' }, // kana - katakana (iroha)
- };
-
- private static boolean isStartOfNumericSpecial(int s) {
- for (int[] ss : supportedSpecials) {
- assert ss.length >= 1;
- if (ss[0] == s) {
- return true;
- }
- }
- return false;
- }
-
- private SpecialNumberFormatter getSpecialFormatter(int one, int letterValue, String features, String language, String country) {
- if (one == (int) 'I') {
- return new RomanNumeralsFormatter();
- } else if (one == (int) 'i') {
- return new RomanNumeralsFormatter();
- } else if (one == (int) '\u0391') {
- return new IsopsephryNumeralsFormatter();
- } else if (one == (int) '\u03B1') {
- return new IsopsephryNumeralsFormatter();
- } else if (one == (int) '\u05D0') {
- return new GematriaNumeralsFormatter();
- } else if (one == (int) '\u0623') {
- return new ArabicNumeralsFormatter();
- } else if (one == (int) '\u0627') {
- return new ArabicNumeralsFormatter();
- } else if (one == (int) '\u0E01') {
- return new ThaiNumeralsFormatter();
- } else if (one == (int) '\u3042') {
- return new KanaNumeralsFormatter();
- } else if (one == (int) '\u3044') {
- return new KanaNumeralsFormatter();
- } else if (one == (int) '\u30A2') {
- return new KanaNumeralsFormatter();
- } else if (one == (int) '\u30A4') {
- return new KanaNumeralsFormatter();
- } else {
- return null;
- }
- }
-
- private static Integer[] toUpperCase(Integer[] sa) {
- assert sa != null;
- for (int i = 0, n = sa.length; i < n; i++) {
- Integer s = sa [ i ];
- sa [ i ] = Character.toUpperCase(s);
- }
- return sa;
- }
-
- private static Integer[] toLowerCase(Integer[] sa) {
- assert sa != null;
- for (int i = 0, n = sa.length; i < n; i++) {
- Integer s = sa [ i ];
- sa [ i ] = Character.toLowerCase(s);
- }
- return sa;
- }
-
- /* not yet used
- private static Integer[] toTitleCase ( Integer[] sa ) {
- assert sa != null;
- if ( sa.length > 0 ) {
- sa [ 0 ] = Character.toTitleCase ( sa [ 0 ] );
- }
- return sa;
- }
- */
-
- private static List<String> convertWordCase(List<String> words, int caseType) {
- List<String> wl = new ArrayList<String>();
- for (String w : words) {
- wl.add(convertWordCase(w, caseType));
- }
- return wl;
- }
-
- private static String convertWordCase(String word, int caseType) {
- if (caseType == Character.UPPERCASE_LETTER) {
- return word.toUpperCase();
- } else if (caseType == Character.LOWERCASE_LETTER) {
- return word.toLowerCase();
- } else if (caseType == Character.TITLECASE_LETTER) {
- StringBuffer sb = new StringBuffer();
- for (int i = 0, n = word.length(); i < n; i++) {
- String s = word.substring(i, i + 1);
- if (i == 0) {
- sb.append(s.toUpperCase());
- } else {
- sb.append(s.toLowerCase());
- }
- }
- return sb.toString();
- } else {
- return word;
- }
- }
-
- private static String joinWords(List<String> words, String separator) {
- StringBuffer sb = new StringBuffer();
- for (String w : words) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(w);
- }
- return sb.toString();
- }
-
- /**
- * Special number formatter.
- */
- interface SpecialNumberFormatter {
- /**
- * Format number with special numeral system.
- * @param number to be formatted
- * @param one unicode scalar value denoting numeric value 1
- * @param letterValue letter value (must be one of the above letter value enumeration values)
- * @param features features (feature sub-parameters)
- * @param language denotes applicable language
- * @param country denotes applicable country
- * @return formatted number as array of unicode scalars
- */
- Integer[] format(long number, int one, int letterValue, String features, String language, String country);
- }
-
- /**
- * English Word Numerals
- */
- private static String[] englishWordOnes = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
- private static String[] englishWordTeens = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
- private static String[] englishWordTens = { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
- private static String[] englishWordOthers = { "hundred", "thousand", "million", "billion" };
- private static String[] englishWordOnesOrd = { "none", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth" };
- private static String[] englishWordTeensOrd = { "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth" };
- private static String[] englishWordTensOrd = { "", "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetith" };
- private static String[] englishWordOthersOrd = { "hundredth", "thousandth", "millionth", "billionth" };
- private static class EnglishNumberAsWordFormatter implements SpecialNumberFormatter {
- private int caseType = Character.UPPERCASE_LETTER;
- EnglishNumberAsWordFormatter(int caseType) {
- this.caseType = caseType;
- }
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- List<String> wl = new ArrayList<String>();
- if (number >= 1000000000000L) {
- return null;
- } else {
- boolean ordinal = hasFeature(features, "ordinal");
- if (number == 0) {
- wl.add(englishWordOnes [ 0 ]);
- } else if (ordinal && (number < 10)) {
- wl.add(englishWordOnesOrd [ (int) number ]);
- } else {
- int ones = (int) (number % 1000);
- int thousands = (int) ((number / 1000) % 1000);
- int millions = (int) ((number / 1000000) % 1000);
- int billions = (int) ((number / 1000000000) % 1000);
- if (billions > 0) {
- wl = formatOnesInThousand(wl, billions);
- if (ordinal && ((number % 1000000000) == 0)) {
- wl.add(englishWordOthersOrd[3]);
- } else {
- wl.add(englishWordOthers[3]);
- }
- }
- if (millions > 0) {
- wl = formatOnesInThousand(wl, millions);
- if (ordinal && ((number % 1000000) == 0)) {
- wl.add(englishWordOthersOrd[2]);
- } else {
- wl.add(englishWordOthers[2]);
- }
- }
- if (thousands > 0) {
- wl = formatOnesInThousand(wl, thousands);
- if (ordinal && ((number % 1000) == 0)) {
- wl.add(englishWordOthersOrd[1]);
- } else {
- wl.add(englishWordOthers[1]);
- }
- }
- if (ones > 0) {
- wl = formatOnesInThousand(wl, ones, ordinal);
- }
- }
- wl = convertWordCase(wl, caseType);
- return UTF32.toUTF32(joinWords(wl, " "), 0, true);
- }
- }
- private List<String> formatOnesInThousand(List<String> wl, int number) {
- return formatOnesInThousand(wl, number, false);
- }
- private List<String> formatOnesInThousand(List<String> wl, int number, boolean ordinal) {
- assert number < 1000;
- int ones = number % 10;
- int tens = (number / 10) % 10;
- int hundreds = (number / 100) % 10;
- if (hundreds > 0) {
- wl.add(englishWordOnes [ hundreds ]);
- if (ordinal && ((number % 100) == 0)) {
- wl.add(englishWordOthersOrd[0]);
- } else {
- wl.add(englishWordOthers[0]);
- }
- }
- if (tens > 0) {
- if (tens == 1) {
- if (ordinal) {
- wl.add(englishWordTeensOrd [ ones ]);
- } else {
- wl.add(englishWordTeens [ ones ]);
- }
- } else {
- if (ordinal && (ones == 0)) {
- wl.add(englishWordTensOrd [ tens ]);
- } else {
- wl.add(englishWordTens [ tens ]);
- }
- if (ones > 0) {
- if (ordinal) {
- wl.add(englishWordOnesOrd [ ones ]);
- } else {
- wl.add(englishWordOnes [ ones ]);
- }
- }
- }
- } else if (ones > 0) {
- if (ordinal) {
- wl.add(englishWordOnesOrd [ ones ]);
- } else {
- wl.add(englishWordOnes [ ones ]);
- }
- }
- return wl;
- }
- }
-
- /**
- * French Word Numerals
- */
- private static String[] frenchWordOnes = { "z\u00e9ro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf" };
- private static String[] frenchWordTeens = { "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf" };
- private static String[] frenchWordTens = { "", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante-dix", "quatre-vingt", "quatre-vingt-dix" };
- private static String[] frenchWordOthers = { "cent", "cents", "mille", "million", "millions", "milliard", "milliards" };
- private static String[] frenchWordOnesOrdMale = { "premier", "deuxi\u00e8me", "troisi\u00e8me", "quatri\u00e8me", "cinqui\u00e8me", "sixi\u00e8me", "septi\u00e8me", "huiti\u00e8me", "neuvi\u00e8me", "dixi\u00e8me" };
- private static String[] frenchWordOnesOrdFemale = { "premi\u00e8re", "deuxi\u00e8me", "troisi\u00e8me", "quatri\u00e8me", "cinqui\u00e8me", "sixi\u00e8me", "septi\u00e8me", "huiti\u00e8me", "neuvi\u00e8me", "dixi\u00e8me" };
- private static class FrenchNumberAsWordFormatter implements SpecialNumberFormatter {
- private int caseType = Character.UPPERCASE_LETTER;
- FrenchNumberAsWordFormatter(int caseType) {
- this.caseType = caseType;
- }
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- List<String> wl = new ArrayList<String>();
- if (number >= 1000000000000L) {
- return null;
- } else {
- boolean ordinal = hasFeature(features, "ordinal");
- if (number == 0) {
- wl.add(frenchWordOnes [ 0 ]);
- } else if (ordinal && (number <= 10)) {
- boolean female = hasFeature(features, "female");
- if (female) {
- wl.add(frenchWordOnesOrdFemale [ (int) number ]);
- } else {
- wl.add(frenchWordOnesOrdMale [ (int) number ]);
- }
- } else {
- int ones = (int) (number % 1000);
- int thousands = (int) ((number / 1000) % 1000);
- int millions = (int) ((number / 1000000) % 1000);
- int billions = (int) ((number / 1000000000) % 1000);
- if (billions > 0) {
- wl = formatOnesInThousand(wl, billions);
- if (billions == 1) {
- wl.add(frenchWordOthers[5]);
- } else {
- wl.add(frenchWordOthers[6]);
- }
- }
- if (millions > 0) {
- wl = formatOnesInThousand(wl, millions);
- if (millions == 1) {
- wl.add(frenchWordOthers[3]);
- } else {
- wl.add(frenchWordOthers[4]);
- }
- }
- if (thousands > 0) {
- if (thousands > 1) {
- wl = formatOnesInThousand(wl, thousands);
- }
- wl.add(frenchWordOthers[2]);
- }
- if (ones > 0) {
- wl = formatOnesInThousand(wl, ones);
- }
- }
- wl = convertWordCase(wl, caseType);
- return UTF32.toUTF32(joinWords(wl, " "), 0, true);
- }
- }
- private List<String> formatOnesInThousand(List<String> wl, int number) {
- assert number < 1000;
- int ones = number % 10;
- int tens = (number / 10) % 10;
- int hundreds = (number / 100) % 10;
- if (hundreds > 0) {
- if (hundreds > 1) {
- wl.add(frenchWordOnes [ hundreds ]);
- }
- if ((hundreds > 1) && (tens == 0) && (ones == 0)) {
- wl.add(frenchWordOthers[1]);
- } else {
- wl.add(frenchWordOthers[0]);
- }
- }
- if (tens > 0) {
- if (tens == 1) {
- wl.add(frenchWordTeens [ ones ]);
- } else if (tens < 7) {
- if (ones == 1) {
- wl.add(frenchWordTens [ tens ]);
- wl.add("et");
- wl.add(frenchWordOnes [ ones ]);
- } else {
- StringBuffer sb = new StringBuffer();
- sb.append(frenchWordTens [ tens ]);
- if (ones > 0) {
- sb.append('-');
- sb.append(frenchWordOnes [ ones ]);
- }
- wl.add(sb.toString());
- }
- } else if (tens == 7) {
- if (ones == 1) {
- wl.add(frenchWordTens [ 6 ]);
- wl.add("et");
- wl.add(frenchWordTeens [ ones ]);
- } else {
- StringBuffer sb = new StringBuffer();
- sb.append(frenchWordTens [ 6 ]);
- sb.append('-');
- sb.append(frenchWordTeens [ ones ]);
- wl.add(sb.toString());
- }
- } else if (tens == 8) {
- StringBuffer sb = new StringBuffer();
- sb.append(frenchWordTens [ tens ]);
- if (ones > 0) {
- sb.append('-');
- sb.append(frenchWordOnes [ ones ]);
- } else {
- sb.append('s');
- }
- wl.add(sb.toString());
- } else if (tens == 9) {
- StringBuffer sb = new StringBuffer();
- sb.append(frenchWordTens [ 8 ]);
- sb.append('-');
- sb.append(frenchWordTeens [ ones ]);
- wl.add(sb.toString());
- }
- } else if (ones > 0) {
- wl.add(frenchWordOnes [ ones ]);
- }
- return wl;
- }
- }
-
- /**
- * Spanish Word Numerals
- */
- private static String[] spanishWordOnes = { "cero", "uno", "dos", "tres", "cuatro", "cinco", "seise", "siete", "ocho", "nueve" };
- private static String[] spanishWordTeens = { "diez", "once", "doce", "trece", "catorce", "quince", "diecis\u00e9is", "diecisiete", "dieciocho", "diecinueve" };
- private static String[] spanishWordTweens = { "veinte", "veintiuno", "veintid\u00f3s", "veintitr\u00e9s", "veinticuatro", "veinticinco", "veintis\u00e9is", "veintisiete", "veintiocho", "veintinueve" };
- private static String[] spanishWordTens = { "", "diez", "veinte", "treinta", "cuarenta", "cincuenta", "sesenta", "setenta", "ochenta", "noventa" };
- private static String[] spanishWordHundreds = { "", "ciento", "doscientos", "trescientos", "cuatrocientos", "quinientos", "seiscientos", "setecientos", "ochocientos", "novecientos" };
- private static String[] spanishWordOthers = { "un", "cien", "mil", "mill\u00f3n", "millones" };
- private static String[] spanishWordOnesOrdMale = { "ninguno", "primero", "segundo", "tercero", "cuarto", "quinto", "sexto", "s\u00e9ptimo", "octavo", "novento", "d\u00e9cimo" };
- private static String[] spanishWordOnesOrdFemale = { "ninguna", "primera", "segunda", "tercera", "cuarta", "quinta", "sexta", "s\u00e9ptima", "octava", "noventa", "d\u00e9cima" };
- private static class SpanishNumberAsWordFormatter implements SpecialNumberFormatter {
- private int caseType = Character.UPPERCASE_LETTER;
- SpanishNumberAsWordFormatter(int caseType) {
- this.caseType = caseType;
- }
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- List<String> wl = new ArrayList<String>();
- if (number >= 1000000000000L) {
- return null;
- } else {
- boolean ordinal = hasFeature(features, "ordinal");
- if (number == 0) {
- wl.add(spanishWordOnes [ 0 ]);
- } else if (ordinal && (number <= 10)) {
- boolean female = hasFeature(features, "female");
- if (female) {
- wl.add(spanishWordOnesOrdFemale [ (int) number ]);
- } else {
- wl.add(spanishWordOnesOrdMale [ (int) number ]);
- }
- } else {
- int ones = (int) (number % 1000);
- int thousands = (int) ((number / 1000) % 1000);
- int millions = (int) ((number / 1000000) % 1000);
- int billions = (int) ((number / 1000000000) % 1000);
- if (billions > 0) {
- if (billions > 1) {
- wl = formatOnesInThousand(wl, billions);
- }
- wl.add(spanishWordOthers[2]);
- wl.add(spanishWordOthers[4]);
- }
- if (millions > 0) {
- if (millions == 1) {
- wl.add(spanishWordOthers[0]);
- } else {
- wl = formatOnesInThousand(wl, millions);
- }
- if (millions > 1) {
- wl.add(spanishWordOthers[4]);
- } else {
- wl.add(spanishWordOthers[3]);
- }
- }
- if (thousands > 0) {
- if (thousands > 1) {
- wl = formatOnesInThousand(wl, thousands);
- }
- wl.add(spanishWordOthers[2]);
- }
- if (ones > 0) {
- wl = formatOnesInThousand(wl, ones);
- }
- }
- wl = convertWordCase(wl, caseType);
- return UTF32.toUTF32(joinWords(wl, " "), 0, true);
- }
- }
- private List<String> formatOnesInThousand(List<String> wl, int number) {
- assert number < 1000;
- int ones = number % 10;
- int tens = (number / 10) % 10;
- int hundreds = (number / 100) % 10;
- if (hundreds > 0) {
- if ((hundreds == 1) && (tens == 0) && (ones == 0)) {
- wl.add(spanishWordOthers[1]);
- } else {
- wl.add(spanishWordHundreds [ hundreds ]);
- }
- }
- if (tens > 0) {
- if (tens == 1) {
- wl.add(spanishWordTeens [ ones ]);
- } else if (tens == 2) {
- wl.add(spanishWordTweens [ ones ]);
- } else {
- wl.add(spanishWordTens [ tens ]);
- if (ones > 0) {
- wl.add("y");
- wl.add(spanishWordOnes [ ones ]);
- }
- }
- } else if (ones > 0) {
- wl.add(spanishWordOnes [ ones ]);
- }
- return wl;
- }
- }
-
- /**
- * Roman (Latin) Numerals
- */
- private static int[] romanMapping = {
- 100000,
- 90000,
- 50000,
- 40000,
- 10000,
- 9000,
- 5000,
- 4000,
- 1000,
- 900,
- 500,
- 400,
- 100,
- 90,
- 50,
- 40,
- 10,
- 9,
- 8,
- 7,
- 6,
- 5,
- 4,
- 3,
- 2,
- 1
- };
- private static String[] romanStandardForms = {
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- "m",
- "cm",
- "d",
- "cd",
- "c",
- "xc",
- "l",
- "xl",
- "x",
- "ix",
- null,
- null,
- null,
- "v",
- "iv",
- null,
- null,
- "i"
- };
- private static String[] romanLargeForms = {
- "\u2188",
- "\u2182\u2188",
- "\u2187",
- "\u2182\u2187",
- "\u2182",
- "\u2180\u2182",
- "\u2181",
- "\u2180\u2181",
- "m",
- "cm",
- "d",
- "cd",
- "c",
- "xc",
- "l",
- "xl",
- "x",
- "ix",
- null,
- null,
- null,
- "v",
- "iv",
- null,
- null,
- "i"
- };
- private static String[] romanNumberForms = {
- "\u2188",
- "\u2182\u2188",
- "\u2187",
- "\u2182\u2187",
- "\u2182",
- "\u2180\u2182",
- "\u2181",
- "\u2180\u2181",
- "\u216F",
- "\u216D\u216F",
- "\u216E",
- "\u216D\u216E",
- "\u216D",
- "\u2169\u216D",
- "\u216C",
- "\u2169\u216C",
- "\u2169",
- "\u2168",
- "\u2167",
- "\u2166",
- "\u2165",
- "\u2164",
- "\u2163",
- "\u2162",
- "\u2161",
- "\u2160"
- };
- private static class RomanNumeralsFormatter implements SpecialNumberFormatter {
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- List<Integer> sl = new ArrayList<Integer>();
- if (number == 0) {
- return null;
- } else {
- String[] forms;
- int maxNumber;
- if (hasFeature(features, "unicode-number-forms")) {
- forms = romanNumberForms;
- maxNumber = 199999;
- } else if (hasFeature(features, "large")) {
- forms = romanLargeForms;
- maxNumber = 199999;
- } else {
- forms = romanStandardForms;
- maxNumber = 4999;
- }
- if (number > maxNumber) {
- return null;
- } else {
- while (number > 0) {
- for (int i = 0, n = romanMapping.length; i < n; i++) {
- int d = romanMapping [ i ];
- if ((number >= d) && (forms [ i ] != null)) {
- appendScalars(sl, UTF32.toUTF32(forms [ i ], 0, true));
- number = number - d;
- break;
- }
- }
- }
- if (one == (int) 'I') {
- return toUpperCase(sl.toArray(new Integer [ sl.size() ]));
- } else if (one == (int) 'i') {
- return toLowerCase(sl.toArray(new Integer [ sl.size() ]));
- } else {
- return null;
- }
- }
- }
- }
- }
-
- /**
- * Isopsephry (Greek) Numerals
- */
- private static class IsopsephryNumeralsFormatter implements SpecialNumberFormatter {
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- return null;
- }
- }
-
- /**
- * Gematria (Hebrew) Numerals
- */
- private static int[] hebrewGematriaAlphabeticMap = {
- // ones
- 0x05D0, // ALEF
- 0x05D1, // BET
- 0x05D2, // GIMEL
- 0x05D3, // DALET
- 0x05D4, // HE
- 0x05D5, // VAV
- 0x05D6, // ZAYIN
- 0x05D7, // HET
- 0x05D8, // TET
- // tens
- 0x05D9, // YOD
- 0x05DB, // KAF
- 0x05DC, // LAMED
- 0x05DE, // MEM
- 0x05E0, // NUN
- 0x05E1, // SAMEKH
- 0x05E2, // AYIN
- 0x05E4, // PE
- 0x05E6, // TSADHI
- // hundreds
- 0x05E7, // QOF
- 0x05E8, // RESH
- 0x05E9, // SHIN
- 0x05EA, // TAV
- 0x05DA, // FINAL KAF
- 0x05DD, // FINAL MEM
- 0x05DF, // FINAL NUN
- 0x05E3, // FINAL PE
- 0x05E5, // FINAL TSADHI
- };
- private class GematriaNumeralsFormatter implements SpecialNumberFormatter {
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- if (one == 0x05D0) {
- if (letterValue == LETTER_VALUE_ALPHABETIC) {
- return formatNumberAsSequence(number, one, hebrewGematriaAlphabeticMap.length, hebrewGematriaAlphabeticMap);
- } else if (letterValue == LETTER_VALUE_TRADITIONAL) {
- if ((number == 0) || (number > 1999)) {
- return null;
- } else {
- return formatAsGematriaNumber(number, features, language, country);
- }
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
- private Integer[] formatAsGematriaNumber(long number, String features, String language, String country) {
- List<Integer> sl = new ArrayList<Integer>();
- assert hebrewGematriaAlphabeticMap.length == 27;
- assert hebrewGematriaAlphabeticMap[0] == 0x05D0; // ALEF
- assert hebrewGematriaAlphabeticMap[21] == 0x05EA; // TAV
- assert number != 0;
- assert number < 2000;
- int[] map = hebrewGematriaAlphabeticMap;
- int thousands = (int) ((number / 1000) % 10);
- int hundreds = (int) ((number / 100) % 10);
- int tens = (int) ((number / 10) % 10);
- int ones = (int) ((number / 1) % 10);
- if (thousands > 0) {
- sl.add(map [ 0 + (thousands - 1) ]);
- sl.add(0x05F3);
- }
- if (hundreds > 0) {
- assert hundreds < 10;
- if (hundreds < 5) {
- sl.add(map [ 18 + (hundreds - 1) ]);
- } else if (hundreds < 9) {
- sl.add(map [ 18 + (4 - 1) ]);
- sl.add(0x05F4);
- sl.add(map [ 18 + (hundreds - 5) ]);
- } else if (hundreds == 9) {
- sl.add(map [ 18 + (4 - 1) ]);
- sl.add(map [ 18 + (4 - 1) ]);
- sl.add(0x05F4);
- sl.add(map [ 18 + (hundreds - 9) ]);
- }
- }
- if (number == 15) {
- sl.add(map [ 9 - 1]);
- sl.add(0x05F4);
- sl.add(map [ 6 - 1]);
- } else if (number == 16) {
- sl.add(map [ 9 - 1 ]);
- sl.add(0x05F4);
- sl.add(map [ 7 - 1 ]);
- } else {
- if (tens > 0) {
- assert tens < 10;
- sl.add(map [ 9 + (tens - 1) ]);
- }
- if (ones > 0) {
- assert ones < 10;
- sl.add(map [ 0 + (ones - 1) ]);
- }
- }
- return sl.toArray(new Integer [ sl.size() ]);
- }
- }
-
- /**
- * Arabic Numerals
- */
- private static int[] arabicAbjadiAlphabeticMap = {
- // ones
- 0x0623, // ALEF WITH HAMZA ABOVE
- 0x0628, // BEH
- 0x062C, // JEEM
- 0x062F, // DAL
- 0x0647, // HEH
- 0x0648, // WAW
- 0x0632, // ZAIN
- 0x062D, // HAH
- 0x0637, // TAH
- // tens
- 0x0649, // ALEF MAQSURA
- 0x0643, // KAF
- 0x0644, // LAM
- 0x0645, // MEEM
- 0x0646, // NOON
- 0x0633, // SEEN
- 0x0639, // AIN
- 0x0641, // FEH
- 0x0635, // SAD
- // hundreds
- 0x0642, // QAF
- 0x0631, // REH
- 0x0634, // SHEEN
- 0x062A, // TEH
- 0x062B, // THEH
- 0x062E, // KHAH
- 0x0630, // THAL
- 0x0636, // DAD
- 0x0638, // ZAH
- // thousands
- 0x063A, // GHAIN
- };
- private static int[] arabicHijaiAlphabeticMap = {
- 0x0623, // ALEF WITH HAMZA ABOVE
- 0x0628, // BEH
- 0x062A, // TEH
- 0x062B, // THEH
- 0x062C, // JEEM
- 0x062D, // HAH
- 0x062E, // KHAH
- 0x062F, // DAL
- 0x0630, // THAL
- 0x0631, // REH
- 0x0632, // ZAIN
- 0x0633, // SEEN
- 0x0634, // SHEEN
- 0x0635, // SAD
- 0x0636, // DAD
- 0x0637, // TAH
- 0x0638, // ZAH
- 0x0639, // AIN
- 0x063A, // GHAIN
- 0x0641, // FEH
- 0x0642, // QAF
- 0x0643, // KAF
- 0x0644, // LAM
- 0x0645, // MEEM
- 0x0646, // NOON
- 0x0647, // HEH
- 0x0648, // WAW
- 0x0649, // ALEF MAQSURA
- };
- private class ArabicNumeralsFormatter implements SpecialNumberFormatter {
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- if (one == 0x0627) {
- int[] map;
- if (letterValue == LETTER_VALUE_TRADITIONAL) {
- map = arabicAbjadiAlphabeticMap;
- } else if (letterValue == LETTER_VALUE_ALPHABETIC) {
- map = arabicHijaiAlphabeticMap;
- } else {
- map = arabicAbjadiAlphabeticMap;
- }
- return formatNumberAsSequence(number, one, map.length, map);
- } else if (one == 0x0623) {
- if ((number == 0) || (number > 1999)) {
- return null;
- } else {
- return formatAsAbjadiNumber(number, features, language, country);
- }
- } else {
- return null;
- }
- }
- private Integer[] formatAsAbjadiNumber(long number, String features, String language, String country) {
- List<Integer> sl = new ArrayList<Integer>();
- assert arabicAbjadiAlphabeticMap.length == 28;
- assert arabicAbjadiAlphabeticMap[0] == 0x0623; // ALEF WITH HAMZA ABOVE
- assert arabicAbjadiAlphabeticMap[27] == 0x063A; // GHAIN
- assert number != 0;
- assert number < 2000;
- int[] map = arabicAbjadiAlphabeticMap;
- int thousands = (int) ((number / 1000) % 10);
- int hundreds = (int) ((number / 100) % 10);
- int tens = (int) ((number / 10) % 10);
- int ones = (int) ((number / 1) % 10);
- if (thousands > 0) {
- assert thousands < 2;
- sl.add(map [ 27 + (thousands - 1) ]);
- }
- if (hundreds > 0) {
- assert thousands < 10;
- sl.add(map [ 18 + (hundreds - 1) ]);
- }
- if (tens > 0) {
- assert tens < 10;
- sl.add(map [ 9 + (tens - 1) ]);
- }
- if (ones > 0) {
- assert ones < 10;
- sl.add(map [ 0 + (ones - 1) ]);
- }
- return sl.toArray(new Integer [ sl.size() ]);
- }
- }
-
- /**
- * Kana (Japanese) Numerals
- */
- private static int[] hiraganaGojuonAlphabeticMap = {
- 0x3042, // A
- 0x3044, // I
- 0x3046, // U
- 0x3048, // E
- 0x304A, // O
- 0x304B, // KA
- 0x304D, // KI
- 0x304F, // KU
- 0x3051, // KE
- 0x3053, // KO
- 0x3055, // SA
- 0x3057, // SI
- 0x3059, // SU
- 0x305B, // SE
- 0x305D, // SO
- 0x305F, // TA
- 0x3061, // TI
- 0x3064, // TU
- 0x3066, // TE
- 0x3068, // TO
- 0x306A, // NA
- 0x306B, // NI
- 0x306C, // NU
- 0x306D, // NE
- 0x306E, // NO
- 0x306F, // HA
- 0x3072, // HI
- 0x3075, // HU
- 0x3078, // HE
- 0x307B, // HO
- 0x307E, // MA
- 0x307F, // MI
- 0x3080, // MU
- 0x3081, // ME
- 0x3082, // MO
- 0x3084, // YA
- 0x3086, // YU
- 0x3088, // YO
- 0x3089, // RA
- 0x308A, // RI
- 0x308B, // RU
- 0x308C, // RE
- 0x308D, // RO
- 0x308F, // WA
- 0x3090, // WI
- 0x3091, // WE
- 0x3092, // WO
- 0x3093, // N
- };
- private static int[] katakanaGojuonAlphabeticMap = {
- 0x30A2, // A
- 0x30A4, // I
- 0x30A6, // U
- 0x30A8, // E
- 0x30AA, // O
- 0x30AB, // KA
- 0x30AD, // KI
- 0x30AF, // KU
- 0x30B1, // KE
- 0x30B3, // KO
- 0x30B5, // SA
- 0x30B7, // SI
- 0x30B9, // SU
- 0x30BB, // SE
- 0x30BD, // SO
- 0x30BF, // TA
- 0x30C1, // TI
- 0x30C4, // TU
- 0x30C6, // TE
- 0x30C8, // TO
- 0x30CA, // NA
- 0x30CB, // NI
- 0x30CC, // NU
- 0x30CD, // NE
- 0x30CE, // NO
- 0x30CF, // HA
- 0x30D2, // HI
- 0x30D5, // HU
- 0x30D8, // HE
- 0x30DB, // HO
- 0x30DE, // MA
- 0x30DF, // MI
- 0x30E0, // MU
- 0x30E1, // ME
- 0x30E2, // MO
- 0x30E4, // YA
- 0x30E6, // YU
- 0x30E8, // YO
- 0x30E9, // RA
- 0x30EA, // RI
- 0x30EB, // RU
- 0x30EC, // RE
- 0x30ED, // RO
- 0x30EF, // WA
- 0x30F0, // WI
- 0x30F1, // WE
- 0x30F2, // WO
- 0x30F3, // N
- };
- private class KanaNumeralsFormatter implements SpecialNumberFormatter {
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- if ((one == 0x3042) && (letterValue == LETTER_VALUE_ALPHABETIC)) {
- return formatNumberAsSequence(number, one, hiraganaGojuonAlphabeticMap.length, hiraganaGojuonAlphabeticMap);
- } else if ((one == 0x30A2) && (letterValue == LETTER_VALUE_ALPHABETIC)) {
- return formatNumberAsSequence(number, one, katakanaGojuonAlphabeticMap.length, katakanaGojuonAlphabeticMap);
- } else {
- return null;
- }
- }
- }
-
- /**
- * Thai Numerals
- */
- private static int[] thaiAlphabeticMap = {
- 0x0E01,
- 0x0E02,
- 0x0E03,
- 0x0E04,
- 0x0E05,
- 0x0E06,
- 0x0E07,
- 0x0E08,
- 0x0E09,
- 0x0E0A,
- 0x0E0B,
- 0x0E0C,
- 0x0E0D,
- 0x0E0E,
- 0x0E0F,
- 0x0E10,
- 0x0E11,
- 0x0E12,
- 0x0E13,
- 0x0E14,
- 0x0E15,
- 0x0E16,
- 0x0E17,
- 0x0E18,
- 0x0E19,
- 0x0E1A,
- 0x0E1B,
- 0x0E1C,
- 0x0E1D,
- 0x0E1E,
- 0x0E1F,
- 0x0E20,
- 0x0E21,
- 0x0E22,
- 0x0E23,
- // 0x0E24, // RU - not used in modern sequence
- 0x0E25,
- // 0x0E26, // LU - not used in modern sequence
- 0x0E27,
- 0x0E28,
- 0x0E29,
- 0x0E2A,
- 0x0E2B,
- 0x0E2C,
- 0x0E2D,
- 0x0E2E,
- };
- private class ThaiNumeralsFormatter implements SpecialNumberFormatter {
- public Integer[] format(long number, int one, int letterValue, String features, String language, String country) {
- if ((one == 0x0E01) && (letterValue == LETTER_VALUE_ALPHABETIC)) {
- return formatNumberAsSequence(number, one, thaiAlphabeticMap.length, thaiAlphabeticMap);
- } else {
- return null;
- }
- }
- }
-
- }
|