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

net.time4j.format.NumberSystem Maven / Gradle / Ivy

There is a newer version: 4.38
Show newest version
/*
 * -----------------------------------------------------------------------
 * Copyright © 2013-2016 Meno Hochschild, 
 * -----------------------------------------------------------------------
 * This file (NumberSystem.java) is part of project Time4J.
 *
 * Time4J is free software: You can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Time4J is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Time4J. If not, see .
 * -----------------------------------------------------------------------
 */

package net.time4j.format;


import net.time4j.base.MathUtils;

import java.util.Locale;

/**
 * 

Defines the number system.

* * @author Meno Hochschild * @since 3.11/4.8 */ /*[deutsch] *

Definiert ein Zahlsystem.

* * @author Meno Hochschild * @since 3.11/4.8 */ public enum NumberSystem { //~ Statische Felder/Initialisierungen -------------------------------- /** * Arabic numbers with digits 0-9 (default setting). */ /*[deutsch] * Arabische Zahlen mit den Ziffern 0-9 (Standardeinstellung). */ ARABIC() { @Override public String toNumeral(int number) { return Integer.toString(number); } @Override public int toInteger(String numeral, Leniency leniency) { return Integer.parseInt(numeral); } @Override public boolean contains(char digit) { return ((digit >= '0') && (digit <= '9')); } }, /** * Ethiopic numerals. * *

See also A Look at Ethiopic Numerals.

*/ /*[deutsch] * Äthiopische Numerale. * *

Siehe auch A Look at Ethiopic Numerals.

*/ ETHIOPIC() { @Override public String toNumeral(int number) { if (number < 1) { throw new IllegalArgumentException("Can only convert positive numbers: " + number); } String value = String.valueOf(number); int n = value.length() - 1; if ((n % 2) == 0) { value = "0" + value; n++; } StringBuilder numeral = new StringBuilder(); char asciiOne, asciiTen, ethioOne, ethioTen; for (int place = n; place >= 0; place--) { ethioOne = ethioTen = 0x0; asciiTen = value.charAt(n - place); place--; asciiOne = value.charAt(n - place); if (asciiOne != '0') { ethioOne = (char) ((int) asciiOne + (ETHIOPIC_ONE - '1')); } if (asciiTen != '0') { ethioTen = (char) ((int) asciiTen + (ETHIOPIC_TEN - '1')); } int pos = (place % 4) / 2; char sep = 0x0; if (place != 0) { sep = ( (pos != 0) ? (((ethioOne != 0x0) || (ethioTen != 0x0)) ? ETHIOPIC_HUNDRED : 0x0) : ETHIOPIC_TEN_THOUSAND); } if ((ethioOne == ETHIOPIC_ONE) && (ethioTen == 0x0) && (n > 1)) { if ((sep == ETHIOPIC_HUNDRED) || ((place + 1) == n)) { ethioOne = 0x0; } } if (ethioTen != 0x0) { numeral.append(ethioTen); } if (ethioOne != 0x0) { numeral.append(ethioOne); } if (sep != 0x0) { numeral.append(sep); } } return numeral.toString(); } @Override public int toInteger(String numeral, Leniency leniency) { int total = 0; int sum = 0; int factor = 1; boolean hundred = false; boolean thousand = false; int n = numeral.length() - 1; for (int place = n; place >= 0; place--) { char digit = numeral.charAt(place); if ((digit >= ETHIOPIC_ONE) && (digit < ETHIOPIC_TEN)) { // 1-9 sum += (1 + digit - ETHIOPIC_ONE); } else if ((digit >= ETHIOPIC_TEN) && (digit < ETHIOPIC_HUNDRED)) { // 10-90 sum += ((1 + digit - ETHIOPIC_TEN) * 10); } else if (digit == ETHIOPIC_TEN_THOUSAND) { if (hundred && (sum == 0)) { sum = 1; } total = add(total, sum, factor); if (hundred) { factor *= 100; } else { factor *= 10000; } sum = 0; hundred = false; thousand = true; } else if (digit == ETHIOPIC_HUNDRED) { total = add(total, sum, factor); factor *= 100; sum = 0; hundred = true; thousand = false; } } if ((hundred || thousand) && (sum == 0)) { sum = 1; } total = add(total, sum, factor); return total; } @Override public boolean contains(char digit) { return ((digit >= ETHIOPIC_ONE) && (digit <= ETHIOPIC_TEN_THOUSAND)); } }, /** * Roman numerals in range 1-3999. * *

If the leniency is strict then parsing of Roman numerals will only follow modern usage. * The parsing is always case-insensitive. See also * Roman Numerals.

*/ /*[deutsch] * Römische Numerale im Wertbereich 1-3999. * *

Wenn die Nachsichtigkeit strikt ist, wird das Interpretieren von römischen Numeralen * nur dem modernen Gebrauch folgen. Die Groß- und Kleinschreibung spielt keine Rolle. Siehe * auch Roman Numerals.

*/ ROMAN() { @Override public String toNumeral(int number) { if ((number < 1) || (number > 3999)) { throw new IllegalArgumentException("Out of range (1-3999): " + number); } int n = number; StringBuilder roman = new StringBuilder(); for (int i = 0; i < NUMBERS.length; i++) { while (n >= NUMBERS[i]) { roman.append(LETTERS[i]); n -= NUMBERS[i]; } } return roman.toString(); } @Override public int toInteger(String numeral, Leniency leniency) { if (numeral.isEmpty()) { throw new NumberFormatException("Empty Roman numeral."); } String ucase = numeral.toUpperCase(Locale.US); // use ASCII-base boolean strict = leniency.isStrict(); int len = numeral.length(); int i = 0; int total = 0; while (i < len) { char roman = ucase.charAt(i); int value = getValue(roman); int j = i + 1; int count = 1; if (j == len) { total += value; } else { while (j < len) { char test = ucase.charAt(j); j++; if (test == roman) { count++; if ((count >= 4) && strict) { throw new NumberFormatException( "Roman numeral contains more than 3 equal letters in sequence: " + numeral); } if (j == len) { total += (value * count); } } else { int next = getValue(test); if (next < value) { total += (value * count); j--; } else { // next > value if (strict) { if ((count > 1) || !isValidCombination(roman, test)) { throw new NumberFormatException("Not conform with modern usage: " + numeral); } } total = total + next - (value * count); } break; } } } i = j; } if (total > 3999) { throw new NumberFormatException("Roman numbers bigger than 3999 not supported."); } else if (strict) { if (total >= 900 && ucase.contains("DCD")) { throw new NumberFormatException("Roman number contains invalid sequence DCD."); } if (total >= 90 && ucase.contains("LXL")) { throw new NumberFormatException("Roman number contains invalid sequence LXL."); } if (total >= 9 && ucase.contains("VIV")) { throw new NumberFormatException("Roman number contains invalid sequence VIV."); } } return total; } @Override public boolean contains(char digit) { char c = Character.toUpperCase(digit); return ((c == 'I') || (c == 'V') || (c == 'X') || (c == 'L') || (c == 'C') || (c == 'D') || (c == 'M')); } }; private static final char ETHIOPIC_ONE = 0x1369; // 1, 2, ..., 8, 9 private static final char ETHIOPIC_TEN = 0x1372; // 10, 20, ..., 80, 90 private static final char ETHIOPIC_HUNDRED = 0x137B; private static final char ETHIOPIC_TEN_THOUSAND = 0x137C; private static final int[] NUMBERS = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; private static final String[] LETTERS = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; //~ Methoden ---------------------------------------------------------- /** *

Converts given integer to a text numeral.

* * @param number number to be displayed as text * @return text numeral * @throws IllegalArgumentException if the conversion is not supported for given number * @since 3.11/4.8 */ /*[deutsch] *

Konvertiert die angegebene Zahl zu einem Textnumeral.

* * @param number number to be displayed as text * @return text numeral * @throws IllegalArgumentException if the conversion is not supported for given number * @since 3.11/4.8 */ public String toNumeral(int number) { throw new AbstractMethodError(); } /** *

Converts given text numeral to an integer in smart mode.

* * @param numeral text numeral to be evaluated as number * @return integer * @throws NumberFormatException if given number has wrong format * @throws ArithmeticException if int-range overflows * @since 3.11/4.8 */ /*[deutsch] *

Konvertiert das angegebene Numeral zu einer Ganzzahl im SMART-Modus.

* * @param numeral text numeral to be evaluated as number * @return integer * @throws NumberFormatException if given number has wrong format * @throws ArithmeticException if int-range overflows * @since 3.11/4.8 */ public final int toInteger(String numeral) { return this.toInteger(numeral, Leniency.SMART); } /** *

Converts given text numeral to an integer.

* *

In most cases, the leniency will not be taken into account, but parsing of some odd roman numerals * can be enabled in non-strict mode (for example: IIXX instead of XVIII).

* * @param numeral text numeral to be evaluated as number * @param leniency determines how lenient the parsing of given numeral should be * @return integer * @throws NumberFormatException if given number has wrong format * @throws ArithmeticException if int-range overflows * @since 3.15/4.12 */ /*[deutsch] *

Konvertiert das angegebene Numeral zu einer Ganzzahl.

* *

In den meisten Fällen wird das Nachsichtigkeitsargument nicht in Betracht gezogen. Aber die * Interpretation von nicht dem modernen Gebrauch entsprechenden römischen Numeralen kann im * nicht-strikten Modus erfolgen (zum Beispiel IIXX statt XVIII).

* * @param numeral text numeral to be evaluated as number * @param leniency determines how lenient the parsing of given numeral should be * @return integer * @throws NumberFormatException if given number has wrong format * @throws ArithmeticException if int-range overflows * @since 3.15/4.12 */ public int toInteger( String numeral, Leniency leniency ) { throw new AbstractMethodError(); } /** *

Does this number system contains given digit char?

* * @param digit numerical char to be checked * @return boolean * @since 3.11/4.8 */ /*[deutsch] *

Enthält dieses Zahlensystem die angegebene Ziffer?

* * @param digit numerical char to be checked * @return boolean * @since 3.11/4.8 */ public boolean contains(char digit) { throw new AbstractMethodError(); } private static int add( int total, int sum, int factor ) { return MathUtils.safeAdd(total, MathUtils.safeMultiply(sum, factor)); } private static int getValue(char roman) { switch (roman) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: throw new NumberFormatException("Invalid Roman digit: " + roman); } } private static boolean isValidCombination( char previous, char next ) { switch (previous) { case 'C': return ((next == 'M') || (next == 'D')); case 'X': return ((next == 'C') || (next == 'L')); case 'I': return ((next == 'X') || (next == 'V')); default: return false; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy