package rescuecore2.worldmodel;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import rescuecore2.registry.Registry;
/**
Abstract base class for WorldModel implementations.
@param <T> The subclass of Entity that this world model holds.
*/
public abstract class AbstractWorldModel<T extends Entity> implements WorldModel<T> {
private Set<WorldModelListener<? super T>> listeners;
private Collection<Class<? extends T>> allowedClasses;
/**
Construct a new abstract world model.
*/
protected AbstractWorldModel() {
listeners = new HashSet<WorldModelListener<? super T>>();
allowedClasses = new HashSet<Class<? extends T>>();
}
@Override
public final void addWorldModelListener(WorldModelListener<? super T> l) {
synchronized (listeners) {
listeners.add(l);
}
}
@Override
public final void removeWorldModelListener(WorldModelListener<? super T> l) {
synchronized (listeners) {
listeners.remove(l);
}
}
@Override
@SuppressWarnings("unchecked")
public final void addEntity(Entity e) {
if (isAllowed(e)) {
addEntityImpl((T)e);
}
else {
throw new IllegalArgumentException(this + " does not accept entities of type " + e.getClass().getName());
}
}
@Override
public final void addEntities(Collection<? extends Entity> e) {
for (Entity next : e) {
addEntity(next);
}
}
@Override
public final void removeEntity(T e) {
removeEntity(e.getID());
}
/**
Subclasses should provide their implementation of addEntity here.
@param t The entity to add.
*/
protected abstract void addEntityImpl(T t);
@Override
public void merge(Collection<? extends Entity> toMerge) {
for (Entity next : toMerge) {
T existing = getEntity(next.getID());
if (existing == null) {
addEntity(next);
}
else {
Set<Property> props = existing.getProperties();
for (Property prop : props) {
Property other = next.getProperty(prop.getURN());
if (other.isDefined()) {
prop.takeValue(other);
}
}
}
}
}
@Override
public void merge(ChangeSet changeSet) {
for (EntityID e : changeSet.getChangedEntities()) {
Entity existingEntity = getEntity(e);
boolean add = false;
if (existingEntity == null) {
// Construct a new entity
existingEntity = Registry.getCurrentRegistry().createEntity(changeSet.getEntityURN(e), e);
if (existingEntity == null) {
// Bail out
continue;
}
add = true;
}
for (Property p : changeSet.getChangedProperties(e)) {
Property existingProperty = existingEntity.getProperty(p.getURN());
existingProperty.takeValue(p);
}
if (add) {
addEntity(existingEntity);
}
}
for (EntityID next : changeSet.getDeletedEntities()) {
removeEntity(next);
}
}
/**
Notify listeners that an entity has been added.
@param e The new entity.
*/
protected final void fireEntityAdded(T e) {
for (WorldModelListener<? super T> l : getListeners()) {
l.entityAdded(this, e);
}
}
/**
Notify listeners that an entity has been removed.
@param e The entity that has been removed.
*/
protected final void fireEntityRemoved(T e) {
for (WorldModelListener<? super T> l : getListeners()) {
l.entityRemoved(this, e);
}
}
/**
Find out if a particular Entity is allowed into this world model. The default implementation checks that the object is assignable to at least one class previously registered with {@link #registerAllowedClass(Class)}.
@param e The entity to check.
@return True if the entity is allowed, false otherwise.
*/
protected boolean isAllowed(Entity e) {
for (Class<? extends T> next : allowedClasses) {
if (next.isAssignableFrom(e.getClass())) {
return true;
}
}
return false;
}
/**
Register an allowed class.
@param clazz The subclass of Entity that is allowed in this world model.
*/
protected void registerAllowedClass(Class<? extends T> clazz) {
allowedClasses.add(clazz);
}
private Collection<WorldModelListener<? super T>> getListeners() {
synchronized (listeners) {
return new HashSet<WorldModelListener<? super T>>(listeners);
}
}
}