001 /**
002 * =========================================
003 * LibFormula : a free Java formula library
004 * =========================================
005 *
006 * Project Info: http://reporting.pentaho.org/libformula/
007 *
008 * (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
009 *
010 * This library is free software; you can redistribute it and/or modify it under the terms
011 * of the GNU Lesser General Public License as published by the Free Software Foundation;
012 * either version 2.1 of the License, or (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016 * See the GNU Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public License along with this
019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020 * Boston, MA 02111-1307, USA.
021 *
022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023 * in the United States and other countries.]
024 *
025 *
026 * ------------
027 * $Id: DefaultDataTable.java 3521 2007-10-16 10:55:14Z tmorgner $
028 * ------------
029 * (C) Copyright 2006-2007, by Pentaho Corporation.
030 */
031 package org.jfree.formula.lvalues;
032
033 import org.jfree.formula.EvaluationException;
034 import org.jfree.formula.FormulaContext;
035 import org.jfree.formula.LibFormulaErrorValue;
036 import org.jfree.formula.typing.Type;
037 import org.jfree.formula.typing.ArrayCallback;
038 import org.jfree.formula.typing.coretypes.AnyType;
039 import org.jfree.util.ObjectTable;
040
041 /**
042 * Creation-Date: 05.11.2006, 13:34:01
043 *
044 * @author Thomas Morgner
045 * @author Cedric Pronzato
046 */
047 public class DefaultDataTable extends ObjectTable implements DataTable
048 {
049
050 private static class DefaultArrayCallback implements ArrayCallback
051 {
052 private DefaultDataTable table;
053 private TypeValuePair[][] backend;
054
055 private DefaultArrayCallback(final DefaultDataTable table)
056 {
057 this.table = table;
058 backend = new TypeValuePair[table.getRowCount()][table.getColumnCount()];
059 }
060
061 public LValue getRaw(final int row, final int column)
062 {
063 return table.getValueAt(row, column);
064 }
065
066 public Object getValue(final int row, final int column) throws EvaluationException
067 {
068 final TypeValuePair value = get(row, column);
069 return value.getValue();
070 }
071
072 private TypeValuePair get(final int row, final int column)
073 throws EvaluationException
074 {
075 TypeValuePair value = backend[row][column];
076 if(value == null)
077 {
078 value = getRaw(row, column).evaluate();
079 backend[row][column] = value;
080 }
081 return value;
082 }
083
084 public Type getType(final int row, final int column) throws EvaluationException
085 {
086 return get(row, column).getType();
087 }
088
089 public int getColumnCount()
090 {
091 return table.getColumnCount();
092 }
093
094 public int getRowCount()
095 {
096 return table.getRowCount();
097 }
098 }
099
100 private transient Boolean constant;
101 private static final LValue[] EMPTY_LVALUES = new LValue[0];
102
103 /**
104 * Creates a new table.
105 */
106 public DefaultDataTable()
107 {
108 }
109
110 public DefaultDataTable(final LValue[][] array)
111 {
112 if(array != null && array.length > 0)
113 {
114 final int colCount = array[0].length;
115 //TODO: check if it is safe to do that
116 setData(array, colCount);
117 }
118 }
119
120 public String getColumnName(int column)
121 {
122 final StringBuffer result = new StringBuffer();
123 for (; column >= 0; column = column / 26 - 1)
124 {
125 final int colChar = (char) (column % 26) + 'A';
126 result.append(colChar);
127 }
128 return result.toString();
129 }
130
131 /**
132 * Sets the object for a cell in the table. The table is expanded if
133 * necessary.
134 *
135 * @param row the row index (zero-based).
136 * @param column the column index (zero-based).
137 * @param object the object.
138 */
139 public void setObject(final int row, final int column, final LValue object)
140 {
141 super.setObject(row, column, object);
142 }
143
144 public LValue getValueAt(final int row, final int column)
145 {
146 return (LValue) getObject(row, column);
147 }
148
149 public void initialize(final FormulaContext context) throws EvaluationException
150 {
151 final int rows = getRowCount();
152 final int cols = getColumnCount();
153 for (int row = 0; row < rows; row++)
154 {
155 for (int col = 0; col < cols; col++)
156 {
157 final LValue value = getValueAt(row, col);
158 if(value != null)
159 {
160 value.initialize(context);
161 }
162 }
163 }
164 }
165
166 public TypeValuePair evaluate() throws EvaluationException
167 {
168 int colCount = -1;
169 final LValue[][] array = (LValue[][])getData();
170 for(int i=0; i<array.length; i++)
171 {
172 final LValue[] row = array[i];
173 if(colCount > 0 && row.length != colCount)
174 {
175 // error, different column count is not allowed
176 throw new EvaluationException(LibFormulaErrorValue.ERROR_ILLEGAL_ARRAY_VALUE);
177 }
178 else
179 {
180 colCount = row.length;
181 }
182 }
183 return new TypeValuePair(AnyType.ANY_ARRAY, new DefaultArrayCallback(this));
184 }
185
186 public Object clone() throws CloneNotSupportedException
187 {
188 final DefaultDataTable table = (DefaultDataTable) super.clone();
189 final Object[][] data = getData();
190 final Object[][] targetData = (Object[][]) data.clone();
191 for (int i = 0; i < targetData.length; i++)
192 {
193 final Object[] objects = targetData[i];
194 if (objects == null)
195 {
196 continue;
197 }
198
199 targetData[i] = (Object[]) objects.clone();
200 for (int j = 0; j < objects.length; j++)
201 {
202 final LValue object = (LValue) objects[j];
203 if (object == null)
204 {
205 continue;
206 }
207 objects[j] = object.clone();
208 }
209 }
210
211 table.setData(targetData, getColumnCount());
212 return table;
213 }
214
215 /**
216 * Querying the value type is only valid *after* the value has been
217 * evaluated.
218 *
219 * @return
220 */
221 public Type getValueType()
222 {
223 return AnyType.ANY_ARRAY;
224 }
225
226 /**
227 * Returns any dependent lvalues (parameters and operands, mostly).
228 *
229 * @return
230 */
231 public LValue[] getChildValues()
232 {
233 // too expensive ...
234 return EMPTY_LVALUES;
235 }
236
237 /**
238 * Checks, whether the LValue is constant. Constant lvalues always return the
239 * same value.
240 *
241 * @return
242 */
243 public boolean isConstant()
244 {
245 if (constant == null)
246 {
247 if (computeConstantValue())
248 {
249 constant = Boolean.TRUE;
250 }
251 else
252 {
253 constant = Boolean.FALSE;
254 }
255 }
256
257 return Boolean.TRUE.equals(constant);
258 }
259
260 private boolean computeConstantValue()
261 {
262 final int rows = getRowCount();
263 final int cols = getColumnCount();
264 for (int row = 0; row < rows; row++)
265 {
266 for (int col = 0; col < cols; col++)
267 {
268 final LValue value = getValueAt(row, col);
269 if (value.isConstant() == false)
270 {
271 return false;
272 }
273 }
274 }
275 return true;
276 }
277 }