History

Moving running scripts functionality to scripting library

Brad Davis committed 10 years ago

Commit d28f80a1 parent ddd9b0820 files · +664 −569Browse files

Showing the changes to libraries/script-engine/src/ScriptsModel.cpp (1 of 20 files). Show all

Unified · Split

libraries/script-engine/src/ScriptsModel.cpp+311−0
@@ -0,0 +1,311 @@
1//
2// Created by Ryan Huffman on 05/12/14.
3// Copyright 2014 High Fidelity, Inc.
4//
5// S3 request code written with ModelBrowser as a reference.
6//
7// Distributed under the Apache License, Version 2.0.
8// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9//
10
11#include "ScriptsModel.h"
12
13#include <QUrl>
14#include <QUrlQuery>
15#include <QXmlStreamReader>
16
17#include <NetworkAccessManager.h>
18
19#include "ScriptEngine.h"
20#include "ScriptEngines.h"
21#include "ScriptEngineLogging.h"
22#define __STR2__(x) #x
23#define __STR1__(x) __STR2__(x)
24#define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning Msg: "
25
26
27static const QString S3_URL = "http://s3.amazonaws.com/hifi-public";
28static const QString PUBLIC_URL = "http://public.highfidelity.io";
29static const QString MODELS_LOCATION = "scripts/";
30
31static const QString PREFIX_PARAMETER_NAME = "prefix";
32static const QString MARKER_PARAMETER_NAME = "marker";
33static const QString IS_TRUNCATED_NAME = "IsTruncated";
34static const QString CONTAINER_NAME = "Contents";
35static const QString KEY_NAME = "Key";
36
37TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNodeType type) :
38 _parent(parent),
39 _type(type),
40 _name(name) {
41};
42
43TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) :
44 TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT),
45 _localPath(localPath),
46 _fullPath(fullPath),
47 _origin(origin) {
48};
49
50TreeNodeFolder::TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent) :
51 TreeNodeBase(parent, foldername, TREE_NODE_TYPE_FOLDER) {
52};
53
54ScriptsModel::ScriptsModel(QObject* parent) :
55 QAbstractItemModel(parent),
56 _loadingScripts(false),
57 _localDirectory(),
58 _fsWatcher(),
59 _treeNodes()
60{
61 _localDirectory.setFilter(QDir::Files | QDir::Readable);
62 _localDirectory.setNameFilters(QStringList("*.js"));
63
64 auto scriptEngines = DependencyManager::get<ScriptEngines>();
65 connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &ScriptsModel::reloadLocalFiles);
66 reloadLocalFiles();
67 reloadRemoteFiles();
68}
69
70ScriptsModel::~ScriptsModel() {
71 for (int i = _treeNodes.size() - 1; i >= 0; i--) {
72 delete _treeNodes.at(i);
73 }
74 _treeNodes.clear();
75}
76
77TreeNodeBase* ScriptsModel::getTreeNodeFromIndex(const QModelIndex& index) const {
78 if (index.isValid()) {
79 return static_cast<TreeNodeBase*>(index.internalPointer());
80 }
81 return NULL;
82}
83
84QModelIndex ScriptsModel::index(int row, int column, const QModelIndex& parent) const {
85 if (row < 0 || column < 0) {
86 return QModelIndex();
87 }
88 return createIndex(row, column, getFolderNodes(static_cast<TreeNodeFolder*>(getTreeNodeFromIndex(parent))).at(row));
89}
90
91QModelIndex ScriptsModel::parent(const QModelIndex& child) const {
92 TreeNodeFolder* parent = (static_cast<TreeNodeBase*>(child.internalPointer()))->getParent();
93 if (!parent) {
94 return QModelIndex();
95 }
96 TreeNodeFolder* grandParent = parent->getParent();
97 int row = getFolderNodes(grandParent).indexOf(parent);
98 return createIndex(row, 0, parent);
99}
100
101QVariant ScriptsModel::data(const QModelIndex& index, int role) const {
102 TreeNodeBase* node = getTreeNodeFromIndex(index);
103 if (node->getType() == TREE_NODE_TYPE_SCRIPT) {
104 TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
105 if (role == Qt::DisplayRole) {
106 return QVariant(script->getName() + (script->getOrigin() == SCRIPT_ORIGIN_LOCAL ? " (local)" : ""));
107 } else if (role == ScriptPath) {
108 return QVariant(script->getFullPath());
109 }
110 } else if (node->getType() == TREE_NODE_TYPE_FOLDER) {
111 TreeNodeFolder* folder = static_cast<TreeNodeFolder*>(node);
112 if (role == Qt::DisplayRole) {
113 return QVariant(folder->getName());
114 }
115 }
116 return QVariant();
117}
118
119int ScriptsModel::rowCount(const QModelIndex& parent) const {
120 return getFolderNodes(static_cast<TreeNodeFolder*>(getTreeNodeFromIndex(parent))).count();
121}
122
123int ScriptsModel::columnCount(const QModelIndex& parent) const {
124 return 1;
125}
126
127void ScriptsModel::updateScriptsLocation(const QString& newPath) {
128 _fsWatcher.removePath(_localDirectory.absolutePath());
129
130 if (!newPath.isEmpty()) {
131 _localDirectory.setPath(newPath);
132
133 if (!_localDirectory.absolutePath().isEmpty()) {
134 _fsWatcher.addPath(_localDirectory.absolutePath());
135 }
136 }
137
138 reloadLocalFiles();
139}
140
141void ScriptsModel::reloadRemoteFiles() {
142 if (!_loadingScripts) {
143 _loadingScripts = true;
144 for (int i = _treeNodes.size() - 1; i >= 0; i--) {
145 TreeNodeBase* node = _treeNodes.at(i);
146 if (node->getType() == TREE_NODE_TYPE_SCRIPT &&
147 static_cast<TreeNodeScript*>(node)->getOrigin() == SCRIPT_ORIGIN_REMOTE)
148 {
149 delete node;
150 _treeNodes.removeAt(i);
151 }
152 }
153 requestRemoteFiles();
154 }
155}
156
157void ScriptsModel::requestRemoteFiles(QString marker) {
158 QUrl url(S3_URL);
159 QUrlQuery query;
160 query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION);
161 if (!marker.isEmpty()) {
162 query.addQueryItem(MARKER_PARAMETER_NAME, marker);
163 }
164 url.setQuery(query);
165
166 QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
167 QNetworkRequest request(url);
168 request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
169 QNetworkReply* reply = networkAccessManager.get(request);
170 connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
171}
172
173void ScriptsModel::downloadFinished() {
174 QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
175 bool finished = true;
176
177 if (reply->error() == QNetworkReply::NoError) {
178 QByteArray data = reply->readAll();
179
180 if (!data.isEmpty()) {
181 finished = parseXML(data);
182 } else {
183 qCDebug(scriptengine) << "Error: Received no data when loading remote scripts";
184 }
185 }
186
187 reply->deleteLater();
188 sender()->deleteLater();
189
190 if (finished) {
191 _loadingScripts = false;
192 }
193}
194
195bool ScriptsModel::parseXML(QByteArray xmlFile) {
196 beginResetModel();
197
198 QXmlStreamReader xml(xmlFile);
199 QRegExp jsRegex(".*\\.js");
200 bool truncated = false;
201 QString lastKey;
202
203 while (!xml.atEnd() && !xml.hasError()) {
204 if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == IS_TRUNCATED_NAME) {
205 while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == IS_TRUNCATED_NAME)) {
206 xml.readNext();
207 if (xml.text().toString() == "true") {
208 truncated = true;
209 }
210 }
211 }
212
213 if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) {
214 while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) {
215 if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) {
216 xml.readNext();
217 lastKey = xml.text().toString();
218 if (jsRegex.exactMatch(xml.text().toString())) {
219 _treeNodes.append(new TreeNodeScript(lastKey.mid(MODELS_LOCATION.length()), S3_URL + "/" + lastKey, SCRIPT_ORIGIN_REMOTE));
220 }
221 }
222 xml.readNext();
223 }
224 }
225 xml.readNext();
226 }
227 rebuildTree();
228 endResetModel();
229
230 // Error handling
231 if (xml.hasError()) {
232 qCDebug(scriptengine) << "Error loading remote scripts: " << xml.errorString();
233 return true;
234 }
235
236 if (truncated) {
237 requestRemoteFiles(lastKey);
238 }
239
240 // If this request was not truncated, we are done.
241 return !truncated;
242}
243
244void ScriptsModel::reloadLocalFiles() {
245 beginResetModel();
246
247 for (int i = _treeNodes.size() - 1; i >= 0; i--) {
248 TreeNodeBase* node = _treeNodes.at(i);
249 if (node->getType() == TREE_NODE_TYPE_SCRIPT &&
250 static_cast<TreeNodeScript*>(node)->getOrigin() == SCRIPT_ORIGIN_LOCAL)
251 {
252 delete node;
253 _treeNodes.removeAt(i);
254 }
255 }
256
257 _localDirectory.refresh();
258
259 const QFileInfoList localFiles = _localDirectory.entryInfoList();
260 for (int i = 0; i < localFiles.size(); i++) {
261 QFileInfo file = localFiles[i];
262 _treeNodes.append(new TreeNodeScript(file.fileName(), file.absoluteFilePath(), SCRIPT_ORIGIN_LOCAL));
263 }
264 rebuildTree();
265 endResetModel();
266}
267
268void ScriptsModel::rebuildTree() {
269 for (int i = _treeNodes.size() - 1; i >= 0; i--) {
270 if (_treeNodes.at(i)->getType() == TREE_NODE_TYPE_FOLDER) {
271 delete _treeNodes.at(i);
272 _treeNodes.removeAt(i);
273 }
274 }
275 QHash<QString, TreeNodeFolder*> folders;
276 for (int i = 0; i < _treeNodes.size(); i++) {
277 TreeNodeBase* node = _treeNodes.at(i);
278 if (node->getType() == TREE_NODE_TYPE_SCRIPT) {
279 TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
280 TreeNodeFolder* parent = NULL;
281 QString hash;
282 QStringList pathList = script->getLocalPath().split(tr("/"));
283 pathList.removeLast();
284 QStringList::const_iterator pathIterator;
285 for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) {
286 hash.append(*pathIterator + "/");
287 if (!folders.contains(hash)) {
288 folders[hash] = new TreeNodeFolder(*pathIterator, parent);
289 }
290 parent = folders[hash];
291 }
292 script->setParent(parent);
293 }
294 }
295 QHash<QString, TreeNodeFolder*>::const_iterator folderIterator;
296 for (folderIterator = folders.constBegin(); folderIterator != folders.constEnd(); ++folderIterator) {
297 _treeNodes.append(*folderIterator);
298 }
299 folders.clear();
300}
301
302QList<TreeNodeBase*> ScriptsModel::getFolderNodes(TreeNodeFolder* parent) const {
303 QList<TreeNodeBase*> result;
304 for (int i = 0; i < _treeNodes.size(); i++) {
305 TreeNodeBase* node = _treeNodes.at(i);
306 if (node->getParent() == parent) {
307 result.append(node);
308 }
309 }
310 return result;
311}