⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 reportmanager.java

📁 sample目录下是例子程序。 1.分组汇总例子 TestGroupReport.java 2.交叉报表例子 TestCrossReport.java 需要使用crossTabSample.x
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
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 + -