Excel模板错误检查方案

来源:互联网 发布:火星网络加速器 编辑:程序博客网 时间:2024/04/27 18:43

1        背景

项目中经常会遇到Excel模板的导入,其中一个重要功能是对模板中的数据进行错误检查。由于模板中每个字段的类型和取值范围不同,检查时既要给出错误数据也要能够提示错误原因,以便用户定位修正。实现该功能时主要遇到了以下问题:

1.      当模板中出现多处错误时要如何向用户展示?

2.      如何应用到所有模板,而不用每个模板都重新开发?

2        解决方案

2.1      先简单说下我们针对上述两个问题的解决方案:

1.      在模板的基础上增加一个错误信息列,当一行存在错误数据时,填写该行每个错误数据的错误原因,并将底色设置为红色,同时将错误数据所在列的列头以及错误数据所在单元格的底色设为红色,最后将修改后的模板文件返回给用户

2.      采用xml配置文件的方式定义每个模板要检查的列及列的类型、取值范围等,这样如果有新增模板需要导入,只需在该配置文件中增加该模板的检查项即可

2.2      下面对每个部分做下介绍:

2.2.1       列检查器

列检查器是这个功能里最关键的部分,它的输入输出见下图

  •  数据指当前检查的单元格的字符串
  •  检查器指当前检查单元格所在列的检查器
  • 检查信息指对单元格的检查结果,包括是否通过、错误的具体原因

在列检查器的设计中我们采用了责任链的方式。每个检查器在对数据进行检查后,如果数据出现错误,则直接返回检查不通过和错误原因;如果数据没有错误且存在后继检查器时,将数据传给下个检查器继续检查(某些特殊检查器在数据没错时不会传递给后继检查器,例如列可空检查器),否则直接返回检查不通过和错误原因。具体的检查器接口如下:

package com.example; import java.util.ArrayList;import java.util.List;import java.util.Map; /** * 检查错误信息类 * */class CheckDetail{   //检查是否通过   privatebooleanisPassed=false;      //检查不通过时的错误详细信息   private String detail="";     publicCheckDetail(boolean isPassed,String detail){      this.isPassed=isPassed;      this.detail=detail;   }      publicbooleanisPassed() {      return isPassed;   }   public String getDetail() {      return detail;   }   } abstract classColumnChecker{   //错误提示信息   privateString errorInfo="";      //该检查器依赖的列   privateString dependCol="";      //该检查器的后继检查器   privateColumnChecker successor=null;      /**     * 检查器返回的错误信息类,仅限于检查器内部使用     *     */   privateclassColumnCheckDetail{      //是否检查通过      private boolean isPassed=false;           //检查通过时是否传递给后继检查器      private boolean toSuccessor=false;           //检查失败时的详细错误信息        private String detail="";      public ColumnCheckDetail(booleanisPassed,boolean toSuccessor,String detail){         this.isPassed=isPassed;         this.toSuccessor=toSuccessor;         this.detail=detail;      }           public boolean isPassed() {         return isPassed;      }      public String getDetail() {         return detail;      }       public boolean isToSuccessor(){         return toSuccessor;      }   }      /**     * 检查单元格的值是否有错误,有错误则返回详细错误信息     * @param value 单元格值     * @param row 单元格所在行     * @param dependData 检查该单元格值依赖的列数据     * @return检查详细信息     */   finalpublicCheckDetail check(String value,int row,Map<String,List<String>> dependData){           //如果该检查器依赖的列不为空,但传入的依赖数据中不包含依赖列信息,则直接跳过该检查器,后继检查器继续检查      if(!dependCol.equals("")&&!dependData.containsKey(dependCol)){         return this.successor.check(value, row, dependData);      }              //如果该检查器的依赖列不为空,提取出该检查器依赖列的数据      List<String>colDependData=newArrayList<String>();      if(!dependCol.equals("")){         colDependData.addAll(dependData.get(dependCol));      }           //将依赖类数据传入当前检查器进行检查      ColumnCheckDetail selfDetail=this.selfCheck(value, row,colDependData);           //如果当前检查器检查通过且允许传递给下个检查器,当下个检查器存在时直接返回下个检查器的检查结果。否则返回当前检查器的检查结果      if(selfDetail.isPassed&&selfDetail.toSuccessor&&this.successor!=null){         return this.successor.check(value, row, dependData);      }else{         return new CheckDetail(selfDetail.isPassed,selfDetail.detail);      }        }      /**     * 设置该检查器返回的错误信息     * @param error 错误信息     */   finalpublicvoidsetError(String error){      this.errorInfo=error;   }      /**     * 设置该检查器依赖的列     * @param col 依赖列的名称     */   finalpublicvoidsetDependCol(String col){      this.dependCol=col;   }      /**     * 获取该检查器依赖的列,如果存在子检查器,则一并返回子检查器的依赖列名称     * @return该检查器依赖的所有列名称     */   finalpublicList<String> getDependCol(){      List<String> lst=newArrayList<String>();      if(!this.dependCol.equals("")){         lst.add(dependCol);      }           if(successor!=null){         lst.addAll(successor.getDependCol());      }           return lst;   }      /**     * 处理检查器的参数     * @param args 参数列表     * @return true 处理成功 false 处理失败     */   protectedabstractbooleanprocessParameters(List<String> args);      /**     * 对单元格值进行检查     * @param data 单元格值     * @param row 单元格所在行     * @param dependData 当前检查器依赖的列数据,如果不存在依赖列,则该列表为空     * @return检查详细信息     */   protectedabstractColumnCheckDetail selfCheck(String data,int row,List<String> dependData);}


 

  • 所有具体的检查器都要继承该抽象类,并实现processParameters和selfCheck接口
  •  常用的检查器类型有:是否允许为空、必须为整数、取值必须在一定范围内、取值必须在另一列中存在、某列取特定值时当前列的值必须在一定范围内等。通过继承上述抽象类均可实现。

 

2.2.2       配置文件

配置文件主要定义了每个模板要检查的列,每个列要执行的检查器、检查器的参数、错误提示信息等。具体定义示例如下:

<Templates>   <!--模板名称-->   <Example>       <!--模板要检查的Sheet页信息,name表示Sheet页名称,header表示列头所在行,start表示数据起始行-->       <Sheet name="sheet1" header="" start="">           <!--该Sheet页要检查的列信息,name表示列名称-->           <Column name="col1">                <!--该列要执行的检查器信息,class表示检查器对应的类,param表示检查器参数(多个参数以空格分隔),error表示检查失败时的错误信息-->                <Checkerclass="com.example.AllowEmpty" param="False"error="col1 不允许为空" />                <Checkerclass="com.example.RegMatch" param="^[1-9]\d*$"error="col1 必须为正整数"/>                <Checkerclass="com.example.ValueInRange" param="1 100"error="col1 的值必须属于1到100"/>           </Column>           <Column name="col2">                <Checkerclass="com.example.AllowEmpty" param="False"error="col2 不允许为空" />                <Checkerclass="com.example.RegMatch" param="^[1-9]\d*$"error="col2 必须为正整数"/>           </Column>           <!--该Sheet页要执行的全局检查项,一般为依赖于其他列的检查项-->           <Global>                <!--需要依赖其他列进行检查的列信息,name表示列名称-->                <Columnname="col2">                    <!--该列需要执行的检查器信息,calss表示检查器对应的类,depend表示该检查器依赖的列名称,param表示该检查器的参数,error表示检查失败时的错误信息-->                    <Checkerclass="com.example.ValueInOtherColumn" depend="col1"param="" error="col2 的值必须在col1中" />               </Column>           </Global>       </Sheet>   </Example></Templates>


2.2.3       Excel修改

我们使用jxl对Excel进行操作,检查器执行完成后,如果检查失败,就将当前检查的单元格所属的行、列、Sheet页名称、错误信息等记录下来,带所有检查器执行完成后,将记录下的信息统一写入模板中返回给用户。

                                                                                

0 0
原创粉丝点击