更新多个类以支持过期策略和序列化功能,包括Assert.java、ContainerGui.java、EntryLoader.java、ExpirationListener.java、ExpirationPolicy.java、ExpiringEntryLoader.java、ExpiringMap.java、ExpiringValue.java、Gui.java、NamedThreadFactory.java、PageList.java、SerializeHelper.java,并修改pom.xml以引入必要的依赖。

This commit is contained in:
YuTian 2025-02-06 01:32:01 +08:00
parent ddc438b2d3
commit b117a26310
13 changed files with 1656 additions and 1547 deletions

View File

@ -121,6 +121,11 @@
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.18.2</version> <version>2.18.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-afterburner</artifactId>
<version>2.18.2</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.luben</groupId> <groupId>com.github.luben</groupId>
<artifactId>zstd-jni</artifactId> <artifactId>zstd-jni</artifactId>

View File

@ -7,11 +7,11 @@ package com.io.yutian.elementoriginlib.expiringmap;
* @param <V> Value type * @param <V> Value type
*/ */
public interface EntryLoader<K, V> { public interface EntryLoader<K, V> {
/** /**
* Called to load a new value for the {@code key} into an expiring map. * Called to load a new value for the {@code key} into an expiring map.
* *
* @param key to load a value for * @param key to load a value for
* @return new value to load * @return new value to load
*/ */
V load(K key); V load(K key);
} }

View File

@ -7,11 +7,11 @@ package com.io.yutian.elementoriginlib.expiringmap;
* @param <V> Value type * @param <V> Value type
*/ */
public interface ExpirationListener<K, V> { public interface ExpirationListener<K, V> {
/** /**
* Called when a map entry expires. * Called when a map entry expires.
* *
* @param key Expired key * @param key Expired key
* @param value Expired value * @param value Expired value
*/ */
void expired(K key, V value); void expired(K key, V value);
} }

View File

@ -4,8 +4,12 @@ package com.io.yutian.elementoriginlib.expiringmap;
* Determines how ExpiringMap entries should be expired. * Determines how ExpiringMap entries should be expired.
*/ */
public enum ExpirationPolicy { public enum ExpirationPolicy {
/** Expires entries based on when they were last accessed */ /**
ACCESSED, * Expires entries based on when they were last accessed
/** Expires entries based on when they were created */ */
CREATED; ACCESSED,
/**
* Expires entries based on when they were created
*/
CREATED;
} }

View File

@ -7,11 +7,11 @@ package com.io.yutian.elementoriginlib.expiringmap;
* @param <V> Value type * @param <V> Value type
*/ */
public interface ExpiringEntryLoader<K, V> { public interface ExpiringEntryLoader<K, V> {
/** /**
* Called to load a new value for the {@code key} into an expiring map. * Called to load a new value for the {@code key} into an expiring map.
* *
* @param key to load a value for * @param key to load a value for
* @return contains new value to load along with its expiry duration * @return contains new value to load along with its expiry duration
*/ */
ExpiringValue<V> load(K key); ExpiringValue<V> load(K key);
} }

View File

@ -8,115 +8,115 @@ import java.util.concurrent.TimeUnit;
* @param <V> the type of value being stored * @param <V> the type of value being stored
*/ */
public final class ExpiringValue<V> { public final class ExpiringValue<V> {
private static final long UNSET_DURATION = -1L; private static final long UNSET_DURATION = -1L;
private final V value; private final V value;
private final ExpirationPolicy expirationPolicy; private final ExpirationPolicy expirationPolicy;
private final long duration; private final long duration;
private final TimeUnit timeUnit; private final TimeUnit timeUnit;
/** /**
* Creates an ExpiringValue to be stored in an {@link ExpiringMap}. The map's default values for * Creates an ExpiringValue to be stored in an {@link ExpiringMap}. The map's default values for
* {@link ExpirationPolicy expiration policy} and {@link ExpiringMap#getExpiration()} expiration} will be used. * {@link ExpirationPolicy expiration policy} and {@link ExpiringMap#getExpiration()} expiration} will be used.
* *
* @param value the value to store * @param value the value to store
* @see ExpiringMap#put(Object, Object) * @see ExpiringMap#put(Object, Object)
*/ */
public ExpiringValue(V value) { public ExpiringValue(V value) {
this(value, UNSET_DURATION, null, null); this(value, UNSET_DURATION, null, null);
}
/**
* Creates an ExpiringValue to be stored in an {@link ExpiringMap}. The map's default
* {@link ExpiringMap#getExpiration()} expiration} will be used.
*
* @param value the value to store
* @param expirationPolicy the expiration policy for the value
* @see ExpiringMap#put(Object, Object, ExpirationPolicy)
*/
public ExpiringValue(V value, ExpirationPolicy expirationPolicy) {
this(value, UNSET_DURATION, null, expirationPolicy);
}
/**
* Creates an ExpiringValue to be stored in an {@link ExpiringMap}. The map's default {@link ExpirationPolicy
* expiration policy} will be used.
*
* @param value the value to store
* @param duration the length of time after an entry is created that it should be removed
* @param timeUnit the unit that {@code duration} is expressed in
* @see ExpiringMap#put(Object, Object, long, TimeUnit)
* @throws NullPointerException on null timeUnit
*/
public ExpiringValue(V value, long duration, TimeUnit timeUnit) {
this(value, duration, timeUnit, null);
if (timeUnit == null) {
throw new NullPointerException();
}
}
/**
* Creates an ExpiringValue to be stored in an {@link ExpiringMap}.
*
* @param value the value to store
* @param duration the length of time after an entry is created that it should be removed
* @param timeUnit the unit that {@code duration} is expressed in
* @param expirationPolicy the expiration policy for the value
* @see ExpiringMap#put(Object, Object, ExpirationPolicy, long, TimeUnit)
* @throws NullPointerException on null timeUnit
*/
public ExpiringValue(V value, ExpirationPolicy expirationPolicy, long duration, TimeUnit timeUnit) {
this(value, duration, timeUnit, expirationPolicy);
if (timeUnit == null) {
throw new NullPointerException();
}
}
private ExpiringValue(V value, long duration, TimeUnit timeUnit, ExpirationPolicy expirationPolicy) {
this.value = value;
this.expirationPolicy = expirationPolicy;
this.duration = duration;
this.timeUnit = timeUnit;
}
public V getValue() {
return value;
}
public ExpirationPolicy getExpirationPolicy() {
return expirationPolicy;
}
public long getDuration() {
return duration;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
} }
ExpiringValue<?> that = (ExpiringValue<?>) o; /**
return !(value != null ? !value.equals(that.value) : that.value != null) * Creates an ExpiringValue to be stored in an {@link ExpiringMap}. The map's default
&& expirationPolicy == that.expirationPolicy && duration == that.duration && timeUnit == that.timeUnit; * {@link ExpiringMap#getExpiration()} expiration} will be used.
*
* @param value the value to store
* @param expirationPolicy the expiration policy for the value
* @see ExpiringMap#put(Object, Object, ExpirationPolicy)
*/
public ExpiringValue(V value, ExpirationPolicy expirationPolicy) {
this(value, UNSET_DURATION, null, expirationPolicy);
}
} /**
* Creates an ExpiringValue to be stored in an {@link ExpiringMap}. The map's default {@link ExpirationPolicy
* expiration policy} will be used.
*
* @param value the value to store
* @param duration the length of time after an entry is created that it should be removed
* @param timeUnit the unit that {@code duration} is expressed in
* @throws NullPointerException on null timeUnit
* @see ExpiringMap#put(Object, Object, long, TimeUnit)
*/
public ExpiringValue(V value, long duration, TimeUnit timeUnit) {
this(value, duration, timeUnit, null);
if (timeUnit == null) {
throw new NullPointerException();
}
}
@Override /**
public String toString() { * Creates an ExpiringValue to be stored in an {@link ExpiringMap}.
return "ExpiringValue{" + "value=" + value + ", expirationPolicy=" + expirationPolicy + ", duration=" + duration *
+ ", timeUnit=" + timeUnit + '}'; * @param value the value to store
} * @param duration the length of time after an entry is created that it should be removed
* @param timeUnit the unit that {@code duration} is expressed in
* @param expirationPolicy the expiration policy for the value
* @throws NullPointerException on null timeUnit
* @see ExpiringMap#put(Object, Object, ExpirationPolicy, long, TimeUnit)
*/
public ExpiringValue(V value, ExpirationPolicy expirationPolicy, long duration, TimeUnit timeUnit) {
this(value, duration, timeUnit, expirationPolicy);
if (timeUnit == null) {
throw new NullPointerException();
}
}
private ExpiringValue(V value, long duration, TimeUnit timeUnit, ExpirationPolicy expirationPolicy) {
this.value = value;
this.expirationPolicy = expirationPolicy;
this.duration = duration;
this.timeUnit = timeUnit;
}
public V getValue() {
return value;
}
public ExpirationPolicy getExpirationPolicy() {
return expirationPolicy;
}
public long getDuration() {
return duration;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ExpiringValue<?> that = (ExpiringValue<?>) o;
return !(value != null ? !value.equals(that.value) : that.value != null)
&& expirationPolicy == that.expirationPolicy && duration == that.duration && timeUnit == that.timeUnit;
}
@Override
public String toString() {
return "ExpiringValue{" + "value=" + value + ", expirationPolicy=" + expirationPolicy + ", duration=" + duration
+ ", timeUnit=" + timeUnit + '}';
}
} }

View File

@ -6,27 +6,27 @@ import java.util.NoSuchElementException;
* @author Jonathan Halterman * @author Jonathan Halterman
*/ */
public final class Assert { public final class Assert {
private Assert() { private Assert() {
} }
public static <T> T notNull(T reference, String parameterName) { public static <T> T notNull(T reference, String parameterName) {
if (reference == null) if (reference == null)
throw new NullPointerException(parameterName + " cannot be null"); throw new NullPointerException(parameterName + " cannot be null");
return reference; return reference;
} }
public static void operation(boolean condition, String message) { public static void operation(boolean condition, String message) {
if (!condition) if (!condition)
throw new UnsupportedOperationException(message); throw new UnsupportedOperationException(message);
} }
public static void state(boolean expression, String errorMessageFormat, Object... args) { public static void state(boolean expression, String errorMessageFormat, Object... args) {
if (!expression) if (!expression)
throw new IllegalStateException(String.format(errorMessageFormat, args)); throw new IllegalStateException(String.format(errorMessageFormat, args));
} }
public static void element(Object element, Object key) { public static void element(Object element, Object key) {
if (element == null) if (element == null)
throw new NoSuchElementException(key.toString()); throw new NoSuchElementException(key.toString());
} }
} }

View File

@ -7,21 +7,21 @@ import java.util.concurrent.atomic.AtomicInteger;
* Named thread factory. * Named thread factory.
*/ */
public class NamedThreadFactory implements ThreadFactory { public class NamedThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1); private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String nameFormat; private final String nameFormat;
/** /**
* Creates a thread factory that names threads according to the {@code nameFormat} by supplying a * Creates a thread factory that names threads according to the {@code nameFormat} by supplying a
* single argument to the format representing the thread number. * single argument to the format representing the thread number.
*/ */
public NamedThreadFactory(String nameFormat) { public NamedThreadFactory(String nameFormat) {
this.nameFormat = nameFormat; this.nameFormat = nameFormat;
} }
@Override @Override
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
Thread thread = new Thread(r, String.format(nameFormat, threadNumber.getAndIncrement())); Thread thread = new Thread(r, String.format(nameFormat, threadNumber.getAndIncrement()));
thread.setDaemon(true); thread.setDaemon(true);
return thread; return thread;
} }
} }

View File

@ -0,0 +1,66 @@
package com.io.yutian.elementoriginlib.gui;
import com.io.yutian.elementoriginlib.gui.button.Button;
import com.io.yutian.elementoriginlib.gui.button.ClickType;
import net.kyori.adventure.text.Component;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import java.util.Set;
public abstract class ContainerGui extends Gui {
private Set<Integer> slots;
public ContainerGui(Player player, Component title, int size) {
super(player, title, size);
}
public void addSlot(int slot) {
if (slot > inventory.getSize()) {
return;
}
slots.add(slot);
}
@Override
public void handler(Player player, int slot, InventoryClickEvent event) {
if (buttons.containsKey(slot)) {
Button button = buttons.get(slot);
if (button == null) {
if (slot < inventory.getSize()) {
event.setCancelled(true);
} else {
if (event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) {
event.setCancelled(true);
}
}
return;
}
event.setCancelled(true);
clickButton(event, slot, button);
if (button.isPlaySound()) {
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1.0f, 1.0f);
}
InventoryAction action = event.getAction();
ClickType clickType = ClickType.LEFT_CLICK;
if (action.equals(InventoryAction.PICKUP_ALL)) {
clickType = ClickType.LEFT_CLICK;
} else if (action.equals(InventoryAction.PICKUP_HALF)) {
clickType = ClickType.RIGHT_CLICK;
} else if (action.equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) {
clickType = ClickType.SHIFT_CLICK;
}
if (button.getClickConsumer() != null) {
button.getClickConsumer().accept(player, clickType);
}
} else {
if (slot < inventory.getSize()) {
event.setCancelled(true);
}
}
}
}

View File

@ -15,7 +15,7 @@ import java.util.Map;
public class Gui extends IGui { public class Gui extends IGui {
private Map<Integer, Button> buttons = new HashMap<>(); public Map<Integer, Button> buttons = new HashMap<>();
public Gui(Player player, Component title, int size) { public Gui(Player player, Component title, int size) {
super(player, title, size); super(player, title, size);

View File

@ -4,50 +4,42 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.IntStream;
public class PageList<T> { public class PageList<T> {
private List<T> list;
private Map<Integer, List<T>> map = new HashMap<>(); private Map<Integer, List<T>> map = new HashMap<>();
private int amount; private final int totalPages;
public PageList(List<T> list, int amount) { public PageList(List<T> list, int amount) {
this.list = list; if (list == null || list.isEmpty() || amount <= 0) {
this.amount = amount; totalPages = 0;
if (list == null) {
return;
}
if (list.size() <= amount) {
List<T> newList = new ArrayList<T>();
list.forEach(o-> newList.add(o));
map.put(1, newList);
} else { } else {
int x = 0; totalPages = (list.size() - 1) / amount + 1;
int c = list.size() / amount; IntStream.rangeClosed(1, totalPages).forEach(page -> {
for (int j = 0; j <= c;j++) { int start = (page - 1) * amount;
int min = j * amount; int end = Math.min(page * amount, list.size());
int max = (j+1) * amount; map.put(page, new ArrayList<>(list.subList(start, end)));
List<T> newList = new ArrayList<T>(); });
for (int k = min; k < max; k++) {
if (k >= list.size()) {
break;
}
newList.add(list.get(k));
}
map.put(j+1, newList);
}
} }
} }
public int size() { public int size() {
return map.size(); return totalPages;
} }
public List<T> getList(int page) { public List<T> getList(int page) {
if (page <= 0) { if (page < 1 || page > totalPages) {
page = 1; throw new IllegalArgumentException("Invalid page number: " + page);
} }
return map.get(page); return map.get(page);
} }
public boolean hasNextPage(int page) {
return page < totalPages;
}
public int getTotalPages() {
return totalPages;
}
} }

View File

@ -2,24 +2,27 @@ package com.io.yutian.elementoriginlib.serialize;
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker; import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;
import com.io.yutian.elementoriginlib.serialize.serializers.ItemStackDeserializer; import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
import com.io.yutian.elementoriginlib.serialize.serializers.ItemStackSerializer; import com.io.yutian.elementoriginlib.logger.Logger;
import com.io.yutian.elementoriginlib.serialize.serializers.UUIDDeserializer; import com.io.yutian.elementoriginlib.serialize.serializers.*;
import com.io.yutian.elementoriginlib.serialize.serializers.UUIDSerializer;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.UUID; import java.util.UUID;
public class SerializeHelper { public class SerializeHelper {
private static final Logger LOGGER = Logger.getLogger(SerializeHelper.class);
private static final ObjectMapper objectMapper; private static final ObjectMapper objectMapper;
private static ObjectWriter objectWriter;
private static ObjectReader objectReader;
static { static {
objectMapper = new ObjectMapper(); objectMapper = new ObjectMapper();
objectMapper.setVisibility( objectMapper.setVisibility(
VisibilityChecker.Std.defaultInstance() VisibilityChecker.Std.defaultInstance()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY) .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
@ -29,31 +32,61 @@ public class SerializeHelper {
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE) .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)
); );
objectMapper.registerModule(new AfterburnerModule());
objectMapper.disable(MapperFeature.AUTO_DETECT_GETTERS);
objectMapper.disable(MapperFeature.AUTO_DETECT_SETTERS);
objectMapper.disable(MapperFeature.AUTO_DETECT_IS_GETTERS);
objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.disable(SerializationFeature.INDENT_OUTPUT);
SimpleModule module = new SimpleModule(); SimpleModule module = new SimpleModule();
module.addSerializer(UUID.class, new UUIDSerializer()); module.addSerializer(UUID.class, new UUIDSerializer());
module.addDeserializer(UUID.class, new UUIDDeserializer()); module.addDeserializer(UUID.class, new UUIDDeserializer());
module.addSerializer(ItemStack.class, new ItemStackSerializer()); module.addSerializer(ItemStack.class, new ItemStackSerializer());
module.addDeserializer(ItemStack.class, new ItemStackDeserializer()); module.addDeserializer(ItemStack.class, new ItemStackDeserializer());
objectMapper.registerModule(module); objectMapper.registerModule(module);
objectWriter = objectMapper.writer();
objectReader = objectMapper.reader();
}
public static void registerModule(Module module) {
objectMapper.registerModule(module);
objectWriter = objectMapper.writer();
objectReader = objectMapper.reader();
}
public static void registerSerializer(Class clazz, JsonSerializer serializer) {
SimpleModule module = new SimpleModule();
module.addSerializer(clazz, serializer);
registerModule(module);
}
public static void registerDeserializer(Class clazz, JsonDeserializer deserializer) {
SimpleModule module = new SimpleModule();
module.addDeserializer(clazz, deserializer);
registerModule(module);
} }
public static String serialize(Object obj) { public static String serialize(Object obj) {
try { try {
return objectMapper.writeValueAsString(obj); return objectWriter.writeValueAsString(obj);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException("Failed to serialize object: " + obj, e); LOGGER.error("Failed to serialize object: " + obj, e);
return null;
} }
} }
public static <T> T deserialize(String json, Class<T> clazz) { public static <T> T deserialize(String json, Class<T> clazz) {
try { try {
return objectMapper.readValue(json, clazz); return objectReader.forType(clazz).readValue(json);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException("Failed to deserialize JSON: " + json, e); LOGGER.error("Failed to deserialize object: " + json, e);
return null;
} }
} }
} }