/******************************************************************************* * * 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.Set; 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.ValueException; import org.overture.interpreter.scheduler.SharedStateListner; import org.overture.parser.config.Properties; /** * A class to hold an updatable value. This is almost identical to a ReferenceValue, except that is has a set method * which changes the referenced value and calls a listener, and all the remaining methods here are synchronized, to * guarantee that the sets and gets see all changes (to the same UpdateableValue) produced by other threads. */ public class UpdatableValue extends ReferenceValue { private static final long serialVersionUID = 1L; public ValueListenerList listeners; protected final PType restrictedTo; public static UpdatableValue factory(Value value, ValueListenerList listeners) { return factory(value, listeners, null); } public static UpdatableValue factory(Value value, ValueListenerList listeners, PType type) { if (Settings.dialect == Dialect.VDM_RT && Properties.rt_duration_transactions) { return new TransactionValue(value, listeners, type); } else { return new UpdatableValue(value, listeners, type); } } public static UpdatableValue factory(ValueListenerList listeners) { return factory(listeners, null); } public static UpdatableValue factory(ValueListenerList listeners, PType type) { if (Settings.dialect == Dialect.VDM_RT && Properties.rt_duration_transactions) { return new TransactionValue(listeners, type); } else { return new UpdatableValue(listeners, type); } } protected UpdatableValue(Value value, ValueListenerList listeners, PType type) { super(value); this.listeners = listeners; this.restrictedTo = type; } protected UpdatableValue(ValueListenerList listeners, PType type) { super(); this.listeners = listeners; this.restrictedTo = type; } @Override public synchronized Value getUpdatable(ValueListenerList watch) { // if (watch != null) // { // addListeners(watch); // } // // // We have to calculate the getUpdates to propagate the combined // // listeners to the rest of the structure, but we do not want to // // create a new UpdatableValue, having updated the listeners. // // UpdatableValue uv = (UpdatableValue)value.getUpdatable(listeners); // value = uv.value; // return this; // Create new object every time, because we end up with two references // to the same value otherwise (Overture bug #544) return UpdatableValue.factory(value, watch); } @Override public synchronized Value getConstant() { return value.getConstant(); } @Override protected synchronized Value convertValueTo(PType to, Context ctxt, Set<PType> done) throws AnalysisException { return value.convertValueTo(to, ctxt, done).getUpdatable(listeners); } @Override public void set(ILexLocation location, Value newval, Context ctxt) throws AnalysisException { // Anything with structure added to an UpdateableValue has to be // updatable, otherwise you can "freeze" part of the substructure // such that it can't be changed. And we have to set the listeners // to be "our" listeners, regardless of any it had before. synchronized (this) { value = newval.getConstant().getUpdatable(listeners); value = ((UpdatableValue) value).value; // To avoid nested updatables if (restrictedTo != null) { value = value.convertTo(restrictedTo, ctxt); } } // Experimental hood added for DESTECS if (Settings.dialect == Dialect.VDM_RT) { SharedStateListner.variableChanged(this, location); } // The listeners are outside the sync because they have to lock // the object they notify, which can be holding a lock on this one. if (listeners != null) { listeners.changedValue(location, value, ctxt); } } public void addListener(ValueListener listener) { if (listeners != null) { if (!listeners.contains(listener)) { listeners.add(listener); } } else { listeners = new ValueListenerList(listener); } } public void addListeners(ValueListenerList list) { if (listeners == list) // Same list { return; } else if (listeners != null) { for (ValueListener vl: list) { if (!listeners.contains(vl)) { listeners.add(vl); } } } else { listeners = new ValueListenerList(list); } } @Override public synchronized Object clone() { return new UpdatableValue((Value) value.clone(), listeners, restrictedTo); } @Override public synchronized boolean isType(Class<? extends Value> valueclass) { return valueclass.isInstance(value.deref()); } @Override public synchronized Value deref() { return value.deref(); } @Override public synchronized boolean isUndefined() { return value.isUndefined(); } @Override public synchronized boolean isVoid() { return value.isVoid(); } @Override public synchronized double realValue(Context ctxt) throws ValueException { return value.realValue(ctxt); } @Override public synchronized long intValue(Context ctxt) throws ValueException { return value.intValue(ctxt); } @Override public synchronized long natValue(Context ctxt) throws ValueException { return value.nat1Value(ctxt); } @Override public synchronized long nat1Value(Context ctxt) throws ValueException { return value.nat1Value(ctxt); } @Override public synchronized boolean boolValue(Context ctxt) throws ValueException { return value.boolValue(ctxt); } @Override public synchronized char charValue(Context ctxt) throws ValueException { return value.charValue(ctxt); } @Override public synchronized ValueList tupleValue(Context ctxt) throws ValueException { return value.tupleValue(ctxt); } @Override public synchronized RecordValue recordValue(Context ctxt) throws ValueException { return value.recordValue(ctxt); } @Override public synchronized ObjectValue objectValue(Context ctxt) throws ValueException { return value.objectValue(ctxt); } @Override public synchronized String quoteValue(Context ctxt) throws ValueException { return value.quoteValue(ctxt); } @Override public synchronized ValueList seqValue(Context ctxt) throws ValueException { return value.seqValue(ctxt); } @Override public synchronized ValueSet setValue(Context ctxt) throws ValueException { return value.setValue(ctxt); } @Override public synchronized String stringValue(Context ctxt) throws ValueException { return value.stringValue(ctxt); } @Override public synchronized ValueMap mapValue(Context ctxt) throws ValueException { return value.mapValue(ctxt); } @Override public synchronized FunctionValue functionValue(Context ctxt) throws ValueException { return value.functionValue(ctxt); } @Override public synchronized OperationValue operationValue(Context ctxt) throws ValueException { return value.operationValue(ctxt); } @Override public synchronized boolean equals(Object other) { if (other instanceof Value) { Value val = ((Value) other).deref(); if (val instanceof ReferenceValue) { ReferenceValue rvo = (ReferenceValue) val; return value.equals(rvo.value); } else { return value.equals(other); } } return false; } @Override public synchronized String kind() { return value.kind(); } @Override public synchronized int hashCode() { return value.hashCode(); } @Override public synchronized String toString() { return value.toString(); } }