fixed some build related problems
[qsqlmon.git] / libqf / libqfcore / script / scriptdriver / qfscriptdriver.cpp
blob35d1abc36017b8eff8067e4d51e1ea660e77726f
2 //
3 // Author: Frantisek Vacek <fanda.vacek@volny.cz>, (C) 2007
4 //
5 // Copyright: See COPYING file that comes with this distribution
6 //
8 #include "qfscriptdriver.h"
10 #include <qffileutils.h>
11 #include <qfsqlquery.h>
12 #include <qfxmltable.h>
13 #include <qfappdbconnectioninterface.h>
14 #include <qfjson.h>
15 #include <qfsearchdirs.h>
17 #include <QScriptEngine>
18 #include <QScriptValueIterator>
20 #include <qflogcust.h>
22 //=========================================================
23 // QFScriptDriver
24 //=========================================================
25 QStringList QFScriptDriver::f_path;
27 QFScriptDriver::QFScriptDriver(QObject *parent)
28 : QObject(parent), f_scriptEngine(NULL)
32 QFScriptDriver::~QFScriptDriver()
36 QScriptEngine * QFScriptDriver::scriptEngine()
38 if(!f_scriptEngine) {
39 f_scriptEngine = new QScriptEngine(this);
40 //qfInfo() << "created script engine:" << f_scriptEngine;
41 QScriptValue sv = f_scriptEngine->newQObject(this);
42 f_scriptEngine->globalObject().setProperty("driver", sv);
43 QString code = loadModuleObjectCode("qf.qf");
44 evaluate(code);
45 #if defined QT_DEBUG
46 evaluate("qf.config.isDebug = true");
47 #endif
49 return f_scriptEngine;
52 QScriptValue QFScriptDriver::resolveFunction(const QString & function_name, const QString & script_name, bool throw_exc)
54 qfTrash() << QF_FUNC_NAME << "fn:" << function_name << "script:" << script_name << "throw_exc:" << throw_exc;
55 //QScriptValue sv = scriptEngine()->evaluate(function_name);
56 QScriptValue sv = scriptEngine()->evaluate("qf.getObject("SARG(function_name)")");
58 if(!sv.isFunction()) {
59 sv = QScriptValue();
60 if(!script_name.isEmpty()) {
61 QString found_fn;
62 QString code = loadScriptCode(script_name, &found_fn);
63 if(!found_fn.isEmpty()) {
64 evaluate(code, throw_exc);
65 //sv = scriptEngine()->evaluate(function_name);
66 sv = scriptEngine()->evaluate("qf.getObject("SARG(function_name)")");
67 if(throw_exc && !sv.isFunction()) QF_EXCEPTION(tr("Skript '%1' neobsahuje funkci '%2'.").arg(script_name).arg(function_name));
69 else if(throw_exc) QF_EXCEPTION(tr("Skript '%1' nebyl nalezen.").arg(script_name));
71 else if(throw_exc) QF_EXCEPTION(tr("Nazev skriptu je prazdny.").arg(script_name));
73 return sv;
76 QScriptValue QFScriptDriver::resolveModuleObjectFunction(const QString & function_name, const QString & module_name, bool throw_exc)
78 qfLogFuncFrame() << "fn:" << function_name << "module_name:" << module_name << "throw_exc:" << throw_exc;
79 QString fn = module_name + "." + function_name;
80 /// tohle by melo fungovat stejne, ale pokud modul obsahuje klicove slovo javascriptu, tak NE
81 ///QScriptValue sv = scriptEngine()->evaluate(fn);
82 QScriptValue sv = scriptEngine()->evaluate("qf.getObject("SARG(fn)")");
84 if(!sv.isFunction()) {
85 sv = moduleObject(module_name, throw_exc);
86 if(sv.isObject()) {
87 sv = scriptEngine()->evaluate("qf.getObject("SARG(fn)")");
88 if(throw_exc && !sv.isFunction()) QF_EXCEPTION(tr("Modul '%1' neobsahuje funkci '%2'.").arg(module_name).arg(function_name));
90 else {
91 sv = QScriptValue();
92 if(throw_exc) QF_EXCEPTION(tr("Modul '%1' neni objekt ani funkce.").arg(module_name));
95 return sv;
98 QStringList QFScriptDriver::functionList(const QString & script_name)
100 QStringList fnc_lst;
101 QString found_fn;
102 QString code = loadScriptCode(script_name, &found_fn);
103 if(!found_fn.isEmpty()) {
104 QRegExp rx("function\\s+([A-Za-z]\\w*)\\s*\\(\\s*\\)");
105 int ix = 0;
106 while((ix = rx.indexIn(code, ix)) != -1) {
107 ix += rx.matchedLength();
108 fnc_lst << rx.capturedTexts()[1];
111 return fnc_lst;
114 QStringList & QFScriptDriver::searchPathRef()
116 if(f_path.isEmpty()) {
117 f_path << QFFileUtils::joinPath(QFFileUtils::appDir(), "script");
118 f_path << ":/libqf/script";
120 return f_path;
123 QString QFScriptDriver::resolveIncludes(const QString & code, const QString & code_file)
125 qfLogFuncFrame() << "code_file:" << code_file;
126 //qfTrash() << "code:\n" << code;
128 QString ret = code;
131 static const QString s_wrap = "#wrap_module";
132 if(ret.indexOf(s_wrap) >= 0) {
133 QRegExp rx_declare("#declare_module\\s*\\(([A-Za-z_][A-Za-z0-9_]*)\\)");
134 int ix = rx_declare.indexIn(ret);
135 if(ix < 0) QF_EXCEPTION("#wrap_module withouth #declare_module in " + code_file);
137 QString declare_replace = rx_declare.cap(0);
138 QString obj_name = rx_declare.cap(1).trimmed();
139 if(obj_name.isEmpty()) QF_EXCEPTION("#declare_module() object name is empty");
141 static const QString s_prefix = "if(!qf.getObject('${MODULE_NAME}')) { (function()";
142 static const QString s_postfix = ")(); }";
143 static const QString s_declare = "qf.declare('${MODULE_NAME}', %1);";
145 ret.replace(declare_replace, s_declare.arg(obj_name));
146 ret.replace(s_wrap, s_prefix);
147 ret += s_postfix;
148 //qfInfo() << ret;
152 static QRegExp re_include;
153 if(re_include.isEmpty()) {
154 re_include = QRegExp("#include\\s*\"(.+)\"");
155 re_include.setMinimal(true);
157 int pos = 0;
158 while ((pos = re_include.indexIn(code, pos)) != -1) {
159 QString inc = re_include.cap(0);
160 QString fn = re_include.cap(1);
161 qfTrash() << "\t found:" << inc;
162 pos += re_include.matchedLength();
163 QFSearchDirs sd = searchDirs();
164 //QStringList path = searchPathRef();
165 QString code_file_path = QFFileUtils::path(code_file);
166 //if(!path.contains(code_file_path)) searchPathRef().prepend(code_file_path);
167 if(!code_file_path.isEmpty()) {
168 searchDirsRef().prependDir(code_file_path);
170 QString found_fn;
171 QString inc_code = loadScriptCode(fn, &found_fn);
172 searchDirsRef() = sd;
173 if(found_fn.isEmpty()) {
174 qfWarning() << "can't resolve include " << inc;
176 else {
177 ret.replace(inc, inc_code);
178 qfTrash() << "\t resolved";
181 return ret;
184 static QString replace_module_name(const QString &code, const QString & module_object_name)
186 static const QString s_mn = "${MODULE_NAME}";
187 QString s = code;
188 return s.replace(s_mn, module_object_name);
191 QString QFScriptDriver::loadModuleObjectCode(const QString & module_object_name, QString *found_file_name)
193 qfLogFuncFrame() << "module_object_name:" << module_object_name;
194 QString fn = module_object_name;
195 fn = fn.replace('.', '/') + ".js";
196 QString ffn;
197 QString code = QFScriptDriver::loadScriptCode(fn, &ffn);
198 if(!ffn.isEmpty()) {
199 code = replace_module_name(code, module_object_name);
200 if(found_file_name) *found_file_name = ffn;
202 return code;
205 QString QFScriptDriver::loadScriptCode(const QString & script_name, QString *found_file_name)
207 qfLogFuncFrame() << "script_name:" << script_name;
208 QString ret;
209 QString found_fn;
210 QString fn = searchDirs().findFile(script_name);
212 foreach(QString s, searchPathRef()) {
213 QString s2 = QFFileUtils::joinPath(s, script_name);
214 qfTrash() << "\t trying:" << s2;
215 if(QFile::exists(s2)) {
216 fn = s2;
217 break;
221 if(fn.isEmpty()) {
222 if(QFile::exists(script_name)) fn = script_name;
224 if(!fn.isEmpty()) {
225 QFile f(fn);
226 if(f.open(QFile::ReadOnly)) {
227 qfTrash() << "\t opening script:" << fn;
228 found_fn = fn;
229 QByteArray ba = f.readAll();
230 ret = QString::fromUtf8(ba.constData(), ba.size());
231 ret = resolveIncludes(ret, fn);
234 if(found_file_name) *found_file_name = found_fn;
235 return ret;
240 QScriptValue QFScriptDriver::evaluateFirstFunction(const QString & script_name, bool throw_exc) throw( QFException )
242 QStringList fnc_lst = functionList(script_name);
243 if(fnc_lst.isEmpty()) QF_EXCEPTION(tr("Skript %1 nebyl nalezen nebo neobsahuje zadne funkce.").arg(script_name));
244 QScriptValue sv;
245 sv = evaluateFunction(fnc_lst[0], script_name, throw_exc);
246 return sv;
250 static QString number_lines(const QString &code)
252 QStringList code_lines = code.split('\n');
253 QStringList lines_with_numbers;
254 int n = 0;
255 foreach(QString s, code_lines) lines_with_numbers << QString::number(++n) + ": " + s;
256 return lines_with_numbers.join("\n");
259 QScriptValue QFScriptDriver::evaluate(const QString & script_code, bool throw_exc) throw( QFException )
261 qfLogFuncFrame();
262 QScriptEngine *eng = scriptEngine();
263 qfTrash().color(QFLog::Yellow) << script_code.mid(0, 380);
264 //qfTrash() << dumpContext();
265 QScriptValue ret = eng->evaluate(script_code);
266 if(throw_exc && eng->hasUncaughtException()) {
267 QString s = tr("Vyjjimka \n%3\n pri provadeni skriptu na radku %1\n%2\n\n%4")
268 .arg(eng->uncaughtExceptionLineNumber())
269 .arg(eng->uncaughtExceptionBacktrace().join("\n")).arg(ret.toString()).arg(number_lines(script_code));
270 QF_EXCEPTION(s);
272 if(ret.isError() && throw_exc) {
273 QF_EXCEPTION(ret.toString());
275 qfTrash() << "\t return:" << scriptValueToJsonString(ret);
276 return ret;
279 QScriptValue QFScriptDriver::evaluateScript(const QString & script_name, bool throw_exc) throw( QFException )
281 QString found_fn;
282 QString script_code = loadScriptCode(script_name, &found_fn);
283 if(found_fn.isEmpty()) {
284 if(throw_exc) QF_EXCEPTION(tr("Skript '%1' nebyl nalezen.").arg(script_name));
285 else {
286 qfWarning() << "script code" << script_name << "load error";
287 return QScriptValue();
290 return evaluate(script_code, throw_exc);
293 QScriptValue QFScriptDriver::moduleObject(const QString & module_name, bool throw_exc) throw( QFException )
295 qfLogFuncFrame() << "module_name:" << module_name << "throw_exc:" << throw_exc;
296 /// tohle by melo fungovat stejne, ale pokud modul obsahuje klicove slovo javascriptu, tak NE
297 ///QScriptValue sv = scriptEngine()->evaluate(module_name);
298 QScriptValue sv = scriptEngine()->evaluate("qf.getObject("SARG(module_name)")");
299 qfTrash() << "\t sv:" << sv.toString();
300 if(!sv.isObject() || sv.isError()) {
301 qfTrash() << "\t not found, try to load it";
302 QString ffn;
303 QString code = loadModuleObjectCode(module_name, &ffn);
304 if(ffn.isEmpty()) {
305 if(throw_exc) QF_EXCEPTION(tr("Can't load code for module object: '%1'.").arg(module_name));
306 return QScriptValue();
308 evaluate(code, throw_exc);
309 sv = scriptEngine()->evaluate("qf.getObject("SARG(module_name)")");
310 //sv = scriptEngine()->evaluate(module_name);
311 //qfInfo() << QFScriptDriver::scriptValueToJsonString(sv);
312 if(!sv.isObject() || sv.isError()) {
313 if(sv.isError()) {
314 if(throw_exc) QF_EXCEPTION(tr("Load module object error '%1'.").arg(module_name));
316 else {
317 if(throw_exc) QF_EXCEPTION(tr("Load module object error '%1' is neighter object nor function.").arg(module_name));
319 sv = QScriptValue();
322 return sv;
325 QScriptValue QFScriptDriver::callObjectFunction(const QString function_name, const QString & object_name, const QScriptValue & this_object, const QScriptValueList & args, bool throw_exc) throw( QFException )
327 //QString object_name = module_name;
328 //object_name.replace('.', '_');
329 return callFunction(module_name + "." + function_name, object_name, this_object, args, throw_exc);
333 QScriptValue QFScriptDriver::callModuleObjectFunction(const QString function_name, const QString & module_object_name, const QScriptValue & this_object, const QScriptValueList & args, bool throw_exc) throw( QFException )
335 qfLogFuncFrame();
336 qfTrash().color(QFLog::Green) << "function_name:" << function_name << "module_name:" << module_object_name;
337 QScriptValue sv = resolveModuleObjectFunction(function_name, module_object_name, throw_exc);
338 qfTrash() << "\t fn:" << sv.toString();
339 qfTrash() << "\t this_object:" << this_object.toString();
340 sv = callFunction(sv, this_object, args, throw_exc);
341 return sv;
344 QScriptValue QFScriptDriver::callFunction(const QString function_name, const QString & script_name, const QScriptValue & this_object, const QScriptValueList & args, bool throw_exc) throw( QFException )
346 qfLogFuncFrame();
347 qfTrash().color(QFLog::Green) << "function_name:" << function_name << "script_name:" << script_name;
348 QScriptValue sv = resolveFunction(function_name, script_name, throw_exc);
349 QScriptValue this_o = this_object;
350 if(!this_o.isValid()) {
351 QStringList sl = function_name.split('.');
352 if(sl.count() > 1) {
353 sl.removeLast();
354 this_o = evaluate(sl.join("."), throw_exc);
357 qfTrash() << "\t fn:" << sv.toString();
358 qfTrash() << "\t this_object:" << this_o.toString();
359 sv = callFunction(sv, this_o, args, throw_exc);
360 return sv;
363 QScriptValue QFScriptDriver::callFunction(QScriptValue & fn, const QScriptValue & this_object, const QScriptValueList & args, bool throw_exc) throw( QFException )
365 qfLogFuncFrame();
366 if(throw_exc && !fn.isFunction()) {
367 QString s = tr("Objekt neni funkce.");
368 QF_EXCEPTION(s);
370 QScriptValue ret = fn.call(this_object, args);
371 QScriptEngine *eng = fn.engine();
372 if(throw_exc && eng->hasUncaughtException()) {
373 QString s = tr("Vyjjimka ve funkci na radku %1\n%2\n\n%3")
374 .arg(eng->uncaughtExceptionLineNumber())
375 .arg(eng->uncaughtExceptionBacktrace().join("\n"))
376 .arg(number_lines(fn.toString()));
377 QF_EXCEPTION(s);
379 return ret;
382 void QFScriptDriver::log(int level, const QString & message)
384 //qfInfo() << QF_FUNC_NAME;
385 QFLog(level, QString("%1:%2").arg("#script").arg('#')) << message;
388 QScriptValue QFScriptDriver::variantToScriptValue(const QVariant & v)
390 QScriptValue ret;
391 //qfInfo() << "value:" << v.toString() << "type:" << QVariant::typeToName(v.type());
392 switch(v.type()) {
393 case QVariant::String:
394 ret = QScriptValue(scriptEngine(), v.toString());
395 break;
396 case QVariant::Bool:
397 ret = QScriptValue(scriptEngine(), v.toBool());
398 break;
399 case QVariant::Int:
400 ret = QScriptValue(scriptEngine(), v.toInt());
401 break;
402 case QVariant::UInt:
403 ret = QScriptValue(scriptEngine(), v.toUInt());
404 break;
405 case QVariant::Double:
406 //qfInfo() << "double:" << v.toDouble();
407 ret = QScriptValue(scriptEngine(), v.toDouble());
408 break;
409 case QVariant::Time:
410 case QVariant::Date:
411 case QVariant::DateTime:
412 ret = scriptEngine()->newDate(v.toDateTime());
413 break;
414 case QVariant::List:
416 ret = scriptEngine()->newArray();
417 quint32 ix = 0;
418 foreach(QVariant v1, v.toList()) {
419 ret.setProperty(ix++, variantToScriptValue(v1));
422 break;
423 case QVariant::Map:
425 ret = scriptEngine()->newObject();
426 //qfInfo() << "map" << ret.toString();
427 QMapIterator<QString, QVariant> it(v.toMap());
428 while (it.hasNext()) {
429 it.next();
430 QString key = it.key();
431 QVariant val = it.value();
432 ret.setProperty(key, variantToScriptValue(val));
435 break;
436 default:
437 if(v.isNull()) ret = scriptEngine()->nullValue();
438 else ret = scriptEngine()->newVariant(v);
440 return ret;
443 QVariant QFScriptDriver::scriptValueToVariant(const QScriptValue & sv)
445 qfLogFuncFrame() << "sv:" << sv.toString() << "__qfTypeName:" << sv.property("__qfTypeName").toString();
446 QVariant ret;
447 if(sv.isObject()) {
448 if(sv.property("__qfTypeName").toString() == "qf.sql.ResultSet") {
449 QFXmlTableDocument doc;
450 QFXmlTable xt = doc.toTable();
452 QScriptValue flds = sv.property("fields");
453 QScriptValueIterator it(flds);
454 while (it.hasNext()) {
455 it.next();
456 //qfInfo() << it.name() << ": " << it.value().toString();
457 QScriptValue fld = it.value();
458 QByteArray ba = fld.property("type").toString().toAscii();
459 xt.appendColumn(fld.property("name").toString(), QVariant::nameToType(ba.constData()));
463 QScriptValue rows = sv.property("rows");
464 QScriptValueIterator it(rows);
465 while (it.hasNext()) {
466 it.next();
467 QScriptValue row = it.value();
468 QFXmlTableRow xr = xt.appendRow();
469 QScriptValueIterator it2(row.property("values"));
470 while (it2.hasNext()) {
471 it2.next();
472 xr.setValue(it2.name().toInt(), it2.value().toVariant());
476 ret.setValue(doc);
477 //qfInfo() << scriptValueToJsonString(sv);
478 //qfInfo() << doc.toString();
480 else {
481 ret = sv.toVariant();
484 else {
485 qfTrash() << "\t isDate:" << sv.isDate() << "toDate:" << sv.toDateTime().toString();
486 ret = sv.toVariant();
488 qfTrash() << "\t return:" << ret.toString() << "type:" << QVariant::typeToName(ret.type());
489 return ret;
492 QString QFScriptDriver::scriptValueToJsonString(const QScriptValue & sv)
494 //qfLogFuncFrame() << sv.toString();
495 //static int limit = 0;
496 QString ret;
497 if(sv.isString()) {
498 ret = QFJson::variantToString(sv.toString());
500 else if(sv.isArray()) {
501 QStringList sl;
502 int len = sv.property("length").toInt32();
503 for(int i=0; i<len; i++) {
504 QScriptValue v = sv.property((quint32)i);
505 sl << scriptValueToJsonString(v);
507 ret = '[' + sl.join(",") + ']';
509 else if(sv.isObject()) {
510 QStringList sl;
511 QScriptValueIterator it(sv);
512 while(it.hasNext()) {
513 it.next();
514 QString k = it.name();
515 QScriptValue v = it.value();
516 QString val_str;
517 if(k == "prototype") {
518 continue;
519 //val_str = v.toString();
521 else if(k == "stack") {
522 val_str = "stackTrace";
524 else {
525 if(sv.isFunction()) {
526 val_str = "function()";
528 else {
529 //qfTrash() << "OBJECT key:" << k << "value:" << v.toString();
530 val_str = scriptValueToJsonString(v);
533 QString s = k + ":" + val_str;
534 sl << s;
536 ret = '{' + sl.join(",") + '}';
538 else {
539 ret = sv.toString();
541 //qfTrash() << "\treturn:" << ret;
542 return ret;
545 QString QFScriptDriver::dumpContext()
547 QStringList sl;
548 QScriptContext *ctx = scriptEngine()->currentContext();
549 sl << "============ CONTEXT DUMP =============";
550 sl << "context addr: " + ((ctx)? QString::number((long)ctx, 16): "NULL");
551 if(ctx) {
553 QStringList sl1;
554 QScriptContext *ctx1 = ctx->parentContext();
555 while(ctx1) {
556 sl1 << QString::number((long)ctx1, 16);
557 ctx1 = ctx1->parentContext();
559 sl << "parents: \n\t" + sl1.join("\n\t");
561 sl << "engine addr: " + QString::number((long)ctx->engine(), 16);
562 sl << "this object: " + ctx->thisObject().toString();
563 sl << "callee: " + ctx->callee().toString();
564 sl << "backtrace: \n\t" + ctx->backtrace().join("\n\t");
566 return sl.join("\n");
569 //=========================================================
570 // QFSqlScriptDriver
571 //=========================================================
572 #ifdef QF_SCRIPT_DRIVER_CACHE_SCRIPT_CODES
573 QFSqlScriptDriver::ScriptCodeMap QFSqlScriptDriver::scriptCodeMap;
574 #endif
575 QFSqlScriptDriver::QFSqlScriptDriver(QObject * parent)
576 : QFScriptDriver(parent)
578 f_scriptTableName = "scripts";
579 f_keyFieldName = "ckey";
580 f_codeFieldName = "code";
583 QStringList QFSqlScriptDriver::availableScriptNames(const QString &path)
585 static QString s_qs = "SELECT ${KEY_FLDNAME} FROM ${SCRIPT_TBLNAME} %1 ORDER BY ${KEY_FLDNAME}";
586 QStringList ret;
588 QFSqlQuery q(appConnection());
589 QString domain_filter = path.trimmed();
590 if(!domain_filter.isEmpty()) {
591 domain_filter = QString("WHERE ${KEY_FLDNAME} LIKE '%1.%'").arg(domain_filter);
593 QString qs = s_qs.arg(domain_filter);
594 qs = correctNames(qs);
595 //qfInfo() << qs;
596 q.exec(qs);
597 while(q.next()) {
598 ret << q.value(0).toString();
599 //qfTrash() << "\t src from DB:" << src;
601 return ret;
604 QString QFSqlScriptDriver::loadScriptCode(const QString & script_name, QString * found_file_name_ptr)
606 qfLogFuncFrame() << script_name;
607 QString src;
608 QString found_fn;
609 #ifdef QF_SCRIPT_DRIVER_CACHE_SCRIPT_CODES
610 if(scriptCodeMap.contains(script_name)) {
611 src = scriptCodeMap.value(script_name).code;
612 found_fn = script_name;
614 else
615 #endif
617 QFSqlQuery q(appConnection());
618 static const QString s_qs = "SELECT ${CODE_FLDNAME} FROM ${SCRIPT_TBLNAME} WHERE ${KEY_FLDNAME}='%1'";
619 QString qs = s_qs.arg(script_name);
620 qs = correctNames(qs);
621 qfTrash() << qs;
622 q.exec(qs);
623 if(q.next()) {
624 src = q.value(0).toString();
625 //src = unescapeScriptCodeFromSQL(q.value(0).toString());
626 src = resolveIncludes(src, script_name);
627 #ifdef QF_SCRIPT_DRIVER_CACHE_SCRIPT_CODES
628 scriptCodeMap[script_name] = ScriptCode(src);
629 #endif
630 found_fn = script_name;
631 qfTrash() << "\t found";
634 if(found_fn.isEmpty()) {
635 src = QFScriptDriver::loadScriptCode(script_name, &found_fn);
636 #ifdef QF_SCRIPT_DRIVER_CACHE_SCRIPT_CODES
637 if(!found_fn.isEmpty()) scriptCodeMap[script_name] = ScriptCode(src);
638 #endif
640 if(found_file_name_ptr) *found_file_name_ptr = found_fn;
641 //qfInfo() << src;
642 return src;
645 QString QFSqlScriptDriver::loadModuleObjectCode(const QString & module_object_name, QString * found_file_name)
647 qfLogFuncFrame() << "module_object_name:" << module_object_name;
648 QString ffn;
649 QString code = QFSqlScriptDriver::loadScriptCode(module_object_name, &ffn);
650 if(ffn.isEmpty()) {
651 code = QFScriptDriver::loadModuleObjectCode(module_object_name, &ffn);
653 else {
654 code = replace_module_name(code, module_object_name);
656 if(found_file_name) *found_file_name = ffn;
657 return code;
660 void QFSqlScriptDriver::saveScriptCode(const QString & script_name, const QString & script_code)
662 qfTrash() << QF_FUNC_NAME << script_name;
663 QString qs;
664 qs = "INSERT INTO ${SCRIPT_TBLNAME} (${KEY_FLDNAME}, ${CODE_FLDNAME})"
665 " VALUES('%1', '%2')"
666 " ON DUPLICATE KEY UPDATE ${CODE_FLDNAME} = VALUES(${CODE_FLDNAME})";
667 qs = qs.arg(script_name).arg(script_code);
668 qs = correctNames(qs);
669 QFSqlQuery q(appConnection());
670 //qfInfo() << qs;
671 q.exec(qs);
674 void QFSqlScriptDriver::deleteScriptCode(const QString & script_name)
676 qfTrash() << QF_FUNC_NAME << script_name;
677 QString qs;
678 qs = "DELETE FROM ${SCRIPT_TBLNAME}"
679 " WHERE ${KEY_FLDNAME} = '%1'";
680 qs = qs.arg(script_name);
681 qs = correctNames(qs);
682 QFSqlQuery q(appConnection());
683 //qfInfo() << qs;
684 q.exec(qs);
687 QScriptValue QFSqlScriptDriver::execSql(const QString & query)
689 qfLogFuncFrame() << "query:" << query;
690 QScriptValue ret;
691 QFSqlQuery q(appConnection());
692 q.exec(query);
693 if(q.isSelect()) {
694 QSqlRecord rec = q.record();
695 QScriptEngine *eng = scriptEngine();
696 QScriptValue fields = eng->newArray(rec.count());
697 for(int i=0; i<rec.count(); i++) {
698 QSqlField fld = rec.field(i);
699 QScriptValue field = eng->newObject();
700 field.setProperty("name", QScriptValue(eng, fld.name()));
701 field.setProperty("type", QScriptValue(eng, QVariant::typeToName(fld.type())));
702 fields.setProperty((quint32)i, field);
704 qfTrash() << "\t fields:" << scriptValueToJsonString(fields);
705 qfTrash() << "\t looking for QFSqlResultSet, script engine:" << scriptEngine();
706 QScriptValue resultset_ctor = evaluate("qf.require('qf.sql.ResultSet')");
707 //if(!ctor.isFunction()) { QF_EXCEPTION("QFSqlResultSet javascript initialization error."); }
708 //qfTrash() << ctor.toString();
709 if(!resultset_ctor.isFunction()) { QF_EXCEPTION("qf.sql.Sql.ResultSet javascript initialization error."); }
711 //qfInfo() << "\t fields:" << scriptValueToJsonString(fields);
712 QScriptValueList ctor_params_for_fields;
713 ctor_params_for_fields << fields;
714 QScriptValue result_set = resultset_ctor.construct(ctor_params_for_fields);
715 //qfInfo() << "\t result_set:" << scriptValueToJsonString(result_set);
716 QScriptValue fn_append_row = result_set.property("appendRow");
717 if(!fn_append_row.isFunction()) { QF_EXCEPTION("qf.sql.ResultSet.appendRow() javascript lookup error."); }
718 QScriptValue sqlrow_ctor = evaluate("qf.require('qf.sql.Row')");
719 //QScriptValue fn_set_value = ctor.property("setValue");
720 //if(!fn_set_value.isFunction()) { QF_EXCEPTION("SqlRow::setValue() javascript lookup error."); }
721 QScriptValueList set_value_args;
722 set_value_args << QScriptValue() << QScriptValue();
723 //qfInfo() << "SqlRow:" << eng->evaluate("SqlRow.prototype.setValue").toString();
724 //qfInfo() << "SqlRow.prototype:" << scriptValueToJsonString(eng->evaluate("SqlRow.prototype"));
725 /// pozor QtScript funkce prototype() vraci jiny objekt (property __proto__) nez obj.prototype volany z Javascriptu
726 /// var o = new Object();
727 /// (o.__proto__ === Object.prototype); // this evaluates to true
728 /// presto tohle funguje
729 QScriptValue set_value = eng->evaluate("qf.sql.Row.prototype.setValue");
730 /// tohle ne
731 ///QScriptValue set_value = sqlrow_ctor.prototype().property("setValue");
732 /// tohle taky ne
733 ///QScriptValue set_value = sqlrow_ctor.property("__proto__").property("setValue");
734 //qfInfo() << "setValue:" << set_value.toString();
735 if(!set_value.isFunction()) { QF_EXCEPTION("QFSqlRow::setValue() javascript lookup error."); }
736 while(q.next()) {
737 QScriptValue row = sqlrow_ctor.construct(ctor_params_for_fields);
738 /// tohle funguje
739 ///QScriptValue set_value = row.property("setValue");
740 //if(!set_value.isFunction()) { QF_EXCEPTION("SqlRow::setValue() javascript lookup error."); }
741 for(int i=0; i<rec.count(); i++) {
742 QVariant val = q.value(i);
743 set_value_args[0] = QScriptValue(eng, i);
744 set_value_args[1] = variantToScriptValue(val);
745 //qfInfo() << "value:" << val.toString() << "type:" << QVariant::typeToName(val.type());
746 //set_value.call(row, set_value_args);
747 callFunction(set_value, row, set_value_args);
749 //qfTrash() << "appendRow:" << scriptValueToJsonString(append_row) << append_row.toString();
750 //qfTrash() << "appending row:" << scriptValueToJsonString(row);
751 callFunction(fn_append_row,result_set, QScriptValueList() << row);
753 ret = result_set;
755 //qfTrash() << "execSql ret:" << scriptValueToJsonString(ret);
756 return ret;
759 QString QFSqlScriptDriver::correctNames(const QString & _s)
761 QString s = _s;
762 s.replace("${SCRIPT_TBLNAME}", scriptTableName());
763 s.replace("${KEY_FLDNAME}", keyFieldName());
764 s.replace("${CODE_FLDNAME}", codeFieldName());
765 return s;
768 void QFScriptDriver::resetEngine()
770 if(f_scriptEngine) {
771 qfTrash().color(QFLog::Yellow) << "deleting script engine";
772 SAFE_DELETE(f_scriptEngine);
776 QScriptEngine * QFSqlScriptDriver::scriptEngine()
778 return QFScriptDriver::scriptEngine();
780 if(!f_scriptEngine) {
781 QFScriptDriver::scriptEngine();
782 /// QFSqlResultSet nahraju hned, protoze nahravani ve funkci execSql(), sice funguje, ale je-li tato funkce volana ze skriptu, QFSqlResultSet stejne nezustane v namespace enginu.
783 evaluateScript("qfsql.js");
784 QScriptValue ctor = f_scriptEngine->evaluate("QFSqlResultSet");
785 if(!ctor.isFunction()) qfError() << "QFSqlResultSet javascript initialization error.";
787 return f_scriptEngine;
791 QFSqlConnection & QFSqlScriptDriver::appConnection()
793 QCoreApplication *core_app = QCoreApplication::instance();
794 QFAppDbConnectionInterface *ifc = dynamic_cast<QFAppDbConnectionInterface*>(core_app); /// cross cast
795 if(ifc) return ifc->connection();
796 QF_EXCEPTION("No QFDbAppConfigInterface");
797 return QFAppDbConnectionInterface::dummyConnection();
800 QFSearchDirs & QFScriptDriver::searchDirsRef()
802 static QFSearchDirs sd;
803 if(sd.dirs().isEmpty()) {
804 sd.appendDir(QFFileUtils::joinPath(QFFileUtils::appDir(), "script"));
805 sd.appendDir(":/libqfcore/script");
807 return sd;
810 void QFScriptDriver::prependSearchDir(const QString & dir)
812 searchDirsRef().prependDir(dir);