【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十):WTP TLD内容模型介绍

【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十):WTP TLD内容模型介绍

           前面的系列文章中,我们已经分析了WTP的语法Document(IStructuredDocument)和语义Documnt(ICSSDocument或者IDOMDocument)以及和二者密切相关的IStructuredModel,并在这基础之上对WTP默认提供的StructuredTextEditor进行了部分功能定制。


            
            问题出现了,我们想要的信息全部包含在IStructuredDocument、IDOMDocument(ICSSDocument)或IStructuredModel中吗? 没有。例如,如果我们需要访问上图JSP文档TLD相关信息(例如:判断当前JSP文档中使用的特定标签在TLD中是如何声明的、和当前JSP文档想关联的TLD是怎样定义的、、、),这些信息并不是直接放置于语法Document(IStructuredDocument)或者语义Document(IDOMDocument或者ICSSDocument)中的。除了TLD相关的信息外,我们需要的还有其他的描述信息,所有这些信息可以看做元数据信息,WTP将其称为content model(直译为内容模型吧^_^)。在本节中我们就先介绍一种内容模型:TLD内容模型(TLD Content Model),在后面紧接下来的章节中,我们会基于本节介绍的TLD内容模型开发一个自动编辑策略(auto edit strategy)。

         【TLD Content Document】
          所谓的TLD Content Document,从字面上就可以猜测出来是对某一TLD的描述文档。那我们就先看一个TLD定义文件:
<? 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 > test1 </ shortname >
    
< uri > http://www.blogjava.net/zhuxing/tags/test1 </ uri >
    
< tag >
        
< name > test </ name >
        
< tagclass > any </ tagclass >
        
< bodycontent > empty </ bodycontent >
        
< attribute >
            
< name > scope </ name >
            
< required > true </ required >
            
< rtexprvalue > true </ rtexprvalue >
        
</ attribute >
    
</ tag >
</ taglib >

          
            从结构上看,我们的一个TLD可以看做一个树形结构的对象,具体节点种类可以分为:taglib、tag、attribute,示意图如下:
            
            对应于上图,我们定义了一个uri为http://www.blogjava.net/zhuxing/tags/test1的TLD(TLD Document),内含一个名为test的标签(TLD Element),该标签中含有一个名为scope的属性(TLD Attribute)。如果我们能够拿到这样的一个树形结构文档对象,我们就可以获取到我们想获取的有关特定TLD的信息了。上图中,其实也列举了几个相关的重要接口,这也是我们以后在操作TLD Content Model的时候需要经常打交道的:
        org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDDocument
        org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration
        org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDAttributeDeclaration

      【WTP Content Model】
           我们在本节开头部分就说了,TLD Content Model只是一种WTP Content Model,WTP为所有类型的content model设计了统一的类型接口(树形结构composite的接口实现):
            
            从上面的类型体系图可以看出,CMNode就是WTP内容模型树的统一顶级接口,相关接口定义在org.eclipse.wst.xml.core插件中,大家可以去看一下。我们在使用WTP content model的时候常用的接口有:
            1、org.eclipse.wst.xml.core.internal.contentmodel.CMNode
            2、org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap
            3、org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration
            4、org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration
            5、org.eclipse.wst.xml.core.internal.contentmodel.CMDocument
            6、org.eclipse.wst.xml.core.internal.contentmodel.CMEntityDeclaration
            7、org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMNodeWrapper(CMNode的适配接口)

            我们上面列举的三个关于TLD Content Model的接口都是CMNode子接口:
            1、TLDDocument接口是CMDocument的子接口
            2、TLDElementDeclaration接口是CMElementDeclaration(CMContent)的子接口
            3、TLDAttributeDeclaration接口是CMAttributeDeclaration的子接口
        
            PS:有兴趣可以看一下另外两种常用的content model:HTMLCMDocument和JSPCMDocument。
            
            【使用TLD Content Model】
               WTP content model的创建不需要我们负责,我们只是读取content model里的数据,常用接口有两类:
               1、org.eclipse.wst.xml.core插件中定义的CMNode系列抽象接口
               2、特定类型content model相关的扩展接口
              上面说的第一类接口我们已经列举过,有关第二类接口也非常常用,它帮助我们更方便的访问特定content model相关的信息。例如,相对于TLD Content Model,相关的扩展接口就是我们上面提到的TLDDocument、TLDElementDeclaration和TLDAttributeDeclaration。
            
                【TLDDocument、TLDElementDeclaration和TLDAttributeDeclaration】
                1、TLDDocument接口提供的操作:
                
                我们可以很方便地借助TLDDocument接口访问到的信息:URI、version、shortname等,通过其父接口CMDocument.getElements操作获取tld element列表(TLDTLDElementDeclaration列表)。

                2、TLDElementDeclaration接口提供的操作:
                
                我们可以很方便地借助TLDElementDeclaration接口访问到的信息:tag class、body content等,通过其父接口CMElementDeclaration.getAttributes操作获取tld attribute列表(TLDAttributeDeclaration列表)。

                3、TLDAttributeDeclaration接口提供的操作:
                
                我们可以很方便地借助TLDAttributeDeclaration接口访问到的信息:是否是必填属性等
                
                【TaglibTracker:taglib条目】
        
                  如上图所示,每一个tld条目可以被表示为一个taglib tracker,这里的taglib tracker是位置相关的!!!我们接着看一下TaglibTracker(org.eclipse.jst.jsp.core.internal.contentmodel.tld.TaglibTracker)的实现代码:   

/** *****************************************************************************
 * Copyright (c) 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * 
http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 ******************************************************************************
*/
package  org.eclipse.jst.jsp.core.internal.contentmodel.tld;



import  org.eclipse.jst.jsp.core.internal.contentmodel.CMDocumentWrapperImpl;
import  org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import  org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
import  org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMDocumentTracker;

/**
 * TaglibTracker class
 
*/
public   class  TaglibTracker  extends  CMDocumentWrapperImpl  implements  CMDocumentTracker {

    
private  IStructuredDocumentRegion fStructuredDocumentRegion;

    
public  TaglibTracker(String newURI, String newPrefix, CMDocument tld, IStructuredDocumentRegion aStructuredDocumentRegion) {
        
super (newURI, newPrefix, tld);
        fStructuredDocumentRegion 
=  aStructuredDocumentRegion;
    }

    
public  IStructuredDocumentRegion getStructuredDocumentRegion() {
        
return  fStructuredDocumentRegion;
    }

    
public  String toString() {
        
if  (getStructuredDocumentRegion()  !=   null )
            
return  getPrefix()  +   " @ "   +  getStructuredDocumentRegion().getStartOffset();  // $NON-NLS-1$
         return   super .toString();
    }
}

                分析以上代码实现,我们看到两点:
                1、TaglibTracker是和特定IStructuredDocumentRegion绑定的,间接是位置相关的
                2、TaglibTracker实现了CMNodeWrapper接口,可以适配为对应的original CMNode,可以通过TaglibTracker获取对应的CMDocument(TLDDocument)。

                 附加说明,taglib tracker的收集通过两种途径:一是通过taglib指令直接引用;二是include指令间接导入(被include文件中的tld将被加入到当前JSP 文档对应的taglib tracker列表中)

            【TLDCMDocumentManager】
            
问题出来了:前面讲了这么多,怎么获取TLD Content Model实例呢?TLDCMDocumentManager(org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager),TLDCMDocumentManager的获取需要借助于TaglibController(org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController)中定义的getTLDCMDocumentManager(IDocument document)接口。   
          
                TLDCMDocumentManager中定义了几个非常重要的操作,我们使用的时候要区分对待:
                List  getTaglibTrackers():当前文档相关的taglib tracker条目,随着对应IStructuredDocument的变化而变化
                Hashtable  getDocuments():当前文档相关的taglib tracker条目,只增不减,不会随着IStructuredDocument的变化而即时删除掉对应的无效taglib tracker条目
                【说明】:如果想获取实时准确地taglib tracker信息,请使用getTaglibTrackers操作!!!                

                List getCMDocumentTrackers(int offset):特定位置之前引入的taglib tracker条目
                List getCMDocumentTrackers(String prefix, int offset):特定位置之前引入的指定prefix的taglib tracker条目

                【说明】:前面我们介绍TaglibTracker的时候已经说过了TaglibTracker是位置相关的,一个TaglibTracker是和特定的IStructuredDocumentRegion绑定的,由于IStructuredDocumentRegion本身持有准确的位置信息,所以查询特定位置之前引入的taglib tracker条目成为可能。联系实际更加直观,在特定taglib指令之前使用对应的标签,则会出现标签不识别的问题。
                
                【TLD Content Model使用的一般流程】
                                

                【代码示例】
                 示例代码一:获取特定JSP文档对应的TLD Content Model(TLD CMDocument实现)列表

/**
     * 获取特定JSP文档相关的TLD列表
     * 
     * 
@param  structuredDocument
     * 
@return
     
*/
    
public   static  TLDDocument[] getTLDDoucments(IStructuredDocument structuredDocument) {
        
// 获取本structuredDocument对应的TLDCMDocumentManager实例
        TLDCMDocumentManager tldDocumentManager  =  TaglibController.getTLDCMDocumentManager(structuredDocument);
        
        
// 获取当前文档中的taglib tracker条目
        List taglibTrackers  =  tldDocumentManager.getTaglibTrackers();
        
        
// 获取taglib tracker条目对应的TLDDocument
        TLDDocument[] tldDocuments  =   new  TLDDocument[taglibTrackers.size()];
        
for  ( int  i  =   0 ; i  <  taglibTrackers.size(); i ++ ) {
            TaglibTracker taglibTracker 
=  (TaglibTracker)taglibTrackers.get(i);
            tldDocuments[i] 
=  (TLDDocument)taglibTracker.getDocument();
        }
        
return  tldDocuments;
    }
                
                示例代码二:分析一个特定的TLDDocument(分析tld element,分析tld attribute)  
/**
     * 获取特定TLDDocument中的tag列表(tld element列表)
     * 
     * 
@param  tldDocument
     * 
@return
     
*/
    
public   static  TLDElementDeclaration[] getTags(TLDDocument tldDocument) {
        
// 获取tld element列表
        CMNamedNodeMap children  =  tldDocument.getElements();
        
        TLDElementDeclaration[] tags 
=   new  TLDElementDeclaration[children.getLength()];
        
for  ( int  i  =   0 ; i  <  tags.length; i ++ ) {
            tags[i] 
=  (TLDElementDeclaration)children.item(i);
        }
        
return  tags;
    }
    
    
/**
     * 获取特定tag中(tld element)定义的属性列表
     * 
     * 
@param  tagElement
     * 
@return
     
*/
    
public   static  TLDAttributeDeclaration[] getAttributes(TLDElementDeclaration tagElement) {
        
// 获取tld attribute列表
        CMNamedNodeMap children  =  tagElement.getAttributes();
        
        TLDAttributeDeclaration[] attributes 
=   new  TLDAttributeDeclaration[children.getLength()];
        
for  ( int  i  =   0 ; i  <  attributes.length; i ++ ) {
            attributes[i] 
=  (TLDAttributeDeclaration)children.item(i);
        }
        
return  attributes;
    }

            
           PS:说明,有关TLDCMDocumentManager中提供的位置相关的taglib tracker查询接口大家可以自己编写相应的测试代码,着重理解taglib tracker位置相关的特性。

        

            下一节中,我们会开发一个TLD Content Model分析视图,之后一节会定制WTP编辑器默认的自动编辑策略(auto edit strategy)。



本博客中的所有文章、随笔除了标题中含有引用或者转载字样的,其他均为原创。转载请注明出处,谢谢!

你可能感兴趣的:(【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十):WTP TLD内容模型介绍)