[gnumeric] Conditional formats: handle ends-with for xls.



commit 2c5837b7446061d89840d30bf3e40b8cf7ebb0b9
Author: Morten Welinder <terra gnome org>
Date:   Thu Mar 13 09:43:59 2014 -0400

    Conditional formats: handle ends-with for xls.

 samples/cond-format-tests.gnumeric |  Bin 2355 -> 2443 bytes
 src/expr.c                         |    2 +-
 src/expr.h                         |    1 +
 src/style-conditions.c             |  115 +++++++++++++++++++++++++++++++-----
 4 files changed, 101 insertions(+), 17 deletions(-)
---
diff --git a/samples/cond-format-tests.gnumeric b/samples/cond-format-tests.gnumeric
index 7308171..ec9de92 100644
Binary files a/samples/cond-format-tests.gnumeric and b/samples/cond-format-tests.gnumeric differ
diff --git a/src/expr.c b/src/expr.c
index 0e9bef8..f2283ec 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -566,7 +566,7 @@ gnm_expr_array_corner_get_type (void)
  *   Named expressions must refer the same name, having equivalent names is
  *   insufficeient.
  */
-static gboolean
+gboolean
 gnm_expr_equal (GnmExpr const *a, GnmExpr const *b)
 {
        if (a == b)
diff --git a/src/expr.h b/src/expr.h
index 9846298..3ce615f 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -87,6 +87,7 @@ GnmFunc       *gnm_expr_get_func_def (GnmExpr const *expr);
 
 void     gnm_expr_free              (GnmExpr const *expr);
 GnmExpr const *gnm_expr_copy         (GnmExpr const *expr);
+gboolean  gnm_expr_equal (GnmExpr const *a, GnmExpr const *b);
 gboolean  gnm_expr_is_rangeref      (GnmExpr const *expr);
 gboolean  gnm_expr_is_data_table     (GnmExpr const *expr,
                                      GnmCellPos *c_in, GnmCellPos *r_in);
diff --git a/src/style-conditions.c b/src/style-conditions.c
index cfb5236..0e69ee6 100644
--- a/src/style-conditions.c
+++ b/src/style-conditions.c
@@ -242,6 +242,37 @@ gnm_style_cond_set_overlay (GnmStyleCond *cond, GnmStyle *overlay)
        cond->overlay = overlay;
 }
 
+static GnmExpr const *
+generate_end_match (const char *endfunc, gboolean force,
+                   GnmExprTop const *sexpr, GnmCellRef *cr)
+{
+       GnmValue const *v = gnm_expr_get_constant (sexpr->expr);
+       GnmExpr const *len_expr;
+
+       if (v && VALUE_IS_STRING (v)) {
+               int len = g_utf8_strlen (value_peek_string (v), -1);
+               len_expr = gnm_expr_new_constant (value_new_int (len));
+       } else if (force) {
+               /*
+                * This is imperfect because the expression gets
+                * evaluated twice.
+                */
+               len_expr = gnm_expr_new_funcall1
+                       (gnm_func_lookup_or_add_placeholder ("LEN"),
+                        gnm_expr_copy (sexpr->expr));
+       } else
+               return NULL;
+
+       return gnm_expr_new_funcall2
+               (gnm_func_lookup_or_add_placeholder ("EXACT"),
+                gnm_expr_new_funcall2
+                (gnm_func_lookup_or_add_placeholder (endfunc),
+                 gnm_expr_new_cellref (cr),
+                 len_expr),
+                gnm_expr_copy (sexpr->expr));
+}
+
+
 /**
  * gnm_style_cond_get_alternate_expr:
  * @cond: condition
@@ -255,11 +286,18 @@ gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
        GnmCellRef self;
        GnmExpr const *expr;
        gboolean negate = FALSE;
+       GnmExprTop const *sexpr = NULL;
 
        g_return_val_if_fail (cond != NULL, NULL);
 
        gnm_cellref_init (&self, NULL, 0, 0, TRUE);
 
+       if (gnm_style_cond_op_operands (cond->op) > 0) {
+               sexpr = gnm_style_cond_get_expr (cond, 0);
+               if (!sexpr)
+                       return NULL;
+       }
+
        switch (cond->op) {
        case GNM_STYLE_COND_NOT_CONTAINS_ERR:
                negate = TRUE; /* ...and fall through */
@@ -271,10 +309,7 @@ gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
 
        case GNM_STYLE_COND_CONTAINS_STR:
                negate = TRUE; /* ...and fall through */
-       case GNM_STYLE_COND_NOT_CONTAINS_STR: {
-               GnmExprTop const *sexpr = gnm_style_cond_get_expr (cond, 0);
-               if (!sexpr)
-                       return NULL;
+       case GNM_STYLE_COND_NOT_CONTAINS_STR:
                expr = gnm_expr_new_funcall1
                        (gnm_func_lookup_or_add_placeholder ("ISERROR"),
                         gnm_expr_new_funcall2
@@ -282,7 +317,6 @@ gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
                          gnm_expr_copy (sexpr->expr),
                          gnm_expr_new_cellref (&self)));
                break;
-       }
 
        case GNM_STYLE_COND_CONTAINS_BLANKS:
                negate = TRUE; /* ...and fall through */
@@ -297,17 +331,17 @@ gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
 
        case GNM_STYLE_COND_NOT_BEGINS_WITH_STR:
                negate = TRUE; /* ...and fall through */
-       case GNM_STYLE_COND_BEGINS_WITH_STR: {
-               GnmExprTop const *sexpr = gnm_style_cond_get_expr (cond, 0);
-               if (!sexpr)
-                       return NULL;
+       case GNM_STYLE_COND_BEGINS_WITH_STR:
+               expr = generate_end_match ("LEFT", FALSE, sexpr, &self);
+               if (expr)
+                       break;
 
                /*
-                * We can do much better if sexpr is a constant.  We are
+                * We get here if sexpr is not a constant.  We are
                 * constrained by using only Excel functions and not
-                * evaluating the needle more than once.  The expression
-                * used here works, but FIND is more expensive that it
-                * ought to be.
+                * evaluating the needle more than once.  The
+                * expression used here works, but FIND is more
+                * expensive that it ought to be.
                 */
                expr = gnm_expr_new_binary
                        (gnm_expr_new_funcall2
@@ -320,11 +354,17 @@ gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
                         GNM_EXPR_OP_EQUAL,
                         gnm_expr_new_constant (value_new_int (1)));
                break;
-       }
 
-       case GNM_STYLE_COND_ENDS_WITH_STR:
        case GNM_STYLE_COND_NOT_ENDS_WITH_STR:
-               /* we ought to do the above */
+               negate = TRUE; /* ...and fall through */
+       case GNM_STYLE_COND_ENDS_WITH_STR:
+               /*
+                * We are constrained by using only Excel functions and not
+                * evaluating the needle more than once.  We cannot fulfill
+                * that and end up computing the needle twice.
+                */
+               expr = generate_end_match ("RIGHT", TRUE, sexpr, &self);
+               break;
 
        default:
                return NULL;
@@ -351,6 +391,41 @@ isself (GnmExpr const *expr)
                cr->col_relative && cr->row_relative);
 }
 
+static GnmExprTop const *
+decode_end_match (const char *endfunc, GnmExpr const *expr)
+{
+       GnmExpr const *needle;
+       GnmExpr const *expr2;
+
+       if (GNM_EXPR_GET_OPER (expr) == GNM_EXPR_OP_FUNCALL &&
+           expr->func.argc == 2 &&
+           expr->func.func == gnm_func_lookup_or_add_placeholder ("EXACT") &&
+           (needle = expr->func.argv[1]) &&
+           (expr2 = expr->func.argv[0]) &&
+           GNM_EXPR_GET_OPER (expr2) == GNM_EXPR_OP_FUNCALL &&
+           expr2->func.argc == 2 &&
+           expr2->func.func == gnm_func_lookup_or_add_placeholder (endfunc) &&
+           isself (expr2->func.argv[0])) {
+               GnmExpr const *len_expr = expr2->func.argv[1];
+               GnmValue const *v, *vl;
+
+               if (GNM_EXPR_GET_OPER (len_expr) == GNM_EXPR_OP_FUNCALL &&
+                   len_expr->func.argc == 1 &&
+                   len_expr->func.func == gnm_func_lookup_or_add_placeholder ("LEN") &&
+                   gnm_expr_equal (len_expr->func.argv[0], needle))
+                       return gnm_expr_top_new (gnm_expr_copy (needle));
+
+               if ((v = gnm_expr_get_constant (needle)) &&
+                   VALUE_IS_STRING (v) &&
+                   (vl = gnm_expr_get_constant (len_expr)) &&
+                   VALUE_IS_NUMBER (vl) &&
+                   value_get_as_float (vl) == g_utf8_strlen (value_peek_string (v), -1))
+                       return gnm_expr_top_new (gnm_expr_copy (needle));
+       }
+
+       return NULL;
+}
+
 /**
  * gnm_style_cond_canonicalize:
  * @cond: condition
@@ -435,6 +510,14 @@ gnm_style_cond_canonicalize (GnmStyleCond *cond)
                newop = negate
                        ? GNM_STYLE_COND_NOT_BEGINS_WITH_STR
                        : GNM_STYLE_COND_BEGINS_WITH_STR;
+       } else if ((texpr = decode_end_match ("LEFT", expr))) {
+               newop = negate
+                       ? GNM_STYLE_COND_NOT_BEGINS_WITH_STR
+                       : GNM_STYLE_COND_BEGINS_WITH_STR;
+       } else if ((texpr = decode_end_match ("RIGHT", expr))) {
+               newop = negate
+                       ? GNM_STYLE_COND_NOT_ENDS_WITH_STR
+                       : GNM_STYLE_COND_ENDS_WITH_STR;
        }
 
        if (newop != GNM_STYLE_COND_CUSTOM) {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]