/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2006 - 2013 Pentaho Corporation and Contributors. All rights reserved.
*/
package org.pentaho.reporting.libraries.formula.lvalues;
import org.pentaho.reporting.libraries.base.util.ObjectTable;
import org.pentaho.reporting.libraries.formula.EvaluationException;
import org.pentaho.reporting.libraries.formula.FormulaContext;
import org.pentaho.reporting.libraries.formula.LibFormulaErrorValue;
import org.pentaho.reporting.libraries.formula.typing.ArrayCallback;
import org.pentaho.reporting.libraries.formula.typing.Type;
import org.pentaho.reporting.libraries.formula.typing.coretypes.AnyType;
/**
* Creation-Date: 05.11.2006, 13:34:01
*
* @author Thomas Morgner
* @author Cedric Pronzato
*/
public class DefaultDataTable extends ObjectTable implements DataTable {
private static class DefaultArrayCallback implements ArrayCallback {
private DefaultDataTable table;
private TypeValuePair[][] backend;
private int rowCount;
private int columnCount;
private DefaultArrayCallback( final DefaultDataTable table ) {
this.table = table;
this.rowCount = table.getRowCount();
this.columnCount = table.getColumnCount();
this.backend = new TypeValuePair[ rowCount ][ columnCount ];
}
public LValue getRaw( final int row, final int column ) {
return table.getValueAt( row, column );
}
public Object getValue( final int row, final int column ) throws EvaluationException {
final TypeValuePair value = get( row, column );
return value.getValue();
}
private TypeValuePair get( final int row, final int column )
throws EvaluationException {
if ( row < 0 || row >= rowCount ) {
throw EvaluationException.getInstance( LibFormulaErrorValue.ERROR_ILLEGAL_ARRAY_VALUE );
}
if ( column < 0 || column >= columnCount ) {
throw EvaluationException.getInstance( LibFormulaErrorValue.ERROR_ILLEGAL_ARRAY_VALUE );
}
try {
TypeValuePair value = backend[ row ][ column ];
if ( value == null ) {
value = getRaw( row, column ).evaluate();
backend[ row ][ column ] = value;
}
return value;
} catch ( IndexOutOfBoundsException ioe ) {
throw EvaluationException.getInstance( LibFormulaErrorValue.ERROR_ILLEGAL_ARRAY_VALUE );
}
}
public Type getType( final int row, final int column ) throws EvaluationException {
return get( row, column ).getType();
}
public int getColumnCount() {
return table.getColumnCount();
}
public int getRowCount() {
return table.getRowCount();
}
}
private transient Boolean constant;
private static final LValue[] EMPTY_LVALUES = new LValue[ 0 ];
private static final long serialVersionUID = 4942690291611203409L;
private ParsePosition parsePosition;
/**
* Creates a new table.
*/
public DefaultDataTable() {
setData( new LValue[ 0 ][ 0 ], 0 );
}
public DefaultDataTable( final LValue[][] array ) {
if ( array != null && array.length > 0 ) {
final int colCount = array[ 0 ].length;
setData( array, colCount );
} else {
setData( new LValue[ 0 ][ 0 ], 0 );
}
}
public ParsePosition getParsePosition() {
return parsePosition;
}
public void setParsePosition( final ParsePosition parsePosition ) {
this.parsePosition = parsePosition;
}
public String getColumnName( int column ) {
final StringBuffer result = new StringBuffer( 10 );
for (; column >= 0; column = column / 26 - 1 ) {
final int colChar = (char) ( column % 26 ) + 'A';
result.append( colChar );
}
return result.toString();
}
/**
* Sets the object for a cell in the table. The table is expanded if necessary.
*
* @param row the row index (zero-based).
* @param column the column index (zero-based).
* @param object the object.
*/
public void setObject( final int row, final int column, final LValue object ) {
super.setObject( row, column, object );
}
public LValue getValueAt( final int row, final int column ) {
return (LValue) getObject( row, column );
}
public void initialize( final FormulaContext context ) throws EvaluationException {
final int rows = getRowCount();
final int cols = getColumnCount();
for ( int row = 0; row < rows; row++ ) {
for ( int col = 0; col < cols; col++ ) {
final LValue value = getValueAt( row, col );
if ( value != null ) {
value.initialize( context );
}
}
}
}
public TypeValuePair evaluate() throws EvaluationException {
int colCount = -1;
final LValue[][] array = (LValue[][]) getData();
for ( int i = 0; i < array.length; i++ ) {
final LValue[] row = array[ i ];
if ( colCount > 0 && row.length != colCount ) {
// error, different column count is not allowed
throw EvaluationException.getInstance( LibFormulaErrorValue.ERROR_ILLEGAL_ARRAY_VALUE );
} else {
colCount = row.length;
}
}
return new TypeValuePair( AnyType.ANY_ARRAY, new DefaultArrayCallback( this ) );
}
public ArrayCallback getAsArray() {
return new DefaultArrayCallback( this );
}
public Object clone() throws CloneNotSupportedException {
final DefaultDataTable table = (DefaultDataTable) super.clone();
final Object[][] data = getData();
final Object[][] targetData = (Object[][]) data.clone();
for ( int i = 0; i < targetData.length; i++ ) {
final Object[] objects = targetData[ i ];
if ( objects == null ) {
continue;
}
targetData[ i ] = (Object[]) objects.clone();
for ( int j = 0; j < objects.length; j++ ) {
final LValue object = (LValue) objects[ j ];
if ( object == null ) {
continue;
}
objects[ j ] = object.clone();
}
}
table.setData( targetData, getColumnCount() );
return table;
}
/**
* Querying the value type is only valid *after* the value has been evaluated.
*
* @return
*/
public Type getValueType() {
return AnyType.ANY_ARRAY;
}
/**
* Returns any dependent lvalues (parameters and operands, mostly).
*
* @return
*/
public LValue[] getChildValues() {
// too expensive ...
return EMPTY_LVALUES;
}
/**
* Checks whether the LValue is constant. Constant lvalues always return the same value.
*
* @return
*/
public boolean isConstant() {
if ( constant == null ) {
if ( computeConstantValue() ) {
constant = Boolean.TRUE;
} else {
constant = Boolean.FALSE;
}
}
return Boolean.TRUE.equals( constant );
}
private boolean computeConstantValue() {
final int rows = getRowCount();
final int cols = getColumnCount();
for ( int row = 0; row < rows; row++ ) {
for ( int col = 0; col < cols; col++ ) {
final LValue value = getValueAt( row, col );
if ( value.isConstant() == false ) {
return false;
}
}
}
return true;
}
public String toString() {
final StringBuffer b = new StringBuffer();
final int rowcount = getRowCount();
final int colcount = getColumnCount();
b.append( "{" );
for ( int row = 0; row < rowcount; row += 1 ) {
if ( row > 0 ) {
b.append( "|" );
}
for ( int col = 0; col < colcount; col += 1 ) {
if ( col > 0 ) {
b.append( ";" );
}
b.append( getValueAt( row, col ) );
}
}
b.append( "}" );
return b.toString();
}
}