///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition 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 General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.common; import java.math.BigDecimal; import java.security.SecureRandom; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; import org.projectforge.core.Configuration; import org.projectforge.core.ConfigurationParam; import org.projectforge.user.PFUserContext; /** * Some helper methods ... * @author Kai Reinhard (k.reinhard@micromata.de) */ public class NumberHelper { public static final String ALLOWED_PHONE_NUMBER_CHARS = "+-/()."; public static final int KILO_BYTES = 1024; public static final BigDecimal KB_BD = new BigDecimal(KILO_BYTES); public static final int MEGA_BYTES = KILO_BYTES * 1024; public static final BigDecimal MB_BD = new BigDecimal(MEGA_BYTES); public static final int GIGA_BYTES = MEGA_BYTES * 1024; public static final BigDecimal GB_BD = new BigDecimal(GIGA_BYTES); public static final BigDecimal TWENTY = new BigDecimal(20); public static final BigDecimal HUNDRED = new BigDecimal(100); public static final BigDecimal THOUSAND = new BigDecimal(1000); public static final BigDecimal THREE_THOUSAND_SIX_HUNDRED = new BigDecimal(3600); public static final BigDecimal MINUS_TWENTY = new BigDecimal(-20); public static final BigDecimal MINUS_HUNDRED = new BigDecimal(-100); public static final BigDecimal BILLION = new BigDecimal(1000000000); private static final Logger log = Logger.getLogger(NumberHelper.class); public static NumberFormat getCurrencyFormat(final Locale locale) { return getNumberFraction2Format(locale); } public static NumberFormat getNumberFraction2Format(final Locale locale) { final NumberFormat format = NumberFormat.getNumberInstance(locale); format.setMaximumFractionDigits(2); format.setMinimumFractionDigits(2); return format; } public static NumberFormat getNumberFractionFormat(final Locale locale, final int fractionDigits) { final NumberFormat format = NumberFormat.getNumberInstance(locale); format.setMaximumFractionDigits(fractionDigits); format.setMinimumFractionDigits(fractionDigits); return format; } /** * Pretty output of bytes, "1023 bytes", "1.1 kb", "523 kb", "1.7 Mb", "143 Gb" etc. * @param bytes * @return */ public static String formatBytes(final long bytes) { if (bytes < KILO_BYTES) { return String.valueOf(bytes) + " bytes"; } if (bytes < MEGA_BYTES) { BigDecimal no = new BigDecimal(bytes).divide(KB_BD, 1, BigDecimal.ROUND_HALF_UP); if (no.longValue() >= 100) { no = no.setScale(0, BigDecimal.ROUND_HALF_UP); } return NumberFormat.getInstance(PFUserContext.getLocale()).format(no) + " kb"; } if (bytes < GIGA_BYTES) { BigDecimal no = new BigDecimal(bytes).divide(MB_BD, 1, BigDecimal.ROUND_HALF_UP); if (no.longValue() >= 100) { no = no.setScale(0, BigDecimal.ROUND_HALF_UP); } return NumberFormat.getInstance(PFUserContext.getLocale()).format(no) + " Mb"; } BigDecimal no = new BigDecimal(bytes).divide(GB_BD, 1, BigDecimal.ROUND_HALF_UP); if (no.longValue() >= 100) { no = no.setScale(0, BigDecimal.ROUND_HALF_UP); } return NumberFormat.getInstance(PFUserContext.getLocale()).format(no) + " Gb"; } /** * @param value * @return true, if value is not null and greater zero. */ public static boolean greaterZero(final Integer value) { return value != null && value.intValue() > 0; } /** * @param value * @return true, if value is not null and greater zero. */ public static boolean greaterZero(final Long value) { return value != null && value.intValue() > 0; } public static boolean isZeroOrNull(final Integer value) { return (value == null || value == 0); } public static boolean isGreaterZero(final BigDecimal value) { return (value != null && value.compareTo(BigDecimal.ZERO) > 0); } /** * @param value * @return true, if the given value is not null and not zero. */ public static boolean isNotZero(final Integer value) { return !isZeroOrNull(value); } /** * Parses the given string as integer value. * @param value The string representation of the integer value to parse. * @return Integer value or null if an empty string was given or a syntax error occurs. */ public static Integer parseInteger(String value) { if (value == null) { return null; } value = value.trim(); if (value.length() == 0) { return null; } Integer result = null; try { result = new Integer(value); } catch (final NumberFormatException ex) { log.warn("Can't parse integer: '" + value + "'."); } return result; } /** * Parses the given string as short value. * @param value The string representation of the short value to parse. * @return Short value or null if an empty string was given or a syntax error occurs. */ public static Short parseShort(String value) { if (value == null) { return null; } value = value.trim(); if (value.length() == 0) { return null; } Short result = null; try { result = new Short(value); } catch (final NumberFormatException ex) { log.debug(ex.getMessage(), ex); } return result; } /** * Catches any NumberFormatException and returns 0, otherwise the long value represented by the given value is returned. */ public static long parseLong(String value) { if (value == null) { return 0; } value = value.trim(); if (value.length() == 0) { return 0; } Long result = null; try { result = new Long(value); } catch (final NumberFormatException ex) { log.debug(ex.getMessage(), ex); } return result; } /** */ public static BigDecimal parseBigDecimal(String value) { if (value == null) { return null; } value = value.trim(); if (value.length() == 0) { return null; } BigDecimal result = null; try { if (value.indexOf(',') > 0) { // Replace the german decimal character by '.': value = value.replace(',', '.'); } result = new BigDecimal(value); } catch (final NumberFormatException ex) { log.debug(ex.getMessage(), ex); } return result; } /** */ public static BigDecimal parseCurrency(String value, final Locale locale) { if (value == null) { return null; } value = value.trim(); if (value.length() == 0) { return null; } final NumberFormat format = getCurrencyFormat(locale); BigDecimal result = null; try { final Number number = format.parse(value); if (number != null) { result = new BigDecimal(number.toString()); result = result.setScale(2, BigDecimal.ROUND_HALF_UP); } } catch (final ParseException ex) { log.debug(ex.getMessage(), ex); } return result; } /** * @param v1 null is supported. * @param v2 null is supported. * @return */ public static BigDecimal add(final BigDecimal v1, final BigDecimal v2) { if (v1 == null) { if (v2 == null) { return BigDecimal.ZERO; } else { return v2; } } else { if (v2 == null) { return v1; } else { return v1.add(v2); } } } /** * Returns the given integer value as String representation. * @param value The integer value to convert. * @return The String representation or empty String, if value is null. */ public static String getAsString(final Number value) { if (value == null) { return ""; } else { return String.valueOf(value); } } /** * Returns the given number value as String representation. * @param value The number value to convert. * @param format The format to use. * @return The String representation or empty String, if value is null. */ public static String getAsString(final Number value, final NumberFormat format) { if (value == null) { return ""; } else { return format.format(value); } } /** * @see PFUserContext#getLocale() */ public static String formatFraction2(final Number value) { final Locale locale = PFUserContext.getLocale(); final NumberFormat format = getNumberFraction2Format(locale); return format.format(value); } /** * Uses the default country phone prefix from the configuration. * @see #extractPhonenumber(String, String) */ public static String extractPhonenumber(final String str) { final String defaultCountryPhonePrefix = Configuration.getInstance().getStringValue(ConfigurationParam.DEFAULT_COUNTRY_PHONE_PREFIX); return extractPhonenumber(str, defaultCountryPhonePrefix); } /** * Extracts the phone number of the given string. All characters of the set "+-/()." and white spaces will be deleted and +## will be * replaced by 00##. Example: +49 561 / 316793-0 -> 00495613167930 <br/> * Ignores any characters after the first occurence of ':' or any letter. * @param str * @param countryPrefix If country prefix is given, for all numbers beginning with the country prefix the country prefix will be replaced * by 0. Example: ("+49 561 / 316793-0", "+49") -> 05613167930; ("+39 123456", "+49") -> 0039123456. * @return */ public static String extractPhonenumber(String str, final String countryPrefix) { if (str == null) { return null; } str = str.trim(); final StringBuffer buf = new StringBuffer(); if (StringUtils.isNotEmpty(countryPrefix) == true && str.startsWith(countryPrefix) == true) { buf.append('0'); str = str.substring(countryPrefix.length()); } else if (str.length() > 3 && str.charAt(0) == '+' && Character.isDigit(str.charAt(1)) == true && Character.isDigit(str.charAt(2)) == true) { buf.append("00"); buf.append(str.charAt(1)); buf.append(str.charAt(2)); str = str.substring(3); } for (int i = 0; i < str.length(); i++) { final char ch = str.charAt(i); if (Character.isDigit(str.charAt(i)) == true) { buf.append(ch); } else if (Character.isWhitespace(ch) == true) { // continue. } else if (ALLOWED_PHONE_NUMBER_CHARS.indexOf(ch) < 0) { break; } } return buf.toString(); } /** * Compares two given BigDecimals. They are equal if the value is equal independent of the scale (5.70 is equals to 5.7 and null is equals * null, but null is not equals to 0). * @param value1 * @param value2 * @return * @see BigDecimal#compareTo(BigDecimal) */ public static boolean isEqual(final BigDecimal value1, final BigDecimal value2) { if (value1 == null) { return (value2 == null) ? true : false; } if (value2 == null) { return false; } return value1.compareTo(value2) == 0; } /** * @param value * @return true, if the given value is not null and not zero. */ public static boolean isNotZero(final BigDecimal value) { return !isZeroOrNull(value); } public static boolean isZeroOrNull(final BigDecimal value) { return (value == null || value.compareTo(BigDecimal.ZERO) == 0); } /** * Compares two given Integers using compareTo method. * @param value1 * @param value * @return * @see Integer#compareTo(Integer) */ public static boolean isEqual(final Integer value1, final Integer value) { if (value1 == null) { return (value == null) ? true : false; } if (value == null) { return false; } return value1.compareTo(value) == 0; } /** * Splits string representation of the given number into digits. Examples:<br/> * NumberHelper.splitToInts(11110511, 1, 3, 2, 2) = {1, 111, 5, 11}<br/> * NumberHelper.splitToInts(10000511, 1, 3, 2, 2) = { 1, 0, 5, 11}<br/> * NumberHelper.splitToInts(511, 1, 3, 2, 2) = { 0, 0, 5, 11} * @param value * @param split * @return */ public static int[] splitToInts(final Number value, final int... split) { int numberOfDigits = 0; for (final int n : split) { numberOfDigits += n; } final String str = StringUtils.leftPad(String.valueOf(value.intValue()), numberOfDigits, '0'); final int[] result = new int[split.length]; int pos = 0; int i = 0; for (final int n : split) { result[i++] = parseInteger(str.substring(pos, pos + n)); pos += n; } return result; } /** * If given string is an number (NumberUtils.isNumber(String)) then it will be converted to a plain string via BigDecimal.toPlainString(). * Any exponent such as 1E7 will be avoided. * @param str * @return Converted string if number, otherwise the origin string. * @see NumberUtils#isNumber(String) * @see NumberUtils#createBigDecimal(String) * @see BigDecimal#toPlainString() */ public static String toPlainString(final String str) { if (NumberUtils.isNumber(str) == true) { final BigDecimal bd = NumberUtils.createBigDecimal(str); return bd.toPlainString(); } else { return str; } } /** * Sets scale 0 for numbers greater 100, 1 for numbers greater 20 and 2 as default. * @param number * @return */ public static BigDecimal setDefaultScale(final BigDecimal number) { if (number == null) { return null; } if (number.compareTo(NumberHelper.HUNDRED) >= 0 || number.compareTo(NumberHelper.MINUS_HUNDRED) <= 0) { return number.setScale(0, BigDecimal.ROUND_HALF_UP); } else if (number.compareTo(NumberHelper.TWENTY) >= 0 || number.compareTo(NumberHelper.MINUS_TWENTY) <= 0) { return number.setScale(1, BigDecimal.ROUND_HALF_UP); } return number.setScale(2, BigDecimal.ROUND_HALF_UP); } /** * Generates secure random bytes of the given length and return base 64 encoded bytes as url safe String. This is not the length of the * resulting string! * @param numberOfBytes * @return */ public static String getSecureRandomUrlSaveString(final int numberOfBytes) { final SecureRandom random = new SecureRandom(); final byte[] bytes = new byte[numberOfBytes]; random.nextBytes(bytes); return Base64.encodeBase64URLSafeString(bytes); } /** * Generates secure random bytes of the given length and return base 64 encoded bytes as url safe String. This is not the length of the * resulting string! * @param numberOfBytes * @return */ public static String getSecureRandomBase64String(final int numberOfBytes) { final SecureRandom random = new SecureRandom(); final byte[] bytes = new byte[numberOfBytes]; random.nextBytes(bytes); return org.apache.commons.codec.binary.StringUtils.newStringUtf8(Base64.encodeBase64(bytes, false)); } public static boolean isIn(final int value, final int... numbers) { if (numbers == null) { return false; } for (final int number : numbers) { if (value == number) { return true; } } return false; } }