package com.nicewuerfel.blockown;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class OwnedEntity implements Ownable {
private static final long serialVersionUID = -5197934920645104143L;
@Nonnull
private transient WeakReference<Entity> entityRef = new WeakReference<>(null);
@Nullable
private transient Material material = null;
@Nonnull
private final UUID id;
@Nonnull
private final String worldName;
/**
* Creates a new OwnedEntity instance holding a WeakReference to the given block.
*
* @param entity the entity
* @return the new OwnedEntity instance
*/
@Nonnull
public static OwnedEntity newInstance(Entity entity) {
if (entity == null) {
throw new NullPointerException("Entity can't be null");
}
return new OwnedEntity(entity.getUniqueId(), entity.getWorld().getName(),
new Material(entity.getType()), entity);
}
/**
* Creates a new OwnedEntity instance. The corresponding Entity will be looked up when getEntity()
* is called.
*
* @param id the UUID
* @param worldName the worldName
* @param material the material
* @return the new OwnedEntity instance
*/
@Nonnull
public static OwnedEntity newInstance(UUID id, String worldName, Material material) {
if (id == null) {
throw new NullPointerException("UUID can't be null");
}
if (worldName == null) {
throw new NullPointerException("world name can't be null");
}
// it's okay if material is null
return new OwnedEntity(id, worldName, material, null);
}
/**
* Creates a new OwnedEntity instance. The corresponding Entity and its Material will be looked up
* when needed.
*
* @param id the UUID
* @param worldName the worldName
* @return the new OwnedEntity instance
*/
@Nonnull
public static OwnedEntity newInstance(UUID id, String worldName) {
return newInstance(id, worldName, null);
}
private OwnedEntity(UUID id, String worldName, Material material, Entity entity) {
this.id = id;
this.worldName = worldName;
this.material = material;
this.entityRef = new WeakReference<>(entity);
}
/**
* Gets the entity corresponding to this OwnedEntity. Keep in mind that this can be a very
* expensive operation.
*
* @return the entity
* @throws InvalidWorldNameException if the world name is invalid
* @throws InvalidUUIDException if the entity can't be found in the specified world
*/
@Nonnull
public Entity getEntity() throws InvalidWorldNameException, InvalidUUIDException {
Entity result = entityRef.get();
if (result == null) {
Class<? extends Entity> materialClass = getMaterial().getEntityType().getEntityClass();
Collection<? extends Entity> entities;
if (materialClass == null) {
entities = getWorld().getEntities();
} else {
entities = getWorld().getEntitiesByClass(materialClass);
}
for (Entity entity : entities) {
if (entity.getUniqueId().equals(id)) {
entityRef = new WeakReference<>(entity);
return entity;
}
}
throw new InvalidUUIDException(
"No entity with UUID " + id + " could be found in world " + worldName);
} else {
return result;
}
}
@Nonnull
public UUID getUniqueId() {
return id;
}
/**
* Returns the world this entity lives in.
*
* @throws InvalidWorldNameException if the world name is invalid
*/
@Override
@Nonnull
public World getWorld() throws InvalidWorldNameException {
World world = Bukkit.getServer().getWorld(worldName);
if (world == null) {
throw new InvalidWorldNameException("There is no world called " + worldName);
}
return world;
}
@Override
@Nonnull
public String getWorldName() {
return worldName;
}
@Override
@Nonnull
public Material getMaterial() throws InvalidWorldNameException {
if (material == null) {
material = new Material(getEntity().getType());
}
return material;
}
/**
* Checks if the entity is living. This can be a very expensive operation if it needs to look up
* the entity.
*
* @return true, if it is a LivingEntity
* @throws InvalidWorldNameException if the world name is invalid
* @throws InvalidUUIDException if the entity can't be found in the specified world
*/
@Nonnull
public boolean isLiving() throws InvalidWorldNameException, InvalidUUIDException {
EntityType type = getMaterial().getEntityType();
return type.isAlive();
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
}
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
entityRef = new WeakReference<>(null);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof OwnedEntity)) {
return false;
}
OwnedEntity other = (OwnedEntity) obj;
if (!id.equals(other.id)) {
return false;
}
return true;
}
@Override
public String toString() {
return "OwnedEntity [id=" + id + ", worldName=" + worldName + ", material=" + material + "]";
}
}