第八章、epub文件处理 — 定位指定段落

  • Post author:
  • Post category:其他




第八章、


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