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 free_criteria (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
)free_criteria
);
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
)free_criteria
);
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
=
404 workbook_date_conv (sheet
->workbook
);
407 for (i
= b_row
; i
<= e_row
; i
++) {
408 GnmDBCriteria
*new_criteria
= g_new (GnmDBCriteria
, 1);
409 GSList
*conditions
= NULL
;
411 for (j
= b_col
; j
<= e_col
; j
++) {
413 GnmCell
*cell
= sheet_cell_get (sheet
, j
, i
);
415 gnm_cell_eval (cell
);
416 if (gnm_cell_is_empty (cell
))
419 cond
= parse_criteria (cell
->value
, date_conv
,
421 cond
->column
= (field_ind
!= NULL
)
422 ? field_ind
[j
- b_col
]
424 conditions
= g_slist_prepend (conditions
, cond
);
427 new_criteria
->conditions
= g_slist_reverse (conditions
);
428 criterias
= g_slist_prepend (criterias
, new_criteria
);
431 return g_slist_reverse (criterias
);
435 * parse_database_criteria:
437 * @database: #GnmValue
438 * @criteria: #GnmValue
440 * Parses the criteria cell range.
441 * Returns: (element-type GnmDBCriteria) (transfer full):
444 parse_database_criteria (GnmEvalPos
const *ep
, GnmValue
const *database
, GnmValue
const *criteria
)
449 int b_col
, b_row
, e_col
, e_row
;
453 g_return_val_if_fail (VALUE_IS_CELLRANGE (criteria
), NULL
);
455 sheet
= eval_sheet (criteria
->v_range
.cell
.a
.sheet
, ep
->sheet
);
456 b_col
= criteria
->v_range
.cell
.a
.col
;
457 b_row
= criteria
->v_range
.cell
.a
.row
;
458 e_col
= criteria
->v_range
.cell
.b
.col
;
459 e_row
= criteria
->v_range
.cell
.b
.row
;
467 /* Find the index numbers for the columns of criterias */
468 field_ind
= g_new (int, e_col
- b_col
+ 1);
469 for (i
= b_col
; i
<= e_col
; i
++) {
470 cell
= sheet_cell_get (sheet
, i
, b_row
);
473 gnm_cell_eval (cell
);
474 if (gnm_cell_is_empty (cell
))
476 field_ind
[i
- b_col
] =
477 find_column_of_field (ep
, database
, cell
->value
);
478 if (field_ind
[i
- b_col
] == -1) {
484 res
= parse_criteria_range (sheet
, b_col
, b_row
+ 1,
485 e_col
, e_row
, field_ind
,
492 * find_rows_that_match:
494 * @first_col: first column.
495 * @first_row: first row.
496 * @last_col: last column.
497 * @last_row: laset row.
498 * @criterias: (element-type GnmDBCriteria): the criteria to use.
501 * Finds the rows from the given database that match the criteria.
502 * Returns: (element-type int) (transfer full): the list of matching rows.
505 find_rows_that_match (Sheet
*sheet
, int first_col
, int first_row
,
506 int last_col
, int last_row
,
507 GSList
*criterias
, gboolean unique_only
)
510 GSList
const *crit_ptr
, *cond_ptr
;
515 GnmValue
const *empty
= value_new_empty ();
517 for (row
= first_row
; row
<= last_row
; row
++) {
519 for (crit_ptr
= criterias
; crit_ptr
; crit_ptr
= crit_ptr
->next
) {
520 GnmDBCriteria
const *crit
= crit_ptr
->data
;
522 for (cond_ptr
= crit
->conditions
;
523 cond_ptr
!= NULL
; cond_ptr
= cond_ptr
->next
) {
524 GnmCriteria
*cond
= cond_ptr
->data
;
525 test_cell
= sheet_cell_get (sheet
, cond
->column
, row
);
526 if (test_cell
!= NULL
)
527 gnm_cell_eval (test_cell
);
528 if (!cond
->fun (test_cell
? test_cell
->value
: empty
, cond
)) {
545 for (c
= rows
; c
!= NULL
; c
= c
->next
) {
546 trow
= *((gint
*) c
->data
);
547 for (i
= first_col
; i
<= last_col
; i
++) {
548 test_cell
= sheet_cell_get (sheet
, i
, trow
);
549 cell
= sheet_cell_get (sheet
, i
, row
);
551 /* FIXME: this is probably not right, but crashing is more wrong. */
552 if (test_cell
== NULL
|| cell
== NULL
)
556 ? value_peek_string (cell
->value
)
558 t2
= test_cell
->value
559 ? value_peek_string (test_cell
->value
)
561 if (strcmp (t1
, t2
) != 0)
571 rows
= g_slist_prepend (rows
, (gpointer
) p
);
577 return g_slist_reverse (rows
);
580 /****************************************************************************/
584 * @data: (element-type GnmValue):
585 * @crits: (element-type GnmCriteria): criteria
587 * @fun: (scope call): function to evaluate on filtered data
588 * @err: error value in case @fun fails.
589 * @ep: evaluation position
594 gnm_ifs_func (GPtrArray
*data
, GPtrArray
*crits
, GnmValue
const *vals
,
595 float_range_function_t fun
, GnmStdError err
,
596 GnmEvalPos
const *ep
, CollectFlags flags
)
599 unsigned ui
, N
= 0, nalloc
= 0;
600 gnm_float
*xs
= NULL
;
601 GnmValue
*res
= NULL
;
604 g_return_val_if_fail (data
->len
== crits
->len
, NULL
);
606 if (flags
& ~(COLLECT_IGNORE_STRINGS
|
607 COLLECT_IGNORE_BOOLS
|
608 COLLECT_IGNORE_BLANKS
|
609 COLLECT_IGNORE_ERRORS
)) {
610 g_warning ("unsupported flags in gnm_ifs_func %x", flags
);
613 sx
= value_area_get_width (vals
, ep
);
614 sy
= value_area_get_height (vals
, ep
);
615 for (ui
= 0; ui
< data
->len
; ui
++) {
616 GnmValue
const *datai
= g_ptr_array_index (data
, ui
);
617 if (value_area_get_width (datai
, ep
) != sx
||
618 value_area_get_height (datai
, ep
) != sy
)
619 return value_new_error_VALUE (ep
);
622 for (y
= 0; y
< sy
; y
++) {
623 for (x
= 0; x
< sx
; x
++) {
625 gboolean match
= TRUE
;
627 for (ui
= 0; match
&& ui
< crits
->len
; ui
++) {
628 GnmCriteria
*crit
= g_ptr_array_index (crits
, ui
);
629 GnmValue
const *datai
= g_ptr_array_index (data
, ui
);
630 v
= value_area_get_x_y (datai
, x
, y
, ep
);
632 match
= crit
->fun (v
, crit
);
637 // Match. Maybe collect the data point.
639 v
= value_area_get_x_y (vals
, x
, y
, ep
);
640 if ((flags
& COLLECT_IGNORE_STRINGS
) && VALUE_IS_STRING (v
))
642 if ((flags
& COLLECT_IGNORE_BOOLS
) && VALUE_IS_BOOLEAN (v
))
644 if ((flags
& COLLECT_IGNORE_BLANKS
) && VALUE_IS_EMPTY (v
))
646 if ((flags
& COLLECT_IGNORE_ERRORS
) && VALUE_IS_ERROR (v
))
649 if (VALUE_IS_ERROR (v
)) {
655 nalloc
= (2 * nalloc
) + 100;
656 xs
= g_renew (gnm_float
, xs
, nalloc
);
658 xs
[N
++] = value_get_as_float (v
);
662 if (fun (xs
, N
, &fres
)) {
663 res
= value_new_error_std (ep
, err
);
665 res
= value_new_float (fres
);
672 /****************************************************************************/