3 typedef enum { CRIT_NULL
, CRIT_FLOAT
, CRIT_WRONGTYPE
, CRIT_STRING
} CritType
;
6 criteria_inspect_values (GnmValue
const *x
, gnm_float
*xr
, gnm_float
*yr
,
7 GnmCriteria
*crit
, gboolean coerce_to_float
)
9 GnmValue
const *y
= crit
->x
;
11 if (x
== NULL
|| y
== NULL
)
14 switch (y
->v_any
.type
) {
16 /* If we're searching for a bool -- even one that is
17 from a string search value -- we match only bools. */
18 if (!VALUE_IS_BOOLEAN (x
))
19 return CRIT_WRONGTYPE
;
20 *xr
= value_get_as_float (x
);
21 *yr
= value_get_as_float (y
);
25 return CRIT_WRONGTYPE
;
28 if (!VALUE_IS_STRING (x
))
29 return CRIT_WRONGTYPE
;
33 g_warning ("This should not happen. Please report.");
34 return CRIT_WRONGTYPE
;
38 *yr
= value_get_as_float (y
);
40 if (VALUE_IS_BOOLEAN (x
) || VALUE_IS_ERROR (x
))
41 return CRIT_WRONGTYPE
;
42 else if (VALUE_IS_FLOAT (x
)) {
43 *xr
= value_get_as_float (x
);
48 return CRIT_WRONGTYPE
;
50 vx
= format_match (value_peek_string (x
), NULL
, crit
->date_conv
);
51 if (VALUE_IS_EMPTY (vx
) ||
52 VALUE_IS_BOOLEAN (y
) != VALUE_IS_BOOLEAN (vx
)) {
54 return CRIT_WRONGTYPE
;
57 *xr
= value_get_as_float (vx
);
66 criteria_test_equal (GnmValue
const *x
, GnmCriteria
*crit
)
69 GnmValue
const *y
= crit
->x
;
71 switch (criteria_inspect_values (x
, &xf
, &yf
, crit
, TRUE
)) {
73 g_assert_not_reached ();
80 /* FIXME: _ascii_??? */
81 return g_ascii_strcasecmp (value_peek_string (x
),
82 value_peek_string (y
)) == 0;
87 criteria_test_unequal (GnmValue
const *x
, GnmCriteria
*crit
)
91 switch (criteria_inspect_values (x
, &xf
, &yf
, crit
, FALSE
)) {
93 g_assert_not_reached ();
100 /* FIXME: _ascii_??? */
101 return g_ascii_strcasecmp (value_peek_string (x
),
102 value_peek_string (crit
->x
)) != 0;
107 criteria_test_less (GnmValue
const *x
, GnmCriteria
*crit
)
110 GnmValue
const *y
= crit
->x
;
112 switch (criteria_inspect_values (x
, &xf
, &yf
, crit
, FALSE
)) {
114 g_assert_not_reached ();
119 return go_utf8_collate_casefold (value_peek_string (x
),
120 value_peek_string (y
)) < 0;
127 criteria_test_greater (GnmValue
const *x
, GnmCriteria
*crit
)
130 GnmValue
const *y
= crit
->x
;
132 switch (criteria_inspect_values (x
, &xf
, &yf
, crit
, FALSE
)) {
134 g_assert_not_reached ();
139 return go_utf8_collate_casefold (value_peek_string (x
),
140 value_peek_string (y
)) > 0;
147 criteria_test_less_or_equal (GnmValue
const *x
, GnmCriteria
*crit
)
150 GnmValue
const *y
= crit
->x
;
152 switch (criteria_inspect_values (x
, &xf
, &yf
, crit
, FALSE
)) {
154 g_assert_not_reached ();
159 return go_utf8_collate_casefold (value_peek_string (x
),
160 value_peek_string (y
)) <= 0;
167 criteria_test_greater_or_equal (GnmValue
const *x
, GnmCriteria
*crit
)
170 GnmValue
const *y
= crit
->x
;
172 switch (criteria_inspect_values (x
, &xf
, &yf
, crit
, FALSE
)) {
174 g_assert_not_reached ();
179 return go_utf8_collate_casefold (value_peek_string (x
),
180 value_peek_string (y
)) >= 0;
187 criteria_test_match (GnmValue
const *x
, GnmCriteria
*crit
)
192 // Only strings are matched
193 if (!VALUE_IS_STRING (x
))
196 return go_regexec (&crit
->rx
, value_peek_string (x
), 0, NULL
, 0) ==
201 criteria_test_empty (GnmValue
const *x
, GnmCriteria
*crit
)
203 return VALUE_IS_EMPTY (x
);
207 criteria_test_nonempty (GnmValue
const *x
, GnmCriteria
*crit
)
209 return !VALUE_IS_EMPTY (x
);
213 * Finds a column index of a field.
216 find_column_of_field (GnmEvalPos
const *ep
,
217 GnmValue
const *database
, GnmValue
const *field
)
222 int begin_col
, end_col
, row
, n
, column
;
225 // I'm not certain we should demand this, but the code clearly wants
227 if (!VALUE_IS_CELLRANGE (database
))
230 offset
= database
->v_range
.cell
.a
.col
;
232 if (VALUE_IS_FLOAT (field
))
233 return value_get_as_int (field
) + offset
- 1;
235 if (!VALUE_IS_STRING (field
))
238 sheet
= eval_sheet (database
->v_range
.cell
.a
.sheet
, ep
->sheet
);
239 field_name
= value_get_as_string (field
);
242 /* find the column that is labeled after `field_name' */
243 begin_col
= database
->v_range
.cell
.a
.col
;
244 end_col
= database
->v_range
.cell
.b
.col
;
245 row
= database
->v_range
.cell
.a
.row
;
247 for (n
= begin_col
; n
<= end_col
; n
++) {
251 cell
= sheet_cell_get (sheet
, n
, row
);
254 gnm_cell_eval (cell
);
257 ? value_peek_string (cell
->value
)
259 match
= (g_ascii_strcasecmp (field_name
, txt
) == 0);
271 gnm_criteria_unref (GnmCriteria
*criteria
)
273 if (!criteria
|| criteria
->ref_count
-- > 1)
275 value_release (criteria
->x
);
276 if (criteria
->has_rx
)
277 go_regfree (&criteria
->rx
);
282 gnm_criteria_ref (GnmCriteria
*criteria
)
284 criteria
->ref_count
++;
289 gnm_criteria_get_type (void)
294 t
= g_boxed_type_register_static ("GnmCriteria",
295 (GBoxedCopyFunc
)gnm_criteria_ref
,
296 (GBoxedFreeFunc
)gnm_criteria_unref
);
303 * @criterias: (element-type GnmCriteria) (transfer full): the criteria to be
305 * Frees the allocated memory.
308 free_criterias (GSList
*criterias
)
310 GSList
*list
= criterias
;
312 while (criterias
!= NULL
) {
313 GnmDBCriteria
*criteria
= criterias
->data
;
314 g_slist_free_full (criteria
->conditions
,
315 (GFreeFunc
)gnm_criteria_unref
);
317 criterias
= criterias
->next
;
324 * @crit_val: #GnmValue
325 * @date_conv: #GODateConventions
327 * Returns: (transfer full): GnmCriteria which caller must free.
338 parse_criteria (GnmValue
const *crit_val
, GODateConventions
const *date_conv
,
342 char const *criteria
;
343 GnmCriteria
*res
= g_new0 (GnmCriteria
, 1);
346 res
->iter_flags
= CELL_ITER_IGNORE_BLANK
;
347 res
->date_conv
= date_conv
;
349 if (VALUE_IS_NUMBER (crit_val
)) {
350 res
->fun
= criteria_test_equal
;
351 res
->x
= value_dup (crit_val
);
355 criteria
= value_peek_string (crit_val
);
356 if (strncmp (criteria
, "<=", 2) == 0) {
357 res
->fun
= criteria_test_less_or_equal
;
359 } else if (strncmp (criteria
, ">=", 2) == 0) {
360 res
->fun
= criteria_test_greater_or_equal
;
362 } else if (strncmp (criteria
, "<>", 2) == 0) {
363 /* "<>" by itself is special: */
364 res
->fun
= (criteria
[2] == 0) ? criteria_test_nonempty
: criteria_test_unequal
;
366 } else if (*criteria
== '<') {
367 res
->fun
= criteria_test_less
;
369 } else if (*criteria
== '=') {
370 /* "=" by itself is special: */
371 res
->fun
= (criteria
[1] == 0) ? criteria_test_empty
: criteria_test_equal
;
373 } else if (*criteria
== '>') {
374 res
->fun
= criteria_test_greater
;
377 res
->fun
= criteria_test_match
;
378 res
->has_rx
= (gnm_regcomp_XL (&res
->rx
, criteria
, GO_REG_ICASE
, TRUE
, anchor_end
) == GO_REG_OK
);
382 res
->x
= format_match_number (criteria
+ len
, NULL
, date_conv
);
384 res
->x
= value_new_string (criteria
+ len
);
385 else if (len
== 0 && VALUE_IS_NUMBER (res
->x
))
386 res
->fun
= criteria_test_equal
;
388 empty
= value_new_empty ();
389 if (res
->fun (empty
, res
))
390 res
->iter_flags
&= ~CELL_ITER_IGNORE_BLANK
;
391 value_release (empty
);
399 parse_criteria_range (Sheet
*sheet
, int b_col
, int b_row
, int e_col
, int e_row
,
400 int *field_ind
, gboolean anchor_end
)
402 GSList
*criterias
= NULL
;
403 GODateConventions
const *date_conv
= sheet_date_conv (sheet
);
406 for (i
= b_row
; i
<= e_row
; i
++) {
407 GnmDBCriteria
*new_criteria
= g_new (GnmDBCriteria
, 1);
408 GSList
*conditions
= NULL
;
410 for (j
= b_col
; j
<= e_col
; j
++) {
412 GnmCell
*cell
= sheet_cell_get (sheet
, j
, i
);
414 gnm_cell_eval (cell
);
415 if (gnm_cell_is_empty (cell
))
418 cond
= parse_criteria (cell
->value
, date_conv
,
420 cond
->column
= (field_ind
!= NULL
)
421 ? field_ind
[j
- b_col
]
423 conditions
= g_slist_prepend (conditions
, cond
);
426 new_criteria
->conditions
= g_slist_reverse (conditions
);
427 criterias
= g_slist_prepend (criterias
, new_criteria
);
430 return g_slist_reverse (criterias
);
434 * parse_database_criteria:
436 * @database: #GnmValue
437 * @criteria: #GnmValue
439 * Parses the criteria cell range.
440 * Returns: (element-type GnmDBCriteria) (transfer full):
443 parse_database_criteria (GnmEvalPos
const *ep
, GnmValue
const *database
, GnmValue
const *criteria
)
448 int b_col
, b_row
, e_col
, e_row
;
452 g_return_val_if_fail (VALUE_IS_CELLRANGE (criteria
), NULL
);
454 sheet
= eval_sheet (criteria
->v_range
.cell
.a
.sheet
, ep
->sheet
);
455 b_col
= criteria
->v_range
.cell
.a
.col
;
456 b_row
= criteria
->v_range
.cell
.a
.row
;
457 e_col
= criteria
->v_range
.cell
.b
.col
;
458 e_row
= criteria
->v_range
.cell
.b
.row
;
466 /* Find the index numbers for the columns of criterias */
467 field_ind
= g_new (int, e_col
- b_col
+ 1);
468 for (i
= b_col
; i
<= e_col
; i
++) {
469 cell
= sheet_cell_get (sheet
, i
, b_row
);
472 gnm_cell_eval (cell
);
473 if (gnm_cell_is_empty (cell
))
475 field_ind
[i
- b_col
] =
476 find_column_of_field (ep
, database
, cell
->value
);
477 if (field_ind
[i
- b_col
] == -1) {
483 res
= parse_criteria_range (sheet
, b_col
, b_row
+ 1,
484 e_col
, e_row
, field_ind
,
491 * find_rows_that_match:
493 * @first_col: first column.
494 * @first_row: first row.
495 * @last_col: last column.
496 * @last_row: last row.
497 * @criterias: (element-type GnmDBCriteria): the criteria to use.
500 * Finds the rows from the given database that match the criteria.
501 * Returns: (element-type int) (transfer full): the list of matching rows.
504 find_rows_that_match (Sheet
*sheet
, int first_col
, int first_row
,
505 int last_col
, int last_row
,
506 GSList
*criterias
, gboolean unique_only
)
509 GSList
const *crit_ptr
, *cond_ptr
;
514 GnmValue
const *empty
= value_new_empty ();
516 for (row
= first_row
; row
<= last_row
; row
++) {
518 for (crit_ptr
= criterias
; crit_ptr
; crit_ptr
= crit_ptr
->next
) {
519 GnmDBCriteria
const *crit
= crit_ptr
->data
;
521 for (cond_ptr
= crit
->conditions
;
522 cond_ptr
!= NULL
; cond_ptr
= cond_ptr
->next
) {
523 GnmCriteria
*cond
= cond_ptr
->data
;
524 test_cell
= sheet_cell_get (sheet
, cond
->column
, row
);
525 if (test_cell
!= NULL
)
526 gnm_cell_eval (test_cell
);
527 if (!cond
->fun (test_cell
? test_cell
->value
: empty
, cond
)) {
542 for (c
= rows
; c
!= NULL
; c
= c
->next
) {
543 int trow
= GPOINTER_TO_INT (c
->data
);
544 for (i
= first_col
; i
<= last_col
; i
++) {
545 test_cell
= sheet_cell_get (sheet
, i
, trow
);
546 cell
= sheet_cell_get (sheet
, i
, row
);
548 /* FIXME: this is probably not right, but crashing is more wrong. */
549 if (test_cell
== NULL
|| cell
== NULL
)
553 ? value_peek_string (cell
->value
)
555 t2
= test_cell
->value
556 ? value_peek_string (test_cell
->value
)
558 if (strcmp (t1
, t2
) != 0)
566 rows
= g_slist_prepend (rows
, GINT_TO_POINTER (row
));
572 return g_slist_reverse (rows
);
575 /****************************************************************************/
579 * @data: (element-type GnmValue):
580 * @crits: (element-type GnmCriteria): criteria
582 * @fun: (scope call): function to evaluate on filtered data
583 * @err: error value in case @fun fails.
584 * @ep: evaluation position
585 * @flags: #CollectFlags flags describing the collection and interpretation
586 * of values from @data.
588 * This implements a Gnumeric sheet database function of the "*IFS" type
589 * This function collects the arguments and uses @fun to do
590 * the actual computation.
592 * Returns: (transfer full): Function result or error value.
595 gnm_ifs_func (GPtrArray
*data
, GPtrArray
*crits
, GnmValue
const *vals
,
596 float_range_function_t fun
, GnmStdError err
,
597 GnmEvalPos
const *ep
, CollectFlags flags
)
600 unsigned ui
, N
= 0, nalloc
= 0;
601 gnm_float
*xs
= NULL
;
602 GnmValue
*res
= NULL
;
605 g_return_val_if_fail (data
->len
== crits
->len
, NULL
);
607 if (flags
& ~(COLLECT_IGNORE_STRINGS
|
608 COLLECT_IGNORE_BOOLS
|
609 COLLECT_IGNORE_BLANKS
|
610 COLLECT_IGNORE_ERRORS
)) {
611 g_warning ("unsupported flags in gnm_ifs_func %x", flags
);
614 sx
= value_area_get_width (vals
, ep
);
615 sy
= value_area_get_height (vals
, ep
);
616 for (ui
= 0; ui
< data
->len
; ui
++) {
617 GnmValue
const *datai
= g_ptr_array_index (data
, ui
);
618 if (value_area_get_width (datai
, ep
) != sx
||
619 value_area_get_height (datai
, ep
) != sy
)
620 return value_new_error_VALUE (ep
);
623 for (y
= 0; y
< sy
; y
++) {
624 for (x
= 0; x
< sx
; x
++) {
626 gboolean match
= TRUE
;
628 for (ui
= 0; match
&& ui
< crits
->len
; ui
++) {
629 GnmCriteria
*crit
= g_ptr_array_index (crits
, ui
);
630 GnmValue
const *datai
= g_ptr_array_index (data
, ui
);
631 v
= value_area_get_x_y (datai
, x
, y
, ep
);
633 match
= crit
->fun (v
, crit
);
638 // Match. Maybe collect the data point.
640 v
= value_area_get_x_y (vals
, x
, y
, ep
);
641 if ((flags
& COLLECT_IGNORE_STRINGS
) && VALUE_IS_STRING (v
))
643 if ((flags
& COLLECT_IGNORE_BOOLS
) && VALUE_IS_BOOLEAN (v
))
645 if ((flags
& COLLECT_IGNORE_BLANKS
) && VALUE_IS_EMPTY (v
))
647 if ((flags
& COLLECT_IGNORE_ERRORS
) && VALUE_IS_ERROR (v
))
650 if (VALUE_IS_ERROR (v
)) {
656 nalloc
= (2 * nalloc
) + 100;
657 xs
= g_renew (gnm_float
, xs
, nalloc
);
659 xs
[N
++] = value_get_as_float (v
);
663 if (fun (xs
, N
, &fres
)) {
664 res
= value_new_error_std (ep
, err
);
666 res
= value_new_float (fres
);
673 /****************************************************************************/