1 /* 2 Actiona 3 Copyright (C) 2005 Jonathan Mercier-Ganady 4 5 Actiona is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 Actiona is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 Contact : jmgr@jmgr.info 19 */ 20 21 #include "actioninstance.h" 22 #include "actiondefinition.h" 23 #include "actionfactory.h" 24 #include "elementdefinition.h" 25 #include "parameterdefinition.h" 26 #include "groupdefinition.h" 27 #include "script.h" 28 #include "code/point.h" 29 #include "code/color.h" 30 #include "code/image.h" 31 32 #include <QDateTime> 33 #include <QScriptValueIterator> 34 #include <QApplication> 35 #include <QDesktopWidget> 36 37 namespace ActionTools 38 { operator ==(const ActionInstanceData & other) const39 bool ActionInstanceData::operator==(const ActionInstanceData &other) const 40 { 41 return (parametersData == other.parametersData && 42 definition == other.definition && 43 comment == other.comment && 44 label == other.label && 45 color == other.color && 46 enabled == other.enabled && 47 selected == other.selected && 48 exceptionActionInstances == other.exceptionActionInstances && 49 pauseBefore == other.pauseBefore && 50 pauseAfter == other.pauseAfter && 51 timeout == other.timeout); 52 } 53 54 55 const QRegExp ActionInstance::NumericalIndex(QStringLiteral("(\\d+)")); 56 const QRegExp ActionInstance::NameRegExp(QStringLiteral("^[A-Za-z_][A-Za-z0-9_]*$"), Qt::CaseSensitive, QRegExp::RegExp2); 57 const QRegExp ActionInstance::VariableRegExp(QStringLiteral("\\$([A-Za-z_][A-Za-z0-9_]*)"), Qt::CaseSensitive, QRegExp::RegExp2); 58 qint64 ActionInstance::mCurrentRuntimeId = 0; 59 ActionInstance(const ActionDefinition * definition,QObject * parent)60 ActionInstance::ActionInstance(const ActionDefinition *definition, QObject *parent) 61 : QObject(parent), 62 mRuntimeId(mCurrentRuntimeId), 63 d(new ActionInstanceData()) 64 { 65 d->definition = definition; 66 ++mCurrentRuntimeId; 67 68 //Set the default values 69 if(definition) 70 { 71 for(ElementDefinition *element: definition->elements()) 72 element->setDefaultValues(this); 73 74 //Set the default exception action 75 for(int i = 0; i < ActionTools::ActionException::ExceptionCount; ++i) 76 { 77 setExceptionActionInstance(static_cast<ActionTools::ActionException::Exception>(i), 78 ActionTools::ActionException::ExceptionActionInstance(ActionTools::ActionException::ExceptionDefaultAction[i], QString())); 79 } 80 } 81 } 82 ActionInstance(const ActionInstance & other)83 ActionInstance::ActionInstance(const ActionInstance &other) 84 : QObject(), 85 mRuntimeId(mCurrentRuntimeId), 86 d(other.d) 87 { 88 ++mCurrentRuntimeId; 89 } 90 operator ==(const ActionInstance & other) const91 bool ActionInstance::operator==(const ActionInstance &other) const 92 { 93 return ((*d) == (*other.d)); 94 } 95 operator !=(const ActionInstance & other) const96 bool ActionInstance::operator!=(const ActionInstance &other) const 97 { 98 return !((*d) == (*other.d)); 99 } 100 callProcedure(const QString & procedureName)101 bool ActionInstance::callProcedure(const QString &procedureName) 102 { 103 //Search for the corresponding ActionBeginProcedure action 104 int beginProcedureLine = script()->findProcedure(procedureName); 105 if(beginProcedureLine == -1) 106 { 107 emit executionException(ActionTools::ActionException::InvalidParameterException, tr("Unable to find any procedure named \"%1\"").arg(procedureName)); 108 109 return false; 110 } 111 112 setNextLine(beginProcedureLine + 2);//Lines start at 1 113 114 script()->addProcedureCall(scriptLine()); 115 116 return true; 117 } 118 doStartExecution()119 void ActionInstance::doStartExecution() 120 { 121 ++d->executionCounter; 122 123 d->executionTimer.start(); 124 125 startExecution(); 126 } 127 doStopExecution()128 void ActionInstance::doStopExecution() 129 { 130 stopExecution(); 131 } 132 doPauseExecution()133 void ActionInstance::doPauseExecution() 134 { 135 pauseExecution(); 136 137 d->executionDuration += d->executionTimer.elapsed(); 138 } 139 doResumeExecution()140 void ActionInstance::doResumeExecution() 141 { 142 d->executionTimer.start(); 143 144 resumeExecution(); 145 } 146 copyActionDataFrom(const ActionInstance & other)147 void ActionInstance::copyActionDataFrom(const ActionInstance &other) 148 { 149 setComment(other.comment()); 150 setLabel(other.label()); 151 setParametersData(other.parametersData()); 152 setColor(other.color()); 153 setEnabled(other.isEnabled()); 154 setSelected(other.isSelected()); 155 setExceptionActionInstances(other.exceptionActionInstances()); 156 setPauseBefore(other.pauseBefore()); 157 setPauseAfter(other.pauseAfter()); 158 setTimeout(other.timeout()); 159 } 160 convertToVariableName(const QString & input)161 QString ActionInstance::convertToVariableName(const QString &input) 162 { 163 QString back = input; 164 165 for(int i = 0; i < back.size(); ++i) 166 { 167 if(back[i] >= QLatin1Char('a') && back[i] <= QLatin1Char('z')) 168 continue; 169 if(back[i] >= QLatin1Char('A') && back[i] <= QLatin1Char('Z')) 170 continue; 171 if(i > 0 && back[i] >= QLatin1Char('0') && back[i] <= QLatin1Char('9')) 172 continue; 173 174 back[i] = QLatin1Char('_'); 175 } 176 177 return back; 178 } 179 findVariables(const QString & input,bool code)180 QSet<QString> ActionInstance::findVariables(const QString &input, bool code) 181 { 182 QSet<QString> back; 183 184 if(code) 185 { 186 for(const QString &codeLine: input.split(QRegExp(QStringLiteral("[\n\r;]")), QString::SkipEmptyParts)) 187 { 188 int position = 0; 189 190 while((position = Script::CodeVariableDeclarationRegExp.indexIn(codeLine, position)) != -1) 191 { 192 QString foundVariableName = Script::CodeVariableDeclarationRegExp.cap(1); 193 194 position += Script::CodeVariableDeclarationRegExp.cap(1).length(); 195 196 if(!foundVariableName.isEmpty()) 197 back << foundVariableName; 198 } 199 } 200 } 201 else 202 { 203 int position = 0; 204 205 while((position = ActionInstance::VariableRegExp.indexIn(input, position)) != -1) 206 { 207 QString foundVariableName = ActionInstance::VariableRegExp.cap(1); 208 209 position += ActionInstance::VariableRegExp.cap(0).length(); 210 211 if(!foundVariableName.isEmpty()) 212 back << foundVariableName; 213 } 214 } 215 216 return back; 217 } 218 evaluateValue(bool & ok,const QString & parameterName,const QString & subParameterName)219 QScriptValue ActionInstance::evaluateValue(bool &ok, 220 const QString ¶meterName, 221 const QString &subParameterName) 222 { 223 if(!ok) 224 return QScriptValue(); 225 226 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 227 QScriptValue result; 228 229 if(subParameter.isCode()) 230 result = evaluateCode(ok, subParameter); 231 else 232 result = evaluateText(ok, subParameter); 233 234 if(!ok) 235 return QScriptValue(); 236 237 return result; 238 } 239 evaluateString(bool & ok,const QString & parameterName,const QString & subParameterName)240 QString ActionInstance::evaluateString(bool &ok, 241 const QString ¶meterName, 242 const QString &subParameterName) 243 { 244 if(!ok) 245 return QString(); 246 247 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 248 QString result; 249 250 if(subParameter.isCode()) 251 result = evaluateCode(ok, subParameter).toString(); 252 else 253 result = evaluateText(ok, subParameter); 254 255 if(!ok) 256 return QString(); 257 258 return result; 259 } 260 evaluateImage(bool & ok,const QString & parameterName,const QString & subParameterName)261 QImage ActionInstance::evaluateImage(bool &ok, const QString ¶meterName, const QString &subParameterName) 262 { 263 if(!ok) 264 return QImage(); 265 266 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 267 QString filename; 268 269 if(subParameter.isCode()) 270 { 271 QScriptValue evaluationResult = evaluateCode(ok, subParameter); 272 if(auto codeImage = qobject_cast<Code::Image*>(evaluationResult.toQObject())) 273 return codeImage->image(); 274 275 if(!evaluationResult.isString()) 276 { 277 ok = false; 278 279 emit executionException(ActionException::InvalidParameterException, tr("Invalid image.")); 280 281 return QImage(); 282 } 283 284 filename = evaluationResult.toString(); 285 } 286 else 287 filename = evaluateText(ok, subParameter); 288 289 if(!ok || filename.isEmpty()) 290 return QImage(); 291 292 QImage image(filename); 293 294 if(!image.isNull()) 295 return image; 296 297 ok = false; 298 299 emit executionException(ActionException::InvalidParameterException, tr("Unable to load image: %1").arg(filename)); 300 301 return QImage(); 302 } 303 evaluateVariable(bool & ok,const QString & parameterName,const QString & subParameterName)304 QString ActionInstance::evaluateVariable(bool &ok, 305 const QString ¶meterName, 306 const QString &subParameterName) 307 { 308 QString result = evaluateString(ok, parameterName, subParameterName); 309 310 if(!ok) 311 return QString(); 312 313 if(!result.isEmpty() && !NameRegExp.exactMatch(result)) 314 { 315 ok = false; 316 317 emit executionException(ActionException::InvalidParameterException, tr("A variable name can only contain alphanumeric characters and cannot start with a digit.")); 318 319 return QString(); 320 } 321 322 return result; 323 } 324 evaluateVariableArray(bool & ok,const QScriptValue & scriptValue)325 QString ActionInstance::evaluateVariableArray(bool &ok, const QScriptValue &scriptValue) 326 { 327 QString result; 328 329 QScriptValueIterator it(scriptValue); 330 331 if(scriptValue.isArray()) 332 { 333 int lastIndex = -1; 334 result = QStringLiteral("["); 335 336 while (it.hasNext()) { 337 it.next(); 338 339 if (it.flags() & QScriptValue::SkipInEnumeration) 340 continue; 341 342 QScriptValue nextScriptValue = it.value(); 343 //is it an array ? 344 if(nextScriptValue.isArray()) 345 result += evaluateVariableArray(ok, nextScriptValue); 346 else 347 if(NumericalIndex.exactMatch(it.name())) //it.name : numerical only ? 348 { 349 int newIndex = it.name().toInt(); 350 if( newIndex > lastIndex+1) 351 { 352 //insert some commas 353 for(lastIndex++ ; lastIndex < newIndex; lastIndex++ ) 354 result += QStringLiteral(","); 355 } 356 lastIndex = newIndex; 357 result += it.value().toString(); 358 } 359 else 360 result += it.name().append(QStringLiteral("=")).append(it.value().toString()); 361 362 result += QStringLiteral(","); 363 } 364 365 if(result == QLatin1String("[")) 366 result += QStringLiteral("]"); 367 else 368 result[result.lastIndexOf(QStringLiteral(","))] = QLatin1Char(']'); 369 } 370 else 371 result = it.value().toString(); 372 373 return result; 374 } 375 evaluateInteger(bool & ok,const QString & parameterName,const QString & subParameterName)376 int ActionInstance::evaluateInteger(bool &ok, 377 const QString ¶meterName, 378 const QString &subParameterName) 379 { 380 QString result = evaluateString(ok, parameterName, subParameterName); 381 382 if(!ok || result.isEmpty()) 383 return 0; 384 385 int intResult = result.toInt(&ok); 386 387 if(!ok) 388 { 389 ok = false; 390 391 emit executionException(ActionException::InvalidParameterException, tr("Integer value expected.")); 392 393 return 0; 394 } 395 396 return intResult; 397 } 398 evaluateBoolean(bool & ok,const QString & parameterName,const QString & subParameterName)399 bool ActionInstance::evaluateBoolean(bool &ok, 400 const QString ¶meterName, 401 const QString &subParameterName) 402 { 403 QVariant result = evaluateString(ok, parameterName, subParameterName); 404 405 if(!ok) 406 return false; 407 408 return result.toBool(); 409 } 410 evaluateDouble(bool & ok,const QString & parameterName,const QString & subParameterName)411 double ActionInstance::evaluateDouble(bool &ok, 412 const QString ¶meterName, 413 const QString &subParameterName) 414 { 415 QString result = evaluateString(ok, parameterName, subParameterName); 416 417 if(!ok || result.isEmpty()) 418 return 0.0; 419 420 double doubleResult = result.toDouble(&ok); 421 422 if(!ok) 423 { 424 ok = false; 425 426 emit executionException(ActionException::InvalidParameterException, tr("Decimal value expected.")); 427 428 return 0.0; 429 } 430 431 return doubleResult; 432 } 433 evaluateIfAction(bool & ok,const QString & parameterName)434 IfActionValue ActionInstance::evaluateIfAction(bool &ok, 435 const QString ¶meterName) 436 { 437 QString action = evaluateString(ok, parameterName, QStringLiteral("action")); 438 439 if(!ok) 440 return IfActionValue(); 441 442 return IfActionValue(action, subParameter(parameterName, QStringLiteral("line"))); 443 } 444 evaluateSubParameter(bool & ok,const SubParameter & subParameter)445 QString ActionInstance::evaluateSubParameter(bool &ok, 446 const SubParameter &subParameter) 447 { 448 if(!ok || subParameter.value().isEmpty()) 449 return QString(); 450 451 QString result; 452 453 if(subParameter.isCode()) 454 result = evaluateCode(ok, subParameter).toString(); 455 else 456 result = evaluateText(ok, subParameter); 457 458 if(!ok) 459 return QString(); 460 461 return result; 462 } 463 evaluateEditableListElement(bool & ok,const Tools::StringListPair & listElements,const QString & parameterName,const QString & subParameterName)464 QString ActionInstance::evaluateEditableListElement(bool &ok, const Tools::StringListPair &listElements, const QString ¶meterName, const QString &subParameterName) 465 { 466 if(!ok) 467 return QString(); 468 469 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 470 QString result; 471 472 if(subParameter.isCode()) 473 result = evaluateCode(ok, subParameter).toString(); 474 else 475 result = evaluateText(ok, subParameter); 476 477 if(!ok) 478 return QString(); 479 480 //Search in the translated items 481 for(int i=0;i<listElements.second.size();++i) 482 { 483 if(listElements.second.at(i) == result) 484 return listElements.first.at(i); 485 } 486 487 if(result.isEmpty()) 488 { 489 ok = false; 490 491 setCurrentParameter(parameterName, subParameterName); 492 493 emit executionException(ActionException::InvalidParameterException, tr("Please choose a value for this field.")); 494 495 return QString(); 496 } 497 498 return result; 499 } 500 computePercentPosition(QPointF & point,const SubParameter & unitSubParameter)501 void computePercentPosition(QPointF &point, const SubParameter &unitSubParameter) 502 { 503 if(unitSubParameter.value().toInt() == 1)//Percents 504 { 505 QRect screenGeometry = QApplication::desktop()->screenGeometry(); 506 507 point.setX((point.x() * screenGeometry.width()) / 100.0f); 508 point.setY((point.y() * screenGeometry.height()) / 100.0f); 509 } 510 } 511 evaluatePoint(bool & ok,const QString & parameterName,const QString & subParameterName,bool * empty)512 QPoint ActionInstance::evaluatePoint(bool &ok, 513 const QString ¶meterName, 514 const QString &subParameterName, 515 bool *empty) 516 { 517 if(empty) 518 *empty = false; 519 520 if(!ok) 521 return QPoint(); 522 523 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 524 const SubParameter &unitSubParameter = retreiveSubParameter(parameterName, QStringLiteral("unit")); 525 QString result; 526 527 if(subParameter.isCode()) 528 { 529 QScriptValue evaluationResult = evaluateCode(ok, subParameter); 530 if(auto codePoint = qobject_cast<Code::Point*>(evaluationResult.toQObject())) 531 { 532 QPointF point = QPointF(codePoint->point().x(), codePoint->point().y()); 533 534 computePercentPosition(point, unitSubParameter); 535 536 return QPoint(point.x(), point.y()); 537 } 538 539 result = evaluationResult.toString(); 540 } 541 else 542 result = evaluateText(ok, subParameter); 543 544 if(!ok) 545 return QPoint(); 546 547 if(result.isEmpty() || result == QLatin1String(":")) 548 { 549 if(empty) 550 *empty = true; 551 552 return QPoint(); 553 } 554 555 QStringList positionStringList = result.split(QStringLiteral(":")); 556 if(positionStringList.count() != 2) 557 { 558 ok = false; 559 560 emit executionException(ActionException::InvalidParameterException, tr("\"%1\" is not a valid position.").arg(result)); 561 562 return QPoint(); 563 } 564 565 QPointF point(positionStringList.at(0).toFloat(&ok), positionStringList.at(1).toFloat(&ok)); 566 if(!ok) 567 { 568 emit executionException(ActionException::InvalidParameterException, tr("\"%1\" is not a valid position.").arg(result)); 569 570 return QPoint(); 571 } 572 573 computePercentPosition(point, unitSubParameter); 574 575 return {static_cast<int>(point.x()), static_cast<int>(point.y())}; 576 } 577 evaluateItemList(bool & ok,const QString & parameterName,const QString & subParameterName)578 QStringList ActionInstance::evaluateItemList(bool &ok, const QString ¶meterName, const QString &subParameterName) 579 { 580 if(!ok) 581 return QStringList(); 582 583 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 584 QString result; 585 586 if(subParameter.isCode()) 587 result = evaluateCode(ok, subParameter).toString(); 588 else 589 result = evaluateText(ok, subParameter); 590 591 if(!ok) 592 return QStringList(); 593 594 return result.split(QLatin1Char('\n'), QString::SkipEmptyParts); 595 } 596 evaluatePolygon(bool & ok,const QString & parameterName,const QString & subParameterName)597 QPolygon ActionInstance::evaluatePolygon(bool &ok, 598 const QString ¶meterName, 599 const QString &subParameterName) 600 { 601 if(!ok) 602 return QPolygon(); 603 604 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 605 QString result; 606 607 if(subParameter.isCode()) 608 result = evaluateCode(ok, subParameter).toString(); 609 else 610 result = evaluateText(ok, subParameter); 611 612 if(!ok) 613 return QPolygon(); 614 615 if(result.isEmpty() || result == QLatin1String(";")) 616 return QPolygon(); 617 618 QStringList pointStrings = result.split(QLatin1Char(';'), QString::SkipEmptyParts); 619 QPolygon polygon; 620 621 for(const QString &pointString: pointStrings) 622 { 623 QStringList pointComponents = pointString.split(QLatin1Char(':'), QString::SkipEmptyParts); 624 if(pointComponents.size() != 2) 625 continue; 626 627 polygon << QPoint(pointComponents.at(0).toInt(), pointComponents.at(1).toInt()); 628 } 629 630 return polygon; 631 } 632 evaluateColor(bool & ok,const QString & parameterName,const QString & subParameterName)633 QColor ActionInstance::evaluateColor(bool &ok, 634 const QString ¶meterName, 635 const QString &subParameterName) 636 { 637 if(!ok) 638 return {}; 639 640 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 641 QString result; 642 643 if(subParameter.isCode()) 644 { 645 QScriptValue evaluationResult = evaluateCode(ok, subParameter); 646 if(auto codeColor = qobject_cast<Code::Color*>(evaluationResult.toQObject())) 647 return codeColor->color(); 648 649 result = evaluationResult.toString(); 650 } 651 else 652 result = evaluateText(ok, subParameter); 653 654 if(!ok) 655 return QColor(); 656 657 if(result.isEmpty() || result == QLatin1String("::")) 658 return QColor(); 659 660 QStringList colorStringList = result.split(QStringLiteral(":")); 661 if(colorStringList.count() != 3) 662 { 663 ok = false; 664 665 emit executionException(ActionException::InvalidParameterException, tr("\"%1\" is not a valid color.").arg(result)); 666 667 return QColor(); 668 } 669 670 QColor color = QColor(colorStringList.at(0).toInt(&ok), colorStringList.at(1).toInt(&ok), colorStringList.at(2).toInt(&ok)); 671 if(!ok) 672 { 673 emit executionException(ActionException::InvalidParameterException, tr("\"%1\" is not a valid color.").arg(result)); 674 675 return QColor(); 676 } 677 678 return color; 679 } 680 evaluateDateTime(bool & ok,const QString & parameterName,const QString & subParameterName)681 QDateTime ActionInstance::evaluateDateTime(bool &ok, const QString ¶meterName, const QString &subParameterName) 682 { 683 if(!ok) 684 return QDateTime(); 685 686 const SubParameter &subParameter = retreiveSubParameter(parameterName, subParameterName); 687 QString result; 688 689 if(subParameter.isCode()) 690 { 691 QScriptValue evaluationResult = evaluateCode(ok, subParameter); 692 if(evaluationResult.isDate()) 693 return evaluationResult.toDateTime(); 694 695 result = evaluationResult.toString(); 696 } 697 else 698 result = evaluateText(ok, subParameter); 699 700 if(!ok) 701 return QDateTime(); 702 703 QDateTime dateTime = QDateTime::fromString(result, QStringLiteral("dd/MM/yyyy hh:mm:ss")); 704 705 if(!dateTime.isValid()) 706 { 707 ok = false; 708 709 return QDateTime(); 710 } 711 712 return dateTime; 713 } 714 validateParameterRange(bool & ok,int parameter,const QString & parameterName,const QString & parameterTranslatedName,int minimum,int maximum)715 void ActionInstance::validateParameterRange(bool &ok, int parameter, const QString ¶meterName, const QString ¶meterTranslatedName, int minimum, int maximum) 716 { 717 if(ok && (parameter < minimum || parameter > maximum)) 718 { 719 ok = false; 720 721 setCurrentParameter(parameterName); 722 emit executionException(ActionTools::ActionException::InvalidParameterException, tr("Invalid %1 value : %2").arg(parameterTranslatedName).arg(parameter)); 723 return; 724 } 725 } 726 nextLine() const727 QString ActionInstance::nextLine() const 728 { 729 return d->scriptEngine->property("Script.nextLine").toString(); 730 } 731 setNextLine(const QString & nextLine,bool doNotResetPreviousActions)732 void ActionInstance::setNextLine(const QString &nextLine, bool doNotResetPreviousActions) 733 { 734 QScriptValue scriptValue = d->scriptEngine->globalObject().property(QStringLiteral("Script")); 735 scriptValue.setProperty(QStringLiteral("nextLine"), d->scriptEngine->newVariant(QVariant(nextLine))); 736 scriptValue.setProperty(QStringLiteral("doNotResetPreviousActions"), doNotResetPreviousActions); 737 } 738 setNextLine(int nextLine,bool doNotResetPreviousActions)739 void ActionInstance::setNextLine(int nextLine, bool doNotResetPreviousActions) 740 { 741 setNextLine(QString::number(nextLine), doNotResetPreviousActions); 742 } 743 setArray(const QString & name,const QStringList & stringList)744 void ActionInstance::setArray(const QString &name, const QStringList &stringList) 745 { 746 if(stringList.isEmpty()) 747 return; 748 749 QScriptValue back = d->scriptEngine->newArray(stringList.count()); 750 751 for(int index = 0; index < stringList.count(); ++index) 752 back.setProperty(index, stringList.at(index)); 753 754 if(!name.isEmpty() && NameRegExp.exactMatch(name)) 755 d->scriptEngine->globalObject().setProperty(name, back); 756 } 757 setArrayKeyValue(const QString & name,const QHash<QString,QString> & hashKeyValue)758 void ActionInstance::setArrayKeyValue(const QString &name, const QHash<QString, QString> &hashKeyValue) 759 { 760 if(hashKeyValue.isEmpty()) 761 return; 762 763 QScriptValue back = d->scriptEngine->newArray(hashKeyValue.count()); 764 765 QHashIterator<QString, QString> it(hashKeyValue); 766 while (it.hasNext()) 767 { 768 it.next(); 769 back.setProperty(it.key(), it.value()); 770 } 771 772 setVariable(name, back); 773 } 774 setVariable(const QString & name,const QScriptValue & value)775 void ActionInstance::setVariable(const QString &name, const QScriptValue &value) 776 { 777 if(!name.isEmpty() && NameRegExp.exactMatch(name)) 778 d->scriptEngine->globalObject().setProperty(name, value); 779 } 780 variable(const QString & name)781 QScriptValue ActionInstance::variable(const QString &name) 782 { 783 if(name.isEmpty() || !NameRegExp.exactMatch(name)) 784 return QScriptValue(); 785 786 return d->scriptEngine->globalObject().property(name); 787 } 788 setCurrentParameter(const QString & parameterName,const QString & subParameterName)789 void ActionInstance::setCurrentParameter(const QString ¶meterName, const QString &subParameterName) 790 { 791 d->scriptEngine->globalObject().setProperty(QStringLiteral("currentParameter"), parameterName, QScriptValue::ReadOnly); 792 d->scriptEngine->globalObject().setProperty(QStringLiteral("currentSubParameter"), subParameterName, QScriptValue::ReadOnly); 793 } 794 executionEnded()795 void ActionInstance::executionEnded() 796 { 797 emit executionEndedSignal(); 798 799 d->executionDuration += d->executionTimer.elapsed(); 800 } 801 retreiveSubParameter(const QString & parameterName,const QString & subParameterName)802 SubParameter ActionInstance::retreiveSubParameter(const QString ¶meterName, const QString &subParameterName) 803 { 804 setCurrentParameter(parameterName, subParameterName); 805 806 SubParameter back = subParameter(parameterName, subParameterName); 807 808 // Re-evaluate the field as code if it contains a single variable 809 if(!back.isCode() && back.value().startsWith(QLatin1Char('$'))) 810 { 811 QString stringValue = back.value(); 812 QString variableName = stringValue.right(stringValue.size() - 1); 813 const QScriptValue &value = d->scriptEngine->globalObject().property(variableName); 814 815 if(value.isValid()) 816 { 817 back.setCode(true); 818 back.setValue(variableName); 819 } 820 } 821 822 return back; 823 } 824 evaluateCode(bool & ok,const QString & toEvaluate)825 QScriptValue ActionInstance::evaluateCode(bool &ok, const QString &toEvaluate) 826 { 827 ok = true; 828 829 QScriptValue result = d->scriptEngine->evaluate(toEvaluate); 830 if(result.isError()) 831 { 832 ok = false; 833 834 emit executionException(ActionException::CodeErrorException, result.toString()); 835 return QScriptValue(); 836 } 837 838 if(!result.isValid()) 839 { 840 ok = false; 841 842 return QScriptValue(); 843 } 844 845 return result; 846 } 847 evaluateCode(bool & ok,const SubParameter & toEvaluate)848 QScriptValue ActionInstance::evaluateCode(bool &ok, const SubParameter &toEvaluate) 849 { 850 return evaluateCode(ok, toEvaluate.value()); 851 } 852 evaluateText(bool & ok,const QString & toEvaluate)853 QString ActionInstance::evaluateText(bool &ok, const QString &toEvaluate) 854 { 855 ok = true; 856 857 int position = 0; 858 859 return evaluateTextString(ok, toEvaluate, position); 860 } 861 evaluateText(bool & ok,const SubParameter & toEvaluate)862 QString ActionInstance::evaluateText(bool &ok, const SubParameter &toEvaluate) 863 { 864 return evaluateText(ok, toEvaluate.value()); 865 } 866 evaluateTextString(bool & ok,const QString & toEvaluate,int & position)867 QString ActionInstance::evaluateTextString(bool &ok, const QString &toEvaluate, int &position) 868 { 869 ok = true; 870 871 int startIndex = position; 872 873 QString result; 874 875 while(position < toEvaluate.length()) 876 { 877 if(toEvaluate[position] == QLatin1Char('$')) 878 { 879 //find a variable name 880 if(VariableRegExp.indexIn(toEvaluate, position) != -1) 881 { 882 QString foundVariableName = VariableRegExp.cap(1); 883 QScriptValue foundVariable = d->scriptEngine->globalObject().property(foundVariableName); 884 885 position += foundVariableName.length(); 886 887 if(!foundVariable.isValid()) 888 { 889 ok = false; 890 891 emit executionException(ActionException::InvalidParameterException, tr("Undefined variable \"%1\"").arg(foundVariableName)); 892 return QString(); 893 } 894 895 QString stringEvaluationResult; 896 897 if(foundVariable.isNull()) 898 stringEvaluationResult = QStringLiteral("[Null]"); 899 else if(foundVariable.isUndefined()) 900 stringEvaluationResult = QStringLiteral("[Undefined]"); 901 else if(foundVariable.isArray()) 902 { 903 while((position + 1 < toEvaluate.length()) && toEvaluate[position + 1] == QLatin1Char('[')) 904 { 905 position += 2; 906 QString indexArray = evaluateTextString(ok, toEvaluate, position); 907 908 if((position < toEvaluate.length()) && toEvaluate[position] == QLatin1Char(']')) 909 { 910 QScriptString internalIndexArray = d->scriptEngine->toStringHandle(indexArray); 911 bool flag = true; 912 int numIndex = internalIndexArray.toArrayIndex(&flag); 913 914 if(flag) //numIndex is valid 915 foundVariable = foundVariable.property(numIndex); 916 else //use internalIndexArray 917 foundVariable = foundVariable.property(internalIndexArray); 918 } 919 else 920 { 921 //syntax error 922 ok = false; 923 924 emit executionException(ActionException::InvalidParameterException, tr("Invalid parameter. Unable to evaluate string")); 925 return QString(); 926 } 927 928 //COMPATIBILITY: we break the while loop if foundVariable is no more of Array type 929 if(!foundVariable.isArray()) 930 break; 931 } 932 //end of while, no more '[' 933 if(foundVariable.isArray()) 934 stringEvaluationResult = evaluateVariableArray(ok, foundVariable); 935 else 936 stringEvaluationResult = foundVariable.toString(); 937 } 938 else if(foundVariable.isVariant()) 939 { 940 QVariant variantEvaluationResult = foundVariable.toVariant(); 941 switch(variantEvaluationResult.type()) 942 { 943 case QVariant::StringList: 944 stringEvaluationResult = variantEvaluationResult.toStringList().join(QStringLiteral("\n")); 945 break; 946 case QVariant::ByteArray: 947 stringEvaluationResult = QStringLiteral("[Raw data]"); 948 break; 949 default: 950 stringEvaluationResult = foundVariable.toString(); 951 break; 952 } 953 } 954 else 955 stringEvaluationResult = foundVariable.toString(); 956 957 result.append(stringEvaluationResult); 958 } 959 960 } 961 else if (toEvaluate[position] == QLatin1Char(']')) 962 { 963 if(startIndex == 0) 964 //in top level evaluation isolated character ']' is accepted (for compatibility reason), now prefer "\]" 965 //i.e without matching '[' 966 result.append(toEvaluate[position]); 967 else 968 //on other levels, the parsing is stopped at this point 969 return result; 970 } 971 else if(toEvaluate[position] == QLatin1Char('\\')) 972 { 973 if(startIndex == 0) 974 { 975 //for ascendant compatibility reason 976 //in top level evaluation '\' is not only an escape character, 977 //but can also be a standard character in some cases 978 if((position + 1) < toEvaluate.length()) 979 { 980 position++; 981 if(toEvaluate[position] == QLatin1Char('$') || toEvaluate[position] == QLatin1Char('[') || toEvaluate[position] == QLatin1Char(']') || toEvaluate[position] == QLatin1Char('\\')) 982 result.append(toEvaluate[position]); 983 else 984 { 985 position--; 986 result.append(toEvaluate[position]); 987 } 988 } 989 else 990 result.append(toEvaluate[position]); 991 } 992 else 993 { 994 position++; 995 if( position < toEvaluate.length() ) 996 result.append(toEvaluate[position]); 997 } 998 } 999 else 1000 result.append(toEvaluate[position]); 1001 1002 position++; 1003 } 1004 1005 return result; 1006 } 1007 operator <<(QDataStream & s,const ActionInstance & actionInstance)1008 QDataStream &operator << (QDataStream &s, const ActionInstance &actionInstance) 1009 { 1010 s << actionInstance.label(); 1011 s << actionInstance.comment(); 1012 s << actionInstance.parametersData(); 1013 s << actionInstance.color(); 1014 s << actionInstance.isEnabled(); 1015 s << actionInstance.isSelected(); 1016 s << actionInstance.exceptionActionInstances(); 1017 s << actionInstance.pauseBefore(); 1018 s << actionInstance.pauseAfter(); 1019 s << actionInstance.timeout(); 1020 1021 return s; 1022 } 1023 operator >>(QDataStream & s,ActionInstance & actionInstance)1024 QDataStream &operator >> (QDataStream &s, ActionInstance &actionInstance) 1025 { 1026 QString label; 1027 QString comment; 1028 ParametersData parametersData; 1029 QColor color; 1030 bool enabled; 1031 bool selected; 1032 ExceptionActionInstancesHash exceptionActionInstances; 1033 int pauseBefore; 1034 int pauseAfter; 1035 int timeout; 1036 1037 s >> label; 1038 s >> comment; 1039 s >> parametersData; 1040 s >> color; 1041 s >> enabled; 1042 s >> selected; 1043 s >> exceptionActionInstances; 1044 s >> pauseBefore; 1045 s >> pauseAfter; 1046 s >> timeout; 1047 1048 actionInstance.setLabel(label); 1049 actionInstance.setComment(comment); 1050 actionInstance.setParametersData(parametersData); 1051 actionInstance.setColor(color); 1052 actionInstance.setEnabled(enabled); 1053 actionInstance.setSelected(selected); 1054 actionInstance.setExceptionActionInstances(exceptionActionInstances); 1055 actionInstance.setPauseBefore(pauseBefore); 1056 actionInstance.setPauseAfter(pauseAfter); 1057 actionInstance.setTimeout(timeout); 1058 1059 return s; 1060 } 1061 operator <<(QDebug & dbg,const ActionInstance & actionInstance)1062 QDebug &operator << (QDebug &dbg, const ActionInstance &actionInstance) 1063 { 1064 dbg.space() << "Id:" << actionInstance.definition()->id(); 1065 dbg.space() << "Label:" << actionInstance.label(); 1066 dbg.space() << "Comment:" << actionInstance.comment(); 1067 dbg.space() << "Color:" << actionInstance.color(); 1068 dbg.space() << "Enabled:" << actionInstance.isEnabled(); 1069 dbg.space() << "Selected:" << actionInstance.isSelected(); 1070 dbg.space() << "Exception action instances:" << actionInstance.exceptionActionInstances(); 1071 dbg.space() << "Data:" << actionInstance.parametersData(); 1072 dbg.space() << "Pause before:" << actionInstance.pauseBefore(); 1073 dbg.space() << "Pause after:" << actionInstance.pauseAfter(); 1074 dbg.space() << "Timeout:" << actionInstance.timeout(); 1075 1076 return dbg.maybeSpace(); 1077 } 1078 operator <<(QDebug & dbg,const ParametersData & parametersData)1079 QDebug &operator << (QDebug &dbg, const ParametersData ¶metersData) 1080 { 1081 for(const QString ¶meterName: parametersData.keys()) 1082 { 1083 dbg.space() << parameterName << "=" << parametersData.value(parameterName); 1084 } 1085 1086 return dbg.maybeSpace(); 1087 } 1088 operator <<(QDebug & dbg,const ExceptionActionInstancesHash & exceptionActionInstancesHash)1089 QDebug &operator << (QDebug &dbg, const ExceptionActionInstancesHash &exceptionActionInstancesHash) 1090 { 1091 for(ActionException::Exception exception: exceptionActionInstancesHash.keys()) 1092 { 1093 dbg.space() << exception << "=" << exceptionActionInstancesHash.value(exception); 1094 } 1095 1096 return dbg.maybeSpace(); 1097 } 1098 } 1099