tech.sirwellington.alchemy.generator.NumberGenerators.java Source code

Java tutorial

Introduction

Here is the source code for tech.sirwellington.alchemy.generator.NumberGenerators.java

Source

/*
 * Copyright 2015 SirWellington Tech.
 *
 * 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 tech.sirwellington.alchemy.generator;

import java.util.List;
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.sirwellington.alchemy.annotations.access.Internal;
import tech.sirwellington.alchemy.annotations.access.NonInstantiable;
import tech.sirwellington.alchemy.annotations.designs.patterns.StrategyPattern;

import static tech.sirwellington.alchemy.annotations.designs.patterns.StrategyPattern.Role.CONCRETE_BEHAVIOR;
import static tech.sirwellington.alchemy.generator.BooleanGenerators.booleans;
import static tech.sirwellington.alchemy.generator.Checks.checkNotNull;
import static tech.sirwellington.alchemy.generator.Checks.checkThat;

/**
 * Common {@linkplain AlchemyGenerator Generators} for NumberGenerators.
 * <br>
 * Includes:
 * <br>
 * <pre>
 * + Integers
 * + Longs
 * + Doubles
 * </pre>
 *
 * @author SirWellington
 */
@NonInstantiable
@StrategyPattern(role = CONCRETE_BEHAVIOR)
public final class NumberGenerators {

    private final static Logger LOG = LoggerFactory.getLogger(NumberGenerators.class);

    NumberGenerators() throws IllegalAccessException {
        throw new IllegalAccessException("cannot instantiate this class");
    }

    /**
     * Creates a series of integer values within the specified bounds.
     *
     * @param inclusiveLowerBound The inclusive lower bound
     * @param exclusiveUpperBound The exclusive upper bound
     *
     * @return
     *
     * @throws IllegalArgumentException If {@code lowerBound >= upperBound}
     */
    public static AlchemyGenerator<Integer> integers(int inclusiveLowerBound, int exclusiveUpperBound)
            throws IllegalArgumentException {
        checkThat(inclusiveLowerBound < exclusiveUpperBound, "Lower Bound must be < Upper Bound");

        final boolean isNegativeLowerBound = inclusiveLowerBound < 0;
        // <= because of the fact that 0 would be the *exclusive* upper bound.
        final boolean isNegativeUpperBound = exclusiveUpperBound <= 0;

        return () -> {

            if (isNegativeLowerBound && isNegativeUpperBound) {
                int min = (-exclusiveUpperBound);
                int max = inclusiveLowerBound == Integer.MIN_VALUE ? Integer.MAX_VALUE : -inclusiveLowerBound;

                //Adjust by one, for inclusivity
                int minAdjustedForInclusivity = safeIncrement(min);
                int maxAdjustedForInclusivity = safeIncrement(max);

                int value = RandomUtils.nextInt(minAdjustedForInclusivity, maxAdjustedForInclusivity);
                return -value;
            } else if (isNegativeLowerBound) {
                boolean shouldProduceNegative = booleans().get();
                if (shouldProduceNegative) {
                    int max = inclusiveLowerBound == Integer.MIN_VALUE ? Integer.MAX_VALUE : -inclusiveLowerBound;
                    int maxAdjustedForInclusivity = safeIncrement(max);
                    return -RandomUtils.nextInt(0, maxAdjustedForInclusivity);
                } else {
                    return RandomUtils.nextInt(0, exclusiveUpperBound);
                }
            } else //Positive bounds
            {
                return RandomUtils.nextInt(inclusiveLowerBound, exclusiveUpperBound);
            }
        };

    }

    /**
     * Creates a series of integer values, negative and positive.
     * The range is {@code Integer.MIN_VALUE...Integer.MAX_VALUE}.
     * 
     * @return 
     */
    public static AlchemyGenerator<Integer> anyIntegers() {
        return integers(Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    /**
     * Creates a series of positive integer values from 1 to Integer.MAX_VALUE
     *
     * @return
     * @see #smallPositiveIntegers()
     * @see #positiveLongs()
     */
    public static AlchemyGenerator<Integer> positiveIntegers() {
        return integers(1, Integer.MAX_VALUE);
    }

    /**
     * Creates a series of small positive integers from 1 to 1000.
     *
     * @return
     * @see #positiveIntegers()
     */
    public static AlchemyGenerator<Integer> smallPositiveIntegers() {
        return integers(1, 1000);
    }

    /**
     * Creates a series of negative integer values from Integer.MIN_VALUE to -1
     *
     * @return
     */
    public static AlchemyGenerator<Integer> negativeIntegers() {
        return () -> {
            int value = positiveIntegers().get();
            return value < 0 ? value : -value;
        };
    }

    /**
     * Produces long values within the specified Range
     *
     * @param inclusiveLowerBound inclusive lower bound
     * @param exclusiveUpperBound exclusive upper bound
     *
     * @return
     *
     * @throws IllegalArgumentException If {@code lowerBound >= upperBound}
     */
    public static AlchemyGenerator<Long> longs(long inclusiveLowerBound, long exclusiveUpperBound)
            throws IllegalArgumentException {
        checkThat(inclusiveLowerBound < exclusiveUpperBound, "Lower Bound must be < Upper Bound");

        final boolean negativeLowerBound = inclusiveLowerBound < 0;
        // <= because of the fact that 0 would be the *exclusive* upper bound.
        final boolean negativeUpperBound = exclusiveUpperBound <= 0;

        return () -> {

            if (negativeLowerBound && negativeUpperBound) {
                //Reverse the min and max
                long min = (-exclusiveUpperBound);
                long max = inclusiveLowerBound == Long.MIN_VALUE ? Long.MAX_VALUE : -inclusiveLowerBound;

                //Adjust by one, for inclusivity
                long minAdjustedForInclusivity = safeIncrement(min);
                long maxAdjustedForInclusivity = safeIncrement(max);

                long value = RandomUtils.nextLong(minAdjustedForInclusivity, maxAdjustedForInclusivity);
                return -value;
            } else if (negativeLowerBound) {
                boolean shouldProduceNegative = booleans().get();

                if (shouldProduceNegative) {
                    long min = 0L;
                    long max = inclusiveLowerBound == Long.MIN_VALUE ? Long.MAX_VALUE : -inclusiveLowerBound;
                    long maxAdjustedForInclusivity = safeIncrement(max);

                    long value = -RandomUtils.nextLong(min, maxAdjustedForInclusivity);
                    return value;
                } else {
                    return RandomUtils.nextLong(0, exclusiveUpperBound);
                }
            } else //Positive bounds
            {
                return RandomUtils.nextLong(inclusiveLowerBound, exclusiveUpperBound);
            }
        };
    }

    /**
     * Creates a series of Longs, both negative and positive.
     * The range is {@code Long.MIN_VALUE...Long.MAX_VALUE}.
     * 
     * @return 
     */
    public static AlchemyGenerator<Long> anyLongs() {
        return longs(Long.MIN_VALUE, Long.MAX_VALUE);
    }

    /**
     * Produces a series of positive values from {@code 1} to {@code Long.MAX_VALUE}
     *
     * @return
     * @see #smallPositiveLongs()
     * @see #positiveIntegers()
     */
    public static AlchemyGenerator<Long> positiveLongs() {
        return longs(1L, Long.MAX_VALUE);
    }

    /**
     * Produces a series of positive values from 1 to 10,000
     *
     * @return
     *
     * @see #positiveLongs()
     * @see #positiveLongs()
     */
    public static AlchemyGenerator<Long> smallPositiveLongs() {
        return longs(1L, 10_000L);
    }

    /**
     * Creates a series of double values within the specified range
     *
     * @param inclusiveLowerBound The inclusive lower bound
     * @param inclusiveUpperBound The inclusive upper bound
     *
     * @return
     *
     * @throws IllegalArgumentException If {@code lowerBound >= upperBound}
     */
    public static AlchemyGenerator<Double> doubles(double inclusiveLowerBound, double inclusiveUpperBound)
            throws IllegalArgumentException {
        checkThat(inclusiveLowerBound <= inclusiveUpperBound, "Upper Bound must be greater than Lower Bound");
        final boolean negativeLowerBound = inclusiveLowerBound < 0;
        final boolean negativeUpperBound = inclusiveUpperBound < 0;

        return () -> {
            if (negativeLowerBound && negativeUpperBound) {
                return -RandomUtils.nextDouble(-inclusiveUpperBound, -inclusiveLowerBound);
            } else if (negativeLowerBound) {
                boolean shouldProduceNegative = booleans().get();
                if (shouldProduceNegative) {
                    return -RandomUtils.nextDouble(0, -inclusiveLowerBound);
                } else {
                    return RandomUtils.nextDouble(0, inclusiveUpperBound);
                }
            } else //Positive bounds
            {
                return RandomUtils.nextDouble(inclusiveLowerBound, inclusiveUpperBound);
            }
        };
    }

    /**
     * Creates a series of doubles, both negative and positive.
     * The range is {@code -Double.MAX_VALUE...Double.MAX_VALUE}.
     * 
     * @return 
     */
    public static AlchemyGenerator<Double> anyDoubles() {
        return doubles(-Double.MAX_VALUE, Double.MAX_VALUE);
    }

    /**
     * Creates a series of positive double values from 0 to Double.MAX_VALUE.
     *
     * @return
     * @see #smallPositiveDoubles()
     * @see #positiveIntegers()
     */
    public static AlchemyGenerator<Double> positiveDoubles() {
        return doubles(0.1, Double.MAX_VALUE);
    }

    /**
     * Creates a series of positive doubles from 0.1 to 1000.0
     *
     * @return
     *
     * @see #positiveDoubles()
     * @see #positiveIntegers()
     */
    public static AlchemyGenerator<Double> smallPositiveDoubles() {
        return doubles(0.1, 1000);
    }

    /**
     * Generates an integer value from the specified set.
     *
     * @param values
     *
     * @return
     */
    public static AlchemyGenerator<Integer> integersFromFixedList(List<Integer> values) {
        checkNotNull(values);
        checkThat(!values.isEmpty(), "No values specified");
        return () -> {
            int index = integers(0, values.size()).get();
            return values.get(index);
        };
    }

    /**
     * Generates a double value from the specified set.
     *
     * @param values
     *
     * @return
     */
    public static AlchemyGenerator<Double> doublesFromFixedList(List<Double> values) {
        checkNotNull(values);
        checkThat(!values.isEmpty(), "No values specified");
        return () -> {
            int index = integers(0, values.size()).get();
            return values.get(index);
        };
    }

    //==============================================================================================
    //Internal
    //==============================================================================================
    /**
     * Attempts to increment the value without potentially circling back to {@link Long#MIN_VALUE}
     *
     * @param value
     *
     * @return
     */
    @Internal
    static long safeIncrement(long value) {
        return value == Long.MAX_VALUE ? value : value + 1;
    }

    /**
     * Attempts to decrement the value without potentially circling back to {@link Long#MAX_VALUE}.
     *
     * @param value
     *
     * @return
     */
    @Internal
    static long safeDecrement(long value) {
        return value == Long.MIN_VALUE ? value : value - 1;
    }

    /**
     * Attempts to increment the value without potentially circling back to
     * {@link Integer#MIN_VALUE}
     *
     * @param value
     * @return
     */
    @Internal
    static int safeIncrement(int value) {
        return value == Integer.MAX_VALUE ? value : value + 1;
    }

    /**
     * Attempts to decrement the value without potentially circling back to
     * {@link Integer#MAX_VALUE}.
     *
     * @param value
     * @return
     */
    @Internal
    static int safeDecrement(int value) {
        return value == Integer.MIN_VALUE ? value : value - 1;
    }
}