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+
27+static const QString S3_URL = "http://s3.amazonaws.com/hifi-public";
28+static const QString PUBLIC_URL = "http://public.highfidelity.io";
29+static const QString MODELS_LOCATION = "scripts/";
30+
31+static const QString PREFIX_PARAMETER_NAME = "prefix";
32+static const QString MARKER_PARAMETER_NAME = "marker";
33+static const QString IS_TRUNCATED_NAME = "IsTruncated";
34+static const QString CONTAINER_NAME = "Contents";
35+static const QString KEY_NAME = "Key";
36+
37+TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNodeType type) :
38+ _parent(parent),
39+ _type(type),
40+ _name(name) {
41+};
42+
43+TreeNodeScript::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+
50+TreeNodeFolder::TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent) :
51+ TreeNodeBase(parent, foldername, TREE_NODE_TYPE_FOLDER) {
52+};
53+
54+ScriptsModel::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+
70+ScriptsModel::~ScriptsModel() {
71+ for (int i = _treeNodes.size() - 1; i >= 0; i--) {
72+ delete _treeNodes.at(i);
73+ }
74+ _treeNodes.clear();
75+}
76+
77+TreeNodeBase* ScriptsModel::getTreeNodeFromIndex(const QModelIndex& index) const {
78+ if (index.isValid()) {
79+ return static_cast<TreeNodeBase*>(index.internalPointer());
80+ }
81+ return NULL;
82+}
83+
84+QModelIndex 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+
91+QModelIndex 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+
101+QVariant 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+
119+int ScriptsModel::rowCount(const QModelIndex& parent) const {
120+ return getFolderNodes(static_cast<TreeNodeFolder*>(getTreeNodeFromIndex(parent))).count();
121+}
122+
123+int ScriptsModel::columnCount(const QModelIndex& parent) const {
124+ return 1;
125+}
126+
127+void 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+
141+void 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+
157+void 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+
173+void 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+
195+bool 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+
244+void 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+
268+void 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+
302+QList<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+}