最近有理会HTML的需求,在Java中,好用的HTML理会框架也较量多,如
JSoup,HTMLParser,JTidy等等。在比拟几款框架之后,最终选取了HTMLParser做为第一版实现的框架。所以对HTMLParser的源码举办了一次整理。由于这种理会类的框架内部细节出格多,所以这里并不会出格的存眷所有细节,而是偏重梳理HTMLParser整个理会的流程。
类图
对我而言,画类图是进修一个框架源码较量直接的方法,一是有利于本身梳理逻辑,二是今后本身看类图照旧会很容易遐想起个中的一些细节。所以,这里放出类图,下面会对主要的类源码举办阐明。
整体先容
HTMLParser主要靠Node来暗示一个节点,细分为Text、Remark和Tag,通过以上三种形式的组合来暗示Html,个中Text接口暗示纯文本,Remark暗示注释,Tag暗示标签。
Parser是直接对外提供处事的类,其parse要领可以返回整个HTML文档被转换后的NodeList。
Lexer直译过来为词法阐明措施,它主要认真将Html转换为Node节点的,其nextNode要领使我们后文阐明的重点。Lexer与Parser的干系就像老板与员工,Parser是对外谈生意的,Lexer才是实打实干活的店员。
Page暗示整个HTML文档,但它内里的主要逻辑都交由Source认真,Source是对HTML源的一个抽象,HTMLParser中有InputStreamSource与StringSource两种实现,前一种可以处理惩罚网络可能文件类的流信息,后者可以处理惩罚纯文本的HTML。
理会流程
HTMLParser的利用很是简朴,如下就是最根基的形式:
Parser parser = new Parser(TEXT); NodeList list = parser.parse(null);
个中的TEXT可以使纯文本HTML,也可以是一个url,HTMLParser内部会自动判定,可是其判定的逻辑很是简朴:
length = resource.length ();
html = false;
for (int i = 0; i < length; i++)
{
ch = resource.charAt (i);
if (!Character.isWhitespace (ch))
{
if ('<' == ch)
html = true;
break;
}
}
只是判定了首个不为空缺的字符是否为<。
接下来我们主要看Parser的parse要领:
public NodeList parse (NodeFilter filter) throws ParserException
{
NodeIterator e;
Node node;
NodeList ret;
ret = new NodeList ();
for (e = elements (); e.hasMoreNodes (); )
{
node = e.nextNode ();
if (null != filter)
node.collectInto (ret, filter);
else
ret.add (node);
}
return (ret);
}
整体来看,这个函数并没有做什么对象,独一大概巨大的就是在变量e(NodeIterator)的获取上,我们追进elements要领:
public NodeIterator elements () throws ParserException
{
return (new IteratorImpl (getLexer (), getFeedback ()));
}
这个要领最终返回了IteratorImpl实例,getLexer要领获取了前面说过认真将Html转换为Node节点的Lexer,Lexer的实例是在Parser的结构函数中建设的,getFeedback要领返回的是ParserFeedback的一个实例,它的主要浸染就是输出一些信息。所以,我们主要来看下IteratorImpl的结构函数的实现:
public IteratorImpl (Lexer lexer, ParserFeedback fb)
{
mLexer = lexer;
mFeedback = fb;
mCursor = new Cursor (mLexer.getPage (), 0);
}
首先,缓存变量lexer与fb,紧接着,生成Cursor变量,这个Cursor用来暗示当前处理惩罚的位置信息。
紧接着,我们来看IteratorImpl的hasMoreNodes要领:
public boolean hasMoreNodes() throws ParserException
{
boolean ret;
mCursor.setPosition (mLexer.getPosition ());
ret = Page.EOF != mLexer.getPage ().getCharacter (mCursor); // more characters?
return (ret);
}
这里需要明晰的是,mLexer是用来处理惩罚HTML的,所以它知道当前处理惩罚的位置,而这个位置,劳务派遣管理系统,就用cursor暗示。Page暗示整个HTML文档,所以,它可以按照cursor的信息来查询当前cursor所对应的字符。因此,上述函数翻译过来就是查察当前处理惩罚的节点是否为竣事符,假如是,则暗示没有更多节点了,返回false。
接下来,来看nextNode函数,这里省略一些异常处理惩罚:
ret = mLexer.nextNode ();
if (null != ret)
{
// kick off recursion for the top level node
if (ret instanceof Tag)
{
tag = (Tag)ret;
if (!tag.isEndTag ())
{
// now recurse if there is a scanner for this type of tag
scanner = tag.getThisScanner ();
if (null != scanner)
{
stack = new NodeList ();
ret = scanner.scan (tag, mLexer, stack);
}
}
}
}
return ret;