001 /** 002 * planningtool - A Planning Tool with Critiquing Support. 003 * 004 * Copyright (C) 2006 olale 005 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License 008 * as published by the Free Software Foundation; either version 2 009 * of the License, or (at your option) any later version. 010 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 020 * Contact information: 021 * E-mail: olale@ida.liu.se 022 * olale@lysator.liu.se 023 */ 024 package se.liu.ida.critiquer.resources; 025 026 import java.awt.Color; 027 import java.awt.GridLayout; 028 import java.io.Serializable; 029 import java.util.ArrayList; 030 import java.util.HashMap; 031 032 import javax.swing.JPanel; 033 034 import se.liu.ida.critiquer.activities.Activity; 035 import se.liu.ida.critiquer.activities.ActivityUtils; 036 import se.liu.ida.critiquer.activities.parameters.AgentChangedListener; 037 import se.liu.ida.critiquer.activities.parameters.AgentParameter; 038 import se.liu.ida.critiquer.gui.AgentLabel; 039 import se.liu.ida.critiquer.mics.Comparer; 040 import se.liu.ida.critiquer.mics.ReferenceHolder; 041 import se.liu.ida.critiquer.mics.Utils; 042 import se.liu.ida.critiquer.scenarios.standard.AgentParameterName; 043 import se.liu.ida.critiquer.simulation.AgentSimulationState; 044 import se.liu.ida.critiquer.simulation.DefaultAgentSimulationState; 045 import se.liu.ida.critiquer.simulation.SimulationEngine; 046 047 /** 048 * <p> 049 * Agents are a part of and perform activities. They are added and removed via 050 * the Resource View. 051 * </p> 052 * <p> 053 * In order for an agent to be considered suitable for an activity is should put 054 * the appropriate entries into the <code>fitnessMap</code> class field. 055 * </p> 056 * <p> 057 * Also, in order to calculate how long it takes for an agent to perform an 058 * activity, the <code>missionContributionCalculators</code> map is used to 059 * look up procedures that calculate the time it takes an agent to perform some 060 * unit of work on an activity. Moreover, performing an operation may have 061 * side-effects which are modelled by having a <code>HashMap</code> as an 062 * argument to a <code>AgentContributionCalculator</code> that may be modified 063 * by the agent while performing some action. 064 * </p> 065 * <p> 066 * It is not intended that a single "use" of an agent solves a task, since it 067 * may be impossible for an agent to complete a task on its own or it may be 068 * more appropriate if the agent performs only a small part of it. As it is 069 * right now, the agent reports some number on how long it takes to perform 070 * something and the activity is responsible for accumulating this information 071 * into some overall time for the mission as a whole. 072 * </p> 073 */ 074 075 public abstract class Agent implements Serializable { 076 077 /** 078 * 079 */ 080 private static final long serialVersionUID = 1L; 081 082 /** 083 * Placeholder agent to be used when the side-effects of using an agent is 084 * desirable and the most convenient way is to have a placeholder agent 085 */ 086 public static Agent dummyAgent = new DefaultAgent("none"); 087 static { 088 dummyAgent.setTimeLineColor(Color.GREEN); 089 } 090 091 private Color timeLineColor = Color.BLACK; 092 093 protected static HashMap<Class, HashMap<Class, Suitability>> fitnessMap = new HashMap<Class, HashMap<Class, Suitability>>(); 094 095 public HashMap<Class, AgentContributionCalculator> missionContributionCalculators = new HashMap<Class, AgentContributionCalculator>(); 096 097 private Agent parent = null; 098 099 private ArrayList<Agent> children = new ArrayList<Agent>(); 100 101 public String name; 102 103 private transient JPanel panel; 104 105 private ArrayList<AgentChangedListener> agentChangedListeners = new ArrayList<AgentChangedListener>(); 106 107 /** 108 * Alla <code>agentParameters</code> used by the agent must be added in 109 * the constructor of the agent. Here, an agentParameter means a parameter 110 * that determines how the agent works, not an activity. 111 */ 112 private HashMap<AgentParameterName, ParameterInAgent<?>> agentParameters = new HashMap<AgentParameterName, ParameterInAgent<?>>(); 113 114 protected transient AgentSimulationState agentSimulationState = new DefaultAgentSimulationState(this); 115 116 public <T> void addParameter(ParameterInAgent<T> param) { 117 agentParameters.put(param.getName(), param); 118 119 } 120 121 @SuppressWarnings("unchecked") 122 public <C> ParameterInAgent<C> getParamByClassAndName(Class<C> c, AgentParameterName name) { 123 return (ParameterInAgent<C>) agentParameters.get(name); 124 } 125 126 /** 127 * Returns the name of this agent 128 * 129 * @see java.lang.Object#toString() 130 */ 131 @Override 132 public String toString() { 133 return name; 134 } 135 136 /** 137 * Take all user-visible parameters of this agent and add them to the panel 138 * 139 */ 140 protected void initPanel() { 141 panel = new JPanel(); 142 panel.setLayout(new GridLayout(0, 1)); 143 for (ParameterInAgent agentParam : agentParameters.values()) { 144 if (agentParam.getType() == ParameterInAgent.Type.EXTERNAL) { 145 panel.add(agentParam.getComponent()); 146 147 } 148 } 149 } 150 151 public ArrayList<Activity> getActivitiesForAgent() { 152 return Utils.findAll(ReferenceHolder.allActivities, new Comparer<Activity>() { 153 154 public boolean check(Activity activity) { 155 return ActivityUtils.agentInActivity(Agent.this, activity); 156 } 157 158 }); 159 } 160 161 public void initAgentSimulationState(SimulationEngine engine) { 162 agentSimulationState.initSimulation(engine); 163 } 164 165 public AgentSimulationState getAgentSimulationState() { 166 return agentSimulationState; 167 } 168 169 /** 170 * 171 * Compare agents with respect to their names 172 * 173 * @see java.lang.Object#equals(java.lang.Object) 174 */ 175 @Override 176 public boolean equals(Object o) { 177 boolean ret = false; 178 if (o instanceof Agent) { 179 Agent agent = (Agent) o; 180 ret = toString().equals(agent.toString()); 181 } 182 return ret; 183 } 184 185 /** 186 * 187 * Use the name for hash value 188 * 189 * @see java.lang.Object#hashCode() 190 */ 191 @Override 192 public int hashCode() { 193 return toString().hashCode(); 194 } 195 196 /** 197 * @param name 198 */ 199 public Agent(String name) { 200 this.name = name; 201 } 202 203 /** 204 * @param parent 205 * is supposed to be an organizational unit that contains this 206 * agent, null if not applicable 207 * @param name 208 */ 209 public Agent(Agent parent, String name) { 210 this.name = name; 211 this.parent = parent; 212 this.parent.addChild(this); 213 } 214 215 public void addChild(Agent child) { 216 if (!children.contains(child)) { 217 children.add(child); 218 } 219 220 } 221 222 public AgentLabel getLabel(AgentParameter param) { 223 return new AgentLabel(this, param); 224 } 225 226 /** 227 * Should agents or constraints be responsible for knowing which agent is 228 * suitable for which task? Right now we require agents to provide this 229 * information. It could easily be lifted to a GUI instead, if that's really 230 * preferable. 231 */ 232 public static Suitability getSuitability(Agent agent, Activity activity) { 233 Suitability fitness = Suitability.FIT; 234 if (fitnessMap.containsKey(agent.getClass())) { 235 if (fitnessMap.get(agent.getClass()).containsKey(activity.getClass())) { 236 fitness = fitnessMap.get(agent.getClass()).get(activity.getClass()); 237 } 238 } 239 return fitness; 240 } 241 242 /** 243 * Agents have knowledge of how long it takes them to complete a mission of 244 * a certain class, or should activities know how to calculate that? 245 * 246 */ 247 248 @SuppressWarnings("unchecked") 249 public <T extends Activity> long perform(Class<T> activityClass, T activity, HashMap attributes) throws UnsupportedOperationException { 250 if (missionContributionCalculators.containsKey(activityClass)) { 251 return missionContributionCalculators.get(activityClass).perform(activity, attributes); 252 } else { 253 throw new UnsupportedOperationException("unsupported operation perform on " + this.getClass() 254 .getSimpleName() 255 + " with parameter of type " 256 + activityClass.getSimpleName()); 257 } 258 } 259 260 /** 261 * @return Returns the timeLineColor. 262 */ 263 public Color getTimeLineColor() { 264 return timeLineColor; 265 } 266 267 /** 268 * @param timeLineColor 269 * The timeLineColor to set. 270 */ 271 public void setTimeLineColor(Color timeLineColor) { 272 this.timeLineColor = timeLineColor; 273 } 274 275 /** 276 * @return Returns the children. 277 */ 278 public ArrayList<Agent> getChildren() { 279 return children; 280 } 281 282 /** 283 * @return Returns the parent. 284 */ 285 public Agent getParent() { 286 return parent; 287 } 288 289 /** 290 * @return Returns the panel. 291 */ 292 public JPanel getPanel() { 293 if (panel == null) { 294 initPanel(); 295 } 296 return panel; 297 } 298 299 public void addAgentChangedListener(AgentParameter parameter) { 300 agentChangedListeners.add(parameter); 301 } 302 303 public void removeAgentChangedListener(AgentParameter parameter) { 304 agentChangedListeners.remove(parameter); 305 } 306 307 /** 308 * Notify AgentParameters containing this agent that it has changed some property 309 * 310 */ 311 public void notifyAgentChangedListeners() { 312 for (AgentChangedListener listener : agentChangedListeners) { 313 listener.agentChanged(this); 314 } 315 } 316 317 }