Entry.java 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package me.lortseam.completeconfig.data;
  2. import lombok.Getter;
  3. import lombok.NonNull;
  4. import lombok.experimental.Accessors;
  5. import lombok.extern.log4j.Log4j2;
  6. import me.lortseam.completeconfig.CompleteConfig;
  7. import me.lortseam.completeconfig.api.ConfigContainer;
  8. import me.lortseam.completeconfig.api.ConfigEntry;
  9. import me.lortseam.completeconfig.data.transform.Transformation;
  10. import me.lortseam.completeconfig.data.transform.Transformer;
  11. import me.lortseam.completeconfig.data.structure.DataPart;
  12. import me.lortseam.completeconfig.data.structure.Identifiable;
  13. import me.lortseam.completeconfig.data.structure.TooltipSupplier;
  14. import me.lortseam.completeconfig.text.TranslationKey;
  15. import me.lortseam.completeconfig.exception.IllegalAnnotationParameterException;
  16. import me.lortseam.completeconfig.extensions.CompleteConfigExtension;
  17. import me.lortseam.completeconfig.util.ReflectionUtils;
  18. import net.fabricmc.api.EnvType;
  19. import net.fabricmc.api.Environment;
  20. import org.apache.commons.lang3.StringUtils;
  21. import org.spongepowered.configurate.CommentedConfigurationNode;
  22. import org.spongepowered.configurate.serialize.SerializationException;
  23. import java.beans.IntrospectionException;
  24. import java.lang.reflect.Field;
  25. import java.lang.reflect.InvocationTargetException;
  26. import java.lang.reflect.Method;
  27. import java.lang.reflect.Type;
  28. import java.util.Arrays;
  29. import java.util.Objects;
  30. import java.util.Optional;
  31. import java.util.function.UnaryOperator;
  32. @Log4j2(topic = "CompleteConfig")
  33. public class Entry<T> implements DataPart, Identifiable, TooltipSupplier {
  34. private static final Transformer DEFAULT_TRANSFORMER = Entry::new;
  35. static {
  36. for (Transformation[] transformations : CompleteConfig.collectExtensions(CompleteConfigExtension.class, CompleteConfigExtension::getTransformations)) {
  37. ConfigRegistry.register(transformations);
  38. }
  39. }
  40. static Entry<?> of(BaseCollection parent, Field field, ConfigContainer object) {
  41. EntryOrigin origin = new EntryOrigin(parent, field, object);
  42. return ConfigRegistry.getTransformations().stream().filter(transformation -> {
  43. return transformation.test(origin);
  44. }).findFirst().map(Transformation::getTransformer).orElse(DEFAULT_TRANSFORMER).transform(origin);
  45. }
  46. protected final EntryOrigin origin;
  47. @Getter
  48. private final Type type;
  49. @Getter
  50. private final Class<T> typeClass;
  51. @Getter
  52. private final String id;
  53. @Getter
  54. private final T defaultValue;
  55. @Environment(EnvType.CLIENT)
  56. private TranslationKey translation;
  57. @Environment(EnvType.CLIENT)
  58. private TranslationKey[] tooltipTranslation;
  59. @Accessors(fluent = true)
  60. @Getter
  61. private final boolean requiresRestart;
  62. private final String comment;
  63. private final UnaryOperator<T> valueModifier;
  64. protected Entry(EntryOrigin origin, UnaryOperator<T> valueModifier) {
  65. ConfigRegistry.register(origin);
  66. this.origin = origin;
  67. if (!origin.getField().isAccessible()) {
  68. origin.getField().setAccessible(true);
  69. }
  70. type = origin.getType();
  71. typeClass = (Class<T>) ReflectionUtils.getTypeClass(type);
  72. this.valueModifier = valueModifier;
  73. defaultValue = getValue();
  74. Optional<ConfigEntry> annotation = origin.getOptionalAnnotation(ConfigEntry.class);
  75. id = annotation.isPresent() && !StringUtils.isBlank(annotation.get().value()) ? annotation.get().value() : origin.getField().getName();
  76. requiresRestart = annotation.isPresent() && annotation.get().requiresRestart();
  77. comment = annotation.isPresent() && !StringUtils.isBlank(annotation.get().comment()) ? annotation.get().comment() : null;
  78. }
  79. protected Entry(EntryOrigin origin) {
  80. this(origin, null);
  81. }
  82. public T getValue() {
  83. if (update()) {
  84. return getValue();
  85. }
  86. return getFieldValue();
  87. }
  88. private T getFieldValue() {
  89. try {
  90. return (T) Objects.requireNonNull(origin.getField().get(origin.getObject()), origin.getField().toString());
  91. } catch (IllegalAccessException e) {
  92. throw new RuntimeException("Failed to get entry value", e);
  93. }
  94. }
  95. public void setValue(@NonNull T value) {
  96. update(value);
  97. }
  98. private boolean update() {
  99. return update(getFieldValue());
  100. }
  101. private boolean update(T value) {
  102. if (valueModifier != null) {
  103. value = valueModifier.apply(value);
  104. }
  105. if (value.equals(getFieldValue())) {
  106. return false;
  107. }
  108. set(value);
  109. return true;
  110. }
  111. private void set(T value) {
  112. try {
  113. Optional<Method> writeMethod = ReflectionUtils.getWriteMethod(origin.getField());
  114. if (writeMethod.isPresent()) {
  115. writeMethod.get().invoke(origin.getObject(), value);
  116. } else {
  117. origin.getField().set(origin.getObject(), value);
  118. }
  119. } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
  120. throw new RuntimeException("Failed to set entry value", e);
  121. }
  122. }
  123. @Override
  124. public TranslationKey getTranslation() {
  125. if (translation == null) {
  126. Optional<ConfigEntry> annotation = origin.getOptionalAnnotation(ConfigEntry.class);
  127. if (annotation.isPresent() && !StringUtils.isBlank(annotation.get().translationKey())) {
  128. translation = origin.getParent().getTranslation().append(annotation.get().translationKey());
  129. } else {
  130. translation = origin.getParent().getTranslation().append(id);
  131. }
  132. }
  133. return translation;
  134. }
  135. @Override
  136. public TranslationKey[] getTooltipTranslation() {
  137. if (tooltipTranslation == null) {
  138. Optional<ConfigEntry> annotation = origin.getOptionalAnnotation(ConfigEntry.class);
  139. if (annotation.isPresent() && annotation.get().tooltipTranslationKeys().length > 0) {
  140. tooltipTranslation = Arrays.stream(annotation.get().tooltipTranslationKeys()).map(key -> {
  141. if (StringUtils.isBlank(key)) {
  142. throw new IllegalAnnotationParameterException("Tooltip translation key of entry " + origin.getField() + " may not be blank");
  143. }
  144. return getTranslation().root().append(key);
  145. }).toArray(TranslationKey[]::new);
  146. } else {
  147. tooltipTranslation = getTranslation().appendTooltip().orElse(new TranslationKey[0]);
  148. }
  149. }
  150. return tooltipTranslation;
  151. }
  152. @Override
  153. public void apply(CommentedConfigurationNode node) {
  154. try {
  155. setValue((T) node.get(type));
  156. } catch (SerializationException e) {
  157. logger.error("Failed to apply value to entry", e);
  158. }
  159. }
  160. @Override
  161. public void fetch(CommentedConfigurationNode node) {
  162. try {
  163. node.set(type, getValue());
  164. if (comment != null) {
  165. node.comment(comment);
  166. }
  167. } catch (SerializationException e) {
  168. logger.error("Failed to fetch value from entry", e);
  169. }
  170. }
  171. }