扩展WTP2.0图形化Web编辑器——转换器

原文在BlogJava发布了一段时间了,刚才发现原来JavaEyes也有Blog,我就直接再发到这里一篇。
转载请注明出处

1.概述

新的WTP2.0带了一个叫做WebPageEditor的编辑器,打开一开,不得了,是带图型的编辑器耶~~
既然有了这个功能,那它肯定有扩展机制,能够让我们把自己的Taglib标签通过该编辑器的图形功能显示出来。
注意:此文认为阅读者都已经具备了最基本的Eclipse Plugin开发能力。示例项目下载

2.如何扩展

WebPageEditor的如何工作的倪?其实它的工作原理很简单。
首先它支持了对最基本的HTML元素的现实功能,然后如果需要显示我们自己定义的Tag的话,则要将这个自定义的Tag转换成普通的HTML模型。

扩展WTP2.0图形化Web编辑器——转换器


下面我们开始做一个例子。
首先我们建一个简单的TLD文件,我们命名为:mytest.tld.我们在这个文件中定义一个mybutton标签,它具有三个属性,text,width,height。
我们这么规定该标签在页面上的显示:基本现实为一个按钮,按钮上的文字是根据text值来确定的,该按钮的大小则是由width和height值来确定的:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<taglib>
    
<tlib-version>1.0</tlib-version>
    
<jsp-version>1.0</jsp-version>
    
<short-name>m</short-name>
    
<uri>http://mytagconvert.com</uri>
    
<description>test</description>
    
<tag>
        
<name>mybutton</name>
        
<tag-class>null</tag-class>
        
<tei-class>null</tei-class>
        
<body-content>empty</body-content>
        
<description>test</description>
        
<attribute>
            
<name>text</name>
            
<required>false</required>
            
<rtexprvalue>false</rtexprvalue>
        
</attribute>
        
<attribute>
            
<name>width</name>
            
<required>false</required>
            
<rtexprvalue>false</rtexprvalue>
        
</attribute>
        
<attribute>
            
<name>height</name>
            
<required>false</required>
            
<rtexprvalue>false</rtexprvalue>
        
</attribute>
    
</tag>
</taglib>


然后我们新建一个动态web项目,然后新建一个JSP文件,用WebPageEditor打开它,你会发现,画板中仅仅只有HTML的工具栏:

扩展WTP2.0图形化Web编辑器——转换器


肯定有人会问:工具栏就只有HTML和jsp,怎么创建我所需要的拖拽我自定义的标签啊!

别急,这里我说一下。WebPageEditor的工具栏跟所编辑的页面所在项目中,能够读到的TLD文件是有关系的(准确说应该是这个项目的classpath能够读到的TLD)。 就是说,如果你在该项目下放有一些TLD文件,那当你打开WebPageEditor的时候,它会自己读到这些TLD文件(必须保证这些文件能够被读出 来),然后根据TLD文件中对tag的定义,再在画板上显示出所能绘制的tag标签工具Entry。所以,我们这个例子也需要将刚才建好的那个 test.tld文件放置到这个项目中去。然后我们重新用WebPageEditor打开新建好的JSP文件,你会发现,工具栏中出现了我们刚才所定义好 的那个tld:

扩展WTP2.0图形化Web编辑器——转换器


你可以试着把工具栏中的TestButton托到编辑器中,不过它显示出现的是一个空白的玩意儿,上面还有该标签的名称。
ok,准备工作算是做好了,现在我们开始真正讨论如何让自定义标签可以显示,而且还能进行编辑。

首先让我们看一个扩展点:
org.eclipse.jst.pagedesigner.pageDesignerExtension
这个扩展点就是我们需要扩展的。下面几个元素比较重要:

tagConverterFactory
elementEditFactory
attributeCellEditorFactory

我们目前只讨论tagConverterFactory,其他的扩展点会在以后进行讲解。

tagConverterFactory有一个属性值:class。这个class是需要我们给出一个实现了org.eclipse.jst.pagedesigner.converter.IConverterFactory接口的类。

这个接口有两个方法需要我们实现:

getSupportedURI
这个方法什么意思呢?打开TLD文件,大家可以看到,TLD文件中有一个URI的属性值,这个属性值我们可以看成是XML文件中Schema的名字空间, 用来唯一确定该TLD文件的一个标识。WebPageEditor需要我们给出这么一个URI,其目的很明显,就是能够和它将读出的TLD进行匹配。

createConverter
这个方法有点让人很茫然,首先看它的参数:
Element element,int mode
第一个参数element是指,在我们拖拽进入一个标签后,该标签对应的element模型;mode是指现实模式,前面忘记说了,WebPageEditor是多页编辑器,一页是编辑页面的,一页是预览页面的。这里的mode就是指在编辑页面模式还是预览页面模式。我们现在可以不考虑这个参数,在下面的所有文字中,该mode参数一律被视为编辑页面模式。
该方法返回的是一个ITagConverter接口。
可以说,ITagConverter接口诠释了WebPageEditor的如何对标签进行解析并形成一个图形的过程。根据上面我们给出的一个简单的流程 图可以看出,ITagConverter其实就是专门转换taglib中定义的tag到标准HTML DOM的这么一个接口类。

话说多了是水,先看看怎么实现把。
先罗嗦一下步骤:
1.新建一个插件项目,加入依赖项:
  org.eclipse.jst.pagedesigner,
  org.eclipse.wst.sse.core,
  org.eclipse.wst.xml.core
2.新建一个为mybutton标签进行转换的Converter类
3.新建一个映射标签名和Converter的ConverterFactory类
4.建立一个在插件项目中实现刚才提到的 org.eclipse.jst.pagedesigner.pageDesignerExtension 扩展点


新建项目我不就不说了。从新建Converter说起。

3.MyButtonTagConverter

一般情况下,我不建议大家直接实现ITagConverter,很累人的,而且其实里面还有很多需要考虑的东西。所以我们应该从一个抽象类开始继承:
AbstractTagConverter。
这个类实现了大部分ITagConverter的功能,并且还提供了好些方法可供使用,它只将转换DOM的这个工作利用一个抽象函数 doConvertRefresh暴露出来——
doConvertRefresh函数的返回值就是我们最终转换成的DOM。
另外两个抽象函数是isMultiLevel和isWidget,这两个以后介绍。

我们继承这个类后只要将doConvertRefresh函数实现就可以了。
刚才我们新建了一个TLD文件,里面描述了一个mybutton标签,现在我们来实现这个标签的转换。
新建一个类,名为MyButtonTagConverter,然后我们实现这个类的doConvertRefresh方法:


public class MyButtonTagConverter extends AbstractTagConverter{

    @Override
    
protected Element doConvertRefresh() {
        
// 创建一个HTML 的button element
        Element button  = createElement(IHTMLConstants.TAG_BUTTON);
        
// 获得属性值
        String text = getHostElement().getAttribute("text");
        
if(text == null) text = "";
        
// 将text值给button,作为button的content,以便显示
        button.appendChild(createText(text));
        
// 将host element的属性全部复制给新建的button element中:
        JSFConverterUtil.copyAllAttributes(getHostElement(), button, null);
        
return button;
    }
}

我说一下这几个方法:
createElement
这个方法是AbstractTagConverter提供的,它是专门用于创建一个DOM元素的方法,传入的值则是生成该DOM得元素名,这里我用到了一 个维护常量的类IHTMLConstants,它是JST提供的,很有用。如果你有兴趣可以看看AbstractTagConverter如何实现这个方 法的,很简单,但是很麻烦。

getHostElement
这个方法是AbstractTagConverter提供的。返回的是一个被称为host element的DOM对象,什么是host element?它就是我们在页面上写的那个<mybutton/>的dom对象。

createText
这个方法是AbstractTagConverter提供的。创建一个content节点

JSFConverterUtil.copyAllAttributes
一个工具类提供的复制属性的方法。很实用。之所以要进行复制所有的属性,是因为,我们单独创建按出来的HTML DOM只是一个光秃秃的东西,不具备任何属性,那么host element的属性值有一部分是可以赋给生成的HTML DOM的,比如说,代码中给出了这么一段:读出host element的text属性值,然后给button dom的  属性,这样它就可以显示出文字来了,而其他一些属性名相同的属性,在host element和生成的html dom中具有相同的功效,我们是需要复制过来的。

我们可以这么想:
我们所写的<mybutton text="button"/> 被转换成了<button>button</button>HTML dom。

4 MyTagConverterFactory

这个类很简单:
public class MyConverterFactory implements IConverterFactory {

    
/* (non-Javadoc)
     * @see org.eclipse.jst.pagedesigner.converter.IConverterFactory#createConverter(org.w3c.dom.Element, int)
     
*/
    
public ITagConverter createConverter(Element element, int mode) {
        String name 
= element.getLocalName().trim();
        
if(name.equalsIgnoreCase("mybutton")){
            
return new MyButtonTagConverter(element);
        }
        
return new DefaultUnknownTagConverter(element,mode);
    }

    
/* (non-Javadoc)
     * @see org.eclipse.jst.pagedesigner.converter.IConverterFactory#getSupportedURI()
     
*/
    
public String getSupportedURI() {
        
return "http://mytagconvert.com";
    }

}

这里需要强调的是三点:
1.getSupportedURI方法返回的URI必须和TLD里的一致。
2.返回的Converter的构造函数的参数必须给出,因为这个参数就是我们要的Hostelement,而
createConverter传进的element正好就是这个host lement。
3.不要让createConverter返回null,给出一个默认的Converter

5 写好扩展点

我们新建一个
org.eclipse.jst.pagedesigner.pageDesignerExtension,然后写好tagConverterFactory元素,将它的class设成我们新建好的MyTagConverterFactory:

   <extension
         
point="org.eclipse.jst.pagedesigner.pageDesignerExtension">
      
<tagConverterFactory
            
class="tagconverters.MyConverterFactory">
      
</tagConverterFactory>
   
</extension>

6.运行

让我们运行这个插件。
在运行后,建立一个动态的web项目,然后把我们的tld文件复制到项目的WebContent下。再新建一个jsp文件,用WebPageEditor打来,然后我们再将右侧画板上的mybutton拖入编辑器:

扩展WTP2.0图形化Web编辑器——转换器


怎么样,不错吧。

7.未完待续

其实这只是一个开始,记得一开始为什么我会将mybutton设置上width和height,但后面就没说了,这些都要留到后面。
后续文章我还会讲一些其他的东西,比如如何扩展IElementEdit、属性的页、Palette的显示等等。
谢谢大家,再见。