您的位置:首页 > 移动开发 > Cocos引擎

COCOS2D-3.9 DictMaker分析

2015-12-08 22:11 501 查看

XML 文件的格式:

TEST的 config-example.plist 文件为例:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>
<key>data</key>
<dict>
<key>cocos2d.x.fps</key>
<integer>60</integer>
<key>cocos2d.x.display_fps</key>
<true/>
<key>cocos2d.x.gl.projection</key>
<string>3d</string>
<key>cocos2d.x.texture.pixel_format_for_png</key>
<string>rgba8888</string>
<key>cocos2d.x.texture.pvrv2_has_alpha_premultiplied</key>
<false/>
<key>cocos2d.x.testcpp.autorun</key>
<false/>
<key>cocos2d.x.3d.max_dir_light_in_shader</key>
<integer>1</integer>
<key>cocos2d.x.3d.max_point_light_in_shader</key>
<integer>1</integer>
<key>cocos2d.x.3d.max_spot_light_in_shader</key>
<integer>1</integer>
<key>cocos2d.x.3d.animate_quality</key>
<integer>2</integer>
</dict>
<key>metadata</key>
<dict>
<key>format</key>
<integer>1</integer>
</dict>

</dict>

</plist>

文件开始两行为一些标注信息,不用于解析具体类容。节点形式分为以下几类:

<Node><Node1> ... </Node1></Node> 嵌套形式,一个节点中包含多个节点。

<Node>TEXT<Node> 单个节点形式,中间为该节点表示的值。TEXT 是一种特殊节点。

<Node ATT=VAL/> 单个节点或者多个节点都可带属性,ATT 表示该节点包含指定信息。

字典文件格式:

一般来说也是XML格式的文件,只是文件规定了一些规则用于该类型数据的解析,

比如 onfig-example.plist 中一些 NODE 的名字属性:

dict 表示一个字典类型节点,该节点中的子节点将以 KEY-VALUE 的结构存在,在解析时必定遵循该规则。

VALUE 的 NODE 名则需要指定对应的数据类型,各种符合解析规则的数据类型。

DictMaker 继承抽象类 SAXDelegator 需要实现以下纯虚函数:

参数的意思:ctx SAXParser,name NODE,atts 指向属性的数组,KEY-VALUE 顺序直到 NULL

virtual void startElement(void *ctx, const char *name, const char **atts) = 0;

解析 NODE 头的回调。

virtual void endElement(void *ctx, const char *name) = 0;

解析 NODE 尾的回调。

virtual void textHandler(void *ctx, const char *s, int len) = 0;

解析 NODE 文本的回调。

DictMaker 的变量字段:

(很奇葩,全是公有的)

SAXResult _resultType;

表示根节点解析类型 SAX_RESULT_NONE 普通节点, SAX_RESULT_DICT 字典节点, SAX_RESULT_ARRAY 数组节点,数据解析前定义该数据的类型。

ValueMap _rootDict;

字典根节点

ValueVector _rootArray;

数组根节点

std::string _curKey;

当前的KEY

std::string _curValue; 

当前的VALUE,字符串类型

SAXState _state;

当前节点存放VALUE的类型,SAX_NONE 空,  SAX_KEY 键, SAX_DICT 字典, SAX_INT 整型, SAX_REAL 实数, SAX_STRING 字符串, SAX_ARRAY 数组

ValueMap*  _curDict;

当前字典,每次构造字典数据时指向该字典的地址。

ValueVector* _curArray;

当前数组,每次构造数组数据时指向该数组的地址。

std::stack<ValueMap*> _dictStack;

用于保存字典节点的堆栈

std::stack<ValueVector*> _arrayStack;

用于保存数组节点的堆栈

std::stack<SAXState>  _stateStack;

用于保存节点类型的地战

解析实例分析:文件 config-example.plist

CC_UNUSED_PARAM 无意义的宏定义,只是用于防止编译器检测到变量未使用警告。

当一个节点开始时首先调用 startElement,

第一个节点为 plist,不是有效的定义类型,则设置 _state 为 SAX_NONE,这儿可以通过 atts 获取属性值。

plist 不是 TEXT 所以直接调用下一个节点的 startElement,

节点名为 ditct,表示这是一个符合字典定义结构的节点,构造字典节点时需要判断是否为数据节点的根节点,

void startElement(void *ctx, const char *name, const char **atts)

{
CC_UNUSED_PARAM(ctx);
CC_UNUSED_PARAM(atts);
const std::string sName(name);
if( sName == "dict" )
{
if(_resultType == SAX_RESULT_DICT && _rootDict.empty())
{//初始化根字典节点
_curDict = &_rootDict;
}

_state = SAX_DICT;

//获取上一个节点类型
SAXState preState = SAX_NONE;
if (! _stateStack.empty())
{
preState = _stateStack.top();
}

//判断父节点类型
if (SAX_ARRAY == preState)
{//若为数组则创建一个字典对象加入数组中,并赋值当前字典 _curDict
// add a new dictionary into the array
_curArray->push_back(Value(ValueMap()));
_curDict = &(_curArray->rbegin())->asValueMap();
}
else if (SAX_DICT == preState)
{//若为字典构造一个字典对象加入父字典中,并赋值当前字典 _curDict
// add a new dictionary into the pre dictionary 
CCASSERT(! _dictStack.empty(), "The state is wrong!");
ValueMap* preDict = _dictStack.top();
(*preDict)[_curKey] = Value(ValueMap());
_curDict = &(*preDict)[_curKey].asValueMap();
}

//标记当前节点类型,并将当前字典 压入堆栈中
// record the dict state
_stateStack.push(_state);
_dictStack.push(_curDict);
}
else if(sName == "key")
{
_state = SAX_KEY;
}
else if(sName == "integer")
{
_state = SAX_INT;
}
else if(sName == "real")
{
_state = SAX_REAL;
}
else if(sName == "string")
{
_state = SAX_STRING;
}
else if (sName == "array")
{
//标记当前节点为数组
_state = SAX_ARRAY;

if (_resultType == SAX_RESULT_ARRAY && _rootArray.empty())
{//初始化根数组节点
_curArray = &_rootArray;
}
//判断父节点类型
SAXState preState = SAX_NONE;
if (! _stateStack.empty())
{
preState = _stateStack.top();
}

if (preState == SAX_DICT)
{//若为字典则在该节点下创建数组节点,并赋值当前数组 _curArray
(*_curDict)[_curKey] = Value(ValueVector());
_curArray = &(*_curDict)[_curKey].asValueVector();
}
else if (preState == SAX_ARRAY)
{//若为数组则在该节点下创建数组节点,并赋值当前数组 _curArray
CCASSERT(! _arrayStack.empty(), "The state is wrong!");
ValueVector* preArray = _arrayStack.top();
preArray->push_back(Value(ValueVector()));
_curArray = &(_curArray->rbegin())->asValueVector();
}
//标记当前节点类型,并将当前节点 压入堆栈中
// record the array state
_stateStack.push(_state);
_arrayStack.push(_curArray);
}
else
{
_state = SAX_NONE;
}

}

若为KEY则设置当前节点类型为 _state = SAX_KEY,下次将回调 textHandler,指定TEXT的字符串地址和长度,通过 _state 选择设置当前的值。

当前 KEY 内容读完后进入 endElement,根据当父节点类型、当前节点类型和当前值组织 DictMaker 的值。

若文件不以 DICT 为根节点,则会自动默认初始为 DICT,再保存数据。

初始化函数:

ValueMap dictionaryWithContentsOfFile(const std::string& fileName)

通过字典文件初始化一个字典结构,_resultType = SAX_RESULT_DICT;

ValueMap dictionaryWithDataOfFile(const char* filedata, int filesize)

通过字典数据初始化一个字典结构,_resultType = SAX_RESULT_DICT;

ValueVector arrayWithContentsOfFile(const std::string& fileName)

通过数组文件初始化一个数组结构,_resultType = SAX_RESULT_ARRAY;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: