/*
* Copyright (C) 2014 GG-Net GmbH - Oliver Günther.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see <http://www.gnu.org/licenses/>.
*/
package eu.ggnet.lucidcalc;
import java.util.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
/**
* A Simple Table which can be place in a Sheet
*/
// TODO: Make Table honor the type, generics
public class STable implements IDynamicCellContainer {
private class DynamicCCellReference implements CCellReference {
private int relativeColumnIndex;
private int relativeRowIndex;
private int toColumnIndex;
private int toRowIndex;
public DynamicCCellReference(int relativeColumnIndex, int relativeRowIndex) {
this.relativeColumnIndex = relativeColumnIndex;
this.relativeRowIndex = relativeRowIndex;
}
public void setToColumnIndex(int toColumnIndex) {
this.toColumnIndex = toColumnIndex;
}
public void setToRowIndex(int toRowIndex) {
this.toRowIndex = toRowIndex;
}
@Override
public int getColumnIndex() {
return relativeColumnIndex + toColumnIndex;
}
@Override
public int getRowIndex() {
return relativeRowIndex + toRowIndex;
}
}
private class DynamicTableCCellReference extends DynamicCCellReference {
public DynamicTableCCellReference(int relativeColumnIndex) {
super(relativeColumnIndex, 0);
}
@Override
public int getRowIndex() {
return model.getRowCount() + super.toRowIndex;
}
}
private List<STableColumn> columns;
private List<DynamicCCellReference> cellReferences;
@NotNull
@Valid
private STableModel model;
private CFormat headlineFormat;
private CFormat tableFormat;
private Integer headlineHeight;
private Integer rowHeight;
private SRowFormater rowFormater;
public STable() {
this.columns = new ArrayList<>();
this.cellReferences = new ArrayList<>();
}
public STable(STable old) {
this();
if ( old == null ) {
return;
}
this.headlineFormat = old.headlineFormat;
this.tableFormat = old.tableFormat;
this.headlineHeight = old.headlineHeight;
this.rowHeight = old.rowHeight;
for (STableColumn oldColumn : old.columns) {
columns.add(oldColumn);
}
}
public void setModel(STableModel model) {
this.model = model;
}
public void setHeadlineFormat(CFormat format) {
this.headlineFormat = format;
}
public void setTableFormat(CFormat tableFormat) {
this.tableFormat = tableFormat;
}
public Integer getHeadlineHeight() {
return headlineHeight;
}
public void setHeadlineHeight(Integer headlineHeight) {
this.headlineHeight = headlineHeight;
}
public Integer getRowHeight() {
return rowHeight;
}
public void setRowHeight(Integer rowHeight) {
this.rowHeight = rowHeight;
}
public void setRowFormater(SRowFormater rowFormater) {
this.rowFormater = rowFormater;
}
public STable add(STableColumn o) {
columns.add(o);
return this;
}
public void replace(int index, STableColumn o) {
columns.set(index, o);
}
public STableModel getModel() {
return model;
}
@Override
public int getRowCount() {
int count = 1; // Inc. Headline
if ( model != null ) {
count += model.getRowCount();
}
return count;
}
@Override
public int getColumnCount() {
return columns.size();
}
/**
* Returns a reference to the cell for the columnIndex and the first row
*
* @param columnIndex
* @return a reference to the cell for the columnIndex and the first row
*/
public CCellReference getCellFirstRow(int columnIndex) {
DynamicCCellReference cell = new DynamicCCellReference(columnIndex, 1);
cellReferences.add(cell);
return cell;
}
/**
* Returns a reference to the cell for the columnIndex and the last row
*
* @param columnIndex
* @return a reference to the cell for the columnIndex and the last row
*/
public CCellReference getCellLastRow(int columnIndex) {
DynamicCCellReference cell = new DynamicTableCCellReference(columnIndex);
cellReferences.add(cell);
return cell;
}
@Override
public CCellComposite shiftTo(int toColumnIndex, int toRowIndex) {
Set<CCell> cells = getCellsShiftedTo(toColumnIndex, toRowIndex);
Set<CColumnView> columnViews = new HashSet<>();
Set<CRowView> rowViews = new HashSet<>();
for (int i = 0; i < columns.size(); i++) {
if ( columns.get(i).getSize() != null ) columnViews.add(new CColumnView(i + toColumnIndex, columns.get(i).getSize()));
}
if ( headlineHeight != null ) rowViews.add(new CRowView(toRowIndex, headlineHeight));
for (int i = 1; i < getRowCount(); i++) {
if ( rowHeight != null ) rowViews.add(new CRowView(toRowIndex + i, rowHeight));
}
return new CCellComposite(cells, columnViews, rowViews);
}
/**
* Returns the Value at row and column.
* Determines the value by taking the row of the model at rowIndex and:
* <ul>
* <li>If the column has an action, this action is used at the row.</li>
* <li>If the row is an instance of List, it is casted to List and the element a columnIndex is returned.</li>
* <li>If the row is an instance of Object[], it is casted to Object[] and the element a columnIndex is returned.</li>
* <li>The row is returned.</li>
* </ul>
* <p>
* @param toColumnIndex
* @param toRowIndex
* @return the Value at row and column.
*/
@Override
public Set<CCell> getCellsShiftedTo(int toColumnIndex, int toRowIndex) {
for (DynamicCCellReference cell : cellReferences) {
cell.setToColumnIndex(toColumnIndex);
cell.setToRowIndex(toRowIndex);
}
Set<CCell> ccells = new HashSet<>();
for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) {
ccells.add(new CCell(columnIndex + toColumnIndex, 0 + toRowIndex, columns.get(columnIndex).getHead(),
CFormat.combine(headlineFormat, tableFormat)));
}
for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) {
CFormat columnFormat = CFormat.combine(columns.get(columnIndex).getFormat(), tableFormat);
for (int rowIndex = 1; rowIndex <= model.getRowCount(); rowIndex++) {
SAction action = columns.get(columnIndex).getAction();
Object row = model.getRow(rowIndex - 1); // Shift the model
Object value = row;
CFormat rowFormat = columnFormat;
if ( rowFormater != null ) rowFormat = CFormat.combine(rowFormater.getFormat(rowIndex, row), columnFormat);
if ( action != null ) {
value = action.getValue(columnIndex, rowIndex, columnIndex + toColumnIndex, rowIndex + toRowIndex, row);
rowFormat = CFormat.combine(action.getFormat(columnIndex, rowIndex, columnIndex + toColumnIndex, rowIndex + toRowIndex, row), rowFormat);
} // TODO: Remove here and build a default Model on setModel
else if ( row instanceof List ) value = ((List)row).get(columnIndex);
else if ( row instanceof Object[] ) value = ((Object[])row)[columnIndex];
ccells.add(new CCell(columnIndex + toColumnIndex, rowIndex + toRowIndex, value, rowFormat));
}
}
return ccells;
}
}