/* * Copyright 2011-2017 Kay Stenschke * * Licensed 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. */ package com.kstenschke.shifter.models.shiftableTypes; import com.kstenschke.shifter.utils.UtilsTextual; /** * Roman number class */ public class RomanNumber { /** * @param str String to be checked * @return boolean Does the given string represent a CSS length value? */ public static boolean isRomanNumber(String str) { return UtilsTextual.containsOnly(str, new String[]{"I", "V", "X", "L", "C", "D", "M"}); } /** * @param value String representing a roman numeral * @param isUp Shifting up or down? * @return String Value shifted up or down by one */ public String getShifted(String value, boolean isUp) { int intVal = new RomanNumeral(value).toInt(); if (intVal == 1 && !isUp) { return value; } return new RomanNumeral(isUp ? intVal + 1 : intVal -1).toString(); } /** * An object of type RomanNumeral is an integer between 1 and 3999. It can * be constructed either from an integer or from a string that represents * a Roman numeral in this range. The function toString() will return a * standardized Roman numeral representation of the number. The function * toInt() will return the number as a value of type int. */ public class RomanNumeral { private final int num; // The number represented by this Roman numeral. /* The following arrays are used by the toString() function to construct the standard Roman numeral representation of the number. For each i, the number numbers[i] is represented by the corresponding string, letters[i]. */ private final int[] numbers = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; private final String[] letters = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; /** * Constructor. Creates the Roman number w/ the int value specified by the parameter. * Throws a NumberFormatException if arabic is not in the range 1 to 3999 inclusive. */ public RomanNumeral(int arabic) { if (arabic < 1) { throw new NumberFormatException("Value of RomanNumeral must be positive."); } else if (arabic > 3999) { throw new NumberFormatException("Value of RomanNumeral must be 3999 or less."); } num = arabic; } /* * Constructor. Creates the Roman number w/ the given representation. * For example, RomanNumeral("xvii") is 17. If the parameter is not a * legal Roman numeral, a NumberFormatException is thrown. Both upper and * lower case letters are allowed. */ public RomanNumeral(String roman) { if (roman.length() == 0) { throw new NumberFormatException("An empty string does not define a Roman numeral."); } String romanUpper = roman.toUpperCase(); int i = 0; // A position in the string, roman int arabic = 0; // Arabic numeral equivalent of the part of the string that has been converted so far while (i < romanUpper.length()) { char letter = romanUpper.charAt(i); // Letter at current position in string. int number = letterToNumber(letter); // Numerical equivalent of letter. i++; // Move on to next position in the string if (i == romanUpper.length()) { // There is no letter in the string following the one we have just processed. // So just add the number corresponding to the single letter to arabic. arabic += number; } else { // Look at the next letter in the string. If it has a larger Roman numeral // equivalent than number, then the two letters are counted together as // a Roman numeral w/ value (nextNumber - number). int nextNumber = letterToNumber(romanUpper.charAt(i)); if (nextNumber > number) { // Combine the two letters to get one value, and move on to next position in string. arabic += (nextNumber - number); i++; } else { // Don't combine the letters. Just add the value of the one letter onto the number. arabic += number; } } } if (arabic > 3999) { throw new NumberFormatException("Roman numeral must have value 3999 or less."); } num = arabic; } /** * Find the integer value of letter considered as a Roman numeral. * Throws NumberFormatException if letter is not a legal Roman numeral. The letter must be upper case. */ private int letterToNumber(char letter) { switch (letter) { 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("Illegal character \"" + letter + "\" in Roman numeral"); } } /** * Return the standard representation of this Roman numeral. */ public String toString() { String roman = ""; // N represents the part of num that still has to be converted to Roman numeral representation. int nonRoman = num; for (int i = 0; i < numbers.length; i++) { while (nonRoman >= numbers[i]) { roman += letters[i]; nonRoman -= numbers[i]; } } return roman; } /** * Return the value of this Roman numeral as an int. */ public int toInt() { return num; } } }