/* This file is part of the KDE project Copyright (C) 1998-2002 The KSpread Team www.koffice.org/kspread Copyright (C) 2005 Tomas Mecir This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // built-in text functions // please keep it in alphabetical order #include #include #include #include #include "functions.h" #include "valuecalc.h" #include "valueconverter.h" using namespace KSpread; // Functions DOLLAR and FIXED convert data to double, hence they will not // support arbitrary precision, when it will be introduced. // prototypes Value func_char (valVector args, ValueCalc *calc, FuncExtra *); Value func_clean (valVector args, ValueCalc *calc, FuncExtra *); Value func_code (valVector args, ValueCalc *calc, FuncExtra *); Value func_compare (valVector args, ValueCalc *calc, FuncExtra *); Value func_concatenate (valVector args, ValueCalc *calc, FuncExtra *); Value func_dollar (valVector args, ValueCalc *calc, FuncExtra *); Value func_exact (valVector args, ValueCalc *calc, FuncExtra *); Value func_find (valVector args, ValueCalc *calc, FuncExtra *); Value func_fixed (valVector args, ValueCalc *calc, FuncExtra *); Value func_left (valVector args, ValueCalc *calc, FuncExtra *); Value func_len (valVector args, ValueCalc *calc, FuncExtra *); Value func_lower (valVector args, ValueCalc *calc, FuncExtra *); Value func_mid (valVector args, ValueCalc *calc, FuncExtra *); Value func_proper (valVector args, ValueCalc *calc, FuncExtra *); Value func_regexp (valVector args, ValueCalc *calc, FuncExtra *); Value func_regexpre (valVector args, ValueCalc *calc, FuncExtra *); Value func_replace (valVector args, ValueCalc *calc, FuncExtra *); Value func_rept (valVector args, ValueCalc *calc, FuncExtra *); Value func_rot (valVector args, ValueCalc *calc, FuncExtra *); Value func_right (valVector args, ValueCalc *calc, FuncExtra *); Value func_search (valVector args, ValueCalc *calc, FuncExtra *); Value func_sleek (valVector args, ValueCalc *calc, FuncExtra *); Value func_substitute (valVector args, ValueCalc *calc, FuncExtra *); Value func_t (valVector args, ValueCalc *calc, FuncExtra *); Value func_text (valVector args, ValueCalc *calc, FuncExtra *); Value func_toggle (valVector args, ValueCalc *calc, FuncExtra *); Value func_trim (valVector args, ValueCalc *calc, FuncExtra *); Value func_upper (valVector args, ValueCalc *calc, FuncExtra *); Value func_value (valVector args, ValueCalc *calc, FuncExtra *); // registers all text functions void RegisterTextFunctions() { FunctionRepository* repo = FunctionRepository::self(); Function *f; // one-parameter functions f = new Function ("CHAR", func_char); repo->add (f); f = new Function ("CLEAN", func_clean); repo->add (f); f = new Function ("CODE", func_code); repo->add (f); f = new Function ("LEN", func_len); repo->add (f); f = new Function ("LOWER", func_lower); repo->add (f); f = new Function ("PROPER", func_proper); repo->add (f); f = new Function ("ROT", func_rot); repo->add (f); f = new Function ("SLEEK", func_sleek); repo->add (f); f = new Function ("T", func_t); repo->add (f); f = new Function ("TOGGLE", func_toggle); repo->add (f); f = new Function ("TRIM", func_trim); repo->add (f); f = new Function ("UPPER", func_upper); repo->add (f); f = new Function ("VALUE", func_value); repo->add (f); // other functions f = new Function ("COMPARE", func_compare); f->setParamCount (3); repo->add (f); f = new Function ("CONCATENATE", func_concatenate); f->setParamCount (1, -1); f->setAcceptArray (); repo->add (f); f = new Function ("DOLLAR", func_dollar); f->setParamCount (1, 2); repo->add (f); f = new Function ("EXACT", func_exact); f->setParamCount (2); repo->add (f); f = new Function ("FIND", func_find); f->setParamCount (2, 3); repo->add (f); f = new Function ("FIXED", func_fixed); f->setParamCount (1, 3); repo->add (f); f = new Function ("LEFT", func_left); f->setParamCount (2); repo->add (f); f = new Function ("MID", func_mid); f->setParamCount (2, 3); repo->add (f); f = new Function ("REGEXP", func_regexp); f->setParamCount (2, 4); repo->add (f); f = new Function ("REGEXPRE", func_regexpre); f->setParamCount (3); repo->add (f); f = new Function ("REPLACE", func_replace); f->setParamCount (4); repo->add (f); f = new Function ("REPT", func_rept); f->setParamCount (2); repo->add (f); f = new Function ("RIGHT", func_right); f->setParamCount (2); repo->add (f); f = new Function ("SEARCH", func_search); f->setParamCount (2, 3); repo->add (f); f = new Function ("SUBSTITUTE", func_substitute); f->setParamCount (3, 4); repo->add (f); f = new Function ("TEXT", func_text); f->setParamCount (1, 2); repo->add (f); } // Function: CHAR Value func_char (valVector args, ValueCalc *calc, FuncExtra *) { int val = calc->conv()->asInteger (args[0]).asInteger (); return Value (TQString (TQChar (val))); } // Function: CLEAN Value func_clean (valVector args, ValueCalc *calc, FuncExtra *) { TQString str (calc->conv()->asString (args[0]).asString()); TQString result; TQChar c; int i; int l = str.length(); for (i = 0; i < l; ++i) { c = str[i]; if (c.isPrint()) result += c; } return Value (result); } // Function: CODE Value func_code (valVector args, ValueCalc *calc, FuncExtra *) { TQString str (calc->conv()->asString (args[0]).asString()); if (str.length() <= 0) return Value::errorVALUE(); return Value (str[0].unicode()); } // Function: COMPARE Value func_compare (valVector args, ValueCalc *calc, FuncExtra *) { int result = 0; bool exact = calc->conv()->asBoolean (args[2]).asBoolean(); TQString s1 = calc->conv()->asString (args[0]).asString(); TQString s2 = calc->conv()->asString (args[1]).asString(); if (!exact) result = s1.lower().localeAwareCompare(s2.lower()); else result = s1.localeAwareCompare(s2); if (result < 0) result = -1; else if (result > 0) result = 1; return Value (result); } void func_concatenate_helper (Value val, ValueCalc *calc, TQString& tmp) { if (val.isArray()) { for (unsigned int row = 0; row < val.rows(); ++row) for (unsigned int col = 0; col < val.columns(); ++col) func_concatenate_helper (val.element (col, row), calc, tmp); } else tmp += calc->conv()->asString (val).asString(); } // Function: CONCATENATE Value func_concatenate (valVector args, ValueCalc *calc, FuncExtra *) { TQString tmp; for (unsigned int i = 0; i < args.count(); ++i) func_concatenate_helper (args[i], calc, tmp); return Value (tmp); } // Function: DOLLAR Value func_dollar (valVector args, ValueCalc *calc, FuncExtra *) { // ValueConverter doesn't support money directly, hence we need to // use the locale. This code has the same effect as the output // of ValueFormatter for money format. // This function converts data to double/int, hence it won't support // larger precision. double value = calc->conv()->asFloat (args[0]).asFloat(); int precision = 2; if (args.count() == 2) precision = calc->conv()->asInteger (args[1]).asInteger(); // do round, because formatMoney doesn't value = floor (value * pow (10.0, precision) + 0.5) / pow (10.0, precision); TDELocale *locale = calc->conv()->locale(); TQString s = locale->formatMoney (value, locale->currencySymbol(), precision); return Value (s); } // Function: EXACT Value func_exact (valVector args, ValueCalc *calc, FuncExtra *) { TQString s1 = calc->conv()->asString (args[0]).asString(); TQString s2 = calc->conv()->asString (args[1]).asString(); bool exact = (s1 == s2); return Value (exact); } // Function: FIND Value func_find (valVector args, ValueCalc *calc, FuncExtra *) { TQString find_text, within_text; int start_num = 1; find_text = calc->conv()->asString (args[0]).asString(); within_text = calc->conv()->asString (args[1]).asString(); if (args.count() == 3) start_num = calc->conv()->asInteger (args[2]).asInteger(); // conforms to Excel behaviour if (start_num <= 0) return Value::errorVALUE(); if (start_num > (int)within_text.length()) return Value::errorVALUE(); int pos = within_text.find (find_text, start_num - 1); if( pos < 0 ) return Value::errorNA(); return Value (pos + 1); } // Function: FIXED Value func_fixed (valVector args, ValueCalc *calc, FuncExtra *) { // uses double, hence won't support big precision int decimals = 2; bool no_commas = false; double number = calc->conv()->asFloat (args[0]).asFloat(); if (args.count() > 1) decimals = calc->conv()->asInteger (args[1]).asInteger(); if (args.count() == 3) no_commas = calc->conv()->asBoolean (args[2]).asBoolean(); TQString result; TDELocale *locale = calc->conv()->locale(); // unfortunately, we can't just use TDELocale::formatNumber because // * if decimals < 0, number is rounded // * if no_commas is true, thousand separators shouldn't show up if( decimals < 0 ) { decimals = -decimals; number = floor( number/pow(10.0,decimals)+0.5 ) * pow(10.0,decimals); decimals = 0; } bool neg = number < 0; result = TQString::number( neg ? -number:number, 'f', decimals ); int pos = result.find('.'); if (pos == -1) pos = result.length(); else result.replace(pos, 1, locale->decimalSymbol()); if( !no_commas ) while (0 < (pos -= 3)) result.insert(pos, locale->thousandsSeparator()); result.prepend( neg ? locale->negativeSign(): locale->positiveSign() ); return Value (result); } // Function: LEFT Value func_left (valVector args, ValueCalc *calc, FuncExtra *) { TQString str = calc->conv()->asString (args[0]).asString(); int nb = 1; if (args.count() == 2) nb = calc->conv()->asInteger (args[1]).asInteger(); return Value (str.left (nb)); } // Function: LEN Value func_len (valVector args, ValueCalc *calc, FuncExtra *) { int nb = calc->conv()->asString (args[0]).asString().length(); return Value (nb); } // Function: LOWER Value func_lower (valVector args, ValueCalc *calc, FuncExtra *) { return Value (calc->conv()->asString (args[0]).asString().lower()); } // Function: MID Value func_mid (valVector args, ValueCalc *calc, FuncExtra *) { TQString str = calc->conv()->asString (args[0]).asString(); int pos = calc->conv()->asInteger (args[1]).asInteger(); uint len = 0xffffffff; if (args.count() == 3) len = (uint) calc->conv()->asInteger (args[2]).asInteger(); // Excel compatible pos--; return Value (str.mid (pos, len)); } // Function: PROPER Value func_proper (valVector args, ValueCalc *calc, FuncExtra *) { TQString str = calc->conv()->asString (args[0]).asString().lower(); TQChar f; bool first = true; for (unsigned int i = 0; i < str.length(); ++i) { if (first) { f = str[i]; if (f.isNumber()) continue; f = f.upper(); str[i] = f; first = false; continue; } if (str[i] == ' ' || str[i] == '-') first = true; } return Value (str); } // Function: REGEXP Value func_regexp (valVector args, ValueCalc *calc, FuncExtra *) { // ensure that we got a valid regular expression TQRegExp exp (calc->conv()->asString (args[1]).asString()); if (!exp.isValid ()) return Value::errorVALUE(); TQString s = calc->conv()->asString (args[0]).asString(); TQString defText; if (args.count() > 2) defText = calc->conv()->asString (args[2]).asString(); int bkref = 0; if (args.count() == 4) bkref = calc->conv()->asInteger (args[3]).asInteger(); if (bkref < 0) // strange back-reference return Value::errorVALUE(); TQString returnValue; int pos = exp.search (s); if (pos == -1) returnValue = defText; else returnValue = exp.cap (bkref); return Value (returnValue); } // Function: REGEXPRE Value func_regexpre (valVector args, ValueCalc *calc, FuncExtra *) { // ensure that we got a valid regular expression TQRegExp exp (calc->conv()->asString (args[1]).asString()); if (!exp.isValid ()) return Value::errorVALUE(); TQString s = calc->conv()->asString (args[0]).asString(); TQString str = calc->conv()->asString (args[2]).asString(); int pos = 0; while ((pos = exp.search (s, pos)) != -1) { int i = exp.matchedLength(); s = s.replace (pos, i, str); pos += str.length(); } return Value (s); } // Function: REPLACE Value func_replace (valVector args, ValueCalc *calc, FuncExtra *) { TQString text = calc->conv()->asString (args[0]).asString(); int pos = calc->conv()->asInteger (args[1]).asInteger(); int len = calc->conv()->asInteger (args[2]).asInteger(); TQString new_text = calc->conv()->asString (args[3]).asString(); if (pos < 0) pos = 0; TQString result = text.replace (pos-1, len, new_text); return Value (result); } // Function: REPT Value func_rept (valVector args, ValueCalc *calc, FuncExtra *) { TQString s = calc->conv()->asString (args[0]).asString(); int nb = calc->conv()->asInteger (args[1]).asInteger(); TQString result; for (int i = 0; i < nb; i++) result += s; return Value (result); } // Function: RIGHT Value func_right (valVector args, ValueCalc *calc, FuncExtra *) { TQString str = calc->conv()->asString (args[0]).asString(); int nb = 1; if (args.count() == 2) nb = calc->conv()->asInteger (args[1]).asInteger(); return Value (str.right (nb)); } // Function: ROT Value func_rot (valVector args, ValueCalc *calc, FuncExtra *) { TQString text = calc->conv()->asString (args[0]).asString(); for( unsigned i=0; i= 'A' ) && ( c <= 'M' ) ) text[i] = TQChar( text[i].unicode() + 13); if( ( c >= 'N' ) && ( c <= 'Z' ) ) text[i] = TQChar( text[i].unicode() - 13); } return Value (text); } // Function: SEARCH Value func_search (valVector args, ValueCalc *calc, FuncExtra *) { TQString find_text = calc->conv()->asString (args[0]).asString(); TQString within_text = calc->conv()->asString (args[1]).asString(); int start_num = 1; if (args.count() == 3) start_num = calc->conv()->asInteger (args[2]).asInteger(); // conforms to Excel behaviour if (start_num <= 0) return Value::errorVALUE(); if (start_num > (int)within_text.length()) return Value::errorVALUE(); // use globbing feature of TQRegExp TQRegExp regex( find_text, false, true ); int pos = within_text.find( regex, start_num-1 ); if( pos < 0 ) return Value::errorNA(); return Value (pos + 1); } // Function: SLEEK Value func_sleek (valVector args, ValueCalc *calc, FuncExtra *) { TQString str = calc->conv()->asString (args[0]).asString(); TQString result; TQChar c; int i; int l = str.length(); for (i = 0; i < l; ++i) { c = str[i]; if (!c.isSpace()) result += c; } return Value (result); } // Function: SUBSTITUTE Value func_substitute (valVector args, ValueCalc *calc, FuncExtra *) { int num = 1; bool all = true; if (args.count() == 4) { num = calc->conv()->asInteger (args[3]).asInteger(); all = false; } TQString text = calc->conv()->asString (args[0]).asString(); TQString old_text = calc->conv()->asString (args[1]).asString(); TQString new_text = calc->conv()->asString (args[2]).asString(); if( num <= 0 ) return Value::errorVALUE(); if (old_text.length() == 0) return Value (text); TQString result = text; int p = result.find (old_text); while ((p != -1) && (num > 0)) { result.replace( p, old_text.length(), new_text ); // find another location, starting straight after the replaced text p = result.find (old_text, p + new_text.length()); if( !all ) num--; } return Value (result); } // Function: T Value func_t (valVector args, ValueCalc *calc, FuncExtra *) { return calc->conv()->asString (args[0]); } // Function: TEXT Value func_text (valVector args, ValueCalc *calc, FuncExtra *) { //Currently the same as the T function ... //Second parameter is format_text. It is currently ignored. return calc->conv()->asString (args[0]); } // Function: TOGGLE Value func_toggle (valVector args, ValueCalc *calc, FuncExtra *) { TQString str = calc->conv()->asString (args[0]).asString(); int i; int l = str.length(); for (i = 0; i < l; ++i) { TQChar c = str[i]; TQChar lc = c.lower(); TQChar uc = c.upper(); if (c == lc) // it is in lowercase str[i] = c.upper(); else if (c == uc) // it is in uppercase str[i] = c.lower(); } return Value (str); } // Function: TRIM Value func_trim (valVector args, ValueCalc *calc, FuncExtra *) { return Value ( calc->conv()->asString (args[0]).asString().simplifyWhiteSpace()); } // Function: UPPER Value func_upper (valVector args, ValueCalc *calc, FuncExtra *) { return Value (calc->conv()->asString (args[0]).asString().upper()); } // Function: VALUE Value func_value (valVector args, ValueCalc *calc, FuncExtra *) { // same as the N function return calc->conv()->asFloat (args[0]); }