diff --git a/src/main/java/me/braydon/feather/FeatherSettings.java b/src/main/java/me/braydon/feather/FeatherSettings.java index e0d3251..0293bd1 100644 --- a/src/main/java/me/braydon/feather/FeatherSettings.java +++ b/src/main/java/me/braydon/feather/FeatherSettings.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather; import com.google.gson.Gson; diff --git a/src/main/java/me/braydon/feather/annotation/Collection.java b/src/main/java/me/braydon/feather/annotation/Collection.java deleted file mode 100644 index de59baa..0000000 --- a/src/main/java/me/braydon/feather/annotation/Collection.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.braydon.feather.annotation; - -import me.braydon.feather.data.Document; - -import java.lang.annotation.*; - -/** - * Classes tagged with this annotation - * will be treated as a collection that - * holds {@link Document}'s. - * - * @author Braydon - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Documented @Inherited -public @interface Collection { - /** - * The name of this collection. - * - * @return the name - */ - String name() default ""; -} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/annotation/Field.java b/src/main/java/me/braydon/feather/annotation/Field.java index 5477af6..18f7c9b 100644 --- a/src/main/java/me/braydon/feather/annotation/Field.java +++ b/src/main/java/me/braydon/feather/annotation/Field.java @@ -1,10 +1,15 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.annotation; import java.lang.annotation.*; /** * Fields tagged with this annotation will be - * treated as a field within a {@link Collection}. + * treated as a field within a collection. * * @author Braydon */ diff --git a/src/main/java/me/braydon/feather/annotation/Id.java b/src/main/java/me/braydon/feather/annotation/Id.java index 2791c3f..1109a81 100644 --- a/src/main/java/me/braydon/feather/annotation/Id.java +++ b/src/main/java/me/braydon/feather/annotation/Id.java @@ -1,11 +1,18 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.annotation; +import me.braydon.feather.data.Document; + import java.lang.annotation.*; /** - * {@link Field}'s tagged with this annotation will be - * treated as the primary identifying key for documents - * within a {@link Collection}. + * {@link Field}'s tagged with this annotation + * will be treated as the primary identifying + * key for {@link Document}'s within a collection. * * @author Braydon */ diff --git a/src/main/java/me/braydon/feather/annotation/Serializable.java b/src/main/java/me/braydon/feather/annotation/Serializable.java index 66134f4..ce9a4e0 100644 --- a/src/main/java/me/braydon/feather/annotation/Serializable.java +++ b/src/main/java/me/braydon/feather/annotation/Serializable.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.annotation; import com.google.gson.Gson; diff --git a/src/main/java/me/braydon/feather/common/EntityUtils.java b/src/main/java/me/braydon/feather/common/EntityUtils.java deleted file mode 100644 index 2916d6e..0000000 --- a/src/main/java/me/braydon/feather/common/EntityUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.braydon.feather.common; - -import lombok.NonNull; -import lombok.experimental.UtilityClass; -import me.braydon.feather.annotation.Collection; - -/** - * @author Braydon - */ -@UtilityClass -public final class EntityUtils { - /** - * Ensure that the given entity is valid. - * - * @param entity the entity to validate - * @param allowEmptyCollections should empty collections be allowed? - */ - public static void ensureValid(@NonNull Object entity, boolean allowEmptyCollections) { - Class clazz = entity.getClass(); // Get the element class - if (!clazz.isAnnotationPresent(Collection.class)) { // Missing annotation - throw new IllegalStateException("Element is missing @Collection annotation"); - } - Collection annotation = clazz.getAnnotation(Collection.class); // Get the @Collection annotation - String collectionName = annotation.name(); // The name of the collection - if (collectionName.isEmpty() && !allowEmptyCollections) { // Missing collection name - throw new IllegalStateException("Missing collection name in @Collection for " + clazz.getSimpleName()); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/common/FieldUtils.java b/src/main/java/me/braydon/feather/common/FieldUtils.java new file mode 100644 index 0000000..fe326de --- /dev/null +++ b/src/main/java/me/braydon/feather/common/FieldUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ +package me.braydon.feather.common; + +import lombok.NonNull; +import lombok.experimental.UtilityClass; +import me.braydon.feather.annotation.Id; + +import java.lang.reflect.Field; + +/** + * @author Braydon + */ +@UtilityClass +public final class FieldUtils { + /** + * Extract the key to use for the given field. + * + * @param field the field to get the key from + * @return the key for the field + */ + @NonNull + public static String extractKey(@NonNull Field field) { + boolean idField = field.isAnnotationPresent(Id.class); // Is this field annotated with @Id? + me.braydon.feather.annotation.Field annotation = field.getAnnotation(me.braydon.feather.annotation.Field.class); // Get the @Field annotation + String key = idField ? field.getAnnotation(Id.class).key() : annotation.key(); // The key of the database field + if (key.isEmpty()) { // No field in the annotation, use the field name + key = field.getName(); + } + return key; + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/common/Tuple.java b/src/main/java/me/braydon/feather/common/Tuple.java index 139c79d..c702639 100644 --- a/src/main/java/me/braydon/feather/common/Tuple.java +++ b/src/main/java/me/braydon/feather/common/Tuple.java @@ -1,9 +1,11 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.common; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; /** * Represents an object that @@ -13,7 +15,7 @@ import lombok.Setter; * @param the left value * @param the right value */ -@NoArgsConstructor @AllArgsConstructor @Setter @Getter +@NoArgsConstructor @AllArgsConstructor @Setter @Getter @ToString public class Tuple { /** * The left value of this tuple. diff --git a/src/main/java/me/braydon/feather/data/Document.java b/src/main/java/me/braydon/feather/data/Document.java index ac06c45..77843cc 100644 --- a/src/main/java/me/braydon/feather/data/Document.java +++ b/src/main/java/me/braydon/feather/data/Document.java @@ -1,13 +1,18 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.data; import lombok.Getter; import lombok.NonNull; import lombok.ToString; import me.braydon.feather.FeatherSettings; -import me.braydon.feather.annotation.Collection; import me.braydon.feather.annotation.Field; import me.braydon.feather.annotation.Id; import me.braydon.feather.annotation.Serializable; +import me.braydon.feather.common.FieldUtils; import me.braydon.feather.common.Tuple; import me.braydon.feather.database.IDatabase; @@ -18,11 +23,10 @@ import java.util.Map; import java.util.UUID; /** - * A document is a key-value pair that is stored - * within a {@link Collection}. This document is - * based on the Bson {@link org.bson.Document} - * in MongoDB, however this document is universal - * between all {@link IDatabase}'s. + * A document is a key-value pair that is stored within + * a collection. This document is based on the Bson + * {@link org.bson.Document} in MongoDB, however this + * document is universal between all {@link IDatabase}'s. * * @author Braydon * @param the type of value this document holds @@ -61,14 +65,10 @@ public class Document { continue; } field.setAccessible(true); // Make our field accessible - boolean idField = field.isAnnotationPresent(Id.class); // Is this field annotated with @Id? - Field annotation = field.getAnnotation(Field.class); // Get the @Field annotation - String key = idField ? field.getAnnotation(Id.class).key() : annotation.key(); // The key of the database field - if (key.isEmpty()) { // No field in the annotation, use the field name - key = field.getName(); - } + String key = FieldUtils.extractKey(field); // The key of the database field + // The field is annotated with @Id, save it for later - if (idField) { + if (field.isAnnotationPresent(Id.class)) { idKey = key; } Class fieldType = field.getType(); // The type of the field @@ -87,7 +87,7 @@ public class Document { } } assert idKey != null; // We need an id key - this.idKey = idKey; // We have our id key + this.idKey = idKey; // Set our id key Tuple key = mappedData.get(idKey); // Get the id from the data map if (key == null) { // The element is missing an id field diff --git a/src/main/java/me/braydon/feather/database/IDatabase.java b/src/main/java/me/braydon/feather/database/IDatabase.java index 0a832dd..4a63c34 100644 --- a/src/main/java/me/braydon/feather/database/IDatabase.java +++ b/src/main/java/me/braydon/feather/database/IDatabase.java @@ -1,7 +1,11 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.database; import lombok.NonNull; -import me.braydon.feather.repository.Repository; import java.io.Closeable; @@ -11,9 +15,8 @@ import java.io.Closeable; * @author Braydon * @param the bootstrap class of this database * @param the type of credentials this database uses - * @param the type of repository for this database */ -public interface IDatabase> extends Closeable { +public interface IDatabase extends Closeable { /** * Get the name of this database. * @@ -25,8 +28,9 @@ public interface IDatabase> extends Closeabl * Initialize a connection to this database. * * @param credentials the optional credentials to use + * @throws IllegalStateException if already connected */ - void connect(C credentials); + void connect(C credentials) throws IllegalStateException; /** * Check if this database is connected. @@ -50,15 +54,4 @@ public interface IDatabase> extends Closeabl * @see B for bootstrap class */ B getBootstrap(); - - /** - * Create a new repository - * using this database. - * - * @param the id type - * @param the entity type - * @return the repository instance - * @see R for repository - */ - @NonNull R newRepository(); } \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/database/impl/mongodb/MongoDB.java b/src/main/java/me/braydon/feather/database/impl/mongodb/MongoDB.java index 8b8411e..3326e53 100644 --- a/src/main/java/me/braydon/feather/database/impl/mongodb/MongoDB.java +++ b/src/main/java/me/braydon/feather/database/impl/mongodb/MongoDB.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.database.impl.mongodb; import com.mongodb.BasicDBObject; @@ -15,10 +20,9 @@ import me.braydon.feather.database.IDatabase; * @author Braydon * @see MongoClient for the bootstrap class * @see ConnectionString for the credentials class - * @see MongoRepository for the repository class * @see MongoDB Official Site */ -public class MongoDB implements IDatabase> { +public class MongoDB implements IDatabase { /** * The current {@link MongoClient} instance. */ @@ -43,9 +47,11 @@ public class MongoDB implements IDatabase the identifier for type for entities + * @param the entity type the repository stores + * @param collectionName the collection name for the repository + * @param entityClass the class of the entity the repository uses * @return the repository instance + * @throws IllegalStateException if not connected * @see MongoRepository for repository */ - @Override @NonNull - public MongoRepository newRepository() { - return new MongoRepository<>(this); + @NonNull + public MongoRepository newRepository(@NonNull String collectionName, @NonNull Class entityClass) { + if (!isConnected()) { // Not connected + throw new IllegalStateException("Not connected"); + } + return new MongoRepository<>(this, entityClass, database.getCollection(collectionName)); } /** diff --git a/src/main/java/me/braydon/feather/database/impl/mongodb/MongoRepository.java b/src/main/java/me/braydon/feather/database/impl/mongodb/MongoRepository.java index 3f1a8f6..6632d93 100644 --- a/src/main/java/me/braydon/feather/database/impl/mongodb/MongoRepository.java +++ b/src/main/java/me/braydon/feather/database/impl/mongodb/MongoRepository.java @@ -1,13 +1,17 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.database.impl.mongodb; import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoCursor; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Indexes; import com.mongodb.client.model.UpdateOneModel; import com.mongodb.client.model.UpdateOptions; import lombok.NonNull; -import me.braydon.feather.annotation.Collection; -import me.braydon.feather.common.EntityUtils; import me.braydon.feather.common.Tuple; import me.braydon.feather.database.impl.mongodb.annotation.Index; import me.braydon.feather.repository.Repository; @@ -15,10 +19,9 @@ import org.bson.Document; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Predicate; /** * The {@link MongoDB} {@link Repository} implementation. @@ -28,8 +31,14 @@ import java.util.function.Predicate; * @param the entity type this repository stores */ public class MongoRepository extends Repository { - public MongoRepository(@NonNull MongoDB database) { - super(database); + /** + * The {@link MongoCollection} to use for this repository. + */ + @NonNull private final MongoCollection collection; + + public MongoRepository(@NonNull MongoDB database, @NonNull Class entityClass, @NonNull MongoCollection collection) { + super(database, entityClass); + this.collection = collection; } /** @@ -42,34 +51,20 @@ public class MongoRepository extends Repository { */ @Override public E find(@NonNull ID id) { - String idString = id.toString(); // Stringify the ID - throw new UnsupportedOperationException(); + return find("_id", id); } /** - * Find the entity matching the given predicate. + * Get the entity with the given id. * - * @param predicate the predicate to test - * @return the found entity + * @param idKey the key of the id + * @param id the entity id + * @return the entity with the id, null if none + * @see ID for id * @see E for entity - * @see Predicate for predicate */ - @Override - public E findOne(@NonNull Predicate predicate) { - throw new UnsupportedOperationException(); - } - - /** - * Find all entities matching the given predicate. - * - * @param predicate the predicate to test - * @return the found entities - * @see E for entity - * @see Predicate for predicate - */ - @Override - public List findAll(@NonNull Predicate predicate) { - throw new UnsupportedOperationException(); + public E find(@NonNull String idKey, @NonNull ID id) { + return newEntity(collection.find(new Document(idKey, id.toString())).first()); } /** @@ -80,7 +75,13 @@ public class MongoRepository extends Repository { */ @Override public List findAll() { - throw new UnsupportedOperationException(); + List entities = new ArrayList<>(); + try (MongoCursor cursor = collection.find().cursor()) { + while (cursor.hasNext()) { // Add the entity to the list + entities.add(newEntity(cursor.next())); + } + } + return Collections.unmodifiableList(entities); } /** @@ -91,48 +92,30 @@ public class MongoRepository extends Repository { */ @Override public void saveAll(@NonNull E... entities) { - Map>> toSave = new HashMap<>(); // The documents to save + List> updateModels = new ArrayList<>(); // The update models to bulk write - // Iterate over the given entities and ensure they - // are all valid, and if they are, collect them so - // we can bulk save them later for (E entity : entities) { - EntityUtils.ensureValid(entity, false); // Ensure our entity is valid - String collectionName = entity.getClass().getAnnotation(Collection.class).name(); // The name of the collection + me.braydon.feather.data.Document document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity - // Add the document to our list of documents to save - List> documents = toSave.getOrDefault(collectionName, new ArrayList<>()); - documents.add(new me.braydon.feather.data.Document<>(entity)); - toSave.put(collectionName, documents); - } - - // Iterate over the documents we want to save, and create - // an update model for them, as well as update indexes. - for (Map.Entry>> entry : toSave.entrySet()) { - MongoCollection collection = getDatabase().getDatabase().getCollection(entry.getKey()); // The collection to save to + // Add our update model to the list + updateModels.add(new UpdateOneModel<>( + Filters.eq(document.getIdKey(), document.getKey()), + new Document("$set", new Document(document.toMappedData())), + new UpdateOptions().upsert(true) + )); - List> updateModels = new ArrayList<>(); - for (me.braydon.feather.data.Document document : entry.getValue()) { - // Add or update model to the list - updateModels.add(new UpdateOneModel<>( - Filters.eq(document.getIdKey(), document.getKey()), - new Document("$set", new Document(document.toMappedData())), - new UpdateOptions().upsert(true) - )); - - // Create indexes for @Index fields - for (Map.Entry> mappedEntry : document.getMappedData().entrySet()) { - java.lang.reflect.Field field = mappedEntry.getValue().getLeft(); - if (field.isAnnotationPresent(Index.class)) { - collection.createIndex(Indexes.text(mappedEntry.getKey())); - } + // Create indexes for @Index fields specified in the entity + for (Map.Entry> mappedEntry : document.getMappedData().entrySet()) { + java.lang.reflect.Field field = mappedEntry.getValue().getLeft(); + if (field.isAnnotationPresent(Index.class)) { + collection.createIndex(Indexes.text(mappedEntry.getKey())); } } - - // We have updates models present, bulk write them to the database - if (!updateModels.isEmpty()) { - collection.bulkWrite(updateModels); - } + } + + // We have update models to execute, bulk write them + if (!updateModels.isEmpty()) { + collection.bulkWrite(updateModels); } } @@ -144,7 +127,7 @@ public class MongoRepository extends Repository { */ @Override public long count() { - throw new UnsupportedOperationException(); + return collection.countDocuments(); } /** @@ -155,6 +138,7 @@ public class MongoRepository extends Repository { */ @Override public void drop(@NonNull E entity) { - throw new UnsupportedOperationException(); + me.braydon.feather.data.Document document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity + collection.deleteOne(new Document(document.getIdKey(), document.getKey())); // Delete the entity } } \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/database/impl/mongodb/annotation/Index.java b/src/main/java/me/braydon/feather/database/impl/mongodb/annotation/Index.java index 00d9607..6a06b64 100644 --- a/src/main/java/me/braydon/feather/database/impl/mongodb/annotation/Index.java +++ b/src/main/java/me/braydon/feather/database/impl/mongodb/annotation/Index.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.database.impl.mongodb.annotation; import me.braydon.feather.database.impl.mongodb.MongoDB; diff --git a/src/main/java/me/braydon/feather/database/impl/redis/Redis.java b/src/main/java/me/braydon/feather/database/impl/redis/Redis.java index 4c2f6d0..309c0de 100644 --- a/src/main/java/me/braydon/feather/database/impl/redis/Redis.java +++ b/src/main/java/me/braydon/feather/database/impl/redis/Redis.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.database.impl.redis; import io.lettuce.core.RedisClient; @@ -12,10 +17,9 @@ import me.braydon.feather.database.IDatabase; * @author Braydon * @see StatefulRedisConnection for the bootstrap class * @see RedisURI for the credentials class - * @see RedisRepository for the repository class * @see Redis Official Site */ -public class Redis implements IDatabase, RedisURI, RedisRepository> { +public class Redis implements IDatabase, RedisURI> { /** * The current {@link RedisClient} instance. */ @@ -40,9 +44,11 @@ public class Redis implements IDatabase, * Initialize a connection to this database. * * @param credentials the optional credentials to use + * @throws IllegalArgumentException if no credentials are provided + * @throws IllegalStateException if already connected */ @Override - public void connect(RedisURI credentials) { + public void connect(RedisURI credentials) throws IllegalArgumentException, IllegalStateException { if (credentials == null) { // We need valid credentials throw new IllegalArgumentException("No credentials defined"); } @@ -91,17 +97,17 @@ public class Redis implements IDatabase, return connection; } - /** - * Create a new repository - * using this database. - * - * @return the repository instance - * @see RedisRepository for repository - */ - @Override @NonNull - public RedisRepository newRepository() { - return new RedisRepository<>(this); - } +// /** +// * Create a new repository +// * using this database. +// * +// * @return the repository instance +// * @see RedisRepository for repository +// */ +// @NonNull +// public RedisRepository newRepository() { +// return new RedisRepository<>(this); +// } // @Override // public void write(@NonNull Object element) { diff --git a/src/main/java/me/braydon/feather/database/impl/redis/RedisRepository.java b/src/main/java/me/braydon/feather/database/impl/redis/RedisRepository.java index 7232c00..7c133d2 100644 --- a/src/main/java/me/braydon/feather/database/impl/redis/RedisRepository.java +++ b/src/main/java/me/braydon/feather/database/impl/redis/RedisRepository.java @@ -1,10 +1,14 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.database.impl.redis; import lombok.NonNull; import me.braydon.feather.repository.Repository; import java.util.List; -import java.util.function.Predicate; /** * The {@link Redis} {@link Repository} implementation. @@ -14,8 +18,8 @@ import java.util.function.Predicate; * @param the entity type this repository stores */ public class RedisRepository extends Repository { - public RedisRepository(@NonNull Redis database) { - super(database); + public RedisRepository(@NonNull Redis database, @NonNull Class entityClass) { + super(database, entityClass); } /** @@ -31,32 +35,6 @@ public class RedisRepository extends Repository { throw new UnsupportedOperationException(); } - /** - * Find the entity matching the given predicate. - * - * @param predicate the predicate to test - * @return the found entity - * @see E for entity - * @see Predicate for predicate - */ - @Override - public E findOne(@NonNull Predicate predicate) { - throw new UnsupportedOperationException(); - } - - /** - * Find all entities matching the given predicate. - * - * @param predicate the predicate to test - * @return the found entities - * @see E for entity - * @see Predicate for predicate - */ - @Override - public List findAll(@NonNull Predicate predicate) { - throw new UnsupportedOperationException(); - } - /** * Get all entities within this repository. * diff --git a/src/main/java/me/braydon/feather/repository/Repository.java b/src/main/java/me/braydon/feather/repository/Repository.java index 97a44d9..45a0fe8 100644 --- a/src/main/java/me/braydon/feather/repository/Repository.java +++ b/src/main/java/me/braydon/feather/repository/Repository.java @@ -1,13 +1,25 @@ +/* + * Copyright (c) 2023 Braydon (Rainnny). All rights reserved. + * + * For inquiries, please contact braydonrainnny@gmail.com + */ package me.braydon.feather.repository; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NonNull; +import me.braydon.feather.FeatherSettings; +import me.braydon.feather.annotation.Serializable; +import me.braydon.feather.common.FieldUtils; import me.braydon.feather.database.IDatabase; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.util.List; -import java.util.function.Predicate; +import java.util.Map; +import java.util.UUID; /** * A repository belonging to a {@link IDatabase}. @@ -18,7 +30,7 @@ import java.util.function.Predicate; * @param the entity type this repository stores */ @AllArgsConstructor @Getter(AccessLevel.PROTECTED) -public abstract class Repository, ID, E> { +public abstract class Repository, ID, E> { /** * The database this repository belongs to. * @@ -26,6 +38,13 @@ public abstract class Repository, ID, E> { */ @NonNull private final D database; + /** + * The class for the entity this repository uses. + * + * @see E for entity + */ + @NonNull private final Class entityClass; + /** * Get the entity with the given id. * @@ -36,26 +55,6 @@ public abstract class Repository, ID, E> { */ public abstract E find(@NonNull ID id); - /** - * Find the entity matching the given predicate. - * - * @param predicate the predicate to test - * @return the found entity - * @see E for entity - * @see Predicate for predicate - */ - public abstract E findOne(@NonNull Predicate predicate); - - /** - * Find all entities matching the given predicate. - * - * @param predicate the predicate to test - * @return the found entities - * @see E for entity - * @see Predicate for predicate - */ - public abstract List findAll(@NonNull Predicate predicate); - /** * Get all entities within this repository. * @@ -97,4 +96,45 @@ public abstract class Repository, ID, E> { * @see E for entity */ public abstract void drop(@NonNull E entity); + + /** + * Construct a new entity from the given mapped data. + * + * @param mappedData the mapped data to parse + * @return the created entity, null if none + * @see E for entity + */ + protected final E newEntity(Map mappedData) { + if (mappedData == null) { // No mapped data given + return null; + } + try { + Constructor constructor = entityClass.getConstructor(); // Get the no args constructor + E entity = constructor.newInstance(); // Create the entity + + // Get the field tagged with @Id + for (Field field : entityClass.getDeclaredFields()) { + String key = FieldUtils.extractKey(field); // The key of the database field + Class type = field.getType(); // The type of the field + Object value = mappedData.get(key); // The value of the field + + // Field is serializable and is a string, deserialize it using Gson + if (field.isAnnotationPresent(Serializable.class) && value.getClass() == String.class) { + value = FeatherSettings.getGson().fromJson((String) value, type); + } else if (type == UUID.class) { // Type is a UUID, convert it + value = UUID.fromString((String) value); + } + + // Set the value of the field + field.setAccessible(true); + field.set(entity, value); + } + return entity; + } catch (NoSuchMethodException ex) { // We need our no args constructor + throw new IllegalStateException("Entity " + entityClass.getName() + " is missing no args constructor"); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) { + ex.printStackTrace(); + } + return null; + } } \ No newline at end of file