/*
* =============================================================================
*
* Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
*
* 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 org.thymeleaf.util;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
public final class NumberUtils {
public static String format(final Number target, final Integer minIntegerDigits, final Locale locale) {
if (target == null) {
return null;
}
Validate.notNull(minIntegerDigits, "Minimum integer digits cannot be null");
return formatNumber(target, minIntegerDigits, NumberPointType.NONE, Integer.valueOf(0), NumberPointType.NONE, locale);
}
public static String format(final Number target, final Integer minIntegerDigits, final NumberPointType thousandsPointType, final Locale locale) {
if (target == null) {
return null;
}
Validate.notNull(minIntegerDigits, "Minimum integer digits cannot be null");
Validate.notNull(thousandsPointType, "Thousands point type cannot be null");
return formatNumber(target, minIntegerDigits, thousandsPointType, Integer.valueOf(0), NumberPointType.NONE, locale);
}
public static String format(final Number target, final Integer minIntegerDigits, final Integer decimalDigits, final Locale locale) {
if (target == null) {
return null;
}
Validate.notNull(minIntegerDigits, "Minimum integer digits cannot be null");
Validate.notNull(decimalDigits, "Decimal digits cannot be null");
return formatNumber(target, minIntegerDigits, NumberPointType.NONE, decimalDigits, NumberPointType.DEFAULT, locale);
}
public static String format(final Number target, final Integer minIntegerDigits, final Integer decimalDigits, final NumberPointType decimalPointType, final Locale locale) {
if (target == null) {
return null;
}
Validate.notNull(minIntegerDigits, "Minimum integer digits cannot be null");
Validate.notNull(decimalDigits, "Decimal digits cannot be null");
Validate.notNull(decimalPointType, "Decimal point type cannot be null");
return formatNumber(target, minIntegerDigits, NumberPointType.NONE, decimalDigits, decimalPointType, locale);
}
public static String format(final Number target, final Integer minIntegerDigits, final NumberPointType thousandsPointType, final Integer decimalDigits, final Locale locale) {
if (target == null) {
return null;
}
Validate.notNull(minIntegerDigits, "Minimum integer digits cannot be null");
Validate.notNull(thousandsPointType, "Thousands point type cannot be null");
Validate.notNull(decimalDigits, "Decimal digits cannot be null");
return formatNumber(target, minIntegerDigits, thousandsPointType, decimalDigits, NumberPointType.DEFAULT, locale);
}
public static String format(final Number target, final Integer minIntegerDigits, final NumberPointType thousandsPointType, final Integer decimalDigits, final NumberPointType decimalPointType, final Locale locale) {
if (target == null) {
return null;
}
Validate.notNull(minIntegerDigits, "Minimum integer digits cannot be null");
Validate.notNull(thousandsPointType, "Thousands point type cannot be null");
Validate.notNull(decimalDigits, "Decimal digits cannot be null");
Validate.notNull(decimalPointType, "Decimal point type cannot be null");
return formatNumber(target, minIntegerDigits, thousandsPointType, decimalDigits, decimalPointType, locale);
}
/**
* <p>
* Produces an array with a sequence of integer numbers.
* </p>
*
* @param from value to start the sequence from
* @param to value to produce the sequence to
* @return the Integer[] sequence
*
* @since 1.1.2
*/
public static Integer[] sequence(final Integer from, final Integer to) {
return sequence(from, to, Integer.valueOf(from <= to? 1 : -1));
}
/**
* <p>
* Produces an array with a sequence of integer numbers, using a step.
* </p>
*
* @param from value to start the sequence from
* @param to value to produce the sequence to
* @param step the step to be used
* @return the Integer[] sequence
*
* @since 2.0.9
*/
public static Integer[] sequence(final Integer from, final Integer to, final Integer step) {
Validate.notNull(from, "Value to start the sequence from cannot be null");
Validate.notNull(to, "Value to generate the sequence up to cannot be null");
Validate.notNull(step, "Step to generate the sequence cannot be null");
final int iFrom = from.intValue();
final int iTo = to.intValue();
final int iStep = step.intValue();
if (iFrom == iTo) {
return new Integer[] {Integer.valueOf(iFrom)};
}
if (iStep == 0 || (iStep > 0 && iFrom > iTo) || (iStep < 0 && iFrom < iTo)) {
// with iStep == 0, this would only be valid if iFrom == iTo, which it isn't - the rest are impossible
throw new IllegalArgumentException("Cannot create sequence from " + iFrom + " to " + iTo + " with step " + iStep);
}
final List<Integer> values = new ArrayList<Integer>(10);
if (iFrom < iTo) {
int i = iFrom;
while (i <= iTo) {
values.add(Integer.valueOf(i));
i += iStep;
}
} else {
// iFrom > iTo
int i = iFrom;
while (i >= iTo) {
values.add(Integer.valueOf(i));
i += iStep;
}
}
return values.toArray(new Integer[values.size()]);
}
/**
* Formats a number as per the given values.
*
* @param target The number to format.
* @param minIntegerDigits Minimum number digits to return (0 padding).
* @param thousandsPointType Character to use for separating number groups.
* @param fractionDigits Minimum number of fraction digits to format to
* (0 padding).
* @param decimalPointType Character to use for separating decimals.
* @param locale Locale to draw more information from.
* @return The number formatted as specified, or {@code null} if the number
* given is {@code null}.
*/
private static String formatNumber(final Number target, final Integer minIntegerDigits,
final NumberPointType thousandsPointType, final Integer fractionDigits,
final NumberPointType decimalPointType, final Locale locale) {
Validate.notNull(fractionDigits, "Fraction digits cannot be null");
Validate.notNull(decimalPointType, "Decimal point type cannot be null");
Validate.notNull(thousandsPointType, "Thousands point type cannot be null");
Validate.notNull(locale, "Locale cannot be null");
if (target == null) {
return null;
}
DecimalFormat format = (DecimalFormat)NumberFormat.getNumberInstance(locale);
format.setMinimumFractionDigits(fractionDigits.intValue());
format.setMaximumFractionDigits(fractionDigits.intValue());
if (minIntegerDigits != null) {
format.setMinimumIntegerDigits(minIntegerDigits.intValue());
}
format.setDecimalSeparatorAlwaysShown(decimalPointType != NumberPointType.NONE && fractionDigits.intValue() > 0);
format.setGroupingUsed(thousandsPointType != NumberPointType.NONE);
format.setDecimalFormatSymbols(computeDecimalFormatSymbols(decimalPointType, thousandsPointType, locale));
return format.format(target);
}
private static DecimalFormatSymbols computeDecimalFormatSymbols(
final NumberPointType decimalPointType, final NumberPointType thousandsPointType, final Locale locale) {
Validate.notNull(decimalPointType, "Decimal point type cannot be null");
Validate.notNull(thousandsPointType, "Thousands point type cannot be null");
Validate.notNull(locale, "Locale cannot be null");
final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
switch (decimalPointType) {
case POINT :
symbols.setDecimalSeparator('.');
break;
case COMMA :
symbols.setDecimalSeparator(',');
break;
case WHITESPACE :
symbols.setDecimalSeparator(' ');
break;
case DEFAULT :
final DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
symbols.setDecimalSeparator(dfs.getDecimalSeparator());
break;
case NONE :
// This should never happen
symbols.setDecimalSeparator('?');
break;
}
switch (thousandsPointType) {
case POINT :
symbols.setGroupingSeparator('.');
break;
case COMMA :
symbols.setGroupingSeparator(',');
break;
case WHITESPACE :
symbols.setGroupingSeparator(' ');
break;
case DEFAULT :
final DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
symbols.setGroupingSeparator(dfs.getGroupingSeparator());
break;
case NONE :
// This should never be shown
symbols.setGroupingSeparator('?');
break;
}
return symbols;
}
/**
* Formats a number as a currency value according to the specified locale.
*
* @param target The number to format.
* @param locale Locale to use for formatting.
* @return The number formatted as a currency, or {@code null} if the number
* given is {@code null}.
*/
public static String formatCurrency(final Number target, final Locale locale) {
Validate.notNull(locale, "Locale cannot be null");
if (target == null) {
return null;
}
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
return format.format(target);
}
/**
* Formats a number as a percentage value.
*
* @param target The number to format.
* @param minIntegerDigits Minimum number of digits to return (0 padding).
* @param fractionDigits Minimum number of fraction digits to return (0
* padding).
* @param locale Locale to use for formatting.
* @return The number formatted as a percentage, or {@code null} if the
* number given is {@code null}.
*/
public static String formatPercent(final Number target, final Integer minIntegerDigits,
final Integer fractionDigits, final Locale locale) {
Validate.notNull(fractionDigits, "Fraction digits cannot be null");
Validate.notNull(locale, "Locale cannot be null");
if (target == null) {
return null;
}
NumberFormat format = NumberFormat.getPercentInstance(locale);
format.setMinimumFractionDigits(fractionDigits.intValue());
format.setMaximumFractionDigits(fractionDigits.intValue());
if (minIntegerDigits != null) {
format.setMinimumIntegerDigits(minIntegerDigits.intValue());
}
return format.format(target);
}
private NumberUtils() {
super();
}
}