/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package cli_fmw.report.implemenatation;
import cli_fmw.report.TableReportOptions;
import cli_fmw.report.ReporterFactory;
import javax.swing.table.TableModel;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPen;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignField;
import net.sf.jasperreports.engine.design.JRDesignStyle;
import net.sf.jasperreports.engine.design.JRDesignTextField;
/**
*
* @author finder
*/
public class JasperTableBuilder implements JasperDetailBuilder {
/**
* Тип добовляемого поля, для функции создания новых полей, влияет на формат.
*/
static enum FieldTypes {
other,
tableHeader,
table,
}
/**
* Минимальный размер колонки, если колонка меньше этого разера,
* то она будет растянута засчет других колонок.
*/
static final int MINIMUM_COLOUMN_SIZE = 12;
/**
* Общие опции генерации отчета
*/
TableReportOptions options;
/**
* ссылка на модель таблицы, если отчет содержит таблицу
*/
TableModel table;
/**
* Ссылка на итерфейс изменяющий геренацию отчета, для коррекции внешнего вида,
* в процессе построения шаблона отчета, этот интерфейс прифодится к его наследжникам,
* и вызываются соотведствующие методы обработки.
*/
JasperTableBuilderCustomizer customizer;
/**
* Колонка в таблице начиная с которой будет строится отчет.
* Может использоватся для разделения большой таблицы по нескольким отчетам,
* Передачи дополнительных данны в одной из таблиц и тп.
*/
int beginColoumn;
/**
* Оконечная колонка в таблице до которой будет строится отчет.
*/
int endColoumn;
/**
* Стиль которым ресуется таблица. Обычный стиль + окантовка ечеек
*/
JRDesignStyle tableStyle;
/**
* Банд содержит заголовок таблицы высата как у элемента таблицы
*/
JRDesignBand colomnHeaderBand;
/**
* Банд с элементами таблицы
*/
JRDesignBand detalsBand;
/**
* Максимальная ширина всех строк в таблице (destingWidth минус лишний размер элементов)
*/
double stringsWidth;
JasperBuilder builder;
public JasperTableBuilder(TableReportOptions options, TableModel table) {
this.options = options;
this.table = table;
this.beginColoumn = 0;
this.endColoumn = table == null? 0: table.getColumnCount();
}
/**
* Колонка в таблице начиная с которой будет строится отчет.
* @return - Номер колонки начиная с нуля
*/
public int getBeginColoumn() {
return beginColoumn;
}
/**
* Устанавливает номер колонки таблице начиная с которой будет строится отчет.
* Может использоватся для разделения большой таблицы по нескольким отчетам,
* Передачи дополнительных данны в одной из таблиц и тп.
* @param beginColoumn - номер колонки, начиная с нуля
*/
public void setBeginColoumn(int beginColoumn) {
this.beginColoumn = beginColoumn;
}
/**
* Возврящает номер колонкт в таблице, идущей сразу после колонки,
* до которой будет строится отчет.
* @return номер колонки
*/
public int getEndColoumn() {
return endColoumn;
}
/**
* Устанавливает номер колонкт в таблице, идущей сразу после колонки,
* до которой будет строится отчет.
* @param endColoumn
*/
public void setEndColoumn(int endColoumn) {
this.endColoumn = endColoumn;
}
/**
* Возвращает минимальну высоту элемента в пикселях, в которую влазит одна строка текста.
* @return высота элемента
*/
int getElementHeight(){
double target = builder.getFieldHeigth();
JRLineBox box = tableStyle.getLineBox();
target += box.getTopPen().getLineWidth();
return (int) Math.round(target);
}
/**
* Возврашает минимальный размер элемента таблицы по горизонтали, который
* вмещает в себя эту строку без переносов.
* @param line - строка
* @return - длинну
*/
double getElementLenght(String line){
double target = getStringLength(line);
JRLineBox box = tableStyle.getLineBox();
target += box.getLeftPadding();
target += box.getRightPadding();
target += box.getLeftPen().getLineWidth();
target += 1;
return target;
}
/**
* Возвращает размер который элемен таблицы занимает дополнительно к размеру
* содержащейсе внем строки (размер на отступы, бордюр, и тп)
* @return
*/
double getElementOverhead(){
return getElementLenght("");
}
/**
* Возвращает максимальную ширина всех строк в таблице
* (destingWidth минус лишний размер элементов)
*/
public double getStringsWidth() {
return stringsWidth;
}
/**
* Взвращает длинну строки, отпечатонной стилем Normal.
* @param line
* @return
*/
double getStringLength(String str){
return builder.getStringLength(str);
}
/**
* Возвращает число колонок таблицы которое попадет в результирующий отчет.
* @return
*/
int getColoumCount(){
return Math.max(0, endColoumn - beginColoumn);
}
@Override
public void buildDetails(JasperBuilder inBuilder, JasperReportBuilderCustomizer inCustomizer) throws JRException{
if (inBuilder == null) {
throw new IllegalArgumentException("inBuilder can not be null");
}
this.builder = inBuilder;
if (inCustomizer != null && inCustomizer instanceof JasperTableBuilderCustomizer){
this.customizer = (JasperTableBuilderCustomizer)inCustomizer;
this.customizer.customizeTableBuilder(this);
}
else{
this.customizer = null;
}
initStyles();
createTable();
}
private void initStyles() throws JRException{
tableStyle = new JRDesignStyle();
tableStyle.setName("Table");
tableStyle.setDefault(false);
tableStyle.setParentStyle(builder.getNormalStyle());
JRLineBox box = tableStyle.getLineBox();
box.getPen().setLineStyle(JRPen.LINE_STYLE_SOLID);
box.getPen().setLineWidth(1);
builder.getTargetDesting().addStyle(tableStyle);
stringsWidth = builder.getDestingWidth() - (endColoumn - beginColoumn) * getElementOverhead();
if (options.showTableHeader && table != null){
colomnHeaderBand = builder.getColomnHeaderBand();
colomnHeaderBand.setSplitAllowed(false);
colomnHeaderBand.setHeight(getElementHeight());
}
if (table != null){
detalsBand = builder.getDetalsBand();
detalsBand.setSplitAllowed(false);
detalsBand.setHeight(getElementHeight());
}
}
/**
* Служебная функция функции calcTableSizes
* увеличивает значение ячейки массива длин ячеек, в соотведствии с длинной строки.
* @param str - строка чья длинна измеряется
* @param linear - метод увеличения
* @param sizes - размеры.
* @param col - колонка
*/
void increaseTableSizes(String str, boolean linear, double[] sizes, int col){
if (str!= null) {
double len = getStringLength(str);
if (linear){
if (sizes[col] < len) {
sizes[col] = len;
}
}
else{
sizes[col] = (sizes[col] + len * len);
}
}
}
/**
* Находит соотношение размеров колонок в таблице.
* Или максимальнуюю длинну элемента в колонке.
* @param linear - если истина возващает максимальный размер элемента в таблице.
* Если ложь - нелинейные коофиценты отношения размеров элементов.
* @return - массив double каждый элемент которого соотведствует одной колонке,
* притом первый элемент соотведствует превой отображамой колонке.
*/
double[] calcTableSizes(boolean linear){
if (table == null) {
return new double[0];
}
double[] target = new double[getColoumCount()];
for (int col = 0; col < getColoumCount(); col++){
String str;
if (options.showTableHeader){
str = table.getColumnName(col + beginColoumn);
increaseTableSizes(str, linear, target, col);
}
for (int row = table.getRowCount() - 1; row >= 0; row--){
str = ReporterFactory.convertDataToString(table.getValueAt(row, col + beginColoumn));
increaseTableSizes(str, linear, target, col);
}
}
if (!linear){
double rc = table.getRowCount();
if (options.showTableHeader) {
rc += 1;
}
if (rc > 0){
for (int i = 0; i < target.length; i++) {
target[i] = Math.sqrt(target[i] / rc);
}
}
}
return target;
}
JRDesignTextField createItem(FieldTypes type, int x, int y, int w, int h, JRExpression fieldExpression){
JRDesignTextField textField = builder.createItem(JasperBuilder.FieldTypes.other, x, y, w, h, fieldExpression);
textField.setStyle(tableStyle);
textField.setStretchType(JRDesignTextField.STRETCH_TYPE_RELATIVE_TO_BAND_HEIGHT);
switch (type){
case tableHeader:
if (options.tableHeaderColor != null){
textField.setBackcolor(options.tableHeaderColor);
textField.setMode(JRElement.MODE_OPAQUE);
}
break;
case table:
break;
}
return textField;
}
/**
* Создает, или находит, поле данных, ссылающиеся на одно из заначений таблици, по номеру колонки.
* @param coloumn номер колонки в таблице
* @return - поле данных
* @throws net.sf.jasperreports.engine.JRException
*/
public JRField createTableField(int coloumn) throws JRException{
String name = "COLUMN_" + coloumn;
JRDesignField dsfield = (JRDesignField) builder.getTargetDesting().getFieldsMap().get(name);
if (dsfield == null){
dsfield = new JRDesignField();
dsfield.setValueClass(String.class);
dsfield.setName(name);
builder.getTargetDesting().addField(dsfield);
}
return dsfield;
}
/**
* Созадает поле, элемент таблицы, оторое содержит либо статичный заголовок таблицы.
* Либо одну ячейку таблицы.
* @param pos - позиция элемента по горизонтали (по вертикали всегда ноль)
* @param size - размер элемента по горизонтали (по вертикали всегда в одну строку)
* @param coloumn - номер колонки для которой сооздается элемент
* @param toHeader - если истина, то это будет ячейка заголовка, если ложь то ячейка таблицы.
* @throws net.sf.jasperreports.engine.JRException
*/
public void createTableItem(int pos, int size, int coloumn, boolean toHeader) throws JRException{
JRField dataField = null;
JRExpression ex;
if (toHeader){
ex = builder.createStringExpression(table.getColumnName(coloumn));
}
else{
dataField = createTableField(coloumn);
ex = builder.createExpression("$F{"+ dataField.getName() + "}");
}
JRDesignTextField field = createItem(
toHeader? FieldTypes.tableHeader: FieldTypes.table, pos, 0,
size, getElementHeight(), ex);
boolean needIsert = true;
if (customizer != null && customizer instanceof JasperrCustomizerTableItem) {
needIsert = ((JasperrCustomizerTableItem) customizer).onNewTableElement(pos, size,
coloumn, toHeader, dataField, field);
}
if (needIsert){
if (toHeader) {
colomnHeaderBand.addElement(field);
}
else {
detalsBand.addElement(field);
}
}
}
/**
* Создает элементы таблицы
* @throws net.sf.jasperreports.engine.JRException
*/
void createTable() throws JRException{
double[] itemSizes = calcTableSizes(true);
ColoumnAligner aligner = new ColoumnAligner(itemSizes, itemSizes, beginColoumn);
double totalLen = aligner.calcTotalSize();
if (totalLen > stringsWidth){
for (int i = 0; i < itemSizes.length; i++) {
itemSizes[i] *= 1.125;
if (itemSizes[i] < MINIMUM_COLOUMN_SIZE) {
itemSizes[i] = MINIMUM_COLOUMN_SIZE;
}
}
itemSizes = calcTableSizes(false);
aligner.setSizes(itemSizes);
}
aligner.normalize(stringsWidth);
aligner.allignColouns();
double pos = 0;// = targetDesting.getLeftMargin();
for (int i = 0; i < itemSizes.length; i++){
int x = (int)Math.round(pos);
pos += itemSizes[i] + getElementOverhead();
int len = (int)Math.round(pos) - x;
if (options.showTableHeader) {
createTableItem(x, len, i + beginColoumn, true);
}
createTableItem(x, len, i + beginColoumn, false);
}
if (customizer != null && customizer instanceof JasperrCustomizerTable) {
((JasperrCustomizerTable) customizer).onTableCreated(colomnHeaderBand, detalsBand, itemSizes);
}
}
}