package com.cardshifter.modapi.base;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class Retrievers {
public static <T extends Component> ComponentRetriever<T> component(Class<T> clazz) {
return new ComponentRetriever<>(clazz);
}
public static <T extends Component> ComponentRetriever<T> singleton(Class<T> class1) {
return new ComponentRetriever<T>(null) {
@Override
public boolean has(Entity entity) {
return get(entity) != null;
}
@Override
public T get(Entity entity) {
return singleton(entity.getGame(), class1);
}
};
}
public static <T extends Component> T singleton(ECSGame game, Class<T> class1) {
Set<Entity> all = game.getEntitiesWithComponent(class1);
if (all.size() != 1) {
throw new IllegalStateException("Expected to find exactly one " + class1.getSimpleName() + ", found " + all.size());
}
return all.iterator().next().getComponent(class1);
}
public static void inject(Object object, ECSGame game) {
List<Field> fields = AccessController.doPrivileged((PrivilegedAction<List<Field>>)() -> {
Class<?> clazz = object.getClass();
List<Field> result = new ArrayList<>();
do {
result.addAll(Arrays.asList(clazz.getDeclaredFields()));
clazz = clazz.getSuperclass();
}
while (clazz != Object.class);
return result;
});
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
fields.stream().filter(field -> field.getAnnotation(Retriever.class) != null).forEach(field -> injectField(object, field, game));
fields.stream().filter(field -> field.getAnnotation(RetrieverSingleton.class) != null).forEach(field -> injectSingleton(object, field, game));
return null;
});
}
private static void injectSingleton(Object obj, Field field, ECSGame game) {
Class<? extends Component> clazz = field.getType().asSubclass(Component.class);
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
field.setAccessible(true);
try {
field.set(obj, Retrievers.singleton(game, clazz));
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
return null;
});
}
private static void injectField(Object obj, Field field, ECSGame game) {
if (field.getType() != ComponentRetriever.class) {
throw new RuntimeException(field.getType() + " is not a ComponentRetriever");
}
Type genericFieldType = field.getGenericType();
if (genericFieldType instanceof ParameterizedType) {
ParameterizedType aType = (ParameterizedType) genericFieldType;
Type[] fieldArgTypes = aType.getActualTypeArguments();
Class<?> fieldArgClass = (Class<?>) fieldArgTypes[0];
try {
field.setAccessible(true);
field.set(obj, Retrievers.component(fieldArgClass.asSubclass(Component.class)));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}