原文地址: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接口。

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

  1. [论文解读] 阿里DIEN整体代码结构

    [论文解读] 阿里DIEN整体代码结构 目录 [论文解读] 阿里DIEN整体代码结构 0x00 摘要 0x01 文件简介 0x02 总体架构 0x03 总体代码 0x04 模型基类 4.1 基本逻辑 ...

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

    atitit js 开发工具 ide的代码结构显示(func list) outline总结 eclips环境::4.3.1 #-------需要一个js开发工具,可以显示outline或者代码结构显 ...

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

    时空上下文视觉跟踪(STC)算法的解读与代码复现 zouxy09@qq.com http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Z ...

  4. 如何解读IL代码

    如何解读IL代码 关于IL代码,我有将从三个方面去揭开它神秘的面纱.IL代码是什么?我们为什么要去读懂IL代码?我们如何去读懂IL代码?这三个问题的解答,将是我解读IL代码的整体思路. IL代码是什么 ...

  5. JeePlus:代码结构

    ylbtech-JeePlus:代码结构 1.返回顶部 1. 代码结构 1.文件结构 1.1.源码目录 src/main/javacom.jeeplus Jeeplus目录录├ common 公共模块 ...

  6. linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  7. 最全Pycharm教程(43)——Pycharm扩展功能之UML类图使用 代码结构

    版权声明:本文为博主原创文章,转载时麻烦注明源文章链接,谢谢合作 https://blog.csdn.net/u013088062/article/details/50353202 1.什么是UML ...

  8. [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构

    [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 目录 [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 0x00 摘要 0x01 文件简介 0x02 总体架构 0x03 总体代码 0x0 ...

  9. 【Learning Python】【第四章】Python代码结构(一)

    这一章的主旨在于介绍python的代码结构 缩进 在很多的编程语言中,一般{}用于控制代码块,比如以下的一段C代码 if(var <= 10) { printf("....." ...

随机推荐

  1. ExecuteStoreQuery

    using (var webdb = new kyj_NewHouseDBEntities()) { string sql = "select * from developer where ...

  2. web旋转式

    为了获取客户.回馈客户,平台一般会推出抽奖活动类的营销页.因此web页面中,有各式各样的抽奖效果. 格子式(九宫格),背景滚动式(数字/文字/图案),旋转式(转盘),游戏式(砸蛋/拼图...).... ...

  3. [转]ASP.NET MVC Jquery Validate 表单验证的多种方式介绍

    在我们日常开发过程中,前端的表单验证很重要,如果这块处理不当,会出现很多bug .但是如果处理的好,不仅bug会很少,用户体验也会得到很大的提升.在开发过程中我们可以不借助 JS 库,自己去手写 JS ...

  4. MySQL 存储过程删除大表

    1.权限问题 alter routine 编辑或删除存储过程 create routine 建立存储过程 execute 创建存储过程 2.存储过程相关的一些命令 show procedure sta ...

  5. 变形虫mysql的负载均衡 读写分离

    变形虫概述 图片来自   http://docs.hexnova.com/amoeba/amoeba-products.html  文档上说也可以做mongdb的分布式. 应用: 具有负载均衡.高可用 ...

  6. Java虚拟机笔记 – JVM 自定义的类加载器的实现和使用2

    1.用户自定义的类加载器: 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名 ...

  7. HDU 1043 Eight BFS

    题意:就是恢复成1,2,3,4,5,6,7,8,0; 分析:暴力BFS预处理,所有路径,用康拓展开判重,O(1)打印 93ms 还是很快的 #include <iostream> #inc ...

  8. 设备扩展(DEVICE_EXTENSION)

    原文链接:http://blog.csdn.net/hazy/article/details/481705 WDM中的结构   ---设备扩展 设备扩展(DEVICE_EXTENSION)是与设备对象 ...

  9. python错误收集

    Installing 'flask'You are using pip version 6.1.1, however version 7.1.2 is available.You should con ...

  10. [LeetCode]切割字符串,使各个子串都是回文

    题目:Given a string s, partition s such that every substring of the partition is a palindrome. Return ...