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    }