类似数据库回滚,定义一系列的原子操作,定义每种操作的逆操作,在底层监控所有操作、纪录操作,回滚时执行每个原子操作的逆操作。
实现过程中,存在编译问题,具体原因不记得了,大概是两个类互相依赖问题。采用cpp的模板类可以解决这个问题。c语言下实现的话,估计需要用到函数指针解决。
原子操作的类型(非xml原子操作已删除,例如跨文件、数据库)
kEXAT_att_add, /**< 属性添加,逆操作是属性删除*/
kEXAT_att_delete, /**< 属性删除,逆操作是属性添加*/
kEXAT_att_change, /**< 属性变更,逆操作是属性变更*/
kEXAT_node_add, /**< 添加的逆操作是移除,不释放内存*/
kEXAT_node_delete, /**< 删除节点, 释放内存。逆操作是添加*/
kEXAT_node_remove, /**< 移除节点,不释放内存。逆操作时添加*/
//移除操作需要根据后续操作才知道对应逆操作.移除非原子操作,移除是半原子操作。
//XmlObject的移除子节点+添加子节点两个操作需要变为单个移动函数才方便实现逆操作。
//采用移动节点概念替代移除节点。
//kEXAT_node_move, /**< 移动节点,不释放内存。对应其它基本操作*/
kEXAT_node_change, /**< 节点变更、此操作无效*/
kEXAT_father_change, /**< 变更父节点指针*/
kEXAT_text_change, /**< 变更xml节点中间的text内容*/
原子操作类的定义示例
/** @brief 各种XML内存操作信息,属性的增删改,节点的增删改
新增词条的增删改。
新增key、desc数据库的增删改。*/
template<class XmlNodeT, class DicT/*, class KeyDbT*/>
class XmlAction
{
public:
//friend class XmlObjectRollBackManager;
public: //各种操作的构造函数
/** @brief 属性增:ver_id、修改类型、XmlNodeT * obj 、属性名
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] obj 属性变更的节点指针
@param [in] att_name 属性名
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
XmlNodeT *obj, const XBaseString &att_name)
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if (type_temp != kEXAT_att_add //属性增
|| vid <= kInvalidVersionId
|| obj == NULL
|| att_name.length() == 0)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = att_name;
value_from_ = kInvalidString;
value_to_ = kInvalidString;
father_ = NULL;
obj_index_ = kInvalidIndex;
}
/** @brief 属性改:ver_id、修改类型、XmlNodeT *obj 、属性名、修改前的属性值、修改后的属性值
kEXAT_text_change 此属性特殊处理
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] obj 属性变更的节点指针
@param [in] att_name 属性名
@param [in] value_from 属性修改前的值
@param [in] value_to 属性修改后的值
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
XmlNodeT *obj, const XBaseString &att_name,
const XBaseString &value_from, const XBaseString &value_to)
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if ((type_temp != kEXAT_att_change && type_temp != kEXAT_text_change) //属性改
|| vid <= kInvalidVersionId
|| obj == NULL
|| att_name.length() == 0)
{
type_ = kEXAT_unkown;
return;
}
if (type_temp == kEXAT_text_change && att_name != kXmlTextAttName)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = att_name;
value_from_ = value_from;
value_to_ = value_to;
father_ = NULL;
obj_index_ = kInvalidIndex;
}
/** @brief
词条变更,key变更、value变更、key-value的增删改(没有remove)
key变更,value_from_,value_to_, ver_id、修改类型、DicT *dic
value变更,value_from_,value_to_, ver_id、修改类型、DicT *dic
key-value增,att_name_,value_to_, ver_id、修改类型、DicT *dic
删,id,att_name_,value_from_, ver_id、修改类型、DicT *dic
改,att_name_,value_from_,value_to_, ver_id、修改类型、DicT *dic
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] dic 词条管理对象
@param [in] att_name 属性名(不同操作含义不同)
@param [in] value_from 属性修改前的值(不同操作含义不同)
@param [in] value_to 属性修改后的值(不同操作含义不同)
@param [in] id_temp 词条id
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
DicT *dic, const XBaseString &att_name,
const XBaseString &value_from, const XBaseString &value_to, const int id_temp)
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if ((type_temp != kEXAT_word_key_change && type_temp != kEXAT_word_value_change
&& type_temp != kEXAT_word_add && type_temp != kEXAT_word_delete
&& type_temp != kEXAT_word_change) //词条相关变更
|| vid <= kInvalidVersionId
|| dic == NULL)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = NULL;
att_name_ = att_name;
value_from_ = value_from;
value_to_ = value_to;
father_ = NULL;
obj_index_ = id_temp;//词条的id
dic_ = dic;//词条相关变更
}
/** @brief
db变更,key desc的增删改
增,att_name_,value_to_, ver_id、修改类型、KeyDbT *db、父节点xpath
删,att_name_,value_from_, ver_id、修改类型、KeyDbT *db、父节点xpath
改,att_name_,value_from_,value_to_, ver_id、修改类型、KeyDbT *db、父节点xpath
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] db 数据库管理对象
@param [in] att_name 属性名
@param [in] value_from 属性修改前的值
@param [in] value_to 属性修改后的值
@param [in] father_xpath 父节点的路径
@return 无
*/
//XmlAction(const EnumXmlActionType type_temp, const int vid,
// KeyDbT *db, const XBaseString &att_name,
// const XBaseString &value_from, const XBaseString &value_to,
// const XBaseString &father_xpath)
// :vid_(kInvalidVersionId), type_(type_temp),
// father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
// att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
// father_from_(NULL), father_to_(NULL),
// dic_(NULL), db_(NULL), father_xpath_(kInvalidString)
//{
// if ((type_temp != kEXAT_db_key_desc_add
// && type_temp != kEXAT_db_key_desc_delete
// && type_temp != kEXAT_db_key_desc_change)
// || vid <= kInvalidVersionId
// || db == NULL)
// {
// type_ = kEXAT_unkown;
// return;
// }
// type_ = type_temp;
// vid_ = vid;
// att_name_ = att_name;
// value_from_ = value_from;
// value_to_ = value_to;
// db_ = db;
// father_xpath_ = father_xpath;
//}
/** @brief kEXAT_father_change 父节点变更
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] obj 属性变更的节点指针
@param [in] father_from 变更前父节点
@param [in] father_to 变更后父节点
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
XmlNodeT *obj, XmlNodeT *father_from, XmlNodeT *father_to)
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if ((type_temp != kEXAT_father_change) //父节点改
|| vid <= kInvalidVersionId
|| obj == NULL)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = kInvalidString;
father_from_ = father_from;
father_to_ = father_to;
father_ = NULL;
obj_index_ = kInvalidIndex;
}
/** @brief 属性删,实际可能没有此需求:ver_id、修改类型、XmlNodeT * obj 、属性名、属性值
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] att_name 属性名
@param [in] obj 属性变更的节点指针
@param [in] value_from 变更前属性值
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
XmlNodeT *obj, const XBaseString &att_name,
const XBaseString &value_from)
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if (type_temp != kEXAT_att_delete //属性删
|| vid <= kInvalidVersionId
|| obj == NULL
|| att_name.length() == 0)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = att_name;
value_from_ = value_from;
value_to_ = kInvalidString;
father_ = NULL;
obj_index_ = kInvalidIndex;
}
/** @brief
节点改:实际无此需求,等价于属性变更、子节点增删
节点增:ver_id、修改类型、XmlNodeT *father、child_index、XmlNodeT *child
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] father 父节点
@param [in] child_index 子节点在父节点中的序号
@param [in] child 子节点
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
XmlNodeT *father, const int obj_index,
XmlNodeT *obj)
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if (type_temp != kEXAT_node_add
|| vid <= kInvalidVersionId
|| obj == NULL
|| father == NULL
|| obj_index < 0
/*|| obj_index >= father->Size()*/)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = kInvalidString;
value_from_ = kInvalidString;
value_to_ = kInvalidString;
father_ = father;
obj_index_ = obj_index;
}
/** @brief
节点删:ver_id、修改类型、XmlNodeT *father、child_index、XmlNodeT *child
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] father 父节点
@param [in] obj_index 子节点在父节点中的序号
@param [in] obj 子节点
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
XmlNodeT *father, XmlNodeT *obj, const int obj_index)//交换obj和obj_index的位置避免构造函数歧义
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if (type_temp != kEXAT_node_delete
|| vid <= kInvalidVersionId
|| obj == NULL
|| father == NULL
|| obj_index < 0
/*|| obj_index > father->Size()*/)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = kInvalidString;
value_from_ = kInvalidString;
value_to_ = kInvalidString;
father_ = father;
obj_index_ = obj_index;
}
/** @brief
节点移除:ver_id、修改类型、XmlNodeT *father、child_index、XmlNodeT *child
@param [in] type_temp 修改类型
@param [in] vid 版本id
@param [in] father 父节点
@param [in] obj_index 子节点在父节点中的序号
@param [in] obj 子节点
@return 无
*/
XmlAction(const EnumXmlActionType type_temp, const int vid,
const int obj_index, XmlNodeT *father, XmlNodeT *obj)//交换参数位置避免构造函数歧义
:vid_(kInvalidVersionId), type_(type_temp),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{
if (type_temp != kEXAT_node_remove
|| vid <= kInvalidVersionId
|| obj == NULL
|| father == NULL
|| obj_index < 0
/*|| obj_index > father->Size()*/)
{
type_ = kEXAT_unkown;
return;
}
type_ = type_temp;
vid_ = vid;
obj_ = obj;
att_name_ = kInvalidString;
value_from_ = kInvalidString;
value_to_ = kInvalidString;
father_ = father;
obj_index_ = obj_index;
}
public:
/** @brief 默认构造函数
@return 无
*/
XmlAction()
:vid_(kInvalidVersionId), type_(kEXAT_unkown),
father_(NULL), obj_index_(kInvalidIndex), obj_(NULL),
att_name_(kInvalidString), value_from_(kInvalidString), value_to_(kInvalidString),
father_from_(NULL), father_to_(NULL),
dic_(NULL)/*, db_(NULL)*/, father_xpath_(kInvalidString)
{}
/** @brief 默认析构函数
@return 无
*/
~XmlAction()
{
//如果采用析构函数实现回滚,就没有返回值
}
/** @brief 拷贝构造函数
@return 无
*/
XmlAction(const XmlAction &t)
:vid_(t.vid_), type_(t.type_),
father_(t.father_), obj_index_(t.obj_index_), obj_(t.obj_),
att_name_(t.att_name_), value_from_(t.value_from_), value_to_(t.value_to_),
father_from_(t.father_from_), father_to_(t.father_to_),
dic_(t.dic_)/*, db_(t.db_)*/, father_xpath_(t.father_xpath_)
{}
/** @brief 拷贝赋值函数
@return 无
*/
XmlAction &operator=(const XmlAction &t)
{
if (this == &t)
{
return *this;
}
vid_ = t.vid_;
type_ = t.type_;
father_ = t.father_;
obj_index_ = t.obj_index_;
obj_ = t.obj_;
att_name_ = t.att_name_;
value_from_ = t.value_from_;
value_to_ = t.value_to_;
father_from_ = t.father_from_;
father_to_ = t.father_to_;
dic_ = t.dic_;
//db_ = t.db_;
father_xpath_ = t.father_xpath_;
return *this;
}
/** @brief 大小比较
@return 是否小于
*/
bool operator<(const XmlAction &t)const
{
return vid_ < t.vid_;
}
public:
/** @brief 单个操作的回滚 */
bool RollBack()
{
if (vid_ == kInvalidVersionId)
{
return false;
}
switch (type_)
{
case kEXAT_unkown: return false;
case kEXAT_att_add: return RollBackAttAdd();
case kEXAT_att_delete: return RollBackAttDelete();
case kEXAT_att_change: return RollBackAttChange();
case kEXAT_node_add: return RollBackNodeAdd();
case kEXAT_node_delete: return RollBackNodeDelete();
case kEXAT_node_remove: return RollBackNodeRemove();
case kEXAT_node_change: return RollBackNodeChange();
case kEXAT_text_change: return RollBackTextChange();
case kEXAT_father_change: return RollBackFatherChange();
//
case kEXAT_word_key_change: return RollBackWordKeyChange();
case kEXAT_word_value_change: return RollBackWordValueChange();
case kEXAT_word_add: return RollBackWordAdd();
case kEXAT_word_delete: return RollBackWordDelete();
case kEXAT_word_change: return RollBackWordChange();
//
//case kEXAT_db_key_desc_add: return RollBackDbAdd();
//case kEXAT_db_key_desc_delete:return RollBackDbDelete();
//case kEXAT_db_key_desc_change:return RollBackDbChange();
//
default: return false;
}
}
private:
//db key desc的回滚
/** @brief db key desc变更的回滚:增 */
//bool RollBackDbAdd()
//{
// if (db_ == NULL)
// {
// return false;
// }
// if (att_name_ == kXmlKeyAttName)
// {
// return db_->DeleteKey(value_to_, father_xpath_);
// }
// if (att_name_ == kXmlDescAttName)
// {
// return db_->DeleteDesc(value_to_, father_xpath_);
// }
// return false;
//}
/** @brief db key desc变更的回滚:删 */
//bool RollBackDbDelete()
//{
// if (db_ == NULL)
// {
// return false;
// }
// if (att_name_ == kXmlKeyAttName)
// {
// return db_->AddKey(value_from_, father_xpath_);
// }
// if (att_name_ == kXmlDescAttName)
// {
// return db_->AddDesc(value_from_, father_xpath_);
// }
// return false;
//}
/** @brief db key desc变更的回滚:改 */
//bool RollBackDbChange()
//{
// if (db_ == NULL)
// {
// return false;
// }
// if (att_name_ == kXmlKeyAttName)
// {
// return db_->ChangeKey(value_to_, value_from_, father_xpath_);
// }
// if (att_name_ == kXmlDescAttName)
// {
// return db_->ChangeDesc(value_to_, value_from_, father_xpath_);
// }
// return false;
//}
private:
/** @brief 语言变更的回滚 */
bool RollBackWordKeyChange()
{
if (dic_ == NULL)
{
return false;
}
//dic_->set_key_name(value_from_);//与RollBackWordValueChange合并
return false;
}
/** @brief 语言变更的回滚 */
bool RollBackWordValueChange()
{
if (dic_ == NULL)
{
return false;
}
dic_->SetCurrentLanguage(value_from_);
return true;
}
/** @brief 词条添加的回滚 */
bool RollBackWordAdd()
{
if (dic_ == NULL)
{
return false;
}
dic_->DeleteGuid(att_name_);//att_name_是guid的值
return true;
}
/** @brief 词条删除的回滚 */
bool RollBackWordDelete()
{
if (dic_ == NULL)
{
return false;
}
dic_->AddStringByGuid(att_name_, value_from_, obj_index_); //TODO 回滚时未考虑GUID重复问题
return true;
}
/** @brief 词条变更的回滚 */
bool RollBackWordChange()
{
if (dic_ == NULL)
{
return false;
}
dic_->ChangeStringByGuid(att_name_, value_from_);
return true;
}
private:
/** @brief 属性添加的回滚 */
bool RollBackAttAdd()
{
if (obj_ == NULL)
{
return false;
}
obj_->DeleteProperty(att_name_);//TODO 派生类属性是否处理?
return true;
}
/** @brief 属性删除的回滚 */
bool RollBackAttDelete()
{
if (obj_ == NULL)
{
return false;
}
obj_->SetProperty(att_name_, value_from_);//TODO 派生类属性是否处理?
return true;
}
/** @brief 属性变更的回滚 */
bool RollBackAttChange()
{
if (obj_ == NULL)
{
return false;
}
obj_->SetProperty(att_name_, value_from_);//TODO 派生类属性是否处理?
return true;
}
/** @brief text变更的回滚 */
bool RollBackTextChange()
{
if (obj_ == NULL)
{
return false;
}
obj_->set_text(value_from_);//TODO 派生类属性是否处理?
return true;
}
/** @brief 父节点变更的回滚 */
bool RollBackFatherChange()
{
if (obj_ == NULL)
{
return false;
}
obj_->set_xml_parent(father_from_);//TODO 派生类属性是否处理?
return true;
}
/** @brief 子节点添加的回滚 */
bool RollBackNodeAdd()
{
if (father_ == NULL)
{
return false;
}
//bool b = father_->DeleteChild(obj_);
bool b = father_->RemoveChild(obj_);//存在移除操作,此处无法分辨是否该释放内存
obj_ = NULL;//TODO 支持回滚后,此处应该delete
return b;
}
/** @brief 子节点删除的回滚 */
bool RollBackNodeDelete()
{
if (father_ == NULL)
{
return false;
}
bool b = father_->AddChild(obj_index_, obj_);
obj_index_ = kInvalidIndex;
obj_ = NULL;
return b;
}
/** @brief 子节点移除的回滚 */
bool RollBackNodeRemove()
{
if (father_ == NULL)
{
return false;
}
bool b = father_->AddChild(obj_index_, obj_);
obj_index_ = kInvalidIndex;
obj_ = NULL;
return b;
}
/** @brief 子节点变更的回滚 */
bool RollBackNodeChange()
{
if (father_ == NULL)
{
return false;
}
return false;//本函数没有实际意义
}
public:
/** @brief 获得操作类型 */
EnumXmlActionType GetActionType()const
{
return type_;
}
/** @brief 获得当前操作对象的指针 */
XmlNodeT* GetObj()
{
return obj_;
}
private:
int vid_; /**< 版本id*/
EnumXmlActionType type_; /**< 操作类型*/
XmlNodeT *father_; /**< 父节点,仅当增删节点时有用*/
int obj_index_; /**< 当前节点在父节点中的序号,从0开始,仅当增删节点时有用*/
XmlNodeT *obj_; /**< 当前变更的节点*/
XBaseString att_name_; /**< 属性名*/
XBaseString value_from_; /**< 修改前的属性值*/
XBaseString value_to_; /**< 修改后的属性值*/
//仅在父节点变更时有效
XmlNodeT *father_from_; /**< 修改父节点时,存储修改前的父节点指针*/
XmlNodeT *father_to_; /**< 修改父节点时,存储修改后的父节点指针*/
//词条变更,key变更、value变更、key-value的增删改(没有remove)
//key变更,value_from_,value_to_,ver_id、修改类型、DicT *dic
//value变更,value_from_,value_to_,ver_id、修改类型、DicT *dic
//key-value增,att_name_,value_to_,ver_id、修改类型、DicT *dic
//删,att_name_,value_from_,ver_id、修改类型、DicT *dic
//改,att_name_,value_from_,value_to_,ver_id、修改类型、DicT *dic
DicT *dic_; /**< 词条变更相关的DictionaryClass类对象,其它信息存储重用其它变量*/
//
//KeyDbT *db_; /**< key desc DB对象*/
XBaseString father_xpath_; /**< 数据库增删改时需要给出父节点路径*/
};
回滚控制类
/** @brief 回滚管理类。
内存中的xml文件回滚,基于XmlNodeT实现。
加入词条回滚。
保存到文件后不支持回滚。
*/
template<class XmlNodeT, class DicT/*, class KeyDbT*/>
class XmlObjectRollBackManager
{
public:
typedef std::set<XmlNodeT*> XmlObjectPointSet;
typedef XmlAction<XmlObject, DicT/*, KeyDbT*/> MyXmlAction;
typedef std::vector<MyXmlAction *> XmlActionVector;
public:
/** @brief 默认构造函数 */
XmlObjectRollBackManager()
{
remember_action_ = false;
//version_id_ = kInValidVersionId;
action_vector_ = new XmlActionVector;
obj_set_ = new XmlObjectPointSet;
}
/** @brief 默认析构函数 */
~XmlObjectRollBackManager()
{
if (this == NULL)
{
return;
}
if (action_vector_)
{
ClearVersionData();
delete action_vector_;
action_vector_ = NULL;
}
if (obj_set_)
{
ClearObjSet();
delete obj_set_;
obj_set_ = NULL;
}
//version_id_ = kInValidVersionId;
}
private:
/** @brief 清空删除节点时临时保存的子节点。外部目前没有此接口需求 */
void ClearObjSet()
{
if (this == NULL)
{
return;
}
for (XmlObjectPointSet::iterator i = obj_set_->begin();
i != obj_set_->end(); ++i)
{
XmlNodeT *p = *i;
delete p;
}
obj_set_->clear();
}
public:
/** @brief db key desc添加
@param [in] db 数据库管理对象
@param [in] att_name 属性名
@param [in] value_to 添加的属性值
@param [in] father_xpath 父节点的xpath
@return 是否成功监控
*/
//bool DbKeyDescAdd(KeyDbT *db, const XBaseString &att_name,
// const XBaseString &value_to, const XBaseString &father_xpath)
//{
// if (this == NULL)
// {
// return false;
// }
// if (remember_action_ == false)
// {
// return true;
// }
// int vid = GetCurrentVersionId() + 1;
// if (action_vector_)
// {
// action_vector_->push_back(new MyXmlAction(kEXAT_db_key_desc_add, vid, db, att_name, "", value_to, father_xpath));
// return true;
// }
// return false;
//}
/** @brief db key desc删除
@param [in] db 数据库管理对象
@param [in] att_name 属性名
@param [in] value_from 删除的属性值
@param [in] father_xpath 父节点的xpath
@return 是否成功监控
*/
//bool DbKeyDescDelete(KeyDbT *db, const XBaseString &att_name,
// const XBaseString &value_from, const XBaseString &father_xpath)
//{
// if (this == NULL)
// {
// return false;
// }
// if (remember_action_ == false)
// {
// return true;
// }
// int vid = GetCurrentVersionId() + 1;
// if (action_vector_)
// {
// action_vector_->push_back(new MyXmlAction(kEXAT_db_key_desc_delete, vid, db, att_name, value_from, "", father_xpath));
// return true;
// }
// return false;
//}
/** @brief db key desc修改
@param [in] db 数据库管理对象
@param [in] att_name 属性名
@param [in] value_from 修改前的属性值
@param [in] value_to 修改后的属性值
@param [in] father_xpath 父节点的xpath
@return 是否成功监控
*/
//bool DbKeyDescChange(KeyDbT *db, const XBaseString &att_name,
// const XBaseString &value_from, const XBaseString &value_to, const XBaseString &father_xpath)
//{
// if (this == NULL)
// {
// return false;
// }
// if (remember_action_ == false)
// {
// return true;
// }
// int vid = GetCurrentVersionId() + 1;
// if (action_vector_)
// {
// action_vector_->push_back(new MyXmlAction(kEXAT_db_key_desc_change, vid, db, att_name, value_from, value_to, father_xpath));
// return true;
// }
// return false;
//}
private:
/** @brief 词条key变更,此函数不应该被调用 */
bool WordKeyChange(DicT *dic, const XBaseString &unuse1,
const XBaseString &unuse2, const XBaseString &unuse3, const int id_temp = kInvalidId)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
return false;//20171222 此函数不应该被调用。词条key变更是指guid变更,应该用其它方式替代。
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_word_key_change, vid, dic, unuse1, unuse2, unuse3, id_temp));
return true;
}
return false;
}
public:
/** @brief 语言变更
@param [in] dic 词条管理对象
@param [in] guid_name 关键字,一般固定为guid
@param [in] lan_from 切换前的语言
@param [in] lan_to 切换后的语言
@param [in] id_temp 词条id,此处无用
@return 是否成功监控
*/
bool WordValueChange(DicT *dic, const XBaseString &guid_name,
const XBaseString &lan_from, const XBaseString &lan_to, const int id_temp = kInvalidId)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
if (lan_from == lan_to)
{
return true;//20171222 减少无效回滚记录,降低内存。
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_word_value_change, vid, dic, guid_name, lan_from, lan_to, id_temp));
return true;
}
return false;
}
/** @brief 词条添加
@param [in] dic 词条管理对象
@param [in] guid_name 关键字,一般固定为guid
@param [in] str_from 修改前的词条
@param [in] str_to 修改后的词条
@param [in] id_temp 词条id,此处无用
@return 是否成功监控
*/
bool WordAdd(DicT *dic, const XBaseString &guid_temp,
const XBaseString &str_from, const XBaseString &str_to, const int id_temp = kInvalidId)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_word_add, vid, dic, guid_temp, str_from, str_to, id_temp));
return true;
}
return false;
}
//词条变更,key变更、value变更、key-value的增删改(没有remove)
//key变更, ver_id、修改类型、DicT *dic
//value变更,att_name_(key),value_from_,value_to_, ver_id、修改类型、DicT *dic
//key-value增,att_name_,value_to_, ver_id、修改类型、DicT *dic
//删,id,att_name_,value_from_, ver_id、修改类型、DicT *dic
//改,att_name_,value_from_,value_to_, ver_id、修改类型、DicT *dic
//const EnumXmlActionType type_temp, const int vid,
// DicT *dic, const XBaseString &att_name,
// const XBaseString &value_from, const XBaseString &value_to, const int id_temp
/** @brief 词条删除
@param [in] dic 词条管理对象
@param [in] guid_name 关键字,一般固定为guid
@param [in] str_from 修改前的词条
@param [in] str_to 修改后的词条
@param [in] id_temp 词条id,回复时需要指定id
@return 是否成功监控
*/
bool WordDelete(DicT *dic, const XBaseString &guid_temp,
const XBaseString &str_from, const XBaseString &str_to, const int id_temp)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_word_delete, vid, dic, guid_temp, str_from, str_to, id_temp));
return true;
}
return false;
}
/** @brief 词条修改
@param [in] dic 词条管理对象
@param [in] guid_name 关键字,一般固定为guid
@param [in] str_from 修改前的词条
@param [in] str_to 修改后的词条
@param [in] id_temp 词条id,回复时需要指定id
@return 是否成功监控
*/
bool WordChange(DicT *dic, const XBaseString &guid_temp,
const XBaseString &str_from, const XBaseString &str_to, const int id_temp = kInvalidId)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
if (str_from == str_to)
{
return true;//20171222 减少无效回滚记录,降低内存。
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_word_change, vid, dic, guid_temp, str_from, str_to, id_temp));
return true;
}
return false;
}
public:
/** @brief 属性增:XmlNodeT * obj 、属性名
@param [in] obj 属性变更的节点指针
@param [in] att_name 属性名
@return 是否成功监控
*/
bool AddAtt(XmlNodeT *obj, const XBaseString &att_name)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_att_add, vid, obj, att_name));
return true;
}
return false;
}
/** @brief 属性改:XmlNodeT *obj 、属性名、修改前的属性值、修改后的属性值
@param [in] obj 属性变更的节点指针
@param [in] att_name 属性名
@param [in] value_from 属性修改前的值
@param [in] value_to 属性修改后的值
@return 是否成功监控
*/
bool ChangeAtt(XmlNodeT *obj, const XBaseString &att_name,
const XBaseString &value_from, const XBaseString &value_to)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
if (value_from == value_to)
{
return true;//20171222 减少无效回滚记录,降低内存。
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_att_change, vid, obj, att_name, value_from, value_to));
return true;
}
return false;
}
/** @brief 属性text改:XmlNodeT *obj 、属性名、修改前的属性值、修改后的属性值
@param [in] obj 属性变更的节点指针
@param [in] att_name 属性名
@param [in] value_from 属性修改前的值
@param [in] value_to 属性修改后的值
@return 是否成功监控
*/
bool ChangeText(XmlNodeT *obj, const XBaseString &value_from, const XBaseString &value_to)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
if (value_from == value_to)
{
return true;//20171222 减少无效回滚记录,降低内存。
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_text_change, vid, obj, kXmlTextAttName, value_from, value_to));
return true;
}
return false;
}
/** @brief father改:XmlNodeT *obj 、修改前的father、修改后的father
@param [in] obj 属性变更的节点指针
@param [in] father_from 变更前父节点
@param [in] father_to 变更后父节点
@return 是否成功监控
*/
bool ChangeFather(XmlNodeT *obj, XmlNodeT *father_from, XmlNodeT *father_to)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
if (father_from == father_to)
{
return true;//20171222 减少无效回滚记录,降低内存。
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_father_change, vid, obj, father_from, father_to));
return true;
}
return false;
}
/** @brief 属性删:XmlNodeT * obj 、属性名、属性值
@param [in] obj 属性变更的节点指针
@param [in] att_name 属性名
@param [in] value_from 变更前属性值
@return 是否成功监控
*/
bool DeleteAtt(XmlNodeT *obj, const XBaseString &att_name,
const XBaseString &value_from)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_att_delete, vid, obj, att_name, value_from));
return true;
}
return false;
}
/** @brief
节点改:实际无此需求,等价于属性变更、子节点增删
节点增:XmlNodeT *father、child_index、XmlNodeT *child
@param [in] father 父节点
@param [in] obj_index 子节点在父节点中的序号
@param [in] obj 子节点
@return 是否成功监控
*/
bool AddNode(XmlNodeT *father, const int obj_index, XmlNodeT *obj)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_node_add, vid, father, obj_index, obj));
XmlObjectPointSet::iterator iter_temp = obj_set_->find(obj);
if (iter_temp != obj_set_->end())
{
obj_set_->erase(iter_temp);
}
return true;
}
return false;
}
/** @brief 节点删:XmlNodeT *father、child_index、XmlNodeT *child
@param [in] father 父节点
@param [in] obj_index 子节点在父节点中的序号
@param [in] obj 子节点
@return 是否成功监控
*/
bool DeleteNode(XmlNodeT *father, const int obj_index, XmlNodeT *obj)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_node_delete, vid, father, obj, obj_index));
obj_set_->insert(obj);
return true;
}
return false;
}
/** @brief 节点移除:XmlNodeT *father、child_index、XmlNodeT *child
@param [in] father 父节点
@param [in] obj_index 子节点在父节点中的序号
@param [in] obj 子节点
@return 是否成功监控
*/
bool RemoveNode(XmlNodeT *father, const int obj_index, XmlNodeT *obj)
{
if (this == NULL)
{
return false;
}
if (gOpenCurrentRollBack == false)
{
return false;
}
if (remember_action_ == false)
{
return true;
}
int vid = GetCurrentVersionId() + 1;
if (action_vector_)
{
action_vector_->push_back(new MyXmlAction(kEXAT_node_remove, vid, obj_index, father, obj));
obj_set_->insert(obj);
return true;
}
return false;
}
public:
/** @brief 回滚到指定版本号(不含)
@param [in] version_id 指定版本号
@return 是否成功回滚
*/
bool RollbackTo(const int version_id)
{
if (this == NULL)
{
return false;
}
if (version_id < -1 || version_id >= (int)(action_vector_->size()))
{
return false;//版本号取值范围为[0, action_vector_->size() - 1]
}
bool b = remember_action();
set_remember_action(false); //回滚时必须关闭操作记录
for (int i = (int)(action_vector_->size() - 1); i > version_id; --i)
{
//倒序回滚
MyXmlAction *p = action_vector_->at(i);
if (p)
{
EnumXmlActionType type_temp = p->GetActionType();
XmlNodeT *pobj = p->GetObj();
p->RollBack();
switch (type_temp)
{
case kEXAT_node_add:
obj_set_->insert(pobj);
break;
case kEXAT_node_delete:
case kEXAT_node_remove:
{
XmlObjectPointSet::iterator iter_temp = obj_set_->find(pobj);
if (iter_temp != obj_set_->end())
{
obj_set_->erase(iter_temp);
}
//obj_set_->erase(obj_set_->find(pobj));
break;
}
default:
break;
}
delete p;
}
else
{
return false;//TODO log
}
}
set_remember_action(b);
if (action_vector_->size() > 0)
{
int off_temp = version_id + 1;
if (off_temp >= 0 && off_temp < (int)action_vector_->size())
{
action_vector_->erase(action_vector_->begin() + off_temp, action_vector_->end());
//action_vector_->erase(action_vector_->begin() + version_id + 1, action_vector_->end());
}
}
return true;
}
/** @brief 回滚所有操作
@return 是否成功回滚
*/
bool RollbackAll()
{
if (this == NULL)
{
return false;
}
return RollbackTo(kInvalidVersionId);
}
/** @brief 获得当前版本号
@return 当前版本号
*/
int GetCurrentVersionId()
{
if (this == NULL)
{
return kInvalidVersionId;
}
//return version_id_;
return ((int)(action_vector_->size())) - 1;
}
/** @brief 清空部分操作记录
@param [in] version_id 指定版本号
@return 是否成功
*/
bool ClearVersionDataFromVersion(const int version_id)
{
if (this == NULL)
{
return false;
}
//清空范围(version_id, action_vector_->size()),首尾均不包含
if (version_id < -1 || version_id >= (int)(action_vector_->size()))
{
return false;//版本号取值范围为[0, action_vector_->size() - 1]
}
for (int i = ((int)(action_vector_->size())) - 1; i > version_id; --i)
{
//倒序清空
MyXmlAction *p = action_vector_->at(i);
if (p)
{
switch (p->GetActionType())
{
case kEXAT_node_delete:
{
XmlNodeT *pobj = p->GetObj();
delete pobj;
XmlObjectPointSet::iterator iter_temp = obj_set_->find(pobj);
if (iter_temp != obj_set_->end())
{
obj_set_->erase(iter_temp);
}
//obj_set_->erase(obj_set_->find(pobj));
break; //内存此时释放
}
case kEXAT_node_remove:
{
XmlNodeT *pobj = p->GetObj();
XmlObjectPointSet::iterator iter_temp = obj_set_->find(pobj);
if (iter_temp != obj_set_->end())
{
obj_set_->erase(iter_temp);
}
//obj_set_->erase(obj_set_->find(pobj));
break; //内存此时释放
}
default: break;
}
delete p;
}
else
{
return false;//TODO log
}
}
if (action_vector_->size() > 0)
{
int off_temp = version_id + 1;
if (off_temp >= 0 && off_temp < (int)action_vector_->size())
{
action_vector_->erase(action_vector_->begin() + off_temp, action_vector_->end());
//action_vector_->erase(action_vector_->begin() + version_id + 1, action_vector_->end());
}
}
return true;
}
/** @brief 清空所有记录
@return 是否成功
*/
bool ClearVersionData() /**< 清空所有记录*/
{
if (this == NULL)
{
return false;
}
return ClearVersionDataFromVersion(kInvalidVersionId);
}
public:
/** @brief 设置是否监控
@param [in] remember_action 是否监控
@return 无
*/
void set_remember_action(const bool remember_action = true)
{
if (this == NULL)
{
return;
}
remember_action_ = remember_action;
}
/** @brief 返回当前是否正在监控
@return 是否正在监控
*/
bool remember_action()const
{
if (this == NULL)
{
return false;
}
return remember_action_;
}
protected:
private:
//int version_id_; /**< 版本号*/
bool remember_action_; /**< 是否记录操作,一般读取sdc文件时都不记录*/
XmlActionVector *action_vector_; /**< 操作序列*/
XmlObjectPointSet *obj_set_; /**< 移除删除添加节点操作时临时保存指针,处理内存泄露问题*/
private:
std::stack<int> step_stack_; /**< step栈,方便外部按复合操作回滚。一个复合操作可能对应几十个原子操作。*/
public:
/** @brief step栈是否为空
@return 是否为空
*/
bool IsStepStackEmpty()const
{
return step_stack_.empty();
}
/** @brief 保存当前步骤的版本号
@return 当前版本号
*/
int PushStep()
{
int ver_id_temp = GetCurrentVersionId();
step_stack_.push(ver_id_temp);
return ver_id_temp;
}
/** @brief 回滚一个复合操作
@return 是否成功
*/
bool PopStep()
{
if (IsStepStackEmpty())
{
return false;//step栈为空
}
int ver_id_temp = step_stack_.top();
step_stack_.pop();
return RollbackTo(ver_id_temp);
}
};
控制进程内全局唯一对象的方法
头文件
typedef XmlObjectRollBackManager<XmlObject, DictionaryClass> XmlNodeRBM;
extern X_EXPORT XmlNodeRBM *gCurrentRollBack; /**< 内存操作监控、词条增删改监控*/
extern X_EXPORT bool gOpenCurrentRollBack/* = false*/; /**< 简化关闭全局回滚模块,大量代码需要快速关闭快速打开*/
cpp中定义
/* 内存操作监控、词条增删改监控。
进程内唯一对象指针,多个XmlParse对象时,在一个时刻只能指向一个。*/
//CAXB_EXPORT XmlObjectRollBackManager<XmlObject, DictionaryClass/*, KeyManager*/> *gCurrentRollBack = NULL;
X_EXPORT XmlNodeRBM *gCurrentRollBack = NULL;
X_EXPORT bool gOpenCurrentRollBack = false;
以上未考虑多文件,加入支持多文件应该类似,在xml节点中存储回滚管理类对象指针。