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    }