Builder模式在AWTK中的应用
前言
[AWTK](https://github.com/zlgopen/awtk) 全称 Toolkit AnyWhere,是 ZLG 开发的开源 GUI 引擎,旨在为嵌入式系统、WEB、各种小程序、手机和 PC 打造的通用 GUI 引擎,为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎。如果对AWTK感兴趣,非常欢迎你加入AWTK[生态共建计划](https://github.com/zlgopen/awtk/blob/master/docs/awtk_ecology.md)。本周五参加了Builder模式研讨会,这篇博文的目的,一是对Builder模式做一个总结,二是分享AWTK是怎么使用Builder模式的。关于Builder模式,之前有分享了一篇博文[《建造者(Builder)模式应用》](https://blog.csdn.net/woody218/article/details/111827587),介绍的是如何通过aidl文件构造json文件,感兴趣的同学可以去看看。
一、Builder模式总结
1、模式意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。2、参与者
Builder-- 为创建一个Product对象的各个部件指定抽象接口。
ConcreteBuilder
-- 实现Builder的接口以构造和装配该产品的各个部件。
-- 提供一个返回产品的接口。 Director
-- 构造一个使用Builder接口的对象
Product
-- 表示被构造的复杂对象。
3、效果
1)它使得你可以改变一个产品的内部表示-- Builder对象提供给向导器一个构造产品的抽象接口。
2)它将构造代码和表示代码分开
-- 模式通过封装一个复杂对象的创建和表示方式,提高了对象的模块性。
3)它使你可对构造过程进行更精细的控制
-- 模式的产品是在向导器的控制下一步一步构造出来的。
二、Builder模式在AWTK中的应用
在AWTK中,builder模式的一个应用是:根据xml文件构造一个富文本。在这个应用当中,xml解析器充当Director角色,负责指导富文本的创建过程;rich_text_parser中的xml_builder_t充当ConcreteBuilder角色,负责富文本的实际构造。1、解析xml文件
1、通过以下接口,创建xml解析器并设置Builder对象,这样就把解析器和builder绑定起来,通过实际设置的builder对象的接口,来执行实际的构建动作。/**
* @method xml_parser_create
*
* 创建解析器。
*
* @return {XmlParser*} 返回parser对象。
*/
XmlParser* xml_parser_create(void);
/**
* @method xml_parser_set_builder
*
* 设置builder。
*
* @param {XmlParser*} parser parser对象。
* @param {XmlBuilder*} builder builder对象。
*
* @return {void} 返回无。
*/
void xml_parser_set_builder(XmlParser* parser, XmlBuilder* builder);
2、xml开始解析tag,结束tag、解析文本及处理解析错误;1)解析tag,获取属性,调用tag的处理函数:builder->on_start(builder, tag, attrs);
2)结束tag,调用tag结束处理函数:builder->on_end(builder, tag);
3)解析文本,调用文本处理函数:builder->on_text(builder, text, length);
4)遇到解析错误时,调用错误处理函数:builder->on_error(builder, line, col, message);
5)解析结束,调用结束处理函数:builder->destroy(builder);
完整源码可以通过李先静老师的AWTK仓库可以阅读(仓库:https://github.com/zlgopen/awtk,awtk/src/xml/子目录)。
static void xml_parser_parse_start_tag(XmlParser* parser) {
enum _State {
STAT_NAME,
STAT_ATTR,
STAT_END,
} state = STAT_NAME;
char* tag_name = NULL;
const char* start = parser->read_ptr - 1;
for (; *parser->read_ptr != '\0'; parser->read_ptr++) {
char c = *parser->read_ptr;
switch (state) {
case STAT_NAME: {
if (isspace(c) || c == '>' || c == '/') {
tag_name =
tk_pointer_from_int(xml_parser_strdup(parser, start, parser->read_ptr - start, TRUE));
state = (c != '>' && c != '/') ? STAT_ATTR : STAT_END;
}
break;
}
case STAT_ATTR: {
xml_parser_parse_attrs(parser, '/');
state = STAT_END;
break;
}
default:
break;
}
if (state == STAT_END) {
break;
}
}
tag_name = parser->buffer + tk_pointer_to_int(tag_name);
xml_builder_on_start(parser->builder, tag_name, (const char**)parser->attrs);
if (parser->read_ptr[0] == '/') {
xml_builder_on_end(parser->builder, tag_name);
}
for (; *parser->read_ptr != '>' && *parser->read_ptr != '\0'; parser->read_ptr++)
;
return;
}
static void xml_parser_parse_end_tag(XmlParser* parser) {
char* tag_name = NULL;
const char* start = parser->read_ptr;
for (; *parser->read_ptr != '\0'; parser->read_ptr++) {
if (*parser->read_ptr == '>') {
tag_name = parser->buffer + xml_parser_strdup(parser, start, parser->read_ptr - start, TRUE);
xml_builder_on_end(parser->builder, tag_name);
break;
}
}
return;
}
static void xml_parser_on_text(XmlParser* parser) {
if (parser->text.size > 0) {
char* start = parser->text.str;
char* end = parser->text.str + parser->text.size - 1;
if (parser->trim_text) {
while (isspace(*start) && *start) {
start++;
}
while (isspace(*end) && end > start) {
*end = '\0';
end--;
}
}
if (end >= start) {
xml_builder_on_text(parser->builder, start, end - start + 1);
}
}
}
2、构造富文本文件
1、首先,通过初始化builder的参数,指定每个步骤实际的处理函数。当解析xml文件,到响应步骤时,就会调用这些处理函数,完成响应的处理操作。static XmlBuilder* builder_init(xml_builder_t* b, const char* font_name, uint16_t font_size,
color_t color, align_v_t align_v) {
int32_t i = 0;
memset(b, 0x00, sizeof(xml_builder_t));
b->builder.on_start = xml_rich_text_on_start;
b->builder.on_end = xml_rich_text_on_end;
b->builder.on_text = xml_rich_text_on_text;
b->builder.on_error = xml_rich_text_on_error;
b->builder.destroy = xml_rich_text_destroy;
b->font = b->fonts;
for (i = 0; i < MAX_FONT_LEVEL; i++) {
rich_text_font_t* iter = b->fonts + i;
iter->size = font_size;
iter->color = color;
iter->align_v = align_v;
iter->name = font_name == NULL ? NULL : tk_strdup(font_name);
}
str_init(&(b->temp), 100);
return &(b->builder);
}
2、通过处理tag start和text,构造富文本。由于每个步骤的处理思路是相似的,这里仅列举on_start和on_text的处理函数,完整的源码可以通过李先静老师的AWTK仓库可以阅读(仓库:https://github.com/zlgopen/awtk,awtk/src/ext_widgets/rich_text/rich_text_parser.c)。
static void xml_rich_text_on_start(XmlBuilder* thiz, const char* tag, const char** attrs) {
int32_t i = 0;
xml_builder_t* b = (xml_builder_t*)thiz;
if (tk_str_eq(tag, "font")) {
xml_rich_text_push_font(b);
b->font->align_v = ALIGN_V_BOTTOM;
while (attrs[i]) {
const char* key = attrs[i];
const char* value = attrs[i + 1];
if (tk_str_eq(key, "size")) {
b->font->size = tk_atoi(value);
} else if (tk_str_eq(key, "color")) {
b->font->color = color_parse(value);
} else if (tk_str_eq(key, "align_v")) {
const key_type_value_t* kv = align_v_type_find(value);
if (kv != NULL) {
b->font->align_v = (align_v_t)(kv->value);
}
} else if (tk_str_eq(key, "name")) {
TKMEM_FREE(b->font->name);
b->font->name = tk_strdup(value);
}
i += 2;
}
} else if (tk_str_eq(tag, "b")) {
xml_rich_text_push_font(b);
b->font->bold = TRUE;
} else if (tk_str_eq(tag, "i")) {
xml_rich_text_push_font(b);
b->font->italic = TRUE;
} else if (tk_str_eq(tag, "u")) {
xml_rich_text_push_font(b);
b->font->underline = TRUE;
} else if (tk_str_eq(tag, "image")) {
int32_t w = 0;
int32_t h = 0;
int32_t draw_type = IMAGE_DRAW_CENTER;
const char* image_name = NULL;
while (attrs[i]) {
const char* key = attrs[i];
const char* value = attrs[i + 1];
if (tk_str_eq(key, "name")) {
image_name = value;
} else if (tk_str_eq(key, "w")) {
w = tk_atoi(value);
} else if (tk_str_eq(key, "h")) {
h = tk_atoi(value);
} else if (tk_str_eq(key, "draw_type")) {
const key_type_value_t* kv = image_draw_type_find(value);
if (kv != NULL) {
draw_type = kv->value;
}
}
i += 2;
}
if (image_name != NULL) {
b->node = rich_text_node_append(b->node, rich_text_image_create(image_name, w, h, draw_type));
}
}
return;
}
static void xml_rich_text_on_text(XmlBuilder* thiz, const char* text, size_t length) {
xml_builder_t* b = (xml_builder_t*)thiz;
rich_text_font_t* font = b->font;
str_decode_xml_entity_with_len(&(b->temp), text, length);
b->node = rich_text_node_append(b->node, rich_text_text_create(font, b->temp.str));
return;
}