KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 lines
13KB

  1. /* This file is part of the KDE project
  2. Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org>
  3. Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. * Boston, MA 02110-1301, USA.
  16. */
  17. #include "formula.h"
  18. #include "functions.h"
  19. #include "valuecalc.h"
  20. #include <tqdict.h>
  21. #include <tqdom.h>
  22. #include <tqfile.h>
  23. #include <tqvaluevector.h>
  24. #include <kdebug.h>
  25. #include <tdelocale.h>
  26. #include <kstandarddirs.h>
  27. #include <kstaticdeleter.h>
  28. #include "kspread_factory.h"
  29. namespace KSpread
  30. {
  31. class Function::Private
  32. {
  33. public:
  34. TQString name;
  35. FunctionPtr ptr;
  36. int paramMin, paramMax;
  37. bool acceptArray;
  38. bool ne; // need FunctionExtra* when called ?
  39. };
  40. class FunctionRepository::Private
  41. {
  42. public:
  43. TQDict<Function> functions;
  44. TQDict<FunctionDescription> funcs;
  45. };
  46. } // namespace KSpread
  47. using namespace KSpread;
  48. Function::Function( const TQString& name, FunctionPtr ptr )
  49. {
  50. d = new Private;
  51. d->name = name;
  52. d->ptr = ptr;
  53. d->acceptArray = false;
  54. d->paramMin = 1;
  55. d->paramMax = 1;
  56. d->ne = false;
  57. }
  58. Function::~Function()
  59. {
  60. delete d;
  61. }
  62. TQString Function::name() const
  63. {
  64. return d->name;
  65. }
  66. void Function::setParamCount (int min, int max)
  67. {
  68. d->paramMin = min;
  69. d->paramMax = (max == 0) ? min : max;
  70. }
  71. bool Function::paramCountOkay (int count)
  72. {
  73. // less than needed
  74. if (count < d->paramMin) return false;
  75. // no upper limit
  76. if (d->paramMax == -1) return true;
  77. // more than needed
  78. if (count > d->paramMax) return false;
  79. // okay otherwise
  80. return true;
  81. }
  82. void Function::setAcceptArray (bool accept) {
  83. d->acceptArray = accept;
  84. }
  85. bool Function::needsExtra () {
  86. return d->ne;
  87. }
  88. void Function::setNeedsExtra (bool extra) {
  89. d->ne = extra;
  90. }
  91. Value Function::exec (valVector args, ValueCalc *calc, FuncExtra *extra)
  92. {
  93. // check number of parameters
  94. if (!paramCountOkay (args.count()))
  95. return Value::errorVALUE();
  96. // do we need to perform array expansion ?
  97. bool mustExpandArray = false;
  98. if (!d->acceptArray)
  99. for (unsigned int i = 0; i < args.count(); ++i) {
  100. if (args[i].isArray())
  101. mustExpandArray = true;
  102. }
  103. if( !d->ptr ) return Value::errorVALUE();
  104. // perform the actual array expansion if need be
  105. if (mustExpandArray) {
  106. // compute number of rows/cols of the result
  107. int rows = 0;
  108. int cols = 0;
  109. for (unsigned int i = 0; i < args.count(); ++i) {
  110. int x = (args[i].type() == Value::Array) ? args[i].rows() : 1;
  111. if (x > rows) rows = x;
  112. x = (args[i].type() == Value::Array) ? args[i].columns() : 1;
  113. if (x > cols) cols = x;
  114. }
  115. // allocate the resulting array
  116. Value res (cols, rows);
  117. // perform the actual computation for each element of the array
  118. for (int row = 0; row < rows; ++row)
  119. for (int col = 0; col < cols; ++col) {
  120. // fill in the parameter vector
  121. valVector vals (args.count());
  122. for (unsigned int i = 0; i < args.count(); ++i) {
  123. int r = args[i].rows();
  124. int c = args[i].columns();
  125. vals[i] = args[i].isArray() ?
  126. args[i].element (col % c, row % r): args[i];
  127. }
  128. // execute the function on each element
  129. res.setElement (col, row, exec (vals, calc, extra));
  130. }
  131. return res;
  132. }
  133. else
  134. // call the function
  135. return (*d->ptr) (args, calc, extra);
  136. }
  137. // these are defined in kspread_function_*.cc
  138. void RegisterConversionFunctions();
  139. void RegisterDatabaseFunctions();
  140. void RegisterDateTimeFunctions();
  141. void RegisterEngineeringFunctions();
  142. void RegisterFinancialFunctions();
  143. void RegisterInformationFunctions();
  144. void RegisterLogicFunctions();
  145. void RegisterMathFunctions();
  146. void RegisterReferenceFunctions();
  147. void RegisterStatisticalFunctions();
  148. void RegisterTextFunctions();
  149. void RegisterTrigFunctions();
  150. static KStaticDeleter<FunctionRepository> fr_sd;
  151. FunctionRepository* FunctionRepository::s_self = 0;
  152. FunctionRepository* FunctionRepository::self()
  153. {
  154. if( !s_self )
  155. {
  156. kdDebug() << "Creating function repository" << endl;
  157. fr_sd.setObject( s_self, new FunctionRepository() );
  158. kdDebug() << "Registering functions" << endl;
  159. // register all existing functions
  160. RegisterConversionFunctions();
  161. RegisterDatabaseFunctions();
  162. RegisterDateTimeFunctions();
  163. RegisterEngineeringFunctions();
  164. RegisterFinancialFunctions();
  165. RegisterInformationFunctions();
  166. RegisterLogicFunctions();
  167. RegisterMathFunctions();
  168. RegisterReferenceFunctions();
  169. RegisterStatisticalFunctions();
  170. RegisterTextFunctions();
  171. RegisterTrigFunctions();
  172. kdDebug() << "Functions registered, loading descriptions" << endl;
  173. // find all XML description files
  174. TQStringList files = Factory::global()->dirs()->findAllResources
  175. ("extensions", "*.xml", TRUE);
  176. // load desc/help from XML file
  177. for( TQStringList::Iterator it = files.begin(); it != files.end(); ++it )
  178. s_self->loadFile (*it);
  179. kdDebug() << "All ok, repository ready" << endl;
  180. }
  181. return s_self;
  182. }
  183. FunctionRepository::FunctionRepository()
  184. {
  185. d = new Private;
  186. d->functions.setAutoDelete( true );
  187. d->funcs.setAutoDelete( true );
  188. }
  189. FunctionRepository::~FunctionRepository()
  190. {
  191. delete d;
  192. s_self = 0;
  193. }
  194. void FunctionRepository::add( Function* function )
  195. {
  196. if( !function ) return;
  197. d->functions.insert( function->name().upper(), function );
  198. }
  199. Function *FunctionRepository::function (const TQString& name)
  200. {
  201. return d->functions.find (name.upper());
  202. }
  203. FunctionDescription *FunctionRepository::functionInfo (const TQString& name)
  204. {
  205. return d->funcs.find (name.upper());
  206. }
  207. // returns names of function in certain group
  208. TQStringList FunctionRepository::functionNames( const TQString& group )
  209. {
  210. TQStringList lst;
  211. TQDictIterator<FunctionDescription> it (d->funcs);
  212. for(; it.current(); ++it) {
  213. if (group.isNull() || (it.current()->group() == group))
  214. lst.append (it.current()->name());
  215. }
  216. lst.sort();
  217. return lst;
  218. }
  219. void FunctionRepository::loadFile (const TQString& filename)
  220. {
  221. TQFile file (filename);
  222. if (!file.open (IO_ReadOnly))
  223. return;
  224. TQDomDocument doc;
  225. doc.setContent( &file );
  226. file.close();
  227. TQString group = "";
  228. TQDomNode n = doc.documentElement().firstChild();
  229. for (; !n.isNull(); n = n.nextSibling())
  230. {
  231. if (!n.isElement())
  232. continue;
  233. TQDomElement e = n.toElement();
  234. if (e.tagName() == "Group")
  235. {
  236. group = i18n (e.namedItem ("GroupName").toElement().text().utf8());
  237. m_groups.append( group );
  238. m_groups.sort();
  239. TQDomNode n2 = e.firstChild();
  240. for (; !n2.isNull(); n2 = n2.nextSibling())
  241. {
  242. if (!n2.isElement())
  243. continue;
  244. TQDomElement e2 = n2.toElement();
  245. if (e2.tagName() == "Function")
  246. {
  247. FunctionDescription* desc = new FunctionDescription( e2 );
  248. desc->setGroup (group);
  249. if (d->functions.find (desc->name()))
  250. d->funcs.insert (desc->name(), desc);
  251. }
  252. }
  253. group = "";
  254. }
  255. }
  256. }
  257. // ------------------------------------------------------------
  258. static ParameterType toType( const TQString& type )
  259. {
  260. if ( type == "Boolean" )
  261. return KSpread_Boolean;
  262. if ( type == "Int" )
  263. return KSpread_Int;
  264. if ( type == "String" )
  265. return KSpread_String;
  266. if ( type == "Any" )
  267. return KSpread_Any;
  268. return KSpread_Float;
  269. }
  270. static TQString toString (ParameterType type, bool range = FALSE)
  271. {
  272. if ( !range )
  273. {
  274. switch(type) {
  275. case KSpread_String:
  276. return i18n("Text");
  277. case KSpread_Int:
  278. return i18n("Whole number (like 1, 132, 2344)");
  279. case KSpread_Boolean:
  280. return i18n("A truth value (TRUE or FALSE)" );
  281. case KSpread_Float:
  282. return i18n("A floating point value (like 1.3, 0.343, 253 )" );
  283. case KSpread_Any:
  284. return i18n("Any kind of value");
  285. }
  286. }
  287. else
  288. {
  289. switch(type) {
  290. case KSpread_String:
  291. return i18n("A range of strings");
  292. case KSpread_Int:
  293. return i18n("A range of whole numbers (like 1, 132, 2344)");
  294. case KSpread_Boolean:
  295. return i18n("A range of truth values (TRUE or FALSE)" );
  296. case KSpread_Float:
  297. return i18n("A range of floating point values (like 1.3, 0.343, 253 )" );
  298. case KSpread_Any:
  299. return i18n("A range of any kind of values");
  300. }
  301. }
  302. return TQString();
  303. }
  304. FunctionParameter::FunctionParameter()
  305. {
  306. m_type = KSpread_Float;
  307. m_range = FALSE;
  308. }
  309. FunctionParameter::FunctionParameter (const FunctionParameter& param)
  310. {
  311. m_help = param.m_help;
  312. m_type = param.m_type;
  313. m_range = param.m_range;
  314. }
  315. FunctionParameter::FunctionParameter (const TQDomElement& element)
  316. {
  317. m_type = KSpread_Float;
  318. m_range = FALSE;
  319. TQDomNode n = element.firstChild();
  320. for( ; !n.isNull(); n = n.nextSibling() )
  321. if ( n.isElement() )
  322. {
  323. TQDomElement e = n.toElement();
  324. if ( e.tagName() == "Comment" )
  325. m_help = i18n( e.text().utf8() );
  326. else if ( e.tagName() == "Type" )
  327. {
  328. m_type = toType( e.text() );
  329. if ( e.hasAttribute( "range" ))
  330. {
  331. if (e.attribute("range").lower() == "true")
  332. m_range = TRUE;
  333. }
  334. }
  335. }
  336. }
  337. FunctionDescription::FunctionDescription()
  338. {
  339. m_type = KSpread_Float;
  340. }
  341. FunctionDescription::FunctionDescription (const TQDomElement& element)
  342. {
  343. TQDomNode n = element.firstChild();
  344. for( ; !n.isNull(); n = n.nextSibling() )
  345. {
  346. if (!n.isElement())
  347. continue;
  348. TQDomElement e = n.toElement();
  349. if ( e.tagName() == "Name" )
  350. m_name = e.text();
  351. else if ( e.tagName() == "Type" )
  352. m_type = toType( e.text() );
  353. else if ( e.tagName() == "Parameter" )
  354. m_params.append (FunctionParameter (e));
  355. else if ( e.tagName() == "Help" )
  356. {
  357. TQDomNode n2 = e.firstChild();
  358. for( ; !n2.isNull(); n2 = n2.nextSibling() )
  359. {
  360. if (!n2.isElement())
  361. continue;
  362. TQDomElement e2 = n2.toElement();
  363. if ( e2.tagName() == "Text" )
  364. m_help.append ( i18n( e2.text().utf8() ) );
  365. else if ( e2.tagName() == "Syntax" )
  366. m_syntax.append( i18n( e2.text().utf8() ) );
  367. else if ( e2.tagName() == "Example" )
  368. m_examples.append( i18n( e2.text().utf8() ) );
  369. else if ( e2.tagName() == "Related" )
  370. m_related.append( i18n( e2.text().utf8() ) );
  371. }
  372. }
  373. }
  374. }
  375. FunctionDescription::FunctionDescription( const FunctionDescription& desc )
  376. {
  377. m_examples = desc.m_examples;
  378. m_related = desc.m_related;
  379. m_syntax = desc.m_syntax;
  380. m_help = desc.m_help;
  381. m_name = desc.m_name;
  382. m_type = desc.m_type;
  383. }
  384. TQString FunctionDescription::toTQML() const
  385. {
  386. TQString text( "<qt><h1>" );
  387. text += name();
  388. text += "</h1>";
  389. if( !m_help.isEmpty() )
  390. {
  391. text += i18n("<p>");
  392. TQStringList::ConstIterator it = m_help.begin();
  393. for( ; it != m_help.end(); ++it )
  394. {
  395. text += *it;
  396. text += "<p>";
  397. }
  398. text += "</p>";
  399. }
  400. text += i18n("<p><b>Return type: </b>");
  401. text += toString( type() );
  402. text += "</p>";
  403. if ( !m_syntax.isEmpty() )
  404. {
  405. text += i18n("<h2>Syntax</h2><ul>");
  406. TQStringList::ConstIterator it = m_syntax.begin();
  407. for( ; it != m_syntax.end(); ++it )
  408. {
  409. text += "<li>";
  410. text += *it;
  411. }
  412. text += "</ul>";
  413. }
  414. if ( !m_params.isEmpty() )
  415. {
  416. text += i18n("<h2>Parameters</h2><ul>");
  417. TQValueList<FunctionParameter>::ConstIterator it = m_params.begin();
  418. for( ; it != m_params.end(); ++it )
  419. {
  420. text += i18n("<li><b>Comment:</b> ");
  421. text += (*it).helpText();
  422. text += i18n("<br><b>Type:</b> ");
  423. text += toString( (*it).type(), (*it).hasRange() );
  424. }
  425. text += "</ul>";
  426. }
  427. if ( !m_examples.isEmpty() )
  428. {
  429. text += i18n("<h2>Examples</h2><ul>");
  430. TQStringList::ConstIterator it = m_examples.begin();
  431. for( ; it != m_examples.end(); ++it )
  432. {
  433. text += "<li>";
  434. text += *it;
  435. }
  436. text += "</ul>";
  437. }
  438. if ( !m_related.isEmpty() )
  439. {
  440. text += i18n("<h2>Related Functions</h2><ul>");
  441. TQStringList::ConstIterator it = m_related.begin();
  442. for( ; it != m_related.end(); ++it )
  443. {
  444. text += "<li>";
  445. text += "<a href=\"" + *it + "\">";
  446. text += *it;
  447. text += "</a>";
  448. }
  449. text += "</ul>";
  450. }
  451. text += "</qt>";
  452. return text;
  453. }