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.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, Integer>> channelSelectorToInputResourcePathParam = new HashMap<>();
	private Map<Selector, Map<ResourcePath, Integer>> 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) {
			int paramIdx = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath);
			Resource ancestor = outputResource;
			int idx = outputResPath.getPathParams().size();
			while (ancestor != null) {
				if (ancestor.getResourceHierarchy().getNumParameters() > 0) {
					idx--;
					if (idx == paramIdx) 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) {
				Integer paramIdx = channelSelectorToInputResourcePathParam.get(sel).get(inputResPath);
				if (paramIdx != null) {
					Resource ancestor = inputResource;
					int idx = inputResPath.getPathParams().size();
					while (ancestor != null) {
						if (ancestor.getResourceHierarchy().getNumParameters() > 0) {
							idx--;
							if (idx == paramIdx) 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) {
			for (ResourcePath resPath: channel.getInputResources()) {
				int paramIdx = resPath.getPathParams().indexOf(sel.getExpression());
				if (paramIdx >= 0) {
					Map<ResourcePath, Integer> pathToIdx = channelSelectorToInputResourcePathParam.get(sel);
					if (pathToIdx == null) {
						pathToIdx = new HashMap<>();
						channelSelectorToInputResourcePathParam.put(sel, pathToIdx);
					}
					pathToIdx.put(resPath, paramIdx);
				}
			}
			for (ResourcePath resPath: channel.getOutputResources()) {
				int paramIdx = resPath.getPathParams().indexOf(sel.getExpression());
				if (paramIdx >= 0) {
					Map<ResourcePath, Integer> pathToIdx = channelSelectorToOutputResourcePathParam.get(sel);
					if (pathToIdx == null) {
						pathToIdx = new HashMap<>();
						channelSelectorToOutputResourcePathParam.put(sel, pathToIdx);
					}
					pathToIdx.put(resPath, paramIdx);
				}
			}
		}
	}
	
	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;
	}
	
	
	/**
	 * 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 Term constructMessageAndDescendantEvents(IResourceStateValueProvider resourceStateValueProvider, boolean doesUpdateDependingParameters) {
		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);
			}
		};

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

	private Term constructMessageAndDesdendantEvents(DataTransferChannel channel, IResourceStateAccessor resouceStateAccessor,
			Map<ChannelMember, Set<Position>> substitutedPositionsInMessageFromChannels, boolean doesUpdateDependingParameters)
			throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed {
		Term unifiedMessage = null;
		
		// 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.
			Expression messageConstraint = null;
			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 = (Term) messageConstraint;
				} else {
					unifiedMessage = (Term) 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 = (Term) messageConstraint;
				} else {
					unifiedMessage = (Term) unifiedMessage.unify(messageConstraint);
					if (unifiedMessage == null) {
						throw new UnificationFailed();
					}
				}
			}
			return unifiedMessage;
		}
		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);
		}
		Expression messageConstraint = null;
		if ((messageConstraint = getMessage()) instanceof Term) {
			unifiedMessage = (Term) 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 = (Term) messageConstraint;
			} else {
				unifiedMessage = (Term) unifiedMessage.unify(messageConstraint);
				if (unifiedMessage == null) {
					throw new UnificationFailed();
				}
			}
		}
		resolved.addAll(toResolve);
		toResolve.clear();
		
		// Calculate message constraints from remaining members on the channel member dependency graph.
		for (;;) {
			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.
						}
						boolean isSelector = false;
						for (Selector sel: channel.getAllSelectors()) {
							if (sel.getExpression().equals(var)) {
								isSelector = true;
								break;
							}
						}
						if (!isSelector) {
							// 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 = (Term) messageConstraint;
				} else {
					unifiedMessage = (Term) unifiedMessage.unify(messageConstraint);
					if (unifiedMessage == null) {
						throw new UnificationFailed();
					}
				}
			}
			resolved.addAll(toResolve);
			toResolve.clear();
		}
		
		// Calculate message constraints from child channels.
//		for (Channel childChannel: channel.getChildren()) {
//			for (Selector nextSelector: childChannel.getSelectors()) {
//				
//			}
//			Event childEvent = new Event((DataTransferChannel) childChannel, (Expression) messageConstraint.clone(), null, null);
//			messageConstraint = constructMessageAndDesdendantEvents((DataTransferChannel) childChannel, resouceStateAccessor, substitutedPositionsInMessageFromChannels);
//			if (unifiedMessage == null) {
//				unifiedMessage = (Term) messageConstraint;
//			} else {
//				unifiedMessage = (Term) unifiedMessage.unify(messageConstraint);
//				if (unifiedMessage == null) {
//					throw new UnificationFailed();
//				}
//			}
//			childEvents.add(childEvent);
//		}
		
		message = unifiedMessage;
		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, Integer> inputPathParamEnt = channelSelectorToInputResourcePathParam.get(sel);
			if (inputPathParamEnt != null) {
				Integer paramIdx = inputPathParamEnt.get(resPath);
				if (paramIdx != null) {
					resId.setPathParam(paramIdx, chParamEnt.getValue());
				}
			}
		}
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			Map<ResourcePath, Integer> outputPathParamEnt = channelSelectorToOutputResourcePathParam.get(sel);
			if (outputPathParamEnt != null) {
				Integer paramIdx = outputPathParamEnt.get(resPath);
				if (paramIdx != null) {
					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));
			}
		}
		return resId;
	}
	
	public ResourceIdentifier getInputResourceIdentifier(ResourcePath inputResPath) {
		ResourceIdentifier resId = ResourceIdentifier.createFrom(inputResPath);
		for (Map.Entry<Selector, Constant> chParamEnt: channelSelectorAndValues) {
			Selector sel = chParamEnt.getKey();
			if (channelSelectorToInputResourcePathParam.get(sel) != null) {
				Integer paramIdx = channelSelectorToInputResourcePathParam.get(sel).get(inputResPath);
				if (paramIdx != null) {
					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));
			}
		}
		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) {
				Integer paramIdx = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath);
				if (paramIdx != null) {
					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));
			}
		}
		return resId;
	}
	
	
	public interface IResourceStateValueProvider {
		Expression getCurrentStateValueOf(ResourceIdentifier resId);
		Expression getNextStateValueOf(ResourceIdentifier resId);
	}
}