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 "ModifySequenceObjectTask.h"
23
24 #include <U2Core/AddDocumentTask.h>
25 #include <U2Core/AnnotationTableObject.h>
26 #include <U2Core/AppContext.h>
27 #include <U2Core/Counter.h>
28 #include <U2Core/DNAAlphabet.h>
29 #include <U2Core/DNASequenceObject.h>
30 #include <U2Core/DNATranslation.h>
31 #include <U2Core/DocumentModel.h>
32 #include <U2Core/GObject.h>
33 #include <U2Core/GObjectRelationRoles.h>
34 #include <U2Core/GObjectUtils.h>
35 #include <U2Core/IOAdapterUtils.h>
36 #include <U2Core/Log.h>
37 #include <U2Core/MultiTask.h>
38 #include <U2Core/ProjectModel.h>
39 #include <U2Core/SaveDocumentTask.h>
40 #include <U2Core/U2ObjectDbi.h>
41 #include <U2Core/U2SafePoints.h>
42 #include <U2Core/U2SequenceUtils.h>
43
44 namespace U2 {
45
46 typedef QPair<QString, QString> QStrStrPair;
47
ModifySequenceContentTask(const DocumentFormatId & dfId,U2SequenceObject * seqObj,const U2Region & regionTodelete,const DNASequence & seq2Insert,bool recalculateQualifiers,U1AnnotationUtils::AnnotationStrategyForResize str,const GUrl & url,bool mergeAnnotations)48 ModifySequenceContentTask::ModifySequenceContentTask(const DocumentFormatId &dfId, U2SequenceObject *seqObj, const U2Region ®ionTodelete, const DNASequence &seq2Insert, bool recalculateQualifiers, U1AnnotationUtils::AnnotationStrategyForResize str, const GUrl &url, bool mergeAnnotations)
49 : Task(tr("Modify sequence task"), TaskFlags(TaskFlag_NoRun) | TaskFlag_ReportingIsSupported), resultFormatId(dfId),
50 mergeAnnotations(mergeAnnotations), recalculateQualifiers(recalculateQualifiers), curDoc(seqObj->getDocument()), newDoc(nullptr), url(url), strat(str),
51 seqObj(seqObj), regionToReplace(regionTodelete), sequence2Insert(seq2Insert) {
52 GCOUNTER(cvar, "Modify sequence task");
53 inplaceMod = url == curDoc->getURL() || url.isEmpty();
54 }
55
report()56 Task::ReportResult ModifySequenceContentTask::report() {
57 CHECK(!(regionToReplace.isEmpty() && sequence2Insert.seq.isEmpty()), ReportResult_Finished);
58 CHECK_EXT(!curDoc->isStateLocked(), setError(tr("Document is locked")), ReportResult_Finished);
59
60 U2Region seqRegion(0, seqObj->getSequenceLength());
61 if (!seqRegion.contains(regionToReplace)) {
62 algoLog.error(tr("Region to delete is larger than the whole sequence"));
63 return ReportResult_Finished;
64 }
65
66 Project *project = AppContext::getProject();
67 if (project != nullptr) {
68 CHECK(!project->isStateLocked(), ReportResult_CallMeAgain);
69 docs = project->getDocuments();
70 }
71
72 if (!docs.contains(curDoc)) {
73 docs.append(curDoc);
74 }
75
76 if (!inplaceMod) {
77 cloneSequenceAndAnnotations();
78 }
79 seqObj->replaceRegion(regionToReplace, sequence2Insert, stateInfo);
80 CHECK_OP(stateInfo, ReportResult_Finished);
81
82 annotationForReport = FixAnnotationsUtils::fixAnnotations(&stateInfo, seqObj, regionToReplace, sequence2Insert, recalculateQualifiers, strat, docs);
83 if (!annotationForReport.isEmpty()) {
84 setReportingEnabled(true);
85 }
86
87 if (!inplaceMod) {
88 QList<Task *> tasks;
89 IOAdapterFactory *iof = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(IOAdapterUtils::url2io(url));
90 tasks.append(new SaveDocumentTask(seqObj->getDocument(), iof, url.getURLString()));
91 if (project != nullptr) {
92 tasks.append(new AddDocumentTask(newDoc));
93 }
94 AppContext::getTaskScheduler()->registerTopLevelTask(new MultiTask("Save document and add it to project (optional)", tasks));
95 }
96 return ReportResult_Finished;
97 }
98
99 namespace {
100
formatPairList(const QList<QStrStrPair> & pairList,bool useFirst)101 QString formatPairList(const QList<QStrStrPair> &pairList, bool useFirst) {
102 QString result;
103 const QString lineSeparator = "<br>";
104 foreach (const QStrStrPair &pair, pairList) {
105 result += useFirst ? pair.first : pair.second;
106 result += lineSeparator;
107 }
108 result.chop(lineSeparator.length());
109 return result;
110 }
111
112 } // namespace
113
generateReport() const114 QString ModifySequenceContentTask::generateReport() const {
115 CHECK(!annotationForReport.isEmpty(), QString());
116
117 QString report = tr("Some annotations have qualifiers referring a sequence region that has been removed during the sequence editing. "
118 "You might want to change the qualifiers manually. Find them in the table below");
119 report += "<br><table border=\"1\" cellpadding=\"1\">";
120 report += "<tr><th>";
121 report += tr("Annotation Name");
122 report += "</th><th>";
123 report += tr("Annotation Location");
124 report += "</th><th>";
125 report += tr("Qualifier Name");
126 report += "</th><th>";
127 report += tr("Referenced Region");
128 report += "</th></tr>";
129
130 foreach (Annotation *an, annotationForReport.keys()) {
131 if (annotationForReport[an].isEmpty()) {
132 coreLog.error(tr("Unexpected qualifiers count"));
133 assert(false);
134 continue;
135 }
136
137 report += QString("<tr><td>%1</td><td>%2</td>").arg(an->getName()).arg(U1AnnotationUtils::buildLocationString(*an->getLocation()));
138
139 report += QString("<td>%1</td>").arg(formatPairList(annotationForReport[an], true));
140 report += QString("<td>%1</td>").arg(formatPairList(annotationForReport[an], false));
141
142 report += "</tr>";
143 }
144 report += "</table>";
145 return report;
146 }
147
getSequenceLengthDelta() const148 qint64 ModifySequenceContentTask::getSequenceLengthDelta() const {
149 return sequence2Insert.length() + regionToReplace.length;
150 }
151
cloneSequenceAndAnnotations()152 void ModifySequenceContentTask::cloneSequenceAndAnnotations() {
153 IOAdapterRegistry *ioReg = AppContext::getIOAdapterRegistry();
154 IOAdapterFactory *iof = ioReg->getIOAdapterFactoryById(IOAdapterUtils::url2io(url));
155 CHECK(iof != nullptr, );
156 DocumentFormatRegistry *dfReg = AppContext::getDocumentFormatRegistry();
157 DocumentFormat *df = dfReg->getFormatById(resultFormatId);
158 SAFE_POINT(df != nullptr, "Invalid document format!", );
159
160 U2SequenceObject *oldSeqObj = seqObj;
161 newDoc = df->createNewLoadedDocument(iof, url, stateInfo, curDoc->getGHintsMap());
162 CHECK_OP(stateInfo, );
163
164 SAFE_POINT_EXT(df->isObjectOpSupported(newDoc, DocumentFormat::DocObjectOp_Add, GObjectTypes::SEQUENCE),
165 stateInfo.setError(tr("Failed to add sequence object to document!")), );
166
167 U2Sequence clonedSeq = U2SequenceUtils::copySequence(oldSeqObj->getSequenceRef(), newDoc->getDbiRef(), U2ObjectDbi::ROOT_FOLDER, stateInfo);
168 CHECK_OP(stateInfo, );
169
170 seqObj = new U2SequenceObject(oldSeqObj->getGObjectName(), U2EntityRef(newDoc->getDbiRef(), clonedSeq.id), oldSeqObj->getGHintsMap());
171 newDoc->addObject(seqObj);
172
173 if (df->isObjectOpSupported(newDoc, DocumentFormat::DocObjectOp_Add, GObjectTypes::ANNOTATION_TABLE)) {
174 if (mergeAnnotations) {
175 AnnotationTableObject *newDocAto = new AnnotationTableObject("Annotations", newDoc->getDbiRef());
176 newDocAto->addObjectRelation(seqObj, ObjectRole_Sequence);
177
178 for (Document *d : qAsConst(docs)) {
179 QList<GObject *> annotationTablesList = d->findGObjectByType(GObjectTypes::ANNOTATION_TABLE);
180 for (GObject *table : qAsConst(annotationTablesList)) {
181 auto ato = qobject_cast<AnnotationTableObject *>(table);
182 if (ato != nullptr && ato->hasObjectRelation(oldSeqObj, ObjectRole_Sequence)) {
183 foreach (Annotation *ann, ato->getAnnotations()) {
184 newDocAto->addAnnotations(QList<SharedAnnotationData>() << ann->getData(), ann->getGroup()->getName());
185 }
186 }
187 }
188 }
189 newDoc->addObject(newDocAto);
190 } else {
191 // use only sequence-doc annotations
192 foreach (GObject *o, curDoc->getObjects()) {
193 if (auto aObj = qobject_cast<AnnotationTableObject *>(o)) {
194 GObject *cl = aObj->clone(newDoc->getDbiRef(), stateInfo);
195 CHECK_OP(stateInfo, );
196 newDoc->addObject(cl);
197 GObjectUtils::updateRelationsURL(cl, curDoc->getURL(), newDoc->getURL());
198 }
199 }
200 }
201 }
202 docs.append(newDoc);
203 }
204
205 } // namespace U2
206