MetadatableHelper.java
package it.fulminazzo.yagl.metadatable;
import it.fulminazzo.fulmicollection.objects.Refl;
import it.fulminazzo.fulmicollection.utils.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* A support class for {@link Metadatable}.
*/
class MetadatableHelper implements Metadatable {
/**
* A function used to convert the variables in {@link #apply(Object)}.
*/
private static final @NotNull Function<@NotNull String, @NotNull String> VARIABLE_FORMAT = s -> "<" + s + ">";
private final @NotNull Metadatable internal;
private final @NotNull List<Object> alreadyApplied;
/**
* Instantiates a new Metadatable helper.
*
* @param internal the internal
*/
public MetadatableHelper(final @NotNull Metadatable internal) {
this.internal = internal;
this.alreadyApplied = new ArrayList<>();
}
/**
* Applies all the current variables to the given object fields,
* by replacing in every string all the variables in the format {@link #VARIABLE_FORMAT}.
*
* @param <T> the type parameter
* @param object the object
* @return the object parsed
*/
@SuppressWarnings("unchecked")
public <T> T apply(final T object) {
if (object == null) return null;
if (!ReflectionUtils.isPrimitiveOrWrapper(object.getClass()) && this.alreadyApplied.contains(object)) return object;
this.alreadyApplied.add(object);
if (object.getClass().isEnum()) return object;
else if (object.getClass().isArray()) {
Object[] array = (Object[]) object;
for (Object o : array) apply(o);
}
else if (object instanceof String) return (T) apply((String) object);
else if (object instanceof Collection) return (T) apply((Collection<Object>) object);
else if (object instanceof Map) return (T) apply((Map<Object, Object>) object);
else if (!ReflectionUtils.isPrimitiveOrWrapper(object.getClass())) {
final Refl<T> refl = new Refl<>(object);
for (Field field : refl.getNonStaticFields())
ReflectionUtils.setAccessible(field)
.filter(f -> !f.isAnnotationPresent(IgnoreApply.class))
.map(f -> f.get(object))
.filter(o -> !o.toString().contains("Lambda"))
.ifPresent(o -> field.set(object, apply(o)));
}
return object;
}
/**
* Applies all the current variables to the given string.
*
* @param string the string
* @return the string parsed
*/
String apply(@NotNull String string) {
for (final String v : variables().keySet()) {
final String s = getVariable(v);
if (s == null) continue;
final Matcher matcher = Pattern.compile("([^\\\\]|^)" + VARIABLE_FORMAT.apply(v)).matcher(string);
while (matcher.find())
string = string.substring(0, matcher.start()) + matcher.group(1) + s +
string.substring(matcher.end());
}
return string;
}
/**
* Applies all the current variables to the given collection.
*
* @param collection the collection
* @return the collection parsed
*/
@SuppressWarnings("unchecked")
@NotNull Collection<Object> apply(Collection<Object> collection) {
Class<?> clazz = collection.getClass();
// In the case of creation with Arrays.asList()
if (clazz.getCanonicalName().equals(Arrays.class.getCanonicalName() + ".ArrayList"))
clazz = ArrayList.class;
Class<Collection<Object>> finalClass = (Class<Collection<Object>>) clazz;
return collection.stream().map(this::apply)
.collect(Collectors.toCollection(() -> new Refl<>(finalClass, new Object[0]).getObject()));
}
/**
* Applies all the current variables to the given map.
*
* @param map the map
* @return the map parsed
*/
@NotNull Map<Object, Object> apply(final @NotNull Map<Object, Object> map) {
final List<Object> keys = new ArrayList<>(map.keySet());
for (Object key : keys) {
if (key == null) continue;
Object value = map.get(key);
if (value != null) {
map.remove(key);
map.put(apply(key), apply(value));
}
}
return map;
}
@Override
public @NotNull Map<String, String> variables() {
return this.internal.variables();
}
}