/**
* Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* - Apache License, version 2.0
* - Apache Software License, version 1.0
* - GNU Lesser General Public License, version 3
* - Mozilla Public License, versions 1.0, 1.1 and 2.0
* - Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* icense version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
/**
* Part of the diploma thesis of Thomas Everding.
* @author Thomas Everding
*/
package org.n52.ses.eml.v002.pattern;
import java.util.HashMap;
import org.n52.ses.api.eml.ILogicController;
import org.n52.ses.api.event.MapEvent;
import org.n52.ses.eml.v002.Constants;
import org.n52.ses.eml.v002.filterlogic.EMLParser;
import org.n52.ses.eml.v002.filterlogic.esper.customFunctions.MethodNames;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* representation of a complex pattern
*
* @author Thomas Everding
*
*/
public class PatternComplex extends AGuardedViewPattern {
/*
* Logger instance for this class
*/
private static final Logger logger = LoggerFactory
.getLogger(PatternComplex.class);
private PatternOperator operator;
private String firstPatternID;
private String secondPatternID;
private int firstSelectFunctionNumber;
private int secondSelectFunctionNumber;
private ILogicController controller;
private long maxListeningDuration = -1;
private EMLParser parser;
/**
* @return the operator
*/
public PatternOperator getOperator() {
return this.operator;
}
/**
* sets the operator
*
* @param operator the operator to set
*/
public void setOperator(PatternOperator operator) {
this.operator = operator;
}
/**
* @return the first pattern ID
*/
public String getFirstPatternID() {
return this.firstPatternID;
}
/**
* @param firstPatternID the first pattern ID to set
*/
public void setFirstPatternID(String firstPatternID) {
this.firstPatternID = firstPatternID;
}
/**
* @return the second pattern ID
*/
public String getSecondPatternID() {
return this.secondPatternID;
}
/**
* @param secondPatternID the second pattern ID to set
*/
public void setSecondPatternID(String secondPatternID) {
this.secondPatternID = secondPatternID;
}
/**
* @return the firstSelectFunctionNumber
*/
public int getFirstSelectFunctionNumber() {
return this.firstSelectFunctionNumber;
}
/**
* @param firstSelectFunctionNumber the firstSelectFunctionNumber to set
*/
public void setFirstSelectFunctionNumber(int firstSelectFunctionNumber) {
this.firstSelectFunctionNumber = firstSelectFunctionNumber;
}
/**
* @return the secondSelectFunctionNumber
*/
public int getSecondSelectFunctionNumber() {
return this.secondSelectFunctionNumber;
}
/**
* @param secondSelectFunctionNumber the secondSelectFunctionNumber to set
*/
public void setSecondSelectFunctionNumber(int secondSelectFunctionNumber) {
this.secondSelectFunctionNumber = secondSelectFunctionNumber;
}
/**
* @param logicController the controller to set
*/
public void setController(ILogicController logicController) {
this.controller = logicController;
}
/**
* @return the maxListeningDuration (ms)
*/
public long getMaxListeningDuration() {
return this.maxListeningDuration;
}
/**
* @param maxListeningDuration the maxListeningDuration to set (ms)
*/
public void setMaxListeningDuration(long maxListeningDuration) {
this.maxListeningDuration = maxListeningDuration;
}
@Override
public Statement[] createEsperStatements() {
Statement[] result = new Statement[this.selectFunctions.size()];
String firstInputEventName = "";
String secondInputEventName = "";
if (this.controller != null ) {
//get first input event name
firstInputEventName = this.controller.getNewEventName(this.firstPatternID, this.firstSelectFunctionNumber);
//get second input event name
secondInputEventName = this.controller.getNewEventName(this.secondPatternID, this.secondSelectFunctionNumber);
}
else if (this.parser != null) {
firstInputEventName = getNewEventNameWithParser(this.firstSelectFunctionNumber) ;
secondInputEventName = getNewEventNameWithParser(this.secondSelectFunctionNumber);
}
else {
//something went wrong
return null;
}
/*
* build statements
*/
String selectClause;
String fromClause;
String whereClause = "";
SelFunction sel;
Statement stat;
if (this.guard != null) { //TODO workaround
//build where clause (use maxListeningDuration and the Guard!)
String pName = null;
try {
//TODO: does only work with one select function...
HashMap<String, Object> params = this.selectFunctions.get(0).getFunctionParameters();
if (params.containsKey(Constants.SELECT_PARAM_PROPERTY_NAME)) {
pName = params.get(Constants.SELECT_PARAM_PROPERTY_NAME).toString();
}
}
catch (Throwable t) {
PatternComplex.logger.warn("could not load parameters from select function");
}
whereClause = this.guard.createStatement(true);
if (pName != null) pName = pName.trim();
//TODO: wtf? this worked and seems not be changed, but now the (overloaded?) method is gone?!
// whereClause = this.guard.createStatement(true, pName);
}
if (this.operator.equals(Constants.OPERATOR_CAUSE_NAME)
|| this.operator.equals(Constants.OPERATOR_BEFORE_NAME)) { //TODO operator.name ? evtl parallel statt before
/*
* CAUSE and PARALLEL have to work different (using guards with user functions)
*/
//build where clause
if (whereClause.equals("")) {
//no previous where clause, create one
whereClause = Constants.EPL_WHERE
+ " "
+ this.buildCausalCheckString(firstInputEventName, secondInputEventName);
}
else {
//append to existing where clause
whereClause += "and"
+ this.buildCausalCheckString(firstInputEventName, secondInputEventName);
}
//build from clause (always use and)
fromClause = Constants.EPL_FROM
+ " "
+ Constants.EPL_PATTERN
+ " [every ((("
+ firstInputEventName
+ "="
+ firstInputEventName
+ ") and ("
+ secondInputEventName
+ "="
+ secondInputEventName
+ ")) "
+ this.buildPatternTimerClause()
+ ")]";
}
else {
//build from clause
fromClause = this.buildFromClause(firstInputEventName, secondInputEventName);
}
for (int i = 0; i < result.length; i++) {
sel = this.selectFunctions.get(i);
//build select clause
selectClause = Constants.EPL_SELECT
+ " "
+ sel.getSelectString(false)
+ " ";
//build statement
stat = new Statement();
stat.setSelectFunction(sel);
stat.setStatement(selectClause + fromClause + whereClause);
stat.setView(this.view);
//add statement to result
result[i] = stat;
}
return result;
}
/**
* Method for creating statements using just an EMLParser instead
* of EsperController. (added by Matthes Rieke)
*/
private String getNewEventNameWithParser(int selectFunctionNumber) {
int selFuncNumber = selectFunctionNumber;
//get all patterns from parser
HashMap<String, APattern> patterns = this.parser.getPatterns();
//search for pattern
if (!this.parser.getPatterns().containsKey(this.patternID)) {
PatternComplex.logger.warn("pattern ID (" + this.patternID + ") not found");
return null;
}
APattern pattern = patterns.get(this.patternID);
//search for select function
if (!(pattern.getSelectFunctions().size() > selFuncNumber)) {
if (!(pattern.getSelectFunctions().size() >= 0)) {
PatternComplex.logger.warn("No select function and therefore no 'newEventName' defined in pattern '" + this.patternID
+ "'. Can not use this pattern in a repetitive pattern.");
return null;
}
PatternComplex.logger.warn("The pattern with the id '" + this.patternID + "does not define at least "
+ selFuncNumber + " selectfunctions. Using first select function instead.");
//set number to 0
selFuncNumber = 0;
}
//return newEventName
return pattern.getSelectFunctions().get(selFuncNumber).getNewEventName();
}
@Override
public Statement[] createEsperStatements(EMLParser emlParser) {
this.parser = emlParser;
return createEsperStatements();
}
/**
* build a part of the where clause for CAUSE and PARALLEL operators
*
* @param secondInputEventName name of the possible causal ancestor
* @param firstInputEventName name of the event with the causal vector
*
* @return the part of the where clause without 'where'
*/
private String buildCausalCheckString(String firstInputEventName, String secondInputEventName) {
String result = "";
//add operator
if (this.operator.equals(Constants.OPERATOR_CAUSE_NAME)) {
//CAUSE
result += MethodNames.IS_CAUSAL_ANCESTOR_NAME
+ "(";
}
else {
//PARALLEL
result += MethodNames.IS_NOT_CAUSAL_ANCESTOR_NAME
+ "(";
}
//add parameters
result += firstInputEventName
+ ", "
+ secondInputEventName
+ "."
+ MapEvent.CAUSALITY_KEY
+ ")";
return result;
}
/**
* builds a from clause
*
* @param firstInputEventName name of the first input event
* @param secondInputEventName name of the second input event
* @return
*/
private String buildFromClause(String firstInputEventName, String secondInputEventName) {
String clause = Constants.EPL_FROM
+ " "
+ Constants.EPL_PATTERN
+ " [every ((("
+ firstInputEventName
+ "="
+ firstInputEventName
+ ") "
+ this.getOperatorString()
+ " ("
+ secondInputEventName
+ "="
+ secondInputEventName
+ ")) "
+ this.buildPatternTimerClause()
+ ")]"
/*
* see esper-reference 2.3.0 chapter 5.2.2:
* "In addition, a data window view can be declared onto a pattern."
*/
+ this.view.getViewString()
+" ";
return clause;
}
/**
* build the timer:within clause
*
* @return the pattern guard (timer:within)
*/
private String buildPatternTimerClause() {
if(this.maxListeningDuration < 0) {
//no within clause
return "";
}
//build clause
String result = Constants.EPL_WHERE
+ " timer:within("
+ this.maxListeningDuration
+ " msec)";
return result;
}
/**
*
* @return the esper representation for the operator
*/
private String getOperatorString() {
String op = "";
String opName = this.operator.getName();
//AND
if (opName.equals(Constants.OPERATOR_AND_NAME)) {
op = "and";
}
//AND NOT
else if (opName.equals(Constants.OPERATOR_AND_NOT_NAME)) {
op = ") and not (";
/*
* The parentheses are necessary for correct execution.
* The 'every' operator must only be used for the first pattern.
*/
}
//OR
else if (opName.equals(Constants.OPERATOR_OR_NAME)) {
op = "or";
}
//BEFORE
else if (opName.equals(Constants.OPERATOR_BEFORE_NAME)) {
op = "->";
}
//CAUSE
else if (opName.equals(Constants.OPERATOR_CAUSE_NAME)) {
op = "and";
//functionality must be implemented as guard using 'in'
}
//PARALLEL
else if (opName.equals(Constants.OPERATOR_PARALLEL_NAME)) {
op = "and";
//functionality must be implemented as guard using 'in'
}
//user defined operators here
else {
PatternComplex.logger.warn("the operator '" + opName + "' is not supported");
}
return op;
}
}