解读eXtremeComponents代码结构--转载

原文地址:http://blog.csdn.net/lark3/article/details/1937466

大致整理了去年写的东西,罗列如下:

ec是一系列提供高级显示的开源JSP定制标签,当前的包含的组件为eXtremeTable,用于以表形式显示数据。ec现在的版本是1.0.1,由Jeff Johnston开发的,网址:http://www.extremecomponents.org
应该说eXtremeComponents已经实现了一些较为完善的功能,包括排序、过滤等,现在还支持Ajax功能。
用户通过设置标签(如table,row,column等)的属性(大部分的属性和html中的table、tr,td等的属性相同,另外添加一些用于控制的属性)便可轻松实现数据的列表。ec的强大还在于其良好的可扩展性,因为用户可以方便地对其进行二次开发,以满足一些特殊的需求。
由于本文主要是分析ec的代码设计而不是使用说明,因此如何使用ec可以参考相关的指南和参考文档。以下仅列举了发行包中的一个例子test.jsp:(大部分属性的含义都很明显,这里也不作说明了)

以下内容为程序代码:

<ec:table 
items="pres"
action="${pageContext.request.contextPath}/test.jsp"
imagePath="${pageContext.request.contextPath}/images/table/*.gif"
title="test"
width="60%"
rowsDisplayed="5"
>
<ec:exportXls fileName="resourceList.xls" tooltip="Export Excel"/>
<ec:exportPdf fileName="resourceList.pdf" tooltip="Export PDF"/>
<ec:row highlightRow="true">
         <ec:column property="rowcount" cell="rowCount" onClick="alert(‘${pres[0]}‘);" 
            title="No." sortable="false" filterable="false" width="5%" 
            style="text-align:center"/> 
<ec:column property="name" title="姓名" href="#" filterCell="droplist"/>
<ec:column property="nickname" title="昵称" filterable="true" sortable="false"/>
<ec:column property="term"/>
</ec:row>
</ec:table>

ec的精彩点之一是Limit接口及其实现类。
就整个软件的设计架构来说,ec也是非常优秀的。ec完全面向对象,并充分运用了设计模式,重构后的整个代码简洁且高效。

1.  代码结构

1.1四个第一级包:

package org.extremecomponents.table.*;(列表)

package org.extremecomponents.test;(用于测试)

package org.extremecomponents.tree;(树型,尚处于开发中)

package org.extremecomponents.util;(工具类)

其中,util包下的HtmlBuilder类封装了视图输出(如html格式)的各种操作,如函数table()用于输出一个HTML标签<table,该类包含一个StringWriter的私有变量。

而ExtremeUtils类则封装了一些常用的函数,如函数formatDate和formatNumber等。

1.2table包下的主要内容:

(1)package org.extremecomponents.table.bean;

说明:简单的bean类,类似VO。

Attributes抽象类包含一个HashMap的私有变量,用于关联属性及其值。类Column,Export,Row,Table都继承了Attributes并添加了各种的属性变量,如Column中的style变量便是对应HTML中td的style属性,Table中的border变量对应的是HTML中table的border属性,等等。当然,这些bean中还有一些属性是用于控制的,Column中的cell变量用于指明该单元格的输出是用哪个Cell类(其实就是用于控制输出的格式),如果cell=”date”,那么该单元格的输出就采用DateCell类的输出,即日期格式;等等。

还有ColumnDefaults,ExportDefault,RowDefault,TableDefault这几个最终类用于对应的Column,Export,Row,Table初始化时设置一些默认的属性值。这些默认值是由core包下的extremetable.properties文件设置的,在TableModel初始化时通过类TableProperties来读取的。

(2)package org.extremecomponents.table.calc;

说明:用于列的属性(calc,和calcTitle)中,由多个列的值计算而成的值。如:总值,平均值等。

Calc接口,只定义了一个函数getCalcResult(model,column);

类AverageCalc和TotalCalc实现了该接口,分别计算平均值和总值。

(3)package org.extremecomponents.table.callback;

说明:用于检索、过滤和排序行集数据,由TableModel中的execute方法调用。

三个接口:RetrieveRowsCallback, FilterRowsCallback, SortRowsCallback分别定义了函数retrieveRows(model),filterRows(model),sortRows(model),用于检索、过滤和排序数据。FilterRowsCallback的默认实现是得到Beans或Maps的Collection,然后通过实现jakarta Predicate接口来进行过滤。

ProcessRowsCallback类实现了这三个接口,也是ec中默认的对数据进行检索,过滤和排序的类。但是,这种功能的正确实现是基于这么一个事实,即ec得到的数据必须是数据库中未经处理(即过滤或排序)的所有原始数据,否则过滤或排序等处理的结果便不是正确的。还有,ec也能处理数据的分页,但现实中我们的数据量往往都很大(成千上万的),不可能未经处理就把所有的数据全部读出让ec来处理!显然,分页、过滤、排序等等处理都是程序在和数据库交互中完成的,ec仅仅接受处理后的数据然后显示而已。而LimitCallback类便实现了这种处理方案,它也实现了上面的那三个接口,但仅仅是直接返回数据而已。这种“Limit”的实现在limit包中再做讨论。

(4)package org.extremecomponents.table.cell;

说明:用于单元格的格式化输出。

Cell接口定义了两个方法:getExportDisplay和getHtmlDisplay。

AbstractCell抽象类实现了Cell,其实也定义了cell的输出框架,其继承类只需实现getCellValue方法即可。其getHtmlDisplay方法实现如下:

public String getHtmlDisplay(TableModel model, Column column) {

ColumnBuilder columnBuilder = new ColumnBuilder(column);

columnBuilder.tdStart();

columnBuilder.tdBody(getCellValue(model, column));

columnBuilder.tdEnd();

return columnBuilder.toString();

}

DisplayCell继承了AbstractCell,是ec中默认的cell。

DateCell继承了AbstractCell,用于输出日期格式化的单元格。

FilterCell实现了Cell,用于在头部输出一个用于过滤的输入框。

FilterDroplistCell实现了Cell,用于在头部输出一个用于过滤的下拉列表。

HeaderCell实现了Cell,用于输出头部标题的单元格内容。

NumberCell继承了AbstractCell,用于输出数字格式化的单元格。

RowCountCell继承了AbstractCell,用于输出数据集合的序号。

SelectAllHeaderCell实现了Cell,用于在头部生成一个选择框,用于选择所有的数据。

(5)package org.extremecomponents.table.context;

说明:ec中用到的上下文类的封装。

Context接口,用于获得Application,Page,Session,Request等上下文的变量。

HttpServletRequestContext实现了Context接口。

ServletRequestContext实现了Context接口。

JspPageContext实现了Context接口。TableModel初始化时(在TableTag中)使用了该类:

model = new TableModelImpl(new JspPageContext(pageContext), TagUtils.evaluateExpressionAsString("locale", this.locale, this, pageContext));

(6)package org.extremecomponents.table.core;

说明:ec列表的核心包,包括表格模型,配置文件,属性文件,参数封装等。

Registry接口,处理所有的参数(),包括用户自定义的。

AbstractRegistry抽象类实现了Registry,保存一些ec的内部参数及用户参数。

TableRegistry继承了AbstractRegistry类,由TableModel中的addTable函数调用。

Messages接口,即支持国际化显示,从Local中(如ZH_CN)获取正确的资源文件。由resource包中的TableResourceBundle类实现。在TableModelImp中初始化:

Messages messages = TableModelUtils.getMessages(this);

messages.init(context, this.locale);

this.messages = messages;

Preferences接口,用于获取配置文件中的设置值。

TableProperties实现了Preferences,初始化时先加载系统默认的配置文件,然后再加载由用户自己配置的文件,如下:

public void init(Context context, String preferencesLocation) {

try {

properties.load(this.getClass().getResourceAsStream(EXTREMETABLE_PROPERTIES));

if (StringUtils.isNotBlank(preferencesLocation)) {

InputStream input = this.getClass().getResourceAsStream(preferencesLocation);

if (input != null) {

properties.load(input);

}

}

} catch (IOException e) {

if (logger.isErrorEnabled()) {

logger.error("Could not load the eXtremeTable preferences.", e);

}

}

}

其在TableModelImpl中被调用:

Preferences preferences = new TableProperties();

preferences.init(context, TableModelUtils.getPreferencesLocation(context));

this.preferences = preferences;

为了设置属性文件,你应该如下例所示在/WEB-INF/web.xml文件中声明一个context-param,并 指定你的属性文件的路径:

<context-param>
  <param-name>extremecomponentsPreferencesLocation</param-name>  <param-value>/org/extremesite/resource/extremecomponents.properties</param-value>
</context-param>

TableCache类用于获得一些缓存的对象,包括Cell,State,Callback,Interceptor等,因此这些类都是singleton,并且不再线程安全。

TableModel接口,是系统的核心接口,包括其实现类TableModelImpl,因为它们把系统中的所有变量都联系了起来。

TableModelImpl实现了TableModel,初始化时获取Context,Preferences及Messages实例;通过addTable函数获取Registry及LimitFactory,Limit实例。

定义的变量有:Context,Preferences,Messages,Registry, TableHandler,RowHandler,ColumnHandler,ViewHandler,ExportHandler,Limit,Locale等。

变量currentRowBean保存当前处理的bean,并在上下文中设置var变量(table中的var属性)的值指向该bean,这样的话,Row和Column标签中便可以通过var变量来应用这个当前的bean对象,获得一些有意义的值。如下:

public void setCurrentRowBean(Object bean) {

int rowcount = rowHandler.increaseRowCount();

this.currentRowBean = bean;

context.setPageAttribute(TableConstants.ROWCOUNT, String.valueOf(rowcount));

context.setPageAttribute(tableHandler.getTable().getVar(), bean);

}

而collectionOfBeans、collectionOfFilteredBeans、collectionOfPageBeans则分别保存了所有的bean、过滤后的bean、当前页的bean。

TableModelImpl中的execute函数在标签第一次迭代时被调用,先过滤,后排序,然后通过ViewHandler.setView()来设置输出的视图。

(7)package org.extremecomponents.table.filter;

说明:过滤器,用于导出时的过滤,实现了javax.servlet.Filter。

(8)package org.extremecomponents.table.handler;

说明:各种处理句柄,帮助TableModel处理对应的bean,即关联model和bean。

类有:ColumnHandler, ExportHandler, RowHandler, TableHandle, ViewHandler。

(9)package org.extremecomponents.table.interceptor;

说明:拦截器,用于运行时添加和修改对应bean的属性。

接口有:TableInterceptor, RowInterceptor, ColumnInterceptor, ExportInterceptor。

用户可以实现自己的Interceptor,然后在对应的标签中使用Interceptor属性来设置并使用。所有的拦截器接口都定义了一个add方法, add方法被用来处理模型bean第一次创建时的属性。行和列的拦截器还有一个modify 方法,在当行和类进行处理是对属性值进行操作。

(10)package org.extremecomponents.table.limit;

说明:封装排序,过滤及分页的一些信息,用于向后台程序传递Limit对象。

LimitFactory接口,Limit的工厂接口。

AbstractLimitFactory抽象类实现LimitFactory,用于获取是否导出、当前页面数、排序字段及值及过滤集合等。

TableLimitFactory继承了AbstractLimitFactory。

ModelLimitFactory也继承了AbstractLimitFactory。

Filter最终类,值对象,三个String型私有变量:alias, property, value。

FilterSet类,内含一个Filter数组。

Limit接口,定义了一些用于获取limit信息的函数,如排序值、过滤字段及值、等等。

TableLimit最终类,实现了Limit。其构造函数的参数是LimitFactory,即Limit的值是由工厂类得到的。

Sort最终类,值对象,三个String型私有变量:alias,property,value。

(11)package org.extremecomponents.table.resource;

说明:资源文件及操作资源的类。

TableResourceBundle实现了Messages接口,初始化时会加载特定的资源文件以及用户自定义的资源文件,通过在web.xml中定义extremecomponentsMessagesLocation值来获取。

public void init(Context context, Locale locale) {

this.locale = locale;

defaultResourceBundle = findResourceBundle(EXTREMETABLE_RESOURCE_BUNDLE, locale);

String messagesLocation = TableModelUtils.getMessagesLocation(context);

if (StringUtils.isNotBlank(messagesLocation)) {

customResourceBundle = findResourceBundle(messagesLocation, locale);

}

}

(12)package org.extremecomponents.table.state;

说明:处理表格的状态。

State接口,定义了saveParameters和getParameters两个函数。

AbstractState抽象类,实现了State接口,定义了saveParameters函数。

DefaultState类实现了State接口,默认两个函数为空。

(13)package org.extremecomponents.table.tag;

说明:标签类,是ec开始的地方。

ColumnsTag继承TagSupport,用于生成自动产生的类。

ColumnTag继承BodyTagSupport并实现了ColumnInterceptor拦截器。

首次迭代时并不生成视图代码,而是:

Column column = new Column(model);

//设置一些属性。。。

addColumnAttributes(model, column);

model.getColumnHandler().addColumn(column);

第2次迭代开始后便执行真正的视图输出:

if (column != null) { // null if view not allowed

Object bean = TagUtils.getModel(this).getCurrentRowBean();

Object propertyValue = TableModelUtils.getColumnPropertyValue(bean, property);

column.setValue(getColumnValue(propertyValue));

column.setPropertyValue(propertyValue);

modifyColumnAttributes(model, column);

model.getColumnHandler().modifyColumnAttributes(column);

model.getViewHandler().addColumnValueToView(column);

}

最后那个语句的函数代码如下:

public void addColumnValueToView(Column column) {

Cell cell = TableModelUtils.getCell(column);

boolean isExported = model.getLimit().isExported();

if (!isExported) {

column.setCellDisplay(cell.getHtmlDisplay(model, column));

} else {

column.setCellDisplay(cell.getExportDisplay(model, column));

}

getView().body(model, column);

}

通过getView().body()函数的调用便完成了视图的输出。

RowTag继承了TagSupport并实现了RowInterceptor。

和ColumnTag类似,首次迭代时也仅仅是new一个Row对象,然后设置属性并添加到model中。但RowTag并不产生视图的输出,而是在ColumnTag视图输出时判断是否第一个或最后一个Column,若是,则这时才输出Row的视图数据。如下(抽象类AbstractHtmlView中:)

public void body(TableModel model, Column column) {

if (column.isFirstColumn()) {

rowBuilder.rowStart();

}

html.append(column.getCellDisplay());

if (column.isLastColumn()) {

rowBuilder.rowEnd();

}

}

TableTag继承了TagSupport,实现TryCatchFinally和TableInterceptor接口。

在doStartTag()函数中:

初始化TableModel的实例为TableModelImpl类,再实例化一个Table类并设置属性,最后通过model.addTable(table)把Table添加到model中,在该addTable函数中完成TableRegistry和TableLimit的初始化。

在doAfterBody()函数中:

在doEndTag()函数中:

pageContext.getOut().println(model.getViewData());

以上这语句便完成了视图的输出,而model.getViewData()函数的代码如下:

public Object getViewData() throws Exception {

Object viewData = viewHandler.getView().afterBody(this);

if (limit.isExported()) {

context.setRequestAttribute(TableConstants.VIEW_DATA, viewData);

context.setRequestAttribute(TableConstants.VIEW_RESOLVER, exportHandler.getCurrentExport().getViewResolver());

context.setRequestAttribute(TableConstants.EXPORT_FILE_NAME, exportHandler.getCurrentExport().getFileName());

return "";

}

return viewData;

}

还有几个Tag:ExportCsvTag, ExportPdfTag, ExportTag, ExportXlsTag,ParameterTag.等。

而TagUtils类则封装了几个处理函数,如利用ExpressionEvaluatorManager类完成属性的设置?

(14)package org.extremecomponents.table.view;

说明:视图部分,包括HTML,toolbar,pdf,xsl等

View接口,定义三个函数:beforeBody, body, afterBody。

AbstractHtmlView抽象类实现了View,其实也便定义了表格输出的框架,继承类只需实现beforeBodyInternal和afterBodyInternal两个函数即可,分别用于输出表格的表头数据及表尾数据,而其body函数则由ColumnTag标签处理时调用。在beforeBody函数中,该抽象类实例化HtmlBuilder、FormBuilder、TableBuilder、RowBuilder等用于构建相应视图的类,如FormBuilder完成Html中form表单等参数的设置等。

HtmlView继承了AbstractHtmlView类,是ec中默认的视图。

(15)package org.extremecomponents.table.view.html;

说明:用于帮助视图构建输出的类,如ColumnBuilder,FormBuilder,RowBuilder,TableBuilder等,如ColumnBuilder.tdEnd()函数生成的代码是“</td>”。

TableActions类封装了一些js的动作代码,主要用于form动作。

(16) package org.extremecomponents.table.html.toobar;

说明:工具条,类型有:按钮,字符,图形等。

ToolbarItem接口,

AbstractItem抽象类。

ButtonItem,ImageItem,TextItem继承AbstractItem实现了ToolbarItem接口。

时间: 2024-08-19 14:50:16

解读eXtremeComponents代码结构--转载的相关文章

读Zepto源码之代码结构[转载]

虽然最近工作中没有怎么用 zepto ,但是据说 zepto 的源码比较简单,而且网上的资料也比较多,所以我就挑了 zepto 下手,希望能为以后阅读其他框架的源码打下基础吧. 源码版本 本文阅读的源码为 zepto1.2.0 阅读zepto之前需要了解 javascript 原型链和闭包的知识,推荐阅读王福朋的这篇文章:深入理解 Javascript 原型和闭包,写得很详细,也非常易于阅读. 源码结构 整体结构 var Zepto = (function () { ... })() windo

zepto源码分析-代码结构【转载】

本来想学习一下jQuery的源码,但由于jQuery的源码有10000多行,设计相当复杂,所以决定从zepto开始,分析一个成熟的框架的代码结构及执行步骤. 网上也有很多zepto的源码分析,有的给源码添加注释,有的谈与jQuery的不同,但是都没有系统的讲解zepto框架的代码结构及初始化Zepto对象的过程. 准备 默认你已经对面向对象有一定的了解,本文是边实践边写的,虽有些乱,但好处是为大家提供了分析的思路. 英文文档. 中文文档 注意在文中$变量表示一个函数对象,而$()表示执行函数,他

时空上下文视觉跟踪(STC)算法的解读与代码复现(转)

本文转载自zouxy09博客,原文地址为http://blog.csdn.net/zouxy09/article/details/16889905:在此,仅当笔记mark一下及给大家分享一下. 时空上下文视觉跟踪(STC)算法的解读与代码复现 [email protected] http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Zhang等人今年投稿到一个会议的文章,因为会议还没有出结果,所以作者还没有发布他的Matlab源代码

关于网页脚本代码结构的再思考

在很多说法中,总是建议将我们的javascript脚本加载在网页的最后,并用外部文件的形式,然而事实并不是这样,外挂的文件最好不要太多,脚本结构代码本身才是值得我们思考的问题.我们需要重新思考我们撰写的脚本的执行力,并把更优秀的javascript开发思路融入到我们的开发中. 我在读完了几篇关于javascript和jQuery的性能优化的文章之后,才恍然大悟,我以前所做的很多代码结构优化,最终只是让乌徒帮显得臃肿,于是重新设计脚本代码的结构,无论怎么样,乌徒帮现在的网页打开显得更加流畅了. 1

JSqlParser系列之二代码结构(原)

JSqlParser系列之二代码结构(原) 博客园 百味木屋原创,转载请注明出处. 上一篇文章简单介绍如何建立JSqlParser工程,本章对JSqlParser工程的代码结构作大致地介绍. 一.目录结构 JSqlParser的目录结构比较简单,主要有表达式,解析器,语句处理几个目录.下面这张图给出了一个具体的SQL语句与表达式: 接下来,简要介绍一下在JSqlParser中几个抽象概念. 二.SQL语句(statement) JSqlPaser将所有的SQL语句抽象为Statement,Sta

atitit js 开发工具 ide的代码结构显示(func list) outline总结

eclips环境::4.3.1 #-------需要一个js开发工具,可以显示outline或者代码结构显示(func list)的功能的 aptana,webstorm好十好,走十太大的,pass...子能dw,eclps,npp黑头挑选.. VJET是一个 Eclipse 的插件,提供完全的JavaScript 集成开发环境,使标准开发功能的JavaScript验证代码的帮助,搜索类型,语法和语义,类型的轮廓和层次结构,调用链,重构和更多.VJET JavaScript IDE 是一个Ecl

Android Chromium for WebView代码结构

转载请注明出处 http://blog.csdn.net/typename powered by [email protected] 概述 相信关注Android 平台开发的大家已经观察到Android 4.4及其以上WebView的默认实现从Webkit变更为chromium, 这对使用Android WebView来说是利好消息,基于chromium的WebView性能更好更流畅,并且AOSP已经做了老版本API兼容,不需要我们的应用程序做任何修改就能运行在Android 4.4上.本文介绍

maven生成代码结构时XmlPullParserException异常

在使用maven eclipse:eclipse生成Eclipse项目代码结构时,遇到如下Warning提示信息: [WARNING] could not read workspace project from:E:\JavaSpace\webapi-maven org.codehaus.plexus.util.xml.pull.XmlPullParserException: only whitespace content allowed before start tag and not \u9

Java学习不走弯路教程(14 代码结构整理)

代码结构整理 一. 前言 在前上一章教程中,介绍了和浏览器的通讯.本章将在上一章的基础上,进一步扩展程序. 注:1.本文针对初学Java的同学训练学习思路,请不要太纠结于细节问题.2.本文旨在达到抛砖引玉的效果,希望大家扩展本例子,以学到更多知识的精髓. 学习本章需要准备的知识:1.读完本系列教程的前面章节.二. 步入正题 话不多说,大家自己理解,下面步入正题: 为了在后面的课程中走的更远,我们来整理一下代码的结构. 首先我们把业务逻辑都放在app包下,并且将这个包分为三层,web,servic