package io.codearte.jfairy.producer;
import com.google.common.base.Preconditions;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import static java.util.Collections.shuffle;
@Singleton
public class BaseProducer {
private final Random random;
@Inject
public BaseProducer(Random random) {
this.random = random;
}
/**
* Generates random boolean
*
* @return random boolean
*/
public boolean trueOrFalse() {
return random.nextBoolean();
}
/**
* Returns random element from passed List
*
* @param <T> element generic type
* @param elements list to process
* @return random list element
*/
public <T> T randomElement(List<T> elements) {
return elements.get(randomBetween(0, elements.size() - 1));
}
/**
* Returns random element from passed vararg
*
* @param <T> element generic type
* @param elements objects to process
* @return random element
*/
public <T> T randomElement(T... elements) {
return randomElement(Arrays.asList(elements));
}
/**
* Returns random enum value
*
* @param <T> element generic type
* @param enumType enum class
* @return random enum value
*/
public <T extends Enum<?>> T randomElement(Class<T> enumType) {
return enumType.getEnumConstants()[randomBetween(0, enumType.getEnumConstants().length - 1)];
}
/**
* Creates new list being random subset of the passed list
*
* @param <T> element generic type
* @param elements list to process
* @param count returned list size
* @return sublist of the elements list
*/
public <T> List<T> randomElements(List<T> elements, int count) {
shuffle(elements, random);
return elements.subList(0, count % elements.size());
}
/**
* Creates new list being random subset of the passed vararg
*
* @param <T> element generic type
* @param elements objects to process
* @param count returned list size
* @return sublist of the passed elements
*/
public <T> List<T> randomElements(int count, T... elements) {
return randomElements(Arrays.asList(elements), count);
}
/**
* Returns random int value
*
* @param min value of the random number to be returned. Must be positive.
* @param max value of the random number to be returned. Must be positive.
* @return random {@code int} value between {@code min} (inclusive) and {@code max} (inclusive)
*/
public int randomBetween(int min, int max) {
int range = max - min + 1;
int randomInt = range > 0 ? this.random.nextInt(range) : 0;
return min + randomInt;
}
/**
* Returns random int value
*
* @param max value of the random number to be returned. Must be positive.
* @return random {@code int} value between 0 (inclusive) and {@code max} (inclusive)
*/
public int randomInt(int max) {
return randomBetween(0, max);
}
//TODO: MZA: Duplication - should be merged into one method when consistent logic will be determined
public char randomBetween(char min, char max) {
return (char) randomBetween((int) min, (int) max);
}
/**
* Returns random long value from a range (including both range boundaries).
* It required to satisfied condition min <= max.
*
* @param min lower bound of a range
* @param max higher bound of a range
* @return pseudorandom {@code long} value between {@code mon} (inclusive) and {@code max} (inclusive)
*/
public long randomBetween(long min, long max) {
Preconditions.checkArgument(min <= max, "%s has to be <= %s", min, max);
//Can it be done easier for long numbers?
long range = (max - min) + 1;
return min + (long) (random.nextDouble() * range);
}
/**
* Returns random double value
*
* @param min value of the random number to be returned. Must be positive.
* @param max value of the random number to be returned. Must be positive.
* @return random {@code double} value between {@code min} (inclusive) and {@code max} (inclusive)
*/
public double randomBetween(double min, double max) {
double range = max - min;
double randomDouble = range > 0 ? this.random.nextDouble() * range : 0;
return min + randomDouble;
}
/**
* Replaces all {@code '?'} characters with random chars from [a - z] range
*
* @param letterString text to process
* @return text with replaces {@code '?'} chars
*/
public String letterify(String letterString) {
return letterify(letterString, 'a', 'z');
}
/**
* Replaces all {@code '?'} characters with random chars from [{@code from} - {@code to}] range
*
* @param letterString text to process
* @param from start of the range
* @param to end of the range
* @return text with replaced {@code '?'} chars
*/
public String letterify(String letterString, char from, char to) {
return replaceSymbolWithCharsFromTo(letterString, '?', from, to);
}
/**
* Replaces all {@code '#'} characters with random numbers from [0 - 9] range
*
* @param numberString text to process
* @return text with replaced '#' characters
*/
public String numerify(String numberString) {
return numerify(numberString, 0, 9);
}
/**
* Replaces all {@code '#'} characters with random numbers from [{@code from} - {@code to}] range
*
* @param numberString text to process
* @param from start of the range
* @param to end of the range
* @return text with replaced '#' characters
*/
public String numerify(String numberString, int from, int to) {
return replaceSymbolWithCharsFromTo(numberString, '#', Character.forDigit(from, 10), Character.forDigit(to, 10));
}
/**
* Processes text with {@code numerify()} and {@code letterify()} methods
*
* @param string text to process
* @return text with replaced '#' and '?' characters
*/
public String bothify(String string) {
return letterify(numerify(string));
}
private String replaceSymbolWithCharsFromTo(String string, char symbol, char from, char to) {
StringBuilder result = new StringBuilder();
for (char aChar : string.toCharArray()) {
if (aChar == symbol) {
result.append(randomBetween(from, to));
} else {
result.append(aChar);
}
}
return result.toString();
}
}