private Criterion parseExpression(Expression expression) {

    ExpressionTypes type = expression.getType();
    String propertyName = expression.getTargetPropertyName();
    Object value = expression.getValue();
    Class<?> clazz;

    if (log.isTraceEnabled()) {
      log.trace(
          String.format(
              "Parsing expression of type '%s' for propety '%s', value=%s",
              type.name(), propertyName, value));
    }

    switch (type) {
      case EQUAL:
        return org.hibernate.criterion.Restrictions.eq(propertyName, value);
      case GREATER:
        return org.hibernate.criterion.Restrictions.gt(propertyName, value);
      case GREATER_OR_EQUAL:
        return org.hibernate.criterion.Restrictions.ge(propertyName, value);
      case LESS:
        return org.hibernate.criterion.Restrictions.lt(propertyName, value);
      case LESS_OR_EQUAL:
        return org.hibernate.criterion.Restrictions.le(propertyName, value);
      case LIKE:
        String sval = (String) value;
        if (sval.contains("%") || sval.contains("_")) {
          return org.hibernate.criterion.Restrictions.ilike(propertyName, sval);
        } else {
          return org.hibernate.criterion.Restrictions.ilike(propertyName, sval, MatchMode.ANYWHERE);
        }
      case LIKE_END:
        return org.hibernate.criterion.Restrictions.ilike(
            propertyName, (String) value, MatchMode.END);
      case LIKE_START:
        return org.hibernate.criterion.Restrictions.ilike(
            propertyName, (String) value, MatchMode.START);
      case LIKE_EXACT:
        return org.hibernate.criterion.Restrictions.ilike(
            propertyName, (String) value, MatchMode.EXACT);
      case BETWEEN:
        return org.hibernate.criterion.Restrictions.between(
            propertyName, ((Object[]) value)[0], ((Object[]) value)[1]);
      case IN:
        clazz = value.getClass();
        if (clazz.isArray()) {
          return org.hibernate.criterion.Restrictions.in(propertyName, (Object[]) value);
        } else if (Collection.class.isAssignableFrom(clazz)) {
          return org.hibernate.criterion.Restrictions.in(propertyName, (Collection<?>) value);
        } else {
          throw new IllegalStateException(
              "Object for 'IN' criteria must be array or collection, but it is "
                  + (clazz == null ? "(null)" : "'" + clazz.getName() + "'"));
        }
      case IS_NULL:
        return org.hibernate.criterion.Restrictions.isNull(propertyName);
      case IS_NOT_NULL:
        return org.hibernate.criterion.Restrictions.isNotNull(propertyName);
      case NOT_EQUAL:
        return org.hibernate.criterion.Restrictions.ne(propertyName, value);
      case EXISTS:
        return Subqueries.exists(subquery((SubqueryExpression) expression));
      case NOT_EXISTS:
        return Subqueries.notExists(subquery((SubqueryExpression) expression));
      case EQUAL_PROPERTY:
        if (!(value instanceof CharSequence)) {
          throw new IllegalStateException(
              "Value for 'EQUAL_PROPERTY' criteria must be CharSequence, but it is "
                  + (value == null ? "(null)" : "'" + value.getClass().getName() + "'"));
        }
        return org.hibernate.criterion.Restrictions.eqProperty(propertyName, value.toString());
      case GREATER_PROPERTY:
        return org.hibernate.criterion.Restrictions.gtProperty(propertyName, value.toString());
      case LESS_PROPERTY:
        return org.hibernate.criterion.Restrictions.ltProperty(propertyName, value.toString());
      case GREATER_EQUAL_PROPERTY:
        return org.hibernate.criterion.Restrictions.geProperty(propertyName, value.toString());
      case LESS_EQUAL_PROPERTY:
        return org.hibernate.criterion.Restrictions.leProperty(propertyName, value.toString());
      case SQL_RESTICTION:
        return org.hibernate.criterion.Restrictions.sqlRestriction(value.toString());
      default:
        throw new IllegalStateException(
            String.format("Unknown query expression '%s'", type.name()));
    }
  }