kspread

kspread_functions_reference.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998-2002 The KSpread Team
3 www.koffice.org/kspread
4 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22// built-in reference functions
23
24#include "kspread_cell.h"
25#include "kspread_sheet.h"
26#include "kspread_util.h"
27#include "kspread_value.h"
28
29#include "functions.h"
30#include "valuecalc.h"
31#include "valueconverter.h"
32
33using namespace KSpread;
34
35// prototypes (sorted alphabetically)
36Value func_address (valVector args, ValueCalc *calc, FuncExtra *);
37Value func_areas (valVector args, ValueCalc *calc, FuncExtra *);
38Value func_choose (valVector args, ValueCalc *calc, FuncExtra *);
39Value func_column (valVector args, ValueCalc *calc, FuncExtra *);
40Value func_columns (valVector args, ValueCalc *calc, FuncExtra *);
41Value func_hlookup (valVector args, ValueCalc *calc, FuncExtra *);
42Value func_index (valVector args, ValueCalc *calc, FuncExtra *);
43Value func_indirect (valVector args, ValueCalc *calc, FuncExtra *);
44Value func_lookup (valVector args, ValueCalc *calc, FuncExtra *);
45Value func_row (valVector args, ValueCalc *calc, FuncExtra *);
46Value func_rows (valVector args, ValueCalc *calc, FuncExtra *);
47Value func_vlookup (valVector args, ValueCalc *calc, FuncExtra *);
48
49// registers all reference functions
50void RegisterReferenceFunctions()
51{
52 FunctionRepository* repo = FunctionRepository::self();
53 Function *f;
54
55 f = new Function ("ADDRESS", func_address);
56 f->setParamCount (2, 5);
57 repo->add (f);
58 f = new Function ("AREAS", func_areas);
59 f->setParamCount (1);
60 f->setNeedsExtra (true);
61 repo->add (f);
62 f = new Function ("CHOOSE", func_choose);
63 f->setParamCount (2, -1);
64 f->setAcceptArray ();
65 repo->add (f);
66 f = new Function ("COLUMN", func_column);
67 f->setParamCount (0, 1);
68 repo->add (f);
69 f = new Function ("COLUMNS", func_columns);
70 f->setParamCount (1);
71 f->setAcceptArray ();
72 f->setNeedsExtra (true);
73 repo->add (f);
74 f = new Function ("HLOOKUP", func_hlookup);
75 f->setParamCount (3, 4);
76 f->setAcceptArray ();
77 repo->add (f);
78 f = new Function ("INDEX", func_index);
79 f->setParamCount (3);
80 f->setAcceptArray ();
81 repo->add (f);
82 f = new Function ("INDIRECT", func_indirect);
83 f->setParamCount (1, 2);
84 f->setNeedsExtra (true);
85 repo->add (f);
86 f = new Function ("LOOKUP", func_lookup);
87 f->setParamCount (3);
88 f->setAcceptArray ();
89 repo->add (f);
90 f = new Function ("ROW", func_row);
91 f->setParamCount (0, 1);
92 repo->add (f);
93 f = new Function ("ROWS", func_rows);
94 f->setParamCount (1);
95 f->setAcceptArray ();
96 f->setNeedsExtra (true);
97 repo->add (f);
98 f = new Function ("VLOOKUP", func_vlookup);
99 f->setParamCount (3, 4);
100 f->setAcceptArray ();
101 repo->add (f);
102}
103
104// Function: ADDRESS
105Value func_address (valVector args, ValueCalc *calc, FuncExtra *)
106{
107 bool r1c1 = false;
108 TQString sheetName;
109 int absNum = 1;
110 if (args.count() > 2)
111 absNum = calc->conv()->asInteger (args[2]).asInteger();
112 if (args.count() > 3)
113 r1c1 = !(calc->conv()->asBoolean (args[3]).asBoolean());
114 if (args.count() == 5)
115 sheetName = calc->conv()->asString (args[4]).asString();
116
117 TQString result;
118 int row = calc->conv()->asInteger (args[0]).asInteger();
119 int col = calc->conv()->asInteger (args[1]).asInteger();
120
121 if ( !sheetName.isEmpty() )
122 {
123 result += sheetName;
124 result += "!";
125 }
126
127 if ( r1c1 )
128 {
129 // row first
130 bool abs = false;
131 if ( absNum == 1 || absNum == 2 )
132 abs = true;
133
134 result += 'R';
135 if ( !abs )
136 result += '[';
137 result += TQString::number( row );
138
139 if ( !abs )
140 result += ']';
141
142 // column
143 abs = false;
144 if ( absNum == 1 || absNum == 3 )
145 abs = true;
146
147 result += 'C';
148 if ( !abs )
149 result += '[';
150 result += TQString::number( col );
151
152 if ( !abs )
153 result += ']';
154 }
155 else
156 {
157 bool abs = false;
158 if ( absNum == 1 || absNum == 3 )
159 abs = true;
160
161 if ( abs )
162 result += '$';
163
164 result += Cell::columnName( col );
165
166 abs = false;
167 if ( absNum == 1 || absNum == 2 )
168 abs = true;
169
170 if ( abs )
171 result += '$';
172
173 result += TQString::number( row );
174 }
175
176 return Value (result);
177}
178
179bool checkRef( TQString const & ref )
180{
181 Range r( ref );
182 if ( !r.isValid() )
183 {
184 Point p( ref );
185 if ( !p.isValid() )
186 return false;
187 }
188 return true;
189}
190
191// Function: AREAS
192Value func_areas (valVector args, ValueCalc *calc, FuncExtra *e)
193{
194 if (e) {
195 if ((e->ranges[0].col1 != -1) && (e->ranges[0].row1 != -1) &&
196 (e->ranges[0].col2 != -1) && (e->ranges[0].row2 != -1))
197 // we have a range reference - return 1
198 return 1;
199 }
200
201 TQString s = calc->conv()->asString (args[0]).asString();
202 if ( s[0] != '(' || s[s.length() - 1] != ')' )
203 return Value::errorVALUE();
204
205 int l = s.length();
206
207 int num = 0;
208 TQString ref;
209 for ( int i = 1; i < l; ++i )
210 {
211 if ( s[i] == ',' || s[i] == ')' )
212 {
213 if ( !checkRef( ref ) )
214 return Value::errorVALUE();
215 else
216 {
217 ++num;
218 ref = "";
219 }
220 }
221 else
222 ref += s[i];
223 }
224
225 return Value (num);
226}
227
228// Function: CHOOSE
229Value func_choose (valVector args, ValueCalc *calc, FuncExtra *)
230{
231 int cnt = args.count () - 1;
232 int num = calc->conv()->asInteger (args[0]).asInteger();
233 if ((num <= 0) || (num > cnt))
234 return Value::errorVALUE();
235 return args[num];
236}
237
238// Function: HLOOKUP
239Value func_hlookup (valVector args, ValueCalc *calc, FuncExtra *)
240{
241 const Value key = args[0];
242 const Value data = args[1];
243 const int row = calc->conv()->asInteger( args[2] ).asInteger();
244 const int cols = data.columns();
245 const int rows = data.rows();
246 if ( row < 1 || row > rows )
247 return Value::errorVALUE();
248 const bool rangeLookup = ( args.count() > 3 ) ? calc->conv()->asBoolean( args[3] ).asBoolean() : true;
249
250 // now traverse the array and perform comparison
251 Value r;
252 Value v = Value::errorNA();
253 for (int col = 0; col < cols; ++col) {
254 // search in the first row
255 const Value le = data.element(col, 0);
256 if (calc->naturalEqual(key, le)) {
257 return data.element(col, row - 1);
258 }
259 // optionally look for the next largest value that is less than key
260 if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) {
261 r = le;
262 v = data.element(col, row - 1);
263 }
264 }
265 return v;
266}
267
268// Function: INDEX
269Value func_index (valVector args, ValueCalc *calc, FuncExtra *)
270{
271 // first argument can be either a range, then we return a given cell's
272 // value, or a single cell containing an array - then we return the array
273 // element. In any case, this function can assume that the given value
274 // is the same. Because it is.
275
276 Value val = args[0];
277 unsigned row = calc->conv()->asInteger (args[1]).asInteger() - 1;
278 unsigned col = calc->conv()->asInteger (args[2]).asInteger() - 1;
279 if ((row >= val.rows()) || (col >= val.columns()))
280 return Value::errorREF();
281 return val.element (col, row);
282}
283
284// Function: LOOKUP
285Value func_lookup (valVector args, ValueCalc *calc, FuncExtra *)
286{
287 Value num = calc->conv()->asNumeric (args[0]);
288 if (num.isArray())
289 return Value::errorVALUE();
290 Value lookup = args[1];
291 Value rr = args[2];
292 unsigned cols = lookup.columns();
293 unsigned rows = lookup.rows();
294 if ((cols != rr.columns()) || (rows != rr.rows()))
295 return Value::errorVALUE();
296 Value res = Value::errorNA();
297
298 // now traverse the array and perform comparison
299 for (unsigned r = 0; r < rows; ++r)
300 for (unsigned c = 0; c < cols; ++c)
301 {
302 // update the result, return if we cross the line
303 Value le = lookup.element (c, r);
304 if (calc->lower (le, num) || calc->equal (num, le))
305 res = rr.element (c, r);
306 else
307 return res;
308 }
309 return res;
310}
311
312// Function: COLUMN
313Value func_column (valVector args, ValueCalc *, FuncExtra *e)
314{
315 int col = e ? e->mycol : 0;
316 if (e && args.count())
317 col = e->ranges[0].col1;
318 if (col > 0)
319 return Value (col);
320 return Value::errorVALUE();
321}
322
323// Function: ROW
324Value func_row (valVector args, ValueCalc *, FuncExtra *e)
325{
326 int row = e ? e->myrow : 0;
327 if (e && args.count())
328 row = e->ranges[0].row1;
329 if (row > 0)
330 return Value (row);
331 return Value::errorVALUE();
332}
333
334// Function: COLUMNS
335Value func_columns (valVector, ValueCalc *, FuncExtra *e)
336{
337 int col1 = e->ranges[0].col1;
338 int col2 = e->ranges[0].col2;
339 if ((col1 == -1) || (col2 == -1))
340 return Value::errorVALUE();
341 return Value (col2 - col1 + 1);
342}
343
344// Function: ROWS
345Value func_rows (valVector, ValueCalc *, FuncExtra *e)
346{
347 int row1 = e->ranges[0].row1;
348 int row2 = e->ranges[0].row2;
349 if ((row1 == -1) || (row2 == -1))
350 return Value::errorVALUE();
351 return Value (row2 - row1 + 1);
352}
353
354
355// Function: INDIRECT
356Value func_indirect (valVector args, ValueCalc *calc, FuncExtra *e)
357{
358 bool r1c1 = false;
359 TQString ref = calc->conv()->asString (args[0]).asString();
360 if (args.count() == 2)
361 r1c1 = !(calc->conv()->asBoolean (args[1]).asBoolean());
362
363 if (ref.isEmpty())
364 return Value::errorVALUE();
365
366 if ( r1c1 )
367 {
368 // TODO: translate the r1c1 style to a1 style
369 ref = ref;
370 }
371
372 Point p (ref, e->sheet->workbook(), e->sheet);
373
374 if ( !p.isValid() )
375 return Value::errorVALUE();
376
377 Cell * cell = p.cell();
378 if (cell)
379 return cell->value();
380 return Value::errorVALUE();
381}
382
383// Function: VLOOKUP
384Value func_vlookup (valVector args, ValueCalc *calc, FuncExtra *)
385{
386 const Value key = args[0];
387 const Value data = args[1];
388 const int col = calc->conv()->asInteger(args[2]).asInteger();
389 const int cols = data.columns();
390 const int rows = data.rows();
391 if (col < 1 || col > cols)
392 return Value::errorVALUE();
393 const bool rangeLookup = (args.count() > 3) ? calc->conv()->asBoolean(args[3]).asBoolean() : true;
394
395 // now traverse the array and perform comparison
396 Value r;
397 Value v = Value::errorNA();
398 for (int row = 0; row < rows; ++row) {
399 // search in the first column
400 const Value le = data.element(0, row);
401 if (calc->naturalEqual(key, le)) {
402 return data.element(col - 1, row);
403 }
404 // optionally look for the next largest value that is less than key
405 if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) {
406 r = le;
407 v = data.element(col - 1, row);
408 }
409 }
410 return v;
411}
For every cell in the spread sheet there is a Cell object.
const Value value() const
Returns the value that this cell holds.
TQString columnName() const
Returns the column name of the cell.
Represents the position of a single cell in a workbook.
Represents a region within a workbook.
The ValueCalc class is used to perform all sorts of calculations.
Definition valuecalc.h:69
bool equal(const Value &a, const Value &b)
numerical comparison
bool naturalLower(const Value &a, const Value &b, bool CS=true)
string or numerical comparison
bool naturalEqual(const Value &a, const Value &b, bool CS=true)
string or numerical comparison
bool lower(const Value &a, const Value &b)
numerical comparison
Value asNumeric(const Value &value) const
convert to a numeric value - not necessarily float
Provides a wrapper for cell value.
unsigned columns() const
If this value is an array, return the number of columns.
Value element(unsigned column, unsigned row) const
Return an element in the array value.
unsigned rows() const
If this value is an array, return the number of rows.
bool isArray() const
Returns true if the type of this value is array.
static const Value & errorREF()
Returns constant reference to #REF! error.
long asInteger() const
Returns the integer value of this value.
TQString asString() const
Returns the string value of this value.
static const Value & errorVALUE()
Returns constant reference to #VALUE! error.
bool asBoolean() const
Returns the boolean value of this value.
static const Value & errorNA()
Returns constant reference to #N/A error.
The KSpread namespace.
Definition commands.h:39