1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "OpenViewTask.h"
23
24 #include <QApplication>
25 #include <QFileInfo>
26
27 #include <U2Core/AppContext.h>
28 #include <U2Core/AppResources.h>
29 #include <U2Core/BaseDocumentFormats.h>
30 #include <U2Core/DocumentModel.h>
31 #include <U2Core/GHints.h>
32 #include <U2Core/GObject.h>
33 #include <U2Core/GObjectReference.h>
34 #include <U2Core/GObjectRelationRoles.h>
35 #include <U2Core/GObjectSelection.h>
36 #include <U2Core/GObjectTypes.h>
37 #include <U2Core/GObjectUtils.h>
38 #include <U2Core/IOAdapter.h>
39 #include <U2Core/L10n.h>
40 #include <U2Core/LoadDocumentTask.h>
41 #include <U2Core/LoadRemoteDocumentTask.h>
42 #include <U2Core/Log.h>
43 #include <U2Core/ProjectModel.h>
44 #include <U2Core/ResourceTracker.h>
45 #include <U2Core/U2SafePoints.h>
46
47 #include <U2Gui/ObjectViewModel.h>
48
49 namespace U2 {
50
51 /* TRANSLATOR U2::LoadUnloadedDocumentTask */
52
53 const int OpenViewTask::MAX_DOC_NUMBER_TO_OPEN_VIEWS = 5;
54
55 //////////////////////////////////////////////////////////////////////////
56 // LoadUnloadedDocumentAndOpenViewTask
57
LoadUnloadedDocumentAndOpenViewTask(Document * d)58 LoadUnloadedDocumentAndOpenViewTask::LoadUnloadedDocumentAndOpenViewTask(Document *d)
59 : Task("", TaskFlags_NR_FOSCOE | TaskFlag_MinimizeSubtaskErrorText | TaskFlag_CollectChildrenWarnings) {
60 loadUnloadedTask = new LoadUnloadedDocumentTask(d);
61 setUseDescriptionFromSubtask(true);
62
63 setVerboseLogMode(true);
64 setTaskName(tr("Load document: '%1'").arg(d->getName()));
65
66 addSubTask(loadUnloadedTask);
67 }
68
createOpenViewTask(const MultiGSelection & ms)69 static Task *createOpenViewTask(const MultiGSelection &ms) {
70 QList<GObjectViewFactory *> fs = AppContext::getObjectViewFactoryRegistry()->getAllFactories();
71 QList<GObjectViewFactory *> ls;
72
73 foreach (GObjectViewFactory *f, fs) {
74 // check if new view can be created
75 if (f->canCreateView(ms)) {
76 ls.append(f);
77 }
78 }
79
80 if (ls.size() > 1) {
81 GObjectViewFactory *f = AppContext::getObjectViewFactoryRegistry()->getFactoryById(GObjectViewFactory::SIMPLE_TEXT_FACTORY);
82 if (ls.contains(f)) {
83 // ignore auxiliary text data
84 ls.removeAll(f);
85 }
86 }
87
88 if (ls.size() == 1) {
89 GObjectViewFactory *f = ls.first();
90 Task *t = f->createViewTask(ms, true);
91 return t;
92 }
93 return nullptr;
94 }
95
getDocument()96 Document *LoadUnloadedDocumentAndOpenViewTask::getDocument() {
97 return loadUnloadedTask->getDocument();
98 }
99
onSubTaskFinished(Task * subTask)100 QList<Task *> LoadUnloadedDocumentAndOpenViewTask::onSubTaskFinished(Task *subTask) {
101 QList<Task *> res;
102 if (subTask != loadUnloadedTask || hasError() || isCanceled()) {
103 return res;
104 }
105
106 // look if saved state can be loaded
107 Document *doc = loadUnloadedTask->getDocument();
108 assert(doc->isLoaded());
109
110 res.append(new OpenViewTask(doc));
111 return res;
112 }
113
114 //////////////////////////////////////////////////////////////////////////
115 // OpenViewTask
116
OpenViewTask(Document * d)117 OpenViewTask::OpenViewTask(Document *d)
118 : Task("Open view", TaskFlags_NR_FOSCOE | TaskFlag_MinimizeSubtaskErrorText), doc(d) {
119 assert(doc != nullptr);
120 assert(doc->isLoaded());
121 }
122
prepare()123 void OpenViewTask::prepare() {
124 QList<Task *> res;
125
126 // if any of existing views has added an object from the document -> do not open new view
127 const QList<GObject *> &docObjects = doc->getObjects();
128 if (!GObjectViewUtils::findViewsWithAnyOfObjects(docObjects).isEmpty()) {
129 return;
130 }
131
132 // try open new view
133 GObjectSelection os;
134 os.addToSelection(docObjects);
135 MultiGSelection ms;
136 ms.addSelection(&os);
137
138 QList<GObjectViewState *> sl = GObjectViewUtils::selectStates(ms, AppContext::getProject()->getGObjectViewStates());
139 if (sl.size() == 1) {
140 GObjectViewState *state = sl.first();
141 SAFE_POINT_EXT(state, setError(tr("State is NULL")), );
142 GObjectViewFactory *f = AppContext::getObjectViewFactoryRegistry()->getFactoryById(state->getViewFactoryId());
143 SAFE_POINT_EXT(f, setError(tr("GObject factory is NULL")), );
144 res.append(f->createViewTask(state->getViewName(), state->getStateData()));
145 } else {
146 Task *openViewTask = createOpenViewTask(ms);
147 if (openViewTask != nullptr) {
148 openViewTask->setSubtaskProgressWeight(0);
149 res.append(openViewTask);
150 }
151 }
152
153 if (res.isEmpty()) {
154 // no view can be opened -> check special case: loaded object contains annotations associated with sequence
155 // -> load sequence and open view for it;
156 foreach (GObject *obj, doc->findGObjectByType(GObjectTypes::ANNOTATION_TABLE)) {
157 QList<GObjectRelation> rels = obj->findRelatedObjectsByRole(ObjectRole_Sequence);
158 if (rels.isEmpty()) {
159 continue;
160 }
161 const GObjectRelation &rel = rels.first();
162 Document *seqDoc = AppContext::getProject()->findDocumentByURL(rel.ref.docUrl);
163 if (seqDoc != nullptr) {
164 if (seqDoc->isLoaded()) { // try open sequence view
165 GObject *seqObj = seqDoc->findGObjectByName(rel.ref.objName);
166 if (seqObj != nullptr && seqObj->getGObjectType() == GObjectTypes::SEQUENCE) {
167 GObjectSelection os2;
168 os2.addToSelection(seqObj);
169 MultiGSelection ms2;
170 ms2.addSelection(&os2);
171 Task *openViewTask = createOpenViewTask(ms2);
172 if (openViewTask != nullptr) {
173 openViewTask->setSubtaskProgressWeight(0);
174 res.append(openViewTask);
175 }
176 }
177 } else { // try load doc and open sequence view
178 AppContext::getTaskScheduler()->registerTopLevelTask(new LoadUnloadedDocumentAndOpenViewTask(seqDoc));
179 }
180 }
181 if (!res.isEmpty()) { // one view is ok
182 break;
183 }
184 }
185
186 if (res.isEmpty()) {
187 // no view can be opened -> check another special cases: loaded object contains
188 // 1. assemblies with their references
189 // 2. multiple chromatogram alignment with a reference
190 // -> load assemblies/mca and their references and open view for the first object;
191 QList<GObject *> objectsToOpen;
192
193 objectsToOpen << doc->findGObjectByType(GObjectTypes::ASSEMBLY);
194 if (objectsToOpen.isEmpty()) {
195 objectsToOpen << doc->findGObjectByType(GObjectTypes::MULTIPLE_CHROMATOGRAM_ALIGNMENT);
196 }
197
198 if (!objectsToOpen.isEmpty()) {
199 GObjectSelection os2;
200 os2.addToSelection(objectsToOpen.first());
201 MultiGSelection ms2;
202 ms2.addSelection(&os2);
203
204 Task *openViewTask = createOpenViewTask(ms2);
205 if (openViewTask != nullptr) {
206 openViewTask->setSubtaskProgressWeight(0);
207 res.append(openViewTask);
208 }
209 }
210 }
211 }
212
213 foreach (Task *task, res) {
214 addSubTask(task);
215 }
216 }
217
218 //////////////////////////////////////////////////////////////////////////
219
LoadRemoteDocumentAndAddToProjectTask(const QString & accId,const QString & dbName)220 LoadRemoteDocumentAndAddToProjectTask::LoadRemoteDocumentAndAddToProjectTask(const QString &accId, const QString &dbName)
221 : Task(tr("Load remote document and add to project"), TaskFlags_NR_FOSCOE | TaskFlag_MinimizeSubtaskErrorText),
222 mode(LoadRemoteDocumentMode_OpenView), loadRemoteDocTask(nullptr) {
223 accNumber = accId;
224 databaseName = dbName;
225 }
226
LoadRemoteDocumentAndAddToProjectTask(const GUrl & url)227 LoadRemoteDocumentAndAddToProjectTask::LoadRemoteDocumentAndAddToProjectTask(const GUrl &url)
228 : Task(tr("Load remote document and add to project"), TaskFlags_NR_FOSCOE | TaskFlag_MinimizeSubtaskErrorText),
229 mode(LoadRemoteDocumentMode_OpenView), loadRemoteDocTask(nullptr) {
230 docUrl = url;
231 }
232
LoadRemoteDocumentAndAddToProjectTask(const QString & accId,const QString & dbName,const QString & fp,const QString & format,const QVariantMap & hints,LoadRemoteDocumentMode mode)233 LoadRemoteDocumentAndAddToProjectTask::LoadRemoteDocumentAndAddToProjectTask(const QString &accId, const QString &dbName, const QString &fp, const QString &format, const QVariantMap &hints, LoadRemoteDocumentMode mode)
234 : Task(tr("Load remote document and add to project"), TaskFlags_NR_FOSCOE | TaskFlag_MinimizeSubtaskErrorText),
235 accNumber(accId), databaseName(dbName), fileFormat(format), fullpath(fp), hints(hints), mode(mode), loadRemoteDocTask(nullptr) {
236 if (mode == LoadRemoteDocumentMode_LoadOnly) {
237 setReportingSupported(true);
238 setReportingEnabled(true);
239 setTaskName(tr("Load remote document"));
240 }
241 }
242
prepare()243 void LoadRemoteDocumentAndAddToProjectTask::prepare() {
244 if (docUrl.isEmpty()) {
245 loadRemoteDocTask = new LoadRemoteDocumentTask(accNumber, databaseName, fullpath, fileFormat, hints);
246 } else {
247 loadRemoteDocTask = new LoadRemoteDocumentTask(docUrl);
248 }
249 addSubTask(loadRemoteDocTask);
250 }
251
252 namespace {
createLoadedDocTask(Document * loadedDoc,bool openView)253 Task *createLoadedDocTask(Document *loadedDoc, bool openView) {
254 if (loadedDoc->isLoaded() && openView) {
255 return new OpenViewTask(loadedDoc);
256 }
257 if (!loadedDoc->isLoaded() && openView) {
258 return new LoadUnloadedDocumentAndOpenViewTask(loadedDoc);
259 }
260 if (!loadedDoc->isLoaded() && !openView) {
261 return new LoadUnloadedDocumentTask(loadedDoc);
262 }
263 return nullptr;
264 }
265 } // namespace
266
onSubTaskFinished(Task * subTask)267 QList<Task *> LoadRemoteDocumentAndAddToProjectTask::onSubTaskFinished(Task *subTask) {
268 QList<Task *> subTasks;
269 if (subTask->hasError()) {
270 return subTasks;
271 }
272
273 if (subTask->isCanceled()) {
274 return subTasks;
275 }
276
277 if (subTask == loadRemoteDocTask) {
278 if (mode == LoadRemoteDocumentMode_LoadOnly) {
279 return subTasks;
280 }
281 // hack for handling errors with http requests with bad resource id
282 Document *d = loadRemoteDocTask->getDocument();
283 if (d->getDocumentFormatId() == BaseDocumentFormats::PLAIN_TEXT) {
284 setError(tr("Cannot find %1 in %2 database").arg(accNumber).arg(databaseName));
285 // try to delete file with response that was created
286 QFile::remove(d->getURLString());
287 // and remove it from downloaded cache
288 RecentlyDownloadedCache *cache = AppContext::getRecentlyDownloadedCache();
289 if (cache != nullptr) {
290 cache->remove(d->getURLString());
291 }
292 return subTasks;
293 }
294
295 QString fullPath = loadRemoteDocTask->getLocalUrl();
296 Project *proj = AppContext::getProject();
297 if (proj == nullptr) {
298 QVariantMap hints;
299 hints[ProjectLoaderHint_LoadWithoutView] = mode != LoadRemoteDocumentMode_OpenView;
300 Task *openWithProjectTask = AppContext::getProjectLoader()->openWithProjectTask(fullPath, hints);
301 if (openWithProjectTask != nullptr) {
302 subTasks.append(openWithProjectTask);
303 }
304 } else {
305 Document *doc = loadRemoteDocTask->getDocument();
306 SAFE_POINT(doc != nullptr, "loadRemoteDocTask->takeDocument() returns NULL!", subTasks);
307 QString url = doc->getURLString();
308 Document *loadedDoc = proj->findDocumentByURL(url);
309 if (loadedDoc != nullptr) {
310 Task *task = createLoadedDocTask(loadedDoc, mode == LoadRemoteDocumentMode_OpenView);
311 if (nullptr != task) {
312 subTasks.append(task);
313 }
314 } else {
315 // Add document to project
316 doc = loadRemoteDocTask->takeDocument();
317 SAFE_POINT(doc != nullptr, "loadRemoteDocTask->takeDocument() returns NULL!", subTasks);
318 subTasks.append(new AddDocumentTask(doc));
319 if (mode == LoadRemoteDocumentMode_OpenView) {
320 subTasks.append(new LoadUnloadedDocumentAndOpenViewTask(doc));
321 } else {
322 subTasks.append(new LoadUnloadedDocumentTask(doc));
323 }
324 }
325 }
326 }
327
328 return subTasks;
329 }
330
generateReport() const331 QString LoadRemoteDocumentAndAddToProjectTask::generateReport() const {
332 // Note: reporting is enabled only for db + accession mode.
333 if (hasError()) {
334 return tr("Failed to download %1 from %2. Error: %3").arg(accNumber).arg(databaseName).arg(getError());
335 }
336 if (isCanceled()) {
337 return QString();
338 }
339 QString url = loadRemoteDocTask->getLocalUrl();
340 return tr("Document was successfully downloaded: [%1, %2] -> <a href='%3'>%4</a>")
341 .arg(databaseName)
342 .arg(accNumber)
343 .arg(url)
344 .arg(url);
345 }
346
AddDocumentAndOpenViewTask(Document * doc,const AddDocumentTaskConfig & conf)347 AddDocumentAndOpenViewTask::AddDocumentAndOpenViewTask(Document *doc, const AddDocumentTaskConfig &conf)
348 : Task(tr("Opening view for document: 'NONAME'"), TaskFlags_NR_FOSE_COSC | TaskFlag_CollectChildrenWarnings) {
349 if (doc != nullptr) {
350 GUrl url = doc->getURL();
351 setTaskName(tr("Opening view for document: %1").arg(url.fileName()));
352 } else {
353 setError(tr("Provided document is NULL"));
354 return;
355 }
356 setMaxParallelSubtasks(1);
357 addSubTask(new AddDocumentTask(doc, conf));
358 }
359
AddDocumentAndOpenViewTask(DocumentProviderTask * dp,const AddDocumentTaskConfig & conf)360 AddDocumentAndOpenViewTask::AddDocumentAndOpenViewTask(DocumentProviderTask *dp, const AddDocumentTaskConfig &conf)
361 : Task(tr("Opening view for document: 'NONAME'"), TaskFlags_NR_FOSE_COSC | TaskFlag_CollectChildrenWarnings) {
362 if (dp != nullptr) {
363 setTaskName(tr("Opening view for document: %1").arg(dp->getDocumentDescription()));
364 } else {
365 setError(tr("Document provider is NULL"));
366 return;
367 }
368 setMaxParallelSubtasks(1);
369 addSubTask(new AddDocumentTask(dp, conf));
370 }
371
onSubTaskFinished(Task * t)372 QList<Task *> AddDocumentAndOpenViewTask::onSubTaskFinished(Task *t) {
373 QList<Task *> res;
374 AddDocumentTask *addTask = qobject_cast<AddDocumentTask *>(t);
375 if (addTask != nullptr && !addTask->getStateInfo().isCoR()) {
376 Document *doc = addTask->getDocument();
377 assert(doc != nullptr);
378 res << new LoadUnloadedDocumentAndOpenViewTask(doc);
379 }
380 return res;
381 }
382
383 } // namespace U2
384