@Override
 protected JSONObject execute(Map<String, Object> parameters, String data) {
   JSONObject result = new JSONObject();
   JSONObject errorMessage = new JSONObject();
   try {
     OBContext.setAdminMode(true);
     final JSONObject jsonData = new JSONObject(data);
     final String strBankStatementLineId = jsonData.getString("bankStatementLineId");
     String dateStr = jsonData.getString("updated");
     SimpleDateFormat xmlDateTimeFormat = JsonUtils.createJSTimeFormat();
     Date date = null;
     try {
       date = xmlDateTimeFormat.parse(dateStr);
     } catch (ParseException e) {
     }
     final FIN_BankStatementLine bsline =
         OBDal.getInstance().get(FIN_BankStatementLine.class, strBankStatementLineId);
     Date bbddBSLUpdated = bsline.getUpdated();
     // Remove milis
     Calendar calendar = Calendar.getInstance();
     calendar.setTime(OBDateUtils.convertDateToUTC(bbddBSLUpdated));
     calendar.setLenient(true);
     calendar.set(Calendar.MILLISECOND, 0);
     if (date.getTime() != calendar.getTimeInMillis()) {
       throw new OBStaleObjectException("@APRM_StaleDate@");
     }
     final FIN_FinaccTransaction transaction = bsline.getFinancialAccountTransaction();
     if (transaction != null) {
       APRM_MatchingUtility.unmatch(bsline);
     }
   } catch (Exception e) {
     OBDal.getInstance().rollbackAndClose();
     log.error("Error Unmatching Transaction", e);
     try {
       Throwable ex = DbUtility.getUnderlyingSQLException(e);
       String message = OBMessageUtils.translateError(ex.getMessage()).getMessage();
       errorMessage = new JSONObject();
       errorMessage.put("severity", "error");
       errorMessage.put("title", "Error");
       errorMessage.put("text", message);
       result.put("message", errorMessage);
     } catch (Exception e2) {
       log.error("Message could not be built", e2);
     }
   } finally {
     OBContext.restorePreviousMode();
   }
   return result;
 }
class ResultMapCriteriaUtils {
  private static final String CRITERIA_KEY = "criteria";
  private static final String VALUE_KEY = "value";
  private static final String FIELD_NAME_KEY = "fieldName";
  private static final String OPERATOR_KEY = "operator";

  private static final String OPERATOR_AND = "and";
  private static final String OPERATOR_OR = "or";

  private static final String OPERATOR_EQUALS = "equals";
  private static final String OPERATOR_NOTEQUAL = "notEqual";
  private static final String OPERATOR_IEQUALS = "iEquals";
  private static final String OPERATOR_INOTEQUAL = "iNotEqual";
  private static final String OPERATOR_GREATERTHAN = "greaterThan";
  private static final String OPERATOR_LESSTHAN = "lessThan";
  private static final String OPERATOR_GREATEROREQUAL = "greaterOrEqual";
  private static final String OPERATOR_LESSOREQUAL = "lessOrEqual";
  private static final String OPERATOR_IGREATERTHAN = "iGreaterThan";
  private static final String OPERATOR_ILESSTHAN = "iLessThan";
  private static final String OPERATOR_IGREATEROREQUAL = "iGreaterOrEqual";
  private static final String OPERATOR_ILESSOREQUAL = "iLessOrEqual";
  private static final String OPERATOR_CONTAINS = "contains";
  private static final String OPERATOR_STARTSWITH = "startsWith";
  private static final String OPERATOR_ENDSWITH = "endsWith";
  private static final String OPERATOR_ICONTAINS = "iContains";
  private static final String OPERATOR_ISTARTSWITH = "iStartsWith";
  private static final String OPERATOR_IENDSWITH = "iEndsWith";
  private static final String OPERATOR_NOTCONTAINS = "notContains";
  private static final String OPERATOR_NOTSTARTSWITH = "notStartsWith";
  private static final String OPERATOR_NOTENDSWITH = "notEndsWith";
  private static final String OPERATOR_INOTCONTAINS = "iNotContains";
  private static final String OPERATOR_INOTSTARTSWITH = "iNotStartsWith";
  private static final String OPERATOR_INOTENDSWITH = "iNotEndsWith";
  // private static final String OPERATOR_REGEXP = "regexp";
  // private static final String OPERATOR_IREGEXP = "iregexp";
  private static final String OPERATOR_ISNULL = "isNull";
  private static final String OPERATOR_NOTNULL = "notNull";
  private static final String OPERATOR_INSET = "inSet";
  private static final String OPERATOR_NOTINSET = "notInSet";
  private static final String OPERATOR_EQUALSFIELD = "equalsField";
  private static final String OPERATOR_NOTEQUALFIELD = "notEqualField";
  private static final String OPERATOR_GREATERTHANFIElD = "greaterThanField";
  private static final String OPERATOR_LESSTHANFIELD = "lessThanField";
  private static final String OPERATOR_GREATEROREQUALFIELD = "greaterOrEqualField";
  private static final String OPERATOR_LESSOREQUALFIElD = "lessOrEqualField";
  private static final String OPERATOR_CONTAINSFIELD = "containsField";
  private static final String OPERATOR_STARTSWITHFIELD = "startsWithField";
  private static final String OPERATOR_ENDSWITHFIELD = "endsWithField";
  private static final String OPERATOR_NOT = "not";
  private static final String OPERATOR_BETWEEN = "between";
  private static final String OPERATOR_BETWEENINCLUSIVE = "betweenInclusive";
  private static final String OPERATOR_IBETWEEN = "iBetween";
  private static final String OPERATOR_IBETWEENINCLUSIVE = "iBetweenInclusive";
  private static final String OPERATOR_EXISTS = "exists";

  Map<String, Object> recordMap;
  JSONArray criteriaArray;
  boolean mainOperatorIsAnd;

  private int UTCServerMinutesTimeZoneDiff = 0;
  private int clientUTCMinutesTimeZoneDiff = 0;

  private SimpleDateFormat simpleDateFormat = JsonUtils.createDateFormat();
  private SimpleDateFormat simpleDateTimeFormat = JsonUtils.createJSTimeFormat();

  ResultMapCriteriaUtils(Map<String, Object> orderMap, Map<String, String> parameters) {
    JSONArray _criteriaArray = null;
    try {
      _criteriaArray = (JSONArray) JsonUtils.buildCriteria(parameters).get("criteria");
    } catch (JSONException e) {
      _criteriaArray = null;
    }
    String mainOperator = parameters.get("operator");

    this.recordMap = orderMap;
    this.criteriaArray = _criteriaArray;
    this.mainOperatorIsAnd = OPERATOR_AND.equals(mainOperator);
  }

  boolean applyFilter() throws JSONException {
    if (criteriaArray == null) {
      return true;
    }
    boolean finalResult = mainOperatorIsAnd;
    for (int i = 0; i < criteriaArray.length(); i++) {
      // Each element of the criteria array is added assuming an OR statement.
      JSONObject criteria = criteriaArray.getJSONObject(i);
      boolean critResult = parseCriteria(criteria);
      if (mainOperatorIsAnd) {
        finalResult &= critResult;
      } else {
        finalResult |= critResult;
      }
    }
    return finalResult;
  }

  private boolean parseCriteria(JSONObject jsonCriteria) throws JSONException {
    // a constructor so the content is an advanced criteria
    if (jsonCriteria.has("_constructor") || hasOrAndOperator(jsonCriteria)) {
      return parseAdvancedCriteria(jsonCriteria);
    }
    return parseSingleClause(jsonCriteria);
  }

  private boolean hasOrAndOperator(JSONObject jsonCriteria) throws JSONException {
    if (!jsonCriteria.has(OPERATOR_KEY)) {
      return mainOperatorIsAnd;
    }
    return OPERATOR_OR.equals(jsonCriteria.get(OPERATOR_KEY))
        || OPERATOR_AND.equals(jsonCriteria.get(OPERATOR_KEY));
  }

  private boolean parseSingleClause(JSONObject jsonCriteria) throws JSONException {
    String operator = jsonCriteria.getString(OPERATOR_KEY);

    if (operator.equals(OPERATOR_BETWEEN)
        || operator.equals(OPERATOR_BETWEENINCLUSIVE)
        || operator.equals(OPERATOR_IBETWEEN)
        || operator.equals(OPERATOR_IBETWEENINCLUSIVE)) {
      return parseBetween(jsonCriteria, operator, true);
    }

    Object value = jsonCriteria.has(VALUE_KEY) ? jsonCriteria.get(VALUE_KEY) : null;

    if (operator.equals(OPERATOR_EXISTS)) {
      // not supported
      return mainOperatorIsAnd;
    }

    String fieldName = jsonCriteria.getString(FIELD_NAME_KEY);

    // translate to a OR for each value
    if (value instanceof JSONArray) {
      final JSONArray jsonArray = (JSONArray) value;
      final JSONObject advancedCriteria = new JSONObject();
      advancedCriteria.put(OPERATOR_KEY, OPERATOR_OR);
      final JSONArray subCriteria = new JSONArray();
      for (int i = 0; i < jsonArray.length(); i++) {
        final JSONObject subCriterion = new JSONObject();
        subCriterion.put(OPERATOR_KEY, operator);
        subCriterion.put(FIELD_NAME_KEY, fieldName);
        subCriterion.put(VALUE_KEY, jsonArray.get(i));
        subCriteria.put(i, subCriterion);
      }
      advancedCriteria.put(CRITERIA_KEY, subCriteria);
      return parseAdvancedCriteria(advancedCriteria);
    }

    // Retrieves the UTC time zone offset of the client
    if (jsonCriteria.has("minutesTimezoneOffset")) {
      int clientMinutesTimezoneOffset =
          Integer.parseInt(jsonCriteria.get("minutesTimezoneOffset").toString());
      Calendar now = Calendar.getInstance();
      // Obtains the UTC time zone offset of the server
      int serverMinutesTimezoneOffset =
          (now.get(Calendar.ZONE_OFFSET) + now.get(Calendar.DST_OFFSET)) / (1000 * 60);
      // Obtains the time zone offset between the server and the client
      clientUTCMinutesTimeZoneDiff = clientMinutesTimezoneOffset;
      UTCServerMinutesTimeZoneDiff = serverMinutesTimezoneOffset;
    }

    if (operator.equals(OPERATOR_ISNULL) || operator.equals(OPERATOR_NOTNULL)) {
      value = null;
    }

    // if a comparison is done on an equal date then replace
    // with a between start time and end time on that date
    if (operator.equals(OPERATOR_EQUALS) || operator.equals(OPERATOR_EQUALSFIELD)) {
      Object curValue = recordMap.get(fieldName);
      if (curValue instanceof Date) {
        if (operator.equals(OPERATOR_EQUALS)) {
          return parseSimpleClause(fieldName, OPERATOR_GREATEROREQUAL, value)
              && parseSimpleClause(fieldName, OPERATOR_LESSOREQUAL, value);

        } else {
          return parseSimpleClause(fieldName, OPERATOR_GREATEROREQUALFIELD, value)
              && parseSimpleClause(fieldName, OPERATOR_LESSOREQUALFIElD, value);
        }
      }
    }

    return parseSimpleClause(fieldName, operator, value);
  }

  private boolean parseBetween(JSONObject jsonCriteria, String operator, boolean inclusive)
      throws JSONException {
    final String fieldName = jsonCriteria.getString(FIELD_NAME_KEY);
    final Object start = jsonCriteria.get("start");
    final Object end = jsonCriteria.get("end");
    final boolean leftClause =
        parseSimpleClause(fieldName, getBetweenOperator(operator, false), start);
    final boolean rightClause =
        parseSimpleClause(fieldName, getBetweenOperator(operator, true), end);
    return leftClause && rightClause;
  }

  private boolean parseSimpleClause(String fieldName, String operator, Object value)
      throws JSONException {

    String hqlOperator = getHqlOperator(operator);
    String strField = fieldName;
    boolean filterIdentifier = false;
    // if (strField.endsWith("$_identifier")) {
    // strField = strField.substring(0, strField.length() - 12);
    // filterIdentifier = true;
    // }
    Object mapValue = recordMap.get(strField);
    if (operator.equals(OPERATOR_NOTNULL)) {
      return mapValue != null;
    } else if (operator.equals(OPERATOR_ISNULL)) {
      return mapValue == null;
    }

    Object localValue = value;
    if (ignoreCase(mapValue, operator)) {
      localValue = localValue.toString().toUpperCase();
    }

    boolean returnVal = mainOperatorIsAnd;

    if ("id".equals(strField)) {
      // ID is always equals
      returnVal = mapValue != JSONObject.NULL && ((String) localValue).equals(mapValue);
    } else if (filterIdentifier) {
      BaseOBObject currentVal = (BaseOBObject) mapValue;
      String strCurrValIdentifier = currentVal.getIdentifier();
      String strCurrentFilter = (String) localValue;
      returnVal = StringUtils.containsIgnoreCase(strCurrValIdentifier, strCurrentFilter);
    } else if (mapValue instanceof Boolean) {
      returnVal = (Boolean) mapValue == (Boolean) localValue;
    } else if (mapValue instanceof BigDecimal) {
      BigDecimal filterValue = new BigDecimal((Integer) localValue);
      int compare = filterValue.compareTo((BigDecimal) mapValue);
      if ("=".equals(hqlOperator)) {
        returnVal = compare == 0;
      } else if ("!=".equals(hqlOperator)) {
        returnVal = compare != 0;
      } else if (">".equals(hqlOperator)) {
        returnVal = compare > 0;
      } else if (">=".equals(hqlOperator)) {
        returnVal = compare >= 0;
      } else if ("<".equals(hqlOperator)) {
        returnVal = compare < 0;
      } else if ("<=".equals(hqlOperator)) {
        returnVal = compare <= 0;
      }

    } else if (mapValue instanceof Date) {
      try {
        Date filterValue;
        try {
          filterValue = simpleDateTimeFormat.parse(localValue.toString());
        } catch (ParseException e) {
          // When a DateTime column is filtered, plan Date values are used
          // See issue https://issues.openbravo.com/view.php?id=23203
          filterValue = simpleDateFormat.parse(localValue.toString());
        }
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(filterValue);
        // Applies the time zone offset difference of the client
        calendar.add(Calendar.MINUTE, clientUTCMinutesTimeZoneDiff);
        // move the date to the beginning of the day
        if (isGreaterOperator(operator)) {
          calendar.set(Calendar.HOUR, 0);
          calendar.set(Calendar.MINUTE, 0);
          calendar.set(Calendar.SECOND, 0);
          calendar.set(Calendar.MILLISECOND, 0);
        } else if (isLesserOperator(operator)) {
          // move the data to the end of the day
          calendar.set(Calendar.HOUR, 23);
          calendar.set(Calendar.MINUTE, 59);
          calendar.set(Calendar.SECOND, 59);
          calendar.set(Calendar.MILLISECOND, 999);
        }
        // Applies the time zone offset difference of the server
        calendar.add(Calendar.MINUTE, -UTCServerMinutesTimeZoneDiff);
        Date mapDate = (Date) mapValue;
        boolean isBefore = mapDate.before(calendar.getTime());
        boolean isAfter = mapDate.after(calendar.getTime());
        boolean sameDate = mapDate.compareTo(calendar.getTime()) == 0;
        if ("=".equals(hqlOperator)) {
          returnVal = sameDate;
        } else if ("!=".equals(hqlOperator)) {
          returnVal = !sameDate;
        } else if (">".equals(hqlOperator)) {
          returnVal = isAfter;
        } else if (">=".equals(hqlOperator)) {
          returnVal = isAfter || sameDate;
        } else if ("<".equals(hqlOperator)) {
          returnVal = isBefore;
        } else if ("<=".equals(hqlOperator)) {
          returnVal = isBefore || sameDate;
        }
      } catch (Exception e) {
        throw new IllegalArgumentException(e);
      }
    } else if (mapValue instanceof String) {
      String strCurrentValue = (String) mapValue;
      String strCurrentFilter = (String) localValue;
      returnVal = StringUtils.containsIgnoreCase(strCurrentValue, strCurrentFilter);
    }

    if (isNot(operator)) {
      return !returnVal;
    } else {
      return returnVal;
    }
  }

  private boolean isGreaterOperator(String operator) {
    return operator != null
        && (operator.equals(OPERATOR_GREATERTHAN)
            || operator.equals(OPERATOR_GREATEROREQUAL)
            || operator.equals(OPERATOR_IGREATERTHAN)
            || operator.equals(OPERATOR_IGREATEROREQUAL)
            || operator.equals(OPERATOR_GREATERTHANFIElD)
            || operator.equals(OPERATOR_GREATEROREQUALFIELD));
  }

  private boolean isLesserOperator(String operator) {
    return operator != null
        && (operator.equals(OPERATOR_LESSTHAN)
            || operator.equals(OPERATOR_LESSOREQUAL)
            || operator.equals(OPERATOR_ILESSTHAN)
            || operator.equals(OPERATOR_ILESSOREQUAL)
            || operator.equals(OPERATOR_LESSTHANFIELD)
            || operator.equals(OPERATOR_LESSOREQUALFIElD));
  }

  private boolean parseAdvancedCriteria(JSONObject advancedCriteria) throws JSONException {
    final String operator = advancedCriteria.getString(OPERATOR_KEY);
    if (operator.equals(OPERATOR_NOT)) {
      final boolean clause =
          parseStructuredClause(advancedCriteria.getJSONArray(CRITERIA_KEY), OPERATOR_OR);
      return !clause;
    }
    if (operator.equals(OPERATOR_AND)) {
      return parseStructuredClause(advancedCriteria.getJSONArray(CRITERIA_KEY), OPERATOR_AND);
    }
    if (operator.equals(OPERATOR_OR)) {
      final boolean value =
          parseStructuredClause(advancedCriteria.getJSONArray(CRITERIA_KEY), OPERATOR_OR);
      return value;
    }
    return parseSingleClause(advancedCriteria);
  }

  private boolean parseStructuredClause(JSONArray clauses, String hqlOperator)
      throws JSONException {
    boolean doOr = OPERATOR_OR.equals(hqlOperator);
    boolean doAnd = OPERATOR_AND.equals(hqlOperator);
    for (int i = 0; i < clauses.length(); i++) {
      final JSONObject clause = clauses.getJSONObject(i);
      if (clause.has(VALUE_KEY)
          && clause.get(VALUE_KEY) != null
          && clause.getString(VALUE_KEY).equals("")) {
        continue;
      }
      final boolean clauseResult = parseCriteria(clause);
      if (doOr && clauseResult) {
        return true;
      }
      if (doAnd && !clauseResult) {
        return false;
      }
    }
    if (doOr) {
      return false;
    } else if (doAnd) {
      return true;
    }
    return mainOperatorIsAnd;
  }

  private String getBetweenOperator(String operator, boolean rightClause) {
    if (operator.equals(OPERATOR_IBETWEEN)) {
      if (rightClause) {
        return OPERATOR_ILESSTHAN;
      } else {
        return OPERATOR_IGREATERTHAN;
      }
    }
    if (operator.equals(OPERATOR_BETWEEN)) {
      if (rightClause) {
        return OPERATOR_LESSTHAN;
      } else {
        return OPERATOR_GREATERTHAN;
      }
    }
    if (operator.equals(OPERATOR_IBETWEENINCLUSIVE)) {
      if (rightClause) {
        return OPERATOR_ILESSOREQUAL;
      } else {
        return OPERATOR_IGREATEROREQUAL;
      }
    }
    if (operator.equals(OPERATOR_BETWEENINCLUSIVE)) {
      if (rightClause) {
        return OPERATOR_LESSOREQUAL;
      } else {
        return OPERATOR_GREATEROREQUAL;
      }
    }
    throw new IllegalArgumentException("Operator not supported " + operator);
  }

  private boolean ignoreCase(Object mapValue, String operator) {
    if (mapValue instanceof BigDecimal || mapValue instanceof Date) {
      return false;
    }
    return operator.equals(OPERATOR_IEQUALS)
        || operator.equals(OPERATOR_INOTEQUAL)
        || operator.equals(OPERATOR_CONTAINS)
        || operator.equals(OPERATOR_ENDSWITH)
        || operator.equals(OPERATOR_STARTSWITH)
        || operator.equals(OPERATOR_ICONTAINS)
        || operator.equals(OPERATOR_INOTSTARTSWITH)
        || operator.equals(OPERATOR_INOTENDSWITH)
        || operator.equals(OPERATOR_NOTSTARTSWITH)
        || operator.equals(OPERATOR_NOTCONTAINS)
        || operator.equals(OPERATOR_INOTCONTAINS)
        || operator.equals(OPERATOR_NOTENDSWITH)
        || operator.equals(OPERATOR_IENDSWITH)
        || operator.equals(OPERATOR_ISTARTSWITH)
        || operator.equals(OPERATOR_IBETWEEN)
        || operator.equals(OPERATOR_IGREATEROREQUAL)
        || operator.equals(OPERATOR_ILESSOREQUAL)
        || operator.equals(OPERATOR_IGREATERTHAN)
        || operator.equals(OPERATOR_ILESSTHAN)
        || operator.equals(OPERATOR_IBETWEENINCLUSIVE);
  }

  private boolean isNot(String operator) {
    return operator.equals(OPERATOR_NOTCONTAINS)
        || operator.equals(OPERATOR_NOTENDSWITH)
        || operator.equals(OPERATOR_NOTSTARTSWITH)
        || operator.equals(OPERATOR_INOTCONTAINS)
        || operator.equals(OPERATOR_INOTENDSWITH)
        || operator.equals(OPERATOR_INOTSTARTSWITH)
        || operator.equals(OPERATOR_NOT)
        || operator.equals(OPERATOR_NOTINSET);
  }

  private String getHqlOperator(String operator) {
    if (operator.equals(OPERATOR_EQUALS)) {
      return "=";
    } else if (operator.equals(OPERATOR_INSET)) {
      return "in";
    } else if (operator.equals(OPERATOR_NOTINSET)) {
      return "in";
    } else if (operator.equals(OPERATOR_NOTEQUAL)) {
      return "!=";
    } else if (operator.equals(OPERATOR_IEQUALS)) {
      return "=";
    } else if (operator.equals(OPERATOR_INOTEQUAL)) {
      return "!=";
    } else if (operator.equals(OPERATOR_GREATERTHAN)) {
      return ">";
    } else if (operator.equals(OPERATOR_LESSTHAN)) {
      return "<";
    } else if (operator.equals(OPERATOR_GREATEROREQUAL)) {
      return ">=";
    } else if (operator.equals(OPERATOR_LESSOREQUAL)) {
      return "<=";
    } else if (operator.equals(OPERATOR_IGREATERTHAN)) {
      return ">";
    } else if (operator.equals(OPERATOR_ILESSTHAN)) {
      return "<";
    } else if (operator.equals(OPERATOR_IGREATEROREQUAL)) {
      return ">=";
    } else if (operator.equals(OPERATOR_ILESSOREQUAL)) {
      return "<=";
    } else if (operator.equals(OPERATOR_CONTAINS)) {
      return "like";
    } else if (operator.equals(OPERATOR_STARTSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_ENDSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_ICONTAINS)) {
      return "like";
    } else if (operator.equals(OPERATOR_ISTARTSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_IENDSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_NOTCONTAINS)) {
      return "like";
    } else if (operator.equals(OPERATOR_NOTSTARTSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_NOTENDSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_INOTCONTAINS)) {
      return "like";
    } else if (operator.equals(OPERATOR_INOTSTARTSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_INOTENDSWITH)) {
      return "like";
    } else if (operator.equals(OPERATOR_EQUALSFIELD)) {
      return "=";
    } else if (operator.equals(OPERATOR_NOTEQUALFIELD)) {
      return "!=";
    } else if (operator.equals(OPERATOR_GREATERTHANFIElD)) {
      return ">";
    } else if (operator.equals(OPERATOR_LESSTHANFIELD)) {
      return "<";
    } else if (operator.equals(OPERATOR_GREATEROREQUALFIELD)) {
      return ">=";
    } else if (operator.equals(OPERATOR_LESSOREQUALFIElD)) {
      return "<=";
    } else if (operator.equals(OPERATOR_CONTAINSFIELD)) {
      return "like";
    } else if (operator.equals(OPERATOR_STARTSWITHFIELD)) {
      return "like";
    } else if (operator.equals(OPERATOR_ENDSWITHFIELD)) {
      return "like";
    } else if (operator.equals(OPERATOR_ISNULL)) {
      return "is";
    } else if (operator.equals(OPERATOR_NOTNULL)) {
      return "is not";
    } else if (operator.equals(OPERATOR_EXISTS)) {
      return "exists";
    }
    // todo throw exception
    return null;
  }
}