Java tutorial
/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.tinspx.util.base; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.BoundType; import com.google.common.collect.ContiguousSet; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.Range; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nullable; import lombok.NonNull; /** * {@link Number} utility functions. * * @author Ian */ public class NumberUtils { private static final int[] EMPTY_INT_ARRAY = new int[0]; /** * Determines if the the two {@code Number}s are equal by first converting * them to either a {@link BigInteger} or a {@link BigDecimal} and then * comparing the Numbers. BigDecimal numbers are considered equal if * if {@link BigDecimal#compareTo(BigDecimal)} is 0. * * @param a the first Number to compare * @param b the second number to compare * @return true if the two Numbers are equal * @throws NullPointerException if either a or b is null */ public static boolean numberEquals(Number a, Number b) { a = toBigNumber(a); b = toBigNumber(b); if (a instanceof BigDecimal) { return bigDecimalEquals((BigDecimal) a, b); } else if (b instanceof BigDecimal) { return bigDecimalEquals((BigDecimal) b, a); } else { return a.equals(b); } } static boolean bigDecimalEquals(BigDecimal a, Number b) { if (b instanceof BigDecimal) { return bigDecimalEquals(a, (BigDecimal) b); } else if (b instanceof BigInteger) { return bigDecimalEquals(a, new BigDecimal((BigInteger) b)); } else { return false; } } static boolean bigDecimalEquals(BigDecimal a, BigDecimal b) { return a.compareTo(b) == 0; } /** * Converts the the Number {@code n} to either a {@link BigInteger} or a * {@link BigDecimal}, unless the Number is a Float/Double +/- Infinity or * NaN, in which case {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, * or {@link Double#NEGATIVE_INFINITY} is returned. * * @param n the Number to convert * @return the {@link BigInteger} or {@link BigDecimal} equivalent of * {@code n} */ public static Number toBigNumber(Number n) { if (n instanceof BigDecimal || n instanceof BigInteger) { return n; } if (n instanceof Float) { if (((Float) n).isInfinite()) { return n.equals(Float.POSITIVE_INFINITY) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY; } else if (((Float) n).isNaN()) { return Double.NaN; } else { return new BigDecimal(n.toString()); } } if (n instanceof Double) { if (((Double) n).isInfinite() || ((Double) n).isNaN()) { return n; } else { //had issues with BigDecimal.valueOf(double), so String constructor must be used return new BigDecimal(n.toString()); } } if (n instanceof Integer || n instanceof Long || n instanceof Byte || n instanceof Short || n instanceof AtomicInteger || n instanceof AtomicLong) { return new BigInteger(n.toString()); } return new BigDecimal(n.toString()); } /** * {@code from} is inclusive, {@code to} is exclusive */ public static ContiguousSet<Integer> asSet(int from, int to) { return asSet(Range.closedOpen(from, to)); } public static ContiguousSet<Integer> asSet(Range<Integer> range) { return ContiguousSet.create(range, DiscreteDomain.integers()); } public static int getRandomInt(Range<Integer> range, Random random) { int min = range.lowerEndpoint(); if (range.lowerBoundType() == BoundType.OPEN) { min++; } int max = range.upperEndpoint(); if (range.upperBoundType() == BoundType.CLOSED) { max++; } return random.nextInt(max - min) + min; } /** * Concatenates {@code a} and {@code b}. If either {@code a} or {@code b} is * null, it is treated as an empty array. The returned array be the same * reference as {@code a} or {@code b} (this will happen if one of the * arrays is null or empty). The returned array may be empty, but will never * be null. * * @param a the first array, {@code b} will be appended to the end of a * @param b the second array, {@code a} will be prepended to the beginning * of b * @return the non-null result of concatenating {@code b} to the end of * {@code a} */ public static int[] concat(@Nullable int a[], @Nullable int b[]) { if (a == null || a.length == 0) { return MoreObjects.firstNonNull(b, EMPTY_INT_ARRAY); } else if (b == null || b.length == 0) { return a; } int c[] = Arrays.copyOf(a, a.length + b.length); System.arraycopy(b, 0, c, a.length, b.length); return c; } private static final Function<Number, Integer> INT_F = new Function<Number, Integer>() { @Override public Integer apply(Number input) { if (input == null) { return null; } return input instanceof Integer ? (Integer) input : input.intValue(); } }; private static final Function<Number, Long> LONG_F = new Function<Number, Long>() { @Override public Long apply(Number input) { if (input == null) { return null; } return input instanceof Long ? (Long) input : input.longValue(); } }; private static final Function<Number, Float> FLOAT_F = new Function<Number, Float>() { @Override public Float apply(Number input) { if (input == null) { return null; } return input instanceof Float ? (Float) input : input.floatValue(); } }; private static final Function<Number, Double> Double_F = new Function<Number, Double>() { @Override public Double apply(Number input) { if (input == null) { return null; } return input instanceof Double ? (Double) input : input.doubleValue(); } }; public static Function<Number, Integer> intValue() { return INT_F; } public static Function<Number, Long> longValue() { return LONG_F; } public static Function<Number, Float> floatValue() { return FLOAT_F; } public static Function<Number, Double> doubleValue() { return Double_F; } @SuppressWarnings("unchecked") public static Range<Long> toLongRange(@NonNull Range<? extends Number> range) { if (range.hasLowerBound()) { if (range.hasUpperBound()) { if (range.lowerEndpoint() instanceof Long && range.upperEndpoint() instanceof Long) { return (Range<Long>) range; } return Range.range(range.lowerEndpoint().longValue(), range.lowerBoundType(), range.upperEndpoint().longValue(), range.upperBoundType()); } else { if (range.lowerEndpoint() instanceof Long) { return (Range<Long>) range; } return Range.downTo(range.lowerEndpoint().longValue(), range.lowerBoundType()); } } else if (range.hasUpperBound()) { if (range.upperEndpoint() instanceof Long) { return (Range<Long>) range; } return Range.upTo(range.upperEndpoint().longValue(), range.upperBoundType()); } else { return Range.all(); } } @SuppressWarnings("rawtypes") public static <F extends Comparable, T extends Comparable> Range<T> transform(@NonNull Range<F> range, @NonNull Function<? super F, ? extends T> function) { if (range.hasLowerBound()) { if (range.hasUpperBound()) { return Range.range(function.apply(range.lowerEndpoint()), range.lowerBoundType(), function.apply(range.upperEndpoint()), range.upperBoundType()); } else { return Range.downTo(function.apply(range.lowerEndpoint()), range.lowerBoundType()); } } else if (range.hasUpperBound()) { return Range.upTo(function.apply(range.upperEndpoint()), range.upperBoundType()); } else { return Range.all(); } } }