第八章、
epub
文件处理
—
定位指定段落
上一章中我们介绍了用ZLTextPlainModel类里的char数组存储.xhtml文件中的文本信息以及标签信息的流程。
本章中我们将介绍从ZLTextPlainModel类里的char数组中定位指定段落的流程。
本章涉及的核心类是Processor类(ZLTextParagraphCursor类内部类)、EntryIteratorImpl类(ZLTextPlainModel类内部类)
定位到指定段落需要两个方法互相配合:ZLTextParagraphCursor类的cursor方法和ZLTextPage类的moveStartCursor方法
ZLTextParagraphCursor类的cursor方法负责填充代表指定段落的ZLTextParagraphCursor类。在这个过程中,char数组中代表指定段落的部分转换成一个由ZLTextElement类组成的ArrayList。其中,段落中的文本信息会被转换成ZLTextElement类的子类ZLTextWord,而段落中的标签信息会被转换成ZLTextElement类的子类ZLTextControlElement。
ZLTextPage类的moveStartCursor方法负责定位到指定的段落。定位的过程主要是维护ZLTextPage类中的StartCursor属性指向的ZLTextWordCursor类。ZLTextWordCursor类中的三个属性myParagraphCursor、myElementIndex、myCharIndex结合起来就完成来了定位到指定段落的流程。这三个属性中,myParagraphCursor属性指向的就是代表指定段落的ZLTextParagraphCursor类。
当定位流程完成之后,char数组中涉及当前段落的部分会被转换成一个由ZLTextElement子类组成的ArrayList中。这个ArrayList存储在ZLTextParagraphCursor类中的myElements属性中。而同时,ZLTextParagraphCursor这个类又存储在ZLTextPage类的StartCursor属性中。
PS:整个定位流程其实也可以看成ZLTextElement子类 -> ZLTextParagraphCursor类myElements属性 -> ZLTextPage类StartCursor属性不断上推的过程。
下面我们结合ZLTextView的setModel方法详细介绍下调用这两个方法定位到定段落的具体流程:
ZLTextParagraphCursor
类
cursor
方法
:
cursor方法的第一个参数为解析xhtml文件得到的ZLTextPlainModel类(第七章中有详细介绍),第二个参数为当前要显示的段落的索引(初始时这个索引为0)
接着cursor方法以同样的参数调用了ZLTextParagraphCursor类的构造函数
ZLTextParagraphCursor类构造函数
ZLTextParagraphCursor类构造函数调用了本类的
fill
方法,这个方法首先调用了
ZLTextPlainModel
类的
getParagraph
方法,接着又调用了
Processor
类的
fill
方法
ZLTextPlainModel
类的
getParagraph
方法:
这个方法的作用是初始化了一个ZLTextParagraphImpl类,并定义了这个类中的myModel属性(指向ZLTextPlainModel类)与myIndex属性(当前要显示的段落的索引)
Processor类(
ZLTextParagraphCursor
内部类)
fill
方法
这个方法将最终完成将char数组中代表当前段落的部分转换成一个元素为ZLTextElement类的ArrayList的工作。
方法大致流程是:首先通过EntryIteratorImpl类的构造方法获得正在处理的段落具体在char数组的哪个部分(67行)。然后,利用EntryIteratorImpl类next方法在char数组的这个部分里面不断读取内容并进行操作(71行)。操作分为针对文本信息(75行)和针对标签信息(84行)两种。线面在详细描述着两个方法。
EntryIteratorImpl
类(
ZLTextPlainModel
内部类)构造
函数
这个构造方法的作用就是利用当前段落的索引从
ZLTextPlainModel
类的三个属性指向的
int
数组中的当前段落在
char
数组的哪一部分中。
这三个属性我们已经在第七章中有介绍过了。
myStartEntryIndices
属性指向的
int
数组记录了每个段落具体在
CachedCharStorage
类内部的哪一个
char
数组里面;
myStartEntryOffsets
属性指向的
int
数组记录了每个段落从
CachedCharStorage
类内部
char
数组的哪个位置开始;
myParagraphLengths
属性指向的
int
数组记录每个段落在
CachedCharStorage
类内部
char
数组中占据多少长度;
EntryIteratorImpl
类(
ZLTextPlainModel
内部类)
next
方法
这个方法会在
char
数组的代表当前段落的部分里面不断读取内容并进行操作。
在读取的过程中首先会通过
CachedCharStorage
类的
block
方法获取对应需要显示的段落的
char
数组(
125
行),获取对应的
char
数组之后,代码就会利用不断递增的
dataOffset
变量,不断读取
char
数组中的内容。一旦代码读取到代表文本信息或标签信息的标示,就会进入不同的流程。
PS
:我们曾在上一章中介绍过这两种标示,当时我们是这么介绍的:“
每次调用
addControl
方法都会加入
ZLTextParagraph.Entry.CONTROL
(
3
)这个常量,这个常量是一种标示。类似的标示还有常量
ZLTextParagraph.Entry.TEXT
(
1
),我们会在下一章用到这两种变量
”。
CachedCharStorage
类的
block
方法:
我们在上一章中介绍CachedCharStorage
类的时候,曾经说过“
char
数组的长度最长不会超过这个长度(
65536
),一旦超过这个长度,代码就会新建一个
char
数组,同时
旧的数组会被持久化以便以后再用。”所以当前在内存中的
char
数组不一定会包含需要显示的数组,如果不包含需要显示的数组就需要根据
ZLTextWritablePlainModel
类的
myStartEntryIndices
属性找到对应的
char
数组。(这个属性的具体介绍也可以在上一章中找到)
获取到包含需要显示的段落的
char
数组后,
代码就会利用不断递增的
dataOffset
变量,不断读取
char
数组中的内容。一旦代码读取到代表文本信息或标签信息的标示,就会进入不同的流程。
获取到包含需要显示的段落的
char
数组后,
代码就会利用不断递增的
dataOffset
变量,不断读取
char
数组中的内容。一旦代码读取到代表文本信息或标签信息的标示,就会进入不同的流程。
文本信息(
ZLTextParagraph.Entry.TEXT
)
遇到代表文本的
ZLTextParagraph.Entry.TEXT
标示的情况:
这种情况下,代码会对分在这个标示之后的文本信息会进行两部分处理,一部分处理在
EntryIteratorImpl
类
next
方法中进行,一部分在
Processor
类的
fill
方法中进行
在
EntryIteratorImpl
类
next
方法中,会赋值两个
EntryIteratorImpl
类的两个变量。
myTextLength
属性记录文本信息的长度,
myTextData
属性存储当前
char
数组的引用。最后还通过赋值
dataOffset
,在
char
数组中向前跳过了涉及这段文本信息的部分。
在Processor
类
fill方法会调用本类的
processTextEntry
方法
在processTextEntry
方法中代码利用一个
for
循环,一个一个读取
char
数组中的元素,然后对每个元素调用
Processor
类的
addWord
方法。请注意,调用
addWord
方法时的参数。
addWord
方法会初始化一个
ZLTextWord
类,然后将这个类加入
ZLTextParagraphCursor
类
myElements
属性指向的
ArrayList
请注意对比下
ZLTextWord
类构造函数与
调用
addWord
方法时的参数
标签信息(
ZLTextParagraph.Entry.CONTORL
)
遇到代表文本的
ZLTextParagraph.Entry.CONTORL
标示的情况:
这种情况下,代码同样也会对跟在这个标示之后的标签信息会进行两部分处理,一部分处理在
EntryIteratorImpl
类
next
方法中进行,一部分在
Processor
类的
fill
方法中进行
EntryIteratorImpl
类
next
方法会赋值三个属性,请尤其注意下
myControlIsStart
这个属性,我们会在处理样式的时候用到这个属性。
Processor
类的
fill
方法会
初始化一个
ZLTextWord
类,然后将这个类加入
ZLTextParagraphCursor
类
myElements
属性指向的
ArrayList
当代码从ZLTextParagraphCursor类 cursor方法返回是,我们会得到新建的ZLTextParagraphCursor类,这个类中的myElements属性指向的ArrayList已经被填充了代表当前段落中文本信息的ZLTextWord类与标签信息的ZLTextControlElement类。接着就会进入ZLTextPage类的moveStartCursor方法。
ZLTextPage
类的
moveStartCursor
方法
这个方法比较简单,其实就是将通过ZLTextParagraphCursor类的cursor方法填充好的ZLTextParagraphCursor类,赋值给ZLTextPage类内的StartCursor属性
同时,ZLTextPage类内的StartCursor属性指向的ZLTextWordCursor类进行下设置
完成moveStartCursor方法后,我们就完成“定位指定段落”的流程了。
转载于:https://my.oschina.net/u/938986/blog/335928