package variables; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Observable; import java.util.Observer; import parser.ExpressionNode; import parser.Value; import valueTypes.DecimalValue; /** * The class which represents a variable, and also statically manages all * existing variables. All variables are global. * * When Variables are compa * * @author Curran * */ public class Variable extends ExpressionNode { /** * The name of the variable. */ String name; /** * The value contained in the variable. */ Value value; /** * A flag denoting that this Variable has Observers */ boolean hasObservers = false; /** * If set to false, then calling set() in this variable will do nothing. */ public boolean modifiable = true; /** * The interal observable which acts on behalf of the ObservableVariable * class, since it cannot extend Observable itself because it is already * extending Variable. */ ObservableVariableInternalObservable internalObservable = null; /** * The symbol table all global variables. the type is <String name,Variable * variable> */ static Map<String, Variable> variables = new HashMap<String, Variable>(); /** * The explainations for why each observer was added. */ Map<Observer, String> observerExplainations = new HashMap<Observer, String>(); /** * the most rescent system time at which the set() method was called on this * Variable. */ private long timeOfLastChange = 0; /** * Constrict aVariable with the specified name, and a default value of 0. * * @param name * the variable name */ protected Variable(String name) { this.name = name; value = new DecimalValue(0); } /** * Gets a reference to an existing variable with the specified name. If a * variable exists with that name, then a new one is created, mapped to it's * name, and returned. * * @param variableName * the name of the vaiable * @return the variable with the name variableName */ public static Variable getVariable(String variableName) { Variable variable = variables.get(variableName); if (variable != null) return variable; // if we are here then we are creating new variable Variable newVariable = new Variable(variableName); variables.put(variableName, newVariable); return newVariable; } /** * Returns the value of this variable, with no side effect whatsoever. * Inherited from ExpressionNode. */ public Value evaluate() { return value; } /** * @return the name of this Variable. */ public String toString() { return name; } /** * Gets the symbol table which maps variable names to Variable objects. * * @return the symbol table */ public static Map<String, Variable> getSymbolTable() { return variables; } /** * Adds an Observer to this variable which is notified whenever the variable * changes. The argument Object in the notification is a reference to the * Variable which changed. * * @param o * the Observer to add to the Variable * @param explaination * a human readable explaination of why this observer is being * added. Should be phrased as an aside to the Variable name, for * example, "Observed by the text box so the text is updated when * it changes. */ public void addObserver(Observer o, String explaination) { observerExplainations.put(o, explaination); if (internalObservable == null) internalObservable = new ObservableVariableInternalObservable(); internalObservable.addObserver(o); hasObservers = true; } /** * Deletes an observer from the set of observers of this object. * * @param o * the observer to be deleted. */ public void deleteObserver(Observer o) { internalObservable.deleteObserver(o); observerExplainations.remove(o); } /** * Sets the value of this Variable * * @param value * the value to set this variable to. * @return the value this variable is set to. */ public Value set(Value value) { if (modifiable) { // assign the value this.value = value; // update the observers if there are any if (hasObservers) internalObservable.setChangedAndNotifyObservers(this); // update the time of last change timeOfLastChange = System.currentTimeMillis(); } return this.value; } /** * The explainations for why each observer was added. * * @return a collection of strings, one explaining each Observer. */ public Collection<String> getObserverExplainations() { return observerExplainations.values(); } /** * the ObservableVariable class cannot extend Observable because it is * already extending Variable, so this inner class must be created to be the * actual Observable. */ private class ObservableVariableInternalObservable extends Observable { /** * Sends a notification to all Observers that the variable has changed. * * @param variable * the ObservableVariable which will be passed as the * notification argument. * */ public void setChangedAndNotifyObservers(Variable variableWhichChanged) { setChanged(); notifyObservers(variableWhichChanged); } } /** * * @return true if this Variable is being observed, false if not. */ public boolean hasObservers() { return hasObservers; } /** * Returns the most rescent system time at which the set() method was called * on this Variable. */ public long getTimeOfLastChange() { return timeOfLastChange; } }