summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNo'am Rosenthal <noam.rosenthal@nokia.com>2009-11-26 14:37:34 -0800
committerNo'am Rosenthal <noam.rosenthal@nokia.com>2009-11-26 14:37:34 -0800
commit7a19605fcc176c03563016836f208c88c1971987 (patch)
tree9fcba3b2934876281fc17ab784adf0f43d2c794e /src
parentf39fc65768abd12577664cf086ab6255a2f17d01 (diff)
many fixes and optimizations to SCXML
Diffstat (limited to 'src')
-rw-r--r--src/qscxml.cpp183
-rw-r--r--src/qscxml.h21
-rw-r--r--src/qscxml.pri8
3 files changed, 152 insertions, 60 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) {
diff --git a/src/qscxml.h b/src/qscxml.h
index fc6b590..4cf4365 100644
--- a/src/qscxml.h
+++ b/src/qscxml.h
@@ -19,7 +19,7 @@
#include <QStringList>
#include <QScriptValue>
#include <QUrl>
-
+#include <QScriptProgram>
class QScriptEngine;
class QScxml;
@@ -60,22 +60,23 @@ class QScxmlTransition : public QAbstractTransition
{
Q_OBJECT
Q_PROPERTY(QString conditionExpression READ conditionExpression WRITE setConditionExpression)
- Q_PROPERTY(QString eventPrefix READ eventPrefix WRITE setEventPrefix)
+ Q_PROPERTY(QStringList eventPrefixes READ eventPrefixes WRITE setEventPrefixes)
public:
QScxmlTransition (QState* state, QScxml* machine);
- QString conditionExpression () const { return cond; }
- void setConditionExpression (const QString & c) { cond = c; }
- QString eventPrefix () const { return ev; }
- void setEventPrefix (const QString & e) { ev = e; }
+ QString conditionExpression () const;
+ void setConditionExpression (const QString & c);
+ QStringList eventPrefixes () const { return ev; }
+ void setEventPrefixes (const QStringList & e) { ev = e; }
protected:
bool eventTest(QEvent*);
void onTransition (QEvent*);
private:
QScxml* scxml;
- QString ev,cond;
+ QStringList ev;
+ QScriptProgram prog;
};
class QScxmlInvoker : public QObject
@@ -147,10 +148,14 @@ class QScxml : public QStateMachine
void setBaseUrl (const QUrl &);
QUrl baseUrl () const;
static QScxml* load (const QString & filename, QObject* o = NULL);
+ QMap<QString,QVariant> data() const;
+ QStringList knownEventNames() const;
public Q_SLOTS:
void postNamedEvent(const QString &);
void executeScript (const QString &);
+ void executeScript (const QScriptProgram &);
+ void setData(const QString & id, const QVariant & value);
private Q_SLOTS:
void registerSession();
@@ -159,6 +164,8 @@ class QScxml : public QStateMachine
Q_SIGNALS:
void eventTriggered(const QString &);
+ void dataChanged (const QString &, const QVariant &);
+ void configurationChanged();
private:
class QScxmlPrivate* pvt;
diff --git a/src/qscxml.pri b/src/qscxml.pri
index 873c038..97caa28 100644
--- a/src/qscxml.pri
+++ b/src/qscxml.pri
@@ -1,7 +1,5 @@
QT += script
-SOURCES += $$PWD/qscxml.cpp \
- $$PWD/qscxmlgui.cpp
+SOURCES += $$PWD/qscxml.cpp
-HEADERS += $$PWD/qscxml.h \
- $$PWD/qscxmlgui.h
-INCLUDEPATH += $$PWD \ No newline at end of file
+HEADERS += $$PWD/qscxml.h
+INCLUDEPATH += $$PWD