• 解读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接口。

  • 相关阅读:
    事件
    js定时器
    js动画
    oop面向对象编程
    26 正则表达式
    25 异常处理
    javascript操作select的一些基本方法总结
    一个select 列子的思考
    jquery中 toggleClass方法的一种用法
    模拟TAB 采用 attachEvent 遇到的IE兼容问题
  • 原文地址:https://www.cnblogs.com/davidwang456/p/4390544.html
Copyright © 2020-2023  润新知