📄 reportmanager.java
字号:
package com.lucaslee.report;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import org.apache.commons.lang.ObjectUtils;
import com.lucaslee.report.grouparithmetic.GroupArithmetic;
import com.lucaslee.report.model.Report;
import com.lucaslee.report.model.Table;
import com.lucaslee.report.model.TableCell;
import com.lucaslee.report.model.TableColumn;
import com.lucaslee.report.model.TableLine;
import com.lucaslee.report.model.TableRow;
import com.lucaslee.report.model.crosstable.CrossTable;
import com.lucaslee.report.model.crosstable.HeadCol;
/**
* 报表管理器。主要的报表操作都在此类中进行。
* <p>
* Copyright: Copyright (c) 2004
* </p>
* <p>
* Company:Lucas-lee Soft
* </p>
*
* @author Lucas Lee
* @version 1.0
*/
public class ReportManager {
// 为true则打印调试信息
private boolean debug = false;
/** 行方向,即行延伸的方向,水平向右。 */
public static final int ROW_ORIENTATION = 1;
/** 列方向,即列延伸的方向,垂直向下 */
public static final int COLUMN_ORIENTATION = 2;
/**
* 检测一个数是否在数组中。
*
* @param a
* 要检测的数
* @param col
* 数组
* @return
*/
private boolean isAmonge(int a, int[] col) {
for (int i = 0; i < col.length; i++) {
if (a == col[i]) {
return true;
}
}
return false;
}
/**
* 获得一个汇总行.
*
* @param t
* 表格对象
* @param from
* 起始行,包括此行
* @param end
* 终止行,不包括此行
* @param memo
* 汇总行的注释
* @param totalCols
* 进行汇总列号数组
* @param arith
* 统计算法
*/
private TableRow getTotalRow(Table t, int from, int end, String memo,
int[] totalCols, GroupArithmetic arith) throws ReportException {
TableRow totalRow = new TableRow();
int[] tempTotalCols = (int[]) totalCols.clone(); // 克隆到新变量是为了不改变入口参数
Arrays.sort(tempTotalCols);
int nonTotal = 0; // 指明头几个不需要汇总的列,值为这其中最后一个列数。
if (tempTotalCols.length > 0) {
nonTotal = tempTotalCols[0] - 1;
}
// 添加注释单元
TableCell desc = new TableCell(memo);
desc.setColSpan(nonTotal + 1);
totalRow.addCell(desc);
// 这几个单元是为了符合表格标准(每行的cell数一致),使每行的单元数相等,不显示出来
for (int i = 0; i < nonTotal; i++) {
TableCell nullCell = new TableCell("");
nullCell.setIsHidden(true);
totalRow.addCell(nullCell);
}
for (int k = 0; k < t.getColCount(); k++) { // 遍历列
if (k > nonTotal) { // "小计"列不用添加了
TableCell c = new TableCell("");
if (isAmonge(k, totalCols)) { // 指定的统计列才进行汇总,非统计列是一个空字符串
double[] values = new double[end - from]; // 记录要汇总的一个序列的数据
for (int j = from; j < end; j++) { // 从上至下扫描
try {
if (debug) {
System.out.println("row:" + j + " col:" + k);
}
String value = (String) t.getRow(j).getCell(k)
.getContent();
if (value != null) {
values[j - from] = Double.parseDouble(value);
}
} catch (NumberFormatException e) {
}
}
c.setContent(arith.getResult(values) + "");
}
totalRow.addCell(c);
}
}
return totalRow;
}
/**
* 用父级列形成的表格线分割子列。 一个表可能有多列需要进行同值合并,此方法使前面的列总能完全包容后面的列.
* 即从逻辑上说,若此表表示一个分类表,前面的列总是后面的父级分类。 实现的操作是,用父级列形成的表格线分割子列。
*
* @param t
* 表格
* @param cols
* 要进行合并的列,应该是连续的
*/
public Table split(Table t, int[] cols) throws ReportException {
for (int i = 1; i < cols.length; i++) { // 从左至右扫描
TableColumn pre = t.getCol(i - 1);
TableColumn curr = t.getCol(i);
for (int j = 0; j < pre.getCellCount(); j++) { // 从上至下扫描
if (pre.getCell(j).getIsHidden() == false) {
if (curr.getCell(j).getIsHidden() == false || j == 0) {
continue;
}
for (int k = j - 1; k >= 0; k--) { // 从下至上扫描
if (curr.getCell(k).getIsHidden() == false) {
curr.getCell(j).setRowSpan(
curr.getCell(k).getRowSpan() - (j - k));
curr.getCell(k).setRowSpan(j - k);
break;
}
}
curr.getCell(j).setIsHidden(false);
}
}
}
return t;
}
/**
* 格式化交叉表的数据。
*
* @param t
* 交叉数据表
* @param crossTab
* 交叉表定义
* @param f
* 格式化对象
* @return 处理后的数据表
* @throws ReportException
*/
public Table formatData(Table t, CrossTable crossTab, Formatter f)
throws ReportException {
return formatData(t, crossTab.getColHeader().length, t.getRowCount(),
crossTab.getRowHeader().length, t.getColCount(), f);
}
/**
* 格式化数据.
*
* @param t
* 要处理的表
* @param fromRow
* 开始行,包含此行
* @param toRow
* 终止行,不包含此行
* @param fromCol
* 开始列,包含此列
* @param toCol
* 终止列,不包含此列
* @param f
* 格式化对象
* @return 处理完的表
* @throws ReportException
*/
public Table formatData(Table t, int fromRow, int toRow, int fromCol,
int toCol, Formatter f) throws ReportException {
String str;
for (int i = fromRow; i < toRow; i++) {
for (int j = fromCol; j < toCol; j++) {
try {
str = f.format((String) t.getCell(i, j).getContent());
t.getCell(i, j).setContent(str);
} catch (IllegalArgumentException e) {
// 忽略并继续
} catch (ParseException e) {
// 忽略并继续
}
} // for
}
return t;
}
/**
* 格式化表格数据.
*
* @param t
* 表格
* @param cols
* 在此数组里的列才进行格式化
* @param f
* 格式化对象
*/
public Table formatData(Table t, int[] cols, Formatter f)
throws ReportException {
for (int i = 0; i < cols.length; i++) {
formatData(t, 0, t.getRowCount(), cols[i], cols[i] + 1, f);
}
return t;
}
/**
* 操作一个行或列,合并相邻且内容相同的单元。
*
* @param line
* 要操作的列对象。
* @return
*/
public TableLine mergeSameCell(TableLine line) throws ReportException {
String curr = null; // 当前单元内容
String pre = null; // 上一单元内容
int pointer = 0; // 位置指针,指向相邻同值单元组的第一个单元
int count = 1; // 相邻同值单元组内单元数
for (int i = 0; i < line.getCellCount(); i++) { // 从上至下扫描
curr = (String) line.getCell(i).getContent();
if (i > 0) {
if ((curr != null && ObjectUtils.equals(curr, pre))
|| (curr == null && pre == null)) { // 若当前单元与上一个单元值相等,
count++;
// 则让第一个同值单元的span值加1;
line.setSpan(line.getCell(pointer), count);
line.getCell(i).setIsHidden(true); // 且不显示本单元。
} else { // 若不相等
pointer = i; // 则指针指向本单元
count = 1; // 且清空计数器
}
}
pre = curr;
}
return line;
}
/**
* 操作一个表格对象,合并相邻且内容相同的单元。
*
* @param t
* 表格
* @param lines
* 要合并的列的列号。
* @param orientation
* 操作的行或列方向。接受本类中后缀为_ORIENTATION的常数。
*/
public Table mergeSameCells(Table t, int[] lines, int orientation)
throws ReportException {
for (int i = 0; i < lines.length; i++) {
if (orientation == this.ROW_ORIENTATION) {
t
.setRow(lines[i], (TableRow) mergeSameCell(t
.getRow(lines[i])));
} else if (orientation == this.COLUMN_ORIENTATION) {
t.setCol(lines[i], (TableColumn) mergeSameCell(t
.getCol(lines[i])));
}
}
return t;
}
/**
* 获得不重复的元素集合,并按指定顺序排序。
*
* @param line
* 要处理的集合。
* @param seq
* 用于指定排序的元素集合。 如:给出D,B,C元素集合,则结果按这个序列排序,不在此序列中的不排序。
* @return
* @throws ReportException
*/
public Set getDistinctSet(TableLine line, Vector seq)
throws ReportException {
Comparator comp = new SortComparator(seq);
Vector contents = new Vector();
for (int i = 0; i < line.getCellCount(); i++) {
contents.add(line.getCell(i).getContent());
}
// 去除重复元素
TreeSet temp = new TreeSet(contents);
// 按指定顺序排序
TreeSet result = new TreeSet(comp);
result.addAll(temp);
return result;
}
/**
* 在表的指定若干列中查找这几列的单元都匹配的行号
*
* @param col
* 列号数组
* @param val
* 要比较的值数组,应该和col的长度相同
* @param t
* 表
*/
private int searchRow(int[] col, Object[] val, Table t)
throws ReportException {
if (col.length != val.length) {
throw new ReportException("输入的参数有误.");
}
int rt = -1;
if (t == null || t.getRowCount() <= 0) {
return rt;
}
for (int i = 0; i < t.getRowCount(); i++) {
boolean tag = true; // 在每一行的查找前,置为true
for (int j = 0; j < col.length; j++) {
if (debug) {
System.out.println(t.getCell(i, col[j]).getContent() + "--"
+ val[j]);
}
if (!ObjectUtils.equals(t.getCell(i, col[j]).getContent(),
val[j])) {
tag = false;
if (debug) {
System.out.println("不相等:" + tag);
}
break;
}
}
if (tag == true) { // 如果在这一行的比较中,都匹配,则返回行号。
if (debug)
System.out.println("kk:" + tag);
return i;
}
}
if (debug) {
System.out.println("searchRow end-------------");
}
return rt;
}
/**
* 生成交叉表的列汇总统计。 <BR>
* TODO 仅用旋转方式来验证算法,应该直接使用列汇总算法,应该比较容易做到。
*
* @param t
* 数据表
* @param crossTab
* 交叉表对象
* @param isSubTotal
* 是否小计。true则进行小计。
* @param arith
* 统计算法
* @return 处理后的表格
* @throws ReportException
*/
public Table generateCrossTabColTotal(Table t, CrossTable crossTab,
boolean isSubTotal, GroupArithmetic arith) throws ReportException {
int[] cols = new int[t.getRowCount() - crossTab.getColHeader().length];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -