zoukankan      html  css  js  c++  java
  • HtmlParser设计解析(1) 解析器模式(Interpreter)

    HtmlParser设计解析(1) - 解析器模式(Interpreter)

      对于HtmlParser的使用,这方面的介绍很多,而且详细。前段时间我将HtmlParser的源码读了一篇,在此,总结下其HtmlParser的设计,跟大家交流,我们只关注是设计。

          一、Filter设计

          NodeFilter 是htmlParser主要的提取节点的一种方式,其结构灵活,通过组合解释器查找页面上的任一个节点。

          1、先看个测试用例:

    Java代码
    1. /** 
    2.  * Test and filtering. 
    3.  */  
    4. public void testAnd () throws ParserException  
    5. {  
    6.     String guts;  
    7.     String html;  
    8.     NodeList list;  
    9.   
    10.     guts = "<body>Now is the <a id=one><b>time</b></a> for all good <a id=two><b>men</b></a>..</body>";  
    11.     html = "<html>" + guts + "</html>";  
    12.     createParser (html);  
    13.     list = parser.extractAllNodesThatMatch (  
    14.         new AndFilter (  
    15.             new HasChildFilter (  
    16.                 new TagNameFilter ("b")),  
    17.             new HasChildFilter (  
    18.                 new StringFilter ("men")))  
    19.             );  
    20.     assertEquals ("only one element", 1, list.size ());  
    21.     assertType ("should be LinkTag", LinkTag.class, list.elementAt (0));  
    22.     LinkTag link = (LinkTag)list.elementAt (0);  
    23.     assertEquals ("attribute value", "two", link.getAttribute ("id"));  
    24. }  
        /**      * Test and filtering.      */     public void testAnd () throws ParserException     {         String guts;         String html;         NodeList list;          guts = "<body>Now is the <a id=one><b>time</b></a> for all good <a id=two><b>men</b></a>..</body>";         html = "<html>" + guts + "</html>";         createParser (html);         list = parser.extractAllNodesThatMatch (             new AndFilter (                 new HasChildFilter (                     new TagNameFilter ("b")),                 new HasChildFilter (                     new StringFilter ("men")))                 );         assertEquals ("only one element", 1, list.size ());         assertType ("should be LinkTag", LinkTag.class, list.elementAt (0));         LinkTag link = (LinkTag)list.elementAt (0);         assertEquals ("attribute value", "two", link.getAttribute ("id"));     }

           2、NodeFilter 结构图

          

         3、所使用的设计模式

          NodeFilter接口的主要作用是判断该节点是否是客户端所查找的节点,返回一个boolean值。从上图中也可以看出,其接口中只有一个方法:

         boolean accept (Node node); //接受一个Node类型的参数

         在这,HtmlParser作者采用的是解析器模式来实现这个模式。

         我们先了解下解释器模式,然后再结合作者的源码来理解解释器模式,体会作者的设计灵活性。

        Interpreter模式可以定义出其方法的一种表示,并同时提供一个解释器。客户端可以使用解释器来解释这个语言中的句子。

        其中,Interpreter模式的几个要点:

        1、Interpreter模式应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则问题”才适合使用Interpreter模式

        2、使用Interpreter模式来表示方法规则,从而可以使用面向对象技艺来方便地“扩展”方法。

        4、HtmlParser NodeFilter 解释器模式的应用

        抽象表达式角色:    

    Java代码
    1. public interface NodeFilter extends Serializable, Cloneable {  
    2.     /** 
    3.      * Predicate to determine whether or not to keep the given node. 
    4.      * The behaviour based on this outcome is determined by the context 
    5.      * in which it is called. It may lead to the node being added to a list 
    6.      * or printed out. See the calling routine for details. 
    7.      * @return <code>true</code> if the node is to be kept, <code>false</code> 
    8.      * if it is to be discarded. 
    9.      * @param node The node to test. 
    10.      */  
    11.     boolean accept (Node node);  
    12. }  
    public interface NodeFilter extends Serializable, Cloneable {     /**      * Predicate to determine whether or not to keep the given node.      * The behaviour based on this outcome is determined by the context      * in which it is called. It may lead to the node being added to a list      * or printed out. See the calling routine for details.      * @return <code>true</code> if the node is to be kept, <code>false</code>      * if it is to be discarded.      * @param node The node to test.      */     boolean accept (Node node); }

       下面看一个逻辑“与”的操作的实现,这里表示二个过滤器通过逻辑与操作给出一个boolean表达式的操作。代码如下:   

    Java代码
    1. /** 
    2.  * Accepts nodes matching all of its predicate filters (AND operation). 
    3.  */  
    4. public class AndFilter implements NodeFilter {  
    5.     protected NodeFilter[] mPredicates;  
    6.   
    7.     /** 
    8.      * Creates an AndFilter that accepts nodes acceptable to both filters. 
    9.      *  
    10.      * @param left One filter. 
    11.      * @param right The other filter. 
    12.      */  
    13.     public AndFilter(NodeFilter left, NodeFilter right) {  
    14.         NodeFilter[] predicates;  
    15.   
    16.         predicates = new NodeFilter[2];  
    17.         predicates[0] = left;  
    18.         predicates[1] = right;  
    19.         setPredicates(predicates);  
    20.     }  
    21.   
    22.     public void setPredicates(NodeFilter[] predicates) {  
    23.         if (null == predicates)  
    24.             predicates = new NodeFilter[0];  
    25.         mPredicates = predicates;  
    26.     }  
    27.   
    28.     public boolean accept(Node node) {  
    29.         boolean ret;  
    30.   
    31.         ret = true;  
    32.   
    33.         for (int i = 0; ret && (i < mPredicates.length); i++)  
    34.             if (!mPredicates[i].accept(node)) // 这里调用本身构造的解释器再进行判断  
    35.                 ret = false;  
    36.   
    37.         return (ret);  
    38.     }  
    39. }  
    /**  * Accepts nodes matching all of its predicate filters (AND operation).  */ public class AndFilter implements NodeFilter { 	protected NodeFilter[] mPredicates;  	/** 	 * Creates an AndFilter that accepts nodes acceptable to both filters. 	 *  	 * @param left One filter. 	 * @param right The other filter. 	 */ 	public AndFilter(NodeFilter left, NodeFilter right) { 		NodeFilter[] predicates;  		predicates = new NodeFilter[2]; 		predicates[0] = left; 		predicates[1] = right; 		setPredicates(predicates); 	}  	public void setPredicates(NodeFilter[] predicates) { 		if (null == predicates) 			predicates = new NodeFilter[0]; 		mPredicates = predicates; 	}  	public boolean accept(Node node) { 		boolean ret;  		ret = true;  		for (int i = 0; ret && (i < mPredicates.length); i++) 			if (!mPredicates[i].accept(node)) // 这里调用本身构造的解释器再进行判断 				ret = false;  		return (ret); 	} } 

      再来看一个测试用例中的另外一些过滤操作,HasChildFilter 其代码如下:  

    Java代码
    1. public class HasChildFilter implements NodeFilter {  
    2.     protected NodeFilter mChildFilter;  
    3.   
    4.     protected boolean mRecursive;  
    5.   
    6.     public HasChildFilter(NodeFilter filter) {  
    7.         this(filter, false);  
    8.     }  
    9.   
    10.     public HasChildFilter(NodeFilter filter, boolean recursive) {  
    11.         mChildFilter = filter;  
    12.         mRecursive = recursive;  
    13.     }  
    14.   
    15.     public boolean accept(Node node) {  
    16.         CompositeTag tag; // ?1  
    17.         NodeList children;  
    18.         boolean ret;  
    19.   
    20.         ret = false;  
    21.         if (node instanceof CompositeTag) {  
    22.             tag = (CompositeTag) node;  
    23.             children = tag.getChildren();  
    24.             if (null != children) {  
    25.                 for (int i = 0; !ret && i < children.size(); i++)  
    26.                     if (mChildFilter.accept(children.elementAt(i))) // 判断是否包括该元素  
    27.                         ret = true;  
    28.                 // do recursion after all children are checked  
    29.                 // to get breadth first traversal  
    30.                 if (!ret && mRecursive) // 搜索下层节点  
    31.                     for (int i = 0; !ret && i < children.size(); i++)  
    32.                         if (accept(children.elementAt(i)))  
    33.                             ret = true;  
    34.             }  
    35.         }  
    36.   
    37.         return (ret);  
    38.     }  
    39. }  
    public class HasChildFilter implements NodeFilter { 	protected NodeFilter mChildFilter;  	protected boolean mRecursive;  	public HasChildFilter(NodeFilter filter) { 		this(filter, false); 	}  	public HasChildFilter(NodeFilter filter, boolean recursive) { 		mChildFilter = filter; 		mRecursive = recursive; 	}  	public boolean accept(Node node) { 		CompositeTag tag; // ?1 		NodeList children; 		boolean ret;  		ret = false; 		if (node instanceof CompositeTag) { 			tag = (CompositeTag) node; 			children = tag.getChildren(); 			if (null != children) { 				for (int i = 0; !ret && i < children.size(); i++) 					if (mChildFilter.accept(children.elementAt(i))) // 判断是否包括该元素 						ret = true; 				// do recursion after all children are checked 				// to get breadth first traversal 				if (!ret && mRecursive) // 搜索下层节点 					for (int i = 0; !ret && i < children.size(); i++) 						if (accept(children.elementAt(i))) 							ret = true; 			} 		}  		return (ret); 	} }

       TagNameFilter 的代码如下:  

    Java代码
    1. public class TagNameFilter implements NodeFilter {  
    2.     protected String mName;  
    3.   
    4.     public TagNameFilter(String name) {  
    5.         mName = name.toUpperCase(Locale.ENGLISH);  
    6.     }  
    7.   
    8.     public boolean accept(Node node) {  
    9.         return ((node instanceof Tag)   
    10.                 && !((Tag) node).isEndTag()   
    11.                 && ((Tag) node).getTagName().equals(mName));  
    12.     }  
    13. }  
    public class TagNameFilter implements NodeFilter { 	protected String mName;  	public TagNameFilter(String name) { 		mName = name.toUpperCase(Locale.ENGLISH); 	}  	public boolean accept(Node node) { 		return ((node instanceof Tag)  				&& !((Tag) node).isEndTag()  				&& ((Tag) node).getTagName().equals(mName)); 	} } 

      NodeFilter的另外13个子类,都按此实现包装不同的业务逻辑。并且非常容易增加其子类来实现新的“文法”规则。

       客户端则可灵活组装解释器,执行解释。非常灵活,这也满足用户自定义逻辑去查找HTML文件中的各个节点。

       至于HtmlParser是如何人存储HTML结构,在此不做深挖,只需要知道将提供一个迭代器可遍历所有的节点即可(其实HtmlParser中是通过遍历各个字符来映射Node对象及装载各字符的坐标(列数,行数))。

       5、HtmlParser中客户端的调用

       现在来看看测试用例中的Parser类中extractAllNodesThatMatch()。

       Parser: 

    Java代码
    1. public class Parser implements Serializable {  
    2.     ... ....  
    3.   
    4.     /** 
    5.      * Extract all nodes matching the given filter. 
    6.     */  
    7.     public NodeList extractAllNodesThatMatch (NodeFilter filter) throws ParserException {  
    8.         NodeIterator e;  
    9.         NodeList ret;  
    10.   
    11.         ret = new NodeList ();  
    12.         for (e = elements (); e.hasMoreNodes (); ) // elements()返回一个简单的迭代器,遍历所有节点  
    13.             e.nextNode ().collectInto (ret, filter);  
    14.   
    15.         return (ret);  
    16.     }  
    17.     ... ...  
    18. }  
    public class Parser implements Serializable {     ... ....      /**      * Extract all nodes matching the given filter.     */     public NodeList extractAllNodesThatMatch (NodeFilter filter) throws ParserException {         NodeIterator e;         NodeList ret;          ret = new NodeList ();         for (e = elements (); e.hasMoreNodes (); ) // elements()返回一个简单的迭代器,遍历所有节点             e.nextNode ().collectInto (ret, filter);          return (ret);     }     ... ... }

       AbstractNode:  

    Java代码
    1. public abstract class AbstractNode implements Node, Serializable {  
    2.    ... ...  
    3.     public void collectInto (NodeList list, NodeFilter filter) {  
    4.         if (filter.accept (this))  
    5.             list.add (this);  
    6.     }  
    7.     ... ...  
    8. }  
    public abstract class AbstractNode implements Node, Serializable {    ... ...     public void collectInto (NodeList list, NodeFilter filter) {         if (filter.accept (this))             list.add (this);     }     ... ... }
    Java代码
    1. public class CompositeTag extends TagNode { //TagNode extends AbstractNode, AbstractNode implements Node  
    2.     ... ...  
    3.     public void collectInto (NodeList list, NodeFilter filter) {  
    4.         super.collectInto (list, filter); //AbstractNode collectInto  
    5.         for (SimpleNodeIterator e = children(); e.hasMoreNodes ();) {  
    6.             // e.nextNode() 返回一个Node类型 e.nextNode ().collectInto() = this.collectInto() 递归遍历所有节点,并对每个节点进行过滤,将符合条件的节点添加至结果集中(NodeList)  
    7.             e.nextNode ().collectInto (list, filter);   
    8.         }  
    9.         if ((null != getEndTag ()) && (this != getEndTag ()))       
    10.                   getEndTag ().collectInto (list, filter);  
    11.     }  
    12.     ... ...   

  • 相关阅读:
    CAPTCHA---验证码 ---Security code
    Thymeleaf利用layout.html文件生成页面布局框架
    阿里云和腾讯云免费SSL证书 专题
    完整项目基础架构精简版-实现权限管理
    Android 基于XMPP Smack openfire 开发的聊天室
    Android 基于Bmob平台数据管理常用方法整理
    Android图表和图形创建库:EazeGraph
    Android 使用开源库StickyGridHeaders来实现带sections和headers的GridView显示本地图片效果
    Android MagicIndicator系列之一 —— 使用MagicIndicator打造千变万化的ViewPager指示器
    Android 比较两个时间段是否有交集或重复
  • 原文地址:https://www.cnblogs.com/wycg1984/p/1722395.html
Copyright © 2011-2022 走看看