package sushi.query.bpmn;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import sushi.bpmn.decomposition.Component;
import sushi.bpmn.element.AbstractBPMNElement;
import sushi.event.SushiEventType;
import sushi.query.PatternQueryType;
import sushi.query.SushiPatternQuery;
import sushi.query.SushiPatternQueryListener;
import sushi.query.SushiQueryTypeEnum;
/**
* This factory generates queries for loop components. <br>
* @author micha
*/
public class LoopQueryFactory extends AbstractPatternQueryFactory {
/**
* Constructor to create loop queries with a query factory.
* @param patternQueryGenerator
*/
public LoopQueryFactory(PatternQueryGenerator patternQueryGenerator) {
super(patternQueryGenerator);
}
@Override
protected SushiPatternQuery generateQuery(AbstractBPMNElement element, AbstractBPMNElement catchingMonitorableElement, SushiPatternQuery parentQuery) {
if(element instanceof Component){
Component component = (Component) element;
// Operator: UNTIL
//Sequence-Polygone darin wie immer
//Until-Bedingung: beobachtbares Element nach der Loop-Bond-Component
List<AbstractBPMNElement> untilConditionElements = this.getSuccessingMonitorableElement(component);
//QUERY bauen Elemente der Component (ein Polygon?) UNTIL (conditionElement1 OR conditionElement2 OR ...)
SushiPatternQuery query = new SushiPatternQuery(generateQueryName("LOOP"), null, SushiQueryTypeEnum.LIVE, PatternQueryType.LOOP, this.orderElements(component));
String queryString = generateLoopQueryString(component, untilConditionElements, catchingMonitorableElement, query);
addQueryRelationship(parentQuery, query);
query.setQueryString(queryString);
System.out.println(query.getTitle() + ": " + query.getQueryString());
registerQuery(query);
if(query != null && query.getListener() != null){
query.getListener().setCatchingElement(catchingMonitorableElement);
}
SushiPatternQueryListener listener = query.getListener();
Set<SushiEventType> loopBreakEventTypes = new HashSet<SushiEventType>();
for(AbstractBPMNElement conditionElement : untilConditionElements){
loopBreakEventTypes.add(conditionElement.getMonitoringPoints().get(0).getEventType());
}
listener.setLoopBreakEventTypes(new ArrayList<SushiEventType>(loopBreakEventTypes));
return query;
//Element nochmal zu Esper schicken?
} else {
System.err.println("Input element should be a component for a LOOP-query!");
return null;
}
}
private String generateLoopQueryString(Component component, List<AbstractBPMNElement> untilConditionElements, AbstractBPMNElement catchingMonitorableElement, SushiPatternQuery parentQuery) {
//EVERY (([1]S0=SmallSequence) UNTIL (EVERY U0=SecondEvent))
int elementsWithMonitoringPoints = 0;
StringBuilder sequencePatternQueryString = new StringBuilder();
//TODO: Liefert auch noch Gateways am Rand, die nicht betrachtet werden sollten
List<AbstractBPMNElement> orderedChildren = this.orderElements(component);
for(AbstractBPMNElement element : orderedChildren){
//Falls Element Component rekursiv tiefer aufrufen
StringBuilder queryPart = new StringBuilder();
if(element instanceof Component){
SushiPatternQuery subQuery = new PatternQueryFactory(patternQueryGenerator).generateQuery(element, catchingMonitorableElement, parentQuery);
queryPart.append("[1] S" + elementsWithMonitoringPoints + "=");
queryPart.append(subQuery.getTitle());
} else {
continue;
}
if(elementsWithMonitoringPoints == 0){ //Erstes Element
sequencePatternQueryString.append("SELECT * FROM PATTERN [(EVERY ((");
sequencePatternQueryString.append(queryPart);
} else {
sequencePatternQueryString.append(" " + EsperPatternOperators.XOR.operator + " " + queryPart);
}
elementsWithMonitoringPoints++;
}
sequencePatternQueryString.append(") " + EsperPatternOperators.LOOP.operator + " (");
//Eines der UntilConditionElemente sollte als Abbruch ausgelöst werden
int untilConditionElementsCounter = 0;
for(AbstractBPMNElement element : untilConditionElements){
StringBuilder queryPart = new StringBuilder();
queryPart.append("U" + untilConditionElementsCounter + "=");
queryPart.append(element.getMonitoringPoints().get(0).getEventType().getTypeName());
if(untilConditionElementsCounter == 0){ //Erstes Element
sequencePatternQueryString.append(queryPart);
} else {
sequencePatternQueryString.append(" " + EsperPatternOperators.XOR.operator + " " + queryPart);
}
untilConditionElementsCounter++;
}
if(catchingMonitorableElement == null){
sequencePatternQueryString.append(")))]");
} else {
sequencePatternQueryString.append(") " + EsperPatternOperators.XOR.operator + " EVERY C1=");
sequencePatternQueryString.append(catchingMonitorableElement.getMonitoringPoints().get(0).getEventType().getTypeName());
sequencePatternQueryString.append("))]");
}
//gleiche ProcessInstanceID-Bedingung anhängen
//TODO: Funktioniert noch nicht! Es kann nicht auf Pattern-Elemente vor dem Until zugegriffen werden :(
// sequencePatternQueryString.append(" WHERE sushi.esper.SushiUtils.isIntersectionNotEmpty({");
// for(int j = 0; j < elementsWithMonitoringPoints; j++){
// sequencePatternQueryString.append("S" + j + ".ProcessInstances,");
// }
// for(int j = 0; j < untilConditionElementsCounter; j++){
// if(j != untilConditionElementsCounter - 1){ //letztes Element --> kein Komma
// sequencePatternQueryString.append("U" + j + ".ProcessInstances,");
// } else {
// sequencePatternQueryString.append("U" + j + ".ProcessInstances");
// }
// }
// sequencePatternQueryString.append("})");
return sequencePatternQueryString.toString();
}
private List<AbstractBPMNElement> getSuccessingMonitorableElement(Component component) {
Set<AbstractBPMNElement> successingMonitorableElements = new HashSet<AbstractBPMNElement>();
Set<AbstractBPMNElement> visitedElements = new HashSet<AbstractBPMNElement>();
traverseSuccessingMonitorableElements(component.getExitPoint(), visitedElements, successingMonitorableElements);
return new ArrayList<AbstractBPMNElement>(successingMonitorableElements);
}
}