All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jalse.entities.Entities Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package jalse.entities;

import jalse.entities.EntityVisitor.EntityVisitResult;
import jalse.misc.JALSEExceptions;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * A utility for {@link Entity} related functionality (specifically around entity types).
*
* An Entity type allows entities to be used in an Object-Oriented way (get/set). It does this by * providing a number of annotations that specify to the proxy its behaviour. Entity types also * support default methods allowing some logic to be defined.
*
* Entity types are soft types - any entity can be 'cast' ({@link #asType(Entity, Class)}) to * another entity type even if it does not have the defining data. This does not set the entity as * this type but entities can be marked as a type manually for easy filtering/processing ( * {@link Entity#markAsType(Class)}). When creating an entity and supplying an entity type (like * {@link EntityContainer#newEntity(Class)}) this automatically marks the entity as the supplied * type - in fact all of the types in the entity type inheritance tree are added too (removable). An * entity can be marked as multiple entity types so filtering this way can become very useful for * processing similar entities.
* * @author Elliot Ford * * @see EntityProxies * @see #walkEntities(EntityContainer) * @see #walkEntityTree(EntityContainer, EntityVisitor) */ public final class Entities { /** * An empty EntityContainer. */ public static EntityContainer EMPTY_ENTITYCONTAINER = new UnmodifiableDelegateEntityContainer(null); /** * An empty EntityFactory. */ public static EntityFactory EMPTY_ENTITYFACTORY = new UnmodifiableDelegateEntityFactory(null); @SuppressWarnings("unchecked") private static void addDirectTypeAncestors(final Set> ancestry, final Class type) { for (final Class t : type.getInterfaces()) { if (!t.equals(Entity.class) && ancestry.add((Class) t)) { addDirectTypeAncestors(ancestry, t); } } } /** * Gets any entity within the container. * * @param container * Entity container. * * @return Gets an Optional of the resulting entity or an empty Optional if it was not found. */ public static Optional anyEntity(final EntityContainer container) { return container.streamEntities().findAny(); } /** * Gets any entity within the container marked with the specified type. * * @param container * Entity container. * * @param type * Entity type. * * @return Gets an Optional of the resulting entity or an empty Optional if it was not found. * * @see Entity#markAsType(Class) */ public static Optional anyEntityOfType(final EntityContainer container, final Class type) { return container.streamEntitiesOfType(type).findAny(); } /** * Wraps an Entity as the supplied Entity type. This is a convenience method for * {@link EntityProxies#proxyOfEntity(Entity, Class)}. * * @param entity * Entity to wrap. * @param type * Entity type to wrap to. * @return The wrapped Entity. * * @throws NullPointerException * If the Entity or Entity type are null. * @throws IllegalArgumentException * If the Entity type does not meet the criteria defined above. * * @see EntityProxies * @see JALSEExceptions#INVALID_ENTITY_TYPE */ public static T asType(final Entity entity, final Class type) { return EntityProxies.proxyOfEntity(entity, type); } /** * Creates an immutable empty entity container. * * @return Empty entity container. */ public static EntityContainer emptyEntityContainer() { return EMPTY_ENTITYCONTAINER; } /** * Creates an immutable empty entity factory. * * @return Empty entity factory. */ public static EntityFactory emptyEntityFactory() { return EMPTY_ENTITYFACTORY; } /** * Walks through the entity tree looking for an entity. * * @param container * Entity container. * @param id * Entity ID to look for. * @return Whether the entity was found. * * @see #walkEntityTree(EntityContainer, EntityVisitor) */ public static boolean findEntityRecursively(final EntityContainer container, final UUID id) { final AtomicBoolean found = new AtomicBoolean(); walkEntityTree(container, e -> { if (id.equals(e.getID())) { found.set(true); return EntityVisitResult.EXIT; } else { return EntityVisitResult.CONTINUE; } }); return found.get(); } /** * Gets the total entity count (recursive). * * @param container * Entity container. * * @return Total entity count. * * @see #walkEntityTree(EntityContainer, EntityVisitor) */ public static int getEntityCountRecursively(final EntityContainer container) { final AtomicInteger result = new AtomicInteger(); walkEntityTree(container, e -> { result.incrementAndGet(); return EntityVisitResult.CONTINUE; }); return result.get(); } /** * Gets the IDs of all the entities (recursive). * * @param container * Entity container. * * @return Set of all entity identifiers. * * @see #walkEntityTree(EntityContainer, EntityVisitor) */ public static Set getEntityIDsRecursively(final EntityContainer container) { final Set result = new HashSet<>(); walkEntityTree(container, e -> result.add(e.getID()) ? EntityVisitResult.CONTINUE : EntityVisitResult.IGNORE_CHILDREN); return result; } /** * Gets the highest level parent of this container. * * @param container * Container to get parent for. * @return Highest level parent (or this container if it has no parent). */ public static EntityContainer getHighestParent(final EntityContainer container) { Objects.requireNonNull(container); if (container instanceof Entity) { final EntityContainer parent = ((Entity) container).getContainer(); if (parent != null) { return getHighestParent(parent); } } return container; } /** * Gets all ancestors for the specified descendant type (not including {@link Entity}). * * @param type * Descendant type. * @return All ancestors or an empty set if its only ancestor is {@link Entity}. * * @throws IllegalArgumentException * If the Entity type is invalid * * @see JALSEExceptions#INVALID_ENTITY_TYPE */ public static Set> getTypeAncestry(final Class type) { EntityProxies.validateEntityType(type); final Set> ancestry = new HashSet<>(); addDirectTypeAncestors(ancestry, type); return ancestry; } /** * Checks to see if the entity has been tagged with the type. * * @param type * Entity type to check for. * @return Predicate of {@code true} if the entity is of the type or {@code false} if it is not. */ public static Predicate isMarkedAsType(final Class type) { return i -> i.isMarkedAsType(type); } /** * Checks if the specified type is equal to or a descendant from the specified ancestor type. * * @param descendant * Descendant type. * @param ancestor * Ancestor type. * @return Whether the descendant is equal or descended from the ancestor type. */ public static boolean isOrTypeDescendant(final Class descendant, final Class ancestor) { return ancestor.isAssignableFrom(descendant); } /** * Checks to see if the entity has not been tagged with the type. * * @param type * Entity type to check for. * @return Predicate of {@code true} if the entity is not of the type or {@code false} if it is. */ public static Predicate notMarkedAsType(final Class type) { return isMarkedAsType(type).negate(); } /** * Gets a random entity from the container (if there is one). * * @param container * Source container. * @return Random entity (if found). */ public static Optional randomEntity(final EntityContainer container) { return container.streamEntities().skip(ThreadLocalRandom.current().nextInt(container.getEntityCount())) .findFirst(); } /** * Creates an immutable read-only delegate entity container for the supplied container. * * @param container * Container to delegate for. * @return Immutable entity container. */ public static EntityContainer unmodifiableEntityContainer(final EntityContainer container) { return new UnmodifiableDelegateEntityContainer(Objects.requireNonNull(container)); } /** * Creates an immutable read-only delegate entity factory for the supplied factory. * * @param factory * Factory to delegate for. * @return Immutable entity factory. */ public static EntityFactory unmodifiableEntityFactory(final EntityFactory factory) { return new UnmodifiableDelegateEntityFactory(Objects.requireNonNull(factory)); } /** * A lazy-walked stream of entities (recursive and breadth-first). The entire stream will not be * loaded until it is iterated through. * * This is equivalent to {@code walkEntities(container, Integer.MAX_VALUE)} * * @param container * Entity container. * @return Lazy-walked recursive stream of entities. */ public static Stream walkEntities(final EntityContainer container) { return walkEntities(container, Integer.MAX_VALUE); } /** * A lazy-walked stream of entities (recursive and breadth-first). The entire stream will not be * loaded until it is iterated through. * * @param container * Entity container. * @param maxDepth * Maximum depth of the walk. * @return Lazy-walked recursive stream of entities. */ public static Stream walkEntities(final EntityContainer container, final int maxDepth) { final EntityTreeWalker walker = new EntityTreeWalker(container, maxDepth, e -> EntityVisitResult.CONTINUE); final Iterator iterator = new Iterator() { @Override public boolean hasNext() { return walker.isWalking(); } @Override public Entity next() { return walker.walk(); } }; return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false); } /** * Walks through all entities (recursive and breadth-first). Walking can be stopped or filtered * based on the visit result returned. * * This is equivalent to {@code walkEntityTree(container, Integer.MAX_VALUE, visitor)} * * @param container * Entity container. * @param visitor * Entity visitor. * * @see EntityVisitor */ public static void walkEntityTree(final EntityContainer container, final EntityVisitor visitor) { walkEntityTree(container, Integer.MAX_VALUE, visitor); } /** * Walks through all entities (recursive and breadth-first). Walking can be stopped or filtered * based on the visit result returned. * * @param container * Entity container. * @param maxDepth * Maximum depth of the walk. * @param visitor * Entity visitor. * * @see EntityVisitor */ public static void walkEntityTree(final EntityContainer container, final int maxDepth, final EntityVisitor visitor) { final EntityTreeWalker walker = new EntityTreeWalker(container, maxDepth, visitor); while (walker.isWalking()) { walker.walk(); } } /** * Checks to see if an container is in the same tree as the other container (checking highest * parent container). * * @param one * Container to check. * @param two * Container to check. * @return Whether the container is within the same tree as the other container. * * @see #getHighestParent(EntityContainer) */ public static boolean withinSameTree(final EntityContainer one, final EntityContainer two) { return Objects.equals(getHighestParent(one), getHighestParent(two)); } private Entities() { throw new UnsupportedOperationException(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy