arcsscriptcomponent.cpp
1 /*
2  name: lib/arcsscriptcomponent.cpp
3 
4  This file is part of ARCS - Augmented Reality Component System
5  (version 2-current), written by Jean-Yves Didier
6  for IBISC Laboratory (http://www.ibisc.univ-evry.fr)
7 
8  Copyright (C) 2013 Université d'Evry-Val d'Essonne
9 
10  This program is free software: you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation, either version 2 of the License, or
13  (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program. If not, see <http://www.gnu.org/licenses/>.
22 
23 
24  Please send bugreports with examples or suggestions to
25  jean-yves.didier__at__ibisc.univ-evry.fr
26 */
27 
28 
29 #include <arcs/arcsscriptcomponent.h>
30 #include <QRegExp>
31 #include <iostream>
32 
33 ARCSScriptQObjectProxy::ARCSScriptQObjectProxy(QObject* parent) : QObject(parent)
34 {
35  engine = new QScriptEngine(this);
36  engine->globalObject().setProperty("callSignal", engine->newFunction(ARCSScriptQObjectProxy::callSignal));
37 
38 }
39 
40 
41 int ARCSScriptQObjectProxy::paramCount(QString s)
42 {
43  if (s.trimmed().isEmpty())
44  return 0;
45  QStringList tmp = s.trimmed().split(',');
46  return tmp.count();
47 }
48 
49 int ARCSScriptQObjectProxy::qt_metacall(QMetaObject::Call call, int id, void** arguments)
50 {
51  id = QObject::qt_metacall(call,id, arguments);
52 
53  if (id == -1 || call != QMetaObject::InvokeMetaMethod)
54  return -1;
55 
56 
57  if (id < slotList.count())
58  {
59  QScriptValue mySlot = engine->globalObject().property(slotList.at(id));
60  QScriptValueList valueList;
61  for (int i=0; i < paramSlotCount.at(id); i++)
62  {
63  // pb : il faut convertir en fonction du type.
64  int type = (paramSlotType.at(id)).at(i);
65  valueList << engine->newVariant(QVariant(type, arguments[i+1]));
66  }
67  QScriptValue result = mySlot.call(engine->globalObject(), valueList);
68  return -1;
69  }
70 
71  id -= slotList.count();
72 
73 
74  if (id < 0)
75  return -1;
76 
77  QMetaObject::activate(this, metaObject(), id + metaObject()->methodCount()+slotList.count(), arguments);
78 
79  return -1;
80 }
81 
82 
83 int ARCSScriptQObjectProxy::cleanSignalConnect(QString sigName, QString sltName, QString /*objectName*/, QString /*actualSlot*/)
84 {
85  sigName = sigName.section("(",0,0);
86  if (!signalList.contains(sigName))
87  {
88  std::cerr << "[Script] Cannot connect signal " << qPrintable(sigName) << std::endl;
89  return -1;
90  }
91 
92 
93  if (signalCollection.contains(sigName))
94  {
95  QStringList sl = signalCollection[sigName];
96  sl.removeAll(sltName);
97  signalCollection[sigName] = sl;
98  }
99 
100 
101  int res = signalList.indexOf(sigName);
102 
103 
104 
105  return metaObject()->methodCount() + slotList.count() + res;
106 }
107 
108 int ARCSScriptQObjectProxy::cleanSlotConnect(QString /*sigName*/,QString sltName, QString /*objectName*/, QString /*actualSignal*/)
109 {
110  sltName=sltName.section("(",0,0);
111  if (! slotList.contains(sltName))
112  {
113  std::cerr << "[Script] Cannot connect slot " << qPrintable(sltName) << std::endl;
114  return -1;
115  }
116 
117  int res = slotList.indexOf(sltName);
118  return metaObject()->methodCount() + res;
119 }
120 
121 
122 int ARCSScriptQObjectProxy::prepareSignalConnect(QString sigName, QString sltName, QString /*objectName*/, QString /*actualSlot*/,bool /*simulate*/)
123 {
124  sigName = sigName.section("(",0,0);
125  if (!signalList.contains(sigName))
126  {
127  std::cerr << "[Script] Cannot connect signal " << qPrintable(sigName) << std::endl;
128  return -1;
129  }
130 
131  int res = signalList.indexOf(sigName);
132 
133  //std::cout << "sltName to connect to signal :" << qPrintable(sltName) << std::endl;
134 
135 
136  // préparation des types.
137  int numParams = paramCount(sltName.section("(",1,1).section(")", 0,0));
138  QStringList paramTypes = sltName.section("(",1,1).section(")", 0,0).split(",");
139 
140  // premier test : si la liste est vide : on retourne rien
141  if (numParams == paramSignalCount[res])
142  {
143  QList<int> types;
144 
145  for (int i=0; i < numParams; i++)
146  types.append(QMetaType::type(qPrintable(paramTypes.at(i))));
147 
148  paramSignalType[res] = types;
149  }
150  return metaObject()->methodCount() + slotList.count() + res;
151 }
152 
153 int ARCSScriptQObjectProxy::prepareSlotConnect(QString sigName, QString sltName, QString /*objectName*/, QString /*actualSignal*/, bool /*simulate*/)
154 {
155  sltName=sltName.section("(",0,0);
156  if (! slotList.contains(sltName))
157  {
158  std::cerr << "[Script] Cannot connect slot " << qPrintable(sltName) << std::endl;
159  return -1;
160  }
161 
162  int res = slotList.indexOf(sltName);
163 
164  QStringList paramTypes = sigName.section("(",1,1).section(")", 0,0).split(",");
165  QList<int> types;
166 
167  for (int i=0; i < paramTypes.count(); i++)
168  types.append(QMetaType::type(qPrintable(paramTypes.at(i))));
169 
170  paramSlotType[res] = types;
171 
172  return metaObject()->methodCount() + res;
173 }
174 
175 
177 {
178  return signalList;
179 }
180 
182 {
183  return slotList;
184 }
185 
186 
187 void ARCSScriptQObjectProxy::createCallSignalScript()
188 {
189  // last signal on stack :
190  QString name = signalList.last();
191  int num = signalList.count()-1;
192  int numParams = paramSignalCount.last();
193 
194  // constitution des paramètres et des paramètres
195  QStringList temp;
196  QList<int> types;
197  for (int i=0; i < numParams ; i++)
198  {
199  temp << (QString("p")+QString::number(i)) ;
200  types.append(QMetaType::type("QString"));
201  }
202  paramSignalType[num] = types ;
203 
204  QString params = temp.join(",");
205 
206  QString signalScript = "function emit_"+ name + "(" + params + ") {\n" ;
207 
208 
209  if (params.isEmpty())
210  signalScript += "callSignal(" + QString::number(num) + ");\n";
211  else
212  signalScript += "callSignal(" + QString::number(num) + "," + params + ");\n";
213 
214  signalScript += "}\n\n";
215  //std::cout << qPrintable(signalScript) << std::endl;
216 
217  engine->evaluate(signalScript);
218 
219  if (engine->hasUncaughtException())
220  std::cerr << "[Script] Cannot evaluate script " << qPrintable(signalScript) << std::endl;
221 
222 }
223 
224 bool ARCSScriptQObjectProxy::setScript(QString s)
225 {
226  QRegExp slotsSearch("function\\s+(\\S+)\\(");
227  QRegExp signalSearch("emit_(\\S+)\\((.*)\\)");
228  signalSearch.setMinimal(true);
229 
230  scriptString = s ;
231 
232  engine->evaluate(s);
233  if (engine->hasUncaughtException())
234  {
235  std::cout << qPrintable(engine->uncaughtException().toString()) << std::endl ;
236  return false;
237  }
238 
239 
240  int pos = 0;
241 
242  // pb : il faut compter les paramètres passés.
243  while ((pos = slotsSearch.indexIn(s, pos)) != -1)
244  {
245  slotList << slotsSearch.cap(1);
246  paramSlotCount.append(slotsSearch.cap(2).trimmed().split(',').count());
247  paramSlotType.append(QList<int>());
248  pos += slotsSearch.matchedLength();
249  std::cout << "[Script] Found one slot " << qPrintable( slotList.last()) << std::endl;
250  }
251 
252  pos = 0;
253  while ((pos = signalSearch.indexIn(s, pos)) != -1)
254  {
255  // pb plusieurs émissions pourraient avoir lieu.
256  // comptons sur un seul emit .
257  // la règle devra être précisée dans le chapitre sur les scripts.
258  if (!signalList.contains(signalSearch.cap(1)))
259  {
260  signalList << signalSearch.cap(1);
261  //std::cout << "signalSearch.cap(2)" << qPrintable(signalSearch.cap(2)) << std::endl;
262  paramSignalCount.append(paramCount(signalSearch.cap(2)));
263  paramSignalType.append(QList<int>());
264  createCallSignalScript();
265  pos += signalSearch.matchedLength();
266  std::cout << "[Script] Found one signal " << qPrintable( signalList.last()) << std::endl;
267  }
268  else
269  {
270  std::cerr << "[Script] Signal " << qPrintable(signalSearch.cap(1)) << " was already found." << std::endl;
271  }
272  }
273 
274  return true;
275 }
276 
277 QScriptValue ARCSScriptQObjectProxy::callSignal(QScriptContext* ctx, QScriptEngine* engine)
278 {
279  //1 préparer les données
280  int nbargs = ctx->argumentCount();
281  void * args[nbargs+1];
282  args[0] = 0 ;
283 
284  int id = ctx->argument(0).toInt32();
285 
286  ARCSScriptQObjectProxy* proxy = (ARCSScriptQObjectProxy*)engine->parent();
287  QVariant var[nbargs];
288 
289  if (proxy->paramSignalType.at(id).count() < nbargs -1 )
290  {
291  std::cout << "[Script] Trouble : " << proxy->paramSignalType.at(id).count()
292  << " params available for " << nbargs -1 << "params required." << std::endl;
293  return QScriptValue();
294 
295  }
296 
297  for (int i = 1; i < nbargs; i++)
298  {
299  var[i] = ctx->argument(i).toVariant();
300  var[i].convert((QVariant::Type)proxy->paramSignalType.at(id).at(i-1));
301  args[i] = var[i].data() ;
302  }
303 
304  //2 lancer l'appel à l'aide de qt_metacall() ou qt_activate()
305  proxy->qt_metacall( QMetaObject::InvokeMetaMethod ,id + proxy->metaObject()->methodCount() + proxy->slotList.count(),args);
306  return QScriptValue();
307 }
308 
309 
310 
311 
312 QString ARCSScriptQObjectProxy::computeParamList(QString slt)
313 {
314  if (slt.section('(',1,1).section(')',0,0).isEmpty())
315  return QString();
316 
317  int nbParams = slt.count(',') + 1 ;
318 
319 
320  QString res;
321 
322  for (int i = 0; i < nbParams; i++)
323  res += "p" + QString::number(i);
324  return res;
325 }
326 
327 
328 
329 
330 
331 /*******************************************************************
332  * ARCSScriptComponent
333  *******************************************************************/
334 
335 
336 
337 ARCSScriptComponent::ARCSScriptComponent() //: engine()
338 {
339  setType("Script");
340  proxy = new ARCSScriptQObjectProxy();
341 
342 }
343 
344 
345 ARCSScriptComponent::~ARCSScriptComponent()
346 {
347 }
348 
350 {
351  scriptString = s;
352  return proxy->setScript(s);
353 }
354 
355 
356 void ARCSScriptComponent::getProxySlot (QString slot, ObjectList &obj, QStringList &proxySlot)
357 {
358  obj << proxy ;
359  proxySlot << slot ;
360 }
361 
362 
363 void ARCSScriptComponent::getProxySignal (QString signal, ObjectList &obj, QStringList &proxySignal)
364 {
365  obj << proxy ;
366  proxySignal << signal;
367 }
368 
369 
This component will be a QObject proxy in order to implement script behaviours */.
virtual void getProxySlot(QString slot, ObjectList &obj, QStringList &proxySlot)
Creates a proxy slot to interface a component to native ARCS components.
virtual int cleanSlotConnect(QString sigName, QString sltName, QString objectName, QString actualSignal)
virtual int cleanSignalConnect(QString sigName, QString sltName, QString objectName, QString actualSlot)
virtual int prepareSignalConnect(QString sigName, QString sltName, QString objectName, QString actualSlot, bool simulate=false)
Prepares a connection with a slot which does not belong to this object.
virtual void getProxySignal(QString signal, ObjectList &obj, QStringList &proxySignal)
Creates a proxy signal to interface a component to native ARCS components.
virtual int prepareSlotConnect(QString sigName, QString sltName, QString objectName, QString actualSignal, bool simulate=false)
Prepares a connection with a slot which is belonging to this object.
virtual QStringList getSignalList()
virtual QStringList getSlotList()
virtual bool parseString(QString s)
Defines a basic seralization mechanism.
virtual int qt_metacall(QMetaObject::Call, int id, void **arguments)
Method performing the actual callback task.