package jalse.entities; import static jalse.attributes.Attributes.EMPTY_ATTRIBUTECONTAINER; import static jalse.entities.Entities.asType; import java.util.HashSet; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import jalse.attributes.AttributeContainer; import jalse.misc.ListenerSet; /** * This is an {@link Entity} collection. Entities can only be added to a container by creating them * and remove them by killing them. Entity creation and death can be listened for using * {@link EntityListener} and {@link EntityEvent}. * * @author Elliot Ford * * @see DefaultEntityContainer * @see EntityFactory * @see Entities#emptyEntityContainer() * @see Entities#unmodifiableEntityContainer(EntityContainer) */ public interface EntityContainer { /** * Adds a listener for entities. * * @param listener * Listener to add. * * @return {@code true} if container did not already contain this listener. * @throws NullPointerException * if listener is null. * * @see ListenerSet#add(Object) * */ boolean addEntityListener(EntityListener listener); /** * Gets all the entities within the containers. * * @return Gets all entities or an empty set if none were found. * * @see #streamEntities() */ default Set<Entity> getEntities() { return streamEntities().collect(Collectors.toSet()); } /** * Gets all the entities as the specified type. * * @param type * Entity type to check for. * @return Set of entities as the type. * * @see #streamEntitiesAsType(Class) */ default <T extends Entity> Set<T> getEntitiesAsType(final Class<T> type) { return streamEntitiesAsType(type).collect(Collectors.toSet()); } /** * Gets all the entities marked with the specified type. * * @param type * Entity type to check for. * @return Set of entities marked with the type. * * @see #streamEntitiesOfType(Class) */ default <T extends Entity> Set<T> getEntitiesOfType(final Class<T> type) { return streamEntitiesOfType(type).collect(Collectors.toSet()); } /** * Gets the entity with the specified ID. * * @param id * Unique ID of the entity. * @return The entity matching the supplied id or null if none found. */ Entity getEntity(final UUID id); /** * This is a convenience method for getting an entity (no optional). The entity is wrapped with * the supplied entity type. * * @param id * Unique ID of the entity. * @param type * Entity type to wrap to. * @return The entity matching the supplied id or null if none found. * * @see Entities#asType(Entity, Class) */ default <T extends Entity> T getEntityAsType(final UUID id, final Class<T> type) { final Entity e = getEntity(id); return e != null ? asType(e, type) : null; } /** * Gets the direct entity count. * * @return Direct child entity count. */ int getEntityCount(); /** * Gets the IDs of all the entities within the container. * * @return Set of all entity identifiers. */ Set<UUID> getEntityIDs(); /** * Gets all the entity listeners. * * @return All the entity listeners. */ Set<? extends EntityListener> getEntityListeners(); /** * This is a convenience method for getting an entity (optional). * * @param id * Unique ID of the entity. * @return Gets an Optional of the resulting entity or an empty Optional if it was not found. * @throws NullPointerException * If the ID is null. */ default Optional<Entity> getOptEntity(final UUID id) { return Optional.ofNullable(getEntity(id)); } /** * This is a convenience method for getting an entity (optional).The entity is wrapped with the * supplied entity type. * * @param id * Unique ID of the entity. * @param type * Entity type to wrap to. * @return Gets an Optional of the resulting entity or an empty Optional if it was not found. * @throws NullPointerException * If type is null. * * @see Entities#asType(Entity, Class) */ default <T extends Entity> Optional<T> getOptEntityAsType(final UUID id, final Class<T> type) { return getOptEntity(id).map(e -> asType(e, type)); } /** * Checks whether the container has any entities. * * @return Whether the container is not empty. */ default boolean hasEntities() { return getEntityCount() > 0; } /** * Checks whether the entity is contained. * * @param id * Entity ID. * @return Whether the entity was found. */ default boolean hasEntity(final UUID id) { return getEntity(id) != null; } /** * Checks whether the container contains a particular listener. * * @param listener * The EntityListener to check for. * @return Whether the container contains the given EntityListener. */ default boolean hasEntityListener(final EntityListener listener) { return getEntityListeners().contains(listener); } /** * Kills all entities. */ void killEntities(); /** * Kills the specified entity. * * @param id * Entity ID. * @return Whether the entity was alive. */ boolean killEntity(UUID id); /** * Creates a new entity with a random ID. * * @return The newly created entity's ID. * @throws IllegalStateException * If the entity limit has been reached. * * @see UUID#randomUUID() */ default Entity newEntity() { return newEntity(EMPTY_ATTRIBUTECONTAINER); } /** * Creates a new entity with a random ID. * * @param sourceContainer * Source attribute container. * * @return The newly created entity's ID. * @throws IllegalStateException * If the entity limit has been reached. * * @see UUID#randomUUID() */ default Entity newEntity(final AttributeContainer sourceContainer) { return newEntity(UUID.randomUUID(), sourceContainer); } /** * Creates a new entity with a random ID. This entity is marked as the specified entity type and * then wrapped to it. * * @param type * Entity type. * @return The newly created entity. * @throws IllegalStateException * If the entity limit has been reached. * * @see UUID#randomUUID() * @see Entity#markAsType(Class) * @see Entities#asType(Entity, Class) */ default <T extends Entity> T newEntity(final Class<T> type) { return newEntity(type, EMPTY_ATTRIBUTECONTAINER); } /** * Creates a new entity with a random ID. This entity is marked as the specified entity type and * then wrapped to it. * * @param type * Entity type. * @param sourceContainer * Source attribute container. * @return The newly created entity. * @throws IllegalStateException * If the entity limit has been reached. * * @see UUID#randomUUID() * @see Entity#markAsType(Class) * @see Entities#asType(Entity, Class) */ default <T extends Entity> T newEntity(final Class<T> type, final AttributeContainer sourceContainer) { return newEntity(UUID.randomUUID(), type, sourceContainer); } /** * Creates new entity with the specified ID. * * @param id * Entity ID. * @return The newly created entity. * @throws IllegalStateException * If the entity limit has been reached. * @throws IllegalArgumentException * If the entity ID is already assigned. */ default Entity newEntity(final UUID id) { return newEntity(id, EMPTY_ATTRIBUTECONTAINER); } /** * Creates new entity with the specified ID. * * @param id * Entity ID. * @param sourceContainer * Source attribute container. * @return The newly created entity. * @throws IllegalStateException * If the entity limit has been reached. * @throws IllegalArgumentException * If the entity ID is already assigned. */ Entity newEntity(UUID id, AttributeContainer sourceContainer); /** * Creates new entity with the specified ID. This entity is marked as the specified entity type * and then wrapped to it. * * * @param id * Entity ID. * @param type * Entity type. * @return The newly created entity. * @throws IllegalStateException * If the entity limit has been reached. * @throws IllegalArgumentException * If the entity ID is already assigned. * * @see Entity#markAsType(Class) * @see Entities#asType(Entity, Class) */ default <T extends Entity> T newEntity(final UUID id, final Class<T> type) { return newEntity(id, type, EMPTY_ATTRIBUTECONTAINER); } /** * Creates new entity with the specified ID. This entity is marked as the specified entity type * and then wrapped to it. * * * @param id * Entity ID. * @param type * Entity type. * @param sourceContainer * Source attribute container. * @return The newly created entity. * @throws IllegalStateException * If the entity limit has been reached. * @throws IllegalArgumentException * If the entity ID is already assigned. * * @see Entity#markAsType(Class) * @see Entities#asType(Entity, Class) */ <T extends Entity> T newEntity(UUID id, Class<T> type, AttributeContainer sourceContainer); /** * Receives an entity (from a transfer). This method may receive an entity from within or * outside the tree. * * @param e * Entity to receive. * @return Whether the entity was received. * * @see #transferEntity(UUID, EntityContainer) */ boolean receiveEntity(Entity e); /** * Removes a entity listener. * * @param listener * Listener to remove. * * @return {@code true} if the listener was removed. * @throws NullPointerException * if listener is null. * * @see ListenerSet#remove(Object) * */ boolean removeEntityListener(EntityListener listener); /** * Removes all listeners for entities. */ void removeEntityListeners(); /** * Provides a stream of entities from the container. * * @return A stream of entities in the container. */ Stream<Entity> streamEntities(); /** * Gets a stream of as the specified type. * * @param type * Entity type to check for. * @return Stream of entities as the type. * * @see Entity#asType(Class) * @see #streamEntities() */ default <T extends Entity> Stream<T> streamEntitiesAsType(final Class<T> type) { return streamEntities().map(e -> asType(e, type)); } /** * Gets a stream of entities marked with the specified type. * * @param type * Entity type to check for. * @return Stream of entities marked with the type. * * @see Entity#isMarkedAsType(Class) * @see Entity#asType(Class) * @see #streamEntities() */ default <T extends Entity> Stream<T> streamEntitiesOfType(final Class<T> type) { return streamEntities().filter(e -> e.isMarkedAsType(type)).map(e -> asType(e, type)); } /** * Streams IDs of all the entities within the container. * * @return Stream of all entity identifiers. */ default Stream<UUID> streamEntityIDs() { return getEntityIDs().stream(); } /** * Transfers all entities to the destination. * * @param destination * Destination to transfer to. * @return Entities that could not be transferred. * * @see #transferEntities(Set, EntityContainer) */ default Set<UUID> transferAllEntities(final EntityContainer destination) { return transferEntities(getEntityIDs(), destination); } /** * Transfers all entities to the destination. * * @param predicate * Predicate to filter entities. * @param destination * Destination to transfer to. * @return Entities that could not be transferred. */ default Set<UUID> transferEntities(final Predicate<Entity> predicate, final EntityContainer destination) { return transferEntities(streamEntities().filter(predicate).map(Entity::getID).collect(Collectors.toSet()), destination); } /** * Transfers a number of entities * * @param entityIDs * Entities to transfer. * @param destination * Destination to transfer to. * @return Entities that could not be transferred. */ default Set<UUID> transferEntities(final Set<UUID> entityIDs, final EntityContainer destination) { Objects.requireNonNull(destination); final Set<UUID> notTransferred = new HashSet<>(); for (final UUID id : entityIDs) { if (!transferEntity(id, destination)) { notTransferred.add(id); } } return notTransferred; } /** * Transfers the entity to the supplied destination container. * * @param id * Entity ID. * @param destination * Target container. * @return Whether the entity was transferred. * * @see #receiveEntity(Entity) */ boolean transferEntity(UUID id, EntityContainer destination); }