单位里的查询系统是基于struts的,在结果集显示的处理上都是用 <logic:iterate id=".." indexId="id" name=".." scope="request"> 然后在<bean:write name=".." property=".."> 这样子对付的,后来觉得比较麻烦,就写了一个显示结果集的标签
- 行记录用HashMap保存 
 - 结果集用Vector封装 
 - 表格的标题等属性用xml配置 
 - dom4j解析xml文件 
 - 输出时根据配置合并多行内的相同属性列 
 - 求和
 
表格配置文件table-config.xml保存在/WEB-INF下


<?xml version="1.0" encoding="gb2312"?>
<table-config>
   
   
 <table name="jcgl_dzjk" width="800">
  <title>稽查数据电子缴库情况</title>
  <groupby>id_wjdm</groupby>
  <columns>
   <column>
    <name>id_wjdm</name>
    <header>微机代码</header>
    <width>60</width>
    <bind>true</bind>
   </column>
   <column>
    <name>name_nsr</name>
    <header>单位名称</header>
    <width>200</width>
    <bind>true</bind>
   </column>
   <column>
    <name>id_sz</name>
    <header>税种</header>
    <map>map_sz</map>
   </column>
   <column>
    <name>id_sm</name>
    <header>税目</header>
   </column>
   <column>
    <name>ybtse</name>
    <header>查补金额</header>
    <width>100</width>
    <sum>true</sum>
   </column>
   <column>
    <name>date_sbbrq</name>
    <header>申报日期</header>
   </column>
   <column>
    <name>date_jkrq</name>
    <header>扣款日期</header>
   </column>
   <column>
    <name>kkzt</name>
    <header>扣款状态</header>
   </column>
  </columns>
 </table>
   
   
</table-config>

 
用于表格配置的Table.java
package tax.tags;

import java.util.Vector;


public class Table 
{
 private String name;
 private String width;
 private String title;
 private String groupby;
 private Vector<Column> columns;

 public Table()
{
 }

 public Table(String name)
{
  this.name=name;
 }

 public Vector<Column> getColumns() 
{
  return columns;
 }

 public void setColumns(Vector<Column> columns) 
{
  this.columns = columns;
 }

 public String getName() 
{
  return name;
 }

 public void setName(String name) 
{
  this.name = name;
 }

 public String getTitle() 
{
  return title;
 }

 public void setTitle(String title) 
{
  this.title = title;
 }

 public String getWidth() 
{
  return width;
 }

 public void setWidth(String width) 
{
  this.width = width;
 }
 

 public String getGroupby() 
{
  return groupby;
 }

 public void setGroupby(String groupby) 
{
  this.groupby = groupby;
 }

 public String toString()
{
  StringBuffer buf=new StringBuffer();
  buf.append("Name:").append(name).append("\n");
  buf.append("Width:").append(width).append("\n");
  buf.append("Title:").append(title).append("\n");
  buf.append("Groupby:").append(groupby).append("\n");

  for(int i=0;i<columns.size();i++)
{
   Column column=columns.elementAt(i);
   buf.append("column name:").append(column.getName()).append("\n");
   buf.append("       header:").append(column.getHeader()).append("\n");
   buf.append("       width:").append(column.getWidth()).append("\n");
   buf.append("       sum:").append(column.isSum()).append("\n");
   buf.append("       bind:").append(column.isBind()).append("\n");
  }
  return buf.toString();
 }
}




class Column
{
 private String name;
 private String width;
 private String header;
 private String map;
 private boolean sum;
 private boolean bind;


 public Column()
{
  sum=false;
  bind=false;
 }

 public boolean isBind() 
{
  return bind;
 }

 public void setBind(boolean bind) 
{
  this.bind = bind;
 }

 public String getHeader() 
{
  return header;
 }

 public void setHeader(String header) 
{
  this.header = header;
 }

 public String getName() 
{
  return name;
 }

 public void setName(String name) 
{
  this.name = name;
 }

 public boolean isSum() 
{
  return sum;
 }

 public void setSum(boolean sum) 
{
  this.sum = sum;
 }

 public String getWidth() 
{
  return width;
 }

 public void setWidth(String width) 
{
  this.width = width;
 }

 public String getMap() 
{
  return map;
 }

 public void setMap(String map) 
{
  this.map = map;
 }
}


 
 
用dom4j解析table-config.xml的Dom4jTable.java,因为用到了xpath,所以把jaxen-1.1-beta-6.jar也要拷贝到/web-inf/lib里面去
package tax.tags;

import java.util.List;
import java.util.Vector;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;


public class Dom4jTable 
{
 String tableName;


 public Dom4jTable() 
{ }


 public Dom4jTable(String tableName) 
{
  this.tableName = tableName;
 }


 public Document parse(String xmlpath) throws DocumentException 
{
  SAXReader reader = new SAXReader();
  Document doc = null;
  doc = reader.read(xmlpath);
  return doc;
 }


 public Table getTable(Document doc) 
{
  Table table;
  String xpath = "//table[@name='" + tableName + "']";
  Node node = doc.selectSingleNode(xpath);

  if (node == null) 
{
   table = null;

  } else 
{
   table = new Table(tableName);
   table.setWidth(getValue(node,"@width"));
   table.setTitle(getValue(node,"title"));
   table.setGroupby(getValue(node,"groupby"));
   Vector<Column> columns = new Vector<Column>();
   Node nodeColumns = node.selectSingleNode("columns");
   List list = ((Element) nodeColumns).elements();

   for (int i = 0; i < list.size(); i++) 
{
    Column column = new Column();
    Element element = (Element) list.get(i);
    column.setName(getValue(element,"name"));
    column.setHeader(getValue(element,"header"));
    column.setWidth(getValue(element,"width"));
    column.setMap(getValue(element,"map"));
    column.setBind(Boolean.parseBoolean(getValue(element,"bind")));
    column.setSum(Boolean.parseBoolean(getValue(element,"sum")));
    columns.add(column);
   }
   table.setColumns(columns);
  }
  return table;
 }

 private String getValue(Node node,String name)
{
  String value=node.valueOf(name);

  if(value.equals(""))
{
   value=null;
  }
  return value;
 }
}


 
自定义标签ShowTableTag.java,写的有点乱,还好能用,本来用sax解析xml的,后来改为dom4j的
package tax.tags;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Vector;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;


public class ShowTableTag extends TagSupport 
{
 private static final long serialVersionUID = 1L;

 private Log log = LogFactory.getLog(this.getClass());

 private String vectorName;

 private String tableName;

 private boolean displaySN = false;

 private Vector<HashMap> data = null;


 public int doStartTag() throws JspException 
{
  return TagSupport.SKIP_BODY;
 }


 public int doEndTag() throws JspException 
{
  String newline = "\n";
  boolean getsum=false;
  JspWriter out = pageContext.getOut();
  StringBuffer buf = new StringBuffer();

  if (data == null || data.size() == 0) 
{
   buf.append("There is no result available.");

  } else 
{
//   取表格的各项属性设置
   Table table = getTableConfigByDom4j();
//   取表格的各列,然后存入cols数组
   Vector columns = table.getColumns();
   Column[] cols=new Column[columns.size()];
   BigDecimal[] sum=new BigDecimal[columns.size()];

   for(int i=0;i<columns.size();i++)
{
    cols[i]=(Column)columns.elementAt(i);
    if(cols[i].isSum()) getsum=true;
    sum[i]=new BigDecimal(0);
   }
   
/////////////////////////////////////////////////////////////////////////////////////////
//   如果table没有设置title, 就不打印这部分

   if(table.getTitle()!=null)
{
    buf.append("<DIV id=tag_head><div class=label>").append(newline);
    buf.append(table.getTitle()).append("</div></div><div id=tag_body>");
    buf.append(newline);
   }
   
   buf.append("<div class=content>").append(newline);
   buf.append("<table border=1 cellPadding=0 cellSpacing=0 ");
   buf.append("borderColorLight=#000000 borderColorDark=#f8f7f5 width=\"");
   buf.append(table.getWidth()).append("\">").append(newline);
   buf.append("<tr class=tablehead>").append(newline);
   
//   如果标签中的displaySN为true, 则打印序号列

   if (displaySN) 
{
    buf.append("<th>序号</th>").append(newline);
   }

//   打印table的th栏

   for (int i = 0; i < cols.length; i++) 
{
    buf.append("<th");

    if (cols[i].getWidth() != null)
{
     buf.append(" width=").append(cols[i].getWidth());
    }
    buf.append(">").append(cols[i].getHeader()).append("</th>");
    buf.append(newline);
   }
   buf.append("</tr>").append(newline);


//   由table的groupby属性产生key_group<Integer,Integer>
//   第一个Integer是行号,从1开始
//   第二个Integer是跨度span,即连续相同的key的数量
   Hashtable key_group = getKeyGroup(table.getGroupby());
   
   int rowid = 0; //行号
   int distid = 0; //捆绑相同key行后的行号
   Iterator<HashMap> it = data.iterator();

   while (it.hasNext()) 
{
    buf.append("<tr class=tablepad>").append(newline);
    
    rowid++;
    
//    根据行号从key_group中取得pan值, span可能为null  
    Integer span=(key_group==null?null:(Integer)key_group.get(new Integer(rowid)));

//     显示序号

    if (displaySN) 
{

     if(key_group==null)
{
      //key_group为null表示table中不用合并相同的行
      buf.append("<td>").append(rowid).append("</td>");

     }else if(span!=null)
{
      buf.append("<td rowspan=").append(span).append(">");
      buf.append(++distid).append("</td>").append(newline);
     }
    }
//    取得这一行的记录
    HashMap row = (HashMap)it.next();

    for (int i = 0; i < cols.length; i++) 
{
//     取得td中应该显示的内容
     String td = (String) row.get(cols[i].getName());

     if(cols[i].isSum())
{
      sum[i]=sum[i].add(new BigDecimal(td));
     }

     if(cols[i].getMap()!=null)
{
      TreeMap map = (TreeMap) pageContext.getServletContext()
        .getAttribute(cols[i].getMap());
      td=(String)map.get(td);
     }
     if (td == null) td = " ";
//     区别这个列的bind属性确定是否要合并显示

     if(!cols[i].isBind()||key_group==null)
{
      buf.append("<td>");
      buf.append(td).append("</td>").append(newline);

     }else if(span!=null)
{
      buf.append("<td rowspan=").append(span).append(">");
      buf.append(td).append("</td>").append(newline);
     }
    }
    buf.append("</tr>").append(newline);
   }
   //如果需要打印合计数,增加一行

   if(getsum)
{
    buf.append("<tr class=tablepad>");

    if(displaySN)
{
     buf.append("<td> </td>");
    }

    for(int i=0;i<cols.length;i++)
{

     if(cols[i].isSum())
{
      buf.append("<td>").append(sum[i]).append("</td>").append(newline);

     }else
{
      buf.append("<td> </td>").append(newline);
     }
    }
    buf.append("</tr>").append(newline);
   }
   buf.append("</table>").append(newline);
   buf.append("</div></div>");
  }

  try 
{
   out.println(buf.toString());

  } catch (Exception e) 
{
   log.error("e");
   return TagSupport.SKIP_PAGE;
  }
  return TagSupport.EVAL_PAGE;
 }



 private Hashtable getKeyGroup(String key) 
{
  if(key==null) return null;
//  返回Hashtable, 键-从1开始的行号. 值-从这行开始连续的rowspan值.
  Hashtable<Integer, Integer> ht = new Hashtable<Integer, Integer>();
  Iterator it = data.iterator();
  int count = 0;
  int start = 0;
  int current = 0;
  String value = "", prevalue = "";


  while (it.hasNext()) 
{
   current++;
   HashMap row = (HashMap) it.next();
   value = (String) row.get(key);
   if (value.equals(prevalue))
    count++;

   else 
{
    ht.put(new Integer(start),new Integer(count));
    prevalue = value;
    count = 1;
    start=current;
   }
  }
  ht.put(new Integer(start),new Integer(count));
  ht.remove(new Integer(0));
  return ht;
 }


 private Table getTableConfigBySax() 
{
  Table table = null;
  SaxTable sax = new SaxTable();
  sax.setTableName(tableName);
  String xmlfile = pageContext.getServletContext().getRealPath(
    "WEB-INF/table-config.xml");

  try 
{
   XMLReader xdr = XMLReaderFactory
     .createXMLReader("org.apache.xerces.parsers.SAXParser");
   xdr.setContentHandler(sax);
   xdr.parse(xmlfile);

  } catch (Exception e) 
{
   log.error(e);
  }
  table = sax.getTable();
  return table;
 }


 private Table getTableConfigByDom4j()
{
  Dom4jTable dt = new Dom4jTable(tableName);
  Document doc = null;
  String xmlPath = pageContext.getServletContext().getRealPath(
    "WEB-INF/table-config.xml");

  try 
{
   doc = dt.parse(xmlPath);

  } catch (DocumentException e) 
{
   log.error(e);
   return null;
  }
  Table table = dt.getTable(doc);
  return table;
 }
 

 public String getTableName() 
{
  return this.tableName;
 }


 public void setTableName(String tableName) 
{
  this.tableName = tableName;
 }


 public String getVectorName() 
{
  return this.vectorName;
 }


 public void setVectorName(String vectorName) 
{
  this.vectorName = vectorName;
  Object o = pageContext.getRequest().getAttribute(vectorName);

  if (o != null) 
{
   data = (Vector<HashMap>) o;

  } else 
{
   data = null;
  }
 }


 public void setDisplaySN(boolean sn) 
{
  displaySN = sn;
 }


 public boolean isDisplaySN() 
{
  return displaySN;
 }
}


 
在标签库app.tld中的这一段tag定义
 <tag>
  <name>showTable</name>
  <tagclass>tax.tags.ShowTableTag</tagclass>
  <bodycontent>empty</bodycontent>
  <info>
   extract the date from a vector and present date in table
  </info>
  <attribute>
   <name>vectorName</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <name>tableName</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <name>displaySN</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
 </tag>

 
最后就可以在jsp文件中使用了,例如:
<app:showTable tableName="jcgl_dzjk" vectorName="data" displaySN="true"/> 
还没有在标签里做分页的处理,以后在加工吧