diff options
author | Jonas Ruettimann | 2011-07-01 13:00:31 +0000 |
---|---|---|
committer | Jonas Ruettimann | 2011-07-01 13:00:31 +0000 |
commit | 4e31671673053361a81c071fe744f38da4139e78 (patch) | |
tree | e7359b78fb323f69847d7e92b59f945788aebc6f | |
parent | 66d542ae506d202d4eb7df2c96a3610e066f66ef (diff) | |
download | org.eclipse.amp-4e31671673053361a81c071fe744f38da4139e78.tar.gz org.eclipse.amp-4e31671673053361a81c071fe744f38da4139e78.tar.xz org.eclipse.amp-4e31671673053361a81c071fe744f38da4139e78.zip |
Bug 350813
39 files changed, 7168 insertions, 6345 deletions
diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/Scape.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/Scape.java index 6515a5ec..59942bd5 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/Scape.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/Scape.java @@ -146,10 +146,9 @@ import org.ascape.util.vis.PlatformDrawFeature; * scape execution. Normally you don't need to worry about controls as they are * managed automatically by the framework. * - * Scapes can automatically - * collect statistics on their members. (See StatCollectorCSA documentation for - * a description of how stats are created.) Here is an example of how this might - * be done: + * Scapes can automatically collect statistics on their members. (See + * StatCollectorCSA documentation for a description of how stats are created.) + * Here is an example of how this might be done: * * <pre> * agents.addStatCollectors({new StatCollectorCSA() { @@ -200,15 +199,15 @@ import org.ascape.util.vis.PlatformDrawFeature; * * Scapes are initialized and iterated hierarchically. The initialization and * iteration process are rules, and their behavior is well defined. For example, - * if you use the default list space for your root scape, scapes will be initialized and - * iterated in the order in which they were added to the root. Of course, this - * is an imporant consideration whenever there are dependencies between scapes. - * On initialization, each scape first instantiates all of its members, and then - * initializes them. After initialization, statistics are gathered, and views - * are requested to update. Then, each rule is executed on its scape, statistics - * are gathered, and views are requested to update. This continues until a - * control event stops or pauses the model, or the iteration limit provided with - * <code>setAutoStopAt</code> is reached.<BR> + * if you use the default list space for your root scape, scapes will be + * initialized and iterated in the order in which they were added to the root. + * Of course, this is an imporant consideration whenever there are dependencies + * between scapes. On initialization, each scape first instantiates all of its + * members, and then initializes them. After initialization, statistics are + * gathered, and views are requested to update. Then, each rule is executed on + * its scape, statistics are gathered, and views are requested to update. This + * continues until a control event stops or pauses the model, or the iteration + * limit provided with <code>setAutoStopAt</code> is reached.<BR> * <BR> * As stated, all scapes classes may act as a complete ascape model application. * Using the construct methods, basic views and services are automatically added @@ -303,3391 +302,3483 @@ import org.ascape.util.vis.PlatformDrawFeature; */ public class Scape extends CellOccupant implements SpaceContext, Collection, ControlListener, ScapeListener { - /** - * Increment this constant if you modify this class in a way that makes it - * incompatible with the previous version, from a serialization standpoint. - * Note that adding or removing serialized fields does not necessarily cause - * incompatibility. - */ - static final long serialVersionUID = 7686992038072599524L; - - /** - * The current version of the Ascape framework as a whole. Returns "3.0" We - * keep this in Scape since it is typically the invoked class. - */ - public final static String version = "3.1"; - - /** - * Copyright and credits information for ascape w/ HTML style tags. Again, - * we keep this in Scape since it is typically the invoked class. - */ - public final static String copyrightAndCredits = - "<B>Ascape version " - + version - + "</B><BR></BR>" - + "Portions copyright 2000-2007, NuTech Solutions, Inc.<BR></BR>" - + - // "http://ascape.nutechsolutions.com<BR></BR>" - // + - "Portions copyright 1998-2000, The Brookings Institution<BR></BR>" - + "JFreeChart Copyright (c) 2000, 2001, Simba Management Limited and others. (See licence-LGPL.txt)<BR></BR>" - + "Icons (C)1998 Dean S. Jones<BR></BR>" - + "Use subject to license agreement - see License.txt.<BR></BR>" - + "Government Users -- Commercial Software subject to 48 C.F.R. ß12.212 or 48 C.F.R. ßß227.7202-1 through 227.7202-4."; - - /** - * A rule causing the target scape and all its children scapes to be - * populated if auto create is set to true. - */ - public static final Rule CREATE_RULE = new PropogateScapeOnly("Create Scape") { - /** + /** + * Increment this constant if you modify this class in a way that makes it + * incompatible with the previous version, from a serialization standpoint. + * Note that adding or removing serialized fields does not necessarily cause + * incompatibility. + */ + static final long serialVersionUID = 7686992038072599524L; + + /** + * The current version of the Ascape framework as a whole. Returns "3.0" We + * keep this in Scape since it is typically the invoked class. + */ + public final static String version = "3.1"; + + /** + * Copyright and credits information for ascape w/ HTML style tags. Again, we + * keep this in Scape since it is typically the invoked class. + */ + public final static String copyrightAndCredits = "<B>Ascape version " + version + "</B><BR></BR>" + "Portions copyright 2000-2007, NuTech Solutions, Inc.<BR></BR>" + + // "http://ascape.nutechsolutions.com<BR></BR>" + // + + "Portions copyright 1998-2000, The Brookings Institution<BR></BR>" + "JFreeChart Copyright (c) 2000, 2001, Simba Management Limited and others. (See licence-LGPL.txt)<BR></BR>" + "Icons (C)1998 Dean S. Jones<BR></BR>" + "Use subject to license agreement - see License.txt.<BR></BR>" + "Government Users -- Commercial Software subject to 48 C.F.R. ß12.212 or 48 C.F.R. ßß227.7202-1 through 227.7202-4."; + + /** + * A rule causing the target scape and all its children scapes to be populated + * if auto create is set to true. + */ + public static final Rule CREATE_RULE = new PropogateScapeOnly("Create Scape") { + /** * */ - private static final long serialVersionUID = 1L; - - /** - * populates the scape and all its children. - * - * @param agent the playing agent - */ - public void execute(Agent agent) { - if (((Scape) agent).isAutoCreate()) { - ((Scape) agent).createScape(); - } - /* - * if (((Scape) agent).getCustomizer() != null) { ((Scape) - * agent).getCustomizer().build(); } - */ - super.execute(agent); - } - - public boolean isIterateAll() { - return true; - } - }; - - /** - * A rule causing viwews to be created for scape and all subscapes. Creates - * views for the resulting scapes. - */ - public static final Rule CREATE_VIEW_RULE = new PropogateScapeOnly("Create Views") { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Creates views for all of the scapes. - * - * @param agent the playing agent - */ - public void execute(Agent agent) { - ((Scape) agent).createViews(); - super.execute(agent); - } - - public boolean isIterateAll() { - return true; - } - }; - - /** - * A rule causing graphic views to be created for scape and all subscapes. - */ - public static final Rule CREATE_GRAPHIC_VIEW_RULE = new PropogateScapeOnly("Create Graphic Views") { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Creates views for all of the scapes. - * - * @param agent the playing agent - */ - public void execute(Agent agent) { - ((Scape) agent).createGraphicViews(); - super.execute(agent); - } - - public boolean isIterateAll() { - return true; - } - }; - - /** - * A rule causing the target scape to be populated. - */ - public static final Rule CREATE_SCAPE_RULE = new Rule("Create Scape") { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * populates the scape and all its children. - * - * @param agent the playing agent - */ - public void execute(Agent agent) { - ((Scape) agent).createScape(); - } - - public boolean isIterateAll() { - return true; - } - }; - - /** - * A rule causing the targets initial rules to be executed on its members. - */ - public static final Rule INITIAL_RULES_RULE = new PropogateScapeOnly("Initial Rules") { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Initializes the scape and all its children. + private static final long serialVersionUID = 1L; + + /** + * populates the scape and all its children. + * + * @param agent + * the playing agent + */ + public void execute(Agent agent) { + if (((Scape) agent).isAutoCreate()) { + ((Scape) agent).createScape(); + } + /* + * if (((Scape) agent).getCustomizer() != null) { ((Scape) + * agent).getCustomizer().build(); } + */ + super.execute(agent); + } + + public boolean isIterateAll() { + return true; + } + }; + + /** + * A rule causing viwews to be created for scape and all subscapes. Creates + * views for the resulting scapes. + */ + public static final Rule CREATE_VIEW_RULE = new PropogateScapeOnly("Create Views") { + /** * - * @param agent the playing agent - * @see Scape#getInitialRules() */ - public void execute(Agent agent) { - super.execute(agent); - int iterations = 0; - int style = 0; - if (((Scape) agent).getSpace() instanceof Discrete) { - iterations = ((Scape) agent).getAgentsPerIteration(); - style = ((Scape) agent).getExecutionStyle(); - ((Scape) agent).setExecutionStyle(Scape.COMPLETE_TOUR); - ((Scape) agent).setAgentsPerIteration(Scape.ALL_AGENTS); - } - ((Scape) agent).executeOnMembers(((Scape) agent).getInitialRules()); - if (((Scape) agent).getSpace() instanceof Discrete) { - ((Scape) agent).setAgentsPerIteration(iterations); - ((Scape) agent).setExecutionStyle(style); - } - } - - public boolean isIterateAll() { - return true; - } - }; - - /** - * A rule causing all children and members that are scapes to iterate. - * Executes all rules that have been added to each scape, increments the - * scape counter, and collectes and stores statistics for any scapes with a - * statistics collection rule. - */ - public final static Rule EXECUTE_RULES_RULE = new PropogateScapeOnly("Iterate Scape") { - /** + private static final long serialVersionUID = 1L; + + /** + * Creates views for all of the scapes. + * + * @param agent + * the playing agent + */ + public void execute(Agent agent) { + ((Scape) agent).createViews(); + super.execute(agent); + } + + public boolean isIterateAll() { + return true; + } + }; + + /** + * A rule causing graphic views to be created for scape and all subscapes. + */ + public static final Rule CREATE_GRAPHIC_VIEW_RULE = new PropogateScapeOnly("Create Graphic Views") { + /** * */ - private static final long serialVersionUID = 1L; - - /** - * Iterate over every member and child. + private static final long serialVersionUID = 1L; + + /** + * Creates views for all of the scapes. + * + * @param agent + * the playing agent + */ + public void execute(Agent agent) { + ((Scape) agent).createGraphicViews(); + super.execute(agent); + } + + public boolean isIterateAll() { + return true; + } + }; + + /** + * A rule causing the target scape to be populated. + */ + public static final Rule CREATE_SCAPE_RULE = new Rule("Create Scape") { + /** * - * @param agent the target agent */ - public void execute(Agent agent) { - ((Scape) agent).executeOnMembers(); - super.execute(agent); - } - }; - - /** - * A rule causing all children and members that are scapes to iterate. - * Executes all rules that have been added to each scape, increments the - * scape counter, and collectes and stores statistics for any scapes with a - * statistics collection rule. - */ - public final static Rule CLEAR_STATS_RULE = new PropogateScapeOnly("Iterate Scape") { - /** + private static final long serialVersionUID = 1L; + + /** + * populates the scape and all its children. + * + * @param agent + * the playing agent + */ + public void execute(Agent agent) { + ((Scape) agent).createScape(); + } + + public boolean isIterateAll() { + return true; + } + }; + + /** + * A rule causing the targets initial rules to be executed on its members. + */ + public static final Rule INITIAL_RULES_RULE = new PropogateScapeOnly("Initial Rules") { + /** * */ - private static final long serialVersionUID = 1L; - - /** - * Iterate over every member and child. + private static final long serialVersionUID = 1L; + + /** + * Initializes the scape and all its children. + * + * @param agent + * the playing agent + * @see Scape#getInitialRules() + */ + public void execute(Agent agent) { + super.execute(agent); + int iterations = 0; + int style = 0; + if (((Scape) agent).getSpace() instanceof Discrete) { + iterations = ((Scape) agent).getAgentsPerIteration(); + style = ((Scape) agent).getExecutionStyle(); + ((Scape) agent).setExecutionStyle(Scape.COMPLETE_TOUR); + ((Scape) agent).setAgentsPerIteration(Scape.ALL_AGENTS); + } + ((Scape) agent).executeOnMembers(((Scape) agent).getInitialRules()); + if (((Scape) agent).getSpace() instanceof Discrete) { + ((Scape) agent).setAgentsPerIteration(iterations); + ((Scape) agent).setExecutionStyle(style); + } + } + + public boolean isIterateAll() { + return true; + } + }; + + /** + * A rule causing all children and members that are scapes to iterate. + * Executes all rules that have been added to each scape, increments the scape + * counter, and collectes and stores statistics for any scapes with a + * statistics collection rule. + */ + public final static Rule EXECUTE_RULES_RULE = new PropogateScapeOnly("Iterate Scape") { + /** * - * @param agent the target agent */ - public void execute(Agent agent) { - CollectStats collectStats = ((Scape) agent).getCollectStats(); - if (collectStats != null) { - collectStats.clear(); - } - super.execute(agent); - } - }; - - /** - * A rule causing all children and members that are scapes to iterate. - * Executes all rules that have been added to each scape, increments the - * scape counter, and collectes and stores statistics for any scapes with a - * statistics collection rule. - */ - public final static Rule COLLECT_STATS_RULE = new PropogateScapeOnly("Iterate Scape") { - /** + private static final long serialVersionUID = 1L; + + /** + * Iterate over every member and child. + * + * @param agent + * the target agent + */ + public void execute(Agent agent) { + ((Scape) agent).executeOnMembers(); + super.execute(agent); + } + }; + + /** + * A rule causing all children and members that are scapes to iterate. + * Executes all rules that have been added to each scape, increments the scape + * counter, and collectes and stores statistics for any scapes with a + * statistics collection rule. + */ + public final static Rule CLEAR_STATS_RULE = new PropogateScapeOnly("Iterate Scape") { + /** * */ - private static final long serialVersionUID = 1L; - - /** - * Iterate over every member and child. + private static final long serialVersionUID = 1L; + + /** + * Iterate over every member and child. + * + * @param agent + * the target agent + */ + public void execute(Agent agent) { + CollectStats collectStats = ((Scape) agent).getCollectStats(); + if (collectStats != null) { + collectStats.clear(); + } + super.execute(agent); + } + }; + + /** + * A rule causing all children and members that are scapes to iterate. + * Executes all rules that have been added to each scape, increments the scape + * counter, and collectes and stores statistics for any scapes with a + * statistics collection rule. + */ + public final static Rule COLLECT_STATS_RULE = new PropogateScapeOnly("Iterate Scape") { + /** * - * @param agent the target agent */ - public void execute(Agent agent) { - CollectStats collectStats = ((Scape) agent).getCollectStats(); - // Special case for value collection rule - if (collectStats != null) { - /* - * int iterations = ScapeDiscrete.ALL_AGENTS; if (agent - * instanceof ScapeDiscrete) { iterations = ((ScapeDiscrete) - * agent).getAgentsPerIteration(); ((ScapeDiscrete) - * agent).setAgentsPerIteration(ScapeDiscrete.ALL_AGENTS); } - */ - collectStats.setPhase(1); - ((Scape) agent).executeOnMembers(collectStats); - collectStats.setPhase(2); - ((Scape) agent).executeOnMembers(collectStats); - /* - * if (agent instanceof ScapeDiscrete) { ((ScapeDiscrete) - * agent).setAgentsPerIteration(iterations); } - */ - } - super.execute(agent); - if (collectStats != null) { - collectStats.calculateValues(); - } - if (((Scape) agent).isRoot() && ((Scape) agent).getRunner().getData() != null) { - ((Scape) agent).getRunner().getData().update(); - // System.gc(); - } - } - }; - - /** - * The symbol to execute rules against all agents in each iteration. - */ - public final static int ALL_AGENTS = -1; - - /** - * Symbol for by agent execution order. - */ - public final static int AGENT_ORDER = -2; - - /** - * Symbol for by rule execution order. - */ - public final static int RULE_ORDER = -1; - - /** - * Symbol for complete tour excution style. - */ - public final static int COMPLETE_TOUR = 1; - - /** - * Symbol for repeated random draw execution style. - */ - public final static int REPEATED_DRAW = 2; - - /** - * Manages time, model-wide views and other features that are shared between - * scapes. There is one an only one for each model instance. - */ - private Runner runner; - - private Space space; - - /** - * An agent which which may be cloned to produce members of this collection. - * By default, all scapes which have a known number of members are - * initialized with clones of this agent. - */ - protected Agent prototypeAgent; - - /** - * The rules that this scape will execute on its memebers. - */ - private VectorSelection rules = new VectorSelection(new Vector()); - - /** - * The rules that this scape will execute on its members upon initializtion. - */ - protected VectorSelection initialRules = new VectorSelection(new Vector()); - - /** - * The observers of this scape. All listeners are notified when the scape is - * updated and given a chance to update themselves. - */ - private ArrayList scapeListeners = new ArrayList(); - - /** - * The number of agents to execute each rule across for each iteration. - */ - protected int agentsPerIteration = ALL_AGENTS; - - /** - * Order in which rules should be executed. - */ - private int executionOrder = AGENT_ORDER; - - /** - * 'Stlye' of rule execution. - */ - private int executionStyle = COMPLETE_TOUR; - - /** - * Should members of the scape be iterated against? - */ - private boolean membersActive = true; - - /** - * Should members of the scape be automatically created at startup? - */ - private boolean autoCreate = true; - - /** - * Should the scape be populated on creation? - */ - private boolean populateOnCreate = true; - - /** - * Should cells indicate that they need to be updated manually (imroving - * performance significantly) or should all cells be updated every - * iteration. - */ - private boolean cellsRequestUpdates = false; - - /** - * The value collection rule for this scape. Null if no values should be - * collected. - */ - private CollectStats collectStats = null; - - /** - * A view of the scape that delegates back to the scape, often null. - * Automatically created for root when standard model is used. - */ - private ScapeListener selfView; - - /** - * A vector of features available to draw memebers of this scape. - */ - private Vector drawFeatures = new Vector(); - - /** - * Are all the listeners and members (which may have listeners) of this - * scape current? (If so, we can continue updating, otherwise, we must wait. - */ - private boolean listenersAndMembersCurrent = false; - - /** - * Determines how many time steps pass between updating displayed charts and - * graphs. Higher numbers result in faster performance but less frequent - * updates. This setting does not affect model results. - */ - private int iterationsPerRedraw = 1; - - /** - * Count of the number of listeners that have been updated. Used to - * determine when all listeners have been updated. - */ - private int updatedListeners = 0; - - /** - * Count of the number of members that have been updated. Used to determine - * when all members have been updated. - */ - private int updatedMembers = 0; - - private boolean serializable = true; - - /** - * Constructs a scape with default list topology. - */ - public Scape() { - this(new ListSpace()); - } - - /** - * Constructs a scape. - * - * @param space the topology for this scape - */ - public Scape(CollectionSpace space) { - this(space, null, null); - } - - /** - * Constructs a scape of provided geometry, to be populated with clones of - * provided agent. - * - * @param name a descriptive name for the scape - * @param prototypeAgent the agent whose clones will be used to populate - * this scape - */ - public Scape(String name, Agent prototypeAgent) { - this(null, name, prototypeAgent); - } - - /** - * Constructs a scape of provided geometry, to be populated with clones of - * provided agent. - * - * @param name a descriptive name for the scape - * @param prototypeAgent the agent whose clones will be used to populate - * this scape - * @param space the topology for this scape - */ - public Scape(CollectionSpace space, String name, Agent prototypeAgent) { - super(); - setSpace(space); - setName(name); - setPrototypeAgent(prototypeAgent); - scapeListeners = new ArrayList(); - listenersAndMembersCurrent = true; - } - - /** - * Returns the size, or number of agents, of this Scape. - */ - public int getSize() { - return space.getSize(); - // return vector.size(); - } - - /** - * Sets the prototype agent, the agent that, in default implementations, - * will be cloned to populate this scape. It is an error to call while the - * scape is running. - * - * @param prototypeAgent the agent whose clones will populate this scape - */ - public void setPrototypeAgent(Agent prototypeAgent) { - this.prototypeAgent = prototypeAgent; - if (prototypeAgent != null && prototypeAgent.getScape() == null) { - prototypeAgent.setScape(this); - prototypeAgent.scapeCreated(); - } - setInitialized(false); - } - - /** - * Returns the agent that is cloned to populate this scape. - */ - public Agent getPrototypeAgent() { - return prototypeAgent; - } - - /** - * Returns the number of agents to iterate through each iteration cycle. - * *@param returns the number of iterations per cycle - */ - public int getAgentsPerIteration() { - return agentsPerIteration; - } - - /** - * Sets the number of agents to iterate through each iteration cycle. By - * default, set to iterate through all agents. *@param agentsPerIteration - * the number of agents to iterate against per cycle, ALL_AGENTS for all - * agents - */ - public void setAgentsPerIteration(int agentsPerIteration) { - this.agentsPerIteration = agentsPerIteration; - } - - /** - * Returns the number of iterations to perform before updating views. - */ - public int getIterationsPerRedraw() { - return iterationsPerRedraw; - } - - /** - * Sets the number of iterations to perform before updating views, and - * propagates this setting to all scapes and views in the model. - */ - public void setIterationsPerRedraw(int iterationsPerRedraw) { - setIterationsPerRedraw(iterationsPerRedraw, true); - } - - /** - * Sets the number of iterations to perform before updating views, and - * optionally propagates this setting to all scapes and views in the model. - */ - public void setIterationsPerRedraw(int iterationsPerRedraw, boolean propagate) { - this.iterationsPerRedraw = iterationsPerRedraw; - if (propagate) { - executeOnRoot(new NotifyViews(ScapeEvent.REQUEST_CHANGE_ITERATIONS_PER_REDRAW) { - /** - * - */ - private static final long serialVersionUID = 1L; - - public void execute(Agent agent) { - ((Scape) agent).setIterationsPerRedraw(agent.getRoot().getIterationsPerRedraw(), false); - super.execute(agent); - } - }); - } - } - - /** - * Returns the execution order that has been set for this scape. - */ - public int getExecutionOrder() { - return executionOrder; - } - - /** - * Sets the order of rule execution for this scape. If 'rule order', each - * rule is executed on every agent in turn. If 'agent order', every rule is - * executed on each agent in turn. Execution order can be profoundly - * significant to a model's dynamics. For 'synchrounous' style rules that - * subclass ExecuteAndUpdate, 'by rule' execution is the only order that - * makes sense. - * - * @param symbol RULE_ORDER for by rule execution, AGENT_ORDER for by agent - * execution - */ - public void setExecutionOrder(int symbol) { - this.executionOrder = symbol; - } - - /** - * Returns the execution style that has been set for this scape. - */ - public int getExecutionStyle() { - return executionStyle; - } - - /** - * Sets the style that rules will be executed upon this scape. If complete - * tour, every agent is visited once and only once (assuming agents per - * iteration is set to 'all agents'.) If repeated draw, a random agent is - * picked n times for execution. (Actually, if execution order is by agent, - * each rule is then executed upon the picked agent, so that there are n - * total draws. But if execution order is by rule, then for each rule, a - * random agent is picked, which means that there are actually (rules X n) - * draws. In practice, this combination does not seem to make much sense in - * any case.) A complete tour style of execution seems generally more - * plausible, but a repeated draw approach can produce different and - * interesting results. - * - * @param symbol one of COMPLETE_TOUR or REPEATED_DRAW - */ - public void setExecutionStyle(int symbol) { - this.executionStyle = symbol; - } - - /** - * Returns the extent of the scape. The extent can be thought of as the most - * extreme point in the scape. For discrete scape's this will simply be the - * furthest cell, so that for a 20x20 grid, the extent would be {20, 20}. - * For continuous spaces it will be the maximum boundary of the space. For - * lists, it will be the size of lists. Therefore, this method should net be - * confused with the scape's "size". Note that scape graphs will not have - * useful extents, but all other scapes do. - */ - public Coordinate getExtent() { - return space.getExtent(); - } - - /** - * Sets the size of the scape. Note that scape graphs will not have useful - * extents, but all other scapes do. It is an error to set extent while a - * scape is running. - * - * @param extent a coordinate at the maximum extent - */ - public void setExtent(Coordinate extent) { - if (getRunner() != null && getRunner().isRunning()) { - throw new RuntimeException("Tried to modfiy extent while scape was running"); - } - space.setExtent(extent); - } - - /** - * Sets the size of the scape. Note that scape graphs will not have useful - * extents, but all other scapes do. It is an error to set extent while a - * scape is running. - * - * @throws RuntimeException if the scape is currently running - * @param xval coordinate 1 of the extent - */ - public void setExtent(int xval) { - if (runner != null && runner.isRunning()) { - throw new RuntimeException("Tried to modfiy extent while scape was running"); - } - if (getSpace().getGeometry().getDimensionCount() != 1) { - throw new RuntimeException("Tried to set extent as 1-dimension for a scape that isn't 1-dimensional."); - } - ((CollectionSpace) getSpace()).setExtent(xval); - } - - /** - * Sets the size of the scape. Note that scape graphs will not have useful - * extents, but all other scapes do. It is an error to set extent while a - * scape is running. - * - * @param xval coordinate 1 of the extent - * @param yval coordinate 2 of the extent - * @throws RuntimeException if the scape is currently running - * @throws UnsupportedOperationException if the underlying space isn't - * appropriate - */ - public void setExtent(int xval, int yval) { - if (runner != null && runner.isRunning()) { - throw new RuntimeException("Tried to modfiy extent while scape was running"); - } - try { - ((Array2DBase) getSpace()).setExtent(xval, yval); - } catch (ClassCastException e) { - throw new UnsupportedOperationException("Can't set extent as x, y; underlying scape doesn't support it."); - } - } - - /** - * Returns the name of this scape, the model name if this is root and there - * is no name set. - */ - public String getName() { - if (name != null) { - return name; - } else { - return getClass().getName(); - } - } - - private void loadDescriptions() { - if (getRunner().getDescription() == null || getRunner().getHTMLDescription() == null) { - String fileName = "About" + Utility.getClassNameOnly(this.getClass()) + ".html"; - URL aboutFile = this.getClass().getResource(fileName); - if (aboutFile != null) { - getRunner().setDescription(""); - try { - BufferedInputStream is = (BufferedInputStream) aboutFile.getContent(); - BufferedReader ir = new BufferedReader(new InputStreamReader(is)); - String nextLine = ir.readLine(); - // Clean out html tags - StringBuffer htmlFragBuffer = new StringBuffer(); - while (nextLine != null) { - htmlFragBuffer.append(nextLine); - nextLine = ir.readLine(); - } - StringBuffer plainTextBuffer = new StringBuffer(); - // Neccessary to support 1.3 - String htmlString = htmlFragBuffer.toString(); - boolean done = false; - int pos = 0; - while (!done) { - int anglePos = htmlString.indexOf("<", pos); - if (anglePos >= 0) { - plainTextBuffer.append(htmlFragBuffer.substring(pos, anglePos)); - if (htmlFragBuffer.substring(anglePos, anglePos + 4).equalsIgnoreCase("<BR>")) { - plainTextBuffer.append("\n"); - } - pos = htmlString.indexOf(">", anglePos) + 1; - } else { - plainTextBuffer.append(htmlFragBuffer.substring(pos, htmlFragBuffer.length())); - done = true; - } - } - setHTMLDescription(htmlFragBuffer.toString()); - setDescription(plainTextBuffer.toString()); - } catch (java.io.IOException e) { - // Probably file has not been defined; in any case, just use - // toString value.. - getEnvironment().getConsole().println("Non-critical exception: couldn't read \"About\" file: " + e); - getRunner().setDescription(toString()); - } - } else { - // No about file defined - getRunner().setDescription(toString()); - } - } - } - - /** - * Returns a long (paragraph length suggested) description of the scape. The - * root scape should describe the model as a whole; subscapes should - * describe themselves. Plantext. If no description is provided, return the - * standar toString() description. This description is automatically loaded - * from a file called "About[ModelClassName].html" located in the the same - * directory as the .class file for the model, if such a file exists. To use - * this feature simply create such a file and place it in the appropriate - * directory. You can include any normal html style tags in this file, they - * will be stripped from the non-html description. - */ - public String getDescription() { - try { - loadDescriptions(); - return getRunner().getDescription(); - } catch (ClassCastException cce) { - return ""; - } - } - - /** - * Returns a long (paragraph length suggested) description of the scape. The - * root scape should describe the model as a whole; subscapes should - * describe themselves. If no description is provided, return the standar - * toString() description. - */ - public void setDescription(String description) { - getRunner().setDescription(description); - } - - /** - * Returns a long (paragraph length suggested) description of the scape. The - * root scape should describe the model as a whole; subscapes should - * describe themselves. Includes html tags as appropriate. This description - * is automatically loaded from a file called "About[ModelClassName].html" - * located in the the same directory as the .class file for the model, if - * such a file exists. To use this feature simply create such a file and - * place it in the appropriate directory. You can include any normal html - * style tags in this file, they will be stripped from the non-html - * description. If no description is provided, return the standar toString() - * description. - */ - public String getHTMLDescription() { - loadDescriptions(); - return getRunner().getHTMLDescription(); - } - - /** - * Returns a long (paragraph length suggested) description of the scape. The - * root scape should describe the model as a whole; subscapes should - * describe themselves. Should include html tags as appropriate. If no - * description is provided, return the standar toString() description. - */ - public void setHTMLDescription(String description) { - getRunner().setHTMLDescription(description); - } - - /** - * Returns the root of this scape, which may be this scape. - */ - public final Scape getRoot() { - if (scape == null) { - return this; - } else { - return scape.getRoot(); - } - } - - /** - * Is this scape the root within its entire simulation context? That is, - * does this root not have any parent scapes? - */ - public boolean isRoot() { - return scape == null; - } - - /** - * Has a view update been requested for this cell? - */ - public boolean isUpdateNeeded() { - return isUpdateNeeded(getIterationsPerRedraw()); - } - - /** - * Contructs the basic scape structure. Instantiates the agents, but does - * not populate them. It is not neccesary to set extent before initializing - * a collection, unless you want to have a populated vecotr to begin with. - */ - public void construct() { - space.construct(); - if (getSpace() instanceof Discrete && getPrototypeAgent() == null) { - setPrototypeAgent(new Cell()); - } - } - - /** - * Populates the scape with clones of the prototype agent. Prototype agent - * should be set before calling this method. - */ - public void populate() { - space.populate(); - } - - /** - * Create this scape; contruct it, populate it, add rules, create statistic - * collectors, etc. Called automatically at model construction, unless - * isAutoCreate is set to false. By default, automatically populates all - * members with clones of the prototype agent. Of course, this behavior can - * be overridden or embellished. To turn off the populate behavior, set - * populate on create to false. - * - * @see #setPopulateOnCreate - */ - public void createScape() { - // We use Scape as a prototype simply because Scape is abstract and so - // can't be - // instantiated. Any scape will do, since we won't be cloning it to - // populate. - if (isRoot()) { - if (getPrototypeAgent() == null) { - setPrototypeAgent(new Scape()); - } - if (getRunner() == null) { - new NonGraphicRunner().setRootScape(this); - } - } - construct(); - if (isPopulateOnCreate()) { - populate(); - } - } - - /** - * Initializes the state of the scape. This is the appropriate place to put - * any initialization that is dependent on the state of other ascape - * objects. If autoCreate is true, calls createScape() on the scape. Note - * that for root scapes, autoCreate is always false. Objects are initialized - * in the order they are added to parent scapes. - */ - public void initialize() { - if (isRoot()) { - setAutoCreate(false); - } - if (isAutoCreate()) { - createScape(); - } - super.initialize(); - getSpace().initialize(); - if (!(getSpace() instanceof Continuous) && getPrototypeAgent() != null - && getPrototypeAgent().getScape() == this && !getSpace().isMutable()) { - executeOnMembers(Cell.CALCULATE_NEIGHBORS_RULE); - } - } - - /** - * Returns current count of iterations. - */ - public final int getIteration() { - return getRunner().getIteration(); - } - - /** - * Returns the current period, which is just the iteration plus the period - * begin. - */ - public final int getPeriod() { - return getRunner().getPeriod(); - } - - /** - * Returns the name that periods are referred to by. - */ - public String getPeriodName() { - return getRunner().getPeriodName(); - } - - /** - * Returns a string description of the current period, i.e. "Iteration 1", - * "Year 1900", "StarDate 3465.29." - */ - public String getPeriodDescription() { - return getPeriodName() + " " + Integer.toString(getPeriod()); - } - - /** - * Sets the name that periods are referred to by. - */ - public void setPeriodName(String name) { - getRunner().setPeriodName(name); - } - - /** - * Adds a rule to this scape, automatically selecting it. - */ - public synchronized void addRule(Rule rule) { - addRule(rule, true); - } - - /** - * Adds a rule to this scape. Allows setting whether rule be should be - * selected (run) automatically? If the selection is not changed, rules are - * executed in the order they are added. - * - * @param rule the rule to add - * @param select if rule should be run false if rule should just be made - * available to be run - */ - public synchronized void addRule(Rule rule, boolean select) { - if (rule instanceof ExecuteThenUpdate && getExecutionOrder() != RULE_ORDER) { - // bug mtp: need to add similar check when setting rule order. - throw new RuntimeException( - "Tried to add execute and update rule to AGENT_ORDER Scape. Set Scape to RULE_ORDER execution first."); - } - if (rule.getScape() == null) { - rule.setScape(this); - } - rules.addElement(rule, select); - } - - /** - * Returns all rules that this scape might execute. - */ - public VectorSelection getRules() { - return rules; - } - - /** - * Adds a rule to be executed once following initialization. Rule is - * automatically selected for running. - * - * @param rule to be executed at simulation start - */ - public synchronized void addInitialRule(Rule rule) { - addInitialRule(rule, true); - } - - /** - * Adds a rule to be executed once following initialization. If the - * selection is not chagned, rules are executed in the order they are added. - * - * @param rule to be executed at simulation start - * @param select if rule should be run false if rule should just be made - * available to be run - */ - public synchronized void addInitialRule(Rule rule, boolean select) { - if (rule.getScape() == null) { - rule.setScape(this); - } - initialRules.addElement(rule, select); - } - - /** - * Returns all the rules executed following scape initialization. - */ - public VectorSelection getInitialRules() { - return initialRules; - } - - public void setInitialRules(VectorSelection initialRules) { - this.initialRules = initialRules; - } - - /** - * Adds a view to this scape. Takes care of basic housekeeping, including - * registering view as listener, and creating window for view to be - * displayed within. - * - * @param view ComponentView to display in window - */ - public synchronized void addView(ScapeListener view) { - this.addView(view, true); - } - - /** - * Adds a view to this scape. Takes care of basic housekeeping, including - * registering view as listener, and creating window for view to be - * displayed within. This version of the method allow the adding of a view - * without regard to the GUI setting. This method might be useful for - * instace when temporaily instrumenting a non-gui run with diagnostics. - * Normally addScapeListener should be used. - * - * @param view ComponentView to display in window - * @param createFrame should the view be placed within a new window frame? - * @param forceGUI add a GUI view witout regard to the display GUI setting - */ - public synchronized void addView(final ScapeListener view, boolean createFrame, boolean forceGUI) { - if (forceGUI || Runner.isDisplayGraphics() || !view.isGraphic() || Runner.isServeGraphics()) { - try { - this.addScapeListener(view); - // if (view instanceof Component) { - // // try { - // SwingUtilities.invokeLater(new Runnable() { - // public void run() { - // view.scapeNotification(new ScapeEvent(Scape.this, - // ScapeEvent.REPORT_ADDED)); - // } - // }); - // // } catch (InterruptedException e) { - // // e.printStackTrace(); - // // } catch (InvocationTargetException e) { - // // e.printStackTrace(); - // // } - // } else { - view.scapeAdded(new ScapeEvent(this, ScapeEvent.REPORT_ADDED)); - // } - } catch (TooManyListenersException e) { - throw new RuntimeException("Tried to add a view to more than one scape:\n" + e); - } - if (createFrame && view.isGraphic()) { - if (!Runner.isServeGraphics()) { - getEnvironment().addView(view); - } - // topdo remove view dependency - } - } else { - } - } - - /** - * Adds a view to this scape. Takes care of basic housekeeping, including - * registering view as listener, and creating window for view to be - * displayed within. An important exception occurs when a GUI view is added - * and display GUI is set to false. In this case, the view will _not_ be - * added. This makes it easy to add views in many model components without - * worrying about checking for GUI display state. Views can be added - * regardless of the value of display GUI by using addViewForce. Note: Even - * with the conveneince of this method, it is often nec - * - * @param view ComponentView to display in window - * @param createFrame should the view be placed within a new window frame? - */ - public synchronized void addView(ScapeListener view, boolean createFrame) { - addView(view, createFrame, false); - } - - /** - * Adds a view to this scape. Takes care of basic housekeeping, including - * registering view as listener, and creating window for view to be - * displayed within. - * - * @param views ComponentView to display in window - */ - public synchronized void addViews(ScapeListener[] views) { - this.addViews(views, true); - } - - /** - * Adds an array of views to this scape. Takes care of basic housekeeping, - * including registering the views as listeners, and creating window for - * view to be displayed within. This version of the method allow the adding - * of a view without regard to the GUI setting. This method might be useful - * for instace when temporaily instrumenting a non-gui run with diagnostics. - * Normally addScapeListener should be used. - * - * @param views ComponentViews array to display in window - * @param createFrame should the view be placed within a new window frame? - * @param forceGUI add a GUI view witout regard to the dispaly GUI setting - */ - public synchronized void addViews(ScapeListener[] views, boolean createFrame, boolean forceGUI) { - if (forceGUI || Runner.isDisplayGraphics() || !views[0].isGraphic()) { - try { - for (int i = 0; i < views.length; i++) { - this.addScapeListener(views[i]); - views[i].scapeAdded(new ScapeEvent(this, ScapeEvent.REPORT_ADDED)); - } - } catch (TooManyListenersException e) { - throw new RuntimeException("Tried to add a view to more than one scape"); - } - boolean allGraphic = false; - for (int i = 0; i < views.length; i++) { - if (views[i].isGraphic()) { - allGraphic = true; - } else { - allGraphic = false; - break; - } - } - // For now, we're going to assume that if one of the views is - // graphic, they all are - if (createFrame && allGraphic) { - getEnvironment().addViews(views); - } - } - } - - /** - * Adds an array of views to this scape. Takes care of basic housekeeping, - * including registering the views as listeners, and creating window for - * view to be displayed within. An important exception occurs when GUI views - * are added and display GUI is set to false. In this case, the views will - * _not_ be added. This makes it easy to add views in many model components - * without worrying about checking for GUI display state. Views can be added - * regardless of the value of dispaly GUI by using addViewForce. - * - * @param views ComponentViews to display in window - * @param createFrame should the view be placed within a new window frame? - */ - public synchronized void addViews(ScapeListener[] views, boolean createFrame) { - // To do, test and throw error for case where user tries to add mixed - // gui and non gui views, or the view array is empty - addViews(views, createFrame, false); - } - - /** - * Adds an observer to this scape. This observer will be notified when the - * scape has finished iterating, and is expected to notify this scape when - * it has updated itself. This method also adds the scape to the listener as - * a control listener. - * - * @param listener the listern to add - */ - public synchronized void addScapeListener(ScapeListener listener) { - if (listener == null) { - throw new RuntimeException("Tried to add a null listener to Scape."); - } - // Not sure if we want to make this poilicy or not.. - /* - * if (listener.getScape() == null) { throw new - * RuntimeException("Listener must have this scape assigned before - * calling addScapeListener."); } - */ - // change to array copy - scapeListeners.add(listener); - updatedListeners++; - listenerOrMemberUpdated(); - } - - /** - * Adds an observer to this scape. This version simple adds the new listener - * to the beginning of the list. This can be useful if there are listeners - * that need to be called first. - * - * @param listener the listern to add - */ - public synchronized void addScapeListenerFirst(ScapeListener listener) { - if (listener == null) { - throw new RuntimeException("Tried to add a null listener to Scape."); - } - scapeListeners.add(0, listener); - updatedListeners++; - listenerOrMemberUpdated(); - } - - /** - * Returns true if and only if the argument is an observer of this scape. - */ - public boolean isScapeListener(ScapeListener listener) { - return scapeListeners.contains(listener); - } - - /** - * Removes the observer from this scape. This observer will be notified when - * the scape has finished iterating, and is expected to notify this scape - * when it has updated itself. - */ - public synchronized void removeScapeListener(ScapeListener listener) { - boolean success = scapeListeners.remove(listener); - if (!success) { - getEnvironment().getConsole().println( - "WARNING: Tried to remove unregistered scape listener " + listener + " from scape " + this + "."); - } - - listener.scapeRemoved(new ScapeEvent(this, ScapeEvent.REPORT_REMOVED)); - // we may have just removed the last non-updated listener, - // so that everything needing an update has been updated; we need to - // check. - listenerOrMemberUpdated(); - } - - /** - * Returns all listeners for this scape. - */ - public ArrayList getScapeListeners() { - return scapeListeners; - } - - // todo (Tried using thread notification, but too slow...perhpas w/ - // pooling?? - // class NotificationThread extends Thread { - // private ScapeListener listener; - // private int id; - // - // public NotificationThread(ScapeListener listener, int id) { - // super(Scape.this + " Scape Notify " + listener); - // this.listener = listener; - // this.id = id; - // } - // - // public void run() { - // listener.scapeNotification(new ScapeEvent(Scape.this, id)); - // } - // } - - /** - * Notifies all scape listeners that this scapes state has changed. The root - * scape thread then waits until all listeners have been updated. - */ - public void notifyViews(final int id) { - notifyViews(new ScapeEvent(Scape.this, id)); - } - - /** - * Notifies all scape listeners that this scapes state has changed. The root - * scape thread then waits until all listeners have been updated. - */ - public void notifyViews(final ScapeEvent event) { - listenersAndMembersCurrent = false; - updatedListeners = 0; - updatedMembers = 0; - if (scapeListeners.size() > 0) { - ArrayList currentListeners = (ArrayList) scapeListeners.clone(); - for (Object listener : currentListeners) { - getRunner().notify(event, (ScapeListener) listener); - } - } else { - listenerOrMemberUpdated(); - } - } - - /** - * Have all views and views of memebers of this scape been updated? [The - * grammer is terrible, but it fits the text pattern!] - * - * @return boolean true if no views are still updating, false if not - */ - public final boolean isAllViewsUpdated() { - return listenersAndMembersCurrent; - } - - /** - * Called whenever a listener or member scape of this scape has been - * updated. If all listeners and members have been updated, informs parent - * scape. - */ - protected synchronized void listenerOrMemberUpdated() { - // For testing updating.. - // if ((updatedListeners >= scapeListeners.length) && - // ((!(getPrototypeAgent() instanceof Scape)) || (!(((Scape) - // getPrototypeAgent()).isMembersActive())) || (!(getPrototypeAgent() - // instanceof AgentScape)) || (updatedMembers >= getSize()))) { - if (updatedListeners >= scapeListeners.size() - && (updatedMembers >= getSize() || !(getPrototypeAgent() instanceof Scape) - || getSpace() instanceof Singleton || !((Scape) getPrototypeAgent()).isMembersActive())) { - listenersAndMembersCurrent = true; - updatedListeners = 0; - updatedMembers = 0; - if (scape != null) { - scape.memberUpdated(this); - // if (isInitialized() ) { - // getRuntimeEnvironment().getConsole().println(scape. - // updatedListeners - // + "/" + - // scape.scapeListeners.length + ", " + scape.updatedMembers + - // "/"+scape.getSize() + " for " + scape + " from " + this); - // } - } - } - } - - /** - * Called whenever a listener has been updated. - * - * @param listener the listener tha has been updated - */ - public synchronized void listenerUpdated(ScapeListener listener) { - updatedListeners++; - // Useful for testing updating problems. - // getRuntimeEnvironment().getConsole().println("L " + listener + ": " + - // updatedListeners +" of "+ scapeListeners.size()+" --- - // "+updatedMembers + " of - // " + getSize()); - listenerOrMemberUpdated(); - } - - /** - * Called whenever a member has been updated. - * - * @param member the member that has been updated - */ - public synchronized void memberUpdated(Scape member) { - updatedMembers++; - // Useful for testing updating problems. - // getRuntimeEnvironment().getConsole().println("M " + member + ": " + - // updatedListeners +" of "+ scapeListeners.size()+" --- - // "+updatedMembers + " of - // " + getSize()); - listenerOrMemberUpdated(); - } - - /** - * Responds to any control events fired at this scape. Currently reacts to - * start, stop, pause, resume, step, quit, and restart events, as well as - * listener update report events. All control events except listener updates - * are passed up to the root. Any other events trigger an untrapped - * exception. - */ - public void respondControl(ControlEvent control) { - if (control.getID() == ControlEvent.REPORT_LISTENER_UPDATED) { - listenerUpdated((ScapeListener) control.getSource()); - } else { - getRunner().respondControl(control); - } - } - - /** - * This is for grid communication of changes in draw feature. - * - * @param event - */ - public void respondDrawFeature(DrawFeatureEvent event) { - throw new RuntimeException("The client or worker scape should define their own version of this!"); - } - - /** - * Sets the running state for all scapes. Safe to call on any scape in the - * model; the request is propogated to the parent scape. If true, starts the - * parent scape's thread, which causes scape to iterate. If set false, the - * scape will be stopped when the current iteration is complete. - * - * @param running if true, starts the thread, if false, stops it. - */ - public void setRunning(boolean running) { - getRunner().setRunning(running); - } - - /** - * Has the scape been requested to run? <i>Note:</i> if false, indicates - * that a stop has been requested, not neccesarily that it has occured, as - * the simulation continues the current iteration. If you need to know when - * a scape has actually stopped, listen for the scapeStopped event. - * - * @return the current requested running state - */ - public boolean isRunning() { - return getRunner() != null && getRunner().isRunning(); - } - - /** - * Sets the paused state for all parent and member scapes. Safe to call on - * any scape in the model; the request is propogated up to the parent scape. - * If set true, a pause will occur when the current iteration is complete. - * - * @param pause if true, pauses, otherwise resumes iterations - */ - public void setPaused(boolean pause) { - if (pause) { - getRunner().pause(); - } else { - getRunner().resume(); - } - } - - /** - * Has the scape been requested to pause? <i>Note:</i> indicates that a - * pause has been requested, not neccesarily that the simulation is paused; - * it may be completing its current iteration. - * - * @return true if pause requested, false if resume requested or running - * normally - */ - public boolean isPaused() { - return getRunner().isPaused(); - } - - /** - * Sets the earliest period this scape is expected to be run at. 0 by - * default. - * - * @param earliestPeriod the lowest period value this scape can have - */ - public void setEarliestPeriod(int earliestPeriod) { - getRunner().setEarliestPeriod(earliestPeriod); - } - - /** - * Sets the latest period this scape is expected to be run at. Max of - * integer (effectively unlimited) by default. - * - * @param latestPeriod the highest period value this scape can have - */ - public void setLatestPeriod(int latestPeriod) { - getRunner().setLatestPeriod(latestPeriod); - } - - /** - * Is the supplied period a valid period for this scape? - * - * @param period the period to test - * @return true if within earliest and latest periods, false otherwise - */ - public boolean isValidPeriod(int period) { - return getRunner().isValidPeriod(period); - } - - /** - * Returns the period this scape begins running at. By default, the greater - * of earliest period and 0. - */ - public int getStartPeriod() { - return getRunner().getStartPeriod(); - } - - /** - * Sets the start period for this scape. The start period is the period this - * scape is given when a model run is started. - * - * @param startPeriod the period to begin runs at - */ - public void setStartPeriod(int startPeriod) throws SpatialTemporalException { - getRunner().setStartPeriod(startPeriod); - } - - /** - * Returns the period this scape stops running at. By default, the lesser of - * latest period and integer maximum value (effectively unlimited.) - */ - public int getStopPeriod() { - return getRunner().getStopPeriod(); - } - - /** - * Sets the stop period for this scape. The stop period is the period that - * the scape is automatically stopped at. The scape may be automatically set - * to start agina at start value is the scape is set to restart. - * - * @param stopPeriod the period the scape will stop at upon reaching - * @see #setAutoRestart - */ - public void setStopPeriod(int stopPeriod) throws SpatialTemporalException { - getRunner().setStopPeriod(stopPeriod); - } - - /** - * Returns the period to pause on. - */ - public int getPausePeriod() { - return getRunner().getPausePeriod(); - } - - /** - * Causes the model to pause at the specified period. - * - * @param pausePeriod when to pause - */ - public void setPausePeriod(int pausePeriod) { - getRunner().setPausePeriod(pausePeriod); - } - - /** - * Does the scape automatically start upon opening? True by default. - */ - public boolean isStartOnOpen() { - return Runner.isStartOnOpen(); - } - - /** - * Should the scape be automatically started upon opening? True by default. - * - * @param startOnOpen true to start the scape upon opening a model - */ - public void setStartOnOpen(boolean startOnOpen) { - Runner.setStartOnOpen(startOnOpen); - } - - /** - * Should the scape be automatically restarted upon stopping at its stop - * period? Setting this value to true allows easy cycling of models for - * demonstrations, model explorations, etc. See DataOutputView for an - * example of more sophisticated handling of multiple runs. - * - * @param autoRestart true to restart the scape upon reaching stop period, - * false to simple stop - * @see #setStopPeriod - */ - public void setAutoRestart(boolean autoRestart) { - getRunner().setAutoRestart(autoRestart); - } - - /** - * Returns the root of this scape, which may be this scape. This was an - * older usage and is retained for backward compatability only. It is now - * not a all good name as it confuses model for a view / control component, - * and may be removed in the future. - * - * @deprecated please use #getRunner(). - */ - public Runner getModel() { - return getRunner(); - } - - /** - * Returns the runtime model environment, which manages model-wide state - * such as run status. - * - * @return the model environment for entire model - */ - public Runner getRunner() { - return getRoot().runner; - } - - public void setRunner(Runner _runner) { - runner = _runner; - } - - /** - * Returns the path in which all files should by default be stored to and - * retrieved from. Nonstatic, so that parameter can automatically be set - * from command line, but backing variable is static. Default is "./", can - * be modified by calling setHome or providing an ascape.home java property. - * (This may change now since it is no longer neccesary.) - */ - public String getHome() { - return getRunner().getHome(); - } - - /** - * Sets the path in which to store all scape related files. Nonstatic, so - * that parameter can automatically be set from command line, but backing - * variable is static. - * - * @param home the fully qualified path name for this scape - */ - public void setHome(String home) { - getRunner().setHome(home); - } - - /** - * Are members of this active scape model participants, that is, do they - * have rules executed upon them? Default is true. - * - * @return true if members actively execute rules, false otherwise - */ - public boolean isMembersActive() { - return membersActive; - } - - /** - * Sets whether members of this scape actively execute rules upon members. - * - * @param membersActive true if members actively execute rules, false - * otherwise - */ - public void setMembersActive(boolean membersActive) { - this.membersActive = membersActive; - } - - /** - * Do cells request view updates manually or are all cells automatically - * updated every view cycle? While requiring cells to request updates - * manually adds a little to complication to model design and maintenance, - * manual requests allow a significant boost in view performance, as all - * cells do not have to be drawn every cycle. False by default. - * - * @return true if cells must request updates, false if cell updates handled - * automatically - */ - public boolean isCellsRequestUpdates() { - return cellsRequestUpdates; - } - - /** - * Should cells request view updates manually or are all cells automatically - * updated every view cycle? See above. <i>Important:</i> If you set this - * value to be true, you are responsible for ensuring that the - * <code>requestUpdate</code> method is called anytime a cell's state - * changes such that a view may be affected. Some of these calls will be - * handled for you automatically, for instance, it is not neccesary to call - * requestUpdate when a cell moves, since the HostCell calls requestUpdates - * for you. Typically, you will need to request updates when the internal - * state of a cell changes and that is reflected in how a cell is - * represeneted in a view, for example, if you color an agent for wealth, - * you will need to call <code>requestUpdate</code> anytime the agent wealth - * changes. - * - * @param cellsRequestUpdates if cells should request updates, false if cell - * updates should be handled automatically - * @see Cell#requestUpdate - */ - public void setCellsRequestUpdates(boolean cellsRequestUpdates) { - this.cellsRequestUpdates = cellsRequestUpdates; - } - - /** - * Executes the provided rules on the supplied agentArray. - */ - public void execute(List rules, List agents) { - Agent[] agentArray = new Agent[agents.size()]; - agents.toArray(agentArray); - int[] order = CollectionSpace.createOrder(agentArray.length); - if (executionOrder == AGENT_ORDER) { - order = CollectionSpace.randomizeOrder(order, getRandom()); - for (int i = 0; i < agentArray.length; i++) { - for (Iterator iterator = rules.iterator(); iterator.hasNext();) { - Rule rule = (Rule) iterator.next(); - rule.execute(agentArray[order[i]]); - } - } - } else { // executionOrder == RULE_ORDER - // Add call so that cells can respond even if this is not the - // primary agent - for (Iterator iterator = rules.iterator(); iterator.hasNext();) { - Rule rule = (Rule) iterator.next(); - - if (rule.isRandomExecution()) { - order = CollectionSpace.randomizeOrder(order, getRandom()); - } - for (int i = 0; i < agentArray.length; i++) { - rule.execute(agentArray[order[i]]); - } - if (rule instanceof ExecuteThenUpdate) { - if (rule.isRandomExecution()) { - order = CollectionSpace.randomizeOrder(order, getRandom()); - } - for (int i = 0; i < agentArray.length; i++) { - ((ExecuteThenUpdate) rule).update(agentArray[order[i]]); - } - } - } - } - } - - /** - * Executes the provided rule on every member of the lattice, according to - * the rule settings and the execution order of this scape. - */ - public void execute(Rule rule, List agents) { - List rules = new ArrayList(1); - rules.add(rule); - execute(rules, agents); - } - - /** - * Executes all of this scapes selected rules on its members. - */ - public void executeOnMembers() { - executeOnMembers(rules); - } - - /** - * Executes the provided rules on every member of the lattice, according to - * the rule settings and the execution order of this scape. - */ - public void executeOnMembers(VectorSelection ruleSelection) { - executeOnMembers(ruleSelection.getSelection()); - } - - /** - * Executes the provided rule on every member of the lattice, according to - * the rule settings and the execution order of this scape. - */ - public void executeOnMembers(Rule rule) { - Rule[] rules = new Rule[1]; - rules[0] = rule; - executeOnMembers(rules); - } - - /** - * Executes the provided rules on every member of the collection, according - * to the rule settings and the execution order of the scape. - */ - public void executeOnMembers(Object[] rules) { - if (rules.length > 0) { - strategy = new StrategyFactory(this, rules, Scape.threadCount).getStrategy(); - strategy.execute(); - } - } - - /** - * Returns an iterator across all agents in this scape. Note that this is - * simply an iterator of the backing collections members. It will have - * different behavior than is typically desried when iterating behavior - * across a scape*; so for instance, this method is not used by the internal - * rule mechanism. It should be perfectly adequete for tight iterations - * across agents when there are no additions or deletions during the - * iteration; for instance, when calcualting some value across a number of - * agents. *The iterator will not be aware of an agents deletion from the - * scape after its creation; this is because the scape caches these removals - * to improve performance. It may include agents that are added to the scape - * after its creation, and this is typically not desirable behavior when - * touring a collection of current agents. - * - * @return an iterator over the agents in scape order - */ - public Iterator iterator() { - return space.iterator(); - } - - /** - * Propogates the rule for execution up to the root of the scape tree, then - * propogates down to all nodes. - */ - public void executeOnRoot(Rule[] rules) { - if (scape == null) { - // Top level, so execute for all nodes - execute(rules); - } else { - // Still have parent scapes, so propogate up - scape.executeOnRoot(rules); - } - } - - /** - * Propogates the rule for execution up to the root of the scape tree, then - * propogates down to all nodes. - */ - public void executeOnRoot(Rule rule) { - Rule[] rules = new Rule[1]; - rules[0] = rule; - executeOnRoot(rules); - } - - /** - * Holds the current search rule. Make non-static to make searching - * threadsafe. - */ - private static SearchRule defaultSearch; - - /** - * Searches through the scape for an object (agent) that matches the - * supplied key and comparator. Typically will return the first agent found, - * but this behavior is not guranteed. For now, this is implemented as a - * simple linear search, so search time is O(n). Future versions may allow - * use of a binary search where the scape is allready appropriatly sorted, - * or may allow a cached comparator map to be used. For now if the scape is - * sorted in an order matching the comparators order, you can use - * "getAsList" and Collections.binarySearch to get an O(log(n)) search. This - * code is not thread safe, but can easily be made so. - * - * @param comparator the Comparator to use to perfrom the search - * @param key the key that an agent must match in order to be returned. - */ - public Agent search(Comparator comparator, Object key) { - if (defaultSearch == null) { - defaultSearch = new SearchRule("Default Scape Search Rule"); - } - defaultSearch.setComparator(comparator); - defaultSearch.setKey(key); - defaultSearch.setSearchType(SearchRule.SEARCH_EQUAL); - defaultSearch.clear(); - executeOnMembers(defaultSearch); - return defaultSearch.getFoundAgent(); - } - - /** - * Searches through the scape for an object (agent) that has the minimum - * value as defined by the comparator. - * - * @param comparator the Comparator to use to determin the minimum - */ - public Agent searchMin(Comparator comparator) { - if (defaultSearch == null) { - defaultSearch = new SearchRule("Default Scape Search Rule"); - } - defaultSearch.setComparator(comparator); - defaultSearch.setSearchType(SearchRule.SEARCH_MIN); - defaultSearch.clear(); - executeOnMembers(defaultSearch); - return defaultSearch.getFoundAgent(); - } - - /** - * Searches through the scape for an object (agent) that has the minimum - * value as defined by the comparator. - * - * @param comparator the Comparator to use to determin the minimum - */ - public Agent searchMax(Comparator comparator) { - if (defaultSearch == null) { - defaultSearch = new SearchRule("Default Scape Search Rule"); - } - defaultSearch.setComparator(comparator); - defaultSearch.setSearchType(SearchRule.SEARCH_MAX); - defaultSearch.clear(); - executeOnMembers(defaultSearch); - return defaultSearch.getFoundAgent(); - } - - /** - * If true, turns on value (typically for statistics) collection, else turns - * off stat collection. - */ - public void setCollectStats(boolean collect) { - if (collect) { - collectStats = new CollectStats(); - collectStats.setScape(this); - } else { - collectStats = null; - } - } - - /** - * Returns the value collection rule in effect; null if no value collection. - */ - public CollectStats getCollectStats() { - return collectStats; - } - - /** - * Sets the value collection rule to the one supplied. Allows use of custom - * value collection rules. Please let me know if you use this..considering - * removal. - */ - public void setCollectStats(CollectStats collectStats) { - this.collectStats = collectStats; - collectStats.setScape(this); - } - - /** - * Is the scape responsible for creating itself and its members, or are - * other classes responsible for creating the scape? If true (default) calls - * the createScape method on model construction, typically causing the scape - * to be populated with clones of prototype agent. If false, scape must be - * populated manually. - */ - public boolean isAutoCreate() { - return autoCreate; - } - - /** - * Sets wether the scape is responsible for creating itself and its members, - * or other model components handle this. - * - * @param autoCreate if true calls createScape at construction, otherwise - * model is built manually - */ - public void setAutoCreate(boolean autoCreate) { - this.autoCreate = autoCreate; - } - - /** - * Is the scape populated when the scape is created? That is, is the - * populate scape method called when create scape is executed? (Typically, - * the populate scape method will fill each cell with clones of the - * prototype cell, but of course this behavior can be overidden.) True by - * default. - * - * @return true if the scape will be populated when scape create is called, - * false otherwise - */ - public boolean isPopulateOnCreate() { - return populateOnCreate; - } - - /** - * Sets wether the scape is responsible for populating itself. - * - * @param populateOnCreate if true calls createScape at construction, - * otherwise model is built manually - */ - public void setPopulateOnCreate(boolean populateOnCreate) { - this.populateOnCreate = populateOnCreate; - } - - /** - * Adds the specified stat collectors to this scape for automatic collection - * by the scape. If this scape is not allready collecting stats, implicitly - * sets collect stats to true. Adds the stats to the stat collection rule. - * - * @param stats the stat collectors to add to this scape. - */ - public void addStatCollectors(StatCollector[] stats) { - // stats added in collect values - // getData().addStatCollectors(StatCollectors); - - // loop through and give the stats references for their datagroup. - // must be done prior to getData().add(stats), because - // DataGroup.add(Stats) - // calls getAllDataSeries, which calls isCollectingLongitudinal, which - // uses the StatCollector's - // reference to its data group. - for (int i = 0; i < stats.length; i++) { - stats[i].setDataGroup(getRunner().getData()); - } - getRunner().getData().add(stats); - if (collectStats == null) { - setCollectStats(true); - } - collectStats.addStatCollectors(stats); - } - - /** - * Adds the specified stat collector iff and only if it hasn't allready been - * added. - * - * @param stat the stat collector to add to this scape. todo allow - * replacement (cuurent version only adds if a stat does not - * already exist.) possibly get rid of this once issue with - * multiple stat collectors is resolved. - */ - public StatCollector addStatCollectorIfNew(StatCollector stat) { - StatCollector foundStat = getRunner().getData().getStatCollector(stat.getName()); - if (foundStat == null) { - addStatCollector(stat); - foundStat = stat; - } - return foundStat; - } - - /** - * Adds the specified stat collector to this scape for automatic collection - * by the scape. If this scape is not allready collecting stats, implicitly - * sets collect stats to true. Adds the stat to the stat collection rule. - * - * @param stat the stat collector to add to this scape. - */ - public void addStatCollector(StatCollector stat) { - StatCollector[] stats = new StatCollector[1]; - stats[0] = stat; - addStatCollectors(stats); - } - - /** - * Returns the stat collectors currently calcualting stats for this scape. - */ - public StatCollector[] getStatCollectors() { - if (collectStats != null) { - return collectStats.getStatCollectors(); - } else { - throw new RuntimeException("Tried to get stats but they are not being collected"); - } - } - - /** - * Just a class for a delegated proxy for draw features. - */ - public class DrawFeatureObservable extends Observable implements Serializable { - - /** + private static final long serialVersionUID = 1L; + + /** + * Iterate over every member and child. + * + * @param agent + * the target agent + */ + public void execute(Agent agent) { + CollectStats collectStats = ((Scape) agent).getCollectStats(); + // Special case for value collection rule + if (collectStats != null) { + /* + * int iterations = ScapeDiscrete.ALL_AGENTS; if (agent instanceof + * ScapeDiscrete) { iterations = ((ScapeDiscrete) + * agent).getAgentsPerIteration(); ((ScapeDiscrete) + * agent).setAgentsPerIteration(ScapeDiscrete.ALL_AGENTS); } + */ + collectStats.setPhase(1); + ((Scape) agent).executeOnMembers(collectStats); + collectStats.setPhase(2); + ((Scape) agent).executeOnMembers(collectStats); + /* + * if (agent instanceof ScapeDiscrete) { ((ScapeDiscrete) + * agent).setAgentsPerIteration(iterations); } + */ + } + super.execute(agent); + if (collectStats != null) { + collectStats.calculateValues(); + } + if (((Scape) agent).isRoot() && ((Scape) agent).getRunner().getData() != null) { + ((Scape) agent).getRunner().getData().update(); + // System.gc(); + } + } + }; + + /** + * The symbol to execute rules against all agents in each iteration. + */ + public final static int ALL_AGENTS = -1; + + /** + * Symbol for by agent execution order. + */ + public final static int AGENT_ORDER = -2; + + /** + * Symbol for by rule execution order. + */ + public final static int RULE_ORDER = -1; + + /** + * Symbol for complete tour excution style. + */ + public final static int COMPLETE_TOUR = 1; + + /** + * Symbol for repeated random draw execution style. + */ + public final static int REPEATED_DRAW = 2; + + /** + * Manages time, model-wide views and other features that are shared between + * scapes. There is one an only one for each model instance. + */ + private Runner runner; + + private Space space; + + /** + * An agent which which may be cloned to produce members of this collection. + * By default, all scapes which have a known number of members are initialized + * with clones of this agent. + */ + protected Agent prototypeAgent; + + /** + * The rules that this scape will execute on its memebers. + */ + private VectorSelection rules = new VectorSelection(new Vector()); + + /** + * The rules that this scape will execute on its members upon initializtion. + */ + protected VectorSelection initialRules = new VectorSelection(new Vector()); + + /** + * The observers of this scape. All listeners are notified when the scape is + * updated and given a chance to update themselves. + */ + private ArrayList<ScapeListener> scapeListeners = new ArrayList<ScapeListener>(); + + /** + * The number of agents to execute each rule across for each iteration. + */ + protected int agentsPerIteration = ALL_AGENTS; + + /** + * Order in which rules should be executed. + */ + private int executionOrder = AGENT_ORDER; + + /** + * 'Stlye' of rule execution. + */ + private int executionStyle = COMPLETE_TOUR; + + /** + * Should members of the scape be iterated against? + */ + private boolean membersActive = true; + + /** + * Should members of the scape be automatically created at startup? + */ + private boolean autoCreate = true; + + /** + * Should the scape be populated on creation? + */ + private boolean populateOnCreate = true; + + /** + * Should cells indicate that they need to be updated manually (imroving + * performance significantly) or should all cells be updated every iteration. + */ + private boolean cellsRequestUpdates = false; + + /** + * The value collection rule for this scape. Null if no values should be + * collected. + */ + private CollectStats collectStats = null; + + /** + * A view of the scape that delegates back to the scape, often null. + * Automatically created for root when standard model is used. + */ + private ScapeListener selfView; + + /** + * A vector of features available to draw memebers of this scape. + */ + private Vector drawFeatures = new Vector(); + + /** + * Are all the listeners and members (which may have listeners) of this scape + * current? (If so, we can continue updating, otherwise, we must wait. + */ + private boolean listenersAndMembersCurrent = false; + + /** + * Determines how many time steps pass between updating displayed charts and + * graphs. Higher numbers result in faster performance but less frequent + * updates. This setting does not affect model results. + */ + private int iterationsPerRedraw = 1; + + /** + * Count of the number of listeners that have been updated. Used to determine + * when all listeners have been updated. + */ + private int updatedListeners = 0; + + /** + * Count of the number of members that have been updated. Used to determine + * when all members have been updated. + */ + private int updatedMembers = 0; + + private boolean serializable = true; + + /** + * Constructs a scape with default list topology. + */ + public Scape() { + this(new ListSpace()); + } + + /** + * Constructs a scape. + * + * @param space + * the topology for this scape + */ + public Scape(CollectionSpace space) { + this(space, null, null); + } + + /** + * Constructs a scape of provided geometry, to be populated with clones of + * provided agent. + * + * @param name + * a descriptive name for the scape + * @param prototypeAgent + * the agent whose clones will be used to populate this scape + */ + public Scape(String name, Agent prototypeAgent) { + this(null, name, prototypeAgent); + } + + /** + * Constructs a scape of provided geometry, to be populated with clones of + * provided agent. + * + * @param name + * a descriptive name for the scape + * @param prototypeAgent + * the agent whose clones will be used to populate this scape + * @param space + * the topology for this scape + */ + public Scape(CollectionSpace space, String name, Agent prototypeAgent) { + super(); + setSpace(space); + setName(name); + setPrototypeAgent(prototypeAgent); + scapeListeners = new ArrayList(); + listenersAndMembersCurrent = true; + } + + /** + * Returns the size, or number of agents, of this Scape. + */ + public int getSize() { + return space.getSize(); + // return vector.size(); + } + + /** + * Sets the prototype agent, the agent that, in default implementations, will + * be cloned to populate this scape. It is an error to call while the scape is + * running. + * + * @param prototypeAgent + * the agent whose clones will populate this scape + */ + public void setPrototypeAgent(Agent prototypeAgent) { + this.prototypeAgent = prototypeAgent; + if (prototypeAgent != null && prototypeAgent.getScape() == null) { + prototypeAgent.setScape(this); + prototypeAgent.scapeCreated(); + } + setInitialized(false); + } + + /** + * Returns the agent that is cloned to populate this scape. + */ + public Agent getPrototypeAgent() { + return prototypeAgent; + } + + /** + * Returns the number of agents to iterate through each iteration cycle. + * *@param returns the number of iterations per cycle + */ + public int getAgentsPerIteration() { + return agentsPerIteration; + } + + /** + * Sets the number of agents to iterate through each iteration cycle. By + * default, set to iterate through all agents. *@param agentsPerIteration the + * number of agents to iterate against per cycle, ALL_AGENTS for all agents + */ + public void setAgentsPerIteration(int agentsPerIteration) { + this.agentsPerIteration = agentsPerIteration; + } + + /** + * Returns the number of iterations to perform before updating views. + */ + public int getIterationsPerRedraw() { + return iterationsPerRedraw; + } + + /** + * Sets the number of iterations to perform before updating views, and + * propagates this setting to all scapes and views in the model. + */ + public void setIterationsPerRedraw(int iterationsPerRedraw) { + setIterationsPerRedraw(iterationsPerRedraw, true); + } + + /** + * Sets the number of iterations to perform before updating views, and + * optionally propagates this setting to all scapes and views in the model. + */ + public void setIterationsPerRedraw(int iterationsPerRedraw, boolean propagate) { + this.iterationsPerRedraw = iterationsPerRedraw; + if (propagate) { + executeOnRoot(new NotifyViews(ScapeEvent.REQUEST_CHANGE_ITERATIONS_PER_REDRAW) { + private static final long serialVersionUID = 1L; + + @Override + public void execute(Agent agent) { + ((Scape) agent).setIterationsPerRedraw(agent.getRoot().getIterationsPerRedraw(), false); + super.execute(agent); + } + }); + } + } + + /** + * Returns the execution order that has been set for this scape. + */ + public int getExecutionOrder() { + return executionOrder; + } + + /** + * Sets the order of rule execution for this scape. If 'rule order', each rule + * is executed on every agent in turn. If 'agent order', every rule is + * executed on each agent in turn. Execution order can be profoundly + * significant to a model's dynamics. For 'synchrounous' style rules that + * subclass ExecuteAndUpdate, 'by rule' execution is the only order that makes + * sense. + * + * @param symbol + * RULE_ORDER for by rule execution, AGENT_ORDER for by agent + * execution + */ + public void setExecutionOrder(int symbol) { + this.executionOrder = symbol; + } + + /** + * Returns the execution style that has been set for this scape. + */ + public int getExecutionStyle() { + return executionStyle; + } + + /** + * Sets the style that rules will be executed upon this scape. If complete + * tour, every agent is visited once and only once (assuming agents per + * iteration is set to 'all agents'.) If repeated draw, a random agent is + * picked n times for execution. (Actually, if execution order is by agent, + * each rule is then executed upon the picked agent, so that there are n total + * draws. But if execution order is by rule, then for each rule, a random + * agent is picked, which means that there are actually (rules X n) draws. In + * practice, this combination does not seem to make much sense in any case.) A + * complete tour style of execution seems generally more plausible, but a + * repeated draw approach can produce different and interesting results. + * + * @param symbol + * one of COMPLETE_TOUR or REPEATED_DRAW + */ + public void setExecutionStyle(int symbol) { + this.executionStyle = symbol; + } + + /** + * Returns the extent of the scape. The extent can be thought of as the most + * extreme point in the scape. For discrete scape's this will simply be the + * furthest cell, so that for a 20x20 grid, the extent would be {20, 20}. For + * continuous spaces it will be the maximum boundary of the space. For lists, + * it will be the size of lists. Therefore, this method should net be confused + * with the scape's "size". Note that scape graphs will not have useful + * extents, but all other scapes do. + */ + public Coordinate getExtent() { + return space.getExtent(); + } + + /** + * Sets the size of the scape. Note that scape graphs will not have useful + * extents, but all other scapes do. It is an error to set extent while a + * scape is running. + * + * @param extent + * a coordinate at the maximum extent + */ + public void setExtent(Coordinate extent) { + if (getRunner() != null && getRunner().isRunning()) { + throw new RuntimeException("Tried to modfiy extent while scape was running"); + } + space.setExtent(extent); + } + + /** + * Sets the size of the scape. Note that scape graphs will not have useful + * extents, but all other scapes do. It is an error to set extent while a + * scape is running. + * + * @throws RuntimeException + * if the scape is currently running + * @param xval + * coordinate 1 of the extent + */ + public void setExtent(int xval) { + if (runner != null && runner.isRunning()) { + throw new RuntimeException("Tried to modfiy extent while scape was running"); + } + if (getSpace().getGeometry().getDimensionCount() != 1) { + throw new RuntimeException("Tried to set extent as 1-dimension for a scape that isn't 1-dimensional."); + } + ((CollectionSpace) getSpace()).setExtent(xval); + } + + /** + * Sets the size of the scape. Note that scape graphs will not have useful + * extents, but all other scapes do. It is an error to set extent while a + * scape is running. + * + * @param xval + * coordinate 1 of the extent + * @param yval + * coordinate 2 of the extent + * @throws RuntimeException + * if the scape is currently running + * @throws UnsupportedOperationException + * if the underlying space isn't appropriate + */ + public void setExtent(int xval, int yval) { + if (runner != null && runner.isRunning()) { + throw new RuntimeException("Tried to modfiy extent while scape was running"); + } + try { + ((Array2DBase) getSpace()).setExtent(xval, yval); + } catch (ClassCastException e) { + throw new UnsupportedOperationException("Can't set extent as x, y; underlying scape doesn't support it."); + } + } + + /** + * Returns the name of this scape, the model name if this is root and there is + * no name set. + */ + public String getName() { + if (name != null) { + return name; + } else { + return getClass().getName(); + } + } + + private void loadDescriptions() { + if (getRunner().getDescription() == null || getRunner().getHTMLDescription() == null) { + String fileName = "About" + Utility.getClassNameOnly(this.getClass()) + ".html"; + URL aboutFile = this.getClass().getResource(fileName); + if (aboutFile != null) { + getRunner().setDescription(""); + try { + BufferedInputStream is = (BufferedInputStream) aboutFile.getContent(); + BufferedReader ir = new BufferedReader(new InputStreamReader(is)); + String nextLine = ir.readLine(); + // Clean out html tags + StringBuffer htmlFragBuffer = new StringBuffer(); + while (nextLine != null) { + htmlFragBuffer.append(nextLine); + nextLine = ir.readLine(); + } + StringBuffer plainTextBuffer = new StringBuffer(); + // Neccessary to support 1.3 + String htmlString = htmlFragBuffer.toString(); + boolean done = false; + int pos = 0; + while (!done) { + int anglePos = htmlString.indexOf("<", pos); + if (anglePos >= 0) { + plainTextBuffer.append(htmlFragBuffer.substring(pos, anglePos)); + if (htmlFragBuffer.substring(anglePos, anglePos + 4).equalsIgnoreCase("<BR>")) { + plainTextBuffer.append("\n"); + } + pos = htmlString.indexOf(">", anglePos) + 1; + } else { + plainTextBuffer.append(htmlFragBuffer.substring(pos, htmlFragBuffer.length())); + done = true; + } + } + setHTMLDescription(htmlFragBuffer.toString()); + setDescription(plainTextBuffer.toString()); + } catch (java.io.IOException e) { + // Probably file has not been defined; in any case, just use + // toString value.. + getEnvironment().getConsole().println("Non-critical exception: couldn't read \"About\" file: " + e); + getRunner().setDescription(toString()); + } + } else { + // No about file defined + getRunner().setDescription(toString()); + } + } + } + + /** + * Returns a long (paragraph length suggested) description of the scape. The + * root scape should describe the model as a whole; subscapes should describe + * themselves. Plantext. If no description is provided, return the standar + * toString() description. This description is automatically loaded from a + * file called "About[ModelClassName].html" located in the the same directory + * as the .class file for the model, if such a file exists. To use this + * feature simply create such a file and place it in the appropriate + * directory. You can include any normal html style tags in this file, they + * will be stripped from the non-html description. + */ + public String getDescription() { + try { + loadDescriptions(); + return getRunner().getDescription(); + } catch (ClassCastException cce) { + return ""; + } + } + + /** + * Returns a long (paragraph length suggested) description of the scape. The + * root scape should describe the model as a whole; subscapes should describe + * themselves. If no description is provided, return the standar toString() + * description. + */ + public void setDescription(String description) { + getRunner().setDescription(description); + } + + /** + * Returns a long (paragraph length suggested) description of the scape. The + * root scape should describe the model as a whole; subscapes should describe + * themselves. Includes html tags as appropriate. This description is + * automatically loaded from a file called "About[ModelClassName].html" + * located in the the same directory as the .class file for the model, if such + * a file exists. To use this feature simply create such a file and place it + * in the appropriate directory. You can include any normal html style tags in + * this file, they will be stripped from the non-html description. If no + * description is provided, return the standar toString() description. + */ + public String getHTMLDescription() { + loadDescriptions(); + return getRunner().getHTMLDescription(); + } + + /** + * Returns a long (paragraph length suggested) description of the scape. The + * root scape should describe the model as a whole; subscapes should describe + * themselves. Should include html tags as appropriate. If no description is + * provided, return the standar toString() description. + */ + public void setHTMLDescription(String description) { + getRunner().setHTMLDescription(description); + } + + /** + * Returns the root of this scape, which may be this scape. + */ + public final Scape getRoot() { + if (scape == null) { + return this; + } else { + return scape.getRoot(); + } + } + + /** + * Is this scape the root within its entire simulation context? That is, does + * this root not have any parent scapes? + */ + public boolean isRoot() { + return scape == null; + } + + /** + * Has a view update been requested for this cell? + */ + public boolean isUpdateNeeded() { + return isUpdateNeeded(getIterationsPerRedraw()); + } + + /** + * Contructs the basic scape structure. Instantiates the agents, but does not + * populate them. It is not neccesary to set extent before initializing a + * collection, unless you want to have a populated vecotr to begin with. + */ + public void construct() { + space.construct(); + if (getSpace() instanceof Discrete && getPrototypeAgent() == null) { + setPrototypeAgent(new Cell()); + } + } + + /** + * Populates the scape with clones of the prototype agent. Prototype agent + * should be set before calling this method. + */ + public void populate() { + space.populate(); + } + + /** + * Create this scape; contruct it, populate it, add rules, create statistic + * collectors, etc. Called automatically at model construction, unless + * isAutoCreate is set to false. By default, automatically populates all + * members with clones of the prototype agent. Of course, this behavior can be + * overridden or embellished. To turn off the populate behavior, set populate + * on create to false. + * + * @see #setPopulateOnCreate + */ + public void createScape() { + // We use Scape as a prototype simply because Scape is abstract and so + // can't be + // instantiated. Any scape will do, since we won't be cloning it to + // populate. + if (isRoot()) { + if (getPrototypeAgent() == null) { + setPrototypeAgent(new Scape()); + } + if (getRunner() == null) { + new NonGraphicRunner().setRootScape(this); + } + } + construct(); + if (isPopulateOnCreate()) { + populate(); + } + } + + /** + * Initializes the state of the scape. This is the appropriate place to put + * any initialization that is dependent on the state of other ascape objects. + * If autoCreate is true, calls createScape() on the scape. Note that for root + * scapes, autoCreate is always false. Objects are initialized in the order + * they are added to parent scapes. + */ + public void initialize() { + if (isRoot()) { + setAutoCreate(false); + } + if (isAutoCreate()) { + createScape(); + } + super.initialize(); + getSpace().initialize(); + if (!(getSpace() instanceof Continuous) && getPrototypeAgent() != null && getPrototypeAgent().getScape() == this && !getSpace().isMutable()) { + executeOnMembers(Cell.CALCULATE_NEIGHBORS_RULE); + } + } + + /** + * Returns current count of iterations. + */ + public final int getIteration() { + return getRunner().getIteration(); + } + + /** + * Returns the current period, which is just the iteration plus the period + * begin. + */ + public final int getPeriod() { + return getRunner().getPeriod(); + } + + /** + * Returns the name that periods are referred to by. + */ + public String getPeriodName() { + return getRunner().getPeriodName(); + } + + /** + * Returns a string description of the current period, i.e. "Iteration 1", + * "Year 1900", "StarDate 3465.29." + */ + public String getPeriodDescription() { + return getPeriodName() + " " + Integer.toString(getPeriod()); + } + + /** + * Sets the name that periods are referred to by. + */ + public void setPeriodName(String name) { + getRunner().setPeriodName(name); + } + + /** + * Adds a rule to this scape, automatically selecting it. + */ + public synchronized void addRule(Rule rule) { + addRule(rule, true); + } + + /** + * Adds a rule to this scape. Allows setting whether rule be should be + * selected (run) automatically? If the selection is not changed, rules are + * executed in the order they are added. + * + * @param rule + * the rule to add + * @param select + * if rule should be run false if rule should just be made available + * to be run + */ + public synchronized void addRule(Rule rule, boolean select) { + if (rule instanceof ExecuteThenUpdate && getExecutionOrder() != RULE_ORDER) { + // bug mtp: need to add similar check when setting rule order. + throw new RuntimeException("Tried to add execute and update rule to AGENT_ORDER Scape. Set Scape to RULE_ORDER execution first."); + } + if (rule.getScape() == null) { + rule.setScape(this); + } + rules.addElement(rule, select); + } + + /** + * Returns all rules that this scape might execute. + */ + public VectorSelection getRules() { + return rules; + } + + /** + * Adds a rule to be executed once following initialization. Rule is + * automatically selected for running. + * + * @param rule + * to be executed at simulation start + */ + public synchronized void addInitialRule(Rule rule) { + addInitialRule(rule, true); + } + + /** + * Adds a rule to be executed once following initialization. If the selection + * is not chagned, rules are executed in the order they are added. + * + * @param rule + * to be executed at simulation start + * @param select + * if rule should be run false if rule should just be made available + * to be run + */ + public synchronized void addInitialRule(Rule rule, boolean select) { + if (rule.getScape() == null) { + rule.setScape(this); + } + initialRules.addElement(rule, select); + } + + /** + * Returns all the rules executed following scape initialization. + */ + public VectorSelection getInitialRules() { + return initialRules; + } + + public void setInitialRules(VectorSelection initialRules) { + this.initialRules = initialRules; + } + + /** + * Adds a view to this scape. Takes care of basic housekeeping, including + * registering view as listener, and creating window for view to be displayed + * within. + * + * @param view + * ComponentView to display in window + */ + public synchronized void addView(ScapeListener view) { + this.addView(view, true); + } + + /** + * Adds a view to this scape. Takes care of basic housekeeping, including + * registering view as listener, and creating window for view to be displayed + * within. This version of the method allow the adding of a view without + * regard to the GUI setting. This method might be useful for instace when + * temporaily instrumenting a non-gui run with diagnostics. Normally + * addScapeListener should be used. + * + * @param view + * ComponentView to display in window + * @param createFrame + * should the view be placed within a new window frame? + * @param forceGUI + * add a GUI view witout regard to the display GUI setting + */ + public synchronized void addView(final ScapeListener view, boolean createFrame, boolean forceGUI) { + if (forceGUI || Runner.isDisplayGraphics() || !view.isGraphic() || Runner.isServeGraphics()) { + try { + this.addScapeListener(view); + // if (view instanceof Component) { + // // try { + // SwingUtilities.invokeLater(new Runnable() { + // public void run() { + // view.scapeNotification(new ScapeEvent(Scape.this, + // ScapeEvent.REPORT_ADDED)); + // } + // }); + // // } catch (InterruptedException e) { + // // e.printStackTrace(); + // // } catch (InvocationTargetException e) { + // // e.printStackTrace(); + // // } + // } else { + view.scapeAdded(new ScapeEvent(this, ScapeEvent.REPORT_ADDED)); + // } + } catch (TooManyListenersException e) { + throw new RuntimeException("Tried to add a view to more than one scape:\n" + e); + } + if (createFrame && view.isGraphic()) { + if (!Runner.isServeGraphics()) { + getEnvironment().addView(view); + } + // topdo remove view dependency + } + } else { + } + } + + /** + * Adds a view to this scape. Takes care of basic housekeeping, including + * registering view as listener, and creating window for view to be displayed + * within. An important exception occurs when a GUI view is added and display + * GUI is set to false. In this case, the view will _not_ be added. This makes + * it easy to add views in many model components without worrying about + * checking for GUI display state. Views can be added regardless of the value + * of display GUI by using addViewForce. Note: Even with the conveneince of + * this method, it is often nec + * + * @param view + * ComponentView to display in window + * @param createFrame + * should the view be placed within a new window frame? + */ + public synchronized void addView(ScapeListener view, boolean createFrame) { + addView(view, createFrame, false); + } + + /** + * Adds a view to this scape. Takes care of basic housekeeping, including + * registering view as listener, and creating window for view to be displayed + * within. + * + * @param views + * ComponentView to display in window + */ + public synchronized void addViews(ScapeListener[] views) { + this.addViews(views, true); + } + + /** + * Adds an array of views to this scape. Takes care of basic housekeeping, + * including registering the views as listeners, and creating window for view + * to be displayed within. This version of the method allow the adding of a + * view without regard to the GUI setting. This method might be useful for + * instace when temporaily instrumenting a non-gui run with diagnostics. + * Normally addScapeListener should be used. + * + * @param views + * ComponentViews array to display in window + * @param createFrame + * should the view be placed within a new window frame? + * @param forceGUI + * add a GUI view witout regard to the dispaly GUI setting + */ + public synchronized void addViews(ScapeListener[] views, boolean createFrame, boolean forceGUI) { + if (forceGUI || Runner.isDisplayGraphics() || !views[0].isGraphic()) { + try { + for (int i = 0; i < views.length; i++) { + this.addScapeListener(views[i]); + views[i].scapeAdded(new ScapeEvent(this, ScapeEvent.REPORT_ADDED)); + } + } catch (TooManyListenersException e) { + throw new RuntimeException("Tried to add a view to more than one scape"); + } + boolean allGraphic = false; + for (int i = 0; i < views.length; i++) { + if (views[i].isGraphic()) { + allGraphic = true; + } else { + allGraphic = false; + break; + } + } + // For now, we're going to assume that if one of the views is + // graphic, they all are + if (createFrame && allGraphic) { + getEnvironment().addViews(views); + } + } + } + + /** + * Adds an array of views to this scape. Takes care of basic housekeeping, + * including registering the views as listeners, and creating window for view + * to be displayed within. An important exception occurs when GUI views are + * added and display GUI is set to false. In this case, the views will _not_ + * be added. This makes it easy to add views in many model components without + * worrying about checking for GUI display state. Views can be added + * regardless of the value of dispaly GUI by using addViewForce. + * + * @param views + * ComponentViews to display in window + * @param createFrame + * should the view be placed within a new window frame? + */ + public synchronized void addViews(ScapeListener[] views, boolean createFrame) { + // To do, test and throw error for case where user tries to add mixed + // gui and non gui views, or the view array is empty + addViews(views, createFrame, false); + } + + /** + * Adds an observer to this scape. This observer will be notified when the + * scape has finished iterating, and is expected to notify this scape when it + * has updated itself. This method also adds the scape to the listener as a + * control listener. + * + * @param listener + * the listern to add + */ + public synchronized void addScapeListener(ScapeListener listener) { + if (listener == null) { + throw new RuntimeException("Tried to add a null listener to Scape."); + } + // Not sure if we want to make this poilicy or not.. + /* + * if (listener.getScape() == null) { throw new RuntimeException("Listener + * must have this scape assigned before calling addScapeListener."); } + */ + // change to array copy + scapeListeners.add(listener); + updatedListeners++; + listenerOrMemberUpdated(); + } + + /** + * Adds an observer to this scape. This version simple adds the new listener + * to the beginning of the list. This can be useful if there are listeners + * that need to be called first. + * + * @param listener + * the listern to add + */ + public synchronized void addScapeListenerFirst(ScapeListener listener) { + if (listener == null) { + throw new RuntimeException("Tried to add a null listener to Scape."); + } + scapeListeners.add(0, listener); + updatedListeners++; + listenerOrMemberUpdated(); + } + + /** + * Returns true if and only if the argument is an observer of this scape. + */ + public boolean isScapeListener(ScapeListener listener) { + return scapeListeners.contains(listener); + } + + /** + * Removes the observer from this scape. This observer will be notified when + * the scape has finished iterating, and is expected to notify this scape when + * it has updated itself. + */ + public synchronized void removeScapeListener(ScapeListener listener) { + boolean success = scapeListeners.remove(listener); + if (!success) { + getEnvironment().getConsole().println("WARNING: Tried to remove unregistered scape listener " + listener + " from scape " + this + "."); + } + + listener.scapeRemoved(new ScapeEvent(this, ScapeEvent.REPORT_REMOVED)); + // we may have just removed the last non-updated listener, + // so that everything needing an update has been updated; we need to + // check. + listenerOrMemberUpdated(); + } + + /** + * Returns all listeners for this scape. + */ + public ArrayList getScapeListeners() { + return scapeListeners; + } + + // todo (Tried using thread notification, but too slow...perhpas w/ + // pooling?? + // class NotificationThread extends Thread { + // private ScapeListener listener; + // private int id; + // + // public NotificationThread(ScapeListener listener, int id) { + // super(Scape.this + " Scape Notify " + listener); + // this.listener = listener; + // this.id = id; + // } + // + // public void run() { + // listener.scapeNotification(new ScapeEvent(Scape.this, id)); + // } + // } + + /** + * Notifies all scape listeners that this scapes state has changed. The root + * scape thread then waits until all listeners have been updated. + */ + public void notifyListeners(final int id) { + notifyListeners(new ScapeEvent(Scape.this, id)); + } + + /** + * Notifies all scape listeners that this scapes state has changed. The root + * scape thread then waits until all listeners have been updated. + */ + public void notifyListeners(final ScapeEvent event) { + listenersAndMembersCurrent = false; + updatedListeners = 0; + updatedMembers = 0; + if (scapeListeners.size() > 0) { + ArrayList<ScapeListener> currentListeners = (ArrayList) scapeListeners.clone(); + for (ScapeListener listener : currentListeners) { + getRunner().notify(event, listener); + } + } else { + listenerOrMemberUpdated(); + } + } + + /** + * Have all views and views of memebers of this scape been updated? [The + * grammer is terrible, but it fits the text pattern!] + * + * @return boolean true if no views are still updating, false if not + */ + public final boolean isAllViewsUpdated() { + return listenersAndMembersCurrent; + } + + /** + * Called whenever a listener or member scape of this scape has been updated. + * If all listeners and members have been updated, informs parent scape. + */ + protected synchronized void listenerOrMemberUpdated() { + // For testing updating.. + // if ((updatedListeners >= scapeListeners.length) && + // ((!(getPrototypeAgent() instanceof Scape)) || (!(((Scape) + // getPrototypeAgent()).isMembersActive())) || (!(getPrototypeAgent() + // instanceof AgentScape)) || (updatedMembers >= getSize()))) { + if (updatedListeners >= scapeListeners.size() && (updatedMembers >= getSize() || !(getPrototypeAgent() instanceof Scape) || getSpace() instanceof Singleton || !((Scape) getPrototypeAgent()).isMembersActive())) { + listenersAndMembersCurrent = true; + updatedListeners = 0; + updatedMembers = 0; + if (scape != null) { + scape.memberUpdated(this); + // if (isInitialized() ) { + // getRuntimeEnvironment().getConsole().println(scape. + // updatedListeners + // + "/" + + // scape.scapeListeners.length + ", " + scape.updatedMembers + + // "/"+scape.getSize() + " for " + scape + " from " + this); + // } + } + } + } + + /** + * Called whenever a listener has been updated. + * + * @param listener + * the listener tha has been updated + */ + public synchronized void listenerUpdated(ScapeListener listener) { + updatedListeners++; + // Useful for testing updating problems. + // getRuntimeEnvironment().getConsole().println("L " + listener + ": " + + // updatedListeners +" of "+ scapeListeners.size()+" --- + // "+updatedMembers + " of + // " + getSize()); + listenerOrMemberUpdated(); + } + + /** + * Called whenever a member has been updated. + * + * @param member + * the member that has been updated + */ + public synchronized void memberUpdated(Scape member) { + updatedMembers++; + // Useful for testing updating problems. + // getRuntimeEnvironment().getConsole().println("M " + member + ": " + + // updatedListeners +" of "+ scapeListeners.size()+" --- + // "+updatedMembers + " of + // " + getSize()); + listenerOrMemberUpdated(); + } + + /** + * Responds to any control events fired at this scape. Currently reacts to + * start, stop, pause, resume, step, quit, and restart events, as well as + * listener update report events. All control events except listener updates + * are passed up to the root. Any other events trigger an untrapped exception. + */ + public void respondControl(ControlEvent control) { + if (control.getID() == ControlEvent.REPORT_LISTENER_UPDATED) { + listenerUpdated((ScapeListener) control.getSource()); + } else { + getRunner().respondControl(control); + } + } + + /** + * This is for grid communication of changes in draw feature. + * + * @param event + */ + public void respondDrawFeature(DrawFeatureEvent event) { + throw new RuntimeException("The client or worker scape should define their own version of this!"); + } + + /** + * Sets the running state for all scapes. Safe to call on any scape in the + * model; the request is propogated to the parent scape. If true, starts the + * parent scape's thread, which causes scape to iterate. If set false, the + * scape will be stopped when the current iteration is complete. + * + * @param running + * if true, starts the thread, if false, stops it. + */ + public void setRunning(boolean running) { + getRunner().setRunning(running); + } + + /** + * Has the scape been requested to run? <i>Note:</i> if false, indicates that + * a stop has been requested, not neccesarily that it has occured, as the + * simulation continues the current iteration. If you need to know when a + * scape has actually stopped, listen for the scapeStopped event. + * + * @return the current requested running state + */ + public boolean isRunning() { + return getRunner() != null && getRunner().isRunning(); + } + + /** + * Sets the paused state for all parent and member scapes. Safe to call on any + * scape in the model; the request is propogated up to the parent scape. If + * set true, a pause will occur when the current iteration is complete. + * + * @param pause + * if true, pauses, otherwise resumes iterations + */ + public void setPaused(boolean pause) { + if (pause) { + getRunner().pause(); + } else { + getRunner().resume(); + } + } + + /** + * Has the scape been requested to pause? <i>Note:</i> indicates that a pause + * has been requested, not neccesarily that the simulation is paused; it may + * be completing its current iteration. + * + * @return true if pause requested, false if resume requested or running + * normally + */ + public boolean isPaused() { + return getRunner().isPaused(); + } + + /** + * Sets the earliest period this scape is expected to be run at. 0 by default. + * + * @param earliestPeriod + * the lowest period value this scape can have + */ + public void setEarliestPeriod(int earliestPeriod) { + getRunner().setEarliestPeriod(earliestPeriod); + } + + /** + * Sets the latest period this scape is expected to be run at. Max of integer + * (effectively unlimited) by default. + * + * @param latestPeriod + * the highest period value this scape can have + */ + public void setLatestPeriod(int latestPeriod) { + getRunner().setLatestPeriod(latestPeriod); + } + + /** + * Is the supplied period a valid period for this scape? + * + * @param period + * the period to test + * @return true if within earliest and latest periods, false otherwise + */ + public boolean isValidPeriod(int period) { + return getRunner().isValidPeriod(period); + } + + /** + * Returns the period this scape begins running at. By default, the greater of + * earliest period and 0. + */ + public int getStartPeriod() { + return getRunner().getStartPeriod(); + } + + /** + * Sets the start period for this scape. The start period is the period this + * scape is given when a model run is started. + * + * @param startPeriod + * the period to begin runs at + */ + public void setStartPeriod(int startPeriod) throws SpatialTemporalException { + getRunner().setStartPeriod(startPeriod); + } + + /** + * Returns the period this scape stops running at. By default, the lesser of + * latest period and integer maximum value (effectively unlimited.) + */ + public int getStopPeriod() { + return getRunner().getStopPeriod(); + } + + /** + * Sets the stop period for this scape. The stop period is the period that the + * scape is automatically stopped at. The scape may be automatically set to + * start agina at start value is the scape is set to restart. + * + * @param stopPeriod + * the period the scape will stop at upon reaching + * @see #setAutoRestart + */ + public void setStopPeriod(int stopPeriod) throws SpatialTemporalException { + getRunner().setStopPeriod(stopPeriod); + } + + /** + * Returns the period to pause on. + */ + public int getPausePeriod() { + return getRunner().getPausePeriod(); + } + + /** + * Causes the model to pause at the specified period. + * + * @param pausePeriod + * when to pause + */ + public void setPausePeriod(int pausePeriod) { + getRunner().setPausePeriod(pausePeriod); + } + + /** + * Does the scape automatically start upon opening? True by default. + */ + public boolean isStartOnOpen() { + return Runner.isStartOnOpen(); + } + + /** + * Should the scape be automatically started upon opening? True by default. + * + * @param startOnOpen + * true to start the scape upon opening a model + */ + public void setStartOnOpen(boolean startOnOpen) { + Runner.setStartOnOpen(startOnOpen); + } + + /** + * Should the scape be automatically restarted upon stopping at its stop + * period? Setting this value to true allows easy cycling of models for + * demonstrations, model explorations, etc. See DataOutputView for an example + * of more sophisticated handling of multiple runs. + * + * @param autoRestart + * true to restart the scape upon reaching stop period, false to + * simple stop + * @see #setStopPeriod + */ + public void setAutoRestart(boolean autoRestart) { + getRunner().setAutoRestart(autoRestart); + } + + /** + * Returns the root of this scape, which may be this scape. This was an older + * usage and is retained for backward compatability only. It is now not a all + * good name as it confuses model for a view / control component, and may be + * removed in the future. + * + * @deprecated please use #getRunner(). + */ + public Runner getModel() { + return getRunner(); + } + + /** + * Returns the runtime model environment, which manages model-wide state such + * as run status. + * + * @return the model environment for entire model + */ + public Runner getRunner() { + return getRoot().runner; + } + + public void setRunner(Runner _runner) { + runner = _runner; + } + + /** + * Returns the path in which all files should by default be stored to and + * retrieved from. Nonstatic, so that parameter can automatically be set from + * command line, but backing variable is static. Default is "./", can be + * modified by calling setHome or providing an ascape.home java property. + * (This may change now since it is no longer neccesary.) + */ + public String getHome() { + return getRunner().getHome(); + } + + /** + * Sets the path in which to store all scape related files. Nonstatic, so that + * parameter can automatically be set from command line, but backing variable + * is static. + * + * @param home + * the fully qualified path name for this scape + */ + public void setHome(String home) { + getRunner().setHome(home); + } + + /** + * Are members of this active scape model participants, that is, do they have + * rules executed upon them? Default is true. + * + * @return true if members actively execute rules, false otherwise + */ + public boolean isMembersActive() { + return membersActive; + } + + /** + * Sets whether members of this scape actively execute rules upon members. + * + * @param membersActive + * true if members actively execute rules, false otherwise + */ + public void setMembersActive(boolean membersActive) { + this.membersActive = membersActive; + } + + /** + * Do cells request view updates manually or are all cells automatically + * updated every view cycle? While requiring cells to request updates manually + * adds a little to complication to model design and maintenance, manual + * requests allow a significant boost in view performance, as all cells do not + * have to be drawn every cycle. False by default. + * + * @return true if cells must request updates, false if cell updates handled + * automatically + */ + public boolean isCellsRequestUpdates() { + return cellsRequestUpdates; + } + + /** + * Should cells request view updates manually or are all cells automatically + * updated every view cycle? See above. <i>Important:</i> If you set this + * value to be true, you are responsible for ensuring that the + * <code>requestUpdate</code> method is called anytime a cell's state changes + * such that a view may be affected. Some of these calls will be handled for + * you automatically, for instance, it is not neccesary to call requestUpdate + * when a cell moves, since the HostCell calls requestUpdates for you. + * Typically, you will need to request updates when the internal state of a + * cell changes and that is reflected in how a cell is represeneted in a view, + * for example, if you color an agent for wealth, you will need to call + * <code>requestUpdate</code> anytime the agent wealth changes. + * + * @param cellsRequestUpdates + * if cells should request updates, false if cell updates should be + * handled automatically + * @see Cell#requestUpdate + */ + public void setCellsRequestUpdates(boolean cellsRequestUpdates) { + this.cellsRequestUpdates = cellsRequestUpdates; + } + + /** + * Executes the provided rules on the supplied agentArray. + */ + public void execute(List rules, List agents) { + Agent[] agentArray = new Agent[agents.size()]; + agents.toArray(agentArray); + int[] order = CollectionSpace.createOrder(agentArray.length); + if (executionOrder == AGENT_ORDER) { + order = CollectionSpace.randomizeOrder(order, getRandom()); + for (int i = 0; i < agentArray.length; i++) { + for (Iterator iterator = rules.iterator(); iterator.hasNext();) { + Rule rule = (Rule) iterator.next(); + rule.execute(agentArray[order[i]]); + } + } + } else { // executionOrder == RULE_ORDER + // Add call so that cells can respond even if this is not the + // primary agent + for (Iterator iterator = rules.iterator(); iterator.hasNext();) { + Rule rule = (Rule) iterator.next(); + + if (rule.isRandomExecution()) { + order = CollectionSpace.randomizeOrder(order, getRandom()); + } + for (int i = 0; i < agentArray.length; i++) { + rule.execute(agentArray[order[i]]); + } + if (rule instanceof ExecuteThenUpdate) { + if (rule.isRandomExecution()) { + order = CollectionSpace.randomizeOrder(order, getRandom()); + } + for (int i = 0; i < agentArray.length; i++) { + ((ExecuteThenUpdate) rule).update(agentArray[order[i]]); + } + } + } + } + } + + /** + * Executes the provided rule on every member of the lattice, according to the + * rule settings and the execution order of this scape. + */ + public void execute(Rule rule, List agents) { + List rules = new ArrayList(1); + rules.add(rule); + execute(rules, agents); + } + + /** + * Executes all of this scapes selected rules on its members. + */ + public void executeOnMembers() { + executeOnMembers(rules); + } + + /** + * Executes the provided rules on every member of the lattice, according to + * the rule settings and the execution order of this scape. + */ + public void executeOnMembers(VectorSelection ruleSelection) { + executeOnMembers(ruleSelection.getSelection()); + } + + /** + * Executes the provided rule on every member of the lattice, according to the + * rule settings and the execution order of this scape. + */ + public void executeOnMembers(Rule rule) { + Rule[] rules = new Rule[1]; + rules[0] = rule; + executeOnMembers(rules); + } + + /** + * Executes the provided rules on every member of the collection, according to + * the rule settings and the execution order of the scape. + */ + public void executeOnMembers(Object[] rules) { + if (rules.length > 0) { + strategy = new StrategyFactory(this, rules, Scape.threadCount).getStrategy(); + strategy.execute(); + } + } + + /** + * Returns an iterator across all agents in this scape. Note that this is + * simply an iterator of the backing collections members. It will have + * different behavior than is typically desried when iterating behavior across + * a scape*; so for instance, this method is not used by the internal rule + * mechanism. It should be perfectly adequete for tight iterations across + * agents when there are no additions or deletions during the iteration; for + * instance, when calcualting some value across a number of agents. *The + * iterator will not be aware of an agents deletion from the scape after its + * creation; this is because the scape caches these removals to improve + * performance. It may include agents that are added to the scape after its + * creation, and this is typically not desirable behavior when touring a + * collection of current agents. + * + * @return an iterator over the agents in scape order + */ + public Iterator iterator() { + return space.iterator(); + } + + /** + * Propogates the rule for execution up to the root of the scape tree, then + * propogates down to all nodes. + */ + public void executeOnRoot(Rule[] rules) { + if (scape == null) { + // Top level, so execute for all nodes + execute(rules); + } else { + // Still have parent scapes, so propogate up + scape.executeOnRoot(rules); + } + } + + /** + * Propogates the rule for execution up to the root of the scape tree, then + * propogates down to all nodes. + */ + public void executeOnRoot(Rule rule) { + Rule[] rules = new Rule[1]; + rules[0] = rule; + executeOnRoot(rules); + } + + /** + * Holds the current search rule. Make non-static to make searching + * threadsafe. + */ + private static SearchRule defaultSearch; + + /** + * Searches through the scape for an object (agent) that matches the supplied + * key and comparator. Typically will return the first agent found, but this + * behavior is not guranteed. For now, this is implemented as a simple linear + * search, so search time is O(n). Future versions may allow use of a binary + * search where the scape is allready appropriatly sorted, or may allow a + * cached comparator map to be used. For now if the scape is sorted in an + * order matching the comparators order, you can use "getAsList" and + * Collections.binarySearch to get an O(log(n)) search. This code is not + * thread safe, but can easily be made so. + * + * @param comparator + * the Comparator to use to perfrom the search + * @param key + * the key that an agent must match in order to be returned. + */ + public Agent search(Comparator comparator, Object key) { + if (defaultSearch == null) { + defaultSearch = new SearchRule("Default Scape Search Rule"); + } + defaultSearch.setComparator(comparator); + defaultSearch.setKey(key); + defaultSearch.setSearchType(SearchRule.SEARCH_EQUAL); + defaultSearch.clear(); + executeOnMembers(defaultSearch); + return defaultSearch.getFoundAgent(); + } + + /** + * Searches through the scape for an object (agent) that has the minimum value + * as defined by the comparator. + * + * @param comparator + * the Comparator to use to determin the minimum + */ + public Agent searchMin(Comparator comparator) { + if (defaultSearch == null) { + defaultSearch = new SearchRule("Default Scape Search Rule"); + } + defaultSearch.setComparator(comparator); + defaultSearch.setSearchType(SearchRule.SEARCH_MIN); + defaultSearch.clear(); + executeOnMembers(defaultSearch); + return defaultSearch.getFoundAgent(); + } + + /** + * Searches through the scape for an object (agent) that has the minimum value + * as defined by the comparator. + * + * @param comparator + * the Comparator to use to determin the minimum + */ + public Agent searchMax(Comparator comparator) { + if (defaultSearch == null) { + defaultSearch = new SearchRule("Default Scape Search Rule"); + } + defaultSearch.setComparator(comparator); + defaultSearch.setSearchType(SearchRule.SEARCH_MAX); + defaultSearch.clear(); + executeOnMembers(defaultSearch); + return defaultSearch.getFoundAgent(); + } + + /** + * If true, turns on value (typically for statistics) collection, else turns + * off stat collection. + */ + public void setCollectStats(boolean collect) { + if (collect) { + collectStats = new CollectStats(); + collectStats.setScape(this); + } else { + collectStats = null; + } + } + + /** + * Returns the value collection rule in effect; null if no value collection. + */ + public CollectStats getCollectStats() { + return collectStats; + } + + /** + * Sets the value collection rule to the one supplied. Allows use of custom + * value collection rules. Please let me know if you use this..considering + * removal. + */ + public void setCollectStats(CollectStats collectStats) { + this.collectStats = collectStats; + collectStats.setScape(this); + } + + /** + * Is the scape responsible for creating itself and its members, or are other + * classes responsible for creating the scape? If true (default) calls the + * createScape method on model construction, typically causing the scape to be + * populated with clones of prototype agent. If false, scape must be populated + * manually. + */ + public boolean isAutoCreate() { + return autoCreate; + } + + /** + * Sets wether the scape is responsible for creating itself and its members, + * or other model components handle this. + * + * @param autoCreate + * if true calls createScape at construction, otherwise model is + * built manually + */ + public void setAutoCreate(boolean autoCreate) { + this.autoCreate = autoCreate; + } + + /** + * Is the scape populated when the scape is created? That is, is the populate + * scape method called when create scape is executed? (Typically, the populate + * scape method will fill each cell with clones of the prototype cell, but of + * course this behavior can be overidden.) True by default. + * + * @return true if the scape will be populated when scape create is called, + * false otherwise + */ + public boolean isPopulateOnCreate() { + return populateOnCreate; + } + + /** + * Sets wether the scape is responsible for populating itself. + * + * @param populateOnCreate + * if true calls createScape at construction, otherwise model is + * built manually + */ + public void setPopulateOnCreate(boolean populateOnCreate) { + this.populateOnCreate = populateOnCreate; + } + + /** + * Adds the specified stat collectors to this scape for automatic collection + * by the scape. If this scape is not allready collecting stats, implicitly + * sets collect stats to true. Adds the stats to the stat collection rule. + * + * @param stats + * the stat collectors to add to this scape. + */ + public void addStatCollectors(StatCollector[] stats) { + // stats added in collect values + // getData().addStatCollectors(StatCollectors); + + // loop through and give the stats references for their datagroup. + // must be done prior to getData().add(stats), because + // DataGroup.add(Stats) + // calls getAllDataSeries, which calls isCollectingLongitudinal, which + // uses the StatCollector's + // reference to its data group. + for (int i = 0; i < stats.length; i++) { + stats[i].setDataGroup(getRunner().getData()); + } + getRunner().getData().add(stats); + if (collectStats == null) { + setCollectStats(true); + } + collectStats.addStatCollectors(stats); + } + + /** + * Adds the specified stat collector iff and only if it hasn't allready been + * added. + * + * @param stat + * the stat collector to add to this scape. todo allow replacement + * (cuurent version only adds if a stat does not already exist.) + * possibly get rid of this once issue with multiple stat collectors + * is resolved. + */ + public StatCollector addStatCollectorIfNew(StatCollector stat) { + StatCollector foundStat = getRunner().getData().getStatCollector(stat.getName()); + if (foundStat == null) { + addStatCollector(stat); + foundStat = stat; + } + return foundStat; + } + + /** + * Adds the specified stat collector to this scape for automatic collection by + * the scape. If this scape is not allready collecting stats, implicitly sets + * collect stats to true. Adds the stat to the stat collection rule. + * + * @param stat + * the stat collector to add to this scape. + */ + public void addStatCollector(StatCollector stat) { + StatCollector[] stats = new StatCollector[1]; + stats[0] = stat; + addStatCollectors(stats); + } + + /** + * Returns the stat collectors currently calcualting stats for this scape. + */ + public StatCollector[] getStatCollectors() { + if (collectStats != null) { + return collectStats.getStatCollectors(); + } else { + throw new RuntimeException("Tried to get stats but they are not being collected"); + } + } + + /** + * Just a class for a delegated proxy for draw features. + */ + public class DrawFeatureObservable extends Observable implements Serializable { + + /** * */ - private static final long serialVersionUID = 1L; - - /** - * Have to provide this silly method because set changed is protected - * for some reason. - */ - public void setChanged() { - super.setChanged(); - } - }; - - /** - * A delegate keeping track of observers of draw features. - */ - private DrawFeatureObservable drawFeatureObservable = new DrawFeatureObservable(); - - /** - * Adds the provided draw feature to this scape. - * - * @see org.ascape.util.vis.awt.DrawFeature - */ - public void addDrawFeature(PlatformDrawFeature feature) { - // Simple linear search... - // todo, replace with hashmap mechanism - for (Iterator iterator = drawFeatures.iterator(); iterator.hasNext();) { - PlatformDrawFeature drawFeature = (PlatformDrawFeature) iterator.next(); - if (drawFeature.getName().equals(feature.getName())) { - // ignore, don't add feature with same name twice. - return; - } - } - drawFeatures.addElement(feature); - drawFeatureObservable.setChanged(); - drawFeatureObservable.notifyObservers(); - } - - /** - * Removes the provided draw feature. - * - * @param feature the draw feature to be removed - * @return returns true if successful. False, otherwise. - */ - public boolean removeDrawFeature(PlatformDrawFeature feature) { - PlatformDrawFeature found = null; - // todo, replace with hashmap mechanism - for (Iterator iterator = drawFeatures.iterator(); iterator.hasNext();) { - PlatformDrawFeature drawFeature = (PlatformDrawFeature) iterator.next(); - if (drawFeature.getName().equals(feature.getName())) { - found = feature; - } - } - if (found != null) { - drawFeatures.removeElement(found); - drawFeatureObservable.setChanged(); - drawFeatureObservable.notifyObservers(); - return true; - } else { - return false; - } - } - - /** - * Returns an observable delegate that notifies users of draw features that - * a change has occurred. If you need to know when a change in draw features - * occur, implement observer in the appropriate class and add it to the - * Observerable this method returns. - */ - public Observable getDrawFeaturesObservable() { - return drawFeatureObservable; - } - - /** - * Returns, as a vector, the draw features available for interpretation of - * members of this scape. - * - * @see org.ascape.util.vis.awt.DrawFeature - */ - public Vector getDrawFeatures() { - return drawFeatures; - } - - /** - * Returns the user environment for this scape. Returns null if no user - * environmnet exists; that is if we are running in a non graphic context. - * Otherwise, returns the same environmnet as getRuntimeEnvironment, except - * cast apprpriatly. - */ - public AbstractUIEnvironment getUIEnvironment() { - if (getRunner() != null && getRunner().getEnvironment() instanceof AbstractUIEnvironment) { - return (AbstractUIEnvironment) getRunner().getEnvironment(); - } else { - return null; - } - } - - /** - * Returns the runtime environment, if any, for this scape. - */ - public RuntimeEnvironment getEnvironment() { - return getRunner() != null ? getRunner().getEnvironment() : null; - } - - /** - * A class defining a rule causing the target scape to return all accessors. - */ - class RetrieveAccessorsRule extends Rule { - - /** + private static final long serialVersionUID = 1L; + + /** + * Have to provide this silly method because set changed is protected for + * some reason. + */ + public void setChanged() { + super.setChanged(); + } + }; + + /** + * A delegate keeping track of observers of draw features. + */ + private DrawFeatureObservable drawFeatureObservable = new DrawFeatureObservable(); + + /** + * Adds the provided draw feature to this scape. + * + * @see org.ascape.util.vis.awt.DrawFeature + */ + public void addDrawFeature(PlatformDrawFeature feature) { + // Simple linear search... + // todo, replace with hashmap mechanism + for (Iterator iterator = drawFeatures.iterator(); iterator.hasNext();) { + PlatformDrawFeature drawFeature = (PlatformDrawFeature) iterator.next(); + if (drawFeature.getName().equals(feature.getName())) { + // ignore, don't add feature with same name twice. + return; + } + } + drawFeatures.addElement(feature); + drawFeatureObservable.setChanged(); + drawFeatureObservable.notifyObservers(); + } + + /** + * Removes the provided draw feature. + * + * @param feature + * the draw feature to be removed + * @return returns true if successful. False, otherwise. + */ + public boolean removeDrawFeature(PlatformDrawFeature feature) { + PlatformDrawFeature found = null; + // todo, replace with hashmap mechanism + for (Iterator iterator = drawFeatures.iterator(); iterator.hasNext();) { + PlatformDrawFeature drawFeature = (PlatformDrawFeature) iterator.next(); + if (drawFeature.getName().equals(feature.getName())) { + found = feature; + } + } + if (found != null) { + drawFeatures.removeElement(found); + drawFeatureObservable.setChanged(); + drawFeatureObservable.notifyObservers(); + return true; + } else { + return false; + } + } + + /** + * Returns an observable delegate that notifies users of draw features that a + * change has occurred. If you need to know when a change in draw features + * occur, implement observer in the appropriate class and add it to the + * Observerable this method returns. + */ + public Observable getDrawFeaturesObservable() { + return drawFeatureObservable; + } + + /** + * Returns, as a vector, the draw features available for interpretation of + * members of this scape. + * + * @see org.ascape.util.vis.awt.DrawFeature + */ + public Vector getDrawFeatures() { + return drawFeatures; + } + + /** + * Returns the user environment for this scape. Returns null if no user + * environmnet exists; that is if we are running in a non graphic context. + * Otherwise, returns the same environmnet as getRuntimeEnvironment, except + * cast apprpriatly. + */ + public AbstractUIEnvironment getUIEnvironment() { + if (getRunner() != null && getRunner().getEnvironment() instanceof AbstractUIEnvironment) { + return (AbstractUIEnvironment) getRunner().getEnvironment(); + } else { + return null; + } + } + + /** + * Returns the runtime environment, if any, for this scape. + */ + public RuntimeEnvironment getEnvironment() { + return getRunner() != null ? getRunner().getEnvironment() : null; + } + + /** + * A class defining a rule causing the target scape to return all accessors. + */ + class RetrieveAccessorsRule extends Rule { + + /** * */ - private static final long serialVersionUID = 1L; - - List accessors; - - public RetrieveAccessorsRule() { - super("Retrieve Accessors Rule"); - } - - /** - * @param agent the target scape - */ - public void execute(Agent agent) { - try { - accessors.addAll(PropertyAccessor.determineReadWriteAccessors(this, Scape.class, false)); - } catch (IntrospectionException e) { - getEnvironment().getConsole().println( - "An introspection exception occured while trying to determine model properties: " - + e.getMessage()); - } - } - } - - /** - * A class defining a rule causing the target scape to search for all scapes - * and members scapes accessors. - */ - // class RetrieveAllAccessorsRule extends PropogateScapeOnly { - class RetrieveAllAccessorsRule extends Rule { - - /** + private static final long serialVersionUID = 1L; + + List accessors; + + public RetrieveAccessorsRule() { + super("Retrieve Accessors Rule"); + } + + /** + * @param agent + * the target scape + */ + public void execute(Agent agent) { + try { + accessors.addAll(PropertyAccessor.determineReadWriteAccessors(this, Scape.class, false)); + } catch (IntrospectionException e) { + getEnvironment().getConsole().println("An introspection exception occured while trying to determine model properties: " + e.getMessage()); + } + } + } + + /** + * A class defining a rule causing the target scape to search for all scapes + * and members scapes accessors. + */ + // class RetrieveAllAccessorsRule extends PropogateScapeOnly { + class RetrieveAllAccessorsRule extends Rule { + + /** * */ - private static final long serialVersionUID = 1L; - - List accessors; - - public RetrieveAllAccessorsRule() { - super("Retrieve Accessors Rule"); - accessors = new ArrayList(); - } - - /** - * @param agent the target scape - */ - public void execute(Agent agent) { - if (((Scape) agent).isMembersActive()) { - try { - accessors.addAll(PropertyAccessor.determineReadWriteAccessors(agent, Scape.class, false)); - } catch (IntrospectionException e) { - getEnvironment().getConsole().println( - "An introspection exception occured while trying to determine model properties: " - + e.getMessage()); - } - // super.execute(agent); - } - } - } - - /** - * A class defining a rule causing the target scape to search for all scapes - * and members scapes accessors. - */ - class RetrieveAllScapesRule extends PropogateScapeOnly { - - /** + private static final long serialVersionUID = 1L; + + List accessors; + + public RetrieveAllAccessorsRule() { + super("Retrieve Accessors Rule"); + accessors = new ArrayList(); + } + + /** + * @param agent + * the target scape + */ + public void execute(Agent agent) { + if (((Scape) agent).isMembersActive()) { + try { + accessors.addAll(PropertyAccessor.determineReadWriteAccessors(agent, Scape.class, false)); + } catch (IntrospectionException e) { + getEnvironment().getConsole().println("An introspection exception occured while trying to determine model properties: " + e.getMessage()); + } + // super.execute(agent); + } + } + } + + /** + * A class defining a rule causing the target scape to search for all scapes + * and members scapes accessors. + */ + class RetrieveAllScapesRule extends PropogateScapeOnly { + + /** * */ - private static final long serialVersionUID = 1L; - - Vector scapes; - - public RetrieveAllScapesRule() { - super("Retrieve Scapes Rule"); - scapes = new Vector(); - } - - /** - * @param agent the target scape - */ - public void execute(Agent agent) { - scapes.addElement(agent); - super.execute(agent); - } - } - - /** - * Returns all property accessors for this scape and recursivly for all - * member scapes of this scape. - */ - private List retrieveAllAccessorsBase() { - RetrieveAllAccessorsRule retrieveRule = new RetrieveAllAccessorsRule(); - executeOnRoot(retrieveRule); - retrieveRule.accessors.add(new PropertyAccessor(this, "RandomSeed")); - retrieveRule.accessors.add(new PropertyAccessor(this, "StartPeriod")); - retrieveRule.accessors.add(new PropertyAccessor(this, "StopPeriod")); - retrieveRule.accessors.add(new PropertyAccessor(this, "PausePeriod")); - retrieveRule.accessors.add(new PropertyAccessor(this, "ThreadCount")); - return retrieveRule.accessors; - } - - /** - * Returns all property accessors for this scape and recursivly for all - * member scapes of this scape. - */ - public List retrieveAllAccessors() { - List accessors = retrieveAllAccessorsBase(); - return accessors; - } - - public final static Comparator COMPARE_ORDERED_QUALIFIERS = new Comparator() { - public int compare(Object o1, Object o2) { - return Utility.orderedQualifiers(((PropertyAccessor) o1).getLongName()).compareTo( - Utility.orderedQualifiers(((PropertyAccessor) o2).getLongName())); - } - }; - - /** - * Returns all property accessors for this scape and recursivly for all - * member scapes of this scape. - */ - public List retrieveAllAccessorsOrdered() { - List accessors = retrieveAllAccessorsBase(); - Collections.sort(accessors, COMPARE_ORDERED_QUALIFIERS); - return accessors; - } - - /** - * Returns all property accessors for this scape (excluding - * inappropriate/disabled accessors such as size) and recursivly for all - * member scapes of this scape. Can be overriden to only include those - * accessors that should be included in model definitions. - */ - public List retrieveModelAccessorsOrdered() { - List accessors = retrieveAllAccessorsOrdered(); - for (Iterator iterator = accessors.iterator(); iterator.hasNext();) { - PropertyAccessor accessor = (PropertyAccessor) iterator.next(); - if (accessor.getName().equalsIgnoreCase("size")) { - iterator.remove(); - } - } - return accessors; - } - - /** - * Returns all scapes that are composed with this scape. All subscapes, - * parent scapes, and subscapes of parent scapes (more simply, the root - * scape and all of its subscapes) are returned. - */ - public List getAllScapes() { - RetrieveAllScapesRule retrieveRule = new RetrieveAllScapesRule(); - executeOnRoot(retrieveRule); - return retrieveRule.scapes; - } - - /** - * Does the scape view itself? True by default for root scape when - * createViews is used, false otherwise. - */ - public boolean isViewSelf() { - return selfView != null; - } - - /** - * Sets wether the scape is a view of itself. True by default for root scape - * whn createViews is used, false otherwise. Not extensively tested yet. - * - * @param viewSelf should the scape view itself. - */ - public void setViewSelf(boolean viewSelf) { - if (viewSelf && !isViewSelf()) { - createSelfView(); - } else if (!viewSelf && isViewSelf()) { - removeScapeListener(selfView); - } - } - - /** - * Makes the scape a view of itself. - */ - public void createSelfView() { - selfView = new ScapeListenerDelegate(this) { - /** + private static final long serialVersionUID = 1L; + + Vector scapes; + + public RetrieveAllScapesRule() { + super("Retrieve Scapes Rule"); + scapes = new Vector(); + } + + /** + * @param agent + * the target scape + */ + public void execute(Agent agent) { + scapes.addElement(agent); + super.execute(agent); + } + } + + /** + * Returns all property accessors for this scape and recursivly for all member + * scapes of this scape. + */ + private List retrieveAllAccessorsBase() { + RetrieveAllAccessorsRule retrieveRule = new RetrieveAllAccessorsRule(); + executeOnRoot(retrieveRule); + retrieveRule.accessors.add(new PropertyAccessor(this, "RandomSeed")); + retrieveRule.accessors.add(new PropertyAccessor(this, "StartPeriod")); + retrieveRule.accessors.add(new PropertyAccessor(this, "StopPeriod")); + retrieveRule.accessors.add(new PropertyAccessor(this, "PausePeriod")); + retrieveRule.accessors.add(new PropertyAccessor(this, "ThreadCount")); + return retrieveRule.accessors; + } + + /** + * Returns all property accessors for this scape and recursivly for all member + * scapes of this scape. + */ + public List retrieveAllAccessors() { + List accessors = retrieveAllAccessorsBase(); + return accessors; + } + + public final static Comparator COMPARE_ORDERED_QUALIFIERS = new Comparator() { + public int compare(Object o1, Object o2) { + return Utility.orderedQualifiers(((PropertyAccessor) o1).getLongName()).compareTo(Utility.orderedQualifiers(((PropertyAccessor) o2).getLongName())); + } + }; + + /** + * Returns all property accessors for this scape and recursivly for all member + * scapes of this scape. + */ + public List retrieveAllAccessorsOrdered() { + List accessors = retrieveAllAccessorsBase(); + Collections.sort(accessors, COMPARE_ORDERED_QUALIFIERS); + return accessors; + } + + /** + * Returns all property accessors for this scape (excluding + * inappropriate/disabled accessors such as size) and recursivly for all + * member scapes of this scape. Can be overriden to only include those + * accessors that should be included in model definitions. + */ + public List retrieveModelAccessorsOrdered() { + List accessors = retrieveAllAccessorsOrdered(); + for (Iterator iterator = accessors.iterator(); iterator.hasNext();) { + PropertyAccessor accessor = (PropertyAccessor) iterator.next(); + if (accessor.getName().equalsIgnoreCase("size")) { + iterator.remove(); + } + } + return accessors; + } + + /** + * Returns all scapes that are composed with this scape. All subscapes, parent + * scapes, and subscapes of parent scapes (more simply, the root scape and all + * of its subscapes) are returned. + */ + public List getAllScapes() { + RetrieveAllScapesRule retrieveRule = new RetrieveAllScapesRule(); + executeOnRoot(retrieveRule); + return retrieveRule.scapes; + } + + /** + * Does the scape view itself? True by default for root scape when createViews + * is used, false otherwise. + */ + public boolean isViewSelf() { + return selfView != null; + } + + /** + * Sets wether the scape is a view of itself. True by default for root scape + * whn createViews is used, false otherwise. Not extensively tested yet. + * + * @param viewSelf + * should the scape view itself. + */ + public void setViewSelf(boolean viewSelf) { + if (viewSelf && !isViewSelf()) { + createSelfView(); + } else if (!viewSelf && isViewSelf()) { + removeScapeListener(selfView); + } + } + + /** + * Makes the scape a view of itself. + */ + public void createSelfView() { + selfView = new ScapeListenerDelegate(this) { + /** * */ - private static final long serialVersionUID = 1L; - - public String getName() { - return this + " Self-View"; - } - }; - addView(selfView); - } - - /** - * Constructs the views for this scape. If display graphics is set to true, - * calls create graphic views. Calls create nongraphic views in either case. - * Override to create views for the scape. Alternativly, override the - * createGraphicsViews and createNonGraphicViews methods to create views - * appropriate for the current operating mode. This method does NOT get - * called when a model is deserialized, but createGraphicViews does. - */ - public void createViews() { - if (isRoot() && getRunner().getEnvironment() == null) { - getRunner().createEnvironment(); - addView(getRunner().getEnvironment()); - } - createNonGraphicViews(); - if (Runner.isDisplayGraphics() || Runner.isServeGraphics()) { - // SwingUtilities.invokeLater(new Runnable() { - // public void run() { - createGraphicViews(); - // } - // }); - } - } - - /** - * Override to create any graphical views for the scape. This method will - * not be called when display graphics is set to false, and so is a good - * place to put any user interface only views. If root, will setup the user - * interface environment and add an auto customizer. - */ - public void createGraphicViews() { - } - - /** - * Overide to create and non-graphical views for the scape. If root, will - * automatically create control and counter views, create a self view, add a - * standand output view. - */ - public void createNonGraphicViews() { - if (isRoot()) { - createSelfView(); - getEnvironment().getConsole().println("Ascape Model: " + getName()); - getEnvironment().getConsole().println(getDescription()); - } - } - - /* - * public PropertyAccessor[] retrieveAccessors(PropertyAccessor[] accessors) - * throws IntrospectionException { return - * retrieveAccessors(PropertyAccessor.determineAccessors(this, - * Model.class)); } - */ - - /** - * If the scape has delegated a view to itself, called each time a scape - * sends a "initialize" event, indicating it has been initialized. Normally - * wouldn't use in this context. - */ - public void scapeInitialized(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time a scape - * sends a "setup" method, indicating it needs to be setup for a run. - * Possible uses include setting initial vector extents, responding to - * changes in user settings, and changing parameters systematically. (A view - * delegate to the scape is automatically created for root scapes when the - * standard model implementation is used.) - * - * @param scapeEvent the associated scape event - */ - public void scapeSetup(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time the scape - * is iterated. (A view delegate to the scape is automatically created for - * root scapes when the standard model implementation is used.) - * - * @param scapeEvent the associated scape event - */ - public void scapeIterated(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time the scape - * is started. (A view delegate to the scape is automatically created for - * root scapes when the standard model implementation is used.) - * - * @param scapeEvent the associated scape event - */ - public void scapeStarted(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time the scape - * is stopped. (A view delegate to the scape is automatically created for - * root scapes when the standard model implementation is used.) - * - * @param scapeEvent the associated scape event - */ - public void scapeStopped(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time the scape - * is updated. (A view delegate to the scape is automatically created for - * root scapes when the standard model implementation is used.) - * - * @param scapeEvent the associated scape event - */ - public void scapeNotification(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time a scape - * sends a "closing" event. Normally wouldn't use in this context. - * - * @param scapeEvent the associated scape event - */ - public void scapeClosing(ScapeEvent scapeEvent) { - } - - /** - * Method called as the entire envornmnet is about to be exited. - * - * @param scapeEvent the associated scape event - */ - public void environmentQuiting(ScapeEvent scapeEvent) { - } - - /** - * If the scape has delegated a view to itself, called each time a scape - * sends a "deserialized" event. - * - * @param scapeEvent the associated scape event - */ - public void scapeDeserialized(ScapeEvent scapeEvent) { - if (Runner.isDisplayGraphics()) { - if (isRoot()) { - getRunner().createEnvironment(); - addView(getRunner().getEnvironment()); - } - } - } - - /** - * Add a scape to this listener. Just here to fulfill the scape listener - * contract. - * - * @param scapeEvent the associated scape event - */ - public void scapeAdded(ScapeEvent scapeEvent) throws TooManyListenersException { - } - - /** - * Notifies the listener that the scape has removed it. Just here to fulfill - * the scape listener contract. - * - * @param scapeEvent the associated scape event - */ - public void scapeRemoved(ScapeEvent scapeEvent) { - } - - /** - * Returns false the scape is not a graphical user interface component. - */ - public boolean isGraphic() { - return false; - } - - /** - * Returns true (default) if the listener is intended to be used only for - * the current scape; certainly true in this case. - */ - public boolean isLifeOfScape() { - return true; - } - - /* - * Returns the ascape home directory. public static String getAscapeHome() { - * //return System.getProperty("ascape.home", "D:/"); return ""; } - */ - - /** - * Save the state of the scape to a file. - */ - public void save(File file) throws IOException { - OutputStream os = new FileOutputStream(file); - save(os); - } - - /** - * Save the state of the scape to an output stream. - */ - public void save(OutputStream os) throws IOException { - if (!isSerializable()) { - throw new RuntimeException("Tried to save a model that is not serializable."); - } - - getRunner().setInternalRunning(false); - - GZIPOutputStream gzos = new GZIPOutputStream(os); - ObjectOutputStream oos = new ObjectOutputStream(gzos); - - // remove the customizer and any environment (non-scape specific) views - boolean needToAddCustomizer = false; - if (getUIEnvironment() != null && getUIEnvironment().getCustomizer() != null - && isScapeListener(getUIEnvironment().getCustomizer())) { - removeScapeListener(getUIEnvironment().getCustomizer()); - needToAddCustomizer = true; - } - for (int i = 0; i < getEnvironment().getEnvironmentViews().size(); i++) { - ScapeListener l = (ScapeListener) getEnvironment().getEnvironmentViews().get(i); - l.getScape().removeScapeListener(l); - } - - try { - oos.writeObject(this); - } catch (StackOverflowError e) { - e.printStackTrace(); - System.err.println(""); - System.err.println("************************************"); - throw new RuntimeException("PLEASE INCREASE STACK SIZE, e.g. by using java's -Xss command line paramter."); - } - oos.close(); - - // reconnect the customizer and any environment (non-scape specific) - // views - if (needToAddCustomizer) { - addScapeListener(getUIEnvironment().getCustomizer()); - } - for (int i = 0; i < getEnvironment().getEnvironmentViews().size(); i++) { - ScapeListener l = (ScapeListener) getEnvironment().getEnvironmentViews().get(i); - addView(l, false); - } - - // we set startPeriod to scape.period + 1 so that there is not a blank - // first point in the charts - try { - setStartPeriod(getPeriod() + 1); - } catch (SpatialTemporalException e) { - try { - setStartPeriod(getPeriod()); - } catch (SpatialTemporalException e1) { - try { - setStartPeriod(getPeriod()); - } catch (SpatialTemporalException e2) { - throw new RuntimeException("Internal Error"); - } - } - } - getRunner().setInternalRunning(true); - } - - private void writeObject(final java.io.ObjectOutputStream out) throws IOException { - - if (getRunner().getData() != null) { - getRunner().getData().getPeriods().clear(); - } - - getRunner().write(out); - } - - /** - * Sets values for the models paramters based on supplied array of key value - * pairs. Paramters and values are expected to be seperated with an "=", for - * example: "MyParameter=12". - * - * @param args an array of strings with paramter-value paris in the form - * "{paramter-name}={paramter-value}" - * @param reportNotFound if paramters not found should result in a console - * notification and if errors in invocation should be reported, - * false otherwise - */ - public void assignParameters(String[] args, boolean reportNotFound) { - // List allAccessors = retrieveAllAccessors(); - List allAccessors = null; - try { - allAccessors = PropertyAccessor.determineReadWriteAccessors(this, Scape.class, false); - } catch (IntrospectionException e) { - throw new RuntimeException(e); - } - - for (String arg : args) { - String paramName = PropertyAccessor.paramName(arg); - if (paramName != null) { - boolean found = false; - String paramValue = PropertyAccessor.paramValue(arg); - for (Iterator iterator = allAccessors.iterator(); iterator.hasNext();) { - PropertyAccessor accessor = (PropertyAccessor) iterator.next(); - if (accessor.getName().equalsIgnoreCase(paramName)) { - try { - accessor.setAsText(paramValue); - } catch (InvocationTargetException e) { - if (reportNotFound) { - throw new RuntimeException("Exception in called method: " + e.getTargetException()); - } - // Else ignore, its ok if there is a problem calling - // the method at this point - } - found = true; - } - } - - found = found || Runner.assignEnvironmentParameter(paramName, paramValue); - if (!found) { - if (paramName.equalsIgnoreCase("RandomSeed")) { - try { - setRandomSeed(PropertyAccessor.paramValueLong(arg)); - } catch (NumberFormatException e) { - getEnvironment().getConsole().println("Couldn't decode random seed value: " + paramValue); - } - found = true; - } else if (paramName.equalsIgnoreCase("StopPeriod")) { - try { - setStopPeriod(PropertyAccessor.paramValueInt(arg)); - found = true; - } catch (SpatialTemporalException e) { - e.printStackTrace(); // To change body of catch - // statement use File | Settings - // | File Templates. - } - } else if (paramName.equalsIgnoreCase("PausePeriod")) { - setPausePeriod(PropertyAccessor.paramValueInt(arg)); - found = true; - } else if (paramName.equalsIgnoreCase("AutoRestart")) { - setAutoRestart(PropertyAccessor.paramValueBoolean(arg)); - found = true; - } - } - if (!found && reportNotFound) { - getEnvironment().getConsole().println( - "***WARNING: Parameter not found: " + paramName + " in " + getName()); - } - } - } - } - - public void createViews(String[] args) { - if (args != null) { - for (String arg : args) { - if (PropertyAccessor.paramName(arg).equals("view")) { - ScapeListener newView = - (ScapeListener) getRunner().instanceFromName(PropertyAccessor.paramValue(arg)); - if (newView != null) { - addView(newView); - } - } - } - } - } - - /** - * Sets values for the models paramters based on supplied array of key value - * pairs, reporting if any of the keys (parameter names) are not found. - * Paramters and values are expected to be seperated with an "=", for - * example: "MyParameter=12". - * - * @param args an array of strings with paramter-value paris in the form - * "{paramter-name}={paramter-value}" - */ - public void assignParameters(String[] args) { - assignParameters(args, true); - } - - /** - * Moves an agent toward the specified agent. - * - * @param origin the agent moving - * @param target the agent's target - * @param distance the distance to move - */ - public final void moveAway(LocatedAgent origin, Coordinate target, double distance) { - getSpace().moveAway(origin, target, distance); - } - - /** - * Moves an agent toward the specified agent. It is an error to call this - * method on collections (and discrete discrete scapes not composed of - * HostCells. - * - * @param origin the agent moving - * @param target the agent's target - * @param distance the distance to move - */ - public final void moveToward(LocatedAgent origin, Coordinate target, double distance) { - getSpace().moveToward(origin, target, distance); - } - - /** - * Returns the shortest distance between one agent and another. - * - * @param origin the starting agent - * @param target the ending agent - */ - public double calculateDistance(LocatedAgent origin, LocatedAgent target) { - return calculateDistance(origin.getCoordinate(), target.getCoordinate()); - } - - /** - * Returns the shortest distance between one LocatedAgent and another. - * Warning: this default method only returns a coordinate specific distance. - * It uses no information about the scape context; for example wether it is - * a periodic (wrapping) space or not. Therefore, if you implement your own - * versions of Scape, ensure that you have properly implemented a version of - * this method. (All Ascape Scape collections properly overide this method.) - * - * @param origin one LocatedAgent - * @param target another LocatedAgent - */ - public final double calculateDistance(Coordinate origin, Coordinate target) { - return getSpace().calculateDistance(origin, target); - } - - public class ConditionalIterator implements Iterator { - - Iterator iter; - - Conditional condition; - - Object next; - - public ConditionalIterator(Iterator iter, Conditional condition) { - ConditionalIterator.this.iter = iter; - ConditionalIterator.this.condition = condition; - loadNext(); - } - - private void loadNext() { - next = null; - while (iter.hasNext() && next == null) { - Object o = iter.next(); - if (condition.meetsCondition(o)) { - next = o; - } - } - } - - public boolean hasNext() { - return next != null; - } - - public Object next() { - if (next != null) { - Object currentNext = next; - loadNext(); - return currentNext; - } else { - throw new NoSuchElementException(); - } - } - - public void remove() { - throw new UnsupportedOperationException("Can't remove from a conditional iterator."); - } - } - - /** - * Find the maximum cell of some data point. If multiple points have the - * same value, returns a random instance at that value. - * - * @param condition - * @return - */ - public final List find(Conditional condition) { - return space.find(condition); - } - - /** - * Find the maximum cell of some data point. If multiple points have the - * same value, returns a random instance at that value. - * - * @param iter - * @param dataPoint - * @return - */ - public final LocatedAgent findMaximum(final Iterator iter, DataPoint dataPoint) { - // The code is written in such a way that there will not be the cost of - // Array creation if only one maximum exists - ArrayList multipleMaxObjects = null; - double maxValue = -Double.MAX_VALUE; - LocatedAgent maxObject = null; - while (iter.hasNext()) { - Object next = iter.next(); - if (dataPoint.getValue(next) > maxValue) { - maxValue = dataPoint.getValue(next); - maxObject = (LocatedAgent) next; - multipleMaxObjects = null; - } - // Awaiting decision to become depndent on 1.4 - // else if (Double.compare(dataPoint.getValue(next), maxValue) == 0) - // { - else if (DataPointConcrete.equals(dataPoint.getValue(next), maxValue)) { - if (multipleMaxObjects == null) { - multipleMaxObjects = new ArrayList(); - multipleMaxObjects.add(maxObject); - } - multipleMaxObjects.add(next); - } - } - if (multipleMaxObjects == null) { - return maxObject; - } else { - return (LocatedAgent) multipleMaxObjects.get(randomToLimit(multipleMaxObjects.size())); - } - } - - public final LocatedAgent findMinimumWithin(Coordinate coordinate, DataPoint dataPoint, Conditional condition, - boolean includeSelf, double distance) { - return (LocatedAgent) getSpace().findMinimumWithin(coordinate, dataPoint, condition, includeSelf, distance); - } - - public final LocatedAgent findMaximumWithin(Coordinate coordinate, DataPoint dataPoint, Conditional condition, - boolean includeSelf, double distance) { - return (LocatedAgent) getSpace().findMaximumWithin(coordinate, dataPoint, condition, includeSelf, distance); - } - - public final LocatedAgent findMinimum(final Iterator iter, DataPoint dataPoint) { - // The code is written in such a way that there will not be the cost of - // Array creation if only one minimum exists - ArrayList multipleMinObjects = null; - double minValue = Double.MAX_VALUE; - LocatedAgent minObject = null; - while (iter.hasNext()) { - Object next = iter.next(); - if (dataPoint.getValue(next) < minValue) { - minValue = dataPoint.getValue(next); - minObject = (LocatedAgent) next; - multipleMinObjects = null; - } - // Awaiting decision to become depndent on 1.4 - // else if (Double.compare(dataPoint.getValue(next), minValue) == 0) - // { - else if (DataPointConcrete.equals(dataPoint.getValue(next), minValue)) { - if (multipleMinObjects == null) { - multipleMinObjects = new ArrayList(); - multipleMinObjects.add(minObject); - } - multipleMinObjects.add(next); - } - } - if (multipleMinObjects == null) { - return minObject; - } else { - return (LocatedAgent) multipleMinObjects.get(randomToLimit(multipleMinObjects.size())); - } - } - - /** - * Returns an iteration across all agents the specified distance from the - * origin. - * - * @param origin the starting cell - * @param includeSelf should the origin be included - * @param distance the distance agents must be within to be included - */ - public final Iterator withinIterator(final Coordinate origin, Conditional condition, boolean includeSelf, - final double distance) { - return getSpace().withinIterator(origin, condition, includeSelf, distance); - } - - /** - * Returns the agent with the minimum value. - * - * @param point the data point to use to make the comparison for minimum - */ - public LocatedAgent findMinimum(DataPoint point) { - return findMinimum(iterator(), point); - } - - /** - * Returns the agent with the maximum value. - * - * @param point the data point to use to make the comparison for maximum - */ - public LocatedAgent findMaximum(DataPoint point) { - return findMaximum(iterator(), point); - } - - /** - * The strategy that will be used to execute rules across this scape. - */ - private ExecutionStrategy strategy; - - private static int threadCount = 1; - - /** - * Finds the nearest agent that meets some condition. Scapes without - * coordinate meaing should override this method. - * - * @param origin the coordinate to find agents near - * @param condition the condition that found agent must meet - * @param includeOrigin if the origin should be included - * @param distance the maximum distance around the origin to look - */ - public final LocatedAgent findNearest(final Coordinate origin, Conditional condition, boolean includeOrigin, - double distance) { - return (LocatedAgent) getSpace().findNearest(origin, condition, includeOrigin, distance); - } - - /** - * Returns a coordinate randomly selected from the collection's space. - */ - public final Coordinate findRandomCoordinate() { - return getSpace().findRandomCoordinate(); - } - - /** - * Returns all agents within the specified distance of the agent. - * - * @param origin the coordinate at the center of the search - * @param includeSelf whether or not the starting agent should be included - * in the search - * @param distance the distance agents must be within to be included - */ - public final List findWithin(Coordinate origin, Conditional condition, boolean includeSelf, double distance) { - return getSpace().findWithin(origin, condition, includeSelf, distance); - } - - /** - * Returns the number of agents within the specified distance of the agent - * that meet some condition. - * - * @param origin the coordinate at the center of the search - * @param condition the condition the agent must meet to be included - * @param distance the distance agents must be within to be included - */ - public final int countWithin(Coordinate origin, Conditional condition, boolean includeSelf, double distance) { - return getSpace().countWithin(origin, condition, includeSelf, distance); - } - - /** - * Returns if there are agents within the specified distance of the origin - * that meet some Condition. - * - * @param origin the coordinate at the center of the search - * @param condition the condition the agent must meet to be included - * @param distance the distance agents must be within to be included - */ - public final boolean hasWithin(final Coordinate origin, Conditional condition, boolean includeSelf, double distance) { - return getSpace().hasWithin(origin, condition, includeSelf, distance); - } - - public final boolean isMutable() { - return getSpace().isMutable(); - } - - /** - * Returns a string composed of descriptions of the contents. - */ - public String contentsToString() { - String contents = ""; - for (Iterator iterator = space.iterator(); iterator.hasNext();) { - Agent agent = (Agent) iterator.next(); - contents = contents + agent.toString(); - if (iterator.hasNext()) { - contents = contents + ", "; - } - } - return contents; - } - - /** - * Returns a string representation of this scape. - */ - public String toString() { - if (name != null) { - return name; - } else { - if (isRoot()) { - return "Root Scape"; - } else if (prototypeAgent != null) { - return "Scape of " + prototypeAgent.toString() + "(s)"; - } else { - return "Scape of agents of unspecified type"; - } - } - } - - public boolean isSerializable() { - return serializable; - } - - public void setSerializable(boolean serializable) { - this.serializable = serializable; - } - - /** - * Overides the clone method to do a deep clone of member state so that such - * state will not be shared between scapes. - */ - public Object clone() { - Scape clone = (Scape) super.clone(); - clone.scapeListeners = new ArrayList(); - for (Iterator iter = scapeListeners.iterator(); iter.hasNext();) { - ScapeListener thisListener = (ScapeListener) iter.next(); - try { - ScapeListener newListener = (ScapeListener) thisListener.clone(); - removeScapeListener(newListener); - newListener.scapeRemoved(new ScapeEvent(this, ScapeEvent.REPORT_REMOVED)); - newListener.scapeAdded(new ScapeEvent(clone, ScapeEvent.REPORT_ADDED)); - clone.addScapeListener(newListener); - } catch (TooManyListenersException e) { - throw new RuntimeException("Internal error in Scape.clone " + e); - } - } - if (prototypeAgent != null) { - clone.prototypeAgent = (Agent) prototypeAgent.clone(); - } - if (rules != null) { - clone.rules = (VectorSelection) rules.clone(); - } - if (initialRules != null) { - clone.initialRules = (VectorSelection) initialRules.clone(); - } - clone.drawFeatures = (Vector) drawFeatures.clone(); - clone.space = (CollectionSpace) space.clone(); - return clone; - } - - /** - * Returns an agent randomly selected from the collection. If no agents - * exist, returns null. - */ - public final LocatedAgent findRandom() { - return (LocatedAgent) getSpace().findRandom(); - } - - /** - * Returns a random unoccupied discrete location in the space given with the - * lattice. - * - * @param excludeAgent a cell to exclude from get (typically origin) - */ - public Agent findRandom(Location excludeAgent) { - return (LocatedAgent) getSpace().findRandom(excludeAgent); - } - - /** - * Returns an agent randomly that matches a condition. Note: If there are no - * agents in the collection that meet the condition, the method returns - * null. - * - * @param condition the condition that must be matched - */ - public final Agent findRandom(Conditional condition) { - return (LocatedAgent) getSpace().findRandom(condition); - } - - /** - * Creates a new agent in this collection by cloning the prototype agent, - * adding it in an arbitrary place (typically at the end of a list), and - * initializing it. - */ - public synchronized Agent newAgent() { - return newAgent(false); - } - - /** - * Creates a new agent in this collection by cloning the prototype agent, - * adding it to a random or arbitrary (last in most cases) place in the - * collection, and initializing it. - * - * @param randomLocation should the agent be placed in a random location, or - * in an arbitrary location? - */ - public synchronized Agent newAgent(boolean randomLocation) { - Agent newAgent = (LocatedAgent) getSpace().newLocation(randomLocation); - List agents = new LinkedList(); - agents.add(newAgent); - execute(getInitialRules().getVector(), agents); - return newAgent; - } - - /** - * Returns the number of agents in the scape. - * - * @return the number of agents in the scape - */ - public int size() { - return getSize(); - } - - /** - * Are there no agents in this scape? - * - * @return true if the scape is empty - */ - public final boolean isEmpty() { - return getSpace().isEmpty(); - } - - /** - * Returns true if the scape collection contains the object (agent.) - * - * @param o the agent to search for - * @return true if the scape contains the agent - */ - public final boolean contains(Object o) { - return getSpace().contains(o); - } - - /** - * Returns an array containing all of the elements in this collection in - * proper sequence. Obeys the general contract of the - * <tt>Collection.toArray</tt> method. - * - * @return an array containing all of the elements in this collection in - * proper sequence. - * @see java.util.Arrays#asList(java.lang.Object[]) - */ - public final Object[] toArray() { - return getSpace().toArray(); - } - - /** - * Returns an array containing the current agents in this scape; the runtime - * type is specified by the passed array. - * - * @param a the array to copy the agents to - * @return an array containing the agents - * @throws java.lang.ArrayStoreException if the runtime type of the - * specified array doesn't match all agents - */ - public final Object[] toArray(Object a[]) { - return getSpace().toArray(a); - } - - /** - * Returns true if this collection contains all of agents in the specified - * collection. - * - * @param c collection of agents to be found in the scape - * @return true if this scape contains all of the agents in the collection - */ - public final boolean containsAll(Collection c) { - return getSpace().containsAll(c); - } - - /** - * Adds all of the agent in the specified collection to the end of the - * scape. Assumes (but does not check) that all of the elements are - * instances of agent. - * - * @param c collection whose agents are to be added to the scape - * @return true if the scape had new agents added - */ - public final boolean addAll(Collection c) { - return getSpace().addAll(c); - } - - /** - * Removes all of the agnets contained in the collection. No attempt is made - * to cache the removal; the agents are all removed at once. - * - * @param c collection whose agents are to be added to the scape - * @return true if the scape had agents (but not neccessarily all?) removed - */ - public final boolean removeAll(Collection c) { - return getSpace().removeAll(c); - } - - /** - * Retains only the elements in the scape that are in the specified - * collection. - * - * @param c collection whose agents are to be retained in the scape - * @return true if this scape had agents removed - */ - public final boolean retainAll(Collection c) { - return getSpace().retainAll(c); - } - - /** - * Removes all agents from the scape. - */ - public final void clear() { - getSpace().clear(); - } - - /** - * Adds the supplied object (agent) to this collection. - */ - public boolean add(Object a) { - return add(a, true); - } - - /** - * Adds the supplied object (assumed to be an agent) to this collection. The - * object is assumed to be an agent, though that behavior may be loosened at - * some point. - * - * @param agent the agent to add - * @param isParent should this scape be made the parent scape of the agent? - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public final boolean add(Object agent, boolean isParent) { - if (!(agent instanceof Agent)) { - // This may change at some point.. - throw new ClassCastException("Scape collections expect Agents only."); - } - boolean success = getSpace().add(agent, isParent); - if (isParent) { - ((Agent) agent).setScape(this); - } - return success; - } - - /** - * Adds the supplied object (agent) to this collection. - * - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public final void add(int index, Object a) { - add(index, a, true); - } - - /** - * Adds the supplied object (assumed to be an agent) to this collection. The - * object is assumed to be an agent, though that behavior may be loosened at - * some point. - * - * @param o the agent to add - * @param isParent should this scape be made the parent scape of the agent? - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public final void add(int index, Object o, boolean isParent) { - try { - ((ListSpace) getSpace()).add(index, o, isParent); - if (isParent) { - ((Agent) o).setScape(this); - } - } catch (ClassCastException e) { - throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); - } - } - - /** - * Removes the supplied object (agent) from this collection. - * - * @param o the agent to be removed - * @return true if the agent was deleted, false otherwise - */ - public boolean remove(Object o) { - return space.remove(o); - } - - /** - * Removes the object at the index from this collection. - * - * @param index the index for the agent to remove - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public final Object remove(int index) { - try { - return ((List) getSpace()).remove(index); - } catch (ClassCastException e) { - throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); - } - } - - /** - * Returns the cell existing at the specified coordinate. - */ - public final LocatedAgent get(Coordinate coordinate) { - return (LocatedAgent) getSpace().get(coordinate); - } - - /** - * Sets the agent at the specified coordinate to the supplied agent. - * - * @param coordinate the coordinate to add the agent at - * @param agent the agent to add - */ - public final void set(Coordinate coordinate, LocatedAgent agent, boolean isParent) { - getSpace().set(coordinate, agent); - if (isParent) { - agent.setScape(getScape()); - agent.setCoordinate(coordinate); - } - } - - /** - * Sets the agent at the specified coordinate to the supplied agent. - * - * @param coordinate the coordinate to add the agent at - * @param agent the agent to add - */ - public void set(Coordinate coordinate, LocatedAgent agent) { - set(coordinate, agent, true); - } - - /** - * Returns the cell existing at the specified location. Convenience method. - * - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public final Object get(int index) { - try { - return ((ListBase) getSpace()).get(index); - } catch (ClassCastException e) { - throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); - } - } - - /** - * Sets the specified location to the provided agent. Convenience method. - * - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public void set(int index, Object agent) { - set(index, agent, true); - } - - /** - * Sets the specified location to the provided agent. Convenience method. - * - * @throws UnsupportedOperationException if this scape's space is not a - * list. - */ - public void set(int index, Object agent, boolean isParent) { - try { - ((ListBase) getSpace()).set(index, (LocatedAgent) agent, isParent); - if (isParent) { - ((Agent) agent).setScape(this); - } - } catch (ClassCastException e) { - throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); - } - } - - public final ResetableIterator scapeIterator() { - return getSpace().safeIterator(); - } - - public final RandomIterator scapeRandomIterator() { - return getSpace().safeRandomIterator(); - } - - protected final ResetableIterator scapeIterator(int start, int limit) { - return getSpace().safeIterator(start, limit); - } - - public boolean isPeriodic() { - return getSpace().isPeriodic(); - } - - public void setPeriodic(boolean periodic) { - getSpace().setPeriodic(periodic); - } - - public Scape getSuperScape() { - return getSpace() instanceof SubSpace ? (Scape) ((SubSpace) getSpace()).getSuperSpace().getContext() : null; - } - - public void setSuperScape(Scape superScape) { - try { - ((SubSpace) getSpace()).setSuperSpace(superScape.getSpace()); - } catch (ClassCastException e) { - throw new UnsupportedOperationException("Underlying scape is no a SubSpace."); - } - } - - /** - * Returns multiple independently thread safe scape iterators across all - * agents in this scape. - * - * @return an iterator over the agents in scape order - */ - public final ResetableIterator[] scapeIterators(int count) { - return getSpace().safeIterators(count); - } - - public boolean isListenersAndMembersCurrent() { - return listenersAndMembersCurrent; - } - - public final Space getSpace() { - return space; - } - - public void setSpace(Space space) { - this.space = space; - // todo remove circular dependency - space.setContext(this); - space.setRandom(getRandom()); - } - - /** - * Sets the size of the collection, filling with clones of prototype agent. - * It is an error to set size while a scape is running. - * - * @param size a coordinate describing the size of this scape - */ - public void setSize(int size) { - if (runner != null && runner.isRunning()) { - throw new RuntimeException("Tried to set size while scape was running"); - } - space.setSize(size); - } - - public int getThreadCount() { - return Scape.threadCount; - } - - public void setThreadCount(int threadCount) { - Scape.threadCount = threadCount; - } - - public Location getPrototype() { - return (Location) getPrototypeAgent(); - } - - public boolean isHome(Location a) { - return ((Agent) a).getScape() == this; - } - - /** - * Convenience method for obtaining sata for current run. - * - * @return the Runner's Data Group. - */ - public DataGroup getData() { - return getRunner().getData(); - } - - /** - * Returns the UI Environment. - * - * @return a UI environment appropriate for given UI. - * @deprecated retained for backward compatability, please use - * #getUIEnvironment instead. - */ - public AbstractUIEnvironment getUserEnvironment() { - return getUIEnvironment(); - } + private static final long serialVersionUID = 1L; + + public String getName() { + return this + " Self-View"; + } + }; + addView(selfView); + } + + /** + * Constructs the views for this scape. If display graphics is set to true, + * calls create graphic views. Calls create nongraphic views in either case. + * Override to create views for the scape. Alternativly, override the + * createGraphicsViews and createNonGraphicViews methods to create views + * appropriate for the current operating mode. This method does NOT get called + * when a model is deserialized, but createGraphicViews does. + */ + public void createViews() { + if (isRoot() && getRunner().getEnvironment() == null) { + getRunner().createEnvironment(); + addView(getRunner().getEnvironment()); + } + createNonGraphicViews(); + if (Runner.isDisplayGraphics() || Runner.isServeGraphics()) { + // SwingUtilities.invokeLater(new Runnable() { + // public void run() { + createGraphicViews(); + // } + // }); + } + } + + /** + * Override to create any graphical views for the scape. This method will not + * be called when display graphics is set to false, and so is a good place to + * put any user interface only views. If root, will setup the user interface + * environment and add an auto customizer. + */ + public void createGraphicViews() { + } + + /** + * Overide to create and non-graphical views for the scape. If root, will + * automatically create control and counter views, create a self view, add a + * standand output view. + */ + public void createNonGraphicViews() { + if (isRoot()) { + createSelfView(); + getEnvironment().getConsole().println("Ascape Model: " + getName()); + getEnvironment().getConsole().println(getDescription()); + } + } + + /* + * public PropertyAccessor[] retrieveAccessors(PropertyAccessor[] accessors) + * throws IntrospectionException { return + * retrieveAccessors(PropertyAccessor.determineAccessors(this, Model.class)); + * } + */ + + /** + * If the scape has delegated a view to itself, called each time a scape sends + * a "initialize" event, indicating it has been initialized. Normally wouldn't + * use in this context. + */ + public void scapeInitialized(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time a scape sends + * a "setup" method, indicating it needs to be setup for a run. Possible uses + * include setting initial vector extents, responding to changes in user + * settings, and changing parameters systematically. (A view delegate to the + * scape is automatically created for root scapes when the standard model + * implementation is used.) + * + * @param scapeEvent + * the associated scape event + */ + public void scapeSetup(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time the scape is + * iterated. (A view delegate to the scape is automatically created for root + * scapes when the standard model implementation is used.) + * + * @param scapeEvent + * the associated scape event + */ + public void scapeIterated(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time the scape is + * started. (A view delegate to the scape is automatically created for root + * scapes when the standard model implementation is used.) + * + * @param scapeEvent + * the associated scape event + */ + public void scapeStarted(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time the scape is + * stopped. (A view delegate to the scape is automatically created for root + * scapes when the standard model implementation is used.) + * + * @param scapeEvent + * the associated scape event + */ + public void scapeStopped(ScapeEvent scapeEvent) { + } + + public void scapePaused(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time the scape is + * updated. (A view delegate to the scape is automatically created for root + * scapes when the standard model implementation is used.) + * + * @param scapeEvent + * the associated scape event + */ + public void scapeNotification(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time a scape sends + * a "closing" event. Normally wouldn't use in this context. + * + * @param scapeEvent + * the associated scape event + */ + public void scapeClosing(ScapeEvent scapeEvent) { + } + + /** + * Method called as the entire envornmnet is about to be exited. + * + * @param scapeEvent + * the associated scape event + */ + public void environmentQuiting(ScapeEvent scapeEvent) { + } + + /** + * If the scape has delegated a view to itself, called each time a scape sends + * a "deserialized" event. + * + * @param scapeEvent + * the associated scape event + */ + public void scapeDeserialized(ScapeEvent scapeEvent) { + if (Runner.isDisplayGraphics()) { + if (isRoot()) { + getRunner().createEnvironment(); + addView(getRunner().getEnvironment()); + } + } + } + + /** + * Add a scape to this listener. Just here to fulfill the scape listener + * contract. + * + * @param scapeEvent + * the associated scape event + */ + public void scapeAdded(ScapeEvent scapeEvent) throws TooManyListenersException { + } + + /** + * Notifies the listener that the scape has removed it. Just here to fulfill + * the scape listener contract. + * + * @param scapeEvent + * the associated scape event + */ + public void scapeRemoved(ScapeEvent scapeEvent) { + } + + /** + * Returns false the scape is not a graphical user interface component. + */ + public boolean isGraphic() { + return false; + } + + /** + * Returns true (default) if the listener is intended to be used only for the + * current scape; certainly true in this case. + */ + public boolean isLifeOfScape() { + return true; + } + + /* + * Returns the ascape home directory. public static String getAscapeHome() { + * //return System.getProperty("ascape.home", "D:/"); return ""; } + */ + + /** + * Save the state of the scape to a file. + */ + public void save(File file) throws IOException { + OutputStream os = new FileOutputStream(file); + save(os); + } + + /** + * Save the state of the scape to an output stream. + */ + public void save(OutputStream os) throws IOException { + if (!isSerializable()) { + throw new RuntimeException("Tried to save a model that is not serializable."); + } + + getRunner().setInternalRunning(false); + + GZIPOutputStream gzos = new GZIPOutputStream(os); + ObjectOutputStream oos = new ObjectOutputStream(gzos); + + // remove the customizer and any environment (non-scape specific) views + boolean needToAddCustomizer = false; + if (getUIEnvironment() != null && getUIEnvironment().getCustomizer() != null && isScapeListener(getUIEnvironment().getCustomizer())) { + removeScapeListener(getUIEnvironment().getCustomizer()); + needToAddCustomizer = true; + } + for (int i = 0; i < getEnvironment().getEnvironmentViews().size(); i++) { + ScapeListener l = (ScapeListener) getEnvironment().getEnvironmentViews().get(i); + l.getScape().removeScapeListener(l); + } + + try { + oos.writeObject(this); + } catch (StackOverflowError e) { + e.printStackTrace(); + System.err.println(""); + System.err.println("************************************"); + throw new RuntimeException("PLEASE INCREASE STACK SIZE, e.g. by using java's -Xss command line paramter."); + } + oos.close(); + + // reconnect the customizer and any environment (non-scape specific) + // views + if (needToAddCustomizer) { + addScapeListener(getUIEnvironment().getCustomizer()); + } + for (int i = 0; i < getEnvironment().getEnvironmentViews().size(); i++) { + ScapeListener l = (ScapeListener) getEnvironment().getEnvironmentViews().get(i); + addView(l, false); + } + + // we set startPeriod to scape.period + 1 so that there is not a blank + // first point in the charts + try { + setStartPeriod(getPeriod() + 1); + } catch (SpatialTemporalException e) { + try { + setStartPeriod(getPeriod()); + } catch (SpatialTemporalException e1) { + try { + setStartPeriod(getPeriod()); + } catch (SpatialTemporalException e2) { + throw new RuntimeException("Internal Error"); + } + } + } + getRunner().setInternalRunning(true); + } + + private void writeObject(final java.io.ObjectOutputStream out) throws IOException { + + if (getRunner().getData() != null) { + getRunner().getData().getPeriods().clear(); + } + + getRunner().write(out); + } + + /** + * Sets values for the models paramters based on supplied array of key value + * pairs. Paramters and values are expected to be seperated with an "=", for + * example: "MyParameter=12". + * + * @param args + * an array of strings with paramter-value paris in the form + * "{paramter-name}={paramter-value}" + * @param reportNotFound + * if paramters not found should result in a console notification and + * if errors in invocation should be reported, false otherwise + */ + public void assignParameters(String[] args, boolean reportNotFound) { + // List allAccessors = retrieveAllAccessors(); + List allAccessors = null; + try { + allAccessors = PropertyAccessor.determineReadWriteAccessors(this, Scape.class, false); + } catch (IntrospectionException e) { + throw new RuntimeException(e); + } + + for (String arg : args) { + String paramName = PropertyAccessor.paramName(arg); + if (paramName != null) { + boolean found = false; + String paramValue = PropertyAccessor.paramValue(arg); + for (Iterator iterator = allAccessors.iterator(); iterator.hasNext();) { + PropertyAccessor accessor = (PropertyAccessor) iterator.next(); + if (accessor.getName().equalsIgnoreCase(paramName)) { + try { + accessor.setAsText(paramValue); + } catch (InvocationTargetException e) { + if (reportNotFound) { + throw new RuntimeException("Exception in called method: " + e.getTargetException()); + } + // Else ignore, its ok if there is a problem calling + // the method at this point + } + found = true; + } + } + + found = found || Runner.assignEnvironmentParameter(paramName, paramValue); + if (!found) { + if (paramName.equalsIgnoreCase("RandomSeed")) { + try { + setRandomSeed(PropertyAccessor.paramValueLong(arg)); + } catch (NumberFormatException e) { + getEnvironment().getConsole().println("Couldn't decode random seed value: " + paramValue); + } + found = true; + } else if (paramName.equalsIgnoreCase("StopPeriod")) { + try { + setStopPeriod(PropertyAccessor.paramValueInt(arg)); + found = true; + } catch (SpatialTemporalException e) { + e.printStackTrace(); // To change body of catch + // statement use File | Settings + // | File Templates. + } + } else if (paramName.equalsIgnoreCase("PausePeriod")) { + setPausePeriod(PropertyAccessor.paramValueInt(arg)); + found = true; + } else if (paramName.equalsIgnoreCase("AutoRestart")) { + setAutoRestart(PropertyAccessor.paramValueBoolean(arg)); + found = true; + } + } + if (!found && reportNotFound) { + getEnvironment().getConsole().println("***WARNING: Parameter not found: " + paramName + " in " + getName()); + } + } + } + } + + public void createViews(String[] args) { + if (args != null) { + for (String arg : args) { + if (PropertyAccessor.paramName(arg).equals("view")) { + ScapeListener newView = (ScapeListener) getRunner().instanceFromName(PropertyAccessor.paramValue(arg)); + if (newView != null) { + addView(newView); + } + } + } + } + } + + /** + * Sets values for the models paramters based on supplied array of key value + * pairs, reporting if any of the keys (parameter names) are not found. + * Paramters and values are expected to be seperated with an "=", for example: + * "MyParameter=12". + * + * @param args + * an array of strings with paramter-value paris in the form + * "{paramter-name}={paramter-value}" + */ + public void assignParameters(String[] args) { + assignParameters(args, true); + } + + /** + * Moves an agent toward the specified agent. + * + * @param origin + * the agent moving + * @param target + * the agent's target + * @param distance + * the distance to move + */ + public final void moveAway(LocatedAgent origin, Coordinate target, double distance) { + getSpace().moveAway(origin, target, distance); + } + + /** + * Moves an agent toward the specified agent. It is an error to call this + * method on collections (and discrete discrete scapes not composed of + * HostCells. + * + * @param origin + * the agent moving + * @param target + * the agent's target + * @param distance + * the distance to move + */ + public final void moveToward(LocatedAgent origin, Coordinate target, double distance) { + getSpace().moveToward(origin, target, distance); + } + + /** + * Returns the shortest distance between one agent and another. + * + * @param origin + * the starting agent + * @param target + * the ending agent + */ + public double calculateDistance(LocatedAgent origin, LocatedAgent target) { + return calculateDistance(origin.getCoordinate(), target.getCoordinate()); + } + + /** + * Returns the shortest distance between one LocatedAgent and another. + * Warning: this default method only returns a coordinate specific distance. + * It uses no information about the scape context; for example wether it is a + * periodic (wrapping) space or not. Therefore, if you implement your own + * versions of Scape, ensure that you have properly implemented a version of + * this method. (All Ascape Scape collections properly overide this method.) + * + * @param origin + * one LocatedAgent + * @param target + * another LocatedAgent + */ + public final double calculateDistance(Coordinate origin, Coordinate target) { + return getSpace().calculateDistance(origin, target); + } + + public class ConditionalIterator implements Iterator { + + Iterator iter; + + Conditional condition; + + Object next; + + public ConditionalIterator(Iterator iter, Conditional condition) { + ConditionalIterator.this.iter = iter; + ConditionalIterator.this.condition = condition; + loadNext(); + } + + private void loadNext() { + next = null; + while (iter.hasNext() && next == null) { + Object o = iter.next(); + if (condition.meetsCondition(o)) { + next = o; + } + } + } + + public boolean hasNext() { + return next != null; + } + + public Object next() { + if (next != null) { + Object currentNext = next; + loadNext(); + return currentNext; + } else { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw new UnsupportedOperationException("Can't remove from a conditional iterator."); + } + } + + /** + * Find the maximum cell of some data point. If multiple points have the same + * value, returns a random instance at that value. + * + * @param condition + * @return + */ + public final List find(Conditional condition) { + return space.find(condition); + } + + /** + * Find the maximum cell of some data point. If multiple points have the same + * value, returns a random instance at that value. + * + * @param iter + * @param dataPoint + * @return + */ + public final LocatedAgent findMaximum(final Iterator iter, DataPoint dataPoint) { + // The code is written in such a way that there will not be the cost of + // Array creation if only one maximum exists + ArrayList multipleMaxObjects = null; + double maxValue = -Double.MAX_VALUE; + LocatedAgent maxObject = null; + while (iter.hasNext()) { + Object next = iter.next(); + if (dataPoint.getValue(next) > maxValue) { + maxValue = dataPoint.getValue(next); + maxObject = (LocatedAgent) next; + multipleMaxObjects = null; + } + // Awaiting decision to become depndent on 1.4 + // else if (Double.compare(dataPoint.getValue(next), maxValue) == 0) + // { + else if (DataPointConcrete.equals(dataPoint.getValue(next), maxValue)) { + if (multipleMaxObjects == null) { + multipleMaxObjects = new ArrayList(); + multipleMaxObjects.add(maxObject); + } + multipleMaxObjects.add(next); + } + } + if (multipleMaxObjects == null) { + return maxObject; + } else { + return (LocatedAgent) multipleMaxObjects.get(randomToLimit(multipleMaxObjects.size())); + } + } + + public final LocatedAgent findMinimumWithin(Coordinate coordinate, DataPoint dataPoint, Conditional condition, boolean includeSelf, double distance) { + return (LocatedAgent) getSpace().findMinimumWithin(coordinate, dataPoint, condition, includeSelf, distance); + } + + public final LocatedAgent findMaximumWithin(Coordinate coordinate, DataPoint dataPoint, Conditional condition, boolean includeSelf, double distance) { + return (LocatedAgent) getSpace().findMaximumWithin(coordinate, dataPoint, condition, includeSelf, distance); + } + + public final LocatedAgent findMinimum(final Iterator iter, DataPoint dataPoint) { + // The code is written in such a way that there will not be the cost of + // Array creation if only one minimum exists + ArrayList multipleMinObjects = null; + double minValue = Double.MAX_VALUE; + LocatedAgent minObject = null; + while (iter.hasNext()) { + Object next = iter.next(); + if (dataPoint.getValue(next) < minValue) { + minValue = dataPoint.getValue(next); + minObject = (LocatedAgent) next; + multipleMinObjects = null; + } + // Awaiting decision to become depndent on 1.4 + // else if (Double.compare(dataPoint.getValue(next), minValue) == 0) + // { + else if (DataPointConcrete.equals(dataPoint.getValue(next), minValue)) { + if (multipleMinObjects == null) { + multipleMinObjects = new ArrayList(); + multipleMinObjects.add(minObject); + } + multipleMinObjects.add(next); + } + } + if (multipleMinObjects == null) { + return minObject; + } else { + return (LocatedAgent) multipleMinObjects.get(randomToLimit(multipleMinObjects.size())); + } + } + + /** + * Returns an iteration across all agents the specified distance from the + * origin. + * + * @param origin + * the starting cell + * @param includeSelf + * should the origin be included + * @param distance + * the distance agents must be within to be included + */ + public final Iterator withinIterator(final Coordinate origin, Conditional condition, boolean includeSelf, final double distance) { + return getSpace().withinIterator(origin, condition, includeSelf, distance); + } + + /** + * Returns the agent with the minimum value. + * + * @param point + * the data point to use to make the comparison for minimum + */ + public LocatedAgent findMinimum(DataPoint point) { + return findMinimum(iterator(), point); + } + + /** + * Returns the agent with the maximum value. + * + * @param point + * the data point to use to make the comparison for maximum + */ + public LocatedAgent findMaximum(DataPoint point) { + return findMaximum(iterator(), point); + } + + /** + * The strategy that will be used to execute rules across this scape. + */ + private ExecutionStrategy strategy; + + private static int threadCount = 1; + + /** + * Finds the nearest agent that meets some condition. Scapes without + * coordinate meaing should override this method. + * + * @param origin + * the coordinate to find agents near + * @param condition + * the condition that found agent must meet + * @param includeOrigin + * if the origin should be included + * @param distance + * the maximum distance around the origin to look + */ + public final LocatedAgent findNearest(final Coordinate origin, Conditional condition, boolean includeOrigin, double distance) { + return (LocatedAgent) getSpace().findNearest(origin, condition, includeOrigin, distance); + } + + /** + * Returns a coordinate randomly selected from the collection's space. + */ + public final Coordinate findRandomCoordinate() { + return getSpace().findRandomCoordinate(); + } + + /** + * Returns all agents within the specified distance of the agent. + * + * @param origin + * the coordinate at the center of the search + * @param includeSelf + * whether or not the starting agent should be included in the search + * @param distance + * the distance agents must be within to be included + */ + public final List findWithin(Coordinate origin, Conditional condition, boolean includeSelf, double distance) { + return getSpace().findWithin(origin, condition, includeSelf, distance); + } + + /** + * Returns the number of agents within the specified distance of the agent + * that meet some condition. + * + * @param origin + * the coordinate at the center of the search + * @param condition + * the condition the agent must meet to be included + * @param distance + * the distance agents must be within to be included + */ + public final int countWithin(Coordinate origin, Conditional condition, boolean includeSelf, double distance) { + return getSpace().countWithin(origin, condition, includeSelf, distance); + } + + /** + * Returns if there are agents within the specified distance of the origin + * that meet some Condition. + * + * @param origin + * the coordinate at the center of the search + * @param condition + * the condition the agent must meet to be included + * @param distance + * the distance agents must be within to be included + */ + public final boolean hasWithin(final Coordinate origin, Conditional condition, boolean includeSelf, double distance) { + return getSpace().hasWithin(origin, condition, includeSelf, distance); + } + + public final boolean isMutable() { + return getSpace().isMutable(); + } + + /** + * Returns a string composed of descriptions of the contents. + */ + public String contentsToString() { + String contents = ""; + for (Iterator iterator = space.iterator(); iterator.hasNext();) { + Agent agent = (Agent) iterator.next(); + contents = contents + agent.toString(); + if (iterator.hasNext()) { + contents = contents + ", "; + } + } + return contents; + } + + /** + * Returns a string representation of this scape. + */ + public String toString() { + if (name != null) { + return name; + } else { + if (isRoot()) { + return "Root Scape"; + } else if (prototypeAgent != null) { + return "Scape of " + prototypeAgent.toString() + "(s)"; + } else { + return "Scape of agents of unspecified type"; + } + } + } + + public boolean isSerializable() { + return serializable; + } + + public void setSerializable(boolean serializable) { + this.serializable = serializable; + } + + /** + * Overides the clone method to do a deep clone of member state so that such + * state will not be shared between scapes. + */ + public Object clone() { + Scape clone = (Scape) super.clone(); + clone.scapeListeners = new ArrayList(); + for (Iterator iter = scapeListeners.iterator(); iter.hasNext();) { + ScapeListener thisListener = (ScapeListener) iter.next(); + try { + ScapeListener newListener = (ScapeListener) thisListener.clone(); + removeScapeListener(newListener); + newListener.scapeRemoved(new ScapeEvent(this, ScapeEvent.REPORT_REMOVED)); + newListener.scapeAdded(new ScapeEvent(clone, ScapeEvent.REPORT_ADDED)); + clone.addScapeListener(newListener); + } catch (TooManyListenersException e) { + throw new RuntimeException("Internal error in Scape.clone " + e); + } + } + if (prototypeAgent != null) { + clone.prototypeAgent = (Agent) prototypeAgent.clone(); + } + if (rules != null) { + clone.rules = (VectorSelection) rules.clone(); + } + if (initialRules != null) { + clone.initialRules = (VectorSelection) initialRules.clone(); + } + clone.drawFeatures = (Vector) drawFeatures.clone(); + clone.space = (CollectionSpace) space.clone(); + return clone; + } + + /** + * Returns an agent randomly selected from the collection. If no agents exist, + * returns null. + */ + public final LocatedAgent findRandom() { + return (LocatedAgent) getSpace().findRandom(); + } + + /** + * Returns a random unoccupied discrete location in the space given with the + * lattice. + * + * @param excludeAgent + * a cell to exclude from get (typically origin) + */ + public Agent findRandom(Location excludeAgent) { + return (LocatedAgent) getSpace().findRandom(excludeAgent); + } + + /** + * Returns an agent randomly that matches a condition. Note: If there are no + * agents in the collection that meet the condition, the method returns null. + * + * @param condition + * the condition that must be matched + */ + public final Agent findRandom(Conditional condition) { + return (LocatedAgent) getSpace().findRandom(condition); + } + + /** + * Creates a new agent in this collection by cloning the prototype agent, + * adding it in an arbitrary place (typically at the end of a list), and + * initializing it. + */ + public synchronized Agent newAgent() { + return newAgent(false); + } + + /** + * Creates a new agent in this collection by cloning the prototype agent, + * adding it to a random or arbitrary (last in most cases) place in the + * collection, and initializing it. + * + * @param randomLocation + * should the agent be placed in a random location, or in an + * arbitrary location? + */ + public synchronized Agent newAgent(boolean randomLocation) { + Agent newAgent = (LocatedAgent) getSpace().newLocation(randomLocation); + List agents = new LinkedList(); + agents.add(newAgent); + execute(getInitialRules().getVector(), agents); + return newAgent; + } + + /** + * Returns the number of agents in the scape. + * + * @return the number of agents in the scape + */ + public int size() { + return getSize(); + } + + /** + * Are there no agents in this scape? + * + * @return true if the scape is empty + */ + public final boolean isEmpty() { + return getSpace().isEmpty(); + } + + /** + * Returns true if the scape collection contains the object (agent.) + * + * @param o + * the agent to search for + * @return true if the scape contains the agent + */ + public final boolean contains(Object o) { + return getSpace().contains(o); + } + + /** + * Returns an array containing all of the elements in this collection in + * proper sequence. Obeys the general contract of the + * <tt>Collection.toArray</tt> method. + * + * @return an array containing all of the elements in this collection in + * proper sequence. + * @see java.util.Arrays#asList(java.lang.Object[]) + */ + public final Object[] toArray() { + return getSpace().toArray(); + } + + /** + * Returns an array containing the current agents in this scape; the runtime + * type is specified by the passed array. + * + * @param a + * the array to copy the agents to + * @return an array containing the agents + * @throws java.lang.ArrayStoreException + * if the runtime type of the specified array doesn't match all + * agents + */ + public final Object[] toArray(Object a[]) { + return getSpace().toArray(a); + } + + /** + * Returns true if this collection contains all of agents in the specified + * collection. + * + * @param c + * collection of agents to be found in the scape + * @return true if this scape contains all of the agents in the collection + */ + public final boolean containsAll(Collection c) { + return getSpace().containsAll(c); + } + + /** + * Adds all of the agent in the specified collection to the end of the scape. + * Assumes (but does not check) that all of the elements are instances of + * agent. + * + * @param c + * collection whose agents are to be added to the scape + * @return true if the scape had new agents added + */ + public final boolean addAll(Collection c) { + return getSpace().addAll(c); + } + + /** + * Removes all of the agnets contained in the collection. No attempt is made + * to cache the removal; the agents are all removed at once. + * + * @param c + * collection whose agents are to be added to the scape + * @return true if the scape had agents (but not neccessarily all?) removed + */ + public final boolean removeAll(Collection c) { + return getSpace().removeAll(c); + } + + /** + * Retains only the elements in the scape that are in the specified + * collection. + * + * @param c + * collection whose agents are to be retained in the scape + * @return true if this scape had agents removed + */ + public final boolean retainAll(Collection c) { + return getSpace().retainAll(c); + } + + /** + * Removes all agents from the scape. + */ + public final void clear() { + getSpace().clear(); + } + + /** + * Adds the supplied object (agent) to this collection. + */ + public boolean add(Object a) { + return add(a, true); + } + + /** + * Adds the supplied object (assumed to be an agent) to this collection. The + * object is assumed to be an agent, though that behavior may be loosened at + * some point. + * + * @param agent + * the agent to add + * @param isParent + * should this scape be made the parent scape of the agent? + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public final boolean add(Object agent, boolean isParent) { + if (!(agent instanceof Agent)) { + // This may change at some point.. + throw new ClassCastException("Scape collections expect Agents only."); + } + boolean success = getSpace().add(agent, isParent); + if (isParent) { + ((Agent) agent).setScape(this); + } + return success; + } + + /** + * Adds the supplied object (agent) to this collection. + * + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public final void add(int index, Object a) { + add(index, a, true); + } + + /** + * Adds the supplied object (assumed to be an agent) to this collection. The + * object is assumed to be an agent, though that behavior may be loosened at + * some point. + * + * @param o + * the agent to add + * @param isParent + * should this scape be made the parent scape of the agent? + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public final void add(int index, Object o, boolean isParent) { + try { + ((ListSpace) getSpace()).add(index, o, isParent); + if (isParent) { + ((Agent) o).setScape(this); + } + } catch (ClassCastException e) { + throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); + } + } + + /** + * Removes the supplied object (agent) from this collection. + * + * @param o + * the agent to be removed + * @return true if the agent was deleted, false otherwise + */ + public boolean remove(Object o) { + return space.remove(o); + } + + /** + * Removes the object at the index from this collection. + * + * @param index + * the index for the agent to remove + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public final Object remove(int index) { + try { + return ((List) getSpace()).remove(index); + } catch (ClassCastException e) { + throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); + } + } + + /** + * Returns the cell existing at the specified coordinate. + */ + public final LocatedAgent get(Coordinate coordinate) { + return (LocatedAgent) getSpace().get(coordinate); + } + + /** + * Sets the agent at the specified coordinate to the supplied agent. + * + * @param coordinate + * the coordinate to add the agent at + * @param agent + * the agent to add + */ + public final void set(Coordinate coordinate, LocatedAgent agent, boolean isParent) { + getSpace().set(coordinate, agent); + if (isParent) { + agent.setScape(getScape()); + agent.setCoordinate(coordinate); + } + } + + /** + * Sets the agent at the specified coordinate to the supplied agent. + * + * @param coordinate + * the coordinate to add the agent at + * @param agent + * the agent to add + */ + public void set(Coordinate coordinate, LocatedAgent agent) { + set(coordinate, agent, true); + } + + /** + * Returns the cell existing at the specified location. Convenience method. + * + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public final Object get(int index) { + try { + return ((ListBase) getSpace()).get(index); + } catch (ClassCastException e) { + throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); + } + } + + /** + * Sets the specified location to the provided agent. Convenience method. + * + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public void set(int index, Object agent) { + set(index, agent, true); + } + + /** + * Sets the specified location to the provided agent. Convenience method. + * + * @throws UnsupportedOperationException + * if this scape's space is not a list. + */ + public void set(int index, Object agent, boolean isParent) { + try { + ((ListBase) getSpace()).set(index, (LocatedAgent) agent, isParent); + if (isParent) { + ((Agent) agent).setScape(this); + } + } catch (ClassCastException e) { + throw new UnsupportedOperationException("Underlying space is not a list and cannot be accessed randomly."); + } + } + + public final ResetableIterator scapeIterator() { + return getSpace().safeIterator(); + } + + public final RandomIterator scapeRandomIterator() { + return getSpace().safeRandomIterator(); + } + + protected final ResetableIterator scapeIterator(int start, int limit) { + return getSpace().safeIterator(start, limit); + } + + public boolean isPeriodic() { + return getSpace().isPeriodic(); + } + + public void setPeriodic(boolean periodic) { + getSpace().setPeriodic(periodic); + } + + public Scape getSuperScape() { + return getSpace() instanceof SubSpace ? (Scape) ((SubSpace) getSpace()).getSuperSpace().getContext() : null; + } + + public void setSuperScape(Scape superScape) { + try { + ((SubSpace) getSpace()).setSuperSpace(superScape.getSpace()); + } catch (ClassCastException e) { + throw new UnsupportedOperationException("Underlying scape is no a SubSpace."); + } + } + + /** + * Returns multiple independently thread safe scape iterators across all + * agents in this scape. + * + * @return an iterator over the agents in scape order + */ + public final ResetableIterator[] scapeIterators(int count) { + return getSpace().safeIterators(count); + } + + public boolean isListenersAndMembersCurrent() { + return listenersAndMembersCurrent; + } + + public final Space getSpace() { + return space; + } + + public void setSpace(Space space) { + this.space = space; + // todo remove circular dependency + space.setContext(this); + space.setRandom(getRandom()); + } + + /** + * Sets the size of the collection, filling with clones of prototype agent. It + * is an error to set size while a scape is running. + * + * @param size + * a coordinate describing the size of this scape + */ + public void setSize(int size) { + if (runner != null && runner.isRunning()) { + throw new RuntimeException("Tried to set size while scape was running"); + } + space.setSize(size); + } + + public int getThreadCount() { + return Scape.threadCount; + } + + public void setThreadCount(int threadCount) { + Scape.threadCount = threadCount; + } + + public Location getPrototype() { + return (Location) getPrototypeAgent(); + } + + public boolean isHome(Location a) { + return ((Agent) a).getScape() == this; + } + + /** + * Convenience method for obtaining sata for current run. + * + * @return the Runner's Data Group. + */ + public DataGroup getData() { + return getRunner().getData(); + } + + /** + * Returns the UI Environment. + * + * @return a UI environment appropriate for given UI. + * @deprecated retained for backward compatability, please use + * #getUIEnvironment instead. + */ + public AbstractUIEnvironment getUserEnvironment() { + return getUIEnvironment(); + } } diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ControlEvent.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ControlEvent.java index 56832d83..2db736b6 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ControlEvent.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ControlEvent.java @@ -20,149 +20,147 @@ import java.util.EventObject; */ public class ControlEvent extends EventObject { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * A listener of the target has been updated. - */ - public final static int REPORT_LISTENER_UPDATED = 1; - - /** - * The target is requested to start. - */ - public final static int REQUEST_START = 2; - - /** - * The target is requested to stop. - */ - public final static int REQUEST_STOP = 3; - - /** - * The target is requested to step one iteration. - */ - public final static int REQUEST_STEP = 4; - - /** - * The target is requested to restart; that is, stop and start from initial - * state. - */ - public final static int REQUEST_RESTART = 5; - - /** - * The target is requested to pause. - */ - public final static int REQUEST_PAUSE = -1; - - /** - * The target is requested to resume. - */ - public final static int REQUEST_RESUME = -2; - - /** - * The target is requested to exit. - */ - public final static int REQUEST_QUIT = -3; - - /** - * The target is requested to save itself. - */ - public final static int REQUEST_SAVE = -4; - - /** - * The target is requested to open another model. - */ - public final static int REQUEST_CLOSE = -5; - - /** - * The target is requested to open another model, saving itself first. - */ - public final static int REQUEST_OPEN = -6; - - /** - * The target is requested to open a saved run, saving itself first. - */ - public final static int REQUEST_OPEN_SAVED = -7; - - /** - * The id. - */ - private int id; - - /** - * Constructs a control event, used to control a scape. - * - * @param source - * the object firing this alert event. - * @param id - * the id - */ - public ControlEvent(Object source, int id) { - super(source); - this.id = id; - } - - /** - * Gets the id decribing the control event. - * - * @return the ID - */ - public int getID() { - return id; - } - - /** - * Returns a paramter string describing this event. - * - * @return the string - */ - public String paramString() { - String typeStr; - switch (id) { - case REPORT_LISTENER_UPDATED: - typeStr = "Report listener updated"; - break; - case REQUEST_START: - typeStr = "Request start"; - break; - case REQUEST_STEP: - typeStr = "Request step"; - break; - case REQUEST_STOP: - typeStr = "Request stop"; - break; - case REQUEST_PAUSE: - typeStr = "Request pause"; - break; - case REQUEST_RESUME: - typeStr = "Request resume"; - break; - case REQUEST_QUIT: - typeStr = "Request scape quit"; - break; - case REQUEST_SAVE: - typeStr = "Request save"; - break; - case REQUEST_OPEN: - typeStr = "Request open"; - break; - case REQUEST_OPEN_SAVED: - typeStr = "Request open a saved run"; - break; - default: - typeStr = "Unknown request"; - } - return typeStr; - } - - /** - * Returns a string describing this event. - * - * @return the string - */ - public String toString() { - return paramString() + " from " + getSource(); - } + private static final long serialVersionUID = 1L; + + /** + * A listener of the target has been updated. + */ + public final static int REPORT_LISTENER_UPDATED = 1; + + /** + * The target is requested to start. + */ + public final static int REQUEST_START = 2; + + /** + * The target is requested to stop. + */ + public final static int REQUEST_STOP = 3; + + /** + * The target is requested to step one iteration. + */ + public final static int REQUEST_STEP = 4; + + /** + * The target is requested to restart; that is, stop and start from initial + * state. + */ + public final static int REQUEST_RESTART = 5; + + /** + * The target is requested to pause. + */ + public final static int REQUEST_PAUSE = -1; + + /** + * The target is requested to resume. + */ + public final static int REQUEST_RESUME = -2; + + /** + * The target is requested to exit. + */ + public final static int REQUEST_QUIT = -3; + + /** + * The target is requested to save itself. + */ + public final static int REQUEST_SAVE = -4; + + /** + * The target is requested to open another model. + */ + public final static int REQUEST_CLOSE = -5; + + /** + * The target is requested to open another model, saving itself first. + */ + public final static int REQUEST_OPEN = -6; + + /** + * The target is requested to open a saved run, saving itself first. + */ + public final static int REQUEST_OPEN_SAVED = -7; + + /** + * The id. + */ + private int id; + + /** + * Constructs a control event, used to control a scape. + * + * @param source + * the object firing this alert event. + * @param id + * the id + */ + public ControlEvent(Object source, int id) { + super(source); + this.id = id; + } + + /** + * Gets the id decribing the control event. + * + * @return the ID + */ + public int getID() { + return id; + } + + /** + * Returns a paramter string describing this event. + * + * @return the string + */ + public String paramString() { + String typeStr; + switch (id) { + case REPORT_LISTENER_UPDATED: + typeStr = "Report listener updated"; + break; + case REQUEST_START: + typeStr = "Request start"; + break; + case REQUEST_STEP: + typeStr = "Request step"; + break; + case REQUEST_STOP: + typeStr = "Request stop"; + break; + case REQUEST_PAUSE: + typeStr = "Request pause"; + break; + case REQUEST_RESUME: + typeStr = "Request resume"; + break; + case REQUEST_QUIT: + typeStr = "Request scape quit"; + break; + case REQUEST_SAVE: + typeStr = "Request save"; + break; + case REQUEST_OPEN: + typeStr = "Request open"; + break; + case REQUEST_OPEN_SAVED: + typeStr = "Request open a saved run"; + break; + default: + typeStr = "Unknown request"; + } + return typeStr; + } + + /** + * Returns a string describing this event. + * + * @return the string + */ + @Override + public String toString() { + return paramString() + " from " + getSource(); + } } diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/DefaultScapeListener.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/DefaultScapeListener.java index 72b5d50f..17f52b79 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/DefaultScapeListener.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/DefaultScapeListener.java @@ -30,9 +30,6 @@ import org.ascape.model.Scape; */ public abstract class DefaultScapeListener implements ScapeListener, Serializable { - /** - * - */ private static final long serialVersionUID = 1L; /** @@ -99,6 +96,9 @@ public abstract class DefaultScapeListener implements ScapeListener, Serializabl public void scapeStopped(ScapeEvent scapeEvent) { } + public void scapePaused(ScapeEvent scapeEvent) { + } + /** * Called immediatly after scape is iterated. * @@ -213,6 +213,8 @@ public abstract class DefaultScapeListener implements ScapeListener, Serializabl scapeDeserialized(scapeEvent); } else if (scapeEvent.getID() == ScapeEvent.REQUEST_CLOSE) { scapeClosing(scapeEvent); + } else if (scapeEvent.getID() == ScapeEvent.TICK) { + scapePaused(scapeEvent); } if (isNotifyScapeAutomatically()) { notifyScapeUpdated(); @@ -304,11 +306,7 @@ public abstract class DefaultScapeListener implements ScapeListener, Serializabl this.name = name; } - /** - * Clones this object. - * - * @return the object - */ + @Override public Object clone() { try { DefaultScapeListener clone = (DefaultScapeListener) super.clone(); @@ -324,6 +322,7 @@ public abstract class DefaultScapeListener implements ScapeListener, Serializabl * * @return the string */ + @Override public String toString() { return getName(); } diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ScapeListener.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ScapeListener.java index 31d1bfe2..05f55978 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ScapeListener.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/event/ScapeListener.java @@ -30,140 +30,147 @@ import org.ascape.util.HasName; */ public interface ScapeListener extends EventListener, HasName, Cloneable { - /** - * Called immediatly after the scape is initialized. - * - * @param scapeEvent - * the scape event - */ - public void scapeInitialized(ScapeEvent scapeEvent); - - /** - * Called immediatly after the scape is started. - * - * @param scapeEvent - * the scape event - */ - public void scapeStarted(ScapeEvent scapeEvent); - - /** - * Called immediatly after the scape is stopped. - * - * @param scapeEvent - * the scape event - */ - public void scapeStopped(ScapeEvent scapeEvent); - - /** - * Called immediatly after scape is iterated. - * - * @param scapeEvent - * the scape event - */ - public void scapeIterated(ScapeEvent scapeEvent); - - /** - * Method called when the scape is ready for setup. That is, the scape has - * been created (or it has just finished its previous run) but it has not - * yet been initialized. This is an appropriate place to change model - * paramters, persent user's with options, etc. - * - * @param scapeEvent - * the scape event - */ - public void scapeSetup(ScapeEvent scapeEvent); - - /** - * Method called as the scape is about to be closed. Allows any final view - * cleanup. - * - * @param scapeEvent - * the scape event - */ - public void scapeClosing(ScapeEvent scapeEvent); - - /** - * Method called as the environment is about to quit. Note that this method - * may not actually be called by a scape at all. Allows any final view - * cleanup. - * - * @param scapeEvent - * the scape event - */ - public void environmentQuiting(ScapeEvent scapeEvent); - - /** - * Method called immediatly after a model is deserialized. - * - * @param scapeEvent - * the scape event - */ - public void scapeDeserialized(ScapeEvent scapeEvent); - - /** - * Informs the listener that the agent scape has some kind of notification - * for the listener,. - * - * @param scapeEvent - * the scape setup event - */ - public void scapeNotification(ScapeEvent scapeEvent); - - /** - * Notifies the listener that the scape has added it. This is in affect a - * call back from the scape after adding the listener. At this point, the - * listener is responsible for responding back to the scape upon any - * notifications. A listener <i>typically</i> has one and only one scape, - * see below. A "good citizen" listener will typically make certain that it - * isn't added to more than one scape at a time, but one can also imagine - * cases where a listener might be interested in the activities of many - * scapes and this would be a perfectly legitimate usage pattern. - * - * @param scapeEvent - * the scape add event - * @throws TooManyListenersException - * the too many listeners exception - */ - public void scapeAdded(ScapeEvent scapeEvent) throws TooManyListenersException; - - /** - * Removes the scape from this view. Typically there should ony be one scape - * to remove. This method must be called whenever a listener is destroyed or - * becomes unresponsive; otherwise the model will wait indefinetly for the - * listener's updated response. - * - * @param scapeEvent - * the scape removed event - * @see scapeAdded - */ - public void scapeRemoved(ScapeEvent scapeEvent); - - /** - * Returns true if the listener is a graphical user interface component. - * - * @return true, if is graphic - */ - public boolean isGraphic(); - - /** - * Returns true if the listener is intended to be used only for the current - * scape; typical of all but control related listeners. - * - * @return true, if is life of scape - */ - public boolean isLifeOfScape(); - - /** - * Returns the Scape being viewed. - * - * @return the scape - */ - public Scape getScape(); - - /** - * Require public access for clone. - * - * @return the object - */ - public Object clone(); + /** + * Called immediatly after the scape is initialized. + * + * @param scapeEvent + * the scape event + */ + public void scapeInitialized(ScapeEvent scapeEvent); + + /** + * Called immediatly after the scape is started. + * + * @param scapeEvent + * the scape event + */ + public void scapeStarted(ScapeEvent scapeEvent); + + /** + * Called immediatly after the scape is stopped. + * + * @param scapeEvent + * the scape event + */ + public void scapeStopped(ScapeEvent scapeEvent); + + /** + * Called immediately after the scape has been set to pause. + * + * @param scapeEvent + */ + public void scapePaused(ScapeEvent scapeEvent); + + /** + * Called immediatly after scape is iterated. + * + * @param scapeEvent + * the scape event + */ + public void scapeIterated(ScapeEvent scapeEvent); + + /** + * Method called when the scape is ready for setup. That is, the scape has + * been created (or it has just finished its previous run) but it has not yet + * been initialized. This is an appropriate place to change model paramters, + * persent user's with options, etc. + * + * @param scapeEvent + * the scape event + */ + public void scapeSetup(ScapeEvent scapeEvent); + + /** + * Method called as the scape is about to be closed. Allows any final view + * cleanup. + * + * @param scapeEvent + * the scape event + */ + public void scapeClosing(ScapeEvent scapeEvent); + + /** + * Method called as the environment is about to quit. Note that this method + * may not actually be called by a scape at all. Allows any final view + * cleanup. + * + * @param scapeEvent + * the scape event + */ + public void environmentQuiting(ScapeEvent scapeEvent); + + /** + * Method called immediatly after a model is deserialized. + * + * @param scapeEvent + * the scape event + */ + public void scapeDeserialized(ScapeEvent scapeEvent); + + /** + * Informs the listener that the agent scape has some kind of notification for + * the listener,. + * + * @param scapeEvent + * the scape setup event + */ + public void scapeNotification(ScapeEvent scapeEvent); + + /** + * Notifies the listener that the scape has added it. This is in affect a call + * back from the scape after adding the listener. At this point, the listener + * is responsible for responding back to the scape upon any notifications. A + * listener <i>typically</i> has one and only one scape, see below. A + * "good citizen" listener will typically make certain that it isn't added to + * more than one scape at a time, but one can also imagine cases where a + * listener might be interested in the activities of many scapes and this + * would be a perfectly legitimate usage pattern. + * + * @param scapeEvent + * the scape add event + * @throws TooManyListenersException + * the too many listeners exception + */ + public void scapeAdded(ScapeEvent scapeEvent) throws TooManyListenersException; + + /** + * Removes the scape from this view. Typically there should ony be one scape + * to remove. This method must be called whenever a listener is destroyed or + * becomes unresponsive; otherwise the model will wait indefinetly for the + * listener's updated response. + * + * @param scapeEvent + * the scape removed event + * @see scapeAdded + */ + public void scapeRemoved(ScapeEvent scapeEvent); + + /** + * Returns true if the listener is a graphical user interface component. + * + * @return true, if is graphic + */ + public boolean isGraphic(); + + /** + * Returns true if the listener is intended to be used only for the current + * scape; typical of all but control related listeners. + * + * @return true, if is life of scape + */ + public boolean isLifeOfScape(); + + /** + * Returns the Scape being viewed. + * + * @return the scape + */ + public Scape getScape(); + + /** + * Require public access for clone. + * + * @return the object + */ + public Object clone(); } diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViews.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViews.java index a50b6320..8e43cfbe 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViews.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViews.java @@ -9,6 +9,7 @@ package org.ascape.model.rule; import org.ascape.model.Agent; import org.ascape.model.Scape; +import org.ascape.model.event.ScapeEvent; /** * A rule causing the target to notify its views that an update has occured. @@ -21,44 +22,37 @@ import org.ascape.model.Scape; */ public class NotifyViews extends PropogateScapeOnly { - /** - * - */ - private static final long serialVersionUID = 1L; - /** - * The id. - */ - private int id; + private static final long serialVersionUID = 1L; - /** - * Instantiates a new notify views. - * - * @param id - * the id - */ - public NotifyViews(int id) { - super("Notify Views id: " + id); - this.id = id; - } + /** + * The id. + */ + private int id; - /** - * Notify all views of state update. - * - * @param agent - * the agent (scape) to notify veiws - * @see Scape#notifyViews - */ - public void execute(Agent agent) { - //if (!((((Scape) agent).getPrototypeAgent()) instanceof AgentScape)) { - ((Scape) agent).notifyViews(id); - super.execute(agent); - //} - } + /** + * Instantiates a new notify views. + * + * @param id + * the id + */ + public NotifyViews(int id) { + super("Notify Views id: " + id); + this.id = id; + } + + /** + * Notify all views of state update. + * + * @param agent + * the agent (scape) to notify veiws + * @see Scape#notifyListeners(ScapeEvent) + */ + @Override + public void execute(Agent agent) { + // if (!((((Scape) agent).getPrototypeAgent()) instanceof AgentScape)) { + ((Scape) agent).notifyListeners(id); + super.execute(agent); + // } + } - /** - * Only scapes can have views. - */ - //public boolean isScapeOnly() { - // return true; - //} } diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViewsEvent.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViewsEvent.java index 9a65e83b..e8c7393f 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViewsEvent.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/model/rule/NotifyViewsEvent.java @@ -21,37 +21,37 @@ import org.ascape.model.event.ScapeEvent; */ public class NotifyViewsEvent extends PropogateScapeOnly { - /** - * - */ - private static final long serialVersionUID = 1L; - /** - * The event. - */ - private ScapeEvent event; - - /** - * Instantiates a new notify views event. - * - * @param event - * the event - */ - public NotifyViewsEvent(ScapeEvent event) { - super("Notify Views id: " + event); - this.event = event; - } - - /** - * Notify all views of state update. - * - * @param agent - * the agent (scape) to notify veiws - * @see org.ascape.model.Scape#notifyViews - */ - public void execute(Agent agent) { - //if (!((((Scape) agent).getPrototypeAgent()) instanceof AgentScape)) { - ((Scape) agent).notifyViews(event); - super.execute(agent); - //} - } + private static final long serialVersionUID = 1L; + + /** + * The event. + */ + private ScapeEvent event; + + /** + * Instantiates a new notify views event. + * + * @param event + * the event + */ + public NotifyViewsEvent(ScapeEvent event) { + super("Notify Views id: " + event); + this.event = event; + } + + /** + * Notify all views of state update. + * + * @param agent + * the agent (scape) to notify veiws + * @see org.ascape.model.Scape#notifyListeners(ScapeEvent) + */ + @Override + public void execute(Agent agent) { + // if (!((((Scape) agent).getPrototypeAgent()) instanceof AgentScape)) { + ((Scape) agent).notifyListeners(event); + super.execute(agent); + // } + } + } diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/NonGraphicRunner.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/NonGraphicRunner.java index 4da1da35..fd298579 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/NonGraphicRunner.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/NonGraphicRunner.java @@ -6,52 +6,53 @@ import org.ascape.model.Scape; public class NonGraphicRunner extends Runner {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 1L;
- @Override
- public void closeAndOpenSavedFinally(Scape oldScape) {
- throw new InternalError("Unexpected call to close and open for headless runtime.");
- }
+ @Override
+ public void closeAndOpenSavedFinally(Scape oldScape) {
+ throw new InternalError("Unexpected call to close and open for headless runtime.");
+ }
- @Override
- public void saveChoose() {
- throw new InternalError("Unexpected call to save and choose for headless runtime.");
- }
+ @Override
+ public void saveChoose() {
+ throw new InternalError("Unexpected call to save and choose for headless runtime.");
+ }
- /**
- * Creates, initializes and runs the model specified in the argument. To allow the running of a model directly from
- * the command line, you should subclass this method as shown below:
- *
- * <pre><code><BR>
- * public MyModel extends Model {
- * public static void main(String[] args) {
- * (open("mypath.MyModel")).start();
- * }
- * }
- * <BR>
- * </pre>
- *
- * </code> Otherwise, assuming your classpath is set up correctly, to invoke a model from the command line type:
- *
- * <pre><code><BR>
- * java org.ascape.model.Scape mypath.myModel
- * </pre>
- *
- * </code>
- *
- * @param args
- * at index 0; the name of the subclass of this class to run
- */
- public static void main(String[] args) {
- // Register environment
- Runner model = new NonGraphicRunner();
- try {
- model.launch(args);
- } catch (IOException e) {
- throw new RuntimeException("Exception attempting to load model.", e);
- }
- }
+ /**
+ * Creates, initializes and runs the model specified in the argument. To allow
+ * the running of a model directly from the command line, you should subclass
+ * this method as shown below:
+ *
+ * <pre>
+ * <code><BR>
+ * public MyModel extends Model {
+ * public static void main(String[] args) {
+ * (open("mypath.MyModel")).start();
+ * }
+ * }
+ * <BR>
+ * </pre>
+ *
+ * </code> Otherwise, assuming your classpath is set up correctly, to invoke a
+ * model from the command line type:
+ *
+ * <pre>
+ * <code><BR>
+ * java org.ascape.model.Scape mypath.myModel
+ * </pre>
+ *
+ * </code>
+ *
+ * @param args
+ * at index 0; the name of the subclass of this class to run
+ */
+ public static void main(String[] args) {
+ // Register environment
+ Runner model = new NonGraphicRunner();
+ try {
+ model.launch(args);
+ } catch (IOException e) {
+ throw new RuntimeException("Exception attempting to load model.", e);
+ }
+ }
}
diff --git a/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/Runner.java b/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/Runner.java index 784fdda1..bd448940 100644 --- a/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/Runner.java +++ b/org.ascape/plugins/org.ascape.core/src/org/ascape/runtime/Runner.java @@ -42,1560 +42,1557 @@ import org.ascape.util.data.DataGroup; */ public abstract class Runner implements Serializable, Runnable { - /** - * - */ - private static final long serialVersionUID = 6924379091134591724L; - - private Scape scape; - - /** - * Data group for all scapes. At some point this may be made non-static. - */ - private DataGroup dataGroup; - - /** - * Should any scapes opened be started automatically? Default true. true. - */ - private static boolean startOnOpen = true; - - /** - * Manages user space UI, settings etc. - */ - protected transient RuntimeEnvironment environment; - - /** - * The unit of time each iteration or period represents. - */ - private String periodName = "Iteration"; - - /** - * A brief descripiton (including credits) of the scape or of the model, if - * this is root scape. Plaintext. - */ - private String description; - - /** - * A brief descripiton (including credits) of the scape or of the model, if - * this is root scape. Includes HTML style tags as appropriate. - */ - private String HTMLDescription; - - /** - * Iteration to start on when restarting, creating new model, etc... - */ - private int startPeriod = 0; - - /** - * Iteration to stop on. - */ - private int stopPeriod = Integer.MAX_VALUE; - - /** - * Period to pause on. - */ - private int pausePeriod = Integer.MAX_VALUE; - - /** - * The system path in which all files are by default stored to and retrieved - * from. The value of the system variable ascape home. - */ - private String home; - - /** - * The earliest period this scape is expected to be run at. - */ - private int earliestPeriod; - - /** - * The latest period this scape is expected to be run at. - */ - private int latestPeriod = Integer.MAX_VALUE; - - private List restartingViews = new Vector(); - - /** - * The number of iterations since the scape began iterating. - */ - private int iteration; - - /** - * The current period. - */ - private int period; - - /** - * Is the scape currently paused? - */ - private boolean paused = false; - - /** - * Is the scape currently running? - */ - private boolean running = false; - - /** - * Has a step been requested? - */ - private boolean step = false; - - /** - * Has a restart been requested after the current run stops? - */ - private boolean closeAndOpenNewRequested = false; - - /** - * Has loading of a saved run been requested after the current run stops? - */ - private boolean closeAndOpenSavedRequested = false; - - /* - * All of the following will be replaced by a diferred control mechanism. - */ - - /** - * Has a restart been requested after the current run stops? - */ - private boolean restartRequested = false; - - /** - * Has a close been requested after the current run stops? - */ - private boolean closeRequested = false; - - /** - * Has a quit been requested after the current run stops? - */ - private boolean quitRequested = false; - - /** - * Has an open been requested when the current iteration completes? - */ - private boolean openRequested = false; - - /** - * Has a save been requested when the current iteration completes? - */ - private boolean saveRequested = false; - - /** - * Are we currently in the main control loop? - */ - private boolean inMainLoop = false; - - private boolean beginningDeserializedRun = false; - - /** - * Should the scape be restarted automatically after being stopped? - */ - private boolean autoRestart = true; - - /** - * Indicates that GUI should be displayed, if false, not GUI under any - * circumstances. - */ - private static boolean displayGraphics = true; - - /** - * Indicates that we are forwarding graphics to a client scape. - */ - private static boolean serveGraphics = false; - - /** - * Indicates that we are in a multiwin environment and want simple winsow - * strucutures. - */ - private static boolean muiltWinEnvironment; - - private Thread modelThread; - - public Runner() { - this(new RuntimeEnvironment()); - } - - public Runner(RuntimeEnvironment environment) { - this.environment = environment; - } - - protected void initialize() { - setInternalRunning(false); - getData().clear(); - scape.reseed(); - scape.execute(new NotifyViews(ScapeEvent.REQUEST_SETUP)); - waitForViewsUpdate(); - setIteration(0); - setPeriod(getStartPeriod()); - scape.execute(Scape.INITIALIZE_RULE); - scape.execute(new NotifyViews(ScapeEvent.REPORT_INITIALIZED)); - waitForViewsUpdate(); - scape.execute(Scape.INITIAL_RULES_RULE); - setInternalRunning(true); - } - - /** - * The main run loop of a running simulation. Seperated from run so that it - * can be executed in different runtime modes. - */ - protected synchronized void runMainLoop() { - inMainLoop = true; - restartRequested = false; - if (beginningDeserializedRun) { // we are re-starting the main loop - // after reading in a serialized model - beginningDeserializedRun = false; - saveRequested = false; - initialize(); - scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_DESERIALIZED)); - waitForViewsUpdate(); - scape.reseed(); - getEnvironment().getConsole().println("\nNew Random Seed: " + scape.getRandomSeed() + "\n"); - } else { // !beginningDeserializedRun - scape.executeOnRoot(Scape.CLEAR_STATS_RULE); - initialize(); - scape.executeOnRoot(Scape.COLLECT_STATS_RULE); - scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_START)); - waitForViewsUpdate(); - } - while (running) { - if (scape.isListenersAndMembersCurrent() && (!paused || step)) { - scape.executeOnRoot(Scape.CLEAR_STATS_RULE); - iteration++; - period++; - // I've moved the pausePeriod code here because when it was - // located in runMainLoop() the - // views were not getting properly updated when the pause period - // was reached. - Mario - if (period == getPausePeriod() && !paused) { - pause(); - } - scape.executeOnRoot(Scape.EXECUTE_RULES_RULE); - scape.executeOnRoot(Scape.COLLECT_STATS_RULE); - scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_ITERATE)); - step = false; - } else { - if (paused) { - waitForViewsUpdate(); - scape.executeOnRoot(new NotifyViews(ScapeEvent.TICK)); - try { - // Wait for user to unpause model - Thread.sleep(100); - } catch (InterruptedException e) { - } - } else { - try { - // Don't hog cycles while listeners are updating! - Thread.sleep(10); - } catch (InterruptedException e) { - } - } - } - if (period >= getStopPeriod()) { - waitForViewsUpdate(); - running = false; - if (isAutoRestart()) { - restartRequested = true; - } - } - if (closeAndOpenNewRequested) { - new Thread(this, "Ascape Main Execution Loop") { - public void run() { - closeAndOpenNewFinally(scape); - } - }.start(); - closeAndOpenNewRequested = false; - } - if (closeAndOpenSavedRequested) { - new Thread(this, "Ascape Main Execution Loop") { - public void run() { - closeAndOpenSavedFinally(scape); - } - }.start(); - closeAndOpenSavedRequested = false; - } - if (saveRequested) { - waitForViewsUpdate(); - saveChoose(); - saveRequested = false; - } - if (openRequested) { - waitForViewsUpdate(); - openChoose(); - openRequested = false; - } - } - scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_STOP)); - // Wait to ensure that all listeners are notified - waitForViewsUpdate(); - if (restartRequested) { - // Send an event to self for start - scape.respondControl(new ControlEvent(this, ControlEvent.REQUEST_START)); - } - if (closeRequested) { - closeFinally(); - closeRequested = false; - } - if (quitRequested) { - quitFinally(); - } - inMainLoop = false; - } - - /** - * Responds to any control events fired at this scape. Currently reacts to - * start, stop, pause, resume, step, quit, and restart events, as well as - * listener update report events. All control events except listener updates - * are passed up to the root. Any other events trigger an untrapped - * exception. - */ - public void respondControl(ControlEvent control) { - switch (control.getID()) { - case ControlEvent.REQUEST_CLOSE: - close(); - break; - case ControlEvent.REQUEST_OPEN: - closeAndOpenNew(); - break; - case ControlEvent.REQUEST_OPEN_SAVED: - closeAndOpenSaved(); - break; - case ControlEvent.REQUEST_SAVE: - save(); - break; - case ControlEvent.REQUEST_START: - start(); - break; - case ControlEvent.REQUEST_STOP: - stop(); - break; - case ControlEvent.REQUEST_STEP: - setStep(true); - break; - case ControlEvent.REQUEST_RESTART: - restart(); - break; - case ControlEvent.REQUEST_PAUSE: - pause(); - break; - case ControlEvent.REQUEST_RESUME: - resume(); - break; - case ControlEvent.REQUEST_QUIT: - quit(); - break; - default: - throw new RuntimeException("Unknown control event sent to Agent scape: " + control + " [" + control.getID() - + "]"); - } - } - - /** - * Blocks until all views of this scape and this scape's members have been - * updated. - */ - public void waitForViewsUpdate() { - while (!scape.isAllViewsUpdated() && inMainLoop) { - try { - // Don't hog cycles while listeners are updating! - Thread.sleep(10); - } catch (InterruptedException e) { - } - } - } - - /** - * Requests the scape to open another model, closing the existing one. Will - * not occur until the current iteration is complete; use static forms to - * open concurrently. Always called on root. - */ - public void closeAndOpenNew() { - if (running) { - // Running, so we have to allow current iteration to finish - closeAndOpenNewRequested = true; - } else { - closeAndOpenNewFinally(scape); - } - } - - /** - * Requests the scape to open a saved run, closing the existing one. Will - * not occur until the current iteration is complete; use static forms to - * open concurrently. Always called on root. - */ - public void closeAndOpenSaved() { - if (isRunning()) { - // Running, so we have to allow current iteration to finish - setCloseAndOpenNewRequested(true); - } else { - // have to start a new thread or else the GUI locks up - new Thread(this) { - public void run() { - closeAndOpenSavedFinally(getRootScape()); - } - }.start(); - } - } - - /** - * Requests the scape to open another model, closing the existing one. - * Always called on root. - */ - public void closeAndOpenNewFinally(final Scape oldScape) { - boolean oldWasPaused = oldScape.isPaused(); - if (!oldWasPaused) { - oldScape.getRunner().pause(); - } - final String modelName = ((AbstractUIEnvironment) environment).openDialog(); - if (!oldWasPaused) { - oldScape.getRunner().resume(); - } - if (modelName != null) { - // We want the old scape ot close first.. - oldScape.addView(new DefaultScapeListener() { - /** + private static final long serialVersionUID = 6924379091134591724L; + + private Scape scape; + + /** + * Data group for all scapes. At some point this may be made non-static. + */ + private DataGroup dataGroup; + + /** + * Should any scapes opened be started automatically? Default true. true. + */ + private static boolean startOnOpen = true; + + /** + * Manages user space UI, settings etc. + */ + protected transient RuntimeEnvironment environment; + + /** + * The unit of time each iteration or period represents. + */ + private String periodName = "Iteration"; + + /** + * A brief descripiton (including credits) of the scape or of the model, if + * this is root scape. Plaintext. + */ + private String description; + + /** + * A brief descripiton (including credits) of the scape or of the model, if + * this is root scape. Includes HTML style tags as appropriate. + */ + private String HTMLDescription; + + /** + * Iteration to start on when restarting, creating new model, etc... + */ + private int startPeriod = 0; + + /** + * Iteration to stop on. + */ + private int stopPeriod = Integer.MAX_VALUE; + + /** + * Period to pause on. + */ + private int pausePeriod = Integer.MAX_VALUE; + + /** + * The system path in which all files are by default stored to and retrieved + * from. The value of the system variable ascape home. + */ + private String home; + + /** + * The earliest period this scape is expected to be run at. + */ + private int earliestPeriod; + + /** + * The latest period this scape is expected to be run at. + */ + private int latestPeriod = Integer.MAX_VALUE; + + private List restartingViews = new Vector(); + + /** + * The number of iterations since the scape began iterating. + */ + private int iteration; + + /** + * The current period. + */ + private int period; + + /** + * Is the scape currently paused? + */ + private boolean paused = false; + + /** + * Is the scape currently running? + */ + private boolean running = false; + + /** + * Has a step been requested? + */ + private boolean step = false; + + /** + * Has a restart been requested after the current run stops? + */ + private boolean closeAndOpenNewRequested = false; + + /** + * Has loading of a saved run been requested after the current run stops? + */ + private boolean closeAndOpenSavedRequested = false; + + /* + * All of the following will be replaced by a diferred control mechanism. + */ + + /** + * Has a restart been requested after the current run stops? + */ + private boolean restartRequested = false; + + /** + * Has a close been requested after the current run stops? + */ + private boolean closeRequested = false; + + /** + * Has a quit been requested after the current run stops? + */ + private boolean quitRequested = false; + + /** + * Has an open been requested when the current iteration completes? + */ + private boolean openRequested = false; + + /** + * Has a save been requested when the current iteration completes? + */ + private boolean saveRequested = false; + + /** + * Are we currently in the main control loop? + */ + private boolean inMainLoop = false; + + private boolean beginningDeserializedRun = false; + + /** + * Should the scape be restarted automatically after being stopped? + */ + private boolean autoRestart = true; + + /** + * Indicates that GUI should be displayed, if false, not GUI under any + * circumstances. + */ + private static boolean displayGraphics = true; + + /** + * Indicates that we are forwarding graphics to a client scape. + */ + private static boolean serveGraphics = false; + + /** + * Indicates that we are in a multiwin environment and want simple winsow + * strucutures. + */ + private static boolean muiltWinEnvironment; + + private Thread modelThread; + + public Runner() { + this(new RuntimeEnvironment()); + } + + public Runner(RuntimeEnvironment environment) { + this.environment = environment; + } + + protected void initialize() { + setInternalRunning(false); + getData().clear(); + scape.reseed(); + scape.execute(new NotifyViews(ScapeEvent.REQUEST_SETUP)); + waitForViewsUpdate(); + setIteration(0); + setPeriod(getStartPeriod()); + scape.execute(Scape.INITIALIZE_RULE); + scape.execute(new NotifyViews(ScapeEvent.REPORT_INITIALIZED)); + waitForViewsUpdate(); + scape.execute(Scape.INITIAL_RULES_RULE); + setInternalRunning(true); + } + + /** + * The main run loop of a running simulation. Seperated from run so that it + * can be executed in different runtime modes. + */ + protected synchronized void runMainLoop() { + inMainLoop = true; + restartRequested = false; + if (beginningDeserializedRun) { // we are re-starting the main loop + // after reading in a serialized model + beginningDeserializedRun = false; + saveRequested = false; + initialize(); + scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_DESERIALIZED)); + waitForViewsUpdate(); + scape.reseed(); + getEnvironment().getConsole().println("\nNew Random Seed: " + scape.getRandomSeed() + "\n"); + } else { // !beginningDeserializedRun + scape.executeOnRoot(Scape.CLEAR_STATS_RULE); + initialize(); + scape.executeOnRoot(Scape.COLLECT_STATS_RULE); + scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_START)); + waitForViewsUpdate(); + } + while (running) { + if (scape.isListenersAndMembersCurrent() && (!paused || step)) { + scape.executeOnRoot(Scape.CLEAR_STATS_RULE); + iteration++; + period++; + // I've moved the pausePeriod code here because when it was + // located in runMainLoop() the + // views were not getting properly updated when the pause period + // was reached. - Mario + if (period == getPausePeriod() && !paused) { + pause(); + } + scape.executeOnRoot(Scape.EXECUTE_RULES_RULE); + scape.executeOnRoot(Scape.COLLECT_STATS_RULE); + scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_ITERATE)); + step = false; + } else { + if (paused) { + waitForViewsUpdate(); + scape.executeOnRoot(new NotifyViews(ScapeEvent.TICK)); + try { + // Wait for user to unpause model + Thread.sleep(100); + } catch (InterruptedException e) { + } + } else { + try { + // Don't hog cycles while listeners are updating! + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + } + if (period >= getStopPeriod()) { + waitForViewsUpdate(); + running = false; + if (isAutoRestart()) { + restartRequested = true; + } + } + if (closeAndOpenNewRequested) { + new Thread(this, "Ascape Main Execution Loop") { + public void run() { + closeAndOpenNewFinally(scape); + } + }.start(); + closeAndOpenNewRequested = false; + } + if (closeAndOpenSavedRequested) { + new Thread(this, "Ascape Main Execution Loop") { + public void run() { + closeAndOpenSavedFinally(scape); + } + }.start(); + closeAndOpenSavedRequested = false; + } + if (saveRequested) { + waitForViewsUpdate(); + saveChoose(); + saveRequested = false; + } + if (openRequested) { + waitForViewsUpdate(); + openChoose(); + openRequested = false; + } + } + scape.executeOnRoot(new NotifyViews(ScapeEvent.REPORT_STOP)); + // Wait to ensure that all listeners are notified + waitForViewsUpdate(); + if (restartRequested) { + // Send an event to self for start + scape.respondControl(new ControlEvent(this, ControlEvent.REQUEST_START)); + } + if (closeRequested) { + closeFinally(); + closeRequested = false; + } + if (quitRequested) { + quitFinally(); + } + inMainLoop = false; + } + + /** + * Responds to any control events fired at this scape. Currently reacts to + * start, stop, pause, resume, step, quit, and restart events, as well as + * listener update report events. All control events except listener updates + * are passed up to the root. Any other events trigger an untrapped exception. + */ + public void respondControl(ControlEvent control) { + switch (control.getID()) { + case ControlEvent.REQUEST_CLOSE: + close(); + break; + case ControlEvent.REQUEST_OPEN: + closeAndOpenNew(); + break; + case ControlEvent.REQUEST_OPEN_SAVED: + closeAndOpenSaved(); + break; + case ControlEvent.REQUEST_SAVE: + save(); + break; + case ControlEvent.REQUEST_START: + start(); + break; + case ControlEvent.REQUEST_STOP: + stop(); + break; + case ControlEvent.REQUEST_STEP: + setStep(true); + break; + case ControlEvent.REQUEST_RESTART: + restart(); + break; + case ControlEvent.REQUEST_PAUSE: + pause(); + break; + case ControlEvent.REQUEST_RESUME: + resume(); + break; + case ControlEvent.REQUEST_QUIT: + quit(); + break; + default: + throw new RuntimeException("Unknown control event sent to Agent scape: " + control + " [" + control.getID() + "]"); + } + } + + /** + * Blocks until all views of this scape and this scape's members have been + * updated. + */ + public void waitForViewsUpdate() { + while (!scape.isAllViewsUpdated() && inMainLoop) { + try { + // Don't hog cycles while listeners are updating! + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + } + + /** + * Requests the scape to open another model, closing the existing one. Will + * not occur until the current iteration is complete; use static forms to open + * concurrently. Always called on root. + */ + public void closeAndOpenNew() { + if (running) { + // Running, so we have to allow current iteration to finish + closeAndOpenNewRequested = true; + } else { + closeAndOpenNewFinally(scape); + } + } + + /** + * Requests the scape to open a saved run, closing the existing one. Will not + * occur until the current iteration is complete; use static forms to open + * concurrently. Always called on root. + */ + public void closeAndOpenSaved() { + if (isRunning()) { + // Running, so we have to allow current iteration to finish + setCloseAndOpenNewRequested(true); + } else { + // have to start a new thread or else the GUI locks up + new Thread(this) { + public void run() { + closeAndOpenSavedFinally(getRootScape()); + } + }.start(); + } + } + + /** + * Requests the scape to open another model, closing the existing one. Always + * called on root. + */ + public void closeAndOpenNewFinally(final Scape oldScape) { + boolean oldWasPaused = oldScape.isPaused(); + if (!oldWasPaused) { + oldScape.getRunner().pause(); + } + final String modelName = ((AbstractUIEnvironment) environment).openDialog(); + if (!oldWasPaused) { + oldScape.getRunner().resume(); + } + if (modelName != null) { + // We want the old scape ot close first.. + oldScape.addView(new DefaultScapeListener() { + /** * */ - private static final long serialVersionUID = 1L; - - public void scapeClosing(ScapeEvent scapeEvent) { - openInstance(modelName); - } - }); - oldScape.getRunner().close(); - } - } - - public static Scape openSavedRun(InputStream is) throws IOException { - Scape newScape = null; - GZIPInputStream gis = new GZIPInputStream(is); - ObjectInputStream ois = new ObjectInputStream(gis); - - try { - newScape = (Scape) ois.readObject(); - ois.close(); - // startPeriod is static so we have to set it here - // we set it to scape.period + 1 so that there is not a blank first - // point in the charts - try { - newScape.setStartPeriod(newScape.getPeriod() + 1); - } catch (SpatialTemporalException e) { - try { - newScape.setStartPeriod(newScape.getPeriod()); - } catch (SpatialTemporalException e1) { - try { - newScape.setStartPeriod(newScape.getPeriod()); - } catch (SpatialTemporalException e2) { - throw new RuntimeException("Internal Error"); - } - } - } - newScape.getRunner().beginningDeserializedRun = true; - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - return newScape; - } - - public Scape openSavedRun(String fileName, String[] args) throws IOException { - Scape newScape = openSavedRun(new File(fileName)); - - if (newScape != null) { - - if (args.length > 0) { - newScape.assignParameters(args, true); - } - newScape.getRunner().createEnvironment(); - - if (newScape.isPaused() && newScape.isStartOnOpen()) { - newScape.getRunner().resume(); - } - - newScape.getRunner().runMainLoop(); - } - - return newScape; - } - - public Scape openSavedRun(File savedRunFile) throws IOException { - Scape newScape = null; - InputStream is = new FileInputStream(savedRunFile); - newScape = openSavedRun(is); - return newScape; - } - - // public void createNewModel() { - // createNewModel(null, new String[0]); - // } - // - // public void createNewModel(Object applet, String[] args) { - // try { - // if (applet != null) { - // scape.getUIEnvironment().setApplet(applet, scape); - // } - // if (args != null) { - // // first pass of parseSettingArgs, - // // don't report any parameters not found - // scape.assignParameters(args, false); - // } - // scape.executeOnRoot(Scape.CREATE_RULE); - // if (args != null) { - // // second pass of parseSettingArgs, - // // this time report any parameters not found - // scape.assignParameters(args, true); - // } - // // mtp 12/7/2000 - // scape.executeOnRoot(Scape.CREATE_VIEW_RULE); - // if (Runner.isStartOnOpen()) { - // start(); - // } - // } catch (RuntimeException e) { - // if (scape.getUIEnvironment() != null) { - // scape.getUIEnvironment().showErrorDialog(scape, e); - // } else { - // throw (e); - // } - // } - // } - - /** - * Open (create) and run the model, just as in the normal open, but block - * execution. Should be used for testing model run behavior only. - */ - public void testRun() { - try { - scape.executeOnRoot(Scape.CREATE_RULE); - run(); - } catch (RuntimeException e) { - if (scape.getUIEnvironment() != null) { - scape.getUIEnvironment().showErrorDialog(scape, e); - } else { - throw e; - } - } - } - - /** - * Requests the scape to save itself, providing UI for this purpose. Will - * not occur until the current iteration is complete. Always called on root. - */ - public void save() { - if (scape.isRoot()) { - saveRequested = true; - } else { - save(); - } - } - - /** - * The basic execution cycle of a running scape. In normal usage this method - * is not called directly; use start() instead. You might choose to call - * this method directly if you want the calling code to block, for instance, - * in order to test that some behavior occurs. In the current - * implementation, only the root scape is a running thread; all child scapes - * are iterated through the root thread. Synchronous, determined, - * reproducible behavior is expected, let us know if you encounter anything - * different! The cycle always begins by notifying any observers, giving - * them a chance to observe initial state. Then, the scape waits for the - * observers to update. When updated, the simulation iterates the root scape - * and all child scapes with their rules. Again, the scape waits for the - * observers to update, and the cycle of iteration and update continues - * until it is paused or stopped. While paused, tick events will be sent to - * observers, which typically chose to ignore them. - */ - public synchronized void run() { - run(false); - } - - /** - * Sets values for the models paramters based on supplied array of key value - * pairs. Paramters and values are expected to be seperated with an "=", for - * example: "MyParameter=12". - * - * @param args - * an array of strings with paramter-value paris in the form - * "{paramter-name}={paramter-value}" - * @param reportNotFound - * if paramters not found should result in a console notification - * and if errors in invocation should be reported, false - * otherwise - */ - public static boolean assignEnvironmentParameters(String[] args) { - boolean found = args.length == 0; - for (String arg : args) { - String paramName = PropertyAccessor.paramName(arg); - found = found || assignEnvironmentParameter(arg, paramName); - } - return found; - } - - public static boolean assignEnvironmentParameter(String arg, String paramName) { - boolean found = false; - if (paramName != null) { - if (paramName.equalsIgnoreCase("DisplayGraphics")) { - Runner.setDisplayGraphics(PropertyAccessor.paramValueBoolean(arg)); - found = true; - } else if (paramName.equalsIgnoreCase("ServeGraphics")) { - Runner.setServeGraphics(PropertyAccessor.paramValueBoolean(arg)); - found = true; - } else if (paramName.equalsIgnoreCase("MultiWin")) { - Runner.setMultiWinEnvironment(PropertyAccessor.paramValueBoolean(arg)); - found = true; - } else if (paramName.equalsIgnoreCase("RedirectConsole")) { - AbstractUIEnvironment.setRedirectConsole(PropertyAccessor.paramValueBoolean(arg)); - found = true; - } else if (paramName.equalsIgnoreCase("ShowNavigator")) { - AbstractUIEnvironment.setShowNavigator(PropertyAccessor.paramValueBoolean(arg)); - found = true; - } - // if (!found && reportNotFound) { - // getEnvironment().getConsole().println("***WARNING: Parameter not found: " + paramName); - // } - } - return found; - } - - public void launch(String[] args) throws IOException { - if (args.length > 0 && args[0].indexOf("=") == -1) { - assignEnvironmentParameters(args); - // the first arg is not a "key=value" pair, so it must be the - // name of a java class implementing an Ascape model - String[] argsRem = new String[args.length - 1]; - System.arraycopy(args, 1, argsRem, 0, argsRem.length); - open(args[0], argsRem); - } else { - String fileName = null; - List argsList = new LinkedList(Arrays.asList(args)); - - for (ListIterator li = argsList.listIterator(); li.hasNext();) { - String arg = (String) li.next(); - int equalAt = arg.lastIndexOf("="); - if (equalAt < 1) { - getEnvironment().getConsole().println("Syntax error in command line: " + arg); - } else { - String paramName = arg.substring(0, equalAt); - if (paramName.equalsIgnoreCase("SavedRun")) { - fileName = arg.substring(equalAt + 1); - li.remove(); - } - } - } - if (fileName != null) { - scape = openSavedRun(fileName, (String[]) argsList.toArray(new String[0])); - } else { - // moved to top of method, so the license agreement has a frame - // environment = new UIEnvironment(); - // OK to do this here, since we know the user has to be in a GUI - // environment (don't have to wait to assign params... - // environment = new UIEnvironment(); - // UIEnvironment.checkForLicenseAgreement(); - scape = openChoose(args); - if (scape != null) { - scape.getRunner().createEnvironment(); - } else { - System.exit(0); - } - } - } - } - - /** - * The basic execution cycle of a running scape. In normal usage this methos - * is not called directly; use start() instead. You might choose to call - * this method directly if you want the calling code to block, for instance, - * in order to test that some behavior occurs. Also use this version with - * argument "true" if you want to continue using the same thread for - * restarts. - * - * @param singlethread - * CURRENTLY IGNORED! should the run if restarted continue to use the same thread? - */ - public void run(boolean singlethread) { - if (scape.getUIEnvironment() != null - && scape.getUIEnvironment().getRuntimeMode() == AbstractUIEnvironment.RELEASE_RUNTIME_MODE) { - try { - runMainLoop(); - } catch (RuntimeException e) { - scape.getUIEnvironment().showErrorDialog(scape, e); - } - } else { - runMainLoop(); - } - } - - /** - * Requests the scape to start. Note that the scape may not start - * immeadiatly. - * - * @see #setRunning - */ - public void start() { - if (!isRunning()) { - modelThread = new Thread(this, "Ascape Main Execution Loop"); - modelThread.start(); - } else { - System.out.println("Warning: Tried to start an already running scape."); - } - } - - public void notify(final ScapeEvent event, final ScapeListener listener) { - listener.scapeNotification(event); - } - - public void write(final java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - } - - /** - * Requests the scape to stop. Note that the scape will not actually stop - * until the current iteration is complete. - * - * @see #setRunning - */ - public void stop() { - setInternalRunning(false); - } - - /** - * Requests the scape to pause. (Convenience method). - * - * @see #setPaused - */ - public void pause() { - setPaused(true); - } - - /** - * Requests the scape to resume. (Convenience method). - * - * @see #setPaused - */ - public void resume() { - setPaused(false); - } - - /** - * Requests the scape to restart. - */ - public void requestRestart() { - restartRequested = true; - } - - /** - * Stops the scape and requests the scape to restart. (Convenience method). - * - * @see #setRunning - */ - public void restart() { - if (running) { - stop(); - restartRequested = true; - } else { - start(); - } - } - - /** - * Method neccessary because of amibiguous null values in simpler signature - * methods. - */ - public void openImplementation(String[] args, boolean block) { - try { - if (args != null) { - // first pass of parseSettingArgs, - // don't report any parameters not found - scape.assignParameters(args, false); - } - scape.executeOnRoot(Scape.CREATE_RULE); - if (args != null) { - // second pass of parseSettingArgs, - // this time report any parameters not found - scape.assignParameters(args, true); - } - if (scape.getEnvironment() != null) { - scape.addView(scape.getEnvironment()); - } - - scape.executeOnRoot(Scape.CREATE_VIEW_RULE); - scape.createViews(args); - - if (Runner.isStartOnOpen()) { - if (!block) { - start(); - } else { - run(); - } - } - } catch (RuntimeException e) { - if (getEnvironment() instanceof AbstractUIEnvironment) { - ((AbstractUIEnvironment) getEnvironment()).showErrorDialog(scape, e); - } else { - throw e; - } - } - } - - /** - * Creates and runs (if start on open is true) this model scape. - * - * @param applet - * the applet if are we in an applet vm context - * @param args - * paramter arguments for the scape - * @param block - * should this call block or run in a new thread? - */ - public void open(Object applet, String[] args, boolean block) { - openImplementation(args, block); - } - - /** - * Creates and runs (if start on open is true) this model scape. - * - * @param applet - * the applet if are we in an applet vm context - * @param args - * paramter arguments for the scape - */ - public void open(Object applet, String[] args) { - openImplementation(args, false); - } - - /** - * Creates and runs (if start on open is true) this model scape. - * - * @param args - * paramter arguments for the scape - * @param block - * should this call block or run in a new thread? - */ - public void open(String[] args, boolean block) { - openImplementation(args, block); - } - - /** - * Creates and runs (if start on open is true) this model scape. - * - * @param args - * paramter arguments for the scape - */ - public void open(String[] args) { - openImplementation(args, false); - } - - /** - * Creates and runs (if start on open is true) this model scape. - */ - public void open() { - openImplementation(null, false); - } - - /** - * Creates and runs (if start on open is true) this model scape. - * - * @param block - * should this call block or run in a new thread? - */ - public void open(boolean block) { - openImplementation(null, block); - } - - /** - * Requests the scape to open a saved run. - */ - public void openSavedChoose() { - closeAndOpenSavedFinally(null); - } - - public abstract void closeAndOpenSavedFinally(Scape oldScape); - - - public Object instanceFromName(String modelName) { - Object newObject; - try { - newObject = Thread.currentThread().getContextClassLoader().loadClass(modelName).newInstance(); - } catch (NullPointerException e) { - throw new RuntimeException("An error ocurred while attempting to read " + modelName + ": " + e.getMessage(), e); - } catch (InstantiationException e) { - throw new RuntimeException("An error ocurred while attempting to read " + modelName + ": " + e.getMessage(), e); - } catch (IllegalAccessException e) { - throw new RuntimeException("An error ocurred while attempting to read " + modelName + ": " + e.getMessage(), e); - } catch (ClassNotFoundException e) { - try { - Class c = Class.forName(modelName); - newObject = c.newInstance(); - } catch (Exception e2) { - throw new RuntimeException("Couldn't find class: " + modelName); - } - } - return newObject; - } - - /** - * Constructs, creates and runs (if start on open is true) the supplied model. - * - * @deprecated Applets are no longer executed this way - * @param modelName - * the fully qualified name of the Java class for the model's root scape - * @param applet - * the applet if are we in an applet vm context - * @param args - * paramter arguments for the scape - * @param block - * should this call block or run in a new thread? - */ - public Scape open(String modelName, Object applet, String[] args, boolean block) { - return open(modelName, args, block); - } - - /** - * Constructs, creates and runs the supplied model. - * - * @param modelName - * the fully qualified name of the Java class for the model's root scape - * @param args - * paramter arguments for the scape - */ - public Scape open(String modelName, String[] args) { - return open(modelName, args, false); - } - - /** - * Constructs, creates and runs the supplied model. - * - * @param modelName - * the fully qualified name of the Java class for the model's - * root scape - * @param args - * paramter arguments for the scape - * @param block - * should this call block or run in a new thread? - */ - public Scape open(String modelName, String[] args, boolean block) { - - Scape newAgent = (Scape) instanceFromName(modelName); - setRootScape(newAgent); - open(args, block); - return getRootScape(); - } - - /** - * Constructs, creates and runs the supplied model. - * - * @deprecated Applets are no longer executed this way - * @param modelName - * the fully qualified name of the Java class for the model's root scape - * @param applet - * the applet if are we in an applet vm context - */ - public Scape open(String modelName, Object applet) { - return open(modelName, null, null, false); - } - - /** - * Constructs, creates and runs the supplied model. - * - * @param modelName - * the fully qualified name of the Java class for the model's - * root scape - */ - public Scape open(String modelName, boolean block) { - return open(modelName, null, null, block); - } - - /** - * Constructs, creates and runs the supplied model. - * - * @param modelName - * the fully qualified name of the Java class for the model's - * root scape - */ - public Scape openInstance(String modelName) { - return open(modelName, null, new String[0], false); - } - - /** - * Requests the scape to open a model, providing UI for this purpose. - */ - public Scape openChoose() { - return openChoose(null); - } - - /** - * Requests the scape to open a model, providing UI for this purpose. - */ - public Scape openChoose(String[] args) { - String modelName = ((AbstractUIEnvironment) environment).openDialog(); - Scape scape = null; - if (modelName != null) { - scape = open(modelName, args); - } - return scape; - } - - /* - * To be done (perhaps) Opens the specified scape from a class file. - */ - /* - * public static void open(File file) { FileInputStream f = new - * FileInputStream(file); ObjectInputStream s = new ObjectInputStream(f); - * try { Scape newScape = (Scape) s.readObject(); } catch - * (ClassNotFoundException e) { JOptionPane.showMessageDialog(null, "A class - * couldn't be found (make sure you have all the appropriate model classes - * installed): " + e, "Error", JOptionPane.INFORMATION_MESSAGE); } } - */ - - /** - * Save the state of the scape to a file. - */ - public abstract void saveChoose(); - - public void close() { - closeRequested = true; - if (running) { - // Running, so we have to allow current iteration to finish - stop(); - } else { - closeFinally(); - } - } - - /** - * Closes the application; allowing views to close themseleves gracefully. - * Do not call this method directly unless you want to force close; call - * <code>close()</code> instead, allowing a running scape to stop - * gracefully. Override this method if you want to provide any scape related - * pre-quit finalization or clean-up. - * - * @see #quit - */ - public void closeFinally() { - dataGroup = null; - // Send an event to self for quit - scape.executeOnRoot(new NotifyViews(ScapeEvent.REQUEST_CLOSE)); - waitForViewsUpdate(); - } - - /** - * Exits the application; calling stop if running and allowing views to - * close themseleves gracefully. Override <code>quitFinally</code> if you - * want to provide any pre-quit finalization or clean-up. - * - * @see #quitFinally - */ - public void quit() { - if (inMainLoop) { - quitRequested = true; - // Running, so we have to allow current iteration to finish - stop(); - } - // Only one chance a quit request, otherwise we assume that the scape is - // allready fulfilling it. - else if (!quitRequested) { - quitFinally(); - } - } - - /** - * Exits the application; allowing views to close themseleves gracefully. Do - * not call this method directly unless you want to force quit; call - * <code>quit()</code> instead, allowing a running scape to stop - * gracefully. Override this method if you want to provide any scape related - * pre-quit finalization or clean-up. - * - * @see #quit - */ - public void quitFinally() { - closeFinally(); - scape.executeOnRoot(new NotifyViews(ScapeEvent.REQUEST_QUIT)); - waitForViewsUpdate(); - exit(); - } - - /** - * Final kill. Calls System exit, which appears neccessary for vm even when - * code has finished. - */ - public static void exit() { - try { - System.exit(0); - } catch (SecurityException e) { - System.out - .println("Can't quit in this security context. (Scape is probably running in browser or viewer; quit or change that.)"); - } - } - - public void createEnvironment() { - if (environment == null) { - environment = new RuntimeEnvironment(); - } - } - - public static boolean isDisplayGraphics() { - try { - return displayGraphics && !GraphicsEnvironment.isHeadless(); - } catch (HeadlessException e) { - return false; - } - } - - public static void setDisplayGraphics(boolean displayGraphics) { - Runner.displayGraphics = displayGraphics; - } - - public static boolean isServeGraphics() { - return serveGraphics; - } - - public static void setServeGraphics(boolean serveGraphics) { - Runner.serveGraphics = serveGraphics; - } - - public static boolean isMultiWinEnvironment() { - return muiltWinEnvironment; - } - - public static void setMultiWinEnvironment(boolean muiltWinEnvironment) { - Runner.muiltWinEnvironment = muiltWinEnvironment; - } - - /** - * Sets the period name for the delegate - * - * @return the periodName - */ - public String getPeriodName() { - return periodName; - } - - /** - * Sets periodName for the ModelRoot object. - * - * @param periodName - * the periodName - */ - public void setPeriodName(String periodName) { - this.periodName = periodName; - } - - /** - * Gets the description for the ModelRoot object. - * - * @return the description - */ - public String getDescription() { - return description; - } - - /** - * Sets description for the ModelRoot object. - * - * @param description - * the description - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Returns a brief descripiton (including credits) of the scape or of the - * model, if this is root scape. Plaintext. - * - * @return the description - */ - public String getHTMLDescription() { - return HTMLDescription; - } - - /** - * Sets an html-formatted description to be used for the model as a whole. - * - * @param HTMLdescription - * the description - */ - public void setHTMLDescription(String HTMLdescription) { - this.HTMLDescription = HTMLdescription; - } - - /** - * Returns the period that this model started. - * - * @return the startPeriod - */ - public int getStartPeriod() { - return startPeriod; - } - - /** - * Gets the startOnOpen for the Runner object. Start on Open is static - * because we may not have a scape context. - * - * @return the startOnOpen - */ - public static boolean isStartOnOpen() { - return startOnOpen; - } - - /** - * Sets startOnOpen for the ModelRoot object. - * - * @param startOnOpen - * the startOnOpen - */ - public static void setStartOnOpen(boolean _startOnOpen) { - startOnOpen = _startOnOpen; - } - - /** - * Sets the start period for this scape. The start period is the period this - * scape is given when a model run is started. - * - * @param startPeriod - * the period to begin runs at - * @throws org.ascape.model.space.SpatialTemporalException - * exception - */ - public void setStartPeriod(int startPeriod) throws SpatialTemporalException { - if (startPeriod >= earliestPeriod) { - this.startPeriod = startPeriod; - } else { - throw new SpatialTemporalException("Tried to set start period before earliest period"); - } - } - - /** - * Returns the period this scape stops running at. By default, the lesser of - * latest period and integer maximum value (effectively unlimited.) - * - * @return the stopPeriod - */ - public int getStopPeriod() { - return stopPeriod; - } - - /** - * Sets the stop period for this scape. The stop period is the period that - * the scape is automatically stopped at. The scape may be automatically set - * to start agina at start value is the scape is set to restart. - * - * @param stopPeriod - * the period the scape will stop at upon reaching - * @see #setAutoRestart - * @throws org.ascape.model.space.SpatialTemporalException - * exception - */ - public void setStopPeriod(int stopPeriod) throws SpatialTemporalException { - if (stopPeriod <= latestPeriod) { - this.stopPeriod = stopPeriod; - } else { - throw new SpatialTemporalException("Tried to set stop period after latest period"); - } - } - - /** - * Gets the pausePeriod for the ModelRoot object. - * - * @return the pausePeriod - */ - public int getPausePeriod() { - return pausePeriod; - } - - /** - * Sets pausePeriod for the ModelRoot object. - * - * @param pausePeriod - * the pausePeriod - */ - public void setPausePeriod(int pausePeriod) { - this.pausePeriod = pausePeriod; - } - - /** - * Gets the earliestPeriod for the ModelRoot object. - * - * @return the earliestPeriod - */ - public int getEarliestPeriod() { - return earliestPeriod; - } - - /** - * Sets the earliest period this scape is expected to be run at. 0 by - * default. - * - * @param earliestPeriod - * the lowest period value this scape can have - */ - public void setEarliestPeriod(int earliestPeriod) { - this.earliestPeriod = earliestPeriod; - if (startPeriod < earliestPeriod) { - try { - setStartPeriod(earliestPeriod); - } - // "Can't" happen - catch (SpatialTemporalException e) { - throw new RuntimeException("Internal Logic Error"); - } - } - } - - /** - * Gets the latestPeriod for the ModelRoot object. - * - * @return the latestPeriod - */ - public int getLatestPeriod() { - return latestPeriod; - } - - /** - * Sets the latest period this scape is expected to be run at. Max of - * integer (effectively unlimited) by default. - * - * @param latestPeriod - * the highest period value this scape can have - */ - public void setLatestPeriod(int latestPeriod) { - this.latestPeriod = latestPeriod; - if (stopPeriod > latestPeriod) { - try { - setStopPeriod(latestPeriod); - } - // "Can't" happen - catch (SpatialTemporalException e) { - throw new RuntimeException("Internal Logic Error"); - } - } - } - - /** - * Gets the restartingViews for the ModelRoot object. - * - * @return the restartingViews - */ - public List getRestartingViews() { - return restartingViews; - } - - /** - * Sets restartingViews for the ModelRoot object. - * - * @param restartingViews - * the restartingViews - */ - public void setRestartingViews(List restartingViews) { - this.restartingViews = restartingViews; - } - - /** - * Gets the AutoRestart for the ModelRoot object. - * - * @return the Restart state - */ - public boolean isAutoRestart() { - return autoRestart; - } - - /** - * Sets Restart for the ModelRoot object. - * - * @param autoRestart - * should the model restart when it ends? - */ - public void setAutoRestart(boolean autoRestart) { - this.autoRestart = autoRestart; - } - - /** - * Is the supplied period a valid period for this scape? - * - * @param period - * the period to test - * @return true if within earliest and latest periods, false otherwise - */ - public boolean isValidPeriod(int period) { - return period >= earliestPeriod && period <= latestPeriod; - } - - /** - * Returns the path in which all files should by default be stored to and - * retrieved from. Nonstatic, so that parameter can automatically be set - * from command line, but backing variable is static. Default is "./", can - * be modified by calling setHome or providing an ascape.home java property. - * (This may change now since it is no longer neccesary.) - * - * @return the home - */ - public String getHome() { - if (home == null) { - home = "./"; - try { - home = System.getProperty("ascape.home", home); - } - // Ignore security exception; we may be running in an environment - // such as an applet where we can't get ascape home - // no probel,m we just need to make sure that lib, etc. are in the - // right place. - catch (SecurityException e) { - } - } - return home; - } - - /** - * Sets the path in which to store all scape related files. Nonstatic, so - * that parameter can automatically be set from command line, but backing - * variable is static. - * - * @param home - * the home - */ - public void setHome(String home) { - this.home = home; - } - - public RuntimeEnvironment getEnvironment() { - return environment; - } - - public boolean isBeginningDeserializedRun() { - return beginningDeserializedRun; - } - - public void setBeginningDeserializedRun(boolean beginningDeserializedRun) { - this.beginningDeserializedRun = beginningDeserializedRun; - } - - public boolean isCloseAndOpenNewRequested() { - return closeAndOpenNewRequested; - } - - public void setCloseAndOpenNewRequested(boolean closeAndOpenNewRequested) { - this.closeAndOpenNewRequested = closeAndOpenNewRequested; - } - - public boolean isCloseAndOpenSavedRequested() { - return closeAndOpenSavedRequested; - } - - public void setCloseAndOpenSavedRequested(boolean closeAndOpenSavedRequested) { - this.closeAndOpenSavedRequested = closeAndOpenSavedRequested; - } - - public boolean isCloseRequested() { - return closeRequested; - } - - public void setCloseRequested(boolean closeRequested) { - this.closeRequested = closeRequested; - } - - public boolean isInMainLoop() { - return inMainLoop; - } - - public void setInMainLoop(boolean inMainLoop) { - this.inMainLoop = inMainLoop; - } - - public int getIteration() { - return iteration; - } - - public void setIteration(int iteration) { - this.iteration = iteration; - } - - public boolean isOpenRequested() { - return openRequested; - } - - public void setOpenRequested(boolean openRequested) { - this.openRequested = openRequested; - } - - public boolean isPaused() { - return paused; - } - - public void setPaused(boolean paused) { - this.paused = paused; - } - - public int getPeriod() { - return period; - } - - public void setPeriod(int period) { - this.period = period; - } - - public boolean isQuitRequested() { - return quitRequested; - } - - public void setQuitRequested(boolean quitRequested) { - this.quitRequested = quitRequested; - } - - public boolean isRestartRequested() { - return restartRequested; - } - - public void setRestartRequested(boolean restartRequested) { - this.restartRequested = restartRequested; - } - - public boolean isRunning() { - return running; - } - - public void setRunning(boolean running) { - if (running) { - start(); - } else { - stop(); - } - } - - public void setInternalRunning(boolean running) { - this.running = running; - } - - public boolean isSaveRequested() { - return saveRequested; - } - - public void setSaveRequested(boolean saveRequested) { - this.saveRequested = saveRequested; - } - - public boolean isStep() { - return step; - } - - public void setStep(boolean step) { - this.step = step; - } - - public DataGroup getData() { - return dataGroup; - } - - public void setRootScape(Scape scape) { - this.scape = scape; - scape.setRunner(this); - dataGroup = new DataGroup(); - dataGroup.setScape(scape); - } - - public Scape getRootScape() { - return scape; - } - - public void setEnvironment(RuntimeEnvironment environment) { - this.environment = environment; - } - - public Thread getModelThread() { - return modelThread; - } + private static final long serialVersionUID = 1L; + + public void scapeClosing(ScapeEvent scapeEvent) { + openInstance(modelName); + } + }); + oldScape.getRunner().close(); + } + } + + public static Scape openSavedRun(InputStream is) throws IOException { + Scape newScape = null; + GZIPInputStream gis = new GZIPInputStream(is); + ObjectInputStream ois = new ObjectInputStream(gis); + + try { + newScape = (Scape) ois.readObject(); + ois.close(); + // startPeriod is static so we have to set it here + // we set it to scape.period + 1 so that there is not a blank first + // point in the charts + try { + newScape.setStartPeriod(newScape.getPeriod() + 1); + } catch (SpatialTemporalException e) { + try { + newScape.setStartPeriod(newScape.getPeriod()); + } catch (SpatialTemporalException e1) { + try { + newScape.setStartPeriod(newScape.getPeriod()); + } catch (SpatialTemporalException e2) { + throw new RuntimeException("Internal Error"); + } + } + } + newScape.getRunner().beginningDeserializedRun = true; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return newScape; + } + + public Scape openSavedRun(String fileName, String[] args) throws IOException { + Scape newScape = openSavedRun(new File(fileName)); + + if (newScape != null) { + + if (args.length > 0) { + newScape.assignParameters(args, true); + } + newScape.getRunner().createEnvironment(); + + if (newScape.isPaused() && newScape.isStartOnOpen()) { + newScape.getRunner().resume(); + } + + newScape.getRunner().runMainLoop(); + } + + return newScape; + } + + public Scape openSavedRun(File savedRunFile) throws IOException { + Scape newScape = null; + InputStream is = new FileInputStream(savedRunFile); + newScape = openSavedRun(is); + return newScape; + } + + // public void createNewModel() { + // createNewModel(null, new String[0]); + // } + // + // public void createNewModel(Object applet, String[] args) { + // try { + // if (applet != null) { + // scape.getUIEnvironment().setApplet(applet, scape); + // } + // if (args != null) { + // // first pass of parseSettingArgs, + // // don't report any parameters not found + // scape.assignParameters(args, false); + // } + // scape.executeOnRoot(Scape.CREATE_RULE); + // if (args != null) { + // // second pass of parseSettingArgs, + // // this time report any parameters not found + // scape.assignParameters(args, true); + // } + // // mtp 12/7/2000 + // scape.executeOnRoot(Scape.CREATE_VIEW_RULE); + // if (Runner.isStartOnOpen()) { + // start(); + // } + // } catch (RuntimeException e) { + // if (scape.getUIEnvironment() != null) { + // scape.getUIEnvironment().showErrorDialog(scape, e); + // } else { + // throw (e); + // } + // } + // } + + /** + * Open (create) and run the model, just as in the normal open, but block + * execution. Should be used for testing model run behavior only. + */ + public void testRun() { + try { + scape.executeOnRoot(Scape.CREATE_RULE); + run(); + } catch (RuntimeException e) { + if (scape.getUIEnvironment() != null) { + scape.getUIEnvironment().showErrorDialog(scape, e); + } else { + throw e; + } + } + } + + /** + * Requests the scape to save itself, providing UI for this purpose. Will not + * occur until the current iteration is complete. Always called on root. + */ + public void save() { + if (scape.isRoot()) { + saveRequested = true; + } else { + save(); + } + } + + /** + * The basic execution cycle of a running scape. In normal usage this method + * is not called directly; use start() instead. You might choose to call this + * method directly if you want the calling code to block, for instance, in + * order to test that some behavior occurs. In the current implementation, + * only the root scape is a running thread; all child scapes are iterated + * through the root thread. Synchronous, determined, reproducible behavior is + * expected, let us know if you encounter anything different! The cycle always + * begins by notifying any observers, giving them a chance to observe initial + * state. Then, the scape waits for the observers to update. When updated, the + * simulation iterates the root scape and all child scapes with their rules. + * Again, the scape waits for the observers to update, and the cycle of + * iteration and update continues until it is paused or stopped. While paused, + * tick events will be sent to observers, which typically chose to ignore + * them. + */ + public synchronized void run() { + run(false); + } + + /** + * Sets values for the models paramters based on supplied array of key value + * pairs. Paramters and values are expected to be seperated with an "=", for + * example: "MyParameter=12". + * + * @param args + * an array of strings with paramter-value paris in the form + * "{paramter-name}={paramter-value}" + * @param reportNotFound + * if paramters not found should result in a console notification and + * if errors in invocation should be reported, false otherwise + */ + public static boolean assignEnvironmentParameters(String[] args) { + boolean found = args.length == 0; + for (String arg : args) { + String paramName = PropertyAccessor.paramName(arg); + found = found || assignEnvironmentParameter(arg, paramName); + } + return found; + } + + public static boolean assignEnvironmentParameter(String arg, String paramName) { + boolean found = false; + if (paramName != null) { + if (paramName.equalsIgnoreCase("DisplayGraphics")) { + Runner.setDisplayGraphics(PropertyAccessor.paramValueBoolean(arg)); + found = true; + } else if (paramName.equalsIgnoreCase("ServeGraphics")) { + Runner.setServeGraphics(PropertyAccessor.paramValueBoolean(arg)); + found = true; + } else if (paramName.equalsIgnoreCase("MultiWin")) { + Runner.setMultiWinEnvironment(PropertyAccessor.paramValueBoolean(arg)); + found = true; + } else if (paramName.equalsIgnoreCase("RedirectConsole")) { + AbstractUIEnvironment.setRedirectConsole(PropertyAccessor.paramValueBoolean(arg)); + found = true; + } else if (paramName.equalsIgnoreCase("ShowNavigator")) { + AbstractUIEnvironment.setShowNavigator(PropertyAccessor.paramValueBoolean(arg)); + found = true; + } + // if (!found && reportNotFound) { + // getEnvironment().getConsole().println("***WARNING: Parameter not found: " + // + paramName); + // } + } + return found; + } + + public void launch(String[] args) throws IOException { + if (args.length > 0 && args[0].indexOf("=") == -1) { + assignEnvironmentParameters(args); + // the first arg is not a "key=value" pair, so it must be the + // name of a java class implementing an Ascape model + String[] argsRem = new String[args.length - 1]; + System.arraycopy(args, 1, argsRem, 0, argsRem.length); + open(args[0], argsRem); + } else { + String fileName = null; + List argsList = new LinkedList(Arrays.asList(args)); + + for (ListIterator li = argsList.listIterator(); li.hasNext();) { + String arg = (String) li.next(); + int equalAt = arg.lastIndexOf("="); + if (equalAt < 1) { + getEnvironment().getConsole().println("Syntax error in command line: " + arg); + } else { + String paramName = arg.substring(0, equalAt); + if (paramName.equalsIgnoreCase("SavedRun")) { + fileName = arg.substring(equalAt + 1); + li.remove(); + } + } + } + if (fileName != null) { + scape = openSavedRun(fileName, (String[]) argsList.toArray(new String[0])); + } else { + // moved to top of method, so the license agreement has a frame + // environment = new UIEnvironment(); + // OK to do this here, since we know the user has to be in a GUI + // environment (don't have to wait to assign params... + // environment = new UIEnvironment(); + // UIEnvironment.checkForLicenseAgreement(); + scape = openChoose(args); + if (scape != null) { + scape.getRunner().createEnvironment(); + } else { + System.exit(0); + } + } + } + } + + /** + * The basic execution cycle of a running scape. In normal usage this methos + * is not called directly; use start() instead. You might choose to call this + * method directly if you want the calling code to block, for instance, in + * order to test that some behavior occurs. Also use this version with + * argument "true" if you want to continue using the same thread for restarts. + * + * @param singlethread + * CURRENTLY IGNORED! should the run if restarted continue to use the + * same thread? + */ + public void run(boolean singlethread) { + if (scape.getUIEnvironment() != null && scape.getUIEnvironment().getRuntimeMode() == AbstractUIEnvironment.RELEASE_RUNTIME_MODE) { + try { + runMainLoop(); + } catch (RuntimeException e) { + scape.getUIEnvironment().showErrorDialog(scape, e); + } + } else { + runMainLoop(); + } + } + + /** + * Requests the scape to start. Note that the scape may not start immeadiatly. + * + * @see #setRunning + */ + public void start() { + if (!isRunning()) { + modelThread = new Thread(this, "Ascape Main Execution Loop"); + modelThread.start(); + } else { + System.out.println("Warning: Tried to start an already running scape."); + } + } + + public void notify(final ScapeEvent event, final ScapeListener listener) { + listener.scapeNotification(event); + } + + public void write(final java.io.ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + } + + /** + * Requests the scape to stop. Note that the scape will not actually stop + * until the current iteration is complete. + * + * @see #setRunning + */ + public void stop() { + setInternalRunning(false); + } + + /** + * Requests the scape to pause. (Convenience method). + * + * @see #setPaused + */ + public void pause() { + setPaused(true); + } + + /** + * Requests the scape to resume. (Convenience method). + * + * @see #setPaused + */ + public void resume() { + setPaused(false); + } + + /** + * Requests the scape to restart. + */ + public void requestRestart() { + restartRequested = true; + } + + /** + * Stops the scape and requests the scape to restart. (Convenience method). + * + * @see #setRunning + */ + public void restart() { + if (running) { + stop(); + restartRequested = true; + } else { + start(); + } + } + + /** + * Method neccessary because of amibiguous null values in simpler signature + * methods. + */ + public void openImplementation(String[] args, boolean block) { + try { + if (args != null) { + // first pass of parseSettingArgs, + // don't report any parameters not found + scape.assignParameters(args, false); + } + scape.executeOnRoot(Scape.CREATE_RULE); + if (args != null) { + // second pass of parseSettingArgs, + // this time report any parameters not found + scape.assignParameters(args, true); + } + if (scape.getEnvironment() != null) { + scape.addView(scape.getEnvironment()); + } + + scape.executeOnRoot(Scape.CREATE_VIEW_RULE); + scape.createViews(args); + + if (Runner.isStartOnOpen()) { + if (!block) { + start(); + } else { + run(); + } + } + } catch (RuntimeException e) { + if (getEnvironment() instanceof AbstractUIEnvironment) { + ((AbstractUIEnvironment) getEnvironment()).showErrorDialog(scape, e); + } else { + throw e; + } + } + } + + /** + * Creates and runs (if start on open is true) this model scape. + * + * @param applet + * the applet if are we in an applet vm context + * @param args + * paramter arguments for the scape + * @param block + * should this call block or run in a new thread? + */ + public void open(Object applet, String[] args, boolean block) { + openImplementation(args, block); + } + + /** + * Creates and runs (if start on open is true) this model scape. + * + * @param applet + * the applet if are we in an applet vm context + * @param args + * paramter arguments for the scape + */ + public void open(Object applet, String[] args) { + openImplementation(args, false); + } + + /** + * Creates and runs (if start on open is true) this model scape. + * + * @param args + * paramter arguments for the scape + * @param block + * should this call block or run in a new thread? + */ + public void open(String[] args, boolean block) { + openImplementation(args, block); + } + + /** + * Creates and runs (if start on open is true) this model scape. + * + * @param args + * paramter arguments for the scape + */ + public void open(String[] args) { + openImplementation(args, false); + } + + /** + * Creates and runs (if start on open is true) this model scape. + */ + public void open() { + openImplementation(null, false); + } + + /** + * Creates and runs (if start on open is true) this model scape. + * + * @param block + * should this call block or run in a new thread? + */ + public void open(boolean block) { + openImplementation(null, block); + } + + /** + * Requests the scape to open a saved run. + */ + public void openSavedChoose() { + closeAndOpenSavedFinally(null); + } + + public abstract void closeAndOpenSavedFinally(Scape oldScape); + + public Object instanceFromName(String modelName) { + Object newObject; + try { + newObject = Thread.currentThread().getContextClassLoader().loadClass(modelName).newInstance(); + } catch (NullPointerException e) { + throw new RuntimeException("An error ocurred while attempting to read " + modelName + ": " + e.getMessage(), e); + } catch (InstantiationException e) { + throw new RuntimeException("An error ocurred while attempting to read " + modelName + ": " + e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new RuntimeException("An error ocurred while attempting to read " + modelName + ": " + e.getMessage(), e); + } catch (ClassNotFoundException e) { + try { + Class c = Class.forName(modelName); + newObject = c.newInstance(); + } catch (Exception e2) { + throw new RuntimeException("Couldn't find class: " + modelName); + } + } + return newObject; + } + + /** + * Constructs, creates and runs (if start on open is true) the supplied model. + * + * @deprecated Applets are no longer executed this way + * @param modelName + * the fully qualified name of the Java class for the model's root + * scape + * @param applet + * the applet if are we in an applet vm context + * @param args + * paramter arguments for the scape + * @param block + * should this call block or run in a new thread? + */ + public Scape open(String modelName, Object applet, String[] args, boolean block) { + return open(modelName, args, block); + } + + /** + * Constructs, creates and runs the supplied model. + * + * @param modelName + * the fully qualified name of the Java class for the model's root + * scape + * @param args + * paramter arguments for the scape + */ + public Scape open(String modelName, String[] args) { + return open(modelName, args, false); + } + + /** + * Constructs, creates and runs the supplied model. + * + * @param modelName + * the fully qualified name of the Java class for the model's root + * scape + * @param args + * paramter arguments for the scape + * @param block + * should this call block or run in a new thread? + */ + public Scape open(String modelName, String[] args, boolean block) { + + Scape newAgent = (Scape) instanceFromName(modelName); + setRootScape(newAgent); + open(args, block); + return getRootScape(); + } + + /** + * Constructs, creates and runs the supplied model. + * + * @deprecated Applets are no longer executed this way + * @param modelName + * the fully qualified name of the Java class for the model's root + * scape + * @param applet + * the applet if are we in an applet vm context + */ + public Scape open(String modelName, Object applet) { + return open(modelName, null, null, false); + } + + /** + * Constructs, creates and runs the supplied model. + * + * @param modelName + * the fully qualified name of the Java class for the model's root + * scape + */ + public Scape open(String modelName, boolean block) { + return open(modelName, null, null, block); + } + + /** + * Constructs, creates and runs the supplied model. + * + * @param modelName + * the fully qualified name of the Java class for the model's root + * scape + */ + public Scape openInstance(String modelName) { + return open(modelName, null, new String[0], false); + } + + /** + * Requests the scape to open a model, providing UI for this purpose. + */ + public Scape openChoose() { + return openChoose(null); + } + + /** + * Requests the scape to open a model, providing UI for this purpose. + */ + public Scape openChoose(String[] args) { + String modelName = ((AbstractUIEnvironment) environment).openDialog(); + Scape scape = null; + if (modelName != null) { + scape = open(modelName, args); + } + return scape; + } + + /* + * To be done (perhaps) Opens the specified scape from a class file. + */ + /* + * public static void open(File file) { FileInputStream f = new + * FileInputStream(file); ObjectInputStream s = new ObjectInputStream(f); try + * { Scape newScape = (Scape) s.readObject(); } catch (ClassNotFoundException + * e) { JOptionPane.showMessageDialog(null, "A class couldn't be found (make + * sure you have all the appropriate model classes installed): " + e, "Error", + * JOptionPane.INFORMATION_MESSAGE); } } + */ + + /** + * Save the state of the scape to a file. + */ + public abstract void saveChoose(); + + public void close() { + closeRequested = true; + if (running) { + // Running, so we have to allow current iteration to finish + stop(); + } else { + closeFinally(); + } + } + + /** + * Closes the application; allowing views to close themseleves gracefully. Do + * not call this method directly unless you want to force close; call + * <code>close()</code> instead, allowing a running scape to stop gracefully. + * Override this method if you want to provide any scape related pre-quit + * finalization or clean-up. + * + * @see #quit + */ + public void closeFinally() { + dataGroup = null; + // Send an event to self for quit + scape.executeOnRoot(new NotifyViews(ScapeEvent.REQUEST_CLOSE)); + waitForViewsUpdate(); + } + + /** + * Exits the application; calling stop if running and allowing views to close + * themseleves gracefully. Override <code>quitFinally</code> if you want to + * provide any pre-quit finalization or clean-up. + * + * @see #quitFinally + */ + public void quit() { + if (inMainLoop) { + quitRequested = true; + // Running, so we have to allow current iteration to finish + stop(); + } + // Only one chance a quit request, otherwise we assume that the scape is + // allready fulfilling it. + else if (!quitRequested) { + quitFinally(); + } + } + + /** + * Exits the application; allowing views to close themseleves gracefully. Do + * not call this method directly unless you want to force quit; call + * <code>quit()</code> instead, allowing a running scape to stop gracefully. + * Override this method if you want to provide any scape related pre-quit + * finalization or clean-up. + * + * @see #quit + */ + public void quitFinally() { + closeFinally(); + scape.executeOnRoot(new NotifyViews(ScapeEvent.REQUEST_QUIT)); + waitForViewsUpdate(); + exit(); + } + + /** + * Final kill. Calls System exit, which appears neccessary for vm even when + * code has finished. + */ + public static void exit() { + try { + System.exit(0); + } catch (SecurityException e) { + System.out.println("Can't quit in this security context. (Scape is probably running in browser or viewer; quit or change that.)"); + } + } + + public void createEnvironment() { + if (environment == null) { + environment = new RuntimeEnvironment(); + } + } + + public static boolean isDisplayGraphics() { + try { + return displayGraphics && !GraphicsEnvironment.isHeadless(); + } catch (HeadlessException e) { + return false; + } + } + + public static void setDisplayGraphics(boolean displayGraphics) { + Runner.displayGraphics = displayGraphics; + } + + public static boolean isServeGraphics() { + return serveGraphics; + } + + public static void setServeGraphics(boolean serveGraphics) { + Runner.serveGraphics = serveGraphics; + } + + public static boolean isMultiWinEnvironment() { + return muiltWinEnvironment; + } + + public static void setMultiWinEnvironment(boolean muiltWinEnvironment) { + Runner.muiltWinEnvironment = muiltWinEnvironment; + } + + /** + * Sets the period name for the delegate + * + * @return the periodName + */ + public String getPeriodName() { + return periodName; + } + + /** + * Sets periodName for the ModelRoot object. + * + * @param periodName + * the periodName + */ + public void setPeriodName(String periodName) { + this.periodName = periodName; + } + + /** + * Gets the description for the ModelRoot object. + * + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * Sets description for the ModelRoot object. + * + * @param description + * the description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Returns a brief descripiton (including credits) of the scape or of the + * model, if this is root scape. Plaintext. + * + * @return the description + */ + public String getHTMLDescription() { + return HTMLDescription; + } + + /** + * Sets an html-formatted description to be used for the model as a whole. + * + * @param HTMLdescription + * the description + */ + public void setHTMLDescription(String HTMLdescription) { + this.HTMLDescription = HTMLdescription; + } + + /** + * Returns the period that this model started. + * + * @return the startPeriod + */ + public int getStartPeriod() { + return startPeriod; + } + + /** + * Gets the startOnOpen for the Runner object. Start on Open is static because + * we may not have a scape context. + * + * @return the startOnOpen + */ + public static boolean isStartOnOpen() { + return startOnOpen; + } + + /** + * Sets startOnOpen for the ModelRoot object. + * + * @param startOnOpen + * the startOnOpen + */ + public static void setStartOnOpen(boolean _startOnOpen) { + startOnOpen = _startOnOpen; + } + + /** + * Sets the start period for this scape. The start period is the period this + * scape is given when a model run is started. + * + * @param startPeriod + * the period to begin runs at + * @throws org.ascape.model.space.SpatialTemporalException + * exception + */ + public void setStartPeriod(int startPeriod) throws SpatialTemporalException { + if (startPeriod >= earliestPeriod) { + this.startPeriod = startPeriod; + } else { + throw new SpatialTemporalException("Tried to set start period before earliest period"); + } + } + + /** + * Returns the period this scape stops running at. By default, the lesser of + * latest period and integer maximum value (effectively unlimited.) + * + * @return the stopPeriod + */ + public int getStopPeriod() { + return stopPeriod; + } + + /** + * Sets the stop period for this scape. The stop period is the period that the + * scape is automatically stopped at. The scape may be automatically set to + * start agina at start value is the scape is set to restart. + * + * @param stopPeriod + * the period the scape will stop at upon reaching + * @see #setAutoRestart + * @throws org.ascape.model.space.SpatialTemporalException + * exception + */ + public void setStopPeriod(int stopPeriod) throws SpatialTemporalException { + if (stopPeriod <= latestPeriod) { + this.stopPeriod = stopPeriod; + } else { + throw new SpatialTemporalException("Tried to set stop period after latest period"); + } + } + + /** + * Gets the pausePeriod for the ModelRoot object. + * + * @return the pausePeriod + */ + public int getPausePeriod() { + return pausePeriod; + } + + /** + * Sets pausePeriod for the ModelRoot object. + * + * @param pausePeriod + * the pausePeriod + */ + public void setPausePeriod(int pausePeriod) { + this.pausePeriod = pausePeriod; + } + + /** + * Gets the earliestPeriod for the ModelRoot object. + * + * @return the earliestPeriod + */ + public int getEarliestPeriod() { + return earliestPeriod; + } + + /** + * Sets the earliest period this scape is expected to be run at. 0 by default. + * + * @param earliestPeriod + * the lowest period value this scape can have + */ + public void setEarliestPeriod(int earliestPeriod) { + this.earliestPeriod = earliestPeriod; + if (startPeriod < earliestPeriod) { + try { + setStartPeriod(earliestPeriod); + } + // "Can't" happen + catch (SpatialTemporalException e) { + throw new RuntimeException("Internal Logic Error"); + } + } + } + + /** + * Gets the latestPeriod for the ModelRoot object. + * + * @return the latestPeriod + */ + public int getLatestPeriod() { + return latestPeriod; + } + + /** + * Sets the latest period this scape is expected to be run at. Max of integer + * (effectively unlimited) by default. + * + * @param latestPeriod + * the highest period value this scape can have + */ + public void setLatestPeriod(int latestPeriod) { + this.latestPeriod = latestPeriod; + if (stopPeriod > latestPeriod) { + try { + setStopPeriod(latestPeriod); + } + // "Can't" happen + catch (SpatialTemporalException e) { + throw new RuntimeException("Internal Logic Error"); + } + } + } + + /** + * Gets the restartingViews for the ModelRoot object. + * + * @return the restartingViews + */ + public List getRestartingViews() { + return restartingViews; + } + + /** + * Sets restartingViews for the ModelRoot object. + * + * @param restartingViews + * the restartingViews + */ + public void setRestartingViews(List restartingViews) { + this.restartingViews = restartingViews; + } + + /** + * Gets the AutoRestart for the ModelRoot object. + * + * @return the Restart state + */ + public boolean isAutoRestart() { + return autoRestart; + } + + /** + * Sets Restart for the ModelRoot object. + * + * @param autoRestart + * should the model restart when it ends? + */ + public void setAutoRestart(boolean autoRestart) { + this.autoRestart = autoRestart; + } + + /** + * Is the supplied period a valid period for this scape? + * + * @param period + * the period to test + * @return true if within earliest and latest periods, false otherwise + */ + public boolean isValidPeriod(int period) { + return period >= earliestPeriod && period <= latestPeriod; + } + + /** + * Returns the path in which all files should by default be stored to and + * retrieved from. Nonstatic, so that parameter can automatically be set from + * command line, but backing variable is static. Default is "./", can be + * modified by calling setHome or providing an ascape.home java property. + * (This may change now since it is no longer neccesary.) + * + * @return the home + */ + public String getHome() { + if (home == null) { + home = "./"; + try { + home = System.getProperty("ascape.home", home); + } + // Ignore security exception; we may be running in an environment + // such as an applet where we can't get ascape home + // no probel,m we just need to make sure that lib, etc. are in the + // right place. + catch (SecurityException e) { + } + } + return home; + } + + /** + * Sets the path in which to store all scape related files. Nonstatic, so that + * parameter can automatically be set from command line, but backing variable + * is static. + * + * @param home + * the home + */ + public void setHome(String home) { + this.home = home; + } + + public RuntimeEnvironment getEnvironment() { + return environment; + } + + public boolean isBeginningDeserializedRun() { + return beginningDeserializedRun; + } + + public void setBeginningDeserializedRun(boolean beginningDeserializedRun) { + this.beginningDeserializedRun = beginningDeserializedRun; + } + + public boolean isCloseAndOpenNewRequested() { + return closeAndOpenNewRequested; + } + + public void setCloseAndOpenNewRequested(boolean closeAndOpenNewRequested) { + this.closeAndOpenNewRequested = closeAndOpenNewRequested; + } + + public boolean isCloseAndOpenSavedRequested() { + return closeAndOpenSavedRequested; + } + + public void setCloseAndOpenSavedRequested(boolean closeAndOpenSavedRequested) { + this.closeAndOpenSavedRequested = closeAndOpenSavedRequested; + } + + public boolean isCloseRequested() { + return closeRequested; + } + + public void setCloseRequested(boolean closeRequested) { + this.closeRequested = closeRequested; + } + + public boolean isInMainLoop() { + return inMainLoop; + } + + public void setInMainLoop(boolean inMainLoop) { + this.inMainLoop = inMainLoop; + } + + public int getIteration() { + return iteration; + } + + public void setIteration(int iteration) { + this.iteration = iteration; + } + + public boolean isOpenRequested() { + return openRequested; + } + + public void setOpenRequested(boolean openRequested) { + this.openRequested = openRequested; + } + + public boolean isPaused() { + return paused; + } + + public void setPaused(boolean paused) { + this.paused = paused; + } + + public int getPeriod() { + return period; + } + + public void setPeriod(int period) { + this.period = period; + } + + public boolean isQuitRequested() { + return quitRequested; + } + + public void setQuitRequested(boolean quitRequested) { + this.quitRequested = quitRequested; + } + + public boolean isRestartRequested() { + return restartRequested; + } + + public void setRestartRequested(boolean restartRequested) { + this.restartRequested = restartRequested; + } + + public boolean isRunning() { + return running; + } + + public void setRunning(boolean running) { + if (running) { + start(); + } else { + stop(); + } + } + + public void setInternalRunning(boolean running) { + this.running = running; + } + + public boolean isSaveRequested() { + return saveRequested; + } + + public void setSaveRequested(boolean saveRequested) { + this.saveRequested = saveRequested; + } + + public boolean isStep() { + return step; + } + + public void setStep(boolean step) { + this.step = step; + } + + public DataGroup getData() { + return dataGroup; + } + + public void setRootScape(Scape scape) { + this.scape = scape; + scape.setRunner(this); + dataGroup = new DataGroup(); + dataGroup.setScape(scape); + } + + public Scape getRootScape() { + return scape; + } + + public void setEnvironment(RuntimeEnvironment environment) { + this.environment = environment; + } + + /** + * @return <code>true</code> if the {@link #modelThread} is still running and + * alive + */ + protected boolean isModelThreadAlive() { + return modelThread != null && modelThread.isAlive(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/AbstractLifecycleListener.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/AbstractLifecycleListener.java index 667072c7..06c2ad31 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/AbstractLifecycleListener.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/AbstractLifecycleListener.java @@ -15,10 +15,10 @@ */ package org.eclipse.amp.axf.core; -// TODO: Auto-generated Javadoc /** - * The listener interface for receiving abstractModel events. The class that is interested in processing a abstractModel - * event implements this interface, and the object created with that class is registered with a component using the + * The listener interface for receiving abstractModel events. The class that is + * interested in processing a abstractModel event implements this interface, and + * the object created with that class is registered with a component using the * component's <code>addAbstractModelListener<code> method. When * the abstractModel event occurs, that object's appropriate * method is invoked. @@ -27,122 +27,122 @@ package org.eclipse.amp.axf.core; */ public class AbstractLifecycleListener implements ILifeCycleListener { - private String name; - - private IStateListener listener; - - /** - * Instantiates a new abstract model listener. - */ - public AbstractLifecycleListener() { - listener = new LifecycleObservationAdapter(this); - } - - /** - * Instantiates a new abstract model listener. - * - * @param name the name - */ - public AbstractLifecycleListener(String name) { - this(); - this.name = name; - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observationEnd(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observationEnd(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observationEnding(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observationEnding(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeCreate(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observeCreate(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeInitialize(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observeInitialize(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeStart(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observeStart(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeStop(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observeStop(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeUpdate(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observeUpdate(IObservationProvider observed) { - } - - /** - * @param observed - * @see org.eclipse.amp.axf.core.ILifeCycleListener#observing(org.eclipse.amp.axf.core.IObservationProvider) - */ - public void observing(IObservationProvider observed) { - } - - /** - * @param key - * @param updated - * @see org.eclipse.amp.axf.core.IStateListener#stateChange(java.lang.Object, java.lang.Object) - */ - public void stateChange(Object key, Object updated) { - listener.stateChange(key, updated); - } - - /** - * @return the listener - */ - public IStateListener getListener() { - return listener; - } - - /** - * Gets the name. - * - * @return the name - */ - public String getName() { - return name; - } - - /** - * Sets the name. - * - * @param name the new name - */ - public void setName(String name) { - this.name = name; - } - - /** - * @return - * @see java.lang.Object#toString() - */ - public String toString() { - return name; - } + private String name; + + private IStateListener listener; + + /** + * Instantiates a new abstract model listener. + */ + public AbstractLifecycleListener() { + listener = new LifecycleObservationAdapter(this); + } + + /** + * Instantiates a new abstract model listener. + * + * @param name + * the name + */ + public AbstractLifecycleListener(String name) { + this(); + this.name = name; + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observationEnd(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observationEnd(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observationEnding(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observationEnding(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeCreate(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observeCreate(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeInitialize(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observeInitialize(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeStart(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observeStart(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeStop(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observeStop(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observeUpdate(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observeUpdate(IObservationProvider observed) { + } + + /** + * @param observed + * @see org.eclipse.amp.axf.core.ILifeCycleListener#observing(org.eclipse.amp.axf.core.IObservationProvider) + */ + public void observing(IObservationProvider observed) { + } + + /** + * @param key + * @param updated + * @see org.eclipse.amp.axf.core.IStateListener#stateChange(java.lang.Object, + * java.lang.Object) + */ + public void stateChange(Object key, Object updated) { + listener.stateChange(key, updated); + } + + /** + * @return the listener + */ + public IStateListener getListener() { + return listener; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Sets the name. + * + * @param name + * the new name + */ + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IEngine.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IEngine.java index 6747c170..95bbd132 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IEngine.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IEngine.java @@ -17,88 +17,91 @@ package org.eclipse.amp.axf.core; import org.eclipse.amp.axf.time.ITimeGranularity; -// TODO: Auto-generated Javadoc /** * The Interface IEngine. */ public interface IEngine { - /** - * Close. - */ - void close(); + /** + * Close. + */ + void close(); - /** - * Close finally. - */ - void closeFinally(); + /** + * Close finally. + */ + void closeFinally(); - /** - * Checks if is close requested. - * - * @return true, if is close requested - */ - boolean isCloseRequested(); + /** + * Checks if is close requested. + * + * @return true, if is close requested + */ + boolean isCloseRequested(); - /** - * Gets the model thread. - * - * @return the model thread - */ - Thread getModelThread(); + /** + * @return <code>true</code> if the {@link Thread} that runs the engine is + * still alive + */ + boolean isThreadAlive(); - /** - * Checks if is running. - * - * @return true, if is running - */ - boolean isRunning(); + /** + * Checks if is running. + * + * @return true, if is running + */ + boolean isRunning(); - /** - * Checks if is paused. - * - * @return true, if is paused - */ - boolean isPaused(); + /** + * Checks if is paused. + * + * @return true, if is paused + */ + boolean isPaused(); - /** - * Stop. - */ - void stop(); + /** + * Stop. + */ + void stop(); - /** - * Control. - * - * @param ModelControl the model control - */ - void control(EngineControl ModelControl); + /** + * Control. + * + * @param ModelControl + * the model control + */ + void control(EngineControl ModelControl); - /** - * Observation complete. - * - * @param observer the observer - */ - void observationComplete(ILifeCycleListener observer); + /** + * Observation complete. + * + * @param observer + * the observer + */ + void observationComplete(ILifeCycleListener observer); - /** - * Gets the model. - * - * @return the model - */ - IObservationProvider getModel(); + /** + * Gets the model. + * + * @return the model + */ + IObservationProvider getModel(); - /** - * Sets the update granularity. This defines how often observers expect to receive state change notifications. - * Regardless of this value, engines should always provide life-cycle notifications. - * - * @param granularity the desired update granularity (engine specific). - */ - void setUpdateGranularity(ITimeGranularity granularity); + /** + * Sets the update granularity. This defines how often observers expect to + * receive state change notifications. Regardless of this value, engines + * should always provide life-cycle notifications. + * + * @param granularity + * the desired update granularity (engine specific). + */ + void setUpdateGranularity(ITimeGranularity granularity); - /** - * Returns the update granularity. - * - * @param granularity the desired update granularity (engine specific). - */ - ITimeGranularity getUpdateGranularity(); + /** + * Returns the update granularity. + * + * @param granularity + * the desired update granularity (engine specific). + */ + ITimeGranularity getUpdateGranularity(); } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IObservationProvider.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IObservationProvider.java index 074b0cae..ff0fadff 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IObservationProvider.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/IObservationProvider.java @@ -16,9 +16,10 @@ package org.eclipse.amp.axf.core; /** - * A model object capable of providing complete independent life-cycle information and change reporting. During the - * course of its existence as an instantiation within a computational context (i.e. a given vm or distributed system), a - * model may only transition through the following changes in state: + * A model object capable of providing complete independent life-cycle + * information and change reporting. During the course of its existence as an + * instantiation within a computational context (i.e. a given vm or distributed + * system), a model may only transition through the following changes in state: * * <pre> * [Object creation] -> Created @@ -34,17 +35,20 @@ package org.eclipse.amp.axf.core; * Ending -> Ended * </pre> * - * Of course, a model may treat any transition as trivially as desired and so may effectively transition through - * multiple state changes at once (though any models also implementing ILifecycle notifier must report each of these - * transitions individually). A given model then should not undergo the following transition at any point: + * Of course, a model may treat any transition as trivially as desired and so + * may effectively transition through multiple state changes at once (though any + * models also implementing ILifecycle notifier must report each of these + * transitions individually). A given model then should not undergo the + * following transition at any point: * * <pre> * Ended -> {Any other state} * </pre> * - * A model can be in multiple states at once; in fact, given the transition rules above, if a model is in one of the - * following states then they must also be in one of the first set of states and cannot be in any of the following set - * of states. + * A model can be in multiple states at once; in fact, given the transition + * rules above, if a model is in one of the following states then they must also + * be in one of the first set of states and cannot be in any of the following + * set of states. * * <pre> * Initialized: {Created} {Stopped, Ended, Ending} @@ -59,88 +63,101 @@ package org.eclipse.amp.axf.core; */ public interface IObservationProvider { - /** - * Has the model been created? Should return true if observable start time components have been created. It is up to - * implementation to define semantics of when a model is created vs. when it is initialized, but the following - * guidelines are recommended to ensure good model repeatability and performance. To be considered created, a model - * needs: - * - * <pre> - * 1. Internally consistent state. - * 2. Consistent start state across invocations with same input sets. - * 3. Consistent (and ideally, useful and complete!) external state through all supplied providers. - * 4. Accurately updated life cycle state. - * 5. In general, all basic component structure constructed. - * </pre> - * - * A good way to think of this state is as having all scaffolding in place. As outlined in #isInitialized, there - * should be no calls to random functions before the model has passed through this state. - * - * It is up to the implementation to decide (and seems generally reasonable) whether to allow a model to be - * re-created, and clients should generally handle this situation gracefully. - * - * @see isInitialized for important issue re: randomness and repeatability - * - * @return true, if the model is created - */ - public abstract boolean isCreated(); + /** + * Has the model been created? Should return true if observable start time + * components have been created. It is up to implementation to define + * semantics of when a model is created vs. when it is initialized, but the + * following guidelines are recommended to ensure good model repeatability and + * performance. To be considered created, a model needs: + * + * <pre> + * 1. Internally consistent state. + * 2. Consistent start state across invocations with same input sets. + * 3. Consistent (and ideally, useful and complete!) external state through all supplied providers. + * 4. Accurately updated life cycle state. + * 5. In general, all basic component structure constructed. + * </pre> + * + * A good way to think of this state is as having all scaffolding in place. As + * outlined in #isInitialized, there should be no calls to random functions + * before the model has passed through this state. + * + * It is up to the implementation to decide (and seems generally reasonable) + * whether to allow a model to be re-created, and clients should generally + * handle this situation gracefully. + * + * @see isInitialized for important issue re: randomness and repeatability + * + * @return true, if the model is created + */ + public abstract boolean isCreated(); - /** - * Has the model been initialized? Should return true if all time 0 components have been created and initialized. It - * is up to implementation to define semantics of when a model is created vs. when it is initialized, but the - * following guidelines are recommended to ensure good model repeatability and performance. To be considered - * initialized, a model ideally has: - * - * <pre> - * 1. All model components necessary for running the model forward have been created and had initial state set. - * 2. Model state should be recoverable to initialized state after stopping and (re) starting without passing through created state. - * 3. For a given set of inputs, under (2) this state should be be consistent, regardless of whether the model has been newly created or repeatedly returned to initialized state. - * </pre> - * - * Note a very important consequence of the above guidelines for initialized state. No random decisions affecting - * model state should occur before the model leaves the start state and enters the initializing state as those - * decisions would make it impossible to consistently re-enter the same state. - * - * @return true, if the model is fully initialized - */ - public abstract boolean isInitialized(); + /** + * Has the model been initialized? Should return true if all time 0 components + * have been created and initialized. It is up to implementation to define + * semantics of when a model is created vs. when it is initialized, but the + * following guidelines are recommended to ensure good model repeatability and + * performance. To be considered initialized, a model ideally has: + * + * <pre> + * 1. All model components necessary for running the model forward have been created and had initial state set. + * 2. Model state should be recoverable to initialized state after stopping and (re) starting without passing through created state. + * 3. For a given set of inputs, under (2) this state should be be consistent, regardless of whether the model has been newly created or repeatedly returned to initialized state. + * </pre> + * + * Note a very important consequence of the above guidelines for initialized + * state. No random decisions affecting model state should occur before the + * model leaves the start state and enters the initializing state as those + * decisions would make it impossible to consistently re-enter the same state. + * + * @return true, if the model is fully initialized + */ + public abstract boolean isInitialized(); - /** - * Has the model been stopped? When a model has been stopped, it cannot return to active state without being (re) - * started. - * - * @return the period - */ - boolean isStopped(); + /** + * Has the model been stopped? When a model has been stopped, it cannot return + * to active state without being (re) started. + * + * @return the period + */ + boolean isStopped(); - /** - * Has the model been started? - * - * @return the period - */ - boolean isStarted(); + /** + * Has the model been started? + * + * @return the period + */ + boolean isRunning(); - /** - * Is the model currently active? Any model that has been started and has not stopped is considered active, even if - * the model is not actively executing (using CPU cycles or advancing from one state to another.) - * - * @return the period - */ - boolean isActive(); + /** + * @return true if the model has been set to pause + */ + boolean isPaused(); - /** - * Is the model currently in the process of ending itself? This state gives views a chance to get any final state - * while the model is still guaranteed to be available and consistent. - * - * @return the period - */ - boolean isEnding(); + /** + * Is the model currently active? Any model that has been started and has not + * stopped is considered active, even if the model is not actively executing + * (using CPU cycles or advancing from one state to another.) + * + * @return the period + */ + boolean isActive(); - /** - * Has model has ended? In general, a model that has ended can never be (re) started. It is up to implementations to - * define whether model state is consistent and stable following the transition to ended state. - * - * @return the period - */ - boolean isEnded(); + /** + * Is the model currently in the process of ending itself? This state gives + * views a chance to get any final state while the model is still guaranteed + * to be available and consistent. + * + * @return the period + */ + boolean isEnding(); + + /** + * Has model has ended? In general, a model that has ended can never be (re) + * started. It is up to implementations to define whether model state is + * consistent and stable following the transition to ended state. + * + * @return the period + */ + boolean isEnded(); }
\ No newline at end of file diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifeCycleState.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifeCycleState.java index 962a4687..538d7611 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifeCycleState.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifeCycleState.java @@ -12,12 +12,12 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.axf.core; /** * The Enum LifeCycleState. */ public enum LifeCycleState { - OBSERVED, CREATE, INITIALIZE, SETUP, START, UPDATE, STOP, ENDING, END + OBSERVED, CREATE, INITIALIZE, SETUP, START, UPDATE, PAUSED, STOP, ENDING, END } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifecycleObservationAdapter.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifecycleObservationAdapter.java index 05e76eb2..cb7b93aa 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifecycleObservationAdapter.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.core/src/org/eclipse/amp/axf/core/LifecycleObservationAdapter.java @@ -15,59 +15,60 @@ */ package org.eclipse.amp.axf.core; -// TODO: Auto-generated Javadoc /** * The Class LifecycleObservationAdapter. */ public class LifecycleObservationAdapter implements IStateListener { - ILifeCycleListener lifeCycleListener; + ILifeCycleListener lifeCycleListener; - /** - * Instantiates a new lifecycle observation adapter. - * - * @param lifeCycleListener the life cycle listener - */ - public LifecycleObservationAdapter(ILifeCycleListener lifeCycleListener) { - super(); - this.lifeCycleListener = lifeCycleListener; - } + /** + * Instantiates a new lifecycle observation adapter. + * + * @param lifeCycleListener + * the life cycle listener + */ + public LifecycleObservationAdapter(ILifeCycleListener lifeCycleListener) { + super(); + this.lifeCycleListener = lifeCycleListener; + } - /** - * @param notifyType - * @param observed - * @see org.eclipse.amp.axf.core.IStateListener#stateChange(java.lang.Object, java.lang.Object) - */ - public void stateChange(Object notifyType, Object observed) { - if (notifyType instanceof LifeCycleState && observed instanceof IObservationProvider) { - IObservationProvider provider = (IObservationProvider) observed; - switch ((LifeCycleState) notifyType) { - case OBSERVED: - lifeCycleListener.observing(provider); - return; - case CREATE: - lifeCycleListener.observeCreate(provider); - return; - case INITIALIZE: - lifeCycleListener.observeInitialize(provider); - return; - case START: - lifeCycleListener.observeStart(provider); - return; - case UPDATE: - lifeCycleListener.observeUpdate(provider); - return; - case STOP: - lifeCycleListener.observeStop(provider); - return; - case END: - lifeCycleListener.observationEnd(provider); - return; - case ENDING: - lifeCycleListener.observationEnding(provider); - return; - } - } - } + /** + * @param notifyType + * @param observed + * @see org.eclipse.amp.axf.core.IStateListener#stateChange(java.lang.Object, + * java.lang.Object) + */ + public void stateChange(Object notifyType, Object observed) { + if (notifyType instanceof LifeCycleState && observed instanceof IObservationProvider) { + IObservationProvider provider = (IObservationProvider) observed; + switch ((LifeCycleState) notifyType) { + case OBSERVED: + lifeCycleListener.observing(provider); + return; + case CREATE: + lifeCycleListener.observeCreate(provider); + return; + case INITIALIZE: + lifeCycleListener.observeInitialize(provider); + return; + case START: + lifeCycleListener.observeStart(provider); + return; + case UPDATE: + lifeCycleListener.observeUpdate(provider); + return; + case STOP: + lifeCycleListener.observeStop(provider); + return; + case END: + lifeCycleListener.observationEnd(provider); + return; + case ENDING: + lifeCycleListener.observationEnding(provider); + return; + } + } + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/.classpath b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/.classpath index 304e8618..5168cee8 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/.classpath +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/.classpath @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="test"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="output" path="bin"/> diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/META-INF/MANIFEST.MF b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/META-INF/MANIFEST.MF index 12ed61e4..dfe6cffa 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/META-INF/MANIFEST.MF +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/META-INF/MANIFEST.MF @@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.ui, org.eclipse.core.expressions, org.eclipse.amp.axf.core, - org.eclipse.amp.axf.views + org.eclipse.amp.axf.views, + org.junit;bundle-version="4.8.1" Export-Package: org.eclipse.amp.axf.ide, org.eclipse.amp.axf.ide.handlers, org.eclipse.amp.axf.ide.view diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/plugin.xml b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/plugin.xml index 66ee7370..8ca92400 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/plugin.xml +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/plugin.xml @@ -458,5 +458,107 @@ class="org.eclipse.amp.axf.ide.AXFWorkbenchPlugin"> </startup> </extension> + <extension + point="org.eclipse.ui.handlers"> + <handler + class="org.eclipse.amp.axf.ide.handlers.StartHandler" + commandId="org.eclipse.amp.axf.ui.start"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <equals + value="IDLE"> + </equals> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.amp.axf.ide.handlers.StopHandler" + commandId="org.eclipse.amp.axf.ui.stop"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <equals + value="RUNNING"> + </equals> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.amp.axf.ide.handlers.PauseHandler" + commandId="org.eclipse.amp.axf.ui.pause"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <equals + value="RUNNING"> + </equals> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.amp.axf.ide.handlers.ResumeHandler" + commandId="org.eclipse.amp.axf.ui.resume"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <equals + value="PAUSED"> + </equals> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.amp.axf.ide.handlers.RestartHandler" + commandId="org.eclipse.amp.axf.ui.restart"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <equals + value="RUNNING"> + </equals> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.amp.axf.ide.handlers.CloseHandler" + commandId="org.eclipse.amp.axf.ui.close"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <or> + <equals + value="IDLE"> + </equals> + <equals + value="ZOMBIE"> + </equals> + </or> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.amp.axf.ide.handlers.StepHandler" + commandId="org.eclipse.amp.axf.ui.step"> + <enabledWhen> + <with + variable="org.eclipse.amp.axf.ide.engine.state"> + <equals + value="PAUSED"> + </equals> + </with> + </enabledWhen> + </handler> + </extension> + <extension + point="org.eclipse.ui.services"> + <sourceProvider + provider="org.eclipse.amp.axf.ide.EngineStateService"> + <variable + name="org.eclipse.amp.axf.ide.engine.state" + priorityLevel="workbench"> + </variable> + </sourceProvider> + </extension> </plugin> diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/EngineStateService.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/EngineStateService.java new file mode 100644 index 00000000..c0150011 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/EngineStateService.java @@ -0,0 +1,173 @@ +package org.eclipse.amp.axf.ide; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.amp.axf.core.IEngine; +import org.eclipse.amp.axf.core.ILifeCycleListener; +import org.eclipse.amp.axf.core.IModel; +import org.eclipse.amp.axf.core.IObservationProvider; +import org.eclipse.amp.axf.core.LifeCycleState; +import org.eclipse.ui.AbstractSourceProvider; +import org.eclipse.ui.ISourceProvider; +import org.eclipse.ui.ISources; +import org.eclipse.ui.IWorkbenchPart; + +/** + * This {@link ISourceProvider} defines states that can be used to enable or + * disable handlers. The states are related to the execution of only the active + * {@link IModel}s. Execution states are {@link #IDLE}, {@link #RUNNING} and + * {@link #PAUSED}. This is a simplification of all {@link IEngine} states that + * are possible. + * + * @author jonas.ruettimann + */ +public class EngineStateService extends AbstractSourceProvider implements IModelWorkbenchListener, ILifeCycleListener { + public static final String ID = "org.eclipse.amp.axf.ide.engine.state"; + + /** Simulation has been stopped or not even started. */ + public static final String IDLE = "IDLE"; + + /** Simulation is currently running. */ + public static final String RUNNING = "RUNNING"; + + /** Simulation was running but has been set to pause. */ + public static final String PAUSED = "PAUSED"; + + /** Simulation has stopped running, but the {@link Thread} is still alive. */ + public static final String ZOMBIE = "ZOMBIE"; + + private IModel activeModel = null; + + private String lastState; + + public EngineStateService() { + ModelViewManager.getInstance().getManagerListeners().addModelManagerListener(this); + } + + @Override + public Map<String, String> getCurrentState() { + Map<String, String> map = new HashMap<String, String>(1); + map.put(ID, getEngineState()); + return map; + } + + @Override + public String[] getProvidedSourceNames() { + return new String[] { ID }; + } + + private String getEngineState() { + if (activeModel != null && activeModel.getEngine() != null && !activeModel.getEngine().isCloseRequested()) { + if (activeModel.isPaused()) { + return PAUSED; + } + if (activeModel.isRunning()) { + return RUNNING; + } + if (activeModel.getEngine().isThreadAlive()) { + return ZOMBIE; + } + } + + return IDLE; // used as default + } + + @Override + public void stateChange(Object key, Object updated) { + if (key instanceof LifeCycleState) { + possibleStateChange(); + } + } + + private void possibleStateChange() { + String currentState = getEngineState(); + if (!currentState.equals(lastState)) { + lastState = currentState; + notifyObservers(currentState); + } + } + + /** + * This method has been extracted to be able to test notification behavior. + * + * @param currentState + */ + protected void notifyObservers(String currentState) { + fireSourceChanged(ISources.WORKBENCH, ID, currentState); + } + + @Override + public void dispose() { + unregister(); + } + + @Override + public void modelActivated(IModel newModel) { + unregister(); + + newModel.addModelListener(this); + activeModel = newModel; + + possibleStateChange(); + } + + private void unregister() { + if (activeModel != null) { + activeModel.removeModelListener(this); + } + } + + @Override + public void modelAdded(IModel model) { + } + + @Override + public void modelRemoved(IModel model) { + } + + @Override + public void viewAdded(IWorkbenchPart part) { + } + + @Override + public void viewRemoved(IWorkbenchPart part) { + } + + @Override + public void viewActivated(IWorkbenchPart part) { + } + + @Override + public void observing(IObservationProvider observed) { + } + + @Override + public void observeCreate(IObservationProvider observed) { + } + + @Override + public void observeInitialize(IObservationProvider observed) { + } + + @Override + public void observeStart(IObservationProvider observed) { + } + + @Override + public void observeUpdate(IObservationProvider observed) { + } + + @Override + public void observeStop(IObservationProvider observed) { + } + + @Override + public void observationEnding(IObservationProvider observed) { + } + + @Override + public void observationEnd(IObservationProvider observed) { + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/HandlerManager.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/HandlerManager.java deleted file mode 100644 index ae85b95a..00000000 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/HandlerManager.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * <copyright> - * - * Copyright (c) 2009 Metascape, LLC. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Metascape - Initial API and Implementation - * - * </copyright> - * - */ - -package org.eclipse.amp.axf.ide; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.amp.axf.ide.handlers.ModelRunHandler; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.handlers.IHandlerActivation; -import org.eclipse.ui.handlers.IHandlerService; - -/** - * - * @author mparker - * - */ -public class HandlerManager { - - private IHandlerService handlerService; - - private List<IHandlerActivation> handlerActivations; - - private List<ModelRunHandler> handlers; - - public synchronized void activate() { - handlerService = (IHandlerService) AXFWorkbenchPlugin.getDefault().getWorkbench() - .getService(IHandlerService.class); - handlerActivations = new ArrayList<IHandlerActivation>(); - handlers = new ArrayList<ModelRunHandler>(); - } - - public synchronized void deactivate() { - if (handlerService != null) { - handlerService.deactivateHandlers(handlerActivations); - } - } - - /** - * Adds the handler. - * - * @param id the id - * @param handler the handler - */ - protected synchronized void addHandler(String id, final ModelRunHandler handler) { - handlers.add(handler); - handlerActivations.add(handlerService.activateHandler(id, handler)); - } - - public synchronized void notifyHandlers() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - for (ModelRunHandler handler : handlers) { - handler.notifyChange(); - } - } - }); - } -} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/ModelViewManager.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/ModelViewManager.java index 9d81a232..5fe76e28 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/ModelViewManager.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/ModelViewManager.java @@ -24,14 +24,6 @@ import java.util.Map.Entry; import org.eclipse.amp.axf.core.IEngine; import org.eclipse.amp.axf.core.IModel; import org.eclipse.amp.axf.core.IObservationProvider; -import org.eclipse.amp.axf.ide.handlers.CloseHandler; -import org.eclipse.amp.axf.ide.handlers.ModelRunHandler; -import org.eclipse.amp.axf.ide.handlers.PauseHandler; -import org.eclipse.amp.axf.ide.handlers.RestartHandler; -import org.eclipse.amp.axf.ide.handlers.ResumeHandler; -import org.eclipse.amp.axf.ide.handlers.StartHandler; -import org.eclipse.amp.axf.ide.handlers.StepHandler; -import org.eclipse.amp.axf.ide.handlers.StopHandler; import org.eclipse.amp.axf.ide.view.StatusLineView; import org.eclipse.amp.axf.view.IModelPart; import org.eclipse.amp.axf.view.ModelInput; @@ -60,53 +52,51 @@ import org.eclipse.ui.statushandlers.StatusManager; @SuppressWarnings("restriction") public class ModelViewManager implements IAdapterFactory { + public static final String EXECUTION_PERSPECTIVE_ID = "org.eclipse.amp.axf.ExecutionPerspective"; + private static ModelViewManager instance; - public static final String EXECUTION_PERSPECTIVE_ID = "org.eclipse.amp.axf.ExecutionPerspective"; + boolean updated; - /** The perspective ID to execute the simulation */ - private String executionPerspective = EXECUTION_PERSPECTIVE_ID; + String priorPerspectiveID; - private IContextService contextService; + Map<IModel, List<IViewPart>> viewsForModel; - private IContextActivation ideContext; + ModelManagerListeners managerListeners = new ModelManagerListeners(); - private IContextActivation partContext; + /** The perspective ID to execute the simulation */ + String executionPerspective = EXECUTION_PERSPECTIVE_ID; - List<IModel> models = new ArrayList<IModel>(); + IContextService contextService; - List<IViewPart> views = new ArrayList<IViewPart>(); + IContextActivation ideContext; - Map<IModel, IEngine> runnerForModel; + IContextActivation partContext; - Map<IEngine, IModel> modelForRunner = new HashMap<IEngine, IModel>(); + private List<IModel> models = new ArrayList<IModel>(); - Map<Object, IModel> modelForArbitrary = new HashMap<Object, IModel>(); + private List<IViewPart> views = new ArrayList<IViewPart>(); - Map<IModel, List<IViewPart>> viewsForModel; + private Map<IModel, IEngine> runnerForModel; - LifeCycleListeners activeModelListeners; + private Map<IEngine, IModel> modelForRunner = new HashMap<IEngine, IModel>(); - ModelManagerListeners managerListeners = new ModelManagerListeners(); + private Map<Object, IModel> modelForArbitrary = new HashMap<Object, IModel>(); - HandlerManager handlers = new HandlerManager(); + private LifeCycleListeners activeModelListeners = new LifeCycleListeners(); - IModel activeModel; + private IModel activeModel; - IPartListener modelActivationListener; + private IPartListener modelActivationListener; private IObservationProvider[] modelSlots = new IObservationProvider[16]; private SelectionSynchronizer editSelection; - private String priorPerspectiveID; - private StatusLineView statusLineView; private ModelManagerViewPart managerViewPart; - boolean updated; - private ModelViewManager() { } @@ -223,7 +213,7 @@ public class ModelViewManager implements IAdapterFactory { * * @return the active model */ - public IObservationProvider getActiveModel() { + public IModel getActiveModel() { return activeModel; } @@ -240,6 +230,7 @@ public class ModelViewManager implements IAdapterFactory { } final IModel oldModel = this.activeModel; this.activeModel = newModel; + if (newModel != oldModel) { getActiveModelListeners().replaceModel(oldModel, newModel); if (newModel != null) { @@ -525,34 +516,14 @@ public class ModelViewManager implements IAdapterFactory { public void run() { try { contextService = (IContextService) wb.getService(IContextService.class); - // for (Object o : contextService.getActiveContextIds()) { - // System.err.pri0ntln(o); - // contextService - // contextService.deactivateContext(); - // } ideContext = contextService.activateContext("org.eclipse.amp.axf.executionContext"); partContext = contextService.activateContext("org.eclipse.amp.axf.activeEditorContext"); - // contextService.deactivateContext(ideContext); } catch (Exception e) { throw new RuntimeException("Couldn't activate services.", e); } } }); - handlers.activate(); - addHandler("org.eclipse.amp.axf.ui.start", new StartHandler()); - addHandler("org.eclipse.amp.axf.ui.stop", new StopHandler()); - addHandler("org.eclipse.amp.axf.ui.pause", new PauseHandler()); - addHandler("org.eclipse.amp.axf.ui.resume", new ResumeHandler()); - addHandler("org.eclipse.amp.axf.ui.restart", new RestartHandler()); - addHandler("org.eclipse.amp.axf.ui.close", new CloseHandler()); - addHandler("org.eclipse.amp.axf.ui.step", new StepHandler()); - } - - public void addHandler(String id, ModelRunHandler handler) { - handlers.addHandler(id, handler); - getActiveModelListeners().addListener(handler); - getManagerListeners().addModelManagerListener(handler); } private void activatePerspective(final IWorkbench wb, final String perspectiveID, final boolean editors) { @@ -582,7 +553,6 @@ public class ModelViewManager implements IAdapterFactory { contextService.deactivateContext(ideContext); contextService.deactivateContext(partContext); } - handlers.deactivate(); } }); getPage().removePartListener(modelActivationListener); @@ -656,10 +626,6 @@ public class ModelViewManager implements IAdapterFactory { return activeModelListeners; } - public HandlerManager getHandlers() { - return handlers; - } - /** * @return the managerViewPart */ @@ -689,4 +655,5 @@ public class ModelViewManager implements IAdapterFactory { public void setExecutionPerspective(String executionPerspective) { this.executionPerspective = executionPerspective; } + } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/CloseHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/CloseHandler.java index 6127b419..2ad68ad3 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/CloseHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/CloseHandler.java @@ -17,25 +17,20 @@ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; -// TODO: Auto-generated Javadoc /** - * The Class CloseHandler. + * Stop and close the simulation. + * + * @author jonas.ruettimann */ public class CloseHandler extends ModelRunHandler { - /** - * Instantiates a new close handler. - */ - public CloseHandler() { - super(EngineControl.CLOSE); - } + public CloseHandler() { + super(EngineControl.CLOSE); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - public boolean isEnabled() { - return super.isEnabled() || getRunner() != null && getRunner().getModelThread() != null - && getRunner().getModelThread().isAlive(); - } + @Override + public boolean isEnabled() { + assert super.isEnabled() || getRunner() != null && getRunner().isThreadAlive() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ModelRunHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ModelRunHandler.java index 3971458e..121da599 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ModelRunHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ModelRunHandler.java @@ -33,82 +33,57 @@ import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPart; /** - * The Class ModelRunHandler. + * Super class for all {@link AbstractHandler}s to control the {@link IEngine} + * execution. + * + * @author jonas.ruettimann */ public abstract class ModelRunHandler extends AbstractHandler implements ILifeCycleListener, IModelWorkbenchListener { - private IModel model; - private EngineControl control; private IStateListener delegate; - /** - * Instantiates a new model run handler. - * - * @param control - * the control - */ public ModelRunHandler(EngineControl control) { this.control = control; delegate = new LifecycleObservationAdapter(this); } - /** - * @param event - * @return - * @throws ExecutionException - * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent) - */ public Object execute(ExecutionEvent event) throws ExecutionException { getRunner().control(control); - ModelViewManager.getInstance().getHandlers().notifyHandlers(); return null; } - /** - * Gets the runner. - * - * @return the runner - */ - public IEngine getRunner() { - return model != null ? model.getEngine() : null; + protected IEngine getRunner() { + IModel model = ModelViewManager.getInstance().getActiveModel(); + if (model == null) { + return null; + } + return model.getEngine(); } - /** - * @return - * @see org.eclipse.core.commands.AbstractHandler#isEnabled() - */ + @Override public boolean isEnabled() { - return getRunner() != null && !getRunner().isCloseRequested(); + assert getRunner() != null && !getRunner().isCloseRequested() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); } - /** - * Notify change. - */ public void notifyChange() { // its possible we've already left workbench if (AXFWorkbenchPlugin.getDefault() != null) { IWorkbench workbench = AXFWorkbenchPlugin.getDefault().getWorkbench(); - if (model != null && workbench.getDisplay() != null && !workbench.getDisplay().isDisposed()) { + if (workbench.getDisplay() != null && !workbench.getDisplay().isDisposed()) { workbench.getDisplay().asyncExec(new Runnable() { public void run() { - fireHandlerChanged(new HandlerEvent(ModelRunHandler.this, true, false)); + fireChangeEvent(); } }); } } } - public synchronized void observing(IObservationProvider model) { - this.model = (IModel) model; - } - - public synchronized void observationEnd(IObservationProvider model) { - // we may have a lagging model notification from another model - if (model == this.model) { - this.model = null; - } + protected void fireChangeEvent() { + fireHandlerChanged(new HandlerEvent(this, true, false)); } public void stateChange(Object key, Object updated) { @@ -117,10 +92,15 @@ public abstract class ModelRunHandler extends AbstractHandler implements ILifeCy } public synchronized void modelActivated(IModel model) { - this.model = model; notifyChange(); } + public synchronized void observing(IObservationProvider model) { + } + + public synchronized void observationEnd(IObservationProvider model) { + } + public void modelAdded(IModel model) { } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/PauseHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/PauseHandler.java index 25549ef6..566121b8 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/PauseHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/PauseHandler.java @@ -12,30 +12,32 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; -// TODO: Auto-generated Javadoc /** - * The Class PauseHandler. + * Pause the simulation. + * + * @author jonas.ruettimann */ public class PauseHandler extends ModelRunHandler { - /** - * Instantiates a new pause handler. - */ - public PauseHandler() { - super(EngineControl.PAUSE); - } + public PauseHandler() { + super(EngineControl.PAUSE); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + return super.execute(event); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - @Override - public boolean isEnabled() { - return super.isEnabled() && !getRunner().isPaused(); - } + @Override + public boolean isEnabled() { + assert !getRunner().isPaused() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/RestartHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/RestartHandler.java index a5658a5e..9f281d71 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/RestartHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/RestartHandler.java @@ -17,25 +17,20 @@ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; -// TODO: Auto-generated Javadoc /** - * The Class RestartHandler. + * Restart the simulation. + * + * @author jonas.ruettimann */ public class RestartHandler extends ModelRunHandler { - /** - * Instantiates a new restart handler. - */ - public RestartHandler() { - super(EngineControl.RESTART); - } + public RestartHandler() { + super(EngineControl.RESTART); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - public boolean isEnabled() { - // TODO Auto-generated method stub - return super.isEnabled() && getRunner().isRunning(); - } + @Override + public boolean isEnabled() { + assert getRunner().isRunning() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ResumeHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ResumeHandler.java index 5834cdf4..fd9cb112 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ResumeHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/ResumeHandler.java @@ -12,31 +12,25 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; -// TODO: Auto-generated Javadoc /** - * The Class ResumeHandler. + * Resume the simulation execution. + * + * @author jonas.ruettimann */ public class ResumeHandler extends ModelRunHandler { - /** - * Instantiates a new resume handler. - */ - public ResumeHandler() { - super(EngineControl.RESUME); - } + public ResumeHandler() { + super(EngineControl.RESUME); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - @Override - public boolean isEnabled() { - // TODO Auto-generated method stub - return super.isEnabled() && getRunner().isPaused(); - } + @Override + public boolean isEnabled() { + assert getRunner().isPaused() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StartHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StartHandler.java index 4f179b87..4d611873 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StartHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StartHandler.java @@ -12,29 +12,25 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; -// TODO: Auto-generated Javadoc /** - * The Class StartHandler. + * Start the simulation. + * + * @author jonas.ruettimann */ public class StartHandler extends ModelRunHandler { - /** - * Instantiates a new start handler. - */ - public StartHandler() { - super(EngineControl.START); - } + public StartHandler() { + super(EngineControl.START); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - public boolean isEnabled() { - return super.isEnabled() && !getRunner().isRunning(); - } + @Override + public boolean isEnabled() { + assert !getRunner().isRunning() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StepHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StepHandler.java index 57f60cb6..2eed6b86 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StepHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StepHandler.java @@ -12,29 +12,25 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; -// TODO: Auto-generated Javadoc /** - * The Class StepHandler. + * Run a single time step of the simulation. + * + * @author jonas.ruettimann */ public class StepHandler extends ModelRunHandler { - /** - * Instantiates a new step handler. - */ - public StepHandler() { - super(EngineControl.STEP); - } + public StepHandler() { + super(EngineControl.STEP); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - public boolean isEnabled() { - return super.isEnabled() && getRunner().isRunning() && getRunner().isPaused(); - } + @Override + public boolean isEnabled() { + assert getRunner().isRunning() && getRunner().isPaused() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StopHandler.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StopHandler.java index 7c2ea532..b99c90fa 100644 --- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StopHandler.java +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/handlers/StopHandler.java @@ -12,29 +12,25 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.axf.ide.handlers; import org.eclipse.amp.axf.core.EngineControl; -// TODO: Auto-generated Javadoc /** - * The Class StopHandler. + * Stop the simulation. + * + * @author jonas.ruettimann */ public class StopHandler extends ModelRunHandler { - /** - * Instantiates a new stop handler. - */ - public StopHandler() { - super(EngineControl.STOP); - } + public StopHandler() { + super(EngineControl.STOP); + } - /** - * @return - * @see org.eclipse.amp.axf.ide.handlers.ModelRunHandler#isEnabled() - */ - public boolean isEnabled() { - return super.isEnabled() && getRunner().isRunning(); - } + @Override + public boolean isEnabled() { + assert getRunner().isRunning() : "Obviously, this command was declarately enabled not the way it's ment to be."; + return super.isEnabled(); + } } diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/EngineStateServiceTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/EngineStateServiceTest.java new file mode 100644 index 00000000..12dbbd27 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/EngineStateServiceTest.java @@ -0,0 +1,369 @@ +package org.eclipse.amp.axf.ide; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.amp.axf.core.EngineControl; +import org.eclipse.amp.axf.core.IEngine; +import org.eclipse.amp.axf.core.ILifeCycleListener; +import org.eclipse.amp.axf.core.IModel; +import org.eclipse.amp.axf.core.IObservationProvider; +import org.eclipse.amp.axf.core.LifeCycleState; +import org.eclipse.amp.axf.time.ITimeGranularity; +import org.eclipse.ui.ISourceProvider; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.services.ISourceProviderService; +import org.junit.Before; +import org.junit.Test; + +public class EngineStateServiceTest { + + private EngineStateServiceMock classToTest; + + @Before + public void setUp() { + classToTest = new EngineStateServiceMock(); + } + + /** + * Was the {@link EngineStateService} registered by the plugin.xml? + */ + @Test + public void testWasRegisteredByPluginXML() { + ISourceProviderService service = (ISourceProviderService) PlatformUI.getWorkbench().getService(ISourceProviderService.class); + ISourceProvider sourceProvider = service.getSourceProvider(EngineStateService.ID); + assertTrue(sourceProvider instanceof EngineStateService); + } + + /** + * Does the {@link EngineStateService} register itself as a + * {@link IModelWorkbenchListener} on the {@link ModelManagerListeners}? + */ + @Test + public void testEngineStateService() { + ModelViewManager.getInstance().getManagerListeners().manangerListeners.contains(classToTest); + } + + /** + * The activation of an {@link IModel} should cause the + * {@link EngineStateService} to be registered as an + * {@link ILifeCycleListener} on the active {@link IModel}. + */ + @Test + public void testModelActivated() { + IModel modelA = new DummyModel(); + IModel modelB = new DummyModel(); + + // not registered: + assertFalse(modelA.getModelListeners().contains(classToTest)); + assertFalse(modelB.getModelListeners().contains(classToTest)); + + // register to modelA: + classToTest.modelActivated(modelA); + assertTrue(modelA.getModelListeners().contains(classToTest)); + assertFalse(modelB.getModelListeners().contains(classToTest)); + + // unregister at modelA, register at modelB: + classToTest.modelActivated(modelB); + assertTrue(modelB.getModelListeners().contains(classToTest)); + assertFalse(modelA.getModelListeners().contains(classToTest)); + } + + /** + * Without an active {@link IModel} the default state + * {@link EngineStateService#IDLE} should be returned. Otherwise the state of + * the {@link IModel} matters. + */ + @Test + public void testGetCurrentState() { + assertEquals(EngineStateService.IDLE, classToTest.getCurrentState().get(EngineStateService.ID)); + + DummyModel model = new DummyModel(); + classToTest.modelActivated(model); + assertEquals(EngineStateService.IDLE, classToTest.getCurrentState().get(EngineStateService.ID)); + + model.running = true; + assertEquals(EngineStateService.RUNNING, classToTest.getCurrentState().get(EngineStateService.ID)); + + model.getEngine().closeRequested = true; + assertEquals(EngineStateService.IDLE, classToTest.getCurrentState().get(EngineStateService.ID)); + model.getEngine().closeRequested = false; + + model.paused = true; + assertEquals(EngineStateService.PAUSED, classToTest.getCurrentState().get(EngineStateService.ID)); + + model.paused = false; + assertEquals(EngineStateService.RUNNING, classToTest.getCurrentState().get(EngineStateService.ID)); + + model.running = false; + assertEquals(EngineStateService.IDLE, classToTest.getCurrentState().get(EngineStateService.ID)); + + model.getEngine().alive = true; + assertEquals(EngineStateService.ZOMBIE, classToTest.getCurrentState().get(EngineStateService.ID)); + } + + /** + * Only a changing state should lead to firing an event. + */ + @Test + public void testStateChange() { + DummyModel model = new DummyModel(); + + // First state change ever; this should cause a notification: + classToTest.modelActivated(model); + assertTrue(classToTest.isNotificationFired()); + + // Not another notification: + classToTest.stateChange(LifeCycleState.UPDATE, ""); + assertFalse(classToTest.isNotificationFired()); + + // Changing the state should cause notification: + model.running = true; + classToTest.stateChange(LifeCycleState.UPDATE, ""); + assertTrue(classToTest.isNotificationFired()); + + // Not another notification: + classToTest.stateChange(LifeCycleState.UPDATE, ""); + assertFalse(classToTest.isNotificationFired()); + + // A new model that is different should cause notification: + DummyModel differentModel = new DummyModel(); + classToTest.modelActivated(differentModel); + assertTrue(classToTest.isNotificationFired()); + + // But not an equal one: + DummyModel equalModel = new DummyModel(); + classToTest.modelActivated(equalModel); + assertFalse(classToTest.isNotificationFired()); + } + + /** + * Disposing the {@link EngineStateService} should unregister itself at the + * active {@link IModel}. + */ + @Test + public void testDispose() { + IModel modelA = new DummyModel(); + assertFalse(modelA.getModelListeners().contains(classToTest)); + + classToTest.modelActivated(modelA); + assertTrue(modelA.getModelListeners().contains(classToTest)); + + classToTest.dispose(); + assertFalse(modelA.getModelListeners().contains(classToTest)); + } + + /** + * Disposing the {@link EngineStateService} without an active {@link IModel} + * should not lead to an {@link Exception}. + */ + @Test + public void testDispose_noActiveModel() { + try { + classToTest.dispose(); + assertTrue(true); + + } catch (Exception e) { + fail("No active model causes an Exception: " + e.getMessage()); + } + } + + private class EngineStateServiceMock extends EngineStateService { + private boolean notificationFired = false; + + public EngineStateServiceMock() { + } + + public boolean isNotificationFired() { + return notificationFired; + } + + @Override + public void stateChange(Object key, Object updated) { + notificationFired = false; + super.stateChange(key, updated); + } + + @Override + public void modelActivated(IModel newModel) { + notificationFired = false; + super.modelActivated(newModel); + } + + @Override + protected void notifyObservers(String currentState) { + notificationFired = true; + super.notifyObservers(currentState); + } + } + + private class DummyModel implements IModel { + + boolean running = false; + + boolean paused = false; + + private List<ILifeCycleListener> listeners = new ArrayList<ILifeCycleListener>(); + + private DummyEngine engine = new DummyEngine(); + + public DummyModel() { + } + + @Override + public boolean isCreated() { + return false; + } + + @Override + public boolean isInitialized() { + return false; + } + + @Override + public boolean isStopped() { + return false; + } + + @Override + public boolean isRunning() { + return running; + } + + @Override + public boolean isPaused() { + return paused; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public boolean isEnding() { + return false; + } + + @Override + public boolean isEnded() { + return false; + } + + @Override + public void addModelListener(ILifeCycleListener listener) { + listeners.add(listener); + } + + @Override + public Collection<ILifeCycleListener> getModelListeners() { + return listeners; + } + + @Override + public void removeModelListener(ILifeCycleListener listener) { + listeners.remove(listener); + } + + @Override + public String getTimeDescription() { + return null; + } + + @Override + public DummyEngine getEngine() { + return engine; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public int getPeriod() { + return 0; + } + + @Override + public int getStopPeriod() { + return 0; + } + } + + private class DummyEngine implements IEngine { + boolean closeRequested = false; + + boolean alive = false; + + public DummyEngine() { + } + + @Override + public void close() { + } + + @Override + public void closeFinally() { + } + + @Override + public boolean isCloseRequested() { + return closeRequested; + } + + @Override + public boolean isThreadAlive() { + return alive; + } + + @Override + public boolean isRunning() { + return false; + } + + @Override + public boolean isPaused() { + return false; + } + + @Override + public void stop() { + } + + @Override + public void control(EngineControl ModelControl) { + } + + @Override + public void observationComplete(ILifeCycleListener observer) { + } + + @Override + public IObservationProvider getModel() { + return null; + } + + @Override + public void setUpdateGranularity(ITimeGranularity granularity) { + } + + @Override + public ITimeGranularity getUpdateGranularity() { + return null; + } + + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/CloseHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/CloseHandlerTest.java new file mode 100644 index 00000000..357245be --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/CloseHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class CloseHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.close"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return false; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return false; + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/ModelRunHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/ModelRunHandlerTest.java new file mode 100644 index 00000000..f41def77 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/ModelRunHandlerTest.java @@ -0,0 +1,243 @@ +package org.eclipse.amp.axf.ide.handlers; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collection; + +import org.eclipse.amp.axf.core.EngineControl; +import org.eclipse.amp.axf.core.IEngine; +import org.eclipse.amp.axf.core.ILifeCycleListener; +import org.eclipse.amp.axf.core.IModel; +import org.eclipse.amp.axf.core.IObservationProvider; +import org.eclipse.amp.axf.core.LifeCycleState; +import org.eclipse.amp.axf.ide.EngineStateService; +import org.eclipse.amp.axf.ide.ModelViewManager; +import org.eclipse.amp.axf.time.ITimeGranularity; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.NotEnabledException; +import org.eclipse.core.commands.NotHandledException; +import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.services.ISourceProviderService; +import org.junit.Before; +import org.junit.Test; + +public abstract class ModelRunHandlerTest { + + private DummyModel model; + + private EngineStateService stateService; + + private IHandlerService handlerService; + + protected abstract String getCommandToTestId(); + + protected abstract boolean needsRunningStateToBeEnabled(); + + protected abstract boolean needsPauseStateToBeEnabled(); + + @Before + public void setUp() { + model = new DummyModel(); + + ISourceProviderService sourceProviderService = (ISourceProviderService) PlatformUI.getWorkbench().getService(ISourceProviderService.class); + stateService = (EngineStateService) sourceProviderService.getSourceProvider(EngineStateService.ID); + + handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); + + ModelViewManager.getInstance().getModels().add(model); + ModelViewManager.getInstance().setActiveModel(model); + } + + @Test + public void testEnabled() { + model.running = needsRunningStateToBeEnabled(); + model.paused = needsPauseStateToBeEnabled(); + stateService.stateChange(LifeCycleState.UPDATE, ""); + + try { + handlerService.executeCommand(getCommandToTestId(), null); + assertTrue(true); // There should not be an Exception. + + } catch (Exception e) { + fail("Executing " + getCommandToTestId() + " caused an Exception:" + e.getMessage()); + } + } + + @Test + public void testDisabled() throws ExecutionException, NotDefinedException, NotHandledException { + model.running = !needsRunningStateToBeEnabled(); + model.paused = !needsPauseStateToBeEnabled(); + stateService.stateChange(LifeCycleState.UPDATE, ""); + + try { + handlerService.executeCommand(getCommandToTestId(), null); + fail(getCommandToTestId() + " should not be enabled if model is not running."); + + } catch (NotEnabledException e) { + assertTrue(true); // This is expected to happen. + } + } + + private class DummyModel implements IModel { + + boolean running = false; + + boolean paused = false; + + private DummyEngine engine = new DummyEngine(this); + + public DummyModel() { + } + + @Override + public boolean isCreated() { + return false; + } + + @Override + public boolean isInitialized() { + return false; + } + + @Override + public boolean isStopped() { + return false; + } + + @Override + public boolean isRunning() { + return running; + } + + @Override + public boolean isPaused() { + return paused; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public boolean isEnding() { + return false; + } + + @Override + public boolean isEnded() { + return false; + } + + @Override + public void addModelListener(ILifeCycleListener listener) { + } + + @Override + public Collection<ILifeCycleListener> getModelListeners() { + return null; + } + + @Override + public void removeModelListener(ILifeCycleListener listener) { + } + + @Override + public String getTimeDescription() { + return null; + } + + @Override + public DummyEngine getEngine() { + return engine; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public int getPeriod() { + return 0; + } + + @Override + public int getStopPeriod() { + return 0; + } + } + + private class DummyEngine implements IEngine { + + private DummyModel dummyModel; + + public DummyEngine(DummyModel model) { + this.dummyModel = model; + } + + @Override + public void close() { + } + + @Override + public void closeFinally() { + } + + @Override + public boolean isCloseRequested() { + return false; + } + + @Override + public boolean isThreadAlive() { + return false; + } + + @Override + public boolean isRunning() { + return getModel().isRunning(); + } + + @Override + public boolean isPaused() { + return getModel().isPaused(); + } + + @Override + public void stop() { + } + + @Override + public void control(EngineControl ModelControl) { + } + + @Override + public void observationComplete(ILifeCycleListener observer) { + } + + @Override + public IObservationProvider getModel() { + return dummyModel; + } + + @Override + public void setUpdateGranularity(ITimeGranularity granularity) { + } + + @Override + public ITimeGranularity getUpdateGranularity() { + return null; + } + + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/PauseHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/PauseHandlerTest.java new file mode 100644 index 00000000..5fd95fa9 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/PauseHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class PauseHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.pause"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return true; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return false; + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/RestartHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/RestartHandlerTest.java new file mode 100644 index 00000000..b256ddaf --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/RestartHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class RestartHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.restart"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return true; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return false; + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/ResumeHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/ResumeHandlerTest.java new file mode 100644 index 00000000..cb8dd378 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/ResumeHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class ResumeHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.resume"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return true; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return true; + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StartHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StartHandlerTest.java new file mode 100644 index 00000000..23d8afc8 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StartHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class StartHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.start"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return false; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return false; + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StepHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StepHandlerTest.java new file mode 100644 index 00000000..22ab04d0 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StepHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class StepHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.step"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return true; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return true; + } + +} diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StopHandlerTest.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StopHandlerTest.java new file mode 100644 index 00000000..256e49b8 --- /dev/null +++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/test/org/eclipse/amp/axf/ide/handlers/StopHandlerTest.java @@ -0,0 +1,20 @@ +package org.eclipse.amp.axf.ide.handlers; + +public class StopHandlerTest extends ModelRunHandlerTest { + + @Override + protected String getCommandToTestId() { + return "org.eclipse.amp.axf.ui.stop"; + } + + @Override + protected boolean needsRunningStateToBeEnabled() { + return true; + } + + @Override + protected boolean needsPauseStateToBeEnabled() { + return false; + } + +} diff --git a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ModelWrapperScapeListener.java b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ModelWrapperScapeListener.java index 4f33586e..1e461c07 100644 --- a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ModelWrapperScapeListener.java +++ b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ModelWrapperScapeListener.java @@ -12,7 +12,7 @@ * * </copyright> * -*/ + */ package org.eclipse.amp.escape.ascape.wrap; import java.util.TooManyListenersException; @@ -25,155 +25,121 @@ import org.eclipse.amp.axf.core.IModel; import org.eclipse.amp.axf.core.IStateListener; import org.eclipse.amp.axf.core.LifeCycleState; -// TODO: Auto-generated Javadoc /** - * The listener interface for receiving modelWrapperScape events. The class that is interested in processing a - * modelWrapperScape event implements this interface, and the object created with that class is registered with a - * component using the component's <code>addModelWrapperScapeListener<code> method. When + * The listener interface for receiving modelWrapperScape events. The class that + * is interested in processing a modelWrapperScape event implements this + * interface, and the object created with that class is registered with a + * component using the component's + * <code>addModelWrapperScapeListener<code> method. When * the modelWrapperScape event occurs, that object's appropriate * method is invoked. * - * @see ModelWrapperScapeEvent + * @see ScapeEvent */ public class ModelWrapperScapeListener extends DefaultScapeListener { - /** - * - */ - private static final long serialVersionUID = 1L; - - IModel model; - IStateListener wrapped; - - /** - * Instantiates a new model wrapper scape listener. - * - * @param model the model - * @param scape the scape - * @param wrapped the wrapped - */ - public ModelWrapperScapeListener(IModel model, Scape scape, ILifeCycleListener wrapped) { - this.model = model; - this.scape = scape; - this.wrapped = wrapped; - if (model == null) { - throw new RuntimeException("Model cannot be null."); - } - if (scape == null) { - throw new RuntimeException("Scape cannot be null."); - } - if (wrapped == null) { - throw new RuntimeException("Wrapped listener cannot be null." + wrapped + " for " + scape); - } - } - - /** - * @param scapeEvent - * @throws TooManyListenersException - * @see org.ascape.model.event.DefaultScapeListener#scapeAdded(org.ascape.model.event.ScapeEvent) - */ - public void scapeAdded(ScapeEvent scapeEvent) throws TooManyListenersException { - wrapped.stateChange(LifeCycleState.OBSERVED, model); - super.scapeAdded(scapeEvent); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeClosing(org.ascape.model.event.ScapeEvent) - */ - public void scapeClosing(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.ENDING, model); - super.scapeClosing(scapeEvent); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeInitialized(org.ascape.model.event.ScapeEvent) - */ - public void scapeInitialized(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.INITIALIZE, model); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeIterated(org.ascape.model.event.ScapeEvent) - */ - public void scapeIterated(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.UPDATE, model); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeRemoved(org.ascape.model.event.ScapeEvent) - */ - public void scapeRemoved(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.END, model); - super.scapeRemoved(scapeEvent); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeSetup(org.ascape.model.event.ScapeEvent) - */ - public void scapeSetup(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.CREATE, model); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeStarted(org.ascape.model.event.ScapeEvent) - */ - public void scapeStarted(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.START, model); - } - - /** - * @param scapeEvent - * @see org.ascape.model.event.DefaultScapeListener#scapeStopped(org.ascape.model.event.ScapeEvent) - */ - public void scapeStopped(ScapeEvent scapeEvent) { - wrapped.stateChange(LifeCycleState.STOP, model); - } - - /** - * @return - * @see org.ascape.model.event.DefaultScapeListener#getScape() - */ - public Scape getScape() { - return scape; - } - - /** - * @return - * @see org.ascape.model.event.DefaultScapeListener#isGraphic() - */ - public boolean isGraphic() { - return false; - } - - /** - * @return - * @see org.ascape.model.event.DefaultScapeListener#isLifeOfScape() - */ - public boolean isLifeOfScape() { - return false; - } - - /** - * @return - * @see org.ascape.model.event.DefaultScapeListener#getName() - */ - public String getName() { - return wrapped.toString() + " Wrapped"; - } - - /** - * Clones this object. - * - * @return the object - */ - public Object clone() { - ModelWrapperScapeListener clone = (ModelWrapperScapeListener) super.clone(); - return clone; - } + private static final long serialVersionUID = 1L; + + private IModel model; + + private IStateListener wrapped; + + /** + * Instantiates a new model wrapper scape listener. + * + * @param model + * the model + * @param scape + * the scape + * @param wrapped + * the wrapped + */ + public ModelWrapperScapeListener(IModel model, Scape scape, ILifeCycleListener wrapped) { + this.model = model; + this.scape = scape; + this.wrapped = wrapped; + if (model == null) { + throw new RuntimeException("Model cannot be null."); + } + if (scape == null) { + throw new RuntimeException("Scape cannot be null."); + } + if (wrapped == null) { + throw new RuntimeException("Wrapped listener cannot be null." + wrapped + " for " + scape); + } + } + + @Override + public void scapeAdded(ScapeEvent scapeEvent) throws TooManyListenersException { + wrapped.stateChange(LifeCycleState.OBSERVED, model); + super.scapeAdded(scapeEvent); + } + + @Override + public void scapeClosing(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.ENDING, model); + super.scapeClosing(scapeEvent); + } + + @Override + public void scapeInitialized(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.INITIALIZE, model); + } + + @Override + public void scapeIterated(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.UPDATE, model); + } + + @Override + public void scapePaused(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.PAUSED, model); + } + + @Override + public void scapeRemoved(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.END, model); + super.scapeRemoved(scapeEvent); + } + + @Override + public void scapeSetup(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.CREATE, model); + } + + @Override + public void scapeStarted(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.START, model); + } + + @Override + public void scapeStopped(ScapeEvent scapeEvent) { + wrapped.stateChange(LifeCycleState.STOP, model); + } + + @Override + public Scape getScape() { + return scape; + } + + @Override + public boolean isGraphic() { + return false; + } + + @Override + public boolean isLifeOfScape() { + return false; + } + + @Override + public String getName() { + return wrapped.toString() + " Wrapped"; + } + + @Override + public Object clone() { + ModelWrapperScapeListener clone = (ModelWrapperScapeListener) super.clone(); + return clone; + } } diff --git a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java index c17505a1..022d4aff 100644 --- a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java +++ b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java @@ -24,262 +24,129 @@ import org.ascape.model.Scape; import org.eclipse.amp.axf.core.IEngine; import org.eclipse.amp.axf.core.ILifeCycleListener; import org.eclipse.amp.axf.core.IModel; -import org.eclipse.gef.EditPartFactory; -import org.eclipse.jface.viewers.ILabelProviderListener; - - -// TODO: Auto-generated Javadoc -/** - * The Class ScapeWrapperModel. - */ public class ScapeWrapperModel implements IModel { - Scape scape; - - IModel model; - - EditPartFactory factory; - - EditPartFactory treeFactory; - - Collection<ILifeCycleListener> listeners = new ArrayList<ILifeCycleListener>(); - - Map<ILifeCycleListener, ModelWrapperScapeListener> wrapperForListener = new HashMap<ILifeCycleListener, ModelWrapperScapeListener>(); - - boolean closed; - - /** - * Instantiates a new scape wrapper model. - * - * @param scape the scape - */ - public ScapeWrapperModel(Scape scape) { - super(); - this.scape = scape; - if (!scape.isRoot()) { - model = new ScapeWrapperModel(scape.getRoot()); - } else { - model = this; - } - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IModel#getName() - */ - public String getName() { - return scape.getName(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IModel#getPeriod() - */ - public int getPeriod() { - return scape.getPeriod(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IModel#getEngine() - */ - public IEngine getEngine() { - return (IEngine) scape.getRunner(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IModel#getRoot() - */ - public Object getRoot() { - return getScape(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IModel#getStopPeriod() - */ - public int getStopPeriod() { - return scape.getStopPeriod(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IModel#getTimeDescription() - */ - public String getTimeDescription() { - return scape.getPeriodDescription(); - } - - /** - * @param listener - * @see org.eclipse.amp.axf.core.IObservationProvider#addModelListener(org.eclipse.amp.axf.core.ILifeCycleListener) - */ - public void addModelListener(final ILifeCycleListener listener) { - new Thread() { - public void run() { - listeners.add(listener); - if (!(listener instanceof ScapeWrapperModelListener)) { - ModelWrapperScapeListener wrapper = new ModelWrapperScapeListener(ScapeWrapperModel.this, scape, - listener); - scape.addView(wrapper); - wrapperForListener.put(listener, wrapper); - } - }; - }.start(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#getModelListeners() - */ - public Collection<ILifeCycleListener> getModelListeners() { - return listeners; - } - - /** - * @param listener - * @see org.eclipse.amp.axf.core.IObservationProvider#removeModelListener(org.eclipse.amp.axf.core.ILifeCycleListener) - */ - public void removeModelListener(final ILifeCycleListener listener) { - new Thread() { - public void run() { - listeners.remove(listener); - if (listener instanceof ScapeWrapperModelListener) { - scape.removeScapeListener(((ScapeWrapperModelListener) listener).getWrapped()); - } - // ModelWrapperScapeListener wrapper = wrapperForListener.get(listener); - // if (wrapper != null) { - // scape.removeScapeListener(wrapper); - // } - listener.observationEnd(ScapeWrapperModel.this); - } - }.start(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isInitialized() - */ - public boolean isInitialized() { - return scape.isInitialized(); - } - - /** - * Gets the scape. - * - * @return the scape - */ - public Scape getScape() { - return scape; - } - - /** - * Gets the text. - * - * @param element the element - * - * @return the text - */ - public String getText(Object element) { - // TODO Auto-generated method stub - return null; - } - - /** - * Adds the listener. - * - * @param listener the listener - */ - public void addListener(ILabelProviderListener listener) { - // TODO Auto-generated method stub - - } - - /** - * Dispose. - */ - public void dispose() { - // TODO Auto-generated method stub - - } - - /** - * Checks if is label property. - * - * @param element the element - * @param property the property - * - * @return true, if is label property - */ - public boolean isLabelProperty(Object element, String property) { - // TODO Auto-generated method stub - return false; - } - - /** - * Removes the listener. - * - * @param listener the listener - */ - public void removeListener(ILabelProviderListener listener) { - // TODO Auto-generated method stub - - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isActive() - */ - public boolean isActive() { - return getScape().isRunning(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isCreated() - */ - public boolean isCreated() { - return getScape().isRunning(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isEnded() - */ - public boolean isEnded() { - return closed; - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isEnding() - */ - public boolean isEnding() { - return getScape().getRunner().isCloseRequested(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isStarted() - */ - public boolean isStarted() { - return getScape().isInitialized() && getScape().isRunning(); - } - - /** - * @return - * @see org.eclipse.amp.axf.core.IObservationProvider#isStopped() - */ - public boolean isStopped() { - return !getScape().getRunner().isRunning() && !isEnded(); - } - - /** - * @param closed the closed to set - */ - public void setClosed(boolean closed) { - this.closed = closed; - } + Collection<ILifeCycleListener> listeners = new ArrayList<ILifeCycleListener>(); + + Map<ILifeCycleListener, ModelWrapperScapeListener> wrapperForListener = new HashMap<ILifeCycleListener, ModelWrapperScapeListener>(); + + private Scape scape; + + private boolean closed; + + /** + * Instantiates a new scape wrapper model. + * + * @param scape + * the scape + */ + public ScapeWrapperModel(Scape scape) { + super(); + this.scape = scape; + } + + public String getName() { + return scape.getName(); + } + + public int getPeriod() { + return scape.getPeriod(); + } + + public IEngine getEngine() { + return (IEngine) scape.getRunner(); + } + + public Object getRoot() { + return getScape(); + } + + public int getStopPeriod() { + return scape.getStopPeriod(); + } + + public String getTimeDescription() { + return scape.getPeriodDescription(); + } + + public void addModelListener(final ILifeCycleListener listener) { + new Thread() { + @Override + public void run() { + listeners.add(listener); + if (!(listener instanceof ScapeWrapperModelListener)) { + ModelWrapperScapeListener wrapper = new ModelWrapperScapeListener(ScapeWrapperModel.this, getScape(), listener); + getScape().addView(wrapper); + wrapperForListener.put(listener, wrapper); + } + } + }.start(); + } + + public Collection<ILifeCycleListener> getModelListeners() { + return listeners; + } + + public void removeModelListener(final ILifeCycleListener listener) { + new Thread() { + @Override + public void run() { + listeners.remove(listener); + if (listener instanceof ScapeWrapperModelListener) { + getScape().removeScapeListener(((ScapeWrapperModelListener) listener).getWrapped()); + } + listener.observationEnd(ScapeWrapperModel.this); + } + }.start(); + } + + public boolean isInitialized() { + return scape.isInitialized(); + } + + /** + * Gets the scape. + * + * @return the scape + */ + public Scape getScape() { + return scape; + } + + public boolean isActive() { + return getScape().isRunning(); + } + + public boolean isCreated() { + return getScape().isRunning(); + } + + public boolean isEnded() { + return closed; + } + + public boolean isEnding() { + return getScape().getRunner().isCloseRequested(); + } + + public boolean isRunning() { + return getScape().isInitialized() && getScape().isRunning(); + } + + public boolean isPaused() { + return getScape().isInitialized() && getScape().isPaused(); + } + + public boolean isStopped() { + return !getScape().getRunner().isRunning() && !isEnded(); + } + + /** + * @param closed + * the closed to set + */ + public void setClosed(boolean closed) { + this.closed = closed; + } } diff --git a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ide/src/org/eclipse/amp/escape/ide/EclipseEscapeRunner.java b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ide/src/org/eclipse/amp/escape/ide/EclipseEscapeRunner.java index 72eab187..c0fa3358 100644 --- a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ide/src/org/eclipse/amp/escape/ide/EclipseEscapeRunner.java +++ b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ide/src/org/eclipse/amp/escape/ide/EclipseEscapeRunner.java @@ -269,7 +269,7 @@ public class EclipseEscapeRunner extends NonGraphicRunner implements IEngine { } try { long elapsed = 0; - while (isCloseRequested() && getModelThread() != null && getModelThread().isAlive() && elapsed <= KILL_AFTER_NO_RESPONSE_TIME) { + while (isCloseRequested() && isThreadAlive() && elapsed <= KILL_AFTER_NO_RESPONSE_TIME) { if (elapsed >= KILL_AFTER_NO_RESPONSE_TIME) { if (modelMonitor != null) { modelMonitor.setTaskName("Forcing Close"); @@ -481,4 +481,9 @@ public class EclipseEscapeRunner extends NonGraphicRunner implements IEngine { this.closeOnStop = closeOnStop; } + @Override + public boolean isThreadAlive() { + return isModelThreadAlive(); + } + } |