Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / simulator / Event.java
package simulator;

import java.util.AbstractMap;
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.Position;
import models.algebra.Term;
import models.algebra.UnificationFailed;
import models.algebra.Variable;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;

/**
 * Event occurred at a channel 
 * 
 * @author Nitta
 *
 */
public class Event {
	private DataTransferChannel channel;
	private Expression message;
	private boolean isInput = false;
	private ResourcePath inputResourcePath = null;
	private Resource inputResource = null;
	
	private List<Map.Entry<Selector, Constant>> channelSelectorAndValues = new ArrayList<>();
	private Map<Expression, Expression> dependingParameters = new HashMap<>();
	private Set<Resource> outputResources = new HashSet<>();
	private Set<Event> childEvents = new HashSet<>();
	private Map<Selector, Map<ResourcePath, Map.Entry<Integer, Set<Position>>>> channelSelectorToInputOrReferenceResourcePathParam = new HashMap<>();
	private Map<Selector, Map<ResourcePath, Map.Entry<Integer, Set<Position>>>> channelSelectorToOutputResourcePathParam = new HashMap<>();
	
	/**
	 * Constructor for an input event channel
	 * 
	 * @param channel an input event channel
	 * @param message an message for the channel
	 * @param outputResPath an output side resource path of the channel
	 * @param outputResource an output side resource of the channel
	 */
	public Event(DataTransferChannel channel, Expression message, ResourcePath outputResPath, Resource outputResource) {
		this.channel = channel;
		this.message = message;
		this.isInput = true;
		this.outputResources.add(outputResource);
		connectChannelSelectorAndPathParameters();
		
		// Extract the values of the channel selectors from the output resource.
		List<Selector> channelSelectors = channel.getAllSelectors();
		for (Selector sel: channelSelectors) {
			Map.Entry<Integer, Set<Position>> paramIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath);
			Resource ancestor = outputResource;
			int idx = outputResPath.getPathParams().size();
			while (ancestor != null) {
				if (ancestor.getResourceHierarchy().getNumParameters() > 0) {
					idx--;
					if (idx == paramIdxAndPos.getKey()) break;
				}
				ancestor = ancestor.getParent();
			}
			if (ancestor!= null) channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, ancestor.getParameter()));
		}
	}
	
	/**
	 * Constructor for a non-event channel
	 * 
	 * @param channel a non-event channel
	 * @param inputResPath an input side resource path of the channel
	 * @param inputResource an input side resource of the channel
	 */
	public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource) {
		this.channel = channel;
		this.isInput = false;
		this.inputResourcePath = inputResPath;
		this.inputResource = inputResource;
		connectChannelSelectorAndPathParameters();

		// Extract the values of the channel selectors from the input resource.
		List<Selector> channelSelectors = channel.getAllSelectors();
		for (Selector sel: channelSelectors) {
			if (inputResPath != null) {
				Map.Entry<Integer, Set<Position>> paramIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath);
				if (paramIdxAndPos != null) {
					Resource ancestor = inputResource;
					int idx = inputResPath.getPathParams().size();
					while (ancestor != null) {
						if (ancestor.getResourceHierarchy().getNumParameters() > 0) {
							idx--;
							if (idx == paramIdxAndPos.getKey()) break;
						}
						ancestor = ancestor.getParent();
					}
					channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, ancestor.getParameter()));
				}
			}
		}
	}
	
	/**
	 * Constructor for a non-event channel
	 * 
	 * @param channel a non-event channel
	 * @param inputResPath an input side resource path of the channel
	 * @param inputResource an input side resource of the channel
	 * @param channelSelectorValues the values of depended channel selectors
	 * @param dependingVarToVal the values of depending channel selectors
	 */
	public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource, List<Constant> channelSelectorValues, Map<Expression, Expression> dependingVarToVal) {
		this.channel = channel;
		this.isInput = false;
		this.inputResourcePath = inputResPath;
		this.inputResource = inputResource;
		connectChannelSelectorAndPathParameters();

		// Extract the values of the channel selectors from channelSelectorValues.
		List<Selector> channelSelectors = channel.getAllSelectors();
		for (int i = 0; i < channelSelectors.size(); i++) {
			Selector sel = channelSelectors.get(i);
			channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, channelSelectorValues.get(i)));
		}
		this.dependingParameters = dependingVarToVal;
	}

	private void connectChannelSelectorAndPathParameters() {
		List<Selector> channelSelectors = channel.getAllSelectors();
		for (Selector sel: channelSelectors) {
			Set<ResourcePath> inputAndReferenceResPaths = new HashSet<>(channel.getInputResources());
			inputAndReferenceResPaths.addAll(channel.getReferenceResources());
			for (ResourcePath resPath: inputAndReferenceResPaths) {
				for (int paramIdx = 0; paramIdx < resPath.getPathParams().size(); paramIdx++) {
					Expression pathParam = resPath.getPathParams().get(paramIdx);
					if (pathParam.contains(sel.getExpression())) {
						for (Map.Entry<Position, Variable> posAndVar:  pathParam.getVariables().entrySet()) {
							Position p = posAndVar.getKey();
							Variable v = posAndVar.getValue();
							if (v.equals(sel.getExpression())) {
								Map<ResourcePath, Map.Entry<Integer, Set<Position>>> pathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel);
								if (pathToIdxAndPos == null) {
									pathToIdxAndPos = new HashMap<>();
									channelSelectorToInputOrReferenceResourcePathParam.put(sel, pathToIdxAndPos);
								}
								Map.Entry<Integer, Set<Position>> idxAndPos = pathToIdxAndPos.get(resPath);
								if (idxAndPos == null) {
									idxAndPos = new AbstractMap.SimpleEntry<>(paramIdx, new HashSet<>());
									pathToIdxAndPos.put(resPath, idxAndPos);
								}
								idxAndPos.getValue().add(p);
							}
						}
					}
				}
			}
			for (ResourcePath resPath: channel.getOutputResources()) {
				for (int paramIdx = 0; paramIdx < resPath.getPathParams().size(); paramIdx++) {
					Expression pathParam = resPath.getPathParams().get(paramIdx);
					if (pathParam.contains(sel.getExpression())) {
						for (Map.Entry<Position, Variable> posAndVar:  pathParam.getVariables().entrySet()) {
							Position p = posAndVar.getKey();
							Variable v = posAndVar.getValue();
							if (v.equals(sel.getExpression())) {
								Map<ResourcePath, Map.Entry<Integer, Set<Position>>> pathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel);
								if (pathToIdxAndPos == null) {
									pathToIdxAndPos = new HashMap<>();
									channelSelectorToOutputResourcePathParam.put(sel, pathToIdxAndPos);
								}
								Map.Entry<Integer, Set<Position>> idxAndPos = pathToIdxAndPos.get(resPath);
								if (idxAndPos == null) {
									idxAndPos = new AbstractMap.SimpleEntry<>(paramIdx, new HashSet<>());
									pathToIdxAndPos.put(resPath, idxAndPos);
								}
								idxAndPos.getValue().add(p);
							}
						}
					}
				}
			}
		}
	}
	
	public DataTransferChannel getChannel() {
		return channel;
	}

	public Expression getMessage() {
		return message;
	}
	
	public void setMessage(Expression message) {
		this.message = message;
	}

	public boolean isInput() {
		return isInput;
	}

	public List<Map.Entry<Selector, Constant>> getChannelSelectorAndValues() {
		return channelSelectorAndValues;
	}

	public List<Constant> getChannelSelectorValues() {
		List<Constant> channelValues = new ArrayList<>();
		for (Map.Entry<Selector, Constant> chEnt: channelSelectorAndValues) {
			channelValues.add(chEnt.getValue());
		}
		return channelValues;
	}
	
	public void updateChannelSelectorValues(List<Constant> channelSelectorValues) {
		for (int i = 0; i < channelSelectorValues.size(); i++) {
			Map.Entry<Selector, Constant> chEnt = channelSelectorAndValues.get(i);
			chEnt.setValue(channelSelectorValues.get(i));
		}
	}
	
	public Map<Expression, Expression> getDependingParameters() {
		return dependingParameters;
	}
	
	public void addChild(Event child) {
		childEvents.add(child);
	}
	
	public Set<Event> getChildren() {
		return childEvents;
	}
	
	/**
	 * Construct channel message, collect descendant events of this event and update the values of the depending channel selectors
	 * 
	 * @param resourceStateValueProvider a resourceStateValueProvider to provide current and next resource states
	 * @return calculated message constraint by this event
	 */
	public Expression constructMessageAndDescendantEvents(IResourceStateValueProvider resourceStateValueProvider, boolean doesUpdateDependingParameters) {

		try {
			Map<ChannelMember, Set<Position>> substitutedPositionsInMessageFromChannels = new HashMap<>();
			return constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, doesUpdateDependingParameters);
		} catch (ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed e) {
			e.printStackTrace();
		}
		return null;
	}

	private Expression constructMessageAndDesdendantEvents(IResourceStateValueProvider resourceStateValueProvider, 
			Map<ChannelMember, Set<Position>> substitutedPositionsInMessageFromChannels, boolean doesUpdateDependingParameters)
			throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed {
		Expression unifiedMessage = null;
		Expression messageConstraint = null;
		if (message != null) {
			unifiedMessage = (Term) message.clone();
		}
		IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() {
			@Override
			public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
				ResourceIdentifier resId = getInputResourceIdentifier(target.getResource());
				return resourceStateValueProvider.getCurrentStateValueOf(resId);
			}

			@Override
			public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
				ResourceIdentifier resId = getInputResourceIdentifier(target.getResource());
				return resourceStateValueProvider.getNextStateValueOf(resId);
			}

			@Override
			public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) {
				ResourceIdentifier resId = getInputResourceIdentifier(target);
				return resourceStateValueProvider.getCurrentStateValueOf(resId);
			}
		};
		
		// 1. Calculate message constraints from leaf channel members on the 'channel member dependency graph'.
		Map<ChannelMember, Set<ChannelMember>> dependency = channel.getMemberDependency();
		if (dependency.size() == 0) {
			// No channel member dependency.
			for (ChannelMember channelMember: channel.getInputChannelMembers()) {
				// Calculate message constraint from an input state transition
				messageConstraint = channel.calcMessageConstraintForInputMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
				if (unifiedMessage == null) {
					unifiedMessage = messageConstraint;
				} else {
					unifiedMessage = unifiedMessage.unify(messageConstraint);
					if (unifiedMessage == null) {
						throw new UnificationFailed();
					}
				}
			}
			for (ChannelMember channelMember: channel.getReferenceChannelMembers()) {
				// Calculate message constraint from a reference state transition
				messageConstraint = channel.calcMessageConstraintForReferenceMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
				if (unifiedMessage == null) {
					unifiedMessage = messageConstraint;
				} else {
					unifiedMessage = unifiedMessage.unify(messageConstraint);
					if (unifiedMessage == null) {
						throw new UnificationFailed();
					}
				}
			}
		} else {
			Set<ChannelMember> toResolve = new HashSet<>();
			Set<ChannelMember> resolved = new HashSet<>();
			for (Set<ChannelMember> depended: dependency.values()) {
				toResolve.addAll(depended);
			}
			for (ChannelMember depending: dependency.keySet()) {
				toResolve.remove(depending);
			}
			if ((messageConstraint = getMessage()) instanceof Term) {
				unifiedMessage = messageConstraint;
			}
			for (ChannelMember leafMember: toResolve) {
				if (channel.getInputChannelMembers().contains(leafMember)) {
					// Calculate message constraint from an input state transition
					messageConstraint = channel.calcMessageConstraintForInputMember(leafMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
				} else if (channel.getReferenceChannelMembers().contains(leafMember)) {
					// Calculate message constraint from a reference state transition
					messageConstraint = channel.calcMessageConstraintForReferenceMember(leafMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
				}
				if (unifiedMessage == null) {
					unifiedMessage = messageConstraint;
				} else {
					unifiedMessage = unifiedMessage.unify(messageConstraint);
					if (unifiedMessage == null) {
						throw new UnificationFailed();
					}
				}
			}
			resolved.addAll(toResolve);
			toResolve.clear();
			
			// 2. Calculate message constraints from remaining members on the channel member dependency graph.
			for (;;) {
				// Identify the channel members to resolve next.
				for (Map.Entry<ChannelMember, Set<ChannelMember>> dependEnt: dependency.entrySet()) {
					ChannelMember dependingMem = dependEnt.getKey();
					Set<ChannelMember> dependedMems = dependEnt.getValue();
					if (!resolved.contains(dependingMem) && resolved.containsAll(dependedMems)) {
						toResolve.add(dependingMem);
					}
				}
				if (toResolve.size() == 0) break;
				for (ChannelMember dependingMem: toResolve) {
					// Fill the path parameters of the resource path of a depending channel member.
					if (doesUpdateDependingParameters) {
						Set<Position> dependingVarPosInMessage = new HashSet<>();
						ResourcePath filledResPath = channel.fillOutsideResourcePath(dependingMem.getResource(), unifiedMessage, dependingMem.getStateTransition().getMessageExpression(), dependingVarPosInMessage);
						ResourcePath unfilledResPath = dependingMem.getResource();
						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.
								return null;		// Not to fire this event.
							}
							if (!channel.getAllSelectorVariables().contains(var)) {
								// Update a depending channel parameter
								updateDependingParameter(var, val);
							}
						}
					}
					
					// Calculate message constraint
					if (channel.getInputChannelMembers().contains(dependingMem)) {
						// Calculate message constraint from an input state transition
						messageConstraint = channel.calcMessageConstraintForInputMember(dependingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
					} else if (channel.getReferenceChannelMembers().contains(dependingMem)) {
						// Calculate message constraint from a reference state transition
						messageConstraint = channel.calcMessageConstraintForReferenceMember(dependingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
					}
					if (unifiedMessage == null) {
						unifiedMessage = messageConstraint;
					} else {
						unifiedMessage = unifiedMessage.unify(messageConstraint);
						if (unifiedMessage == null) {
							throw new UnificationFailed();
						}
					}
				}
				resolved.addAll(toResolve);
				toResolve.clear();
			}
			// For the channel members that are depended by and depend on no other member. 
			for (ChannelMember remainingMem: channel.getChannelMembers()) {
				if (!resolved.contains(remainingMem)) {
					resolved.add(remainingMem);
					// Calculate message constraint
					messageConstraint = null;
					if (channel.getInputChannelMembers().contains(remainingMem)) {
						// Calculate message constraint from an input state transition
						messageConstraint = channel.calcMessageConstraintForInputMember(remainingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
					} else if (channel.getReferenceChannelMembers().contains(remainingMem)) {
						// Calculate message constraint from a reference state transition
						messageConstraint = channel.calcMessageConstraintForReferenceMember(remainingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels);
					}
					if (messageConstraint != null) {
						if (unifiedMessage == null) {
							unifiedMessage = messageConstraint;
						} else {
							unifiedMessage = unifiedMessage.unify(messageConstraint);
							if (unifiedMessage == null) {
								throw new UnificationFailed();
							}
						}
					}
				}
			}
		}
		if (inputResource == null) return unifiedMessage;
		
		// 3. Propagate parent event message to collect child events and calculate message constraints from child channels.
		Resource baseResource = inputResource;
		while (baseResource.getResourceHierarchy().getNumParameters() == 0) {
			if (baseResource.getParent() == null) break;
			baseResource = baseResource.getParent();
		}
		Expression parentEventMessage = (Expression) unifiedMessage.clone();
		for (Channel childChannel: channel.getChildren()) {
			// Search the deepest input or reference side resource path in each child channel that matches the channel parameters.
			ChannelMember inOrRef = null;
			Set<ChannelMember> channelMembers = new HashSet<>(((DataTransferChannel) childChannel).getInputChannelMembers());
			channelMembers.addAll(((DataTransferChannel) childChannel).getReferenceChannelMembers());
			for (ChannelMember cm: channelMembers) {
				if (!cm.isOutside()) {
					ResourcePath resPath = cm.getResource();
					if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) {
						inOrRef = cm;
						break;
					}
				}
			}
			if (inOrRef != null) {
				// Collect events for all resources under this event's input resource that matches the deepest input side resource path.
				for (Resource res: baseResource.getDescendants(inOrRef.getResource().getResourceHierarchy())) {			
					Event childEvent = new Event((DataTransferChannel) childChannel, inOrRef.getResource(), res);
					childEvent.setMessage(parentEventMessage);
					messageConstraint = childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true);
					if (messageConstraint != null) {
						childEvent.setMessage(messageConstraint);
						if (unifiedMessage == null) {
							unifiedMessage = messageConstraint;
						} else {
							unifiedMessage = unifiedMessage.unify(messageConstraint);
							if (unifiedMessage == null) {
								throw new UnificationFailed();
							}
						}
						childEvents.add(childEvent);
					}
				}
			} else {
				// Search the deepest output side resource path in each child channel that matches the channel parameters.
				ChannelMember out = null;
				for (ChannelMember cm: ((DataTransferChannel) childChannel).getOutputChannelMembers()) {
					if (!cm.isOutside()) {
						ResourcePath resPath = cm.getResource();
						if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) {
							out = cm;
							break;
						}
					}
				}
				if (out != null) {
					// Collect events for all resources under this event's input resource that matches the deepest output side resource path.
					for (Resource res: baseResource.getDescendants(out.getResource().getResourceHierarchy())) {			
						Event childEvent = new Event((DataTransferChannel) childChannel, parentEventMessage, out.getResource(), res);
						childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true);
						childEvents.add(childEvent);
					}
				}
			}
		}
		
		return unifiedMessage;
	}
	
	public void updateDependingParameter(Expression variable, Expression value) {
		dependingParameters.put(variable, value);
	}

	public ResourcePath getInputResourcePath() {
		return inputResourcePath;
	}

	public Resource getInputResource() {
		return inputResource;
	}

	public Set<Resource> getOutputResources() {
		return outputResources;
	}
	
	public ResourceIdentifier getResourceIdentifier(ResourcePath resPath) {
		ResourceIdentifier resId = ResourceIdentifier.createFrom(resPath);
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			Map<ResourcePath, Map.Entry<Integer, Set<Position>>> inputPathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel);
			if (inputPathToIdxAndPos != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = inputPathToIdxAndPos.get(resPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Variable) {
							resId.setPathParam(paramIdx, chParamEnt.getValue());
						}
					}
				}
			}
		}
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			Map<ResourcePath, Map.Entry<Integer, Set<Position>>> outputPathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel);
			if (outputPathToIdxAndPos != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = outputPathToIdxAndPos.get(resPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Variable) {
							resId.setPathParam(paramIdx, chParamEnt.getValue());
						}
					}
				}
			}
		}
		for (Expression var: dependingParameters.keySet()) {
			int paramIdx = resId.getPathParams().indexOf(var);
			if (paramIdx >= 0) {
				resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var));
			}
		}
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			Map<ResourcePath, Map.Entry<Integer, Set<Position>>> inputPathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel);
			if (inputPathToIdxAndPos != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = inputPathToIdxAndPos.get(resPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) {
							pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue());
							resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce());
						}
					}
				}
			}
		}
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			Map<ResourcePath, Map.Entry<Integer, Set<Position>>> outputPathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel);
			if (outputPathToIdxAndPos != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = outputPathToIdxAndPos.get(resPath);
				if (pathParamEnt != null) {
				Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) {
							pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue());
							resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce());
						}
					}
				}
			}
		}
		return resId;
	}
	
	public ResourceIdentifier getInputResourceIdentifier(ResourcePath inputResPath) {
		ResourceIdentifier resId = ResourceIdentifier.createFrom(inputResPath);
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			if (channelSelectorToInputOrReferenceResourcePathParam.get(sel) != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Variable) {
							resId.setPathParam(paramIdx, chParamEnt.getValue());
						}
					}
				}
			}
		}
		for (Expression var: dependingParameters.keySet()) {
			int paramIdx = resId.getPathParams().indexOf(var);
			if (paramIdx >= 0) {
				resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var));
			}
		}
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			if (channelSelectorToInputOrReferenceResourcePathParam.get(sel) != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) {
							pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue());
							resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce());
						}
					}
				}
			}
		}
		return resId;
	}

	public ResourceIdentifier getOutputResourceIdentifier(ResourcePath outputResPath) {
		ResourceIdentifier resId = ResourceIdentifier.createFrom(outputResPath);
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			if (channelSelectorToOutputResourcePathParam.get(sel) != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Variable) {
							resId.setPathParam(paramIdx, chParamEnt.getValue());
						}
					}
				}
			}
		}
		for (Expression var: dependingParameters.keySet()) {
			int paramIdx = resId.getPathParams().indexOf(var);
			if (paramIdx >= 0) {
				resId.setPathParam(paramIdx, dependingParameters.get(var));
			}
		}
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			if (channelSelectorToOutputResourcePathParam.get(sel) != null) {
				Map.Entry<Integer, Set<Position>> pathParamEnt = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath);
				if (pathParamEnt != null) {
					Integer paramIdx = pathParamEnt.getKey();
					if (paramIdx != null) {
						Expression pathParamExp = resId.getPathParams().get(paramIdx);
						if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) {
							pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue());
							resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce());
						}
					}
				}
			}
		}
		return resId;
	}
	
	
	public interface IResourceStateValueProvider {
		Expression getCurrentStateValueOf(ResourceIdentifier resId);
		Expression getNextStateValueOf(ResourceIdentifier resId);
	}
}