Skip to content

Commit

Permalink
improve csv file object
Browse files Browse the repository at this point in the history
  • Loading branch information
xianjimli committed Dec 7, 2023
1 parent 872da56 commit 2c7cb88
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 29 deletions.
3 changes: 3 additions & 0 deletions docs/changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# 最新动态

2023/12/07
* csv file object 支持通过 MVVM 来查询。

2023/12/06
* fscript 增加 date\_time\_format
* 完善 fscript,先找全局函数,再找用户自定义的函数。
Expand Down
188 changes: 161 additions & 27 deletions src/csv/csv_file_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -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);
}
Expand All @@ -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));
}
Expand All @@ -112,19 +169,26 @@ 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;
const char* str = NULL;
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;
}
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
43 changes: 41 additions & 2 deletions src/csv/csv_file_object.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* File: object_csv_file.h
* File: csv_file_object.h
* Author: AWTK Develop Team
* Brief: csv file object
*
Expand Down Expand Up @@ -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对象。
*
* 示例
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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对象保存到指定文件。
Expand All @@ -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*/

0 comments on commit 2c7cb88

Please sign in to comment.