package hu.bme.mit.incquery.cep.runtime.evaluation;
import hu.bme.mit.incquery.cep.api.EventPatternAutomatonOptions;
import hu.bme.mit.incquery.cep.metamodels.cep.AtomicEventPattern;
import hu.bme.mit.incquery.cep.metamodels.cep.ComplexEventPattern;
import hu.bme.mit.incquery.cep.metamodels.cep.ComplexOperator;
import hu.bme.mit.incquery.cep.metamodels.cep.EventPattern;
import hu.bme.mit.incquery.cep.metamodels.cep.Timewindow;
import hu.bme.mit.incquery.cep.metamodels.internalsm.FinalState;
import hu.bme.mit.incquery.cep.metamodels.internalsm.Guard;
import hu.bme.mit.incquery.cep.metamodels.internalsm.InitState;
import hu.bme.mit.incquery.cep.metamodels.internalsm.InternalExecutionModel;
import hu.bme.mit.incquery.cep.metamodels.internalsm.InternalsmFactory;
import hu.bme.mit.incquery.cep.metamodels.internalsm.State;
import hu.bme.mit.incquery.cep.metamodels.internalsm.StateMachine;
import hu.bme.mit.incquery.cep.metamodels.internalsm.TimeConstraint;
import hu.bme.mit.incquery.cep.metamodels.internalsm.TimeConstraintSpecification;
import hu.bme.mit.incquery.cep.metamodels.internalsm.TimeConstraintType;
import hu.bme.mit.incquery.cep.metamodels.internalsm.Transition;
import hu.bme.mit.incquery.cep.metamodels.internalsm.TrapState;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.google.common.base.Strings;
public class StateMachineBuilder {
private final InternalsmFactory SM_FACTORY = InternalsmFactory.eINSTANCE;
private InternalExecutionModel model;
private Map<AtomicEventPattern, List<Timewindow>> flattenedAtomicEventPatterns;
private InitState initState;
private FinalState finalState;
private StateMachine sm;
private EventPattern rootPattern;
private Map<Timewindow, TimeConstraintSpecification> window2constraintSpecMapping;
private EventPatternAutomatonOptions options;
public StateMachineBuilder(InternalExecutionModel model, EventPattern rootPattern,
EventPatternAutomatonOptions options) {
this.model = model;
this.rootPattern = rootPattern;
model.eResource().getContents().add(rootPattern);
window2constraintSpecMapping = new LinkedHashMap<Timewindow, TimeConstraintSpecification>();
this.options = options;
}
public StateMachine buildStateMachine() {
sm = SM_FACTORY.createStateMachine();
flattenedAtomicEventPatterns = SMUtils.flattenEventPatternsWithTimewindows(rootPattern);
buildInitialTrace();
if (flattenedAtomicEventPatterns.size() > 1) {
buildAlternativeTraces();
}
sm.getStates().add(SM_FACTORY.createTrapState());
sm.setEventPattern(rootPattern);
if (options != null) {
sm.setPriority(options.getPriority());
}
model.eResource().getContents().addAll(window2constraintSpecMapping.values());
model.getStateMachines().add(sm);
return sm;
}
private void buildInitialTrace() {
initState = SM_FACTORY.createInitState();
sm.getStates().add(initState);
List<State> states = new ArrayList<State>();
State latestState = initState;
for (AtomicEventPattern ep : flattenedAtomicEventPatterns.keySet()) {
if (!ep.equals(getLastElement(flattenedAtomicEventPatterns.keySet()))) {
State currentState = SM_FACTORY.createState();
states.add(currentState);
createTransition(latestState, currentState, createEventGuard(ep));
if (latestState.equals(initState)) {
assignTimeConstraints(currentState, ep, TimeConstraintType.START);
} else {
assignTimeConstraints(currentState, ep);
}
latestState = currentState;
}
else {
finalState = createFinalState(ep);
assignTimeConstraints(finalState, ep, TimeConstraintType.STOP);
states.add(finalState);
createTransition(latestState, finalState, createEventGuard(ep));
}
}
sm.getStates().addAll(states);
}
private void assignTimeConstraints(State currentState, AtomicEventPattern ep) {
if (SMUtils.isFinal(currentState)) {
assignTimeConstraints(currentState, ep, TimeConstraintType.STOP);
return;
} else
assignTimeConstraints(currentState, ep, TimeConstraintType.CHECK);
}
private TimeConstraint createTimeConstraintForSpec(TimeConstraintSpecification tcs, TimeConstraintType type) {
TimeConstraint timeConstraint = SM_FACTORY.createTimeConstraint();
timeConstraint.setTimeConstraintSpecification(tcs);
timeConstraint.setType(type);
return timeConstraint;
}
private void assignTimeConstraints(State currentState, AtomicEventPattern ep, TimeConstraintType constraintType) {
for (Timewindow timewindow : flattenedAtomicEventPatterns.get(ep)) {
TimeConstraintSpecification timeConstraintSpecification = getRegisteredConstraintSpecForTimewindow(timewindow);
if (timeConstraintSpecification == null) {
timeConstraintSpecification = SM_FACTORY.createTimeConstraintSpecification();
String id = registerWindow2ConstraintSpecMapping(timewindow, timeConstraintSpecification);
timeConstraintSpecification.setId(id);
timeConstraintSpecification.setExpectedLength(timewindow.getLength());
}
if (stateEquippedWithTheGivenTimeconstraintSpec(currentState, timeConstraintSpecification)) {
continue;
}
TimeConstraint timeConstraint = createTimeConstraintForSpec(timeConstraintSpecification, constraintType);
currentState.getTimeConstraints().add(timeConstraint);
continue;
}
}
private String registerWindow2ConstraintSpecMapping(Timewindow timewindow,
TimeConstraintSpecification timeConstraintSpecification) {
UUID uuid = UUID.randomUUID();
window2constraintSpecMapping.put(timewindow, timeConstraintSpecification);
return uuid.toString();
}
private boolean stateEquippedWithTheGivenTimeconstraintSpec(State currentState,
TimeConstraintSpecification timeConstraintSpecification) {
for (TimeConstraint tc : currentState.getTimeConstraints()) {
if (tc.getTimeConstraintSpecification() == null) {
continue;
}
if (tc.getTimeConstraintSpecification().getId().equalsIgnoreCase(timeConstraintSpecification.getId())) {
return true;
}
}
return false;
}
private TimeConstraintSpecification getRegisteredConstraintSpecForTimewindow(Timewindow timewindow) {
for (Timewindow t : window2constraintSpecMapping.keySet()) {
if (timewindow.equals(t)) {
return window2constraintSpecMapping.get(t);
}
}
return null;
}
private AtomicEventPattern getLastElement(Set<AtomicEventPattern> atomicEventPatterns) {
Iterator<AtomicEventPattern> i = atomicEventPatterns.iterator();
AtomicEventPattern lastElement = i.next();
while (i.hasNext()) {
lastElement = i.next();
}
return lastElement;
}
private void buildAlternativeTraces() {
State currentState = finalState;
while (!(currentState.equals(initState) && getAvailableAtomicEventPatterns(currentState).isEmpty())) {
if (currentState instanceof FinalState) {
currentState = stepBack(currentState);
}
List<State> states = new ArrayList<State>();
List<AtomicEventPattern> availableAtomicEventPatterns = getAvailableAtomicEventPatterns(currentState);
while (availableAtomicEventPatterns.isEmpty()) {
currentState = stepBack(currentState);
availableAtomicEventPatterns = getAvailableAtomicEventPatterns(currentState);
if (currentState.equals(initState) && availableAtomicEventPatterns.isEmpty()) {
return;
}
}
while (!availableAtomicEventPatterns.isEmpty()) {
AtomicEventPattern nextEventType = getAvailableAtomicEventPatterns(currentState).get(0);
State foundState = findTrace(currentState, nextEventType);
if (foundState == null) {
State nextState = SM_FACTORY.createState();
states.add(nextState);
createTransition(currentState, nextState, createEventGuard(nextEventType));
assignTimeConstraints(nextState, nextEventType);
currentState = nextState;
availableAtomicEventPatterns = getAvailableAtomicEventPatterns(currentState);
} else if (foundState != null) {
State nextState = foundState;
createTransition(currentState, nextState, createEventGuard(nextEventType));
availableAtomicEventPatterns.remove(nextEventType);
}
}
sm.getStates().addAll(states);
}
}
private FinalState createFinalState(AtomicEventPattern ep) {
FinalState finalState = SM_FACTORY.createFinalState();
finalState.setLabel("final");
return finalState;
}
private State findTrace(State state, AtomicEventPattern nextEventType) {
List<AtomicEventPattern> eventsToLookFor = getUsedAtomicEventPatterns(state);
eventsToLookFor.add(nextEventType);
for (State s : sm.getStates()) {
List<AtomicEventPattern> usedEventPatterns = getUsedAtomicEventPatterns(s);
if (!(usedEventPatterns.size() == eventsToLookFor.size())) {
continue;
}
for (AtomicEventPattern e1 : eventsToLookFor) {
removeByType(usedEventPatterns, e1);
}
if (usedEventPatterns.isEmpty()) {
return s;
}
}
return null;
}
private void removeByType(List<AtomicEventPattern> collection, AtomicEventPattern element) {
for (AtomicEventPattern ap : collection) {
if (ap.getType().equalsIgnoreCase(element.getType())) {
collection.remove(ap);
return;
}
}
}
private List<AtomicEventPattern> getUsedAtomicEventPatterns(State state) {
List<AtomicEventPattern> events = new ArrayList<AtomicEventPattern>();
State currentState = state;
while (!(currentState instanceof InitState)) {
AtomicEventPattern usedEvent = currentState.getInTransitions().get(0).getGuard().getEventType();
events.add(usedEvent);
currentState = stepBack(currentState);
}
return events;
}
private List<AtomicEventPattern> getAvailableAtomicEventPatterns(State state) {
List<AtomicEventPattern> events = new ArrayList<AtomicEventPattern>();
for (AtomicEventPattern ep : flattenedAtomicEventPatterns.keySet()) {
if (isAvailable(ep, state)) {
if (getUsedTimes(events, ep) == 0) {
events.add(ep);
}
}
}
return events;
}
private boolean isAvailable(AtomicEventPattern atomicEventPattern, State state) {
if (appearsOnOutTransition(atomicEventPattern, state)) {
return false;
}
List<AtomicEventPattern> usedAtomicEventPatterns = getUsedAtomicEventPatterns(state);
int usedTimes = getUsedTimes(usedAtomicEventPatterns, atomicEventPattern);
int availableTimes = getUsedTimes(flattenedAtomicEventPatterns.keySet(), atomicEventPattern);
if (!(usedTimes < availableTimes)) {
return false;
}
if (checkPrerequisites(atomicEventPattern, state)) {
return true;
}
return false;
}
private int getUsedTimes(Collection<AtomicEventPattern> usedAtomicEventPatterns,
AtomicEventPattern atomicEventPattern) {
if (usedAtomicEventPatterns.isEmpty()) {
return 0;
}
int count = 0;
for (AtomicEventPattern ap : usedAtomicEventPatterns) {
if (ap.getType().equalsIgnoreCase(atomicEventPattern.getType())) {
count++;
}
}
return count;
}
private boolean isRootPattern(EventPattern pattern) {
if (pattern.getId().equals(rootPattern.getId())) {
return true;
}
return false;
}
private boolean checkPrerequisites(EventPattern pattern, State state) {
if (pattern.eContainer() == null || pattern.eContainer() instanceof AtomicEventPattern) {
return false;
}
ComplexEventPattern cePattern = (ComplexEventPattern) pattern.eContainer();
while (true) {
if (cePattern == null) {
break;
}
if (isOrdered(cePattern)) {
List<EventPattern> compositionEvents = cePattern.getCompositionEvents();
int patternPosition = getPatternPositionInContainerPattern(pattern);
if (patternPosition == 0 && !isRootPattern(cePattern)) {
continue;
}
for (int i = 0; i < patternPosition; i++) {
EventPattern patternToCheck = compositionEvents.get(i);
if (patternToCheck instanceof AtomicEventPattern) {
if (!getUsedAtomicEventPatterns(state).contains(((AtomicEventPattern) patternToCheck))) {
return false;
}
}
}
}
if (isRootPattern(cePattern)) {
break;
}
pattern = cePattern;
cePattern = (ComplexEventPattern) cePattern.eContainer();
}
return true;
}
private int getPatternPositionInContainerPattern(EventPattern patternToFind) {
if (isRootPattern(patternToFind)) {
return 0;
}
ComplexEventPattern container = (ComplexEventPattern) patternToFind.eContainer();
int patternPosition = 0;
for (EventPattern ep : container.getCompositionEvents()) {
if (ep.equals(patternToFind)) {
break;
}
patternPosition++;
}
return patternPosition;
}
private boolean appearsOnOutTransition(AtomicEventPattern atomicEventPattern, State state) {
if (state.getOutTransitions().isEmpty()) {
return false;
}
for (Transition t : state.getOutTransitions()) {
if (t.getGuard().getEventType().getType().equals(atomicEventPattern.getType())) {
return true;
}
}
return false;
}
private State stepBack(State state) {
return state.getInTransitions().get(0).getPreState();
}
private Transition createTransition(State preState, State postState, Guard guard) {
Transition t = SM_FACTORY.createTransition();
t.setGuard(guard);
t.setPreState(preState);
t.setPostState(postState);
if ((postState instanceof FinalState) || (postState instanceof TrapState)) {
return t;
}
String[] split = guard.getEventType().getType().split("\\.");
String additionalEventType = split[split.length - 1];
String postLabel = postState.getLabel();
if (Strings.isNullOrEmpty(postLabel) || postLabel.equalsIgnoreCase("null")) {
postState.setLabel("");
postLabel = postState.getLabel();
}
String newLabel = "";
// adding prestate label
if (!(preState instanceof InitState)) {
String preLabel = preState.getLabel();
if (!Strings.isNullOrEmpty(preLabel)) {
for (String s : Arrays.asList(preLabel.split(""))) {
if (!postLabel.contains(s)) {
newLabel += s;
}
}
}
}
// adding transition
if (!postLabel.contains(additionalEventType)) {
newLabel += additionalEventType;
}
postState.setLabel(postLabel + newLabel);
return t;
}
private Guard createEventGuard(AtomicEventPattern atomicEventPattern) {
Guard g = SM_FACTORY.createGuard();
g.setEventType(atomicEventPattern);
return g;
}
private boolean isOrdered(ComplexEventPattern cePattern) {
if (cePattern.getOperator().equals(ComplexOperator.ORDERED)
|| cePattern.getOperator().equals(ComplexOperator.ORDERED_T)) {
return true;
}
return false;
}
}