diff --git a/database/src/main/java/com/georgev22/library/utilities/Entity.java b/database/src/main/java/com/georgev22/library/utilities/Entity.java new file mode 100644 index 0000000..bcbd40f --- /dev/null +++ b/database/src/main/java/com/georgev22/library/utilities/Entity.java @@ -0,0 +1,63 @@ +package com.georgev22.library.utilities; + +import com.georgev22.library.maps.ConcurrentObjectMap; + +import java.util.UUID; + +/** + * The {@code Entity} interface represents an entity with a unique identifier. + * It provides methods for managing custom data associated with the entity. + * Custom data can be added, retrieved, and accessed using key-value pairs. + */ +public interface Entity { + + /** + * Returns the unique identifier of the entity. + * + * @return the {@link UUID} representing the entity's ID + */ + UUID getId(); + + /** + * Adds custom data to the entity with the specified key and value. + * + * @param key the key of the custom data + * @param value the value of the custom data + * @return the updated entity with the added custom data + */ + default Entity addCustomData(String key, Object value) { + this.getCustomData().append(key, value); + return this; + } + + /** + * Adds custom data to the entity with the specified key and value if the key does not already exist. + * + * @param key the key of the custom data + * @param value the value of the custom data + * @return the updated entity with the added custom data (if the key did not already exist) + */ + default Entity addCustomDataIfNotExists(String key, Object value) { + this.getCustomData().appendIfTrue(key, value, this.getCustomData().containsKey(key)); + return this; + } + + /** + * Retrieves the value of the custom data associated with the specified key. + * + * @param key the key of the custom data + * @param the type of the value to retrieve + * @return the value associated with the specified key, or {@code null} if the key does not exist + */ + default T getCustomData(String key) { + return (T) getCustomData().get(key); + } + + /** + * Retrieves the map of custom data associated with the entity. + * + * @return the {@link ConcurrentObjectMap} containing the custom data of the entity + */ + ConcurrentObjectMap getCustomData(); + +} diff --git a/database/src/main/java/com/georgev22/library/utilities/EntityManager.java b/database/src/main/java/com/georgev22/library/utilities/EntityManager.java index 83c1ffd..77a3e0c 100644 --- a/database/src/main/java/com/georgev22/library/utilities/EntityManager.java +++ b/database/src/main/java/com/georgev22/library/utilities/EntityManager.java @@ -1,63 +1,37 @@ package com.georgev22.library.utilities; -import com.georgev22.library.database.DatabaseWrapper; -import com.georgev22.library.database.DatabaseWrapper.DatabaseObject; -import com.georgev22.library.maps.ConcurrentObjectMap; -import com.georgev22.library.maps.HashObjectMap; import com.georgev22.library.maps.ObjectMap; -import com.georgev22.library.maps.ObjectMap.Pair; -import com.georgev22.library.maps.ObservableObjectMap; -import com.mongodb.annotations.Beta; -import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; /** - * The {@link EntityManager} class is responsible for managing {@link Entity} objects in a persistence storage. - * It supports multiple storage types including MySQL, SQLite, PostgreSQL, MongoDB, and FILE. - * The class provides methods for checking if a {@link Entity} exists, - * loading a {@link Entity}, and creating a {@link Entity}. + * The {@code EntityManager} interface provides methods for managing entities of type {@code T}. + *

+ * It defines operations such as loading, saving, deleting, creating, and retrieving entities. + *

+ * Implementations of this interface handle the persistence and retrieval of entities in a data store. + *

+ *

+ * The interface requires the type parameter {@code T} to extend the {@code Entity} interface. + *

+ * Entities are identified by {@link UUID} values. + *

+ *

+ * This interface provides both synchronous and asynchronous methods for interacting with entities. + *

+ * Asynchronous methods return {@link CompletableFuture} objects that can be used to handle the results + *

+ * of the corresponding operations in an asynchronous manner. + *

+ *

+ * The {@code EntityManager} interface also includes methods for saving and loading multiple entities at once, + *

+ * as well as retrieving the map of currently loaded entities. * - * @author GeorgeV220 + * @param the type of entities managed by this {@code EntityManager} */ -public class EntityManager { - private final File entitiesDirectory; - private final DatabaseWrapper database; - private final String collection; - private final Class entityClazz; - private final ObservableObjectMap loadedEntities = new ObservableObjectMap<>(); - - /** - * Constructor for the EntityManager class - * - * @param obj the object to be used for storage (DatabaseWrapper or File) - * @param collectionName the name of the collection to be used for MONGODB and SQL, null for other types - */ - public EntityManager(Object obj, @Nullable String collectionName, Class clazz) { - this.collection = collectionName; - if (obj instanceof File folder) { - this.entitiesDirectory = folder; - this.database = null; - if (!this.entitiesDirectory.exists()) { - this.entitiesDirectory.mkdirs(); - } - } else if (obj instanceof DatabaseWrapper databaseWrapper) { - this.entitiesDirectory = null; - this.database = databaseWrapper; - } else { - this.entitiesDirectory = null; - this.database = null; - } - this.entityClazz = clazz; - } +public interface EntityManager { /** * Loads the {@link Entity} with the specified ID @@ -65,48 +39,7 @@ public EntityManager(Object obj, @Nullable String collectionName, Class load(UUID entityId) { - return exists(entityId) - .thenCompose(exists -> { - if (exists) { - return CompletableFuture.supplyAsync(() -> { - if (entitiesDirectory != null) { - File file = new File(entitiesDirectory, entityId + ".entity"); - try { - T entity = (T) Utils.deserializeObject(file.getAbsolutePath()); - loadedEntities.append(entityId, entity); - return entity; - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } else if (database != null) { - Pair> retrievedData = database.retrieveData(collection, Pair.create("entity_id", entityId.toString())); - T entity; - try { - entity = (T) entityClazz.getDeclaredConstructor(UUID.class).newInstance(entityId); - } catch (InstantiationException | IllegalAccessException | - InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - retrievedData.value().forEach(databaseObject -> { - ObjectMap databaseObjectData = databaseObject.data(); - databaseObjectData.forEach(entity::addCustomData); - }); - return entity; - } else { - try { - return (T) entityClazz.getDeclaredConstructor(UUID.class).newInstance(entityId); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | - NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - }); - } else { - return createEntity(entityId); - } - }); - } + CompletableFuture load(UUID entityId); /** * Saves the specified {@link Entity}. @@ -114,28 +47,7 @@ public CompletableFuture load(UUID entityId) { * @param entity the {@link Entity} to save * @return a {@link CompletableFuture} that completes when the {@link Entity} is saved */ - public CompletableFuture save(Entity entity) { - return CompletableFuture.runAsync(() -> { - if (entitiesDirectory != null) { - File file = new File(entitiesDirectory, entity.getId() + ".entity"); - try { - Utils.serializeObject(entity, file.getAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } else if (database != null) { - exists(entity.getId()).thenAccept(result -> { - ObjectMap entityData = new HashObjectMap<>(entity.customData); - if (result) { - database.updateData(collection, Pair.create("entity_id", entity.getId().toString()), Pair.create("$set", entityData.removeEntry("entity_id")), null); - } else { - database.addData(collection, Pair.create(entity.entityId.toString(), entityData)); - } - }); - } - this.loadedEntities.append(entity.entityId, (T) entity); - }); - } + CompletableFuture save(T entity); /** * Deletes the specified entity. @@ -143,24 +55,7 @@ public CompletableFuture save(Entity entity) { * @param entity the {@link Entity} to delete * @return a {@link CompletableFuture} that completes when the {@link Entity} is deleted */ - public CompletableFuture delete(Entity entity) { - return CompletableFuture.runAsync(() -> { - if (entitiesDirectory != null) { - File file = new File(entitiesDirectory, entity.getId() + ".entity"); - if (file.exists()) { - file.delete(); - } - } else if (database != null) { - exists(entity.getId()).thenAccept(result -> { - ObjectMap entityData = new HashObjectMap<>(entity.customData); - if (result) { - database.removeData(collection, Pair.create("entity_id", entity.getId()), null); - } - }); - } - this.loadedEntities.remove(entity.getId()); - }); - } + CompletableFuture delete(T entity); /** * Creates a new {@link Entity} with the specified entity ID. @@ -168,16 +63,7 @@ public CompletableFuture delete(Entity entity) { * @param entityId the {@link UUID} of the entity to create * @return a {@link CompletableFuture} that returns the newly created {@link Entity} */ - public CompletableFuture createEntity(UUID entityId) { - T entity; - try { - entity = (T) entityClazz.getDeclaredConstructor(UUID.class).newInstance(entityId); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | - NoSuchMethodException e) { - throw new RuntimeException(e); - } - return CompletableFuture.completedFuture(loadedEntities.append(entityId, entity).get(entityId)); - } + CompletableFuture createEntity(UUID entityId); /** * Determines if a {@link Entity} with the specified entity ID exists. @@ -185,17 +71,7 @@ public CompletableFuture createEntity(UUID entityId) { * @param entityId the {@link UUID} of the entity to check * @return a {@link CompletableFuture} that returns true if a {@link Entity} with the specified ID exists, false otherwise */ - public CompletableFuture exists(UUID entityId) { - return CompletableFuture.supplyAsync(() -> { - if (entitiesDirectory != null) { - return new File(entitiesDirectory, entityId + ".entity").exists(); - } else if (database != null) { - return database.exists(collection, Pair.create("entity_id", entityId), null); - } else { - return false; - } - }); - } + CompletableFuture exists(UUID entityId); /** * Retrieves the {@link Entity} with the given {@link UUID}. @@ -207,127 +83,25 @@ public CompletableFuture exists(UUID entityId) { * @param entityId the {@link UUID} of the entity to retrieve * @return a {@link CompletableFuture} that will contain the {@link Entity} with the given id */ - public CompletableFuture getEntity(UUID entityId) { - if (loadedEntities.containsKey(entityId)) { - return CompletableFuture.completedFuture(loadedEntities.get(entityId)); - } - - return load(entityId); - } + CompletableFuture getEntity(UUID entityId); /** - * Saves all the loaded {@link Entity}s in the {@link #loadedEntities} map. + * Saves all the loaded {@link Entity}s in the {@link #getLoadedEntities()} map. * For each {@link Entity} in the map, - * this method calls the {@link #save(Entity)} method to persist the {@link Entity}. + * this method calls the {@link #save(T)} method to persist the {@link Entity}. */ - public void saveAll() { - ObjectMap entities = new ObservableObjectMap().append(loadedEntities); - entities.forEach((uuid, entity) -> save(entity)); - } - - @Beta - public void loadAll() { - List entityIDs = new ArrayList<>(); - if (entitiesDirectory != null) { - File[] files = this.entitiesDirectory.listFiles((dir, name) -> name.endsWith(".entity")); - if (files != null) { - Arrays.stream(files).forEach(file -> entityIDs.add(UUID.fromString(file.getName().replace(".entity", "")))); - } - } else if (database != null) { - Pair> data = database.retrieveData(collection, Pair.create("entity_id", null)); - data.value().forEach(databaseObject -> entityIDs.add(UUID.fromString(String.valueOf(databaseObject.data().get("entity_id"))))); - } - entityIDs.forEach(this::load); - } + void saveAll(); /** - * Retrieves the current map of loaded entities. - * - * @return the map of loaded entities with UUID as the key and Entity object as the value + * Loads all the entities by retrieving their IDs and invoking the {@link #load(UUID)} method. */ - public ObservableObjectMap getLoadedEntities() { - return loadedEntities; - } + void loadAll(); /** - * A class representing an entity in the system. + * Retrieves the current map of loaded entities. + * + * @return the map of loaded entities with UUID as the key and EntityImpl object as the value */ - public static class Entity implements Serializable { - private final UUID entityId; - private ConcurrentObjectMap customData; - - /** - * Constructs a new entity with a random UUID. - */ - public Entity() { - this(UUID.randomUUID()); - } - - /** - * Constructs a new entity with the specified UUID and name. - * - * @param entityId the UUID of the entity - */ - public Entity(UUID entityId) { - this.entityId = entityId; - this.customData = new ConcurrentObjectMap<>(); - } - - /** - * Returns the entityId of this `Entity` object. - * - * @return the entityId of this `Entity` object. - */ - public UUID getId() { - return entityId; - } - - /** - * Adds a key-value pair to the custom data map. - * - * @param key the key of the data - * @param value the value of the data - */ - public Entity addCustomData(String key, Object value) { - customData.append(key, value); - return this; - } - - /** - * Adds a key-value pair to the custom data map if the key does not already exist. - * - * @param key the key of the data - * @param value the value of the data - */ - public Entity addCustomDataIfNotExists(String key, Object value) { - return !customData.containsKey(key) ? addCustomData(key, value) : this; - } - - /** - * Returns the value of the custom data for the specified key. - * - * @param key the key of the data - * @return the value of the custom data for the specified key - */ - public T getCustomData(String key) { - return (T) customData.get(key); - } - - /** - * Returns the {@link ConcurrentObjectMap} that contains the Entity data - * - * @return the {@link ConcurrentObjectMap} that contains the Entity data - */ - public ConcurrentObjectMap getCustomData() { - return customData; - } + ObjectMap getLoadedEntities(); - @Override - public String toString() { - return "Entity{" + - "entityId=" + entityId + - ", customData=" + customData + - '}'; - } - } }