RandomFieldSupplier.java
package de.slothsoft.random;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import de.slothsoft.random.types.ArrayRandomField;
import de.slothsoft.random.types.BigDecimalRandomField;
import de.slothsoft.random.types.BigIntegerRandomField;
import de.slothsoft.random.types.BirthdayRandomField;
import de.slothsoft.random.types.BooleanRandomField;
import de.slothsoft.random.types.CalendarRandomField;
import de.slothsoft.random.types.CharacterRandomField;
import de.slothsoft.random.types.CityRandomField;
import de.slothsoft.random.types.DateRandomField;
import de.slothsoft.random.types.DoubleRandomField;
import de.slothsoft.random.types.EnumRandomField;
import de.slothsoft.random.types.FirstNameRandomField;
import de.slothsoft.random.types.FloatRandomField;
import de.slothsoft.random.types.IntegerRandomField;
import de.slothsoft.random.types.LastNameRandomField;
import de.slothsoft.random.types.LocalDateRandomField;
import de.slothsoft.random.types.LocalDateTimeRandomField;
import de.slothsoft.random.types.LocalTimeRandomField;
import de.slothsoft.random.types.LongRandomField;
import de.slothsoft.random.types.PostalCodeRandomField;
import de.slothsoft.random.types.ShortRandomField;
import de.slothsoft.random.types.StreetRandomField;
import de.slothsoft.random.types.WordRandomField;
import de.slothsoft.random.types.WordsRandomField;
/**
* Util class for storing managing all the random field this module brings along.
*
* @author Stef Schulz
* @since 2.0.0
*/
public abstract class RandomFieldSupplier {
private static List<RandomFieldSupplier> suppliers;
/**
* Creates a {@link RandomField} for a property name and class.
*
* @param propertyName the property's name
* @param propertyClass the property's class
* @return a {@link RandomField} or null
* @since 2.1.0
*/
public static RandomField createRandomFieldByField(String propertyName, Class<?> propertyClass) {
final RandomFieldSupplier supplier = findSupplierByField(propertyName, propertyClass);
if (supplier != null) {
return supplier.createRandomField(propertyName, propertyClass);
}
return null;
}
/**
* Returns a {@link RandomFieldSupplier} for a property name and class.
*
* @param propertyName the property's name
* @param propertyClass the property's class
* @return a {@link RandomFieldSupplier} or null
*/
public static RandomFieldSupplier findSupplierByField(String propertyName, Class<?> propertyClass) {
final String name = propertyName.toLowerCase();
for (final RandomFieldSupplier supplier : getAllSuppliers()) {
if (supplier.canSupply(name, propertyClass)) {
return supplier;
}
}
return null;
}
/**
* Returns all {@link RandomFieldSupplier}s that this module brings along.
*
* @return a list of {@link RandomFieldSupplier}; never null; probably never empty
*/
public static List<RandomFieldSupplier> getAllSuppliers() {
if (suppliers == null) {
suppliers = new ArrayList<>();
suppliers.add(forSynonymeList("synonyms/birthdays.txt", BirthdayRandomField::new));
suppliers.add(forSynonymeList("synonyms/city-names.txt", CityRandomField::new));
suppliers.add(forSynonymeList("synonyms/last-names.txt", LastNameRandomField::new));
suppliers.add(forSynonymeList("synonyms/first-names.txt", FirstNameRandomField::new));
suppliers.add(forSynonymeList("synonyms/postal-code.txt", PostalCodeRandomField::new));
suppliers.add(forSynonymeList("synonyms/street-names.txt", StreetRandomField::new));
suppliers.add(forSynonymeList("synonyms/word.txt", WordRandomField::new));
suppliers.add(forSynonymeList("synonyms/words.txt", WordsRandomField::new));
suppliers.add(forFieldClass(Date.class, DateRandomField::new));
suppliers.add(forFieldClass(Calendar.class, CalendarRandomField::new));
suppliers.add(forFieldClass(LocalDateTime.class, LocalDateTimeRandomField::new));
suppliers.add(forFieldClass(LocalTime.class, LocalTimeRandomField::new));
suppliers.add(forFieldClass(LocalDate.class, LocalDateRandomField::new));
suppliers.add(new ArrayRandomFieldSupplier());
suppliers.add(forFieldClass(BigInteger.class, BigIntegerRandomField::new));
suppliers.add(forFieldClass(BigDecimal.class, BigDecimalRandomField::new));
suppliers.add(forFieldClass(Boolean.class, BooleanRandomField::new));
suppliers.add(forFieldClass(boolean.class, () -> new BooleanRandomField()));
suppliers.add(forFieldClass(Character.class, CharacterRandomField::new));
suppliers.add(forFieldClass(char.class, CharacterRandomField::new));
suppliers.add(forFieldClass(Double.class, DoubleRandomField::new));
suppliers.add(forFieldClass(double.class, () -> new DoubleRandomField()));
suppliers.add(forFieldClass(Float.class, FloatRandomField::new));
suppliers.add(forFieldClass(float.class, () -> new FloatRandomField()));
suppliers.add(new EnumRandomFieldSupplier());
suppliers.add(forFieldClass(Integer.class, IntegerRandomField::new));
suppliers.add(forFieldClass(int.class, () -> new IntegerRandomField()));
suppliers.add(forFieldClass(Long.class, LongRandomField::new));
suppliers.add(forFieldClass(long.class, () -> new LongRandomField()));
suppliers.add(forFieldClass(short.class, () -> new ShortRandomField()));
suppliers.add(forFieldClass(Short.class, () -> new ShortRandomField()));
}
return suppliers;
}
// these constructors are not that great, but are a problem for future Stef now
static RandomFieldSupplier forFieldClass(Class<?> wantedFieldClass, Supplier<RandomField> supplier) {
return new RandomFieldSupplier(supplier) {
@Override
public boolean canSupply(String fieldName, Class<?> fieldClass) {
return wantedFieldClass == fieldClass;
}
};
}
static RandomFieldSupplier forSynonymeList(String fileName, Supplier<RandomField> supplier) {
final List<String> synonyms = Arrays.asList(readFile(RandomFieldSupplier.class.getResourceAsStream(fileName)));
return new RandomFieldSupplier(supplier) {
@Override
public boolean canSupply(String fieldName, Class<?> fieldClass) {
return synonyms.contains(fieldName.toLowerCase());
}
};
}
static String[] readFile(InputStream inputStream) {
return new BufferedReader(new InputStreamReader(inputStream)).lines().parallel().toArray(String[]::new);
}
static class EnumRandomFieldSupplier extends RandomFieldSupplier {
@SuppressWarnings({"rawtypes", "unchecked"})
protected EnumRandomFieldSupplier() {
super((n, c) -> new EnumRandomField<>((Class<Enum>) c));
}
@Override
public boolean canSupply(String fieldName, Class<?> fieldClass) {
return Enum.class.isAssignableFrom(fieldClass);
}
}
static class ArrayRandomFieldSupplier extends RandomFieldSupplier {
protected ArrayRandomFieldSupplier() {
super((n, c) -> {
final Class<?> elementClass = c.getComponentType();
final RandomField elementRandomField = createRandomFieldByField(n, elementClass);
Objects.requireNonNull(elementRandomField, "Cannot find random field for " + n + " (" + c + ")");
return new ArrayRandomField(elementClass, elementRandomField);
});
}
@Override
public boolean canSupply(String fieldName, Class<?> fieldClass) {
return fieldClass.isArray() && (findSupplierByField(fieldName, fieldClass.getComponentType()) != null);
}
}
private final Supplier<RandomField> supplier;
private final BiFunction<String, Class<?>, RandomField> supplierWithArguments;
protected RandomFieldSupplier(Supplier<RandomField> supplier) {
this(supplier, null);
}
protected RandomFieldSupplier(BiFunction<String, Class<?>, RandomField> supplierWithArguments) {
this(null, supplierWithArguments);
}
private RandomFieldSupplier(Supplier<RandomField> supplier,
BiFunction<String, Class<?>, RandomField> supplierWithArguments) {
this.supplier = supplier;
this.supplierWithArguments = supplierWithArguments;
}
/**
* Returns if this random field should be used to render the field name.
*
* @param fieldName name of the field
* @param fieldClass class of the field
* @return a boolean - returns true if a {@link RandomField} can be supplied
*/
public abstract boolean canSupply(String fieldName, Class<?> fieldClass);
/**
* Creates a <b>new instance</b> of {@link RandomField} to use for a
* {@link RandomFactory}.
*
* @return a new instance
* @deprecated use {@link #createRandomField(String, Class)} instead
*/
@Deprecated
public RandomField createRandomField() {
return createRandomField(null, null);
}
/**
* Creates a <b>new instance</b> of {@link RandomField} to use for a
* {@link RandomFactory}.
*
* @param fieldName the name of the field to be filled
* @param fieldClass the class of the field to be filled
* @return a new instance
*/
public RandomField createRandomField(String fieldName, Class<?> fieldClass) {
if (this.supplier != null) {
return this.supplier.get();
}
return this.supplierWithArguments.apply(fieldName, fieldClass);
}
}