第一部分:abiWord中数据的存储一、哈希表是根据一定的算法在表中的相应位置存储数据的一种容器,在abiWord程序中这个表相当于一个数组。下面列出了ABIWORD哈希表的构成.在列举出表的构成之后,对于构成哈希表的各个组成部分分别进行说明.
1、abiWord中的哈希表template <class T>class ABI_EXPORT UT_GenericStringMap{ //嵌套类UT_Cursor{ …}//代表实际存储的容器hash_slot<T> * m_pMapping;}; //哈希表中键的封装类class key_wrapper{ private: UT_String m_val; //键 UINT m_hashval; //键的哈希值}; // 数据的槽template<class T>class hash_slot{ ………… T m_value; key_wrapper m_key;}; 以上就是一上哈希表的构成我们可以看出从大的结构上看,有四个部分:1、主要类UT_GenericStringMap<class T>:这个类主要实现了基本的哈希表的算法如查找,插入、删除等。2、UT_Cursor:这个类主要是用来遍历哈希表的,我们可以把它理解为一个迭代器.3、Key_wrapper:这个类是哈希表中键的封闭类,相当于一个字符串,它内部包含一个相应于该键值(字符串)的哈希值。4、Hash_slot<class T>这个类用来表示哈希表中存储的键 / 值对,可以理解为哈希表中所存储的元素类型(类型是键 / 值对).注:对于上面的这个数据模型,其实是哈希表与映射表的结合.对于这个类更具体的说明请参考UT_GenericStringMap<T>类UT_GenericStringMap<T>类是一个映射表类
数据成员private: hash_slot<T> * m_pMapping; size_t n_keys; size_t n_deleted; size_t m_nSlots; size_t reorg_threshold; size_t flags; char ** m_list;这个类的友元类 hash_slot<T>,顾名思义,hash_slot<T>是一个(哈希槽)模板类,是UT_GenericStringMap<T>的一个成员,所以类型参数T由传给UT_GenericStringMap<T>的T类型参数决定。hash_slot<T>的成员public:T m_value; key_wrapper m_key; 1、UT_Cursor类(游标)这个类是UT_GenericStringMap<T> 的内嵌友元类,它只有一个成员即private:const UT_GenericStringMap<T> * m_d;int m_index; 这个函数成员函数:inline const UT_String & key(){ return m_d->_key(*this);} inline void make_deleted(){ m_d->_make_deleted(*this);}inline const T first(){ return m_d->_first(*this);} inline const T next(){ return m_d->_next(*this);}inline const T prev(){ return m_d->_prev(this);}inline bool is_valid(){ return (m_index != -1);} private:inline void _set_index(int i ) { m_index = i; }inline int _get_index() { return m_index; }可以看出这个类的公有成员函数:const UT_String & key()调用其成员UT_GenericStringMap<T> m_d的相应成员函数 _key(*this);并以其自身this指针为参数.void make_deleted()调用其成员UT_GenericStringMap<T> m_d的相应成员函数m_d->_make_deleted(*this);并以其自身this指针为参数.const T first();调用其成员UT_GenericStringMap<T> m_d的相应成员函数m_d->_first(*this);并以其自身this指针为参数.const T next();调用其成员UT_GenericStringMap<T> m_d的相应成员函数m_d->_next(*this);并以其自身this指针为参数.const T prev();调用其成员UT_GenericStringMap<T> m_d的相应成员函数m_d->_prev(*this);并以其自身this指针为参数.bool is_valid();只是判断m_index成员是否等于-1,如果等于-1则是无效的,否则是有效的。_set_index(int i )和_get_index()分别是设置和获取m_index成员的值。 2、key_wrapper这个类封闭了一个字符串类UT_String ;其成员包括:private: UT_String m_val; //一个字符串类 UINT m_hashval; //该字符串的散列值成员函数:Public:void die() { m_val.clear(); } bool eq(const char * key) { return (m_val == key); } bool eq(const UT_String & key)const { return (m_val == key); } void operator=(const UT_String & k) { m_val = k; }UINT hashval()const { return m_hashval; } void set_hashval(UINT h) { m_hashval = h; } UT_String & value() { return m_val; } void operator=(const key_wrapper & rhs) { m_val = rhs.m_val;m_hashval = rhs.m_hashval; } static UINT computer_hash(const UT_String & key) { return hashcode(key); }static UINT compute_hash(const char * key) { return hashcode(key); }可以看出基本操作都是针对UT_String类型进行操作。Void die();调用UT_String::Clear()清空缓冲区。 Bool eq(const char * key);调用UT_String::operator==(char * key);判断UT_String成员m_val是否和参数相等。 bool eq(const UT_String & key)const重载版本参数为UT_String类型的常量引用。 void operator=(const UT_String & k);以参数对成员m_val赋值. UINT hashval()const; 返回成员m_hashval的值. void set_hashval(UINT h);设置成员m_hashval的值. UT_String & value();返回成员m_val的值。void operator=(const key_wrapper & rhs);以参数对两个成员赋值. static UINT computer_hash(const UT_String & key);static UINT compute_hash(const char * key); 两个重载函数,都是调用全局函数hashcode();3、hash_slot<T>哈希槽:成员变量:T m_value;key_wrapper m_key; 成员函数:void make_deleted(){ m_value = reinterpret_cast<T>(this);m_key.die();} void make_empty(){ m_value = 0;}const T value() const {return m_value;}void insert(const T v , const UT_String & k ,UINT h){ m_value = v;m_key = k;m_key.set_hashval(h);}void assign(hash_slot<T> * s){ m_value = s->value();m_key = s->m_key;}bool empty()const{ return (m_value == 0);}bool deleted()const{ return static_cast<const void *>(this) == m_value;} 成员T m_value和key_wrapper m_key,就代表了在UT_GenericStringMap<T> 中存储的两个东西,一个是实值m_value,另一个是键值m_key; bool key_eq(const UT_String & test , size_t)const{ return m_key.eq(test);}bool key_eq(const char * test , size_t h)const{ return m_key.eq(test);} 4、UT_GenericStringMap<T>类的说明:UT_GenericStringMap<T>类代表了一个映射表类,它内嵌一个UT_Cursor类,同时以一个hash_slot<T> 类型作为它的一个成员。template <class T> hash_slot<T>*UT_GenericStringMap<T>::find_slot(const char *k, SM_search_type search_type, size_t& slot, bool& key_found, size_t& hashval, const void* v, bool* v_found, void* vi, size_t hashval_in) const{ 这个函数参数的说明SM_search_type search_type //一个枚举类型的值,可以是SM_LOOLUP,SM_INSERT,SM_REORG.Size_t & slot,哈希槽,即位置Bool & key_found, 输出参数,是否发现了这个键Size_t & hashval, 哈希值Const void * v,实值Bool v_found,实值是否被发现。Void * vi,Size_t hashval_in, 输入的哈希值。 二、用来存储属性/样式的类PP_AttrProp PP_AttrProp的基本定义如下:class ABI_EXPORT PP_AttrProp{ public://char ** 像main()函数中的argv ,它包含了名子/值对的多重设置,//名字是在偶数单元 ; 值是在体奇数单元 , 列表是通过一个null名字终止…… bool setAttributes(const char ** attributes); bool setAttributes(const UT_GenericVector<char*> * pVector); bool setProperties(const char ** properties); bool setProperties(const UT_GenericVector<char*> * pVector); …… //主要的数据定义typedef UT_Pair<const char*,const PP_PropertyType *> PropertyPair;UT_GenericStringMap<char*> * m_pAttributes; // of char*UT_GenericStringMap<PropertyPair*> * m_pProperties; // of PropertyPair bool m_bIsReadOnly; UT_uint32 m_checkSum; UT_uint32 m_index; //$HACK };从这个类的定义中我们可以找出几个关键的数据类型:1、typedef UT_Pair<const char * , PP_PropertyType *> PropertyPair;这个typedef语句的作用也就是以类型参数 const char *和PP_PropertyType*实例化一个类模板.之后PropertyPair就代表了UT_Pair<const char * ,PP_PropertyType *>这种类型.2、UT_GenericStringMap<PropertyPair *> m_pProperties;声明了一个对象,这个对象是一个哈希表,并且这个这个哈希表中所存储的元素类型为PropertyPair类型的指针.UT_GenericStringMap<PropertyPair*>*m_pProperties;(哈希表)内部有hash_slot类型的指针用来存储键/值对 以字节为单位的文本数据 template <class T, class U>class UT_Pair{ ……private:const T m_first;const U m_second;}这是一个模板类.PP_AttrProp中又有这样的声明:typedef UT_Pair<const char*,const PP_PropertyType *> PropertyPair;这个类型定义语句,定义了一个新的类型即把UT_Pair类模板的模板参数设置为const char * ,和const PP_PropertyType *; 3、UT_GenericStringMap<char *> m_pAttributes. 声明了一个对象,这个对象是一个哈希表,并且这个哈希表中所存储的元素类型为char *.再看template<class T> class hash_slot类模板,它里面有一个模板参数 T m_value.这时我们就更加能够看明白,在哈希表中所存储的内容了.OK,至此我们可以看出,PP_AttrProp类中有个哈希表的数据成员,而这个哈希表就是解析出关键字的以及关键字所对应的值的存储地。而PP_AttrProp中所有的方法即成员函数无非是对这个哈希表进行操作如设置一个PP_AttrProp类型对象的值即填充内容。对哈希表中值进行查找等等。在这里先说明一下,其实一个PP_AttrProp就代表了一个相同类型的属性集合,也就是说,同一个事物的各种属性的一个集合,针对RTF文件,我们知道,文本是构成RTF文件的一种数据元素,而针对一段文本,它必定要有字体属性,字体属性包括如字号,字体颜色,字体字符集等不同特征,而这一个PP_AttrProp即是这个字体的所有特征的一个集合。对于Word中的RTF文件它有section(节),我们也可以把考虑为一种数据类型。那么这个PP_AttrProp既然是构成RTF文件的同种类型数据(在这里数据指的是实际的文件内容比如”文本”,RTF中的”图像数据”,以及”表格”、”页㞒”、”页脚”等等)的各种属性的集合,它本身在abiWord这个项目中充当何种角色呢?继续往上走我们看到了pp_TableAttrProp这个类。二、pp_TableAttrProp:首先给出类的基本定义。class ABI_EXPORT pp_TableAttrProp{ public:………… bool addAP(PP_AttrProp * pAP, UINT * pSubscript); bool createAP(UINT * pSubscript); bool createAP(const char ** attributes, const char ** properties, UINT * pSubscript); bool createAP(const UT_GenericVector<char*>* pVector, UINT * pSubscript); bool findMatch(const PP_AttrProp * pMatch, UINT * pSubscript) const; const PP_AttrProp * getAP(UINT subscript) const; void ReleaseVec(); protected:UT_GenericVector<PP_AttrProp *> m_vecTable;UT_GenericVector<PP_AttrProp *> m_vecTableSorted;};先看数据成员,只有两个:1、UT_GenericVector<PP_AttrProp *> m_vecTable;2、UT_GenericVector<PP_AttrProp *> m_vecTableSorted;很明显是两个向量即vector,在这里我们可以把它看成数组,数组的元素类型是PP_AttrProp对象的指针。由此我们可以抽象出:pp_TableAttrProp代表一个数组,(尽管它内部有两个vector,我们先不考虑m_vecTableSorted)数组中的元素类型是PP_AttrProp对象的指针。根据组成RTF文件的结构,我们知道,一个PP_AttrProp可能是一段文本字体的特征集合(以后统称属性),也可以是section(节)的属性,或者是paragraph(段落)的属性,当然还有其它的属性。总之不管是什么东西的属性,我们就以一个PP_AttrProp来表示一个事物(以后统称数据元素)的所有属性的集合。PP_AttrProp本身可以看成是一个哈希表。至此我们知道PP_AttrProp是一个哈希表,一个哈希表中存放的是RTF文件中某种具有相同属性的数据元素块的属性集合。比如一段具有相同属性的文本。它的所有属性就用一个PP_AttrProp来描述。而pp_TableAttrProp则是元素类型为PP_AttrProp对象指针的一个数组。自然数组中的元素可以描述一段文本的属性、一个段落的属性或者是一个section的属性。简而言之,pp_TableAttrprop是一个数组,数组中的元素类型是一个哈希表。备注:(引自abiWord程序代码的注释)pp_TableAttrProp实现了一个无限大的pp_AttrProp对象的表,每一个PP_AttrProp代表了文档的一个或多个片断的完整的Attribute/Property状态通过程序代码我们会发现这个pp_Attrprop本身又被包含在一个pt_VarSet的类中,下面我们来看看这个类。三、pt_VarSet类的说明:class ABI_EXPORT pt_VarSet{ public:…… ……//如果pts == PTS_Editing m_currentVarSet = 1;void setPieceTableState(PTState pts);//将pBuf到缓冲区bool appendBuf(const UINT *pBuf , UINT length , UINT * pbi); // bool storeAP(const char ** attributes, UINT * papi); bool storeAP(const UT_GenericVector<XML_Char*>* pVecAttributes, PT_AttrPropIndex * papi); // const UT_UCSChar * getPointer(PT_BufIndex bi) const ; PT_BufIndex getBufIndex(PT_BufIndex bi, UT_uint32 offset) const; const PP_AttrProp * getAP(PT_AttrPropIndex api) const;private:UINT _subscriptFromBufIndex(PT_BufIndex bi) const;UT_uint32 _subscriptFromAPIndex(PT_AttrPropIndex api) const;UT_uint32 _varsetFromBufIndex(PT_BufIndex bi) const;inline UT_uint32 _varsetFromAPIndex(PT_AttrPropIndex api) const; inline PT_AttrPropIndex _makeAPIndex(UT_uint32 varset, UT_uint32 subscript) const; bool _finishConstruction(void); bool m_bInitialized; UT_uint32 m_currentVarSet; //下面这两个数组变量的类型是两个对象,每个数组的大小为2,那么下标0代表装入文档的//文本数据,下标1代表编辑时插入的数据. UT_GrowBuf m_buffer[2]; pp_TableAttrProp m_tableAttrProp[2];};先看数据成员:UT_GrowBuf m_buffer[2];pp_TableAttrProp m_tableAttrProp[2];结合程序代码的分析。我们可以这样理解。在pt_VarSet这个类中m_buffer用来存储RTF文件中的文本即字符,m_tableAttrProp用来存储RTF文件中不同属性的数据元素的属性表即哈希表。在这里数据元素代表文本、段落或者是section等等。至此我们可以看出如果对于一个RTF文件来说,其主要的数据存储位置我们基本上已经可以确定了。备注:对于图像文件来说,它的图像数据存储是否也在这个m_buffer中,还有待进一步考证。当然如果是在这个m_buffer中,也可以实现。
m_buffer[0]即UT_GrowBuf
说明:至于m_buffer和m_tableAttrprop数组的大小均为2,原因是0代表读取的数据,1代表编辑时插入的数据。具体到这两个数据成员上m_buffer[0]中存储的是从RTF文件中读取的数据,m_buffer[1]中存储的是用户在编辑阶段插入到文件中的数据。m_tableAttrprop[0]存储的是从文件中读取的数据元素的属性。m_tableAttrProp[1]中存储的是用户在编辑阶段插入的数据元素的属性。
m_tableAttrProp[0]中所存储的内容pp_TableAttrProp
m_vecTable[0]类型为UT_GenericVector,相当于数组 0号元素:PP_AttrProp *(某个section属性) 1号元素段落属性 …………省略 2号元素文本属性 …………省略第二部分:abiWord中数据的关联.对这一部分的说明,采取主要以数据类型为”文本”的特例来说明.一、pf_Frag类的说明******************************************************************************ppf_Frag代表了一个文档片断, 这可以是文本 (pf_Frag_Text),一个内置对象如一个图像(pf_Frag_object) ,或者结构信息例如一个段落或章节(pf_Frag_Strux_Block , pf_Frag_Strux_Section)pf_Frag是一个抽象基类,我们使用一个枚举成员类型,胜于使用任何运行时原料*******************************************************************************class ABI_EXPORT pf_Frag{ public:typedef enum _PFType {PFT_Text = 0 , PFT_Object , PFT_Strux , PFT_EndOfDoc , PFT_FmtMark}PFType;pf_Frag(pt_PieceTable * pPT , PFType type , UINT length); virtual ~pf_Frag();//返回pf_Frag所代表的类型,正如上面的结构体所描述的类型.inline PFType getType(void)const {return m_type;}//从下面这个函数的功能(返回自身类型的指针,可以看出,这个对象可能被用于链表等数据存储方式)//这个功能有助于类似链表的数据结构进行查找和遍历. inline pf_Frag * getNext(void)const {return m_next;} inline pf_Frag * getPrev(void)const {return m_prev;} pf_Frag * setNext(pf_Frag * pNext); pf_Frag * setPrev(pf_Frag * pPrev); inline UINT getLength(void)const {return m_length;} pt_PieceTable * getPieceTable() {return m_pPieceTable;} fd_Field * getField(); PT_DocPosition getPos(void) const {return m_docPos;} void setPos(PT_DocPosition pos)const{m_docPos = pos;} //这个序号是代表Attr/Prop的序号,Attr/Prop存储在一个vector中 inline PT_AttrPropIndex getIndexAP(void) const {return m_indexAP;} virtual void setIndexAP(PT_AttrPropIndex indexNewAP){m_indexAP = indexNewAP;} //比较两个fragments的内容,忽略格式 bool isContentEqual(const pf_Frag & f2)const; UINT getXID()const {return m_iXID;} void setXID(UINT xid){m_iXID = xid;} // I would much prefer if this was a pure vitual, but we do not have Eod fragvirtual bool usesXID() const {return false;}// compare contents and format of two fragmentsbool operator == (const pf_Frag & f2) const; protected: virtual bool _isContentEqual(const pf_Frag & f2)const {return true;} PFType m_type; //该fragment所代表的类型 UINT m_length; //大小 pf_Frag * m_next; //下一个 pf_Frag * m_prev; //上一个 fd_Field * m_pField; pt_PieceTable * m_pPieceTable; PT_AttrPropIndex m_indexAP; private: mutable PT_DocPosition m_docPos; UINT m_iXID; };这个类是所有pf_Frag_xxxx派生类的基类,从它内部的数据成员我们可以看出它存储于一个链表类型的数据结构中,这个类代表代表了一个文档片断, 这个片断可以是文本 (pf_Frag_Text)、一个内置对象如一个图像(pf_Frag_object) ,或者结构信息例如一个段落或章节(pf_Frag_Strux_Block , pf_Frag_Strux_Section)。那么它是如何把一个数据片断与这个数据片断所对应的属性相对应起来的呢 ? 我们从它内部的数据成员PT_AttrPropIndex m_indexAP和 PT_DocPosition m_docPos;这两个变量身上似乎可以找到答案。第一个代表属性表在缓冲区的索引,第二个是它在文档中的位置。因为这个类是所有pf_Frag_XXX类的基类,所以如果针对一个文本,我们并不能从中看出它的对应关系。因为不管哪个派生类,只要你是RTF文件的数据元素,那么你都有属性,所以属性序号即PT_AttrPropIndex是所有派生类必须有的。所以把这个定义在了基类。至于PT_DocPosition,我们可以看出它为私有成员,那么派生类是无法访问到的。这个变量代表文档的绝对位置。可以猜想是用来为写文件准备的的。暂时不关心。接下来我们看它的一个派生类:代表文本片断的pf_Frag_Text.二、pf_Frag_Text类的说明:/******************************************************************************pf_Frag_text代表了文档的文本片断,注意它没有包含一个PT_DocPostion,文本片断不知道这是在文档中;它只知道它的缓冲区位置class ABI_EXPORT pf_Frag_Text : public pf_Frag{ public: pf_Frag_Text(pt_PieceTable * pPT, UINT bufIndex, UINT length, UINT indexAP, fd_Field * m_pField); virtual ~pf_Frag_Text(); endFragOffset}const;inline PT_BufIndex getBufIndex(void) const { return m_bufIndex; } void changeLength(UINT newLength); void adjustOffsetLength(PT_BufIndex bi , UINT newLength); void setField(fd_Field * pField); virtual bool usesXID() const {return false;}protected: virtual bool _isContentEqual(const pf_Frag & f2)const; UINT m_bufIndex;}; 一眼就能看出最后一个数据成员的定义UINT m_bufIndex;顾名思义,缓冲区的索引。这就是在pt_VarSet中m_buffer[0]中的索引,联系到这个类的基类pf_Frag中的PT_AttrPropIndex m_indexAP;定义,我们已经找到了文本数据与它的属性的联系方式,通过一个pf_Frag对象来保存。下面对pf_Frag的其它派生类进行简要说明:在pf_Frag中定义了一个枚举类型的数据类型:typedef enum _PFType {PFT_Text = 0 , PFT_Object , PFT_Strux , PFT_EndOfDoc , PFT_FmtMark}PFType;PFT_Test:文本PFT_Object:对象如图片PFT_Strux:Strux类型,所有从pf_Frag_Strux派生的类.PFT_EndOfDoc:暂时不予讨论.PFT_FmtMark:暂时不予讨论. 我们主要讨论pf_Frag_Strux这个类三、pf_Frag_Strux类的说明:pf_Frag_Strux代表了文档中的结构信息(例如一个段落或章节).pf_Frag_Strux从pf_Frag派生,但它也是一个基类,pf_Frag_strux_Block和pf_Frag_Strux_Section从它派生.我们使用一枚举以记住Strux的类型。 class ABI_EXPORT pf_Frag_Strux : public pf_Frag{ public :pf_Frag_Strux(pt_PieceTable * pPT, PTStruxType struxType, UINT length, PT_AttrPropIndex indexAP); virtual ~pf_Frag_Strux(); PTStruxType getStruxType(void) const; const void * getFmtHandle(UINT lid)const; bool setFmtHandle(UINT lid , const void * sfh); virtual bool usesXID()const; bool isMatchingType(PTStruxType e)const; bool isMatchingType(const pf_Frag * p)const; protected: virtual bool _isContentEqual(const pf_Frag & f2)const; PTStruxType m_struxType; UT_Vector m_vecFmtHandle;}; PTStruxType m_struxType;这个定义了一种类型,即该Strux对象所代表的类型它的定义如下://PTStruxType标识了FragStrux的子类型typedef enum _PTStruxType{ PTX_Section = 0, // 0 section(节) PTX_Block, // 1 paragraph(段落) PTX_SectionHdrFtr, // 2 (页眉页脚) PTX_SectionEndnote, // 3 (脚注) PTX_SectionTable, // 4 PTX_SectionCell, // 5 PTX_SectionFootnote, // 6 PTX_SectionMarginnote, // 7 PTX_SectionFrame, // 8 PTX_SectionTOC, // 9 PTX_EndCell, // 10PTX_EndTable,PTX_EndFootnote,PTX_EndMarginnote,PTX_EndEndnote,PTX_EndFrame,PTX_EndTOC,PTX_StruxDummy} PTStruxType;那么所有的pf_Frag_Strux的派生类所代表的类型均以在这个枚举型的数据中列出. 下面列出了pf_Frag_Strux_Section类的定义:一个pf_Frag_Strux_Section代表文档中一个节(区域)的结构信息。 class ABI_EXPORT pf_Frag_Strux_Section : public pf_Frag_Strux{ public:pf_Frag_Strux_Section(pt_PieceTable * pPT , PT_AttrPropIndex indexAP);virtual ~pf_Frag_Strux_Section();};由这个类的定义可以看出,是非常简单的。它只是简单的继承了基类的数据成员和方法,自己本身并没有新的数据成员和方法。也就是说父类的数据成员足以描述一个文档中的节(区域)。 同样,其它由pf_Frag_Strux派生的类,分别代表组成文档的不同数据元素。在这里就不一一列出了。既然pf_Frag类的派生类是链表的一个节点,那么它肯定要有一个链表类来管理这些节点,实现对这些节点一些操作如排序、插入、删除、查找等等。在abiWord中的这个链表类是pf_Fragments类,下面我们了解一下这个类的定义:首先给出定义:四、pf_Fragments类的说明:class ABI_EXPORT pf_Fragments{ public: pf_Fragments(); ~pf_Fragments(); void appendFrag(pf_Frag * pf); void insertFrag(pf_Frag * pfPlace , pf_Frag * pfNew); void insertFragBefore(pf_Frag * pfPlace , pf_Frag * pfNew); void unlinkFrag(pf_Frag * pf); void cleanFrags(void) const; pf_Frag * getNthFrag(UINT nthFrag)const; pf_Frag * findFirstFragBeforePos(PT_DocPosition)const; UINT getNumberOfFrags()const; UINT getFragNumber(const pf_Frag * pf)const; pf_Frag * getFirst()const; pf_Frag * getLast()const; void setFragsDirty(void){ m_bAreFragsClean = false;} bool areFragsDirty () const {return !m_bAreFragsClean;} #ifdef PT_TEST void __dump(FILE * fp)const;#endif private: inline pf_Frag * getCache() const {return m_pCache;} inline void setCache(pf_Frag * pf) const {m_pCache = pf;} pf_Frag * m_pFirst; pf_Frag * m_pLast; mutable UT_Vector m_vecFrags; mutable bool m_bAreFragsClean; mutable pf_Frag * m_pCache;};通过分析pf_Fragments类的定义,我们可以看出,pf_Fragmenst只是实现了链表的基本操作,但需要注意的是由于pf_Fragments中的节点类型是pf_Frag类型的指针,而在pf_Frag的类定义中有一个 PT_DocPosition m_docPos;变量的定义,这个变量是文档位置,所以它实现了对于文档位置的查找,比如说给出一个文档位置,要找到包含这个文档位置的片断即pf_Frag。pf_Frag * pf_Fragments::findFirstFragBeforePos(PT_DocPosition pos) const;这个函数就是为此目的而实现的,但这个函数写的不是很好。 同时我们可以看出,在pf_Fragments链表类中有一个 UT_Vector m_vecFrags;数据成员的定义,估计是专门用来为输出到文件准备的,pf_Fragments对这个成员的操作主要包括排序、查找某个pf_Frag 类型的指针在m_vecFrags中的索引等等。并且pf_Fragments有一个专门的CleanFrags( )函数,用来对把在pf_Fragments中按一定顺序(文档顺序)链接的节点顺序添加到m_vecFrags中,CleanFrags( )这个函数的作用就是使m_vecFrags中的节点的顺序和pf_Fragments中节点的链接顺序相一致,我们可以想象这可能会在编辑文档时使用,因为用户编辑文档时,它在文档中插入数据的位置是随机的,并不是只在尾部或其它固定位置插入数据,这就导致了在pf_Fragments中插入节点时要以文档的排版顺序来插入,那么m_vecFrags中节点的存放顺序也必须和链表的链接顺序相一致,CleanFrags( )函数的作用就是为此目的的。 在abiWord中用来遍历pf_Fragments链表时用一个专门的迭代器来实现,这个迭代器以一个类来表示,类名为PD_DocIterator:下面给出这个类的定义和说明: 五、PD_DocIterator类的说明:class ABI_EXPORT PD_DocIterator : public UT_TextIterator{ public: //两个参数分别是文档类的引用和文档位置 PD_DocIterator(const CTestRTFDoc & doc, PT_DocPosition dpos = 0); //返回当前位置的字符 virtual UT_UCS4Char getChar(); //返回当前文档位置 virtual UINT getPosition() const {return m_pos;} //设置文档对象成员m_pos; virtual void setPosition(UINT pos); …… private: PD_DocIterator(pt_PieceTable & pt):m_pt(pt){}; bool _findFrag(); pt_PieceTable & m_pt; PT_DocPosition m_pos; PT_DocPosition m_max_pos; const pf_Frag * m_frag; UTIterStatus m_status;}; 先对数据成员进行说明:1、pt_PieceTable & m_pt; //pt_PieceTable的引用2、PT_DocPosition m_pos; //本迭代器当前的文档位置3、PT_DocPOsition m_max_pos; //本迭代器所能迭代的缓冲区上限即索引4、const pf_Frag * m_frag; //本迭代器所对应的m_frag。5、UTIterStatus m_status; //一个枚举类型的值用来代表当前迭代器的状态。 这个类的目的主要是用来迭代一个m_frag所标识的数据段,对m_frag所标识的缓冲区的数据进行遍历。 六、PD_StruxIterator类的说明PD_StruxIterator 从一个给定的strux正向跨越迭代文档内容;和上面的PD_DocIterator形成对比,它不依赖于PT_Fragments是干净的,但从起始的fragments计算偏移量,这个偏移量是单个fragments的长度. class ABI_EXPORT PD_StruxIterator : public UT_TextIterator{ public: PD_StruxIterator(PL_StruxDocHandle sdh , UINT offset = 0 , UINT maxoffset = 0xffffffff); …… private: PD_StruxIterator(){}; bool _findFrag(); bool _incrementPos(UINT d); pt_PieceTable * m_pPT; //当前位置 UINT m_offset; //这个对象所代表的pf_Frag的偏移量,pf_Frag标识一个section //那么这个偏移量就是起始的位置 UINT m_frag_offset; PL_StruxDocHandle m_sdh; const pf_Frag * m_frag; UTIterStatus m_status; UINT m_max_offset; UINT m_strux_len; };在这里对一些数据成员的作用进行探讨,const pf_Frag * m_frag 是一个指针,它指向了要迭代的那个pf_Frag,在这个类的构造函数中会调用 _findFrag()这个函数,这个函数的作用是定位这个迭代器的 pf_Frag,以使同m_offset 在某一个 pf_Frag的范围内,即定位m_frag的值。同时_findFrag()会对m_frag_offset这个成员赋值,这个值代表该迭代器对应的pf_Frag的起始值。我们知道,pf_Frag代表一个文档的片断,当然至于pf_Frag_Strux类型也可以看成是一个片断,那么这个片断就是文档中的一段数据的范围,此类的pf_frag_offset成员就代表了数据的起始位置。m_strux_len数据成员代表了本pf_Frag_Strux的跨度即所代表的数据范围的长度。m_offset成员可以理解为一个当前位置。 到这里已经总结出了abiWord中数据存放的位置以及数据的关联方法,也看到了链表的一些相关操作,如上面的迭代器。在abiWord中,所有这些类的容器是一个名称为pt_PieceTable的类。这个类容纳了所有的文档数据,以及它们之间的联系。下面我们分析这个类的作用。 七、pt_PieceTable类的说明: class pt_PieceTable { public: bool appendFmt(const char ** attributes);public: struct { UINT m_indexCurrentInlineAP; } loading; /* stuff only valid while m_pts==PTS_Loading */ pt_VarSet m_varset; //容器 pf_Fragments m_fragments; //链表 public: bool appendSpan(const UT_UCSChar * pbuf, UT_uint32 length); bool getAttrProp(PT_AttrPropIndex indexAP, const PP_AttrProp ** ppAP) const; pt_VarSet & getVarSet(void) {return m_varset;} pf_Fragments & getFragments(void) {return m_fragments;}bool appendStrux(PTStruxType pts, const char ** attributes, pf_Frag_Strux ** ppfs_ret = 0); inline const UT_UCSChar *getPointer(PT_BufIndex bi) const { // the pointer that we return is NOT a zero-terminated // string. the caller is responsible for knowing how // long the data is within the span/fragment. return m_varset.getPointer(bi); } CTestRTFDoc *getDocument();}; 上面简要的列出了这个类的一部分声明:下面对这个类的功能进行简要的描述:1、对于section(节)、paragraph(段落),我们都认为它们是一个strux类型,即保存它们相关的信息的pf_Frag派生类为pf_Frag_Strux_xxx , 比如 section相对应的pf_Frag类型为pf_Frag_Strux_Section,与段落相对应的pf_Frag为pf_Frag_Strux_Block。在pt_PieceTable这个类中如果要保存类似这些Strux的属性则调用其成员函数appendStrux();下面画出调用流程:
这是文本与其属性的存储方式分三步:
1、 存储AP并获得AP的索引;2、 存储文本并获得文本在缓冲区中的偏移量3、 存储关联文本和它的AP的pf_Frag_Text对象 appendStrux( )在调用了_makeStrux()之后,此函数会调用m_fragments.appendFrag(pfs); //将pf_Frag加入到链表中 _makeStrux():1、 这个函数会调用pt_VarSet的成员函数storeAP()以存储属性。2、 调用_createStrux(pts,indexAP,&pfs),参数分别为Strux的类型、AP在vector中的索引,以及一个输出参数pf_Frag_Strux & pfs.这个调用执行后会依据pts的类型创建一个pf_Frag_Strux. 这是Strux的存储方式 下面看文本以及其属性的存储方式pt_PieceTable::appendFmt( )
我们知道pt_PieceTable中有一个pt_VarSet类型的对象变量,调用这个对象的storeAP( )方法以存储(AP)属性。注意参数:1、属性字符串;2、一个输出参数会返回AP在vector中的索引。m_varset.storeAP(attributes,&loading.m_indexCurrentInlineAP) bool pt_PieceTable::appendSpan( ) UINT bi; m_varset.appendBuf(pbuf,length,&bi)上面这条语句是调用pt_VarSet::appendBuf();参数说明:1、数据缓冲区指针.2、输出参数会返回中到pv_VarSet的m_buffer[0]中的之后的偏移量. pt_PieceTable::appendSpan( ) pf_Frag_Text * pft = new f_Frag_Text(this,bi,length,loading.m_indexCurrentInlineAP,NULL); 把这个fragment添加到链表末尾. m_fragments.appendFrag(pft);生成一个pf_Frag并把它添加到链表末尾.参数说明:1、pt_PieceTable的this指针;2、数据缓冲区的索引;3、AP在vector中的索引;4、暂时不解释至此,我们已经看到了存储Strux与存储文本的方式。