diff --git a/docs/changes.md b/docs/changes.md index c680ed25aa..87579b11d5 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,5 +1,8 @@ # 最新动态 +2023/12/07 + * csv file object 支持通过 MVVM 来查询。 + 2023/12/06 * fscript 增加 date\_time\_format * 完善 fscript,先找全局函数,再找用户自定义的函数。 diff --git a/src/csv/csv_file_object.c b/src/csv/csv_file_object.c index c4aaa33460..016d5b2ca8 100644 --- a/src/csv/csv_file_object.c +++ b/src/csv/csv_file_object.c @@ -23,19 +23,12 @@ #include "tkc/mem.h" #include "tkc/utils.h" #include "tkc/object.h" +#include "tkc/object_default.h" #include "tkc/data_reader.h" #include "csv_file.h" #include "csv_file_object.h" -typedef struct _csv_file_object_t { - tk_object_t object; - - /*private*/ - csv_file_t* csv; - str_t str; -} csv_file_object_t; - static csv_file_object_t* csv_file_object_cast(tk_object_t* obj); #define CSV_FILE_OBJECT(obj) csv_file_object_cast((tk_object_t*)obj) @@ -45,15 +38,27 @@ typedef struct _csv_path_t { const char* col_name; } csv_path_t; -static ret_t csv_path_parse(csv_path_t* path, csv_file_t* csv, const char* name) { +static ret_t csv_path_parse_impl(csv_path_t* path, csv_file_t* csv, const char* name) { const char* p = name; + return_value_if_fail(path != NULL && csv != NULL && name != NULL, RET_BAD_PARAMS); + memset(path, 0x00, sizeof(*path)); - while (*p && *p != '[') p++; - return_value_if_fail(*p == '[', RET_FAIL); + while (tk_isspace(*p)) p++; + + if (tk_isdigit(*p)) { + path->row = tk_atoi(p); + } else if (*p != '[') { + return RET_BAD_PARAMS; + } else { + p++; /* skip '[' */ + if (tk_isdigit(*p)) { + path->row = tk_atoi(p); + } else { + return RET_BAD_PARAMS; + } + } - p++; - path->row = tk_atoi(p); p = strchr(p, '.'); if (p == NULL) { return RET_OK; @@ -70,6 +75,8 @@ static ret_t csv_path_parse(csv_path_t* path, csv_file_t* csv, const char* name) if (*p == '[') { return_value_if_fail(tk_isdigit(p[1]), RET_BAD_PARAMS); path->col = tk_atoi(p + 1); + } else if (tk_isdigit(*p)) { + path->col = tk_atoi(p); } else { path->col = csv_file_get_col_of_name(csv, p); if (path->col < 0) { @@ -83,11 +90,57 @@ static ret_t csv_path_parse(csv_path_t* path, csv_file_t* csv, const char* name) return RET_OK; } +static ret_t csv_file_object_remove_map(csv_file_object_t* o, uint32_t index) { + uint32_t i = 0; + uint32_t n = 0; + uint32_t* rows_map = NULL; + return_value_if_fail(o != NULL, RET_BAD_PARAMS); + + rows_map = o->rows_map; + n = o->rows_map_size; + for (i = index; i < n - 1; i++) { + rows_map[i] = rows_map[i + 1]; + } + + o->rows_map_size--; + + return RET_OK; +} + +static ret_t csv_path_parse_ex(csv_file_object_t* o, csv_path_t* path, const char* name, + bool_t for_remove) { + ret_t ret = RET_BAD_PARAMS; + return_value_if_fail(o != NULL && path != NULL && name != NULL, RET_BAD_PARAMS); + + ret = csv_path_parse_impl(path, o->csv, name); + if (ret == RET_OK) { + if (o->rows_map != NULL) { + uint32_t index = path->row; + path->row = o->rows_map[path->row]; + if (for_remove) { + csv_file_object_remove_map(o, index); + } + } + return RET_OK; + } else { + return ret; + } +} + +static ret_t csv_path_parse(csv_file_object_t* o, csv_path_t* path, const char* name) { + return csv_path_parse_ex(o, path, name, FALSE); +} + static ret_t csv_file_object_remove_prop(tk_object_t* obj, const char* name) { csv_path_t p; csv_file_object_t* o = CSV_FILE_OBJECT(obj); return_value_if_fail(o != NULL, RET_BAD_PARAMS); - return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL); + + if (tk_str_start_with(name, CSV_QUERY_PREFIX)) { + return tk_object_remove_prop(o->query_args, name); + } + + return_value_if_fail(csv_path_parse_ex(o, &p, name, TRUE) == RET_OK, RET_FAIL); return csv_file_remove_row(o->csv, p.row); } @@ -98,12 +151,16 @@ static ret_t csv_file_object_set_prop(tk_object_t* obj, const char* name, const csv_file_object_t* o = CSV_FILE_OBJECT(obj); return_value_if_fail(o != NULL, RET_BAD_PARAMS); + if (tk_str_start_with(name, CSV_QUERY_PREFIX)) { + return tk_object_set_prop(o->query_args, name, v); + } + rows = csv_file_get_rows(o->csv); if (rows <= 0) { return RET_NOT_FOUND; } - return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL); + return_value_if_fail(csv_path_parse(o, &p, name) == RET_OK, RET_FAIL); if (p.col_name != NULL && tk_str_ieq(p.col_name, TK_OBJECT_PROP_CHECKED)) { return csv_file_set_row_checked(o->csv, p.row, value_bool(v)); } @@ -112,7 +169,6 @@ static ret_t csv_file_object_set_prop(tk_object_t* obj, const char* name, const return csv_file_set(o->csv, p.row, p.col, o->str.str); } - static ret_t csv_file_object_get_prop(tk_object_t* obj, const char* name, value_t* v) { csv_path_t p; uint32_t rows = 0; @@ -120,11 +176,19 @@ static ret_t csv_file_object_get_prop(tk_object_t* obj, const char* name, value_ csv_file_object_t* o = CSV_FILE_OBJECT(obj); return_value_if_fail(o != NULL, RET_BAD_PARAMS); + if (tk_str_start_with(name, CSV_QUERY_PREFIX)) { + return tk_object_get_prop(o->query_args, name, v); + } + rows = csv_file_get_rows(o->csv); if (tk_str_ieq(name, TK_OBJECT_PROP_SIZE)) { - value_set_int(v, rows); + if (o->rows_map != NULL) { + value_set_int(v, o->rows_map_size); + } else { + value_set_int(v, rows); + } return RET_OK; - } else if(tk_str_eq(name, TK_OBJECT_PROP_SELECTED_INDEX)) { + } else if (tk_str_eq(name, TK_OBJECT_PROP_SELECTED_INDEX)) { value_set_int(v, csv_file_get_first_checked(o->csv)); return RET_OK; } @@ -133,7 +197,7 @@ static ret_t csv_file_object_get_prop(tk_object_t* obj, const char* name, value_ return RET_NOT_FOUND; } - return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL); + return_value_if_fail(csv_path_parse(o, &p, name) == RET_OK, RET_FAIL); if (p.col_name != NULL && tk_str_ieq(p.col_name, TK_OBJECT_PROP_CHECKED)) { return_value_if_fail(p.row < rows, RET_FAIL); @@ -165,11 +229,75 @@ static bool_t csv_file_object_can_exec(tk_object_t* obj, const char* name, const return csv_file_get_checked_rows(o->csv) > 0; } else if (tk_str_ieq(name, TK_OBJECT_CMD_ADD)) { return TRUE; + } else if (tk_str_eq(name, CSV_CMD_QUERY)) { + return TRUE; } return FALSE; } +static ret_t csv_file_object_prepare(csv_file_object_t* o) { + o->rows_map_size = 0; + if (o->rows_map != NULL) { + if (o->rows_map_capacity < csv_file_get_rows(o->csv)) { + TKMEM_FREE(o->rows_map); + o->rows_map = NULL; + o->rows_map_capacity = 0; + } + } + + if (o->rows_map == NULL) { + o->rows_map_capacity = csv_file_get_rows(o->csv); + o->rows_map = TKMEM_ZALLOCN(uint32_t, o->rows_map_capacity); + } + + return o->rows_map != NULL ? RET_OK : RET_OOM; +} + +static ret_t csv_file_object_clear_query(tk_object_t* obj) { + csv_file_object_t* o = CSV_FILE_OBJECT(obj); + return_value_if_fail(o != NULL, RET_BAD_PARAMS); + + o->rows_map_size = 0; + TKMEM_FREE(o->rows_map); + o->rows_map_capacity = 0; + + return RET_OK; +} + +static ret_t csv_file_object_query(tk_object_t* obj) { + uint32_t i = 0; + uint32_t n = 0; + csv_file_object_t* o = CSV_FILE_OBJECT(obj); + return_value_if_fail(o != NULL, RET_BAD_PARAMS); + return_value_if_fail(o->filter != NULL, RET_BAD_PARAMS); + return_value_if_fail(csv_file_object_prepare(o) == RET_OK, RET_OOM); + + n = csv_file_get_rows(o->csv); + for (i = 0; i < n; i++) { + csv_row_t* row = csv_file_get_row(o->csv, i); + ret_t ret = o->filter(o->filter_ctx, o->query_args, i, row); + + if (ret == RET_OK) { + o->rows_map[o->rows_map_size++] = i; + } else if (ret == RET_STOP) { + break; + } + } + + return RET_OK; +} + +ret_t csv_file_object_set_filter(tk_object_t* obj, csv_file_object_filter_t filter, void* ctx) { + csv_file_object_t* o = CSV_FILE_OBJECT(obj); + return_value_if_fail(o != NULL, RET_BAD_PARAMS); + + o->filter = filter; + o->filter_ctx = ctx; + + return RET_OK; +} + static ret_t csv_file_object_exec(tk_object_t* obj, const char* name, const char* args) { ret_t ret = RET_NOT_IMPL; csv_file_object_t* o = CSV_FILE_OBJECT(obj); @@ -180,23 +308,26 @@ static ret_t csv_file_object_exec(tk_object_t* obj, const char* name, const char } else if (tk_str_ieq(name, TK_OBJECT_CMD_RELOAD)) { csv_file_reload(o->csv); ret = RET_ITEMS_CHANGED; + csv_file_object_clear_query(obj); } else if (tk_str_ieq(name, TK_OBJECT_CMD_CLEAR)) { csv_file_clear(o->csv); ret = RET_ITEMS_CHANGED; + csv_file_object_clear_query(obj); } else if (tk_str_ieq(name, TK_OBJECT_CMD_REMOVE)) { - const char* index = strrchr(args, '['); - if (index != NULL) { - index++; - } else { - index = args; - } - return_value_if_fail(index != NULL, RET_FAIL); - ret = csv_file_remove_row(o->csv, tk_atoi(index)) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL; + ret = csv_file_object_remove_prop(obj, args) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL; } else if (tk_str_ieq(name, TK_OBJECT_CMD_REMOVE_CHECKED)) { ret = csv_file_remove_checked_rows(o->csv) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL; + csv_file_object_clear_query(obj); } else if (tk_str_ieq(name, TK_OBJECT_CMD_ADD)) { return_value_if_fail(args != NULL, RET_FAIL); ret = csv_file_append_row(o->csv, args) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL; + } else if (tk_str_eq(name, CSV_CMD_QUERY)) { + if (tk_str_eq(args, CSV_CMD_QUERY_ARG_CLEAR)) { + ret = csv_file_object_clear_query(obj); + } else { + ret = csv_file_object_query(obj); + } + ret = RET_ITEMS_CHANGED; } else { return RET_NOT_IMPL; } @@ -215,6 +346,8 @@ static ret_t csv_file_object_destroy(tk_object_t* obj) { csv_file_destroy(o->csv); o->csv = NULL; + TK_OBJECT_UNREF(o->query_args); + TKMEM_FREE(o->rows_map); str_reset(&(o->str)); return RET_OK; @@ -247,6 +380,7 @@ tk_object_t* csv_file_object_create(csv_file_t* csv) { return_value_if_fail(o != NULL, NULL); o->csv = csv; + o->query_args = object_default_create_ex(FALSE); str_init(&(o->str), 0); return obj; diff --git a/src/csv/csv_file_object.h b/src/csv/csv_file_object.h index 79fe511c7c..f6614744b4 100644 --- a/src/csv/csv_file_object.h +++ b/src/csv/csv_file_object.h @@ -1,5 +1,5 @@ /** - * File: object_csv_file.h + * File: csv_file_object.h * Author: AWTK Develop Team * Brief: csv file object * @@ -27,10 +27,18 @@ BEGIN_C_DECLS + +/** + * 返回值: + * RET_OK: 保留 + * RET_STOP: 停止解析 + * RET_FAIL: 忽略此行 + */ +typedef ret_t (*csv_file_object_filter_t)(void* ctx, tk_object_t* args, uint32_t index, csv_row_t* row); + /** * @class csv_file_object_t * @parent tk_object_t - * @annotation["fake"] * 将cvs file包装成object对象。 * * 示例 @@ -85,6 +93,21 @@ BEGIN_C_DECLS * ``` */ +typedef struct _csv_file_object_t { + tk_object_t object; + + /*private*/ + csv_file_t* csv; + str_t str; + + tk_object_t* query_args; + uint32_t* rows_map; + uint32_t rows_map_size; + uint32_t rows_map_capacity; + csv_file_object_filter_t filter; + void* filter_ctx; +} csv_file_object_t; + /** * @method csv_file_object_create * @@ -144,6 +167,18 @@ tk_object_t* csv_file_object_load_from_buff(const void* buff, uint32_t size, cha */ ret_t csv_file_object_save_to_buff(tk_object_t* obj, wbuffer_t* wb); +/** + * @method csv_file_object_set_filter + * 设置过滤器。 + * + * @param {tk_object_t*} obj doc对象。 + * @param {csv_file_object_filter_t} filter 过滤器。 + * @param {void*} ctx 上下文。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败 + */ +ret_t csv_file_object_set_filter(tk_object_t* obj, csv_file_object_filter_t filter, void* ctx); + /** * @method csv_file_object_save_as * 将doc对象保存到指定文件。 @@ -156,6 +191,10 @@ ret_t csv_file_object_save_to_buff(tk_object_t* obj, wbuffer_t* wb); */ ret_t csv_file_object_save_as(tk_object_t* obj, const char* filename); +#define CSV_QUERY_PREFIX "query." +#define CSV_CMD_QUERY "query" +#define CSV_CMD_QUERY_ARG_CLEAR "clear" + END_C_DECLS #endif /*TK_CSV_FILE_OBJECT_H*/