1 /*
2 This file is part of the Okteta Kasten Framework, made within the KDE community.
3
4 SPDX-FileCopyrightText: 2010, 2011, 2012 Alex Richardson <alex.richardson@gmx.de>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9 #include "scripthandler.hpp"
10 #include "scriptengineinitializer.hpp"
11 #include "scriptutils.hpp"
12 #include "scriptlogger.hpp"
13 #include "classes/defaultscriptclass.hpp"
14 #include "../datatypes/datainformation.hpp"
15 #include "../datatypes/topleveldatainformation.hpp"
16 #include "../datatypes/array/arraydatainformation.hpp"
17 #include "../parsers/parserutils.hpp"
18
19 #include <QStringList>
20 #include <QScriptValue>
21 #include <QScriptValueIterator>
22 #include <QScriptEngine>
23
ScriptHandler(QScriptEngine * engine,TopLevelDataInformation * topLevel)24 ScriptHandler::ScriptHandler(QScriptEngine* engine, TopLevelDataInformation* topLevel)
25 : mEngine(engine)
26 , mTopLevel(topLevel)
27 , mHandlerInfo(engine, topLevel->logger())
28 {
29 }
30
31 ScriptHandler::~ScriptHandler() = default;
32
engine() const33 QScriptEngine* ScriptHandler::engine() const
34 {
35 return mEngine.data();
36 }
37
handlerInfo()38 ScriptHandlerInfo* ScriptHandler::handlerInfo()
39 {
40 return &mHandlerInfo;
41 }
42
validateData(DataInformation * data)43 void ScriptHandler::validateData(DataInformation* data)
44 {
45 Q_CHECK_PTR(data);
46
47 if (data->hasBeenValidated()) {
48 return;
49 }
50 // first validate the children
51 for (uint i = 0; i < data->childCount(); ++i) {
52 validateData(data->childAt(i));
53 }
54
55 // check if has a validation function:
56 QScriptValue validationFunc = data->validationFunc();
57 if (validationFunc.isValid()) {
58 QScriptValue result = callFunction(validationFunc, data, ScriptHandlerInfo::Mode::Validating);
59 if (result.isError()) {
60 mTopLevel->logger()->error(data) << "Error occurred while validating element: "
61 << result.toString();
62 data->setValidationError(QStringLiteral("Error occurred in validation: ")
63 + result.toString());
64 } else if (mEngine->hasUncaughtException()) {
65 mTopLevel->logger()->error(data) << "Error occurred while validating element:"
66 << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
67 data->setValidationError(QStringLiteral("Error occurred in validation: ")
68 + result.toString());
69 mEngine->clearExceptions();
70 }
71 if (result.isBool() || result.isBoolean()) {
72 data->mValidationSuccessful = result.toBool();
73 }
74 if (result.isString()) {
75 // error string
76 QString str = result.toString();
77 if (!str.isEmpty()) {
78 data->setValidationError(str);
79 }
80 }
81 data->mHasBeenValidated = true;
82 }
83 }
84
updateDataInformation(DataInformation * data)85 void ScriptHandler::updateDataInformation(DataInformation* data)
86 {
87 Q_CHECK_PTR(data);
88 // check if has an update function:
89 Q_ASSERT(!data->hasBeenUpdated());
90 QScriptValue updateFunc = data->updateFunc();
91 data->mHasBeenUpdated = true;
92 if (updateFunc.isValid()) {
93 QString context = data->fullObjectPath(); // we mustn't use data after updateFunc.call(), save context
94 QScriptValue result = callFunction(updateFunc, data, ScriptHandlerInfo::Mode::Updating);
95 if (result.isError()) {
96 mTopLevel->logger()->error(context) << "Error occurred while updating element: "
97 << result.toString();
98 }
99 if (mEngine->hasUncaughtException()) {
100 mTopLevel->logger()->error(context) << "Error occurred while updating element:"
101 << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
102 mEngine->clearExceptions();
103 }
104 }
105 }
106
updateLength(ArrayDataInformation * array)107 void ScriptHandler::updateLength(ArrayDataInformation* array)
108 {
109 QScriptValue lengthFunc = array->lengthFunction();
110 if (lengthFunc.isValid()) {
111 Q_ASSERT(lengthFunc.isFunction());
112
113 QScriptValue result = callFunction(lengthFunc, array, ScriptHandlerInfo::Mode::DeterminingLength);
114 if (mEngine->hasUncaughtException()) {
115 mTopLevel->logger()->error(array) << "Error occurred while calculating length:"
116 << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
117 mEngine->clearExceptions();
118 }
119 ParsedNumber<uint> value = ParserUtils::uintFromScriptValue(result);
120 if (value.isValid) {
121 array->setArrayLength(value.value);
122 } else {
123 array->logError() << "Length function did not return a valid number! Result was: " << result.toString();
124 }
125 }
126 }
127
customToString(const DataInformation * data,const QScriptValue & func)128 QString ScriptHandler::customToString(const DataInformation* data, const QScriptValue& func)
129 {
130 Q_ASSERT(func.isValid());
131 Q_ASSERT(func.isFunction());
132 Q_ASSERT(data->wasAbleToRead()); // this should never be called if EOF was reached
133 // it is effectively const, since nothing may be modified while mode is CustomToString
134 // const_cast is okay in this case
135 QScriptValue result = callFunction(func, const_cast<DataInformation*>(data), ScriptHandlerInfo::Mode::CustomToString);
136 if (result.isError()) {
137 data->logError() << "toStringFunc caused an error:" << result.toString();
138 }
139 return result.toString();
140 }
141
callFunction(QScriptValue func,DataInformation * data,ScriptHandlerInfo::Mode mode)142 QScriptValue ScriptHandler::callFunction(QScriptValue func, DataInformation* data,
143 ScriptHandlerInfo::Mode mode)
144 {
145 Q_ASSERT(func.isFunction());
146 // value exists, we assume it has been checked to be a function
147 QScriptValue thisObject = data->toScriptValue(mEngine.data(), &mHandlerInfo);
148 QScriptValue mainStruct = data->mainStructure()->toScriptValue(mEngine.data(), &mHandlerInfo);
149 const QScriptValueList args { mainStruct };
150 // ensure we get the right properties
151 mHandlerInfo.setMode(mode);
152 QScriptValue result = func.call(thisObject, args);
153 mHandlerInfo.setMode(ScriptHandlerInfo::Mode::None);
154 return result;
155 }
156