File indexing completed on 2024-05-12 16:35:33

0001 /* This file is part of the KDE project
0002    Copyright (C) 1998-2002 The KSpread Team <calligra-devel@kde.org>
0003    Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; only
0008    version 2 of the License.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 // built-in reference functions
0022 #include "ReferenceModule.h"
0023 
0024 #include "Cell.h"
0025 #include "Region.h"
0026 #include "Sheet.h"
0027 #include "Util.h"
0028 #include "Value.h"
0029 #include "Map.h"
0030 #include "CalculationSettings.h"
0031 #include "CellStorage.h"
0032 #include "Formula.h"
0033 #include "Function.h"
0034 #include "FunctionModuleRegistry.h"
0035 #include "ValueCalc.h"
0036 #include "ValueConverter.h"
0037 
0038 using namespace Calligra::Sheets;
0039 
0040 // prototypes (sorted alphabetically)
0041 Value func_address(valVector args, ValueCalc *calc, FuncExtra *);
0042 Value func_areas(valVector args, ValueCalc *calc, FuncExtra *);
0043 Value func_cell(valVector args, ValueCalc *calc, FuncExtra *e);
0044 Value func_choose(valVector args, ValueCalc *calc, FuncExtra *);
0045 Value func_column(valVector args, ValueCalc *calc, FuncExtra *);
0046 Value func_columns(valVector args, ValueCalc *calc, FuncExtra *);
0047 Value func_hlookup(valVector args, ValueCalc *calc, FuncExtra *);
0048 Value func_index(valVector args, ValueCalc *calc, FuncExtra *);
0049 Value func_indirect(valVector args, ValueCalc *calc, FuncExtra *);
0050 Value func_lookup(valVector args, ValueCalc *calc, FuncExtra *);
0051 Value func_match(valVector args, ValueCalc *calc, FuncExtra *);
0052 Value func_multiple_operations(valVector args, ValueCalc *calc, FuncExtra *);
0053 Value func_offset(valVector args, ValueCalc *calc, FuncExtra *);
0054 Value func_row(valVector args, ValueCalc *calc, FuncExtra *);
0055 Value func_rows(valVector args, ValueCalc *calc, FuncExtra *);
0056 Value func_sheet(valVector args, ValueCalc *calc, FuncExtra *);
0057 Value func_sheets(valVector args, ValueCalc *calc, FuncExtra *);
0058 Value func_vlookup(valVector args, ValueCalc *calc, FuncExtra *);
0059 
0060 
0061 CALLIGRA_SHEETS_EXPORT_FUNCTION_MODULE("kspreadreferencemodule.json", ReferenceModule)
0062 
0063 
0064 ReferenceModule::ReferenceModule(QObject* parent, const QVariantList&)
0065         : FunctionModule(parent)
0066 {
0067     Function *f;
0068 
0069     f = new Function("ADDRESS",  func_address);
0070     f->setParamCount(2, 5);
0071     add(f);
0072     f = new Function("AREAS",    func_areas);
0073     f->setParamCount(1);
0074     f->setNeedsExtra(true);
0075     f->setAcceptArray();
0076     add(f);
0077     f = new Function("CELL", func_cell);
0078     f->setParamCount(1, 2);
0079     f->setAcceptArray();
0080     f->setNeedsExtra(true);
0081     add(f);
0082     f = new Function("CHOOSE",   func_choose);
0083     f->setParamCount(2, -1);
0084     f->setAcceptArray();
0085     add(f);
0086     f = new Function("COLUMN",   func_column);
0087     f->setParamCount(0, 1);
0088     add(f);
0089     f = new Function("COLUMNS",  func_columns);
0090     f->setParamCount(1);
0091     f->setAcceptArray();
0092     f->setNeedsExtra(true);
0093     add(f);
0094     f = new Function("HLOOKUP",  func_hlookup);
0095     f->setParamCount(3, 4);
0096     f->setAcceptArray();
0097     add(f);
0098     f = new Function("INDEX",   func_index);
0099     f->setParamCount(3);
0100     f->setAcceptArray();
0101     add(f);
0102     f = new Function("INDIRECT", func_indirect);
0103     f->setParamCount(1, 2);
0104     f->setNeedsExtra(true);
0105     add(f);
0106     f = new Function("LOOKUP",   func_lookup);
0107     f->setParamCount(3);
0108     f->setAcceptArray();
0109     add(f);
0110     f = new Function("MATCH", func_match);
0111     f->setParamCount(2, 3);
0112     f->setAcceptArray();
0113     f->setNeedsExtra(true);
0114   add(f);
0115     f = new Function("MULTIPLE.OPERATIONS", func_multiple_operations);
0116     f->setParamCount(3, 5);
0117     f->setNeedsExtra(true);
0118     add(f);
0119     f = new Function("OFFSET", func_offset);
0120     f->setParamCount(3, 5);
0121     f->setNeedsExtra(true);
0122     add(f);
0123     f = new Function("ROW",      func_row);
0124     f->setParamCount(0, 1);
0125     add(f);
0126     f = new Function("ROWS",     func_rows);
0127     f->setParamCount(1);
0128     f->setAcceptArray();
0129     f->setNeedsExtra(true);
0130     add(f);
0131     f = new Function("SHEET", func_sheet);
0132     f->setParamCount(0, 1);
0133     f->setNeedsExtra(true);
0134     add(f);
0135     f = new Function("SHEETS", func_sheets);
0136     f->setParamCount(0, 1);
0137     f->setAcceptArray();
0138     f->setNeedsExtra(true);
0139     add(f);
0140     f = new Function("VLOOKUP",  func_vlookup);
0141     f->setParamCount(3, 4);
0142     f->setAcceptArray();
0143     add(f);
0144 }
0145 
0146 QString ReferenceModule::descriptionFileName() const
0147 {
0148     return QString("reference.xml");
0149 }
0150 
0151 
0152 //
0153 // Function: ADDRESS
0154 //
0155 Value func_address(valVector args, ValueCalc *calc, FuncExtra *)
0156 {
0157     bool r1c1 = false;
0158     QString sheetName;
0159     int absNum = 1;
0160     if (args.count() > 2)
0161         absNum = calc->conv()->asInteger(args[2]).asInteger();
0162     if (args.count() > 3)
0163         r1c1 = !(calc->conv()->asBoolean(args[3]).asBoolean());
0164     if (args.count() == 5)
0165         sheetName = calc->conv()->asString(args[4]).asString();
0166 
0167     QString result;
0168     int row = calc->conv()->asInteger(args[0]).asInteger();
0169     int col = calc->conv()->asInteger(args[1]).asInteger();
0170 
0171     if (!sheetName.isEmpty()) {
0172         result += sheetName;
0173         result += '!';
0174     }
0175 
0176     if (r1c1) {
0177         // row first
0178         bool abs = false;
0179         if (absNum == 1 || absNum == 2)
0180             abs = true;
0181 
0182         result += 'R';
0183         if (!abs)
0184             result += '[';
0185         result += QString::number(row);
0186 
0187         if (!abs)
0188             result += ']';
0189 
0190         // column
0191         abs = false;
0192         if (absNum == 1 || absNum == 3)
0193             abs = true;
0194 
0195         result += 'C';
0196         if (!abs)
0197             result += '[';
0198         result += QString::number(col);
0199 
0200         if (!abs)
0201             result += ']';
0202     } else {
0203         bool abs = false;
0204         if (absNum == 1 || absNum == 3)
0205             abs = true;
0206 
0207         if (abs)
0208             result += '$';
0209 
0210         result += Cell::columnName(col);
0211 
0212         abs = false;
0213         if (absNum == 1 || absNum == 2)
0214             abs = true;
0215 
0216         if (abs)
0217             result += '$';
0218 
0219         result += QString::number(row);
0220     }
0221 
0222     return Value(result);
0223 }
0224 
0225 
0226 //
0227 // Function: AREAS
0228 //
0229 Value func_areas(valVector args, ValueCalc *calc, FuncExtra *e)
0230 {
0231     if (e) {
0232         if (e->regions[0].isValid())
0233             return Value(e->regions[0].rects().size());
0234 
0235         if ((e->ranges[0].col1 != -1) && (e->ranges[0].row1 != -1) &&
0236                 (e->ranges[0].col2 != -1) && (e->ranges[0].row2 != -1))
0237             // we have a range reference - return 1
0238             return Value(1);
0239     }
0240 
0241     QString s = calc->conv()->asString(args[0]).asString();
0242     if (s[0] != '(' || s[s.length() - 1] != ')')
0243         return Value::errorVALUE();
0244 
0245     int l = s.length();
0246 
0247     int num = 0;
0248     QString ref;
0249     for (int i = 1; i < l; ++i) {
0250         if (s[i] == ',' || s[i] == ')') {
0251             if (!Calligra::Sheets::Region(ref).isValid())
0252                 return Value::errorVALUE();
0253             else {
0254                 ++num;
0255                 ref.clear();
0256             }
0257         } else
0258             ref += s[i];
0259     }
0260 
0261     return Value(num);
0262 }
0263 
0264 //
0265 // Function: CELL
0266 //
0267 Value func_cell(valVector args, ValueCalc *calc, FuncExtra *e)
0268 {
0269     const QString type = calc->conv()->asString(args[0]).asString().toLower();
0270 
0271     args.pop_front();
0272     FuncExtra extra(*e);
0273     extra.ranges.pop_front();
0274     extra.regions.pop_front();
0275 
0276     if (type == "col") {
0277         return func_column(args, calc, &extra);
0278     }
0279     if (type == "row") {
0280         return func_row(args, calc, &extra);
0281     }
0282     if (type == "sheet") {
0283         return func_sheet(args, calc, &extra);
0284     }
0285     if (type == "address") {
0286         const Calligra::Sheets::Region &region = args.count() ? extra.regions[0] : Calligra::Sheets::Region(QPoint(e->mycol, e->myrow), e->sheet);
0287         QString s;
0288         if (region.firstSheet() && region.firstSheet() != e->sheet)
0289             s += '\'' + region.firstSheet()->sheetName() + "'!";
0290         s += '$' + Cell::columnName(region.firstRange().x()) + '$' + QString::number(region.firstRange().y());
0291         if (region.firstRange() != region.lastRange())
0292             s += ":$" + Cell::columnName(region.lastRange().x()) + '$' + QString::number(region.lastRange().y());
0293         return Value(s);
0294     }
0295     if (type == "filename") {
0296         return Value(calc->settings()->fileName());
0297     }
0298 
0299     /*TODO
0300     if (type == "contents")
0301     if (type == "color")
0302     if (type == "format")
0303     if (type == "width")
0304     if (type == "type")
0305     if (type == "protect")
0306     if (type == "parenthesis")
0307     if (type == "prefix")
0308     */
0309 
0310     return Value::errorVALUE();
0311 }
0312 
0313 //
0314 // Function: CHOOSE
0315 //
0316 Value func_choose(valVector args, ValueCalc *calc, FuncExtra *)
0317 {
0318     int cnt = args.count() - 1;
0319     int num = calc->conv()->asInteger(args[0]).asInteger();
0320     if ((num <= 0) || (num > cnt))
0321         return Value::errorVALUE();
0322     return args[num];
0323 }
0324 
0325 
0326 //
0327 // Function: COLUMN
0328 //
0329 Value func_column(valVector args, ValueCalc *, FuncExtra *e)
0330 {
0331     int col = e ? e->mycol : 0;
0332     if (e && args.count())
0333         col = e->ranges[0].col1;
0334     if (col > 0)
0335         return Value(col);
0336     return Value::errorVALUE();
0337 }
0338 
0339 
0340 //
0341 // Function: COLUMNS
0342 //
0343 Value func_columns(valVector, ValueCalc *, FuncExtra *e)
0344 {
0345     int col1 = e->ranges[0].col1;
0346     int col2 = e->ranges[0].col2;
0347     if ((col1 == -1) || (col2 == -1))
0348         return Value::errorVALUE();
0349     return Value(col2 - col1 + 1);
0350 }
0351 
0352 
0353 //
0354 // Function: HLOOKUP
0355 //
0356 Value func_hlookup(valVector args, ValueCalc *calc, FuncExtra *)
0357 {
0358     const Value key = args[0];
0359     const Value data = args[1];
0360     const int row = calc->conv()->asInteger(args[2]).asInteger();
0361     const int cols = data.columns();
0362     const int rows = data.rows();
0363     if (row < 1 || row > rows)
0364         return Value::errorVALUE();
0365     const bool rangeLookup = (args.count() > 3) ? calc->conv()->asBoolean(args[3]).asBoolean() : true;
0366 
0367     // now traverse the array and perform comparison
0368     Value r;
0369     Value v = Value::errorNA();
0370     for (int col = 0; col < cols; ++col) {
0371         // search in the first row
0372         const Value le = data.element(col, 0);
0373         if (calc->naturalEqual(key, le)) {
0374             return data.element(col, row - 1);
0375         }
0376         // optionally look for the next largest value that is less than key
0377         if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) {
0378             r = le;
0379             v = data.element(col, row - 1);
0380         }
0381     }
0382     return v;
0383 }
0384 
0385 
0386 //
0387 // Function: INDEX
0388 //
0389 Value func_index(valVector args, ValueCalc *calc, FuncExtra *)
0390 {
0391     // first argument can be either a range, then we return a given cell's
0392     // value, or a single cell containing an array - then we return the array
0393     // element. In any case, this function can assume that the given value
0394     // is the same. Because it is.
0395 
0396     Value val = args[0];
0397     unsigned row = calc->conv()->asInteger(args[1]).asInteger() - 1;
0398     unsigned col = calc->conv()->asInteger(args[2]).asInteger() - 1;
0399     if ((row >= val.rows()) || (col >= val.columns()))
0400         return Value::errorREF();
0401     return val.element(col, row);
0402 }
0403 
0404 
0405 //
0406 // Function: INDIRECT
0407 //
0408 Value func_indirect(valVector args, ValueCalc *calc, FuncExtra *e)
0409 {
0410     bool r1c1 = false;
0411     QString ref = calc->conv()->asString(args[0]).asString();
0412     if (args.count() == 2)
0413         r1c1 = !(calc->conv()->asBoolean(args[1]).asBoolean());
0414 
0415     if (ref.isEmpty())
0416         return Value::errorVALUE();
0417 
0418     if (r1c1) {
0419         // TODO: translate the r1c1 style to a1 style
0420         ref = ref;
0421     }
0422 
0423     const Calligra::Sheets::Region region(ref, e->sheet->map(), e->sheet);
0424     if (!region.isValid() || !region.isSingular())
0425         return Value::errorVALUE();
0426 
0427     const Cell cell(region.firstSheet(), region.firstRange().topLeft());
0428     if (!cell.isNull())
0429         return cell.value();
0430     return Value::errorVALUE();
0431 }
0432 
0433 
0434 //
0435 // Function: LOOKUP
0436 //
0437 Value func_lookup(valVector args, ValueCalc *calc, FuncExtra *)
0438 {
0439     Value num = calc->conv()->asNumeric(args[0]);
0440     if (num.isArray())
0441         return Value::errorVALUE();
0442     Value lookup = args[1];
0443     Value rr = args[2];
0444     unsigned cols = lookup.columns();
0445     unsigned rows = lookup.rows();
0446     if ((cols != rr.columns()) || (rows != rr.rows()))
0447         return Value::errorVALUE();
0448     Value res = Value::errorNA();
0449 
0450     // now traverse the array and perform comparison
0451     for (unsigned r = 0; r < rows; ++r)
0452         for (unsigned c = 0; c < cols; ++c) {
0453             // update the result, return if we cross the line
0454             Value le = lookup.element(c, r);
0455             if (calc->lower(le, num) || calc->equal(num, le))
0456                 res = rr.element(c, r);
0457             else
0458                 return res;
0459         }
0460     return res;
0461 }
0462 
0463 //
0464 // Function: MATCH
0465 //
0466 Value func_match(valVector args, ValueCalc *calc, FuncExtra* e)
0467 {
0468     int matchType = 1;
0469     if (args.count() == 3) {
0470         bool ok = true;
0471         matchType = calc->conv()->asInteger(args[2], &ok).asInteger();
0472         if (!ok)
0473             return Value::errorVALUE(); // invalid matchtype
0474     }
0475 
0476     const Value& searchValue = args[0];
0477     const Value& searchArray = args[1];
0478 
0479     if (e->ranges[1].rows() != 1 && e->ranges[1].columns() != 1)
0480         return Value::errorNA();
0481     int dr = 1, dc = 0;
0482     if (searchArray.columns() != 1) {
0483         dr = 0; dc = 1;
0484     }
0485     int n = qMax(searchArray.rows(), searchArray.columns());
0486 
0487     if (matchType == 0) {
0488         // linear search
0489         for (int r = 0, c = 0; r < n && c < n; r += dr, c += dc) {
0490             if (calc->naturalEqual(searchValue, searchArray.element(c, r), false)) {
0491                 return Value(qMax(r, c) + 1);
0492             }
0493         }
0494         return Value::errorNA();
0495     } else if (matchType > 0) {
0496         // binary search
0497         int l = -1;
0498         int h = n;
0499         while (l+1 < h) {
0500             int m = (l+h)/2;
0501             if (calc->naturalLequal(searchArray.element(m*dc, m*dr), searchValue, false)) {
0502                 l = m;
0503             } else {
0504                 h = m;
0505             }
0506         }
0507         if (l == -1) return Value::errorNA();
0508         return Value(l+1);
0509     } else /* matchType < 0 */ {
0510         // binary search
0511         int l = -1;
0512         int h = n;
0513         while (l+1 < h) {
0514             int m = (l+h)/2;
0515             if (calc->naturalGequal(searchArray.element(m*dc, m*dr), searchValue, false)) {
0516                 l = m;
0517             } else {
0518                 h = m;
0519             }
0520         }
0521         if (l == -1) return Value::errorNA();
0522         return Value(l+1);
0523     }
0524 }
0525 
0526 //
0527 // Function: MULTIPLE.OPERATIONS
0528 //
0529 Value func_multiple_operations(valVector args, ValueCalc *, FuncExtra *e)
0530 {
0531     if (args.count() != 3 && args.count() != 5)
0532         return Value::errorVALUE(); // invalid number of parameters
0533 
0534     for (int i = 0; i < args.count(); i++) {
0535         if (e->ranges[i].col1 == -1 || e->ranges[i].row1 == -1)
0536             return Value::errorVALUE();
0537     }
0538 
0539     CellStorage *s = e->sheet->cellStorage();
0540 
0541     // get formula to evaluate
0542     int formulaCol = e->ranges[0].col1;
0543     int formulaRow = e->ranges[0].row1;
0544     Formula formula = s->formula(formulaCol, formulaRow);
0545     if (!formula.isValid())
0546         return Value::errorVALUE();
0547 
0548     CellIndirection cellIndirections;
0549     cellIndirections.insert(Cell(e->sheet, e->ranges[1].col1, e->ranges[1].row1), Cell(e->sheet, e->ranges[2].col1, e->ranges[2].row1));
0550     if (args.count() > 3) {
0551         cellIndirections.insert(Cell(e->sheet, e->ranges[3].col1, e->ranges[3].row1), Cell(e->sheet, e->ranges[4].col1, e->ranges[4].row1));
0552     }
0553 
0554     return formula.eval(cellIndirections);
0555 }
0556 
0557 //
0558 // Function: OFFSET
0559 //
0560 Value func_offset(valVector args, ValueCalc *calc, FuncExtra *e)
0561 {
0562     const int rowPlus = calc->conv()->asInteger(args[1]).asInteger();
0563     const int colPlus = calc->conv()->asInteger(args[2]).asInteger();
0564 
0565     //const int rowNew = args.count() >= 4 ? calc->conv()->asInteger(args[3]).asInteger() : -1;
0566     //const int colNew = args.count() >= 5 ? calc->conv()->asInteger(args[4]).asInteger() : -1;
0567     //if (colNew == 0 || rowNew == 0) return Value::errorVALUE();
0568 
0569     // Doesn't take references to other sheets into account
0570     //const QRect rect(e->ranges[0].col1, e->ranges[0].row1, e->ranges[0].col2, e->ranges[0].row2);
0571     //const Calligra::Sheets::Region region(rect, e->sheet);
0572 
0573     if (e->regions.isEmpty())
0574         return Value::errorVALUE();
0575 
0576     const Calligra::Sheets::Region &region = e->regions[0];
0577 
0578     if (!region.isValid() /* || !region.isSingular() */)
0579         return Value::errorVALUE();
0580 
0581     QPoint p = region.firstRange().topLeft() + QPoint(colPlus, rowPlus);
0582     const Cell cell(region.firstSheet(), p);
0583     if (!cell.isNull())
0584         return cell.value();
0585 
0586     return Value::errorVALUE();
0587 }
0588 
0589 //
0590 // Function: ROW
0591 //
0592 Value func_row(valVector args, ValueCalc *, FuncExtra *e)
0593 {
0594     int row = e ? e->myrow : 0;
0595     if (e && args.count())
0596         row = e->ranges[0].row1;
0597     if (row > 0)
0598         return Value(row);
0599     return Value::errorVALUE();
0600 }
0601 
0602 
0603 //
0604 // Function: ROWS
0605 //
0606 Value func_rows(valVector, ValueCalc *, FuncExtra *e)
0607 {
0608     int row1 = e->ranges[0].row1;
0609     int row2 = e->ranges[0].row2;
0610     if ((row1 == -1) || (row2 == -1))
0611         return Value::errorVALUE();
0612     return Value(row2 - row1 + 1);
0613 }
0614 
0615 //
0616 // Function: SHEET
0617 //
0618 Value func_sheet(valVector /*args*/, ValueCalc *, FuncExtra *e)
0619 {
0620     Sheet *sheet = e->sheet;
0621     if (!e->regions.isEmpty()) {
0622         const Calligra::Sheets::Region &region = e->regions[0];
0623         if (region.isValid())
0624             sheet = region.firstSheet();
0625     }
0626     return Value(sheet->map()->indexOf(sheet) + 1);
0627 }
0628 
0629 //
0630 // Function: SHEETS
0631 //
0632 Value func_sheets(valVector /*args*/, ValueCalc *, FuncExtra *e)
0633 {
0634     if (!e->regions.isEmpty()) {
0635         const Calligra::Sheets::Region &region = e->regions[0];
0636         if (region.isValid()) {
0637             QList<Calligra::Sheets::Sheet*> sheets;
0638             Calligra::Sheets::Region::ConstIterator it(region.constBegin()), end(region.constEnd());
0639             for(; it != end; ++it)
0640                 if (!sheets.contains((*it)->sheet()))
0641                     sheets.append((*it)->sheet());
0642             return Value(sheets.count());
0643         }
0644     }
0645     return Value(e->sheet->map()->count());
0646 }
0647 
0648 //
0649 // Function: VLOOKUP
0650 //
0651 Value func_vlookup(valVector args, ValueCalc *calc, FuncExtra *)
0652 {
0653     const Value key = args[0];
0654     const Value data = args[1];
0655     const int col = calc->conv()->asInteger(args[2]).asInteger();
0656     const int cols = data.columns();
0657     const int rows = data.rows();
0658     if (col < 1 || col > cols)
0659         return Value::errorVALUE();
0660     const bool rangeLookup = (args.count() > 3) ? calc->conv()->asBoolean(args[3]).asBoolean() : true;
0661 
0662     // now traverse the array and perform comparison
0663     Value r;
0664     Value v = Value::errorNA();
0665     for (int row = 0; row < rows; ++row) {
0666         // search in the first column
0667         const Value le = data.element(0, row);
0668         if (calc->naturalEqual(key, le)) {
0669             return data.element(col - 1, row);
0670         }
0671         // optionally look for the next largest value that is less than key
0672         if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) {
0673             r = le;
0674             v = data.element(col - 1, row);
0675         }
0676     }
0677     return v;
0678 }
0679 
0680 #include "reference.moc"