package kernel;
import static rescuecore2.misc.java.JavaTools.instantiate;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.Collections;
import rescuecore2.misc.Pair;
import rescuecore2.config.Config;
import rescuecore2.components.Component;
import rescuecore2.components.Simulator;
import rescuecore2.components.Viewer;
import rescuecore2.components.Agent;
import rescuecore2.log.Logger;
/**
Container class for all kernel startup options.
*/
public class KernelStartupOptions {
private static final String AUTO_SUFFIX = ".auto";
private List<WorldModelCreator> worldOptions;
private List<Perception> perceptionOptions;
private List<CommunicationModel> commsOptions;
private Map<Simulator, Integer> sims;
private Map<Viewer, Integer> viewers;
private Map<Agent, Integer> agents;
private Map<Component, Integer> other;
private WorldModelCreator world;
private Perception perception;
private CommunicationModel comms;
/**
Create a KernelStartupOptions.
@param config The system configuration.
*/
public KernelStartupOptions(Config config) {
Pair<List<WorldModelCreator>, Integer> w = createOptions(config, KernelConstants.GIS_KEY, WorldModelCreator.class);
worldOptions = w.first();
world = worldOptions.get(w.second());
Pair<List<Perception>, Integer> p = createOptions(config, KernelConstants.PERCEPTION_KEY, Perception.class);
perceptionOptions = p.first();
perception = perceptionOptions.get(p.second());
Pair<List<CommunicationModel>, Integer> c = createOptions(config, KernelConstants.COMMUNICATION_MODEL_KEY, CommunicationModel.class);
commsOptions = c.first();
comms = commsOptions.get(c.second());
sims = createComponentOptions(config, KernelConstants.SIMULATORS_KEY, Simulator.class);
viewers = createComponentOptions(config, KernelConstants.VIEWERS_KEY, Viewer.class);
agents = createComponentOptions(config, KernelConstants.AGENTS_KEY, Agent.class);
other = createComponentOptions(config, KernelConstants.COMPONENTS_KEY, Component.class);
}
/**
Get the names of all components that should be started inline.
@return All inline component class names and the requested number of each.
*/
public Collection<Pair<String, Integer>> getInlineComponents() {
List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
for (Map.Entry<Simulator, Integer> next : sims.entrySet()) {
result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue()));
}
for (Map.Entry<Viewer, Integer> next : viewers.entrySet()) {
result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue()));
}
for (Map.Entry<Agent, Integer> next : agents.entrySet()) {
result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue()));
}
for (Map.Entry<Component, Integer> next : other.entrySet()) {
result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue()));
}
return result;
}
/**
Get the WorldModelCreator the kernel should use.
@return The selected WorldModelCreator.
*/
public WorldModelCreator getWorldModelCreator() {
return world;
}
/**
Set the WorldModelCreator the kernel should use.
@param creator The selected WorldModelCreator.
*/
public void setWorldModelCreator(WorldModelCreator creator) {
this.world = creator;
}
/**
Get the list of available WorldModelCreator implementations.
@return All known WorldModelCreators.
*/
public List<WorldModelCreator> getAvailableWorldModelCreators() {
return Collections.unmodifiableList(worldOptions);
}
/**
Get the Perception module the kernel should use.
@return The selected Perception.
*/
public Perception getPerception() {
return perception;
}
/**
Set the Perception module the kernel should use.
@param p The selected Perception.
*/
public void setPerception(Perception p) {
perception = p;
}
/**
Get the list of available Perception implementations.
@return All known Perceptions.
*/
public List<Perception> getAvailablePerceptions() {
return Collections.unmodifiableList(perceptionOptions);
}
/**
Get the CommunicationModel the kernel should use.
@return The selected CommunicationModel.
*/
public CommunicationModel getCommunicationModel() {
return comms;
}
/**
Set the CommunicationModel the kernel should use.
@param c The selected CommunicationModel.
*/
public void setCommunicationModel(CommunicationModel c) {
comms = c;
}
/**
Get the list of available CommunicationModel implementations.
@return All known CommunicationModels.
*/
public List<CommunicationModel> getAvailableCommunicationModels() {
return Collections.unmodifiableList(commsOptions);
}
/**
Get the list of available Simulator components.
@return All known Simulators.
*/
public Collection<Simulator> getAvailableSimulators() {
return Collections.unmodifiableSet(sims.keySet());
}
/**
Get the list of available Viewer components.
@return All known Viewers.
*/
public Collection<Viewer> getAvailableViewers() {
return Collections.unmodifiableSet(viewers.keySet());
}
/**
Get the list of available Agent components.
@return All known Agents.
*/
public Collection<Agent> getAvailableAgents() {
return Collections.unmodifiableSet(agents.keySet());
}
/**
Get the list of available components that are not simulators, viewers or agents.
@return All known Components that are not simulators, viewers or agents.
*/
public Collection<Component> getAvailableComponents() {
return Collections.unmodifiableSet(other.keySet());
}
/**
Get the number of instances of a type of component to start.
@param c The component type.
@return The number of instances to start.
*/
public int getInstanceCount(Component c) {
if (sims.containsKey(c)) {
return sims.get(c);
}
if (viewers.containsKey(c)) {
return viewers.get(c);
}
if (agents.containsKey(c)) {
return agents.get(c);
}
if (other.containsKey(c)) {
return other.get(c);
}
throw new IllegalArgumentException("Component " + c + " not recognised");
}
/**
Set the number of instances of a type of component to start.
@param c The component type.
@param count The number of instances to start.
*/
public void setInstanceCount(Component c, int count) {
if (c instanceof Simulator) {
sims.put((Simulator)c, count);
}
else if (c instanceof Viewer) {
viewers.put((Viewer)c, count);
}
else if (c instanceof Agent) {
agents.put((Agent)c, count);
}
else {
other.put(c, count);
}
}
private <T> Pair<List<T>, Integer> createOptions(Config config, String key, Class<T> expectedClass) {
List<T> instances = new ArrayList<T>();
int index = 0;
int selectedIndex = 0;
Logger.trace("Loading options: " + key);
List<String> classNames = config.getArrayValue(key);
String auto = config.getValue(key + AUTO_SUFFIX, null);
boolean autoFound = false;
for (String next : classNames) {
Logger.trace("Option found: '" + next + "'");
T t = instantiate(next, expectedClass);
if (t != null) {
instances.add(t);
if (next.equals(auto)) {
selectedIndex = index;
autoFound = true;
}
++index;
}
}
if (auto != null && !autoFound) {
Logger.warn("Could not find class " + auto + " in config key " + key + ". Values found: " + classNames);
}
return new Pair<List<T>, Integer>(instances, selectedIndex);
}
private <T> Map<T, Integer> createComponentOptions(Config config, String key, Class<T> expectedClass) {
Logger.trace("Loading component options: " + key);
Map<T, Integer> result = new HashMap<T, Integer>();
List<String> classNames = config.getArrayValue(key, "");
List<String> autoClassNames = config.getArrayValue(key + AUTO_SUFFIX, "");
Set<String> allClassNames = new HashSet<String>(classNames);
allClassNames.addAll(strip(autoClassNames));
for (String next : allClassNames) {
Logger.trace("Option found: '" + next + "'");
T t = instantiate(next, expectedClass);
if (t != null) {
int count = getStartCount(next, autoClassNames);
result.put(t, count);
}
}
return result;
}
private int getStartCount(String className, List<String> auto) {
for (String next : auto) {
if (next.startsWith(className)) {
int index = next.indexOf("*");
if (index == -1) {
return 1;
}
String arg = next.substring(index + 1);
if ("n".equals(arg)) {
return Integer.MAX_VALUE;
}
return Integer.parseInt(arg);
}
}
return 0;
}
private List<String> strip(List<String> autoClassNames) {
List<String> result = new ArrayList<String>(autoClassNames.size());
// Remove any trailing *n
for (String s : autoClassNames) {
int index = s.indexOf("*");
if (index != -1) {
result.add(s.substring(0, index));
}
else {
result.add(s);
}
}
return result;
}
}