package rescuecore2.standard.kernel.comms;
import java.util.Collection;
import java.util.Map;
import java.util.HashSet;
import java.util.ArrayList;
import rescuecore2.worldmodel.Entity;
import rescuecore2.misc.collections.LazyMap;
import rescuecore2.log.Logger;
import rescuecore2.standard.messages.AKSpeak;
/**
Abstract base class for channels.
*/
public abstract class AbstractChannel implements Channel {
/** The set of subscribers. */
protected Collection<Entity> subscribers;
/** This channels's ID. */
protected int channelID;
private Map<Entity, Collection<AKSpeak>> messagesForAgents;
private Noise inputNoise;
private Noise outputNoise;
/**
Construct an AbstractChannel.
@param channelID The ID of this channel.
*/
protected AbstractChannel(int channelID) {
this.channelID = channelID;
subscribers = new HashSet<Entity>();
messagesForAgents = new LazyMap<Entity, Collection<AKSpeak>>() {
@Override
public Collection<AKSpeak> createValue() {
return new ArrayList<AKSpeak>();
}
};
inputNoise = null;
outputNoise = null;
}
@Override
public void setInputNoise(Noise noise) {
inputNoise = noise;
}
@Override
public void setOutputNoise(Noise noise) {
outputNoise = noise;
}
@Override
public void timestep() {
messagesForAgents.clear();
}
@Override
public void addSubscriber(Entity a) {
subscribers.add(a);
}
@Override
public void removeSubscriber(Entity a) {
subscribers.remove(a);
}
@Override
public Collection<Entity> getSubscribers() {
return subscribers;
}
@Override
public Collection<AKSpeak> getMessagesForAgent(Entity agent) {
Collection<AKSpeak> c = messagesForAgents.get(agent);
return new ArrayList<AKSpeak>(c);
}
@Override
public final void push(AKSpeak speak) throws InvalidMessageException {
int channel = speak.getChannel();
if (channel != channelID) {
throw new InvalidMessageException("Tried to push '" + speak + "' to channel " + channelID);
}
Logger.debug("Pushing " + speak + " through channel " + channelID);
speak = applyInputNoise(speak);
Logger.debug("Input noise result: " + speak);
if (speak != null) {
pushImpl(speak);
}
}
/**
Push a message after input noise has been applied.
@param msg The message.
@throws InvalidMessageException If the message is invalid.
*/
protected abstract void pushImpl(AKSpeak msg) throws InvalidMessageException;
/**
Register a message that should be send to an agent on the next call to @{link #getMessagesForAgent(AgentProxy)}. This method will ignore the subscribers list so subclasses should use @{link #isSubscribed(Entity)} if they wish to restrict messages to subscribers only.
@param a The agent.
@param msg The message.
*/
protected void addMessageForAgent(Entity a, AKSpeak msg) {
Logger.debug("Adding message " + msg + " for agent " + a);
msg = applyOutputNoise(msg);
Logger.debug("Output noise result: " + msg);
if (msg != null) {
Collection<AKSpeak> c = messagesForAgents.get(a);
c.add(msg);
}
}
/**
Register a message that should be send to all subscribers.
@param msg The message.
*/
protected void addMessageForSubscribers(AKSpeak msg) {
for (Entity e : subscribers) {
addMessageForAgent(e, msg);
}
}
/**
Find out if an entity is subscribed to this channel.
@param e The entity to check.
@return True iff the entity is subscribed to this channel.
*/
protected boolean isSubscribed(Entity e) {
return subscribers.contains(e);
}
/**
Apply the input noise to a message.
@param msg The message to apply input noise to.
@return A message with noise added, or the original message, or null.
*/
private AKSpeak applyInputNoise(AKSpeak msg) {
if (inputNoise != null) {
return inputNoise.applyNoise(msg);
}
return msg;
}
/**
Apply the output noise to a message.
@param msg The message to apply output noise to.
@return A message with noise added, or the original message, or null.
*/
private AKSpeak applyOutputNoise(AKSpeak msg) {
if (outputNoise != null) {
return outputNoise.applyNoise(msg);
}
return msg;
}
}