package decker.model;
/** A Value holds a value that is generated during script execution or held in a variable.
* Value objects can hold any type of value. */
public final class Value
{
public final static int CONSTANT = 0, BOOLEAN = 1, INTEGER = 2, STRING = 3, STRUCTURE = 4, FUNCTION = 5, REAL = 6;
private final static String[] TYPE_NAME = {"CONSTANT", "BOOLEAN", "INTEGER", "STRING", "STRUCTURE", "FUNCTION", "REAL" };
private int type;
private boolean bool;
private int integer;
private double real;
private Object object;
private Structure visible_structure;
public Value () {
type = CONSTANT;
object = "UNDEFINED";
}
public Value (final Structure _visible_structure) {
type = CONSTANT;
object = "UNDEFINED";
visible_structure = _visible_structure;
}
/** removes all object references from this Value, to make garbage collection easier */
void destroy () {
object = null;
visible_structure = null;
}
private Value executeExpression () {
return FunctionCall.executeFunctionCall(object, null, (visible_structure==null||!visible_structure.variablesSeeEachOther()) ? null : visible_structure);
}
// set ***************************************************************************************************************
public Value set (Value value) {
// if the value contains a function, execute the function. it's okay for the function to return another function. such a returned function will not be executed but stored in this Value
if (value.type == FUNCTION)
value = value.executeExpression();
if (!equalsAbsolute(value)) {
type = value.type;
bool = value.bool;
integer = value.integer;
real = value.real;
object = value.object;
if(visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value set (final boolean b) {
if (type != BOOLEAN || bool != b) {
type = BOOLEAN;
bool = b;
object = null; // this is done to get rid off references to obsolete objects
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value set (final int i) {
if (type != INTEGER || integer != i) {
type = INTEGER;
integer = i;
object = null; // this is done to get rid off references to obsolete objects
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value set (final double r) {
if (type != REAL || !equals(r)) {
type = REAL;
real = r;
object = null; // this is done to get rid off references to obsolete objects
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value set (final String s) {
if (type != STRING || !((String)object).equals(s)) {
type = STRING;
object = s;
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value set (final Structure s) {
if (object != s) { // no need to check whether type is not STRUCTURE here
type = STRUCTURE;
object = s;
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value set (final Function f) {
if (object != f) { // no need to check whether type is not FUNCTION here
type = FUNCTION;
object = f;
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
public Value setConstant (final String constant) {
if (type != CONSTANT || !((String)object).equals(constant)) {
type = CONSTANT;
object = constant;
if (visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
/** sets this Value to value without implicitly calling the function if v contains one */
public Value setDirectly (final Value value, final boolean test_triggers) {
if (!equalsAbsolute(value)) {
type = value.type;
bool = value.bool;
integer = value.integer;
real = value.real;
object = value.object;
if (test_triggers && visible_structure != null && !visible_structure.get("structure_type").equals("LOCAL")) // otherwise it's a temporary Value object and there cannot possibly a listener for it
Global.testTriggers();
}
return this;
}
// add ***************************************************************************************************************
public void add (final Value value) {
if(type == INTEGER && value.type == INTEGER)
set(integer + value.integer);
else
throw new RuntimeException("can only add up two integers");
}
public void add (final int i) {
if(type == INTEGER)
set(integer + i);
else
throw new RuntimeException("can only add up two integers");
}
// equals ************************************************************************************************************
public boolean equalsAbsolute (final Value value) {
if(type != value.type || type == REAL || value.type == REAL) {
if (( type == INTEGER || type == REAL )&&( value.type == INTEGER || value.type == REAL)) {
final Value v = ScriptNode.getValue("REAL_EQUAL_RANGE");
final double range = (v==null||v.type!=REAL) ? 0.000000001 : v.real;
final double a = (type==INTEGER) ? integer : real;
final double b = (value.type==INTEGER) ? value.integer : value.real;
return (a-b<range) && (b-a<range);
}
return false;
}
switch(type) {
case BOOLEAN :
return bool == value.bool;
case INTEGER :
return integer == value.integer;
// case REAL : // REAL is handled in the if clause above
case CONSTANT :
case STRING :
return ((String)object).equals((String)value.object);
default : // STRUCTURE or FUNCTION
return object == value.object;
}
}
public boolean equals (Value value) {
final Value v = (type==FUNCTION) ? executeExpression() : this;
if (value.type == FUNCTION)
value = value.executeExpression();
if(v.type != value.type || v.type == REAL)
return v.equalsAbsolute(value);
switch(type) {
case BOOLEAN :
return v.bool == value.bool;
case INTEGER :
return v.integer == value.integer;
// case REAL : // this case is handled above by calling equalsAbsolute for REAL values
case CONSTANT :
case STRING :
return ((String)v.object).equals((String)value.object);
default : // STRUCTURE or FUNCTION
return v.object == value.object;
}
}
public boolean equals (final boolean b) {
if (type == FUNCTION) {
final Value v = executeExpression();
return v.type == BOOLEAN && v.bool == b;
}
return type == BOOLEAN && bool == b;
}
public boolean equals (final int i) {
final Value v = (type == FUNCTION) ? executeExpression() : this;
if (v.type == REAL) {
final Value rer = ScriptNode.getValue("REAL_EQUAL_RANGE");
final double range = (rer==null||rer.type!=REAL) ? 0.000000001 : rer.real;
final double difference = v.real-i;
return difference < range && -difference < range;
}
return v.type == INTEGER && v.integer == i;
}
public boolean equals (final double r) {
final Value v = (type == FUNCTION) ? executeExpression() : this;
if (v.type != REAL && v.type != INTEGER)
return false;
final Value rer = ScriptNode.getValue("REAL_EQUAL_RANGE");
final double range = (rer==null||rer.type!=REAL) ? 0.000000001 : rer.real;
final double difference = r - ((v.type==REAL)?v.real:v.integer);
return difference < range && -difference < range;
}
public boolean equals (final String s) {
if (type == FUNCTION) {
final Value v = executeExpression();
return v.type == STRING && v.object.equals(s);
}
return type == STRING && object.equals(s);
}
public boolean equals (final Structure s) {
if (type == FUNCTION) {
final Value v = executeExpression();
return v.type == STRUCTURE && v.object == s;
}
return type == STRUCTURE && object == s;
}
public boolean equalsConstant (final String c) {
if (type == FUNCTION) {
final Value v = executeExpression();
return v.type == CONSTANT && v.object.equals(c);
}
return type == CONSTANT && object.equals(c);
}
// "get" *************************************************************************************************************
public Value get (final String name) {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if(v.type != STRUCTURE)
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to STRUCTURE");
return ((Structure)v.object).get(name);
}
public Structure getEnclosingStructure () { return visible_structure; }
public Value getValue () { return (type != FUNCTION) ? this : executeExpression(); }
public boolean isFunction () { return type == FUNCTION; }
public int type () { return (type!=FUNCTION) ? type : executeExpression().type; }
public int typeDirect () { return type; }
public String typeName () { return TYPE_NAME[type()]; }
public String typeNameDirect () { return TYPE_NAME[type]; }
public boolean bool () {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if (v.type != BOOLEAN)
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to BOOLEAN");
return v.bool;
}
public int integer () {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if (v.type != INTEGER)
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to INTEGER");
return v.integer;
}
public double real () {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if (v.type != REAL)
if (v.type == INTEGER)
return v.integer;
else
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to INTEGER");
return v.real;
}
public String string () {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if (v.type != STRING)
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to STRING");
return (String) v.object;
}
public Structure structure () {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if (v.type != STRUCTURE)
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to STRUCTURE");
return (Structure) v.object;
}
public String constant () {
final Value v = (type!=FUNCTION) ? this : executeExpression();
if (v.type != STRUCTURE)
throw new RuntimeException("Cannot convert "+TYPE_NAME[v.type]+" to STRUCTURE");
return (String) v.object;
}
public Function function () {
if(type != FUNCTION)
throw new RuntimeException("Cannot convert "+TYPE_NAME[type]+" to FUNCTION");
return (Function) object;
}
public String toString () { return (type==INTEGER) ? (integer+"") : ( (type==BOOLEAN) ? (bool+"") : ( (type==REAL) ? (real+"") : object.toString() )); }
}