/*******************************************************************************
*
* 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.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.Vector;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.lex.Dialect;
import org.overture.ast.types.PType;
import org.overture.config.Settings;
import org.overture.interpreter.runtime.Context;
import org.overture.interpreter.runtime.ContextException;
import org.overture.interpreter.runtime.ValueException;
import org.overture.interpreter.scheduler.BasicSchedulableThread;
import org.overture.interpreter.scheduler.SharedStateListner;
/**
* A class to hold an updatable value that can be modified by VDM-RT threads in transactions, committed at a duration
* point.
*/
public class TransactionValue extends UpdatableValue
{
private static final long serialVersionUID = 1L;
private static List<TransactionValue> commitList = new Vector<TransactionValue>();
private Value newvalue = null; // The pending value before a commit
private long newthreadid = -1; // The thread that made the change
private ILexLocation lastSetLocation = null; // The location that made the change
protected TransactionValue(Value value, ValueListenerList listeners,
PType type)
{
super(value, listeners, type);
newvalue = value;
}
protected TransactionValue(ValueListenerList listeners, PType type)
{
super(listeners, type);
newvalue = value;
}
private Value select()
{
if (newthreadid > 0
&& BasicSchedulableThread.getThread(Thread.currentThread()) != null
&& BasicSchedulableThread.getThread(Thread.currentThread()).getId() == newthreadid)
{
return newvalue;
} else
{
return value;
}
}
@Override
public synchronized Value getUpdatable(ValueListenerList watch)
{
return new TransactionValue(select(), watch, restrictedTo);
}
@Override
protected
synchronized Value convertValueTo(PType to, Context ctxt, Set<PType> done)
throws AnalysisException
{
return select().convertValueTo(to, ctxt, done).getUpdatable(listeners);
}
@Override
public void set(ILexLocation location, Value newval, Context ctxt)
throws AnalysisException
{
long current = BasicSchedulableThread.getThread(Thread.currentThread()).getId();
if (newthreadid > 0 && current != newthreadid)
{
throw new ContextException(4142, "Value already updated by thread "
+ newthreadid, location, ctxt);
}
synchronized (this)
{
lastSetLocation = location;
newvalue = newval.getUpdatable(listeners);
newvalue = ((UpdatableValue) newvalue).value; // To avoid nested updatables
if (restrictedTo != null)
{
newvalue = newvalue.convertTo(restrictedTo, ctxt);
}
}
if (newthreadid < 0)
{
synchronized (commitList)
{
newthreadid = BasicSchedulableThread.getThread(Thread.currentThread()).getId();
commitList.add(this);
}
}
if (listeners != null)
{
listeners.changedValue(location, newvalue, ctxt);
}
}
public static void commitAll()
{
synchronized (commitList)
{
for (TransactionValue v : commitList)
{
v.commit();
}
commitList.clear();
}
}
public static void commitOne(long tid)
{
synchronized (commitList)
{
ListIterator<TransactionValue> it = commitList.listIterator();
while (it.hasNext())
{
TransactionValue v = it.next();
if (v.newthreadid == tid)
{
v.commit();
it.remove();
}
}
}
}
private void commit()
{
if (newthreadid > 0)
{
value = newvalue; // Listener called for original "set"
newthreadid = -1;
}
// Experimental hood added for DESTECS
if (Settings.dialect == Dialect.VDM_RT)
{
SharedStateListner.variableChanged(this, lastSetLocation);
}
}
@Override
public synchronized Object clone()
{
return new TransactionValue((Value) select().clone(), listeners, restrictedTo);
}
@Override
public synchronized boolean isType(Class<? extends Value> valueclass)
{
return valueclass.isInstance(select());
}
@Override
public synchronized Value deref()
{
return select().deref();
}
@Override
public synchronized Value getConstant()
{
return select().getConstant();
}
@Override
public synchronized boolean isUndefined()
{
return select().isUndefined();
}
@Override
public synchronized boolean isVoid()
{
return select().isVoid();
}
@Override
public synchronized double realValue(Context ctxt) throws ValueException
{
return select().realValue(ctxt);
}
@Override
public synchronized long intValue(Context ctxt) throws ValueException
{
return select().intValue(ctxt);
}
@Override
public synchronized long natValue(Context ctxt) throws ValueException
{
return select().nat1Value(ctxt);
}
@Override
public synchronized long nat1Value(Context ctxt) throws ValueException
{
return select().nat1Value(ctxt);
}
@Override
public synchronized boolean boolValue(Context ctxt) throws ValueException
{
return select().boolValue(ctxt);
}
@Override
public synchronized char charValue(Context ctxt) throws ValueException
{
return select().charValue(ctxt);
}
@Override
public synchronized ValueList tupleValue(Context ctxt)
throws ValueException
{
return select().tupleValue(ctxt);
}
@Override
public synchronized RecordValue recordValue(Context ctxt)
throws ValueException
{
return select().recordValue(ctxt);
}
@Override
public synchronized ObjectValue objectValue(Context ctxt)
throws ValueException
{
return select().objectValue(ctxt);
}
@Override
public synchronized String quoteValue(Context ctxt) throws ValueException
{
return select().quoteValue(ctxt);
}
@Override
public synchronized ValueList seqValue(Context ctxt) throws ValueException
{
return select().seqValue(ctxt);
}
@Override
public synchronized ValueSet setValue(Context ctxt) throws ValueException
{
return select().setValue(ctxt);
}
@Override
public synchronized String stringValue(Context ctxt) throws ValueException
{
return select().stringValue(ctxt);
}
@Override
public synchronized ValueMap mapValue(Context ctxt) throws ValueException
{
return select().mapValue(ctxt);
}
@Override
public synchronized FunctionValue functionValue(Context ctxt)
throws ValueException
{
return select().functionValue(ctxt);
}
@Override
public synchronized OperationValue operationValue(Context ctxt)
throws ValueException
{
return select().operationValue(ctxt);
}
@Override
public synchronized boolean equals(Object other)
{
if (other instanceof Value)
{
Value val = ((Value) other).deref();
if (val instanceof TransactionValue)
{
TransactionValue tvo = (TransactionValue) val;
return select().equals(tvo.select());
} else if (val instanceof ReferenceValue)
{
ReferenceValue rvo = (ReferenceValue) val;
return select().equals(rvo.value);
} else
{
return select().equals(other);
}
}
return false;
}
@Override
public synchronized String kind()
{
return select().kind();
}
@Override
public synchronized int hashCode()
{
return select().hashCode();
}
@Override
public synchronized String toString()
{
return select().toString();
}
}