mirror of
https://github.com/sciwhiz12/Janitor.git
synced 2024-11-10 03:21:26 +00:00
Introduce StorageKey to remove unchecked cast in GuildStorage, force Java 11
This commit is contained in:
parent
eb50a1bab3
commit
631acaf021
|
@ -30,6 +30,11 @@ archivesBaseName = 'janitor_bot'
|
|||
version = getVersion()
|
||||
println("Version: ${version}")
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation group: 'net.dv8tion', name: 'JDA', version: jda_version
|
||||
implementation group: 'com.electronwill.night-config', name: 'toml', version: nightconfig_version
|
||||
|
|
|
@ -10,6 +10,7 @@ import sciwhiz12.janitor.commands.CommandRegistry;
|
|||
import sciwhiz12.janitor.config.BotConfig;
|
||||
import sciwhiz12.janitor.msg.Messages;
|
||||
import sciwhiz12.janitor.msg.Translations;
|
||||
import sciwhiz12.janitor.storage.GuildStorage;
|
||||
import sciwhiz12.janitor.utils.Util;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
|
|
@ -9,9 +9,10 @@ import com.google.gson.reflect.TypeToken;
|
|||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import sciwhiz12.janitor.GuildStorage;
|
||||
import sciwhiz12.janitor.JanitorBot;
|
||||
import sciwhiz12.janitor.storage.GuildStorage;
|
||||
import sciwhiz12.janitor.storage.JsonStorage;
|
||||
import sciwhiz12.janitor.storage.StorageKey;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
|
@ -19,10 +20,10 @@ import java.util.Map;
|
|||
|
||||
public class NoteStorage extends JsonStorage {
|
||||
private static final Type NOTE_MAP_TYPE = new TypeToken<Map<Integer, NoteEntry>>() {}.getType();
|
||||
public static final String STORAGE_KEY = "notes";
|
||||
public static final StorageKey<NoteStorage> KEY = new StorageKey<>("notes", NoteStorage.class);
|
||||
|
||||
public static NoteStorage get(GuildStorage storage, Guild guild) {
|
||||
return storage.getOrCreate(guild, STORAGE_KEY, () -> new NoteStorage(storage.getBot()));
|
||||
return storage.getOrCreate(guild, KEY, () -> new NoteStorage(storage.getBot()));
|
||||
}
|
||||
|
||||
private final Gson gson;
|
||||
|
|
|
@ -8,9 +8,10 @@ import com.google.gson.JsonObject;
|
|||
import com.google.gson.reflect.TypeToken;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import sciwhiz12.janitor.GuildStorage;
|
||||
import sciwhiz12.janitor.JanitorBot;
|
||||
import sciwhiz12.janitor.storage.GuildStorage;
|
||||
import sciwhiz12.janitor.storage.JsonStorage;
|
||||
import sciwhiz12.janitor.storage.StorageKey;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
|
@ -18,10 +19,10 @@ import java.util.Map;
|
|||
|
||||
public class WarningStorage extends JsonStorage {
|
||||
private static final Type WARNING_MAP_TYPE = new TypeToken<Map<Integer, WarningEntry>>() {}.getType();
|
||||
public static final String STORAGE_KEY = "warnings";
|
||||
public static final StorageKey<WarningStorage> KEY = new StorageKey<>("warnings", WarningStorage.class);
|
||||
|
||||
public static WarningStorage get(GuildStorage storage, Guild guild) {
|
||||
return storage.getOrCreate(guild, STORAGE_KEY, () -> new WarningStorage(storage.getBot()));
|
||||
return storage.getOrCreate(guild, KEY, () -> new WarningStorage(storage.getBot()));
|
||||
}
|
||||
|
||||
private final Gson gson;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package sciwhiz12.janitor;
|
||||
package sciwhiz12.janitor.storage;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import sciwhiz12.janitor.storage.IStorage;
|
||||
import sciwhiz12.janitor.JanitorBot;
|
||||
import sciwhiz12.janitor.Logging;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
@ -12,18 +13,20 @@ import java.io.Writer;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.nio.file.StandardOpenOption.*;
|
||||
|
||||
/**
|
||||
* A storage system for guild-specific data.
|
||||
*/
|
||||
public class GuildStorage {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
|
||||
|
||||
private final JanitorBot bot;
|
||||
private final Path mainFolder;
|
||||
private final Map<Guild, Map<String, IStorage>> guildStorage = new IdentityHashMap<>();
|
||||
private final Map<Long, Map<String, InnerStorage<?>>> guildStorages = new HashMap<>();
|
||||
|
||||
public GuildStorage(JanitorBot bot, Path mainFolder) {
|
||||
Preconditions.checkArgument(Files.isDirectory(mainFolder) || Files.notExists(mainFolder));
|
||||
|
@ -35,28 +38,31 @@ public class GuildStorage {
|
|||
return bot;
|
||||
}
|
||||
|
||||
public <T extends IStorage> T getOrCreate(Guild guild, String key, Supplier<T> defaultSupplier) {
|
||||
final Map<String, IStorage> storageMap = guildStorage.computeIfAbsent(guild, g -> new HashMap<>());
|
||||
//noinspection unchecked
|
||||
return (T) storageMap.computeIfAbsent(key, k -> load(guild, key, defaultSupplier.get()));
|
||||
public <S extends IStorage> S getOrCreate(Guild guild, StorageKey<S> key, Supplier<S> defaultSupplier) {
|
||||
return getOrCreate(guild.getIdLong(), key, defaultSupplier);
|
||||
}
|
||||
|
||||
private Path getFile(Guild guild, String key) {
|
||||
final Path guildFolder = makeFolder(guild);
|
||||
public <S extends IStorage> S getOrCreate(long guildID, StorageKey<S> key, Supplier<S> defaultSupplier) {
|
||||
final Map<String, InnerStorage<?>> storageMappy = guildStorages.computeIfAbsent(guildID, id -> new HashMap<>());
|
||||
return key.getType().cast(storageMappy.computeIfAbsent(key.getStorageID(),
|
||||
k -> new InnerStorage<>(key, load(guildID, key.getStorageID(), defaultSupplier.get()))).getStorage());
|
||||
}
|
||||
|
||||
private Path getFile(long guildID, String key) {
|
||||
final Path guildFolder = Path.of(Long.toHexString(guildID));
|
||||
final Path file = Path.of(key + ".json");
|
||||
return mainFolder.resolve(guildFolder).resolve(file);
|
||||
}
|
||||
|
||||
public <T extends IStorage> T load(Guild guild, String key, T storage) {
|
||||
final Path file = getFile(guild, key);
|
||||
public <T extends IStorage> T load(long guildID, String key, T storage) {
|
||||
final Path file = getFile(guildID, key);
|
||||
if (Files.notExists(file)) return storage;
|
||||
|
||||
Logging.JANITOR.debug("Loading storage {} for guild {}", key, guild);
|
||||
Logging.JANITOR.debug("Loading storage {} for guild {}", key, guildID);
|
||||
try (Reader reader = Files.newBufferedReader(file)) {
|
||||
storage.read(reader);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Logging.JANITOR.error("Error while loading storage {} for guild {}", key, guild, e);
|
||||
} catch (IOException e) {
|
||||
Logging.JANITOR.error("Error while loading storage {} for guild {}", key, guildID, e);
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
|
@ -69,23 +75,22 @@ public class GuildStorage {
|
|||
if (!isAutosave)
|
||||
Logging.JANITOR.debug("Saving guild storage to files under {}...", mainFolder);
|
||||
boolean anySaved = false;
|
||||
for (Guild guild : guildStorage.keySet()) {
|
||||
final Map<String, IStorage> storageMap = guildStorage.get(guild);
|
||||
for (long guildID : guildStorages.keySet()) {
|
||||
final Map<String, InnerStorage<?>> storageMap = guildStorages.get(guildID);
|
||||
for (String key : storageMap.keySet()) {
|
||||
final IStorage storage = storageMap.get(key);
|
||||
if (storage.dirty()) {
|
||||
final Path file = getFile(guild, key);
|
||||
final InnerStorage<?> inner = storageMap.get(key);
|
||||
if (inner.dirty()) {
|
||||
final Path file = getFile(guildID, key);
|
||||
try {
|
||||
if (Files.notExists(file.getParent())) Files.createDirectories(file.getParent());
|
||||
if (Files.notExists(file)) Files.createFile(file);
|
||||
try (Writer writer = Files
|
||||
.newBufferedWriter(file, CREATE, WRITE, TRUNCATE_EXISTING)) {
|
||||
storage.write(writer);
|
||||
inner.getStorage().write(writer);
|
||||
anySaved = true;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Logging.JANITOR.error("Error while writing storage {} for guild {}", key, guild, e);
|
||||
} catch (IOException e) {
|
||||
Logging.JANITOR.error("Error while writing storage {} for guild {}", key, guildID, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,10 +99,9 @@ public class GuildStorage {
|
|||
Logging.JANITOR.info("Saved guild storage to files under {}", mainFolder);
|
||||
}
|
||||
|
||||
private Path makeFolder(Guild guild) {
|
||||
return Path.of(Long.toHexString(guild.getIdLong()));
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread that calls {@link GuildStorage#save(boolean)} between specified delays.
|
||||
*/
|
||||
public static class SavingThread extends Thread {
|
||||
private final GuildStorage storage;
|
||||
private volatile boolean running = true;
|
||||
|
@ -117,9 +121,35 @@ public class GuildStorage {
|
|||
public void run() {
|
||||
while (running) {
|
||||
storage.save(true);
|
||||
try { Thread.sleep(storage.getBot().getConfig().AUTOSAVE_INTERVAL.get() * 1000); }
|
||||
catch (InterruptedException ignored) {}
|
||||
try {
|
||||
Thread.sleep(storage.getBot().getConfig().AUTOSAVE_INTERVAL.get() * 1000);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <strong>For internal use only.</strong>
|
||||
*/
|
||||
static class InnerStorage<S extends IStorage> {
|
||||
private final StorageKey<S> key;
|
||||
private final S storage;
|
||||
|
||||
InnerStorage(StorageKey<S> key, S storage) {
|
||||
this.key = key;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public StorageKey<S> getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public S getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public boolean dirty() {
|
||||
return storage.dirty();
|
||||
}
|
||||
}
|
||||
}
|
67
src/main/java/sciwhiz12/janitor/storage/StorageKey.java
Normal file
67
src/main/java/sciwhiz12/janitor/storage/StorageKey.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
package sciwhiz12.janitor.storage;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A storage key, used to retrieve an instance of an {@link IStorage} from a {@link GuildStorage}.
|
||||
*
|
||||
* @param <S> the type of the {@link IStorage}
|
||||
*/
|
||||
public class StorageKey<S extends IStorage> {
|
||||
private final String storageID;
|
||||
private final Class<S> type;
|
||||
|
||||
/**
|
||||
* Creates a {@link StorageKey} with the given storage ID and type.
|
||||
*
|
||||
* @param storageID the storage ID
|
||||
* @param type the class type of the generic type
|
||||
*
|
||||
* @throws NullPointerException if {@code storageID} or {@code type} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code storageID} is empty or blank
|
||||
*/
|
||||
public StorageKey(String storageID, Class<S> type) {
|
||||
Preconditions.checkNotNull(storageID, "Storage ID must not be null");
|
||||
Preconditions.checkArgument(!storageID.isBlank(), "Storage ID must not be empty or blank");
|
||||
Preconditions.checkNotNull(type, "Class type must not be null");
|
||||
this.storageID = storageID;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage ID, used by {@link GuildStorage} to uniquely identify this storage's data.
|
||||
*
|
||||
* <p>This is currently used by {@code GuildStorage} as the folder name of the storage.
|
||||
*
|
||||
* @return the storage ID
|
||||
*/
|
||||
public String getStorageID() {
|
||||
return storageID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class of the {@link IStorage} subtype that this storage key represents, which
|
||||
* is also used in the key's generics.
|
||||
*
|
||||
* @return the class of the generic type
|
||||
*/
|
||||
public Class<S> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
StorageKey<?> that = (StorageKey<?>) o;
|
||||
return storageID.equals(that.storageID) &&
|
||||
type.equals(that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(storageID, type);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user