diff options
Diffstat (limited to 'src/qscxml.cpp')
-rw-r--r-- | src/qscxml.cpp | 183 |
1 files changed, 135 insertions, 48 deletions
diff --git a/src/qscxml.cpp b/src/qscxml.cpp index fd524ee..ec7eb5c 100644 --- a/src/qscxml.cpp +++ b/src/qscxml.cpp @@ -37,9 +37,7 @@ #include <QFinalState> #include <QState> #include <QMetaMethod> -#ifdef QT_GUI_LIB -#include "qscxmlgui.h" -#endif +#include <QScriptProgram> @@ -168,6 +166,8 @@ class QScxmlPrivate void initScriptEngine(QScxml* thiz); + QScriptValue dataObj; + QScriptEngine* scriptEng; QList<QScxmlInvokerFactory*> invokerFactories; @@ -175,6 +175,8 @@ class QScxmlPrivate QString sessionID; QString startScript; + QSet<QString> knownEvents; + QStack<AnchorSnapshot> snapshotStack; QMultiHash<QString,QAbstractTransition*> anchorTransitions; @@ -189,22 +191,25 @@ class QScxmlTimer : public QObject { Q_OBJECT public: - QScxmlTimer(QScriptEngine* engine, const QScriptValue & scr, int delay) : QObject(engine),script(scr) + QScxmlTimer(QScriptEngine* engine, const QScriptValue & scr, int delay) : QObject(engine) { QTimer::singleShot(delay,this,SLOT(exec())); + if (scr.isString()) { + script = engine->evaluate(QString("function(){%1}").arg(scr.toString())); + } else + script = scr; } protected Q_SLOTS: void exec() { + if (script.isFunction()) script.call(); - else if (script.isString()) - script.engine()->evaluate(script.toString()); deleteLater(); } private: - QScriptValue script; + QScriptValue script; }; @@ -418,6 +423,23 @@ static QScriptValue invoke(QScriptContext *context, QScriptEngine *engine) return QScriptValue(); } +static QScriptValue dataAccess(QScriptContext *context, QScriptEngine *) +{ + if (context->argumentCount() == 0) { + // getter + return context->callee().property("value"); + } else if (context->argumentCount() == 1) { + // setter + QScriptValue val = context->argument(0); + context->callee().setProperty("value",val); + QScxml* scxml = qobject_cast<QScxml*>(context->callee().property("scxml").toQObject()); + if (scxml) { + scxml->dataChanged(context->callee().property("key").toString(),val.toVariant()); + } + return val; + } else + return QScriptValue(); +} static QScriptValue isInState(QScriptContext *context, QScriptEngine *engine) { @@ -438,6 +460,8 @@ static QScriptValue isInState(QScriptContext *context, QScriptEngine *engine) } + + }; void QScxmlPrivate::initScriptEngine(QScxml* thiz) @@ -445,7 +469,6 @@ void QScxmlPrivate::initScriptEngine(QScxml* thiz) QScriptValue glob = scriptEng->globalObject(); QScriptValue scxmlObj = scriptEng->newQObject(thiz); glob.setProperty("In",scriptEng->newFunction(QScxmlFunctions::isInState)); -// glob.setProperty("_rcvSig",scriptEng->newFunction(QScxmlFunctions::receiveSignal)); scxmlObj.setProperty("print",scriptEng->newFunction(QScxmlFunctions::script_print)); scxmlObj.setProperty("postEvent",scriptEng->newFunction(QScxmlFunctions::postEvent)); scxmlObj.setProperty("invoke",scriptEng->newFunction(QScxmlFunctions::invoke)); @@ -455,7 +478,9 @@ void QScxmlPrivate::initScriptEngine(QScxml* thiz) scxmlObj.setProperty("clearTimeout",scriptEng->newFunction(QScxmlFunctions::clearTimeout)); scxmlObj.setProperty("connectSignalToEvent",scriptEng->newFunction(QScxmlFunctions::connectSignalToEvent)); QScriptValue dmObj = scriptEng->newObject(); - glob.setProperty("_data",scriptEng->newObject()); + dataObj = scriptEng->newObject(); + dataObj.setProperty("_values",scriptEng->newObject()); + glob.setProperty("_data",dataObj); glob.setProperty("_global",scriptEng->globalObject()); glob.setProperty("scxml",scxmlObj); } @@ -552,6 +577,16 @@ QScxmlTransition::QScxmlTransition (QState* state,QScxml* machine) : QAbstractTransition(state),scxml(machine) { } + +void QScxmlTransition::setConditionExpression(const QString & c) +{ + prog = QScriptProgram(c,scxml->baseUrl().toLocalFile()); +} + +QString QScxmlTransition::conditionExpression() const +{ + return prog.sourceCode(); +} /*! \reimp */ @@ -564,21 +599,26 @@ void QScxmlTransition::onTransition(QEvent*) bool QScxmlTransition::eventTest(QEvent *e) { QScriptEngine* engine = scxml->scriptEngine(); - QString ev; + QString event; if (e) { if (e->type() == QScxmlEvent::eventType()) { - ev = ((QScxmlEvent*)e)->eventName(); + event = ((QScxmlEvent*)e)->eventName(); + } + bool found = false; + for (int i=0; i < ev.size() && !found; ++i) { + QString prefix = ev[i]; + found = prefix=="*" || prefix==event || event.startsWith(prefix) + || (prefix.endsWith(".*") && event.startsWith(prefix.left(prefix.indexOf(".*")))) + ; } - if (!(eventPrefix() == "*" || eventPrefix() == ev || ev.startsWith(eventPrefix()+"."))) + if (!found ) return false; } if (!conditionExpression().isEmpty()) { - - - QScriptValue v = engine->evaluate(conditionExpression(),scxml->baseUrl().toLocalFile()); + QScriptValue v = engine->evaluate(prog); if (engine->hasUncaughtException()) { QScxmlEvent* e = new QScxmlEvent("error.illegalcond", @@ -588,7 +628,7 @@ bool QScxmlTransition::eventTest(QEvent *e) << QVariant(conditionExpression()) << QVariant(engine->uncaughtExceptionLineNumber()) << QVariant(engine->uncaughtExceptionBacktrace())); - qDebug() << engine->uncaughtException().toString(); + qDebug() << engine->uncaughtException().toString() << prog.sourceCode(); e->metaData.kind = QScxmlEvent::MetaData::Platform; scxml->postEvent(e); engine->clearExceptions(); @@ -732,12 +772,6 @@ void QScxml::init() connect(this,SIGNAL(started()),this,SLOT(registerSession())); connect(this,SIGNAL(stopped()),this,SLOT(unregisterSession())); pvt->initScriptEngine(this); -#ifdef QT_GUI_LIB - static QScxmlAutoInvokerFactory<QScxmlMenuInvoker> _s_menuInvokerFactory; - static QScxmlAutoInvokerFactory<QScxmlMessageBoxInvoker> _s_msgboxInvokerFactory; - registerInvokerFactory(&_s_msgboxInvokerFactory); - registerInvokerFactory(&_s_menuInvokerFactory); -#endif } /*! Creates a new QScxml object, with parent \a parent. @@ -862,6 +896,8 @@ void QScxml::endMicrostep(QEvent*) pvt->snapshotStack.remove(0,pvt->snapshotStack.size()-100); } pvt->curSnapshot.clear(); + qDebug() << configuration(); + emit configurationChanged(); /* if (e->type() == QScxmlEvent::eventType()) { qDebug() << "\n" + _q_configToString(rootState(),-1,configuration()); @@ -907,30 +943,43 @@ void QScxml::postNamedEvent(const QString & event) e->metaData.kind = QScxmlEvent::MetaData::External; postEvent(e); } + +void QScxml::setData(const QString & id, const QVariant & val) +{ + QScriptValue accessor = pvt->dataObj.property(id); + if (accessor.isFunction()) { + accessor.setProperty("value",scriptEngine()->newVariant(val)); + } +} + /*! Executes script \a s in the attached script engine. If the script fails, a "error.illegalvalue" event is posted to the state machine. */ -void QScxml::executeScript (const QString & s) +void QScxml::executeScript (const QScriptProgram & s) { -// qDebug() << "Executing\n--------------------------\n"<<s; - pvt->scriptEng->evaluate (s,baseUrl().toLocalFile()); + qDebug() << "Executing\n--------------------------\n"<<s.sourceCode(); + pvt->scriptEng->evaluate (s); if (pvt->scriptEng->hasUncaughtException()) { QScxmlEvent* e = new QScxmlEvent("error.illegalvalue", QStringList()<< "error" << "expr" << "line" << "backtrace", QVariantList() << QVariant(pvt->scriptEng->uncaughtException().toString()) - << QVariant(s) + << QVariant(s.sourceCode()) << QVariant(pvt->scriptEng->uncaughtExceptionLineNumber()) << QVariant(pvt->scriptEng->uncaughtExceptionBacktrace())); e->metaData.kind = QScxmlEvent::MetaData::Platform; - qDebug() << pvt->scriptEng->uncaughtException().toString(); + qDebug() << pvt->scriptEng->uncaughtException().toString() << s.sourceCode(); postEvent(e); pvt->scriptEng->clearExceptions(); } // qDebug() <<"\n--------------------\n"; } +void QScxml::executeScript (const QString & s) +{ + executeScript(QScriptProgram(s,baseUrl().toLocalFile())); +} /*! Enabled invoker factory \a f to be called from <invoke /> tags. @@ -1034,6 +1083,11 @@ void QScxml::setBaseUrl(const QUrl & u) pvt->burl = u; } +QStringList QScxml::knownEventNames() const +{ + return pvt->knownEvents.toList(); +} + void QScxml::registerSession() { pvt->sessionID = QUuid::createUuid().toString(); @@ -1073,16 +1127,17 @@ struct ScTransitionInfo class QScxmlScriptExec : public QObject { Q_OBJECT - QString script; + QScriptProgram prog; QScxml* scxml; public: - QScxmlScriptExec(const QString & scr, QScxml* scx) : script(scr),scxml(scx) + QScxmlScriptExec(const QString & scr, QScxml* scx) : + prog(QScriptProgram(scr,scx->baseUrl().toLocalFile())),scxml(scx) { } public Q_SLOTS: void exec() { - scxml->executeScript(script); + scxml->executeScript(prog); } }; @@ -1330,7 +1385,7 @@ void QScxmlLoader::loadState ( } else if (r.name().toString().compare("script",Qt::CaseInsensitive) == 0) { QString txt = r.readElementText().trimmed(); if (curExecContext.type == ScExecContext::None && curState == topLevelState) { - stateMachine->executeScript(txt); + stateMachine->executeScript(QScriptProgram(txt,stateMachine->baseUrl().toLocalFile())); } else curExecContext.script += txt; } else if (r.name().toString().compare("log",Qt::CaseInsensitive) == 0) { @@ -1365,7 +1420,9 @@ void QScxmlLoader::loadState ( curExecContext.type = ScExecContext::StateExit; curExecContext.script = ""; } else if (r.name().toString().compare("raise",Qt::CaseInsensitive) == 0 || r.name().toString().compare("event",Qt::CaseInsensitive) == 0 ) { - eventName = QString("\"%1\"").arg(r.attributes().value("event").toString()); + eventName = r.attributes().value("event").toString(); + stateMachine->pvt->knownEvents.insert(eventName); + eventName = QString("\"%1\"").arg(eventName); target = "'_internal'"; targetType = "scxml"; content = "{}"; @@ -1377,10 +1434,20 @@ void QScxmlLoader::loadState ( content = "{}"; target = r.attributes().value("target").toString(); - if (target == "") - target = "\"\""; + if (target == "") { + target = r.attributes().value("targetexpr").toString(); + if (target == "") + target = "\"\""; + } else + target = "\""+target+"\""; targetType = r.attributes().value("type").toString(); eventName = r.attributes().value("event").toString(); + if (eventName == "") { + eventName = r.attributes().value("eventexpr").toString(); + } else { + stateMachine->pvt->knownEvents.insert(eventName); + eventName = "'"+eventName+"'"; + } QStringList nameList = r.attributes().value("namelist").toString().split(" "); foreach (QString name,nameList) { if (name != "") { @@ -1393,10 +1460,14 @@ void QScxmlLoader::loadState ( idLocation = r.attributes().value("sendid").toString(); delay = r.attributes().value("delay").toString(); - if (delay == "") - delay = "0"; - else - delay = QString("scxml.cssTime(%1)").arg(delay); + if (delay == "") { + delay = r.attributes().value("delayexpr").toString(); + if (delay == "") + delay = "0"; + } else + delay = "'"+delay+"'"; + + delay = QString("scxml.cssTime(%1)").arg(delay); } else if (r.name().toString().compare("invoke",Qt::CaseInsensitive) == 0) { idLocation = r.attributes().value("idlocation").toString(); @@ -1429,7 +1500,11 @@ void QScxmlLoader::loadState ( curExecContext.script = ""; curTransition = new QScxmlTransition(curState,stateMachine); curTransition->setConditionExpression(r.attributes().value("cond").toString()); - curTransition->setEventPrefix(r.attributes().value("event").toString()); + curTransition->setEventPrefixes(r.attributes().value("event").toString().split(' ')); + foreach(QString pfx, curTransition->eventPrefixes()) { + if (pfx != "*") + stateMachine->pvt->knownEvents.insert(pfx); + } curExecContext.trans = curTransition; QString anc = r.attributes().value("anchor").toString(); if (!anc.isEmpty()) { @@ -1438,10 +1513,12 @@ void QScxmlLoader::loadState ( } inf.transition = curTransition; transitions.append(inf); - if (curTransition->eventPrefix().startsWith("q-signal:")) { - signalEvents.insert(curTransition->eventPrefix()); + foreach (QString prefix, curTransition->eventPrefixes()) { + if (prefix.startsWith("q-signal:")) { + signalEvents.insert(prefix); + } } - curTransition->setObjectName(QString ("%1 to %2 on %3 if %4 (anchor=%5)").arg(curState->objectName()).arg(inf.targets.join(" ")).arg(curTransition->eventPrefix()).arg(curTransition->conditionExpression()).arg(anc)); + curTransition->setObjectName(QString ("%1 to %2 on %3 if %4 (anchor=%5)").arg(curState->objectName()).arg(inf.targets.join(" ")).arg(curTransition->eventPrefixes().join(" ")).arg(curTransition->conditionExpression()).arg(anc)); } } else if (r.name().toString().compare("anchor",Qt::CaseInsensitive) == 0) { QObject::connect(curState,SIGNAL(exited()),new QScxmlAnchorSave(stateMachine,stateMachine->pvt,r.attributes().value("type").toString(),r.attributes().value("snapshot").toString(),curState),SLOT(save())); @@ -1459,8 +1536,11 @@ void QScxmlLoader::loadState ( val = stateMachine->scriptEngine()->evaluate(t); } } - stateMachine->scriptEngine()->evaluate("_data") - .setProperty(id,val); + QScriptValue func = stateMachine->scriptEngine()->newFunction(QScxmlFunctions::dataAccess); + func.setProperty("key",id); + qDebug() << id << val.toVariant(); + stateMachine->pvt->dataObj.setProperty(id,func,QScriptValue::PropertyGetter|QScriptValue::PropertySetter); + stateMachine->pvt->dataObj.setProperty(id,val); } else if (r.name().toString().compare("param",Qt::CaseInsensitive) == 0) { paramNames << r.attributes().value("name").toString(); paramVals << r.attributes().value("expr").toString(); @@ -1509,6 +1589,7 @@ void QScxmlLoader::loadState ( curExecContext.type = r.name().toString().compare("onexit",Qt::CaseInsensitive)==0 ? ScExecContext::StateExit : ScExecContext::StateEntry; curExecContext.applyScript(); curExecContext.type = ScExecContext::None; + curExecContext.script = ""; } else if (r.name().toString().compare("transition",Qt::CaseInsensitive) == 0) { if (!curHistoryState) { curExecContext.trans = curTransition; @@ -1544,6 +1625,16 @@ void QScxmlLoader::loadState ( } } +QMap<QString,QVariant> QScxml::data() const +{ + QMap<QString,QVariant> d; + QScriptValueIterator it (scriptEngine()->evaluate("_data")); + while (it.hasNext()) { + it.next(); + d[it.name()] = it.value().toVariant(); + } + return d; +} QScxml* QScxmlLoader::load(QIODevice* device, QObject* obj, const QString & filename) { @@ -1562,14 +1653,10 @@ QScxml* QScxmlLoader::load(QIODevice* device, QObject* obj, const QString & file foreach (QString s, signalEvents) { QString sig = s; sig = sig.mid(sig.indexOf(':')+1); -// sig = sig.left(sig.indexOf('(')); int liop = sig.lastIndexOf('.'); QString obj = sig.left(liop); sig = sig.mid(liop+1); stateMachine->pvt->startScript += QString("scxml.connectSignalToEvent(%1,'%2',\"%3\");").arg(obj).arg(sig).arg(s); - -// QString scr = QString("%1.connect({e:\"%2\"},_rcvSig);\n").arg(sig).arg(s); -/// stateMachine->pvt->startScript += scr; } foreach (QState* s, statesWithFinal) { |