<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Live a simple life

    沉默(zhu_xing@live.cn)
    隨筆 - 48, 文章 - 0, 評論 - 132, 引用 - 0
    數(shù)據(jù)加載中……

    【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動提示

                前面介紹的內(nèi)容集中在兩點:StructuredTextEditor框架和WTP數(shù)據(jù)模型,在本節(jié)中就可以定制一個我們最常用的WTP StructuredTextEditor的功能,那就是自動提示。

                【W(wǎng)TP StructuredTextEditor提示功能實現(xiàn)分析】
                有關(guān)Eclipse文本編輯器框架、JFace Text Framework和WTP StructuredTextEditor的簡要知識,參見:
               【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(二):基于WTP StructuredTextEditor創(chuàng)建自己的JSPEditor 
                
                【SourceViewer提示策略配置】
                在章節(jié)二中,我們說過如果要對一個ISourceViewer進行自動提示策略的定制,在ISourceViewer對應(yīng)的SourceViewerConfiguration中配置就可以了。對于WTP JSP StructuredTextEditor而言,這里的ISourceViewer就是StructuredTextViewer,這里的SourceViewerConfiguration就是StructuredTextViewerConfigurationJSP。那我們來看一下WTP StructuredTextViewerConfigurationJSP中對自動提示策略的配置:
               (以下代碼摘取子StructuredTextViewerConfigurationJSP類中):
    protected IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) {
            IContentAssistProcessor[] processors 
    = null;
            
            //其他代碼省略......
            
    else if ((partitionType == IXMLPartitions.XML_DEFAULT) || (partitionType == IHTMLPartitions.HTML_DEFAULT) || (partitionType == IHTMLPartitions.HTML_COMMENT) || (partitionType == IJSPPartitions.JSP_DEFAULT) || (partitionType == IJSPPartitions.JSP_DIRECTIVE) || (partitionType == IJSPPartitions.JSP_CONTENT_DELIMITER) || (partitionType == IJSPPartitions.JSP_CONTENT_JAVASCRIPT) || (partitionType == IJSPPartitions.JSP_COMMENT)) {
                
    // jsp
                processors = new IContentAssistProcessor[]{new JSPContentAssistProcessor()};
            }
            
    else if ((partitionType == IXMLPartitions.XML_CDATA) || (partitionType == IJSPPartitions.JSP_CONTENT_JAVA)) {
                
    // jsp java
                processors = new IContentAssistProcessor[]{new JSPJavaContentAssistProcessor()};
            }
            //其他代碼省略......

            
    return processors;
        }
                以上代碼,我們可以看的出來,IContentAssistProcessor是和具體分區(qū)類型(partition type)相關(guān)聯(lián)的。想搞懂這個問題,就需要看一下這個具體分區(qū)類型(partition type)是怎么計算出來的。
                PS:分區(qū)類型是JFace Text Framework中的概念,相關(guān)的知識大家有興趣可以進一步去了解一下JFace Text Framework。

                【分區(qū)類型(partition type)】
                我們先來看一下JFace Text Framework中的基礎(chǔ)知識吧。再JFace Text Framework中有個分區(qū)劃分器的角色(org.eclipse.jface.text.IDocumentPartitioner),這個角色中一個核心操作就是判斷文檔(org.eclipse.jface.text.IDocument)中特定位置所在區(qū)域(region)的分區(qū)類型是什么,其實這里的分區(qū)類型說白了就是在一定程度上反應(yīng)了該區(qū)域(region)的內(nèi)容是什么語義性質(zhì)的^_^。    
                我們接著看一下,WTP提供了什么樣的IDocumentPartitioner呢?
                
                
               上圖中的org.eclipse.jst.jsp.core.internal.text.StructuredTextPartitionerForJSP就是我們針對jsp文件類型的分區(qū)器了,看一下相應(yīng)的實現(xiàn)代碼:    
    public String getPartitionType(ITextRegion region, int offset) {
            String result 
    = null;
            
    final String region_type = region.getType();
            
    if (region_type == DOMJSPRegionContexts.JSP_CONTENT) {
                result 
    = getPartitionTypeForDocumentLanguage();
            }
            
    else if (region_type == DOMJSPRegionContexts.JSP_COMMENT_TEXT || region_type == DOMJSPRegionContexts.JSP_COMMENT_OPEN || region_type == DOMJSPRegionContexts.JSP_COMMENT_CLOSE)
                result 
    = IJSPPartitions.JSP_COMMENT;
            
    else if (region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME || region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE)
                result 
    =
     IJSPPartitions.JSP_DIRECTIVE
    ;
            
    else if (region_type == DOMJSPRegionContexts.JSP_CLOSE || region_type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || region_type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN || region_type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN)
                result 
    = IJSPPartitions.JSP_CONTENT_DELIMITER;
            
    else if (region_type == DOMJSPRegionContexts.JSP_ROOT_TAG_NAME)
                result 
    = IJSPPartitions.JSP_DEFAULT;
            
    else if (region_type == DOMJSPRegionContexts.JSP_EL_OPEN || region_type == DOMJSPRegionContexts.JSP_EL_CONTENT || region_type == DOMJSPRegionContexts.JSP_EL_CLOSE || region_type == DOMJSPRegionContexts.JSP_EL_DQUOTE || region_type == DOMJSPRegionContexts.JSP_EL_SQUOTE || region_type == DOMJSPRegionContexts.JSP_EL_QUOTED_CONTENT)
                result 
    = IJSPPartitions.JSP_DEFAULT_EL;
            
                //其他代碼省略。。。
            
    else {
                result 
    = getEmbeddedPartitioner().getPartitionType(region, offset);
            }
            
    return result;
        }
                
                我們可以看到,對于WTP結(jié)構(gòu)化文本(當然包括JSP)而言,分區(qū)類型(partition type)基本上是根據(jù)ITextRegion的type信息確定的。有關(guān)ITextRegion的type相關(guān)知識,也是我們前面在介紹語法Document(IStructuredDocument)的時候重點內(nèi)容之一,忘記的話,去看一下。

                【自動提示流程】
                既然在StructuredTextViewerConfigurationJSP中根據(jù)分區(qū)類型(partition type)對提示進行了配置,那么是如何來利用這個配置的呢?
                我們在source viewer中觸發(fā)提示的時候,會有一個相應(yīng)的offset信息,前面也說過IDocumentPartitioner(WTP 對應(yīng)于JSP的實現(xiàn)為StructuredTextPartitionerForJSP)提供了根據(jù)offset判斷相應(yīng)區(qū)域分區(qū)類型(partition type)的接口操作,那提示的流程也就出來了:
                
                
                           上圖中的WTP StructuredTextViewerConfigurationJSP對應(yīng)于我們自己的jsp編輯器中的類型為jspeditor.configuration.JSPStructuredTextViewerConfiguration,這個我們在前面第二節(jié)中就定義了。

            【定制WTP StructuredTextEditor的提示功能】
                    通過上面的自動提示流程的分析,我們可以看的出來,如果想在我們自己的JSP編輯器中定制WTP提供的特定分區(qū)類型下的自動提示,只要覆寫WTP StructuredTextViewerConfigurationJSP中的getContentAssistProcessors實現(xiàn),用我們自定義的IContentAssistProcessor實現(xiàn)和特定的分區(qū)類型想綁定就可以了
                   【需求】
                    1、提供針對標簽屬性值提示的定制。
                    2、提供屬性值提示擴展點,允許用戶以動態(tài)掛入的方式提供特定標簽的屬性值提示

                    【實現(xiàn)摘要】
                    1、定義自己的IContentAssistProcessor實現(xiàn)
                        
    public class CustomizedJSPContentAssistantProcessor extends AbstractContentAssistProcessor{
        
        
    /* 
         * 定制自動提示策略:提供自定義的屬性值提示。
         * 
         * @see org.eclipse.wst.xml.ui.internal.contentassist.AbstractContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
         
    */
        
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
            
    if (!this.isOffsetValid(viewer, offset))
                
    return new ICompletionProposal[0];
            
            
    //利用IModelManager獲取對應(yīng)的模型
            IStructuredDocument structuredDocument = (IStructuredDocument)viewer.getDocument();
            IStructuredModel structuredModel 
    = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
            
    if (structuredModel == null)
                
    return new ICompletionProposal[0];
            
            
    try {
                
    //如果當前offset不是位于屬性區(qū)域
                IDOMAttr attrNode = StructuredModelUtil.getAttrAtOffset(structuredModel, offset);
                
    if (attrNode != null)
                    
    return this.computeCustomizedCompletionProposals(viewer, offset, attrNode);
                
    else
                    
    return new ICompletionProposal[0];
            } 
    catch (Exception e) {
                
    //log exception
                IStatus status = new Status(IStatus.ERROR, "jspeditor"100"自動提示失敗", e);
                Activator.getDefault().getLog().log(status);
                
                
    return new ICompletionProposal[0];
            } 
    finally {
                
    //注意,削減引用計數(shù)
                if (structuredModel != null)
                    structuredModel.releaseFromRead();
            }
        }
        
        
    /**
         * 判斷當前位置是否需要啟動我們自定義的JSP標簽屬性值自動提示,標準:
         * 1、當前offset對應(yīng)的區(qū)域的分區(qū)類型(partition type)為IJSPPartitions.JSP_DIRECTIVE
         * 2、當前offset對應(yīng)的text region的類型為DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE
         * 
         * 
    @param viewer
         * 
    @param offset
         * 
    @return
         
    */
        
    private boolean isOffsetValid(ITextViewer viewer, int offset) {
            
    try {
                IStructuredDocument structuredDocument 
    = (IStructuredDocument)viewer.getDocument();
                
                
    //判斷分區(qū)類型
                if (IJSPPartitions.JSP_DIRECTIVE != structuredDocument.getPartition(offset).getType()) 
                    
    return false;
                
    //判斷葉子text region對應(yīng)的region type信息
                ITextRegion textRegion = StructuredDocumentUtil.getTextRegion(structuredDocument, offset);
                
    return DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE == textRegion.getType();
            } 
    catch (Exception e) {
                
    return false;
            }
        }
        
        
    /**
         * 自定義提示結(jié)果:自定義屬性值提示
         * 
         * 
    @param viewer
         * 
    @param offset
         * 
    @param attrNode
         * 
    @return
         * 
    @throws Exception
         
    */
        
    private ICompletionProposal[] computeCustomizedCompletionProposals(ITextViewer viewer, int offset, IDOMAttr attrNode) throws Exception{
            
    //準備上下文數(shù)據(jù)
            String tagName = attrNode.getOwnerElement().getNodeName();
            String attrbuteName 
    = attrNode.getName();
            String inputText 
    = attrNode.getStructuredDocument().get(attrNode.getValueRegionStartOffset() + 1, offset - attrNode.getValueRegionStartOffset() - 1);
            
            
    //獲取相應(yīng)通過擴展點注冊的屬性值提示擴展
            IAssistantContributor contributor = AssistantContributorManager.getInstance().getAssistantContributor(tagName);
            
    return contributor.computeProposals(attrbuteName, inputText, viewer, offset);
        }
        
    }
       
                  關(guān)于細節(jié)實現(xiàn)暫且不說,后面會附上相應(yīng)的源碼。目前只需要了解大致的算法流程就可以:1、如果offset位于特定的JSP標簽屬性值范圍內(nèi),則去獲取對應(yīng)的屬性值自定義提示。

                2、將自定義的IContentAssistProcessor配置到自定義的SourceViewerConfiguration中。再配置之前,我們首先看一下如果offset位于一個屬性值的ITextRegion中,那么分區(qū)類型(partition type)是怎樣的。回過頭看一下,上面WTP StructuredTextPartitionerForJSP代碼黑體加粗部分,我們的屬性值區(qū)域?qū)?yīng)的分區(qū)類型為org.eclipse.jst.jsp.JSP_DIRECTIVE(IJSPPartitions.JSP_DIRECTIVE)。所以在我們自定義的JSPStructuredTextViewerConfiguration中覆寫WTP提供的StructuredTextViewerConfigurationJSP中的相應(yīng)方法:
    public class JSPStructuredTextViewerConfiguration extends
            StructuredTextViewerConfigurationJSP {
        
    /* 
         * 
         * 
         * @see org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP#getContentAssistProcessors(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
         
    */
        
    protected IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) {
            
    //我們目前只自定義JSP標簽屬性值自動提示的情況
            if (partitionType == IJSPPartitions.JSP_DIRECTIVE) {
                
    return new IContentAssistProcessor[]{new CustomizedJSPContentAssistantProcessor(), new JSPContentAssistProcessor()};
            }
            
            
    return super.getContentAssistProcessors(sourceViewer, partitionType);
        }
    }
                    由于我們只想定制JSP標簽的屬性值提示,所以我們將我們自定義的Content Assistant Processor配置為和IJSPPartitions.JSP_DIRECTIVE分區(qū)類型相綁定。

                    說明:如果你想定制類型的自動提示呢? 前面已經(jīng)說過了^_^

                3、定義相關(guān)抽象接口
                      由于針對不同JSP標簽的屬性值自動提示的邏輯是不同的,因為提示的內(nèi)容相同,但是屬性值提示這一概念是一致的。所以,我們就定義了IAssistantContributor接口來代表這一抽象概念,行為的變化通過對該接口的繼承來封裝。
    public interface IAssistantContributor {
        
    /**
         * 提供屬性值內(nèi)容提示
         * 
         * 
    @param attrbuteName 屬性名
         * 
    @param inputText    已輸入屬性值
         * 
    @param viewer       structured text viewer
         * 
    @param offset       光標位置
         * 
    @return
         
    */
        
    public ICompletionProposal[] computeProposals(String attributeName, String inputText, ITextViewer viewer, int offset);
    }
                    上面接口一看就知道是用的策略模式的手法,我在博客的前面的文章中說明了在使用策略模式時候的注意點,其中重點之一就是要關(guān)注上下文信息。  我們在本接口中提供了屬性名(attributeName)和用戶已經(jīng)輸入的屬性值(inputText),這兩個上下文信息對于一般的簡單提示情況下已經(jīng)足夠了。但是,也有可能有比較為復(fù)雜的情況,例如要處理嵌套標簽或者標簽之間有依賴等等情況,通過viewer參數(shù)可以獲取到對應(yīng)的IStructuredDocument和IStructuredModel,在加上offset信息,用戶可以利用這兩個信息自己去分析出進一步的上下文信息^_^。

                   PS:我們在代碼中針對IAssistantContributor接口提供了一個默認適配器類,針對的也就是簡單的提示情況,即只提供屬性名(attributeName)和用戶已經(jīng)輸入的屬性值(inputText)就可以完成提示的情況了。
                    
    public abstract class AbstractAssistantContributor implements
            IAssistantContributor {
        
        
    /* 
         * 子類可以覆寫,提供較為簡單的模版方法,分為計算替代字符串和構(gòu)建completion proposals兩步。
         * 
         * @see jspeditor.assist.contributor.IAssistantContributor#computeProposals(java.lang.String, java.lang.String, org.eclipse.jface.text.ITextViewer, int)
         
    */
        
    public ICompletionProposal[] computeProposals(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
            String[] replaceStrings 
    = this.computeReplaceStrings(attrbuteName, inputText, viewer, offset);
            
    return this.buildCompletionProposals(replaceStrings, inputText, viewer, offset);
        } 
        
        
    /**
         * 子類可以覆寫.
         * 說明:如果需要提供自定義的display string、image等信息,請直接覆寫computeProposals方法。
         * 
         * 
    @param attrbuteName
         * 
    @param inputText
         * 
    @return
         
    */
        
    protected String[] computeReplaceStrings(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
            
    return new String[0];
        }
        
        
    protected final IDOMAttr getDOMAttr(ITextViewer viewer, int offset) {
            IStructuredDocument structuredDocument 
    = (IStructuredDocument)viewer.getDocument();
            IStructuredModel structuredModel 
    = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
            
            IDOMAttr attr 
    = StructuredModelUtil.getAttrAtOffset(structuredModel, offset);
            
            structuredModel.releaseFromRead();
            
    return attr;
        }
        

        
    /**
         * 構(gòu)造ICompletionProposal實例,只提供簡單的ICompletionProposal實例
         * 
         * 
    @param replaceStrings
         * 
    @param inputText
         * 
    @param viewer
         * 
    @param offset
         * 
    @return
         
    */
        
    protected ICompletionProposal[] buildCompletionProposals(String[] replaceStrings, String inputText, ITextViewer viewer, int offset) {
            
    if (replaceStrings == null || replaceStrings.length == 0)
                
    return new ICompletionProposal[0];
            
            
    //計算ICompletionProposal相關(guān)參數(shù)
            IDOMAttr attrNode = getDOMAttr(viewer, offset);
            
    int replaceOffset = attrNode.getValueRegionStartOffset() + 1;
            
    int replaceLength = attrNode.getValueSource().length();
            
            List
    <ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
            
    for (int i = 0; i < replaceStrings.length; i++) {
                
    //根據(jù)用戶輸入執(zhí)行過濾
                if (replaceStrings[i].toLowerCase().startsWith(inputText.trim().toLowerCase())) {
                    
    int cursorPosition = replaceStrings[i].length();
                    ICompletionProposal proposal 
    = new CompletionProposal(replaceStrings[i], replaceOffset, replaceLength, cursorPosition);
                    proposals.add(proposal);
                }
            }
            
    return proposals.toArray(new ICompletionProposal[proposals.size()]);
        }
    }
                可以看的出來,我們這個默認適配器類簡單的應(yīng)用了模版方法的手法去處理簡單情景下的自動提示。
                
              4、定義屬性值自動提示擴展點
                由于JSP默認提供的一些標簽實際業(yè)務(wù)意義不強,而我們自己在開發(fā)應(yīng)用的時候,往往會不斷提供有業(yè)務(wù)意義的標簽供用戶使用,所以我們假設(shè)我們要處理的屬性值自動提示需要允許后續(xù)開發(fā)人員以動態(tài)開發(fā)的方式掛入,支持方便擴展。我們想到了擴展點機制^_^。
                 
                上面的擴展點定義其實很簡單,就是針對特定的JSP tag id(也就是tag名稱,這是唯一的)提供一個 IAssistantContributor接口的實現(xiàn)。
                
                  我們針對用戶掛入的擴展提供了一個管理器AssistantContributorManager,能夠支持以tag id獲取對應(yīng)的IAssistantContributor實現(xiàn),看一下上面我們定義的content assistant processor實現(xiàn)中黑體部分代碼就利用了該manager實例。具體請在附件源碼中看吧^_^

                5、示例
                 我們首先提供一個非常簡單的tld(test.tld),里面包含了一個簡單的測試標簽test,如下
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
    <taglib>
        
    <tlibversion>1.0</tlibversion>
        
    <jspversion>1.0</jspversion>
        
    <shortname>test</shortname>
        
    <uri>http://www.tkk7.com/zhuxing/tags/test</uri>
        <tag>
            
    <name>test</name>
            
    <tagclass>any</tagclass>
            
    <bodycontent>empty</bodycontent>
            
    <attribute>
                
    <name>scope</name>
                
    <required>true</required>
                
    <rtexprvalue>true</rtexprvalue>
            
    </attribute>
        
    </tag>
    </taglib>
                 我們在其中定義了一個scope屬性,下面我們就以擴展點的方式掛入我們的擴展。
                
                 首先實現(xiàn)提供一個IAssistantContributor接口實現(xiàn):
    public class TestTagAssistantContributor extends AbstractAssistantContributor {
        
    /**
         * scope attribute name
         
    */
        
    private static final String ATTR_NAME_SCOPE = "scope";
        
        
    /**
         * scope attribute value
         
    */
        
    private static final String[] ATTR_VALUE_SCOPE = {"request""session"};
        
        
    /* (non-Javadoc)
         * @see jspeditor.assist.contributor.AbstractAssistantContributor#computeReplaceStrings(java.lang.String, java.lang.String, org.eclipse.jface.text.ITextViewer, int)
         
    */
        
    protected String[] computeReplaceStrings(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
            
    if (ATTR_NAME_SCOPE.equals(attrbuteName)) {
                
    return ATTR_VALUE_SCOPE;
            }
            
            
    return new String[0];
        }

    }
                通過上面簡單的代碼可以看的出來,我們的提示邏輯很簡單,如果是scope屬性,就提供“requst”和“session”兩種選擇。

                下面,我們將我們提供的針對test標簽的屬性值提示擴展掛入:  

                6、效果演示

                看到了嗎,我們?yōu)閠est標簽掛入了自動提示實現(xiàn)之后,還真的提示了哈^_^            
                
                【后記】
                  其實本節(jié)中的內(nèi)容為定制自動提示提供了一個完整解決方案的雛形^_^               

               【源碼下載】

                  源碼下載(自動提示定制)

    本博客中的所有文章、隨筆除了標題中含有引用或者轉(zhuǎn)載字樣的,其他均為原創(chuàng)。轉(zhuǎn)載請注明出處,謝謝!

    posted on 2008-09-22 18:17 zhuxing 閱讀(3514) 評論(4)  編輯  收藏 所屬分類: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

    評論

    # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復(fù)  更多評論   

    下班了,先把源碼附上,里面有測試用test.tld
    2008-09-22 18:36 | zhuxing

    # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復(fù)  更多評論   

    期待作者的下文
    2008-09-22 21:54 | 冷月

    # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復(fù)  更多評論   

    內(nèi)容已經(jīng)補充完畢,并更新了代碼,希望對大家有幫助

    下一節(jié)內(nèi)容:為我們的編輯器配置自定義即時校驗
    2008-09-23 15:42 | zhuxing

    # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復(fù)  更多評論   

    我喜歡這篇, zhuxing 哥,這兩天我要好好看看。
    2008-09-24 17:28 | srdrm
    主站蜘蛛池模板: 亚洲中文字幕无码久久综合网| 久热综合在线亚洲精品| 日韩成人毛片高清视频免费看| 老牛精品亚洲成av人片| 俄罗斯极品美女毛片免费播放| 久久久久久噜噜精品免费直播| 亚洲精品免费在线| 成人永久免费福利视频网站| 一级特黄录像免费播放肥| 亚洲国产成人在线视频| 免费成人午夜视频| 91在线手机精品免费观看| 国产精品久久久久久亚洲影视 | 久久午夜夜伦鲁鲁片免费无码影视| 亚洲国产视频久久| 国产亚洲精品看片在线观看| 无码区日韩特区永久免费系列 | 曰批全过程免费视频网址| 免费观看四虎精品成人| 亚洲最大免费视频网| 久久精品国产精品亚洲| 免费无码肉片在线观看| 免费网站看av片| 无遮挡呻吟娇喘视频免费播放| 亚洲福利视频一区二区三区| 不卡一卡二卡三亚洲| 国产小视频在线免费| 国产精品成人免费福利| 成人片黄网站色大片免费观看APP| 性色av极品无码专区亚洲| 亚洲美女色在线欧洲美女| 亚洲一本大道无码av天堂| 暖暖日本免费在线视频| 91九色老熟女免费资源站| 成人av片无码免费天天看| 苍井空亚洲精品AA片在线播放 | 久久乐国产综合亚洲精品| 久久久久亚洲AV无码麻豆| 亚洲精品乱码久久久久久不卡 | 国产精品亚洲片在线观看不卡 | 亚洲福利视频一区二区三区|