Skip to content

Commit

Permalink
Infer type info
Browse files Browse the repository at this point in the history
  • Loading branch information
ThatGravyBoat committed Oct 4, 2024
1 parent 4c8fd42 commit a554942
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.teamresourceful.resourcefulconfig.api.annotations;

import com.teamresourceful.resourcefulconfig.api.types.options.EntryType;
import org.jetbrains.annotations.ApiStatus;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand All @@ -13,7 +14,12 @@

String id();

EntryType type();
/**
* @deprecated Type is no longer required and it will be inferred instead of parsing.
*/
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "1.21.2")
EntryType type() default EntryType.CANNOT_BE_PARSED;

String translation() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,12 @@ private ResourcefulConfig loadConfigClass(Class<?> clazz, Consumer<ConfigPatchEv
config.load(handler);
config.save();
return config;
}catch (Exception e) {
ModUtils.log("Failed to create config for " + clazz.getName(), e);
} catch (Exception e) {
if (ModUtils.isDev()) {
throw new RuntimeException("Failed to create config for " + clazz.getName(), e);
} else {
ModUtils.log("Failed to create config for " + clazz.getName(), e);
}
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ public interface ResourcefulConfigObjectEntry extends ResourcefulConfigEntry {
@NotNull
LinkedHashMap<String, ResourcefulConfigEntry> entries();

default Object instance() {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,14 @@ static Component toComponent(Object value, Component defaultValue) {
}
return Component.literal(Objects.toString(value));
}

static Component toSpeifiedComponent(Object value, Component specified) {
if (value instanceof Translatable translatable) {
return Component.translatable(translatable.getTranslationKey());
}
if (value instanceof StringRepresentable string) {
return Component.literal(string.getSerializedName());
}
return specified;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ public enum EntryType {
BOOLEAN(type -> type == boolean.class || type == Boolean.class),
STRING(type -> type == String.class),
ENUM(Class::isEnum),
OBJECT(type -> type.isAnnotationPresent(ConfigObject.class))
OBJECT(type -> type.isAnnotationPresent(ConfigObject.class)),

CANNOT_BE_PARSED(type -> false)
;

private final Predicate<Class<?>> predicate;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.teamresourceful.resourcefulconfig.client.components.options.types;

import com.teamresourceful.resourcefulconfig.api.types.entries.ResourcefulConfigObjectEntry;
import com.teamresourceful.resourcefulconfig.api.types.info.Translatable;
import com.teamresourceful.resourcefulconfig.client.UIConstants;
import com.teamresourceful.resourcefulconfig.client.components.ModSprites;
import com.teamresourceful.resourcefulconfig.client.components.base.BaseWidget;
Expand Down Expand Up @@ -30,15 +31,15 @@ public ObjectOptionWidget(ResourcefulConfigObjectEntry entry) {
protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
graphics.blitSprite(ModSprites.ofButton(this.isHovered()), getX(), getY(), getWidth(), getHeight());

int contentWidth = font.width(UIConstants.EDIT_OBJECT) + SPACING + SIZE;
int contentWidth = font.width(UIConstants.EDIT) + SPACING + SIZE;

graphics.blitSprite(
ModSprites.EDIT,
getX() + (getWidth() - contentWidth) / 2, getY() + PADDING,
SIZE, SIZE
);
graphics.drawString(
font, UIConstants.EDIT_OBJECT,
font, UIConstants.EDIT,
getX() + (getWidth() - contentWidth) / 2 + SIZE + SPACING,
getY() + (getHeight() - font.lineHeight) / 2 + 1,
UIConstants.TEXT_TITLE
Expand All @@ -59,7 +60,7 @@ protected ObjectEditOverlay(ResourcefulConfigObjectEntry entry) {
super();
this.entry = entry;

this.title = UIConstants.EDIT_OBJECT;
this.title = Translatable.toSpeifiedComponent(entry.instance(), UIConstants.EDIT_OBJECT);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.teamresourceful.resourcefulconfig.common.loader;

import com.teamresourceful.resourcefulconfig.api.annotations.Category;
import com.teamresourceful.resourcefulconfig.api.annotations.Config;
import com.teamresourceful.resourcefulconfig.api.annotations.ConfigButton;
import com.teamresourceful.resourcefulconfig.api.annotations.ConfigEntry;
import com.teamresourceful.resourcefulconfig.api.annotations.*;
import com.teamresourceful.resourcefulconfig.api.loader.ConfigParser;
import com.teamresourceful.resourcefulconfig.api.types.ResourcefulConfig;
import com.teamresourceful.resourcefulconfig.api.types.entries.Observable;
Expand All @@ -20,6 +17,8 @@
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public class JavaConfigParser implements ConfigParser {

Expand All @@ -40,19 +39,21 @@ private static <T extends ResourcefulConfig> T populateEntries(Class<?> clazz, T
for (Field field : clazz.getDeclaredFields()) {
ConfigEntry data = assertEntry(field);
if (data != null) {
if (data.type() == EntryType.OBJECT) {
var type = getEntryType(field, data.type());

if (type == EntryType.OBJECT) {
Object instance = ParsingUtils.getField(field, null);
ParsedObjectEntry objectEntry = new ParsedObjectEntry(data.type(), field);
ParsedObjectEntry objectEntry = new ParsedObjectEntry(field);
populateEntries(instance, objectEntry);
config.entries().put(data.id(), objectEntry);
} else if (field.getType() == Observable.class) {
ParsedObservableEntry observableEntry = ParsedObservableEntry.of(data.type(), field, null);
ParsedObservableEntry observableEntry = ParsedObservableEntry.of(type, field, null);
config.entries().put(data.id(), observableEntry);
if (observableEntry.defaultValue() == null) {
throw new IllegalArgumentException("Entry " + field.getName() + " must not have a null default value!");
}
} else {
ParsedInstanceEntry instanceEntry = new ParsedInstanceEntry(data.type(), field, null);
ParsedInstanceEntry instanceEntry = new ParsedInstanceEntry(type, field, null);
config.entries().put(data.id(), instanceEntry);
if (instanceEntry.defaultValue() == null) {
throw new IllegalArgumentException("Entry " + field.getName() + " must not have a null default value!");
Expand Down Expand Up @@ -84,16 +85,17 @@ private static <T extends ResourcefulConfig> T populateEntries(Class<?> clazz, T
private static void populateEntries(Object instance, ParsedObjectEntry entry) {
assertValidClass(instance.getClass());
for (Field field : instance.getClass().getDeclaredFields()) {
ConfigEntry data = assertAcciessbleEntry(instance, field);
ConfigEntry data = assertAccessibleEntry(instance, field);
if (data == null) continue;
if (data.type() == EntryType.OBJECT) {
EntryType type = getEntryType(field, data.type());
if (type == EntryType.OBJECT) {
throw new IllegalArgumentException("Entry " + field.getName() + " cannot be an object!");
}
ResourcefulConfigValueEntry valueEntry;
if (field.getType() == Observable.class) {
valueEntry = ParsedObservableEntry.of(data.type(), field, null);
valueEntry = ParsedObservableEntry.of(type, field, null);
} else {
valueEntry = new ParsedInstanceEntry(data.type(), field, instance);
valueEntry = new ParsedInstanceEntry(type, field, instance);
}

if (valueEntry.defaultValue() == null) {
Expand All @@ -114,7 +116,7 @@ private static ConfigButton assertButton(Field field) {
}

private static ConfigEntry assertEntry(Field field) {
ConfigEntry data = assertAcciessbleEntry(null, field);
ConfigEntry data = assertAccessibleEntry(null, field);
String name = field.getName();
if (data == null) return null;
if (!Modifier.isStatic(field.getModifiers()))
Expand All @@ -139,21 +141,22 @@ private static Class<?> getFieldType(Object instance, Field field, EntryType ent
return type;
}

private static ConfigEntry assertAcciessbleEntry(Object instance, Field field) {
private static ConfigEntry assertAccessibleEntry(Object instance, Field field) {
ConfigEntry data = field.getAnnotation(ConfigEntry.class);
String name = field.getName();
if (data == null) return null;
if (!Modifier.isPublic(field.getModifiers()))
throw new IllegalArgumentException("Entry " + name + " is not public!");
if (data.type().mustBeFinal() && !Modifier.isFinal(field.getModifiers()))
throw new IllegalArgumentException("Entry " + name + " must be final!");
if (!data.type().mustBeFinal() && Modifier.isFinal(field.getModifiers()))
throw new IllegalArgumentException("Entry " + name + " must not be final!");
Class<?> type = getFieldType(instance, field, data.type());
if (!data.type().test(type))
throw new IllegalArgumentException("Entry " + field.getName() + " is not of type " + data.type().name() + "!");
if (data.id().contains("."))
throw new IllegalArgumentException("Entry " + field.getName() + " has an invalid id! Ids must not contain '.'");
EntryType type = getEntryType(field, data.type());
List<String> errors = new ArrayList<>();
if (!Modifier.isPublic(field.getModifiers())) errors.add("Entry is not public!");
if (type.mustBeFinal() && !Modifier.isFinal(field.getModifiers())) errors.add("Entry must be final!");
if (!type.mustBeFinal() && Modifier.isFinal(field.getModifiers())) errors.add("Entry must not be final!");
if (!type.test(getFieldType(instance, field, type))) errors.add("Entry has an invalid type for " + type + "!");
if (data.id().contains(".")) errors.add(data.id() + " is an invalid id!");
if (!errors.isEmpty()) {
throw new IllegalArgumentException(
"Entry " + name + " is invalid!\n\t" + String.join("\n\t", errors) + "\n"
);
}
return data;
}

Expand All @@ -166,13 +169,39 @@ private static <T extends Annotation> T assertAnnotation(AnnotatedElement elemen
}

private static void assertValidClass(Class<?> config) {
if (!Modifier.isPublic(config.getModifiers()))
throw new IllegalArgumentException("Config class must be public!");
if (config.getEnclosingClass() != null && !Modifier.isStatic(config.getModifiers()))
throw new IllegalArgumentException("Config class must be static!");
if (config.isEnum()) throw new IllegalArgumentException("Config class cannot be an enum!");
if (config.isInterface()) throw new IllegalArgumentException("Config class cannot be an interface!");
if (config.isAnnotation()) throw new IllegalArgumentException("Config class cannot be an annotation!");
if (config.isRecord()) throw new IllegalArgumentException("Config class cannot be a record!");
List<String> errors = new ArrayList<>();
if (!Modifier.isPublic(config.getModifiers())) errors.add("Config class must be public!");
if (config.getEnclosingClass() != null && !Modifier.isStatic(config.getModifiers())) errors.add("Inner config class must be static!");
if (config.isEnum()) errors.add("Config class cannot be an enum!");
if (config.isInterface()) errors.add("Config class cannot be an interface!");
if (config.isAnnotation()) errors.add("Config class cannot be an annotation!");
if (config.isRecord()) errors.add("Config class cannot be a record!");

if (!errors.isEmpty()) {
throw new IllegalArgumentException(
"Config class " + config.getName() + " is invalid!\n\t" + String.join("\n\t", errors) + "\n"
);
}
}

private static EntryType getEntryType(Field field, EntryType defaultValue) {
Class<?> fieldType = field.getType();
if (fieldType == Observable.class) fieldType = ((Observable<?>) ParsingUtils.getField(field, null)).type();
if (fieldType.isArray()) fieldType = fieldType.getComponentType();
return getEntryType(fieldType, defaultValue);
}

private static EntryType getEntryType(Class<?> type, EntryType defaultValue) {
if (type.getAnnotation(ConfigObject.class) != null) return EntryType.OBJECT;
if (type == Long.TYPE || type == Long.class) return EntryType.INTEGER;
if (type == Integer.TYPE || type == Integer.class) return EntryType.INTEGER;
if (type == Short.TYPE || type == Short.class) return EntryType.INTEGER;
if (type == Byte.TYPE || type == Byte.class) return EntryType.INTEGER;
if (type == Double.TYPE || type == Double.class) return EntryType.DOUBLE;
if (type == Float.TYPE || type == Float.class) return EntryType.DOUBLE;
if (type == Boolean.TYPE || type == Boolean.class) return EntryType.BOOLEAN;
if (type == String.class) return EntryType.STRING;
if (type.isEnum()) return EntryType.ENUM;
return defaultValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@ public record ParsedObjectEntry(
LinkedHashMap<String, ResourcefulConfigEntry> entries
) implements ResourcefulConfigObjectEntry {

public ParsedObjectEntry(EntryType type, Field field) {
this(type, field, EntryData.of(field, field.getType()), new LinkedHashMap<>());
public ParsedObjectEntry(Field field) {
this(EntryType.OBJECT, field, EntryData.of(field, field.getType()), new LinkedHashMap<>());
}

@Override
public void reset() {
entries.values().forEach(ResourcefulConfigEntry::reset);
}

@Override
public Object instance() {
try {
return field.get(null);
} catch (Exception e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public static Path getConfigPath() {
throw new NotImplementedException("Not implemented yet");
}

@Contract(pure = true)
@ExpectPlatform
public static boolean isDev() {
throw new NotImplementedException("Not implemented yet");
}

public static void log(String message) {
LOGGER.info("[ResourcefulConfig] {}", message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ public class ModUtilsImpl {
public static Path getConfigPath() {
return FabricLoader.getInstance().getConfigDir();
}

@org.jetbrains.annotations.Contract(pure = true)
public static boolean isDev() {
return FabricLoader.getInstance().isDevelopmentEnvironment();
}
}
Loading

0 comments on commit a554942

Please sign in to comment.