/*******************************************************************************
*
* Copyright (C) 2008 Fujitsu Services Ltd.
*
* Author: Nick Battle
*
* This file is part of VDMJ.
*
* VDMJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VDMJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VDMJ. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
package org.overture.interpreter.values;
import java.io.Serializable;
import java.util.Formattable;
import java.util.FormattableFlags;
import java.util.Formatter;
import java.util.HashSet;
import java.util.Set;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.types.ABracketType;
import org.overture.ast.types.ANamedInvariantType;
import org.overture.ast.types.AOptionalType;
import org.overture.ast.types.AParameterType;
import org.overture.ast.types.AUnionType;
import org.overture.ast.types.AUnknownType;
import org.overture.ast.types.PType;
import org.overture.config.Settings;
import org.overture.interpreter.runtime.Context;
import org.overture.interpreter.runtime.ValueException;
/**
* The parent of all runtime values.
*/
abstract public class Value implements Comparable<Value>, Serializable,
Formattable, Cloneable
{
private static final long serialVersionUID = 1L;
@Override
abstract public String toString();
// This is overridden in the few classes that need to change formatting
public void formatTo(Formatter formatter, int flags, int width,
int precision)
{
formatTo(this.toString(), formatter, flags, width, precision);
}
protected void formatTo(String value, Formatter formatter, int flags,
int width, int precision)
{
StringBuilder sb = new StringBuilder("%");
switch (flags)
{
case FormattableFlags.LEFT_JUSTIFY:
sb.append('-');
break;
case FormattableFlags.ALTERNATE:
sb.append('#');
break;
}
if (width > 0)
{
sb.append(width);
}
if (precision > 0)
{
sb.append('.');
sb.append(precision);
}
sb.append('s');
formatter.format(sb.toString(), value);
}
@Override
abstract public boolean equals(Object other);
/**
* The method for the comparable interface. This is only implemented by numeric types, and allows collections of
* them to be sorted. By default, the method compares the string form of the values, which gives an arbitrary, but
* fixed order for such values.
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Value other)
{
return toString().compareTo(other.toString());
}
@Override
abstract public int hashCode();
/**
* A string with the informal kind of the value, like "set".
*
* @return
*/
abstract public String kind();
@Override
abstract public Object clone();
public Value deepCopy()
{
return (Value) clone();
}
public Value shallowCopy()
{
return (Value) clone();
}
public String toShortString(int max)
{
String value = toString();
if (value.length() > max)
{
value = value.substring(0, max / 2) + "..."
+ value.substring(value.length() - max / 2);
}
return value;
}
/**
* Performing a dynamic type conversion. This method is usually specialized by subclasses that know how to convert
* themselves to other types. If they fail, they delegate the conversion up to this superclass method which deals
* with the special cases: unions, type parameters, optional types, bracketed types and named types. If all these
* also fail, the method throws a runtime dynamic type check exception - though that may be caught, for example by
* the union processing, as it iterates through the types in the union given, trying to convert the value.
*
* @param to
* The target type.
* @param ctxt
* The context in which to make the conversion.
* @return This value converted to the target type.
* @throws AnalysisException
*/
public Value convertTo(PType to, Context ctxt) throws AnalysisException
{
if (Settings.dynamictypechecks)
{
// In VDM++ and VDM-RT, we do not want to do thread swaps half way
// through a DTC check (which can include calculating an invariant),
// so we set the atomic flag around the conversion. This also stops
// VDM-RT from performing "time step" calculations.
Value converted;
try
{
ctxt.threadState.setAtomic(true);
converted = convertValueTo(to, ctxt);
} finally
{
ctxt.threadState.setAtomic(false);
}
return converted;
} else
{
return this; // Good luck!!
}
}
public Value convertValueTo(PType to, Context ctxt)
throws AnalysisException
{
return convertValueTo(to, ctxt, new HashSet<PType>());
}
protected Value convertValueTo(PType to, Context ctxt, Set<PType> done)
throws AnalysisException
{
if (to instanceof AUnionType)
{
AUnionType uto = (AUnionType) to;
for (PType ut : uto.getTypes())
{
if (!done.contains(ut))
{
try
{
if (ut instanceof ANamedInvariantType) // These can "recurse"
{
done.add(ut);
}
return convertValueTo(ut, ctxt, done);
}
catch (ValueException e)
{
// Union type not applicable
}
}
}
} else if (to instanceof AParameterType)
{
AParameterType pt = (AParameterType) to;
// Parameter types are ParameterValues of the given name in
// the context. They exist in the context, if the function has
// been instantiated with type parameters (see FunctionValue).
Value v = ctxt.check(pt.getName());
if (v == null)
{
abort(4147, "Polymorphic function missing @" + pt.getName(), ctxt);
} else if (v instanceof ParameterValue)
{
ParameterValue pv = (ParameterValue) v;
return convertValueTo(pv.type, ctxt, done);
}
abort(4086, "Value of type parameter is not a type", ctxt);
} else if (to instanceof AOptionalType)
{
AOptionalType ot = (AOptionalType) to;
return convertValueTo(ot.getType(), ctxt, done);
} else if (to instanceof ABracketType)
{
ABracketType bt = (ABracketType) to;
return convertValueTo(bt.getType(), ctxt, done);
} else if (to instanceof ANamedInvariantType)
{
ANamedInvariantType ntype = (ANamedInvariantType) to;
Value converted = convertValueTo(ntype.getType(), ctxt, done);
return new InvariantValue(ntype, converted, ctxt);
} else if (to instanceof AUnknownType)
{
return this; // Suppressing DTC for "?" types
}
abort(4087, "Cannot convert " + toShortString(100) + " (" + kind()
+ ") to " + to, ctxt);
return null;
}
/**
* Change the object's value. Normally, values are immutable, but subclasses of {@link UpdatableValue} implement
* this set method to replace the object referenced with another. ReferenceValues like UpdatableValue delegate all
* the other Value method to the contained object.
*
* @param location
* Unused.
* @param newval
* The new value to set
* @param ctxt
* The context used
* @throws ValueException
* @throws AnalysisException
*/
public void set(ILexLocation location, Value newval, Context ctxt)
throws ValueException, AnalysisException
{
abort(4088, "Set not permitted for " + kind(), ctxt);
}
public Value abort(int number, String msg, Context ctxt)
throws ValueException
{
throw new ValueException(number, msg, ctxt);
}
public Value abort(int number, Exception e, Context ctxt)
throws ValueException
{
throw new ValueException(number, e.getMessage(), ctxt);
}
public boolean isUndefined()
{
return false;
}
public boolean isVoid()
{
return false;
}
public boolean isNumeric()
{
return this instanceof NumericValue;
}
public boolean isType(Class<? extends Value> valueclass)
{
return valueclass.isInstance(this);
}
/**
* Find the most primitive underlying value contained. This is typically used to check whether a value is "really"
* (say) a MapValue once the ReferenceValue (Updatablevalue) wrappers have been removed. Note that this strips
* InvariantTypes of their invariance!
*
* @return The primitive value
*/
public Value deref()
{
return this;
}
/**
* Return an UpdatableValue, wrapping this one. This is a deep translation that recurses into all Values that
* contain other Values (sets etc), converting their contents to UpdateableValues. The results can then be modified
* in assignment statements or used as state data etc.
*
* @param listeners
* The listener to inform of updates to the value.
* @return An UpdatableValue for this one.
*/
public Value getUpdatable(ValueListenerList listeners)
{
return UpdatableValue.factory(this, listeners);
}
/**
* Return a simple Value, removing any Updatable/TransactionValue wrappers. This is the opposite of getUpdatable(),
* though it works for all values. Note that this will preserve InvariantValues' integrity (unlike deref).
*
* @return A simple value
*/
public Value getConstant()
{
return this;
}
public double realValue(Context ctxt) throws ValueException
{
abort(4089, "Can't get real value of " + kind(), ctxt);
return 0;
}
public double ratValue(Context ctxt) throws ValueException
{
abort(4090, "Can't get rat value of " + kind(), ctxt);
return 0;
}
public long intValue(Context ctxt) throws ValueException
{
abort(4091, "Can't get int value of " + kind(), ctxt);
return 0;
}
public long natValue(Context ctxt) throws ValueException
{
abort(4092, "Can't get nat value of " + kind(), ctxt);
return 0;
}
public long nat1Value(Context ctxt) throws ValueException
{
abort(4093, "Can't get nat1 value of " + kind(), ctxt);
return 0;
}
public boolean boolValue(Context ctxt) throws ValueException
{
abort(4094, "Can't get bool value of " + kind(), ctxt);
return false;
}
public char charValue(Context ctxt) throws ValueException
{
abort(4095, "Can't get char value of " + kind(), ctxt);
return 0;
}
public ValueList tupleValue(Context ctxt) throws ValueException
{
abort(4096, "Can't get tuple value of " + kind(), ctxt);
return null;
}
public RecordValue recordValue(Context ctxt) throws ValueException
{
abort(4097, "Can't get record value of " + kind(), ctxt);
return null;
}
public String quoteValue(Context ctxt) throws ValueException
{
abort(4098, "Can't get quote value of " + kind(), ctxt);
return null;
}
public ValueList seqValue(Context ctxt) throws ValueException
{
abort(4099, "Can't get sequence value of " + kind(), ctxt);
return null;
}
public ValueSet setValue(Context ctxt) throws ValueException
{
abort(4100, "Can't get set value of " + kind(), ctxt);
return null;
}
public String stringValue(Context ctxt) throws ValueException
{
abort(4101, "Can't get string value of " + kind(), ctxt);
return null;
}
public ValueMap mapValue(Context ctxt) throws ValueException
{
abort(4102, "Can't get map value of " + kind(), ctxt);
return null;
}
public FunctionValue functionValue(Context ctxt) throws ValueException
{
abort(4103, "Can't get function value of " + kind(), ctxt);
return null;
}
public OperationValue operationValue(Context ctxt) throws ValueException
{
abort(4104, "Can't get operation value of " + kind(), ctxt);
return null;
}
public ObjectValue objectValue(Context ctxt) throws ValueException
{
abort(4105, "Can't get object value of " + kind(), ctxt);
return null;
}
}