package simulator;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.Term;
import models.algebra.UnificationFailed;
import models.algebra.ValueUndefined;
import models.algebra.Variable;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
import simulator.Event.IResourceStateValueProvider;
import simulator.interfaces.INativeInitializer;
import simulator.interfaces.INativeReceiver;
/**
* Simulator to run a model
* @author Nitta
*
*/
public class Simulator {
private DataTransferModel model;
private SystemState curState;
private List<INativeReceiver> systemReceivers = new ArrayList<>();
private List<INativeInitializer> systemInitializers = new ArrayList<>();
private Map<DataTransferChannel, Map<ResourceIdentifier, INativeReceiver>> nativeReceivers = new HashMap<>();
private Map<DataTransferChannel, INativeReceiver> nativeChannelReceivers = new HashMap<>();
public Simulator(DataTransferModel model) {
this.model = model;
init();
}
public DataTransferModel getModel() {
return model;
}
public SystemState init() {
curState = new SystemState();
for (ResourceHierarchy res: model.getResourceHierarchies()) {
if (res.getParent() == null) {
// root resource
Resource resource = new Resource(res);
curState.addResource(resource);
Expression initialValue = res.getInitialValue();
if (initialValue != null) {
if (initialValue instanceof Term) {
initialValue = ((Term) initialValue).reduce();
}
curState.updateResourceState(resource.getResourceIdentifier(), null, null, initialValue);
} else if (res.getResourceStateType() != null) {
initialValue = DataConstraintModel.getDefaultValueExpression(res.getResourceStateType());
if (initialValue != null) {
curState.updateResourceState(resource.getResourceIdentifier(), null, null, initialValue);
}
}
}
}
for (Channel channel: model.getChannels()) {
curState.addChannel((DataTransferChannel) channel);
}
for (INativeInitializer initializer: systemInitializers) {
initializer.onInitFromModel(curState);
}
return curState;
}
public SystemState getCurState() {
return curState;
}
/**
* Change the state of the system for a given input event.
*
* @param inputEvent an input event
* @return the next system state
* @throws ParameterizedIdentifierIsFutureWork
* @throws ResolvingMultipleDefinitionIsFutureWork
* @throws InvalidMessage
* @throws UnificationFailed
* @throws ValueUndefined
*/
public SystemState transition(Event inputEvent)
throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined {
SystemState nextSystemState = new SystemState(curState);
Expression message = inputEvent.constructMessageAndDescendantEvents(new ResourceStateValueProvider(curState, nextSystemState), true);
if (message != null) {
inputEvent.setMessage(message);
}
nextSystemState.addEvent(inputEvent);
fireEvent(inputEvent, curState, nextSystemState);
curState = nextSystemState;
for (INativeReceiver receiver: systemReceivers) {
receiver.onReceiveFromModel(inputEvent, nextSystemState);
}
return nextSystemState;
}
public void addSystemReceiver(INativeReceiver receiver) {
systemReceivers.add(receiver);
}
public void addSystemInitializer(INativeInitializer initializer) {
systemInitializers.add(initializer);
}
public void addNativeReceiver(INativeReceiver receiver, DataTransferChannel channel) {
nativeChannelReceivers.put(channel, receiver);
}
public void addNativeReceiver(INativeReceiver receiver, DataTransferChannel channel, Resource resource) {
Map<ResourceIdentifier, INativeReceiver> receivers = nativeReceivers.get(channel);
if (receivers == null) {
receivers = new HashMap<>();
nativeReceivers.put(channel, receivers);
}
receivers.put(resource.getResourceIdentifier(), receiver);
}
public void removeSystemReceiver(INativeReceiver receiver) {
systemReceivers.remove(receiver);
}
public void removeSystemInitializer(INativeInitializer initializer) {
systemInitializers.remove(initializer);
}
public void removeNativeReceiver(DataTransferChannel channel) {
nativeChannelReceivers.remove(channel);
}
public void removeNativeReceiver(DataTransferChannel channel, Resource resource) {
Map<ResourceIdentifier, INativeReceiver> receivers = nativeReceivers.get(channel);
if (receivers != null) {
receivers.remove(resource.getResourceIdentifier());
if (receivers.size() == 0) {
nativeReceivers.remove(channel);
}
}
}
/**
* Fire an given event and construct the next system state from the current system state.
*
* @param event an event
* @param curSystemState the current state of the system
* @param nextSystemState the next state of the system to be constructed
* @throws ParameterizedIdentifierIsFutureWork
* @throws ResolvingMultipleDefinitionIsFutureWork
* @throws InvalidMessage
* @throws UnificationFailed
* @throws ValueUndefined
*/
private void fireEvent(final Event event, final SystemState curSystemState, final SystemState nextSystemState)
throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined {
final ChannelMember[] outTarget = new ChannelMember[1];
final Variable[] outResVar = new Variable[1];
IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
if (target == outTarget[0]) return outResVar[0]; // the current state of each output resource is not to be replaced with its value.
ResourceIdentifier resId = event.getResourceIdentifier(target.getResource());
Resource res = curSystemState.getResource(resId);
if (res == null) return null;
return res.getState().getValue();
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourceIdentifier resId = event.getResourceIdentifier(target.getResource());
Resource res = nextSystemState.getResource(resId);
if (res == null) return null;
return res.getState().getValue();
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) {
ResourceIdentifier resId = event.getResourceIdentifier(target);
Resource res = curSystemState.getResource(resId);
if (res == null) return null;
return res.getState().getValue();
}
};
// First, fire the all child events.
for (Event childEvent: event.getChildren()) {
fireEvent(childEvent, curSystemState, nextSystemState);
}
// Fire this event.
DataTransferChannel channel = event.getChannel();
if (channel.getOutputResources().size() > 0) {
// For each output resource, calculate the next state.
for (ChannelMember out: channel.getOutputChannelMembers()) {
// Calculate the next state expression.
Expression nextResState = null;
outTarget[0] = out;
outResVar[0] = new Variable(channel.getChannelName() + "$" + out.getResource().toString() + "$this"); // A special variable to represent the current state of each output resource.
if (event.getMessage() instanceof Variable) {
nextResState = channel.deriveUpdateExpressionOf(out, resouceStateAccessor).getKey();
} else {
nextResState = channel.deriveUpdateExpressionOf(out, (Term) event.getMessage(), resouceStateAccessor);
}
// Substitute each channel selector in the expression to a value.
for (Map.Entry<Selector, Constant> selestorAndVal: event.getChannelSelectorAndValues()) {
Expression selExp = selestorAndVal.getKey().getExpression();
if (nextResState instanceof Term && selExp instanceof Variable) {
nextResState = ((Term) nextResState).substitute((Variable) selExp, selestorAndVal.getValue());
}
}
// Update the resource state.
ResourceIdentifier outResId = event.getOutputResourceIdentifier(out.getResource());
Expression curResState = null;
if (curSystemState.getResource(outResId) != null) {
curResState = curSystemState.getResource(outResId).getState().getValue();
}
List<ResourceIdentifier> updatedOutResIds = nextSystemState.updateResourceState(outResId, outResVar[0], curResState, nextResState);
if (updatedOutResIds != null) {
if (out.getResource().getResourceHierarchy().isNative()) {
// For a native output resource, neither ancestor nor descendants are changed.
for (Event nextEvent: getNextEvents(outResId, curSystemState, nextSystemState)) {
fireEvent(nextEvent, curSystemState, nextSystemState);
}
} else {
// When a normal resource is updated.
Set<ResourceIdentifier> ancestors = new HashSet<>();
for (ResourceIdentifier updatedOutResId: updatedOutResIds) {
while (updatedOutResId != null && !ancestors.contains(updatedOutResId)) { // In addition to the target state, its ancestors' states are also changed.
ancestors.add(updatedOutResId);
for (Event nextEvent: getNextEvents(updatedOutResId, curSystemState, nextSystemState)) {
fireEvent(nextEvent, curSystemState, nextSystemState);
}
updatedOutResId = (ResourceIdentifier) updatedOutResId.getParent();
}
}
}
}
}
} else if (channel.isNative()) {
// A native output event channel
INativeReceiver receiver = nativeChannelReceivers.get(channel); // receiver for the channel
if (receiver != null) receiver.onReceiveFromModel(event, nextSystemState);
if (nativeReceivers.get(channel) != null) {
receiver = nativeReceivers.get(channel).get(event.getInputResource().getResourceIdentifier()); // receiver for the channel and resource
if (receiver != null) receiver.onReceiveFromModel(event, nextSystemState);
}
}
}
private Set<Event> getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) {
IResourceStateValueProvider resourceStateValueProvider = new ResourceStateValueProvider(curSystemState, nextSystemState);
Set<Event> nextEvents = new HashSet<>();
for (Channel ch: model.getChannels()) {
DataTransferChannel channel = (DataTransferChannel) ch;
ChannelState nextChannelState = nextSystemState.getChannelState(channel);
if (nextChannelState == null) {
nextChannelState = new ChannelState(channel);
nextSystemState.updateChannelState(channel, nextChannelState);
}
Map<ChannelMember, Set<ChannelMember>> dependency = channel.getMemberDependency();
Map<ChannelMember, Set<ChannelMember>> invDependency = new HashMap<>();
for (ChannelMember dependingMem: dependency.keySet()) {
for (ChannelMember dependedMem: dependency.get(dependingMem)) {
Set<ChannelMember> dependings = invDependency.get(dependedMem);
if (dependings == null) {
dependings = new HashSet<>();
invDependency.put(dependedMem, dependings);
}
dependings.add(dependingMem);
}
}
for (ResourcePath inResPath: channel.getInputResources()) {
if (inResId.isInstanceOf(inResPath)) {
// Update the channel state and resource identifiers by the update of the input resource.
boolean isInputResourceDepended = false;
for (ChannelMember dependedMem: invDependency.keySet()) {
if (inResPath == dependedMem.getResource()) {
// 1) If some depending resources are to be updated by the update of an 'depended' input resource.
if (doesSatifsyPathConstraints(inResPath, inResId)) {
Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId));
Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true);
if (message != null) {
nextEvent.setMessage(message);
nextSystemState.updateChannelState(channel, nextChannelState);
List<Constant> channelSelValues = nextEvent.getChannelSelectorValues();
for (Map.Entry<Expression, Expression> paramEnt: nextEvent.getDependingParameters().entrySet()) {
nextChannelState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue());
}
nextEvents.add(nextEvent);
}
}
isInputResourceDepended = true;
}
}
boolean isInputResourceDepending = false;
for (ChannelMember dependingMem: dependency.keySet()) {
if (inResPath == dependingMem.getResource()) {
isInputResourceDepending = true;
}
}
if (!isInputResourceDepended && !isInputResourceDepending) {
if (doesSatifsyPathConstraints(inResPath, inResId)) {
Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId));
Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true);
if (message != null) {
nextEvent.setMessage(message);
nextEvents.add(nextEvent);
}
}
}
for (ChannelMember dependingMem: dependency.keySet()) {
if (inResPath == dependingMem.getResource()) {
// 2) If a 'depending' resource is directly updated.
ResourcePath filledResPath = inResId;
ResourcePath unfilledResPath = inResPath;
// Extract the values of path parameters from the updated resource identifier.
Map<Expression, Expression> selectorVarToVal = new HashMap<>();
Map<Expression, Expression> dependingVarToVal = new HashMap<>();
boolean doesSatisfyConstraint = true;
for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) {
Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey();
Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey();
Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue();
if (constraint != null && !constraint.equals(val)) {
// The value of the path parameter does not satisfy the constraint defined for the parameter.
doesSatisfyConstraint = false;
break;
}
if (channel.getAllSelectorVariables().contains(var)) {
selectorVarToVal.put(var, val);
} else {
dependingVarToVal.put(var, val);
}
}
if (doesSatisfyConstraint) {
List<List<Constant>> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal);
if (dependedChannelSelectorValues != null) {
for (List<Constant> channelSelectorValues: dependedChannelSelectorValues) {
// Guess every tuple of channel selector values that may affects the updated resource.
boolean doesMatch = true;
for (Expression var: selectorVarToVal.keySet()) {
for (int i = 0; i < channel.getAllSelectors().size(); i++) {
if (channel.getAllSelectors().get(i).getExpression().equals(var)) {
if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) {
// If the value of a selector in the updated resource path does not matches a guessed channel selector value.
doesMatch = false;
}
}
}
}
if (doesMatch) {
Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal);
Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false);
if (message != null) {
nextEvent.setMessage(message);
nextEvents.add(nextEvent);
}
}
}
}
}
}
}
}
}
// 3) If a resource in a descendant channel is directly updated.
ResourcePath inResPath = null;
for (ResourcePath path: channel.getInputResources()) {
if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) {
inResPath = path;
}
}
if (inResPath != null) {
for (Map.Entry<List<Constant>, Event> childEventEnt: collectDescendantEvents(channel, channel, inResId, nextSystemState, resourceStateValueProvider)) {
Event childEvent = childEventEnt.getValue();
nextEvents.add(childEvent);
}
}
}
return nextEvents;
}
private List<Map.Entry<List<Constant>, Event>> collectDescendantEvents(DataTransferChannel rootChannel, DataTransferChannel channel, ResourceIdentifier inResId,
SystemState nextSystemState, IResourceStateValueProvider resourceStateValueProvider) {
List<Map.Entry<List<Constant>, Event>> childEvents = new ArrayList<>();
ChannelState nextChannelState = nextSystemState.getChannelState(rootChannel);
for (Channel childCh: channel.getChildren()) {
DataTransferChannel childChannel = (DataTransferChannel) childCh;
Map<ChannelMember, Set<ChannelMember>> dependency = childChannel.getMemberDependency();
Map<ChannelMember, Set<ChannelMember>> invDependency = new HashMap<>();
for (ChannelMember dependingMem: dependency.keySet()) {
for (ChannelMember dependedMem: dependency.get(dependingMem)) {
Set<ChannelMember> dependings = invDependency.get(dependedMem);
if (dependings == null) {
dependings = new HashSet<>();
invDependency.put(dependedMem, dependings);
}
dependings.add(dependingMem);
}
}
for (ResourcePath childInResPath: childChannel.getInputResources()) {
if (inResId.isInstanceOf(childInResPath)) {
boolean isInputResourceDepended = false;
for (ChannelMember dependedMem: invDependency.keySet()) {
if (childInResPath == dependedMem.getResource()) {
// 1) If some depending resources are to be updated by the update of an 'depended' input resource.
if (doesSatifsyPathConstraints(childInResPath, inResId)) {
Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId));
Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true);
if (message != null) {
childEvent.setMessage(message);
List<Constant> childChannelSelValues = childEvent.getChannelSelectorValues();
for (Map.Entry<Expression, Expression> paramEnt: childEvent.getDependingParameters().entrySet()) {
nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue());
}
if (childChannelSelValues.size() >= childChannel.getSelectors().size()) {
List<Constant> channelSelectorValues = new ArrayList<>(childChannelSelValues);
for (int i = 0; i < childChannel.getSelectors().size(); i++) {
channelSelectorValues.remove(childChannelSelValues.size() - 1 - i);
}
childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent));
}
}
}
isInputResourceDepended = true;
}
}
boolean isInputResourceDepending = false;
for (ChannelMember dependingMem: dependency.keySet()) {
if (childInResPath == dependingMem.getResource()) {
isInputResourceDepending = true;
}
}
if (!isInputResourceDepended && !isInputResourceDepending) {
if (doesSatifsyPathConstraints(childInResPath, inResId)) {
Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId));
Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true);
if (message != null) {
childEvent.setMessage(message);
List<Constant> childChannelSelValues = childEvent.getChannelSelectorValues();
for (Map.Entry<Expression, Expression> paramEnt: childEvent.getDependingParameters().entrySet()) {
nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue());
}
if (childChannelSelValues.size() >= childChannel.getSelectors().size()) {
List<Constant> channelSelectorValues = new ArrayList<>(childChannelSelValues);
for (int i = 0; i < childChannel.getSelectors().size(); i++) {
channelSelectorValues.remove(childChannelSelValues.size() - 1 - i);
}
childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent));
}
}
}
}
for (ChannelMember dependingMem: dependency.keySet()) {
if (childInResPath == dependingMem.getResource()) {
// 2) If a 'depending' resource is directly updated.
ResourcePath filledResPath = inResId;
ResourcePath unfilledResPath = childInResPath;
// Extract the values of path parameters from the updated resource identifier.
Map<Expression, Expression> selectorVarToVal = new HashMap<>();
Map<Expression, Expression> dependingVarToVal = new HashMap<>();
boolean doesSatisfyConstraint = true;
for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) {
Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey();
Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey();
Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue();
if (constraint != null && !constraint.equals(val)) {
// The value of the path parameter does not satisfy the constraint defined for the parameter.
doesSatisfyConstraint = false;
break;
}
if (channel.getAllSelectorVariables().contains(var)) {
selectorVarToVal.put(var, val);
} else {
dependingVarToVal.put(var, val);
}
}
if (doesSatisfyConstraint) {
List<List<Constant>> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal);
if (dependedChannelSelectorValues != null) {
for (List<Constant> childChannelSelectorValues: dependedChannelSelectorValues) {
// Guess every tuple of channel selector values that may affects the updated resource.
boolean doesMatch = true;
for (Expression var: selectorVarToVal.keySet()) {
for (int i = 0; i < channel.getAllSelectors().size(); i++) {
if (channel.getAllSelectors().get(i).getExpression().equals(var)) {
if (!childChannelSelectorValues.get(i).equals(selectorVarToVal.get(var))) {
// If the value of a selector in the updated resource path does not matches a guessed channel selector value.
doesMatch = false;
}
}
}
}
if (doesMatch) {
Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId), childChannelSelectorValues, dependingVarToVal);
Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false);
if (message != null) {
childEvent.setMessage(message);
if (childChannelSelectorValues.size() >= childChannel.getSelectors().size()) {
List<Constant> channelSelectorValues = new ArrayList<>(childChannelSelectorValues);
for (int i = 0; i < childChannel.getSelectors().size(); i++) {
channelSelectorValues.remove(childChannelSelectorValues.size() - 1 - i);
}
childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent));
}
}
}
}
}
}
}
}
}
}
// 3) If a resource in a descendant channel is directly updated.
childEvents.addAll(collectDescendantEvents(rootChannel, childChannel, inResId, nextSystemState, resourceStateValueProvider));
}
List<Map.Entry<List<Constant>, Event>> events = new ArrayList<>();
ResourcePath inResPath = null;
for (ResourcePath path: channel.getInputResources()) {
if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) {
inResPath = path;
}
}
for (Map.Entry<List<Constant>, Event> childEventEnt: childEvents) {
List<Constant> channelSelectorValues = childEventEnt.getKey();
Event childEvent = childEventEnt.getValue();
Map<Expression, Expression> dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues);
Event event = new Event((DataTransferChannel) channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal);
event.addChild(childEvent);
if (channelSelectorValues.size() >= channel.getSelectors().size()) {
List<Constant> parentChannelSelectorValues = new ArrayList<>(channelSelectorValues);
for (int i = 0; i < channel.getSelectors().size(); i++) {
parentChannelSelectorValues.remove(channelSelectorValues.size() - 1 - i);
}
events.add(new AbstractMap.SimpleEntry<>(parentChannelSelectorValues, event));
}
}
return events;
}
private boolean doesSatifsyPathConstraints(ResourcePath resPath, ResourceIdentifier resId) {
for (int i = 0; i < resPath.getPathParamsAndConstraints().size(); i++) {
Expression val = resId.getPathParamsAndConstraints().get(i).getKey();
Expression constraint = resPath.getPathParamsAndConstraints().get(i).getValue();
if (constraint != null) {
String valStr = val.toString();
if (val instanceof Constant) {
valStr = (String) ((Constant) val).getValue();
}
String constStr = constraint.toString();
if (constraint instanceof Constant) {
constStr = (String) ((Constant) constraint).getValue();
}
if (!constStr.equals(valStr)) {
// The value of the path parameter does not satisfy the constraint defined for the parameter.
return false; // Not to fire this event.
}
}
}
return true;
}
private static class ResourceStateValueProvider implements IResourceStateValueProvider {
SystemState curSystemState;
SystemState nextSystemState;
public ResourceStateValueProvider(SystemState curSystemState, SystemState nextSystemState) {
this.curSystemState = curSystemState;
this.nextSystemState = nextSystemState;
}
@Override
public Expression getCurrentStateValueOf(ResourceIdentifier resId) {
if (curSystemState.getResource(resId) == null) return null;
return curSystemState.getResource(resId).getState().getValue();
}
@Override
public Expression getNextStateValueOf(ResourceIdentifier resId) {
if (nextSystemState.getResource(resId) == null) return null;
return nextSystemState.getResource(resId).getState().getValue();
}
};
}