Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
framework.Binder Maven / Gradle / Ivy
package framework;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import framework.annotation.Validator.ErrorAppender;
import framework.annotation.Validator.Errors;
/**
* Binder
*/
public class Binder implements ErrorAppender {
/**
* Parameters
*/
Map> parameters;
/**
* Files
*/
Map> files;
/**
* Validator(name, value)
*/
BiConsumer validator;
/**
* Errors
*/
public final Errors errors = new Errors();
/**
* @param parameters parameters
*/
public Binder(Map> parameters) {
this.parameters = parameters;
}
/**
* @param files Files
* @return Self
*/
public Binder files(Map> files) {
this.files = files;
return this;
}
/**
* @param validator Validator
* @return Self
*/
public Binder validator(BiConsumer validator) {
this.validator = validator;
return this;
}
/*
* (non-Javadoc)
*
* @see framework.AbstractValidator.ErrorAppender#addError(java.lang.String, java.lang.String, java.lang.String, java.lang.Object[])
*/
@Override
public void addError(String name, String value, String error, Object... keyValues) {
errors.addError(name, value, error, keyValues);
}
/**
* @param name Name
* @param clazz class
* @param text text
* @param error action if error(allow null:retry that text is "0")
* @return value
*/
Object convert(String name, String text, Type clazz, Function error) {
if (validator != null) {
validator.accept(name, text);
}
Function, Object> toNumber = f -> Try
.s(() -> f.apply(text), error == null ? (Function) (e -> f.apply("0")) : error)
.get();
if (clazz == String.class) {
return text == null ? error == null ? "" : error.apply(null) : text;
}
if (clazz == boolean.class || clazz == Boolean.class) {
if (text != null) {
Optional n = Tool.integer(text);
if (text.equalsIgnoreCase("false") || n.filter(i -> i == 0)
.isPresent()) {
return false;
}
if (text.equalsIgnoreCase("true") || n.filter(i -> i != 0)
.isPresent()) {
return true;
}
}
return error == null ? false : error.apply(null);
}
if (clazz == byte.class || clazz == Byte.class) {
return toNumber.apply(Byte::valueOf);
}
if (clazz == short.class || clazz == Short.class) {
return toNumber.apply(Short::valueOf);
}
if (clazz == int.class || clazz == Integer.class) {
return toNumber.apply(Integer::valueOf);
}
if (clazz == long.class || clazz == Long.class) {
return toNumber.apply(Long::valueOf);
}
if (clazz == char.class || clazz == Character.class) {
return text != null && text.length() > 0 ? text.charAt(0) : error == null ? '\0' : error.apply(null);
}
if (clazz == float.class || clazz == Float.class) {
return toNumber.apply(Float::valueOf);
}
if (clazz == double.class || clazz == Double.class) {
return toNumber.apply(Double::valueOf);
}
if (clazz == BigInteger.class) {
return toNumber.apply(BigInteger::new);
}
if (clazz == BigDecimal.class) {
return toNumber.apply(BigDecimal::new);
}
if (clazz == LocalDate.class) {
return Try.f(LocalDate::parse, (e, s) -> error == null ? null : (LocalDate) error.apply(null))
.apply(text);
}
if (clazz == LocalDateTime.class) {
return Try.f(LocalDateTime::parse, (e, s) -> error == null ? null : (LocalDateTime) error.apply(null))
.apply(text);
}
if (clazz == LocalTime.class) {
return Try.f(LocalTime::parse, (e, s) -> error == null ? null : (LocalTime) error.apply(null))
.apply(text);
}
if (Enum.class.isAssignableFrom((Class>) clazz)) {
return text != null ? Reflector.invoke(((Class>) clazz).getName() + ".valueOf", Tool.array(String.class), text)
: error == null ? ((Class>) clazz).getEnumConstants()[0] : error.apply(null);
}
return text;
}
/**
* @param name name
* @param clazz class
* @param parameterizedType Parameterized type
* @return value
*/
public Object bind(String name, Class> clazz, Type... parameterizedType) {
return bind(parameters, 0, name, clazz, parameterizedType);
}
/**
* @param name Name
* @param nest Nest level
* @param type Value type
* @return Mapper
*/
@SuppressWarnings("unchecked")
public Function> rebind(String name, int nest, Class> type) {
return i -> {
if (i instanceof Map) {
if (type == Map.class) {
return Stream.of(((Map>) i).entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> Tool., String>val(e.getValue(), j -> j.isEmpty() ? "" : j.get(0)))));
}
Object instance = Reflector.instance(type);
Reflector.fields(type)
.forEach(Try.biC((n, field) -> {
field.set(instance, bind((Map>) i, nest + 1, n, field.getType(), Reflector.getGenericParameters(field)));
}));
return Stream.of(instance);
}
if(i != null && type.isPrimitive() || Number.class.isAssignableFrom(type)) {
return Stream.of(((String) i).split("[^-.0-9]+")).map(j -> convert(name, j, type, null));
}
return Stream.of(convert(name, (String) i, type, null));
};
}
/**
* @param parameters Parameters
* @param nest Nest level
* @param name name
* @param clazz class
* @param parameterizedType Parameterized type
* @return value
*/
@SuppressWarnings("unchecked")
public Object bind(Map> parameters, int nest, String name, Class> clazz, Type... parameterizedType) {
if (clazz == null) {
return null;
}
// List values = Tool.or(parameters.get(name), () -> parameters.get(name + "[]")).orElse(null);
List sub = new ArrayList<>();
parameters.forEach((key, value) -> {
int prefixLength = name.length();
int start = key.indexOf('[', prefixLength);
int dot = key.indexOf('.', prefixLength);
int min = start < 0 ? (dot < 0 ? key.length() : dot) : (dot < 0 ? start : Math.min(start, dot));
String realKey = key.substring(0, min);
if (!name.equals(realKey)) {
return;
}
if (key.equals(realKey)) {
sub.addAll(value);
}
if (start >= 0 && (dot == -1 || start < dot)) {
int end = key.indexOf(']', prefixLength);
if (start + 1 < end) {
int index = Integer.parseInt(key.substring(start + 1, end));
while (sub.size() <= index) {
sub.add(null);
}
Object o = sub.get(index);
if (dot > 0) {
if (!(o instanceof Map)) {
sub.set(index, o = new LinkedHashMap<>());
}
((Map>) o).put(key.substring(dot + 1), value);
} else {
for (String i : value) {
sub.set(index, i);
}
}
} else {
key = key.substring(end + 1);
if (key.isEmpty()) {
for (String i : value) {
sub.add(i);
}
} else {
sub.add(Tool.map(key, value));
}
}
}
});
String first = sub.isEmpty() ? Tool.getFirst(parameters, name)
.orElse(null)
: sub.get(0)
.toString();
if (sub.size() == 1 && "".equals(first)) {
sub.clear();
}
// Array
Class> component = clazz.getComponentType();
if (component != null) {
Stream stream = sub.stream()
.map(value -> convert(name, (String) value, component, null));
if (clazz == int[].class) {
return stream.mapToInt(Integer.class::cast)
.toArray();
}
if (clazz == long[].class) {
return stream.mapToLong(Long.class::cast)
.toArray();
}
if (clazz == double[].class) {
return stream.mapToDouble(Double.class::cast)
.toArray();
}
if (clazz == boolean[].class) {
Object[] from = stream.toArray();
boolean[] to = new boolean[from.length];
for (int i = 0, end = from.length; i < end; i++) {
to[i] = (boolean) from[i];
}
return to;
}
if (clazz == byte[].class) {
if (sub.size() == 1 && files.containsKey(first)) {
return Tool.val(files.get(first), t -> t.l == null ? Try.f(Files::readAllBytes)
.apply(t.r.toPath()) : t.l);
}
Object[] from = stream.toArray();
byte[] to = new byte[from.length];
for (int i = 0, end = from.length; i < end; i++) {
to[i] = (byte) from[i];
}
return to;
}
if (clazz == short[].class) {
Object[] from = stream.toArray();
short[] to = new short[from.length];
for (int i = 0, end = from.length; i < end; i++) {
to[i] = (short) from[i];
}
return to;
}
if (clazz == char[].class) {
Object[] from = stream.toArray();
char[] to = new char[from.length];
for (int i = 0, end = from.length; i < end; i++) {
to[i] = (char) from[i];
}
return to;
}
if (clazz == float[].class) {
Object[] from = stream.toArray();
float[] to = new float[from.length];
for (int i = 0, end = from.length; i < end; i++) {
to[i] = (float) from[i];
}
return to;
}
return sub.stream()
.flatMap(rebind(name, nest, component))
.toArray(n -> (Object[]) Array.newInstance(component, n));
}
Function> genericType = index -> {
if(parameterizedType.length > index) {
return (Class>)parameterizedType[index];
}
Type type = clazz.getGenericSuperclass();
if(type != null) {
Type[] types = ((ParameterizedType)type).getActualTypeArguments();
if(types != null && types.length > index) {
return (Class>)types[index];
}
}
return Object.class;
};
if (List.class.isAssignableFrom(clazz)) {
Supplier> constructor = Try.s(() -> (List) Stream.of(clazz.getDeclaredConstructors())
.filter(c -> c.getParameterCount() == 0).findFirst()
.orElseGet(Try.s(ArrayList.class::getConstructor)).newInstance());
return sub.stream()
.flatMap(rebind(name, nest, genericType.apply(0)))
.collect(constructor, List::add, List::addAll);
}
if (Set.class.isAssignableFrom(clazz)) {
Supplier> constructor = Try.s(() -> (Set) Stream.of(clazz.getDeclaredConstructors())
.filter(c -> c.getParameterCount() == 0).findFirst()
.orElseGet(Try.s(HashSet.class::getConstructor)).newInstance());
return sub.stream()
.flatMap(rebind(name, nest, genericType.apply(0)))
.collect(constructor, Set::add, Set::addAll);
}
if (Map.class.isAssignableFrom(clazz)) {
Supplier> constructor = Try.s(() -> (Map) Stream.of(clazz.getDeclaredConstructors())
.filter(c -> c.getParameterCount() == 0).findFirst()
.orElseGet(Try.s(Attributes.Impl.class::getConstructor)).newInstance());
String prefix = name + ".";
return parameters.entrySet()
.stream()
.filter(e -> e.getKey()
.startsWith(prefix))
.collect(constructor, (map, e) -> map.put(e.getKey()
.substring(prefix.length()), bind(parameters, nest + 1, e.getKey(), genericType.apply(1))), Map::putAll);
}
if (clazz == Optional.class) {
Class> c = genericType.apply(0);
return c == String.class ? Tool.string(bind(parameters, nest + 1, name, c)) : Tool.of(bind(parameters, nest + 1, name, c));
}
if (!clazz.isPrimitive() && !Enum.class.isAssignableFrom(clazz) && !Tool.val(clazz.getName(), i -> Stream.of("java.", "com.sun.")
.anyMatch(i::startsWith))) {
Constructor> constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
if (constructor.getParameterCount() > 0) {
Object[] args = Stream.of(constructor.getParameters())
.map(p -> bind(name + "." + p.getName(), p.getType(), Reflector.getGenericParameters(p)))
.toArray();
return Try.s(() -> constructor.newInstance(args))
.get();
}
Object o = Try.s(constructor::newInstance)
.get();
Reflector.fields(o.getClass())
.forEach(Try.biC((fieldName, field) -> field.set(o, bind(name + "." + fieldName, field.getType(), Reflector.getGenericParameters(field)))));
return o;
}
return convert(name, first, clazz, nest == 0 || clazz.isPrimitive() && clazz != char.class ? null : e -> null);
}
}