Live a simple life

沉默(zhu_xing@live.cn)
随笔 - 48, 文章 - 0, 评论 - 132, 引用 - 0
数据加载中……

【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)。



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

posted on 2008-10-13 17:50 zhuxing 阅读(2422) 评论(3)  编辑  收藏 所属分类: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

评论

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十):WTP TLD内容模型介绍  回复  更多评论   

朱星大哥,这一系列的文章简直太经典了,顶一个!
前面的StructrueModel的分析视图代码,好像放错了,放成StructuredDocument的分析视图代码了,能不能更新一下啊,小弟自己跟着步骤,没弄出来,好像treeviewer.serInput 没有调用,我自己把 structureModel传进去,缺又老是报错,求救啊!
2008-10-15 21:17 | 飞扬的麦子

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十):WTP TLD内容模型介绍  回复  更多评论   

好了,我问题解决了,是自己的一些代码没写全呵呵,不过最好还是把 代码换成正确的,方便后来人啊!不好意思,您的名字打错了,应该是朱兴!
2008-10-15 22:39 | 飞扬的麦子

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十):WTP TLD内容模型介绍[未登录]  回复  更多评论   

请问 wtp怎么加载的这个TLD文件,是怎么找到的? 怎么确定是哪一个tld文件,从哪里指定???你程序里面这个TLD都没加载到。。。
2012-04-26 16:51 | john

只有注册用户登录后才能发表评论。


网站导航: