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 "MultipleAlignmentObject.h"
23
24 #include <U2Core/DbiConnection.h>
25 #include <U2Core/MSAUtils.h>
26 #include <U2Core/McaDbiUtils.h>
27 #include <U2Core/MsaDbiUtils.h>
28 #include <U2Core/U2AlphabetUtils.h>
29 #include <U2Core/U2ObjectDbi.h>
30 #include <U2Core/U2OpStatusUtils.h>
31 #include <U2Core/U2SafePoints.h>
32
33 namespace U2 {
34
MaSavedState()35 MaSavedState::MaSavedState()
36 : lastState(nullptr) {
37 }
38
~MaSavedState()39 MaSavedState::~MaSavedState() {
40 delete lastState;
41 }
42
hasState() const43 bool MaSavedState::hasState() const {
44 return nullptr != lastState;
45 }
46
takeState()47 const MultipleAlignment MaSavedState::takeState() {
48 const MultipleAlignment state = *lastState;
49 delete lastState;
50 lastState = nullptr;
51 return state;
52 }
53
setState(const MultipleAlignment & ma)54 void MaSavedState::setState(const MultipleAlignment &ma) {
55 if (nullptr != lastState) {
56 delete lastState;
57 }
58 lastState = new MultipleAlignment(ma->getCopy());
59 }
60
MultipleAlignmentObject(const QString & gobjectType,const QString & name,const U2EntityRef & maRef,const QVariantMap & hintsMap,const MultipleAlignment & alignment)61 MultipleAlignmentObject::MultipleAlignmentObject(const QString &gobjectType,
62 const QString &name,
63 const U2EntityRef &maRef,
64 const QVariantMap &hintsMap,
65 const MultipleAlignment &alignment)
66 : GObject(gobjectType, name, hintsMap),
67 cachedMa(alignment->getCopy()) {
68 entityRef = maRef;
69 dataLoaded = false;
70
71 if (!cachedMa->isEmpty()) {
72 dataLoaded = true;
73 }
74 }
75
~MultipleAlignmentObject()76 MultipleAlignmentObject::~MultipleAlignmentObject() {
77 emit si_invalidateAlignmentObject();
78 }
79
setTrackMod(U2OpStatus & os,U2TrackModType trackMod)80 void MultipleAlignmentObject::setTrackMod(U2OpStatus &os, U2TrackModType trackMod) {
81 // Prepare the connection
82 DbiConnection con(entityRef.dbiRef, os);
83 CHECK_OP(os, );
84
85 U2ObjectDbi *objectDbi = con.dbi->getObjectDbi();
86 SAFE_POINT(nullptr != objectDbi, "NULL Object Dbi", );
87
88 // Set the new status
89 objectDbi->setTrackModType(entityRef.entityId, trackMod, os);
90 }
91
replaceCharacter(int startPos,int rowIndex,char newChar)92 void MultipleAlignmentObject::replaceCharacter(int startPos, int rowIndex, char newChar) {
93 replaceCharacters({startPos, 1}, rowIndex, newChar);
94 }
95
replaceCharacters(const U2Region & columnRange,int rowIndex,char newChar)96 void MultipleAlignmentObject::replaceCharacters(const U2Region &columnRange, int rowIndex, char newChar) {
97 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
98
99 const MultipleAlignment &ma = getMultipleAlignment();
100 SAFE_POINT(U2Region(0, ma->getLength()).contains(columnRange), "Invalid parameters", );
101
102 const MultipleAlignmentRow &row = ma->getRow(rowIndex);
103 qint64 rowId = row->getRowId();
104 U2OpStatus2Log os;
105
106 bool isMca = getGObjectType() == GObjectTypes::MULTIPLE_CHROMATOGRAM_ALIGNMENT;
107 if (newChar != U2Msa::GAP_CHAR) {
108 if (isMca) {
109 McaDbiUtils::replaceCharactersInRow(entityRef, rowId, columnRange, newChar, os);
110 } else {
111 MsaDbiUtils::replaceCharactersInRow(entityRef, rowId, columnRange, newChar, os);
112 }
113 CHECK_OP(os, )
114 } else {
115 if (isMca) {
116 McaDbiUtils::removeCharacters(entityRef, {rowId}, columnRange.startPos, columnRange.length, os);
117 } else {
118 MsaDbiUtils::removeRegion(entityRef, {rowId}, columnRange.startPos, columnRange.length, os);
119 }
120 CHECK_OP(os, )
121 MsaDbiUtils::insertGaps(entityRef, {rowId}, columnRange.startPos, columnRange.length, os, true);
122 CHECK_OP(os, )
123 }
124
125 MaModificationInfo mi;
126 mi.rowContentChanged = true;
127 mi.rowListChanged = false;
128 mi.alignmentLengthChanged = false;
129 mi.modifiedRowIds << rowId;
130
131 if (newChar != ' ' && !ma->getAlphabet()->contains(newChar)) {
132 const DNAAlphabet *alp = U2AlphabetUtils::findBestAlphabet(QByteArray(1, newChar));
133 const DNAAlphabet *newAlphabet = U2AlphabetUtils::deriveCommonAlphabet(alp, ma->getAlphabet());
134 SAFE_POINT(newAlphabet != nullptr, "Common alphabet is NULL", );
135
136 if (newAlphabet->getId() != ma->getAlphabet()->getId()) {
137 MaDbiUtils::updateMaAlphabet(entityRef, newAlphabet->getId(), os);
138 mi.alphabetChanged = true;
139 SAFE_POINT_OP(os, );
140 }
141 }
142 updateCachedMultipleAlignment(mi);
143 }
144
getMultipleAlignment() const145 const MultipleAlignment &MultipleAlignmentObject::getMultipleAlignment() const {
146 ensureDataLoaded();
147 return cachedMa;
148 }
149
setMultipleAlignment(const MultipleAlignment & newMa,MaModificationInfo mi,const QVariantMap & hints)150 void MultipleAlignmentObject::setMultipleAlignment(const MultipleAlignment &newMa, MaModificationInfo mi, const QVariantMap &hints) {
151 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
152
153 U2OpStatus2Log os;
154 updateDatabase(os, newMa);
155 SAFE_POINT_OP(os, );
156
157 mi.hints = hints;
158 updateCachedMultipleAlignment(mi);
159 }
160
getMultipleAlignmentCopy() const161 const MultipleAlignment MultipleAlignmentObject::getMultipleAlignmentCopy() const {
162 return getMultipleAlignment()->getCopy();
163 }
164
setGObjectName(const QString & newName)165 void MultipleAlignmentObject::setGObjectName(const QString &newName) {
166 ensureDataLoaded();
167 CHECK(cachedMa->getName() != newName, );
168
169 if (!isStateLocked()) {
170 U2OpStatus2Log os;
171 MaDbiUtils::renameMa(entityRef, newName, os);
172 CHECK_OP(os, );
173
174 updateCachedMultipleAlignment();
175 } else {
176 GObject::setGObjectName(newName);
177 cachedMa->setName(newName);
178 }
179 }
180
getAlphabet() const181 const DNAAlphabet *MultipleAlignmentObject::getAlphabet() const {
182 return getMultipleAlignment()->getAlphabet();
183 }
184
getLength() const185 qint64 MultipleAlignmentObject::getLength() const {
186 return getMultipleAlignment()->getLength();
187 }
188
getNumRows() const189 qint64 MultipleAlignmentObject::getNumRows() const {
190 return getMultipleAlignment()->getNumRows();
191 }
192
getRows() const193 const QList<MultipleAlignmentRow> &MultipleAlignmentObject::getRows() const {
194 return getMultipleAlignment()->getRows();
195 }
196
getRow(int row) const197 const MultipleAlignmentRow MultipleAlignmentObject::getRow(int row) const {
198 return getMultipleAlignment()->getRow(row);
199 }
200
getRowPosById(qint64 rowId) const201 int MultipleAlignmentObject::getRowPosById(qint64 rowId) const {
202 return getMultipleAlignment()->getRowsIds().indexOf(rowId);
203 }
204
getGapModel() const205 U2MsaListGapModel MultipleAlignmentObject::getGapModel() const {
206 return getMultipleAlignment()->getGapModel();
207 }
208
removeRow(int rowIdx)209 void MultipleAlignmentObject::removeRow(int rowIdx) {
210 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
211
212 const MultipleAlignment &ma = getMultipleAlignment();
213 SAFE_POINT(rowIdx >= 0 && rowIdx < ma->getNumRows(), "Invalid row index", );
214 qint64 rowId = ma->getRow(rowIdx)->getRowId();
215
216 U2OpStatus2Log os;
217 removeRowPrivate(os, entityRef, rowId);
218 SAFE_POINT_OP(os, );
219
220 MaModificationInfo mi;
221 mi.rowContentChanged = false;
222 mi.alignmentLengthChanged = false;
223
224 QList<qint64> removedRowIds;
225 removedRowIds << rowId;
226
227 updateCachedMultipleAlignment(mi, removedRowIds);
228 }
229
removeRowsById(const QList<qint64> & rowIds)230 void MultipleAlignmentObject::removeRowsById(const QList<qint64> &rowIds) {
231 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
232 CHECK(!rowIds.isEmpty(), );
233
234 QList<qint64> removedRowIds;
235 for (qint64 rowId : qAsConst(rowIds)) {
236 U2OpStatus2Log os;
237 removeRowPrivate(os, entityRef, rowId);
238 if (!os.hasError()) {
239 removedRowIds << rowId;
240 }
241 }
242
243 MaModificationInfo mi;
244 mi.rowContentChanged = false;
245 mi.alignmentLengthChanged = false;
246 updateCachedMultipleAlignment(mi, removedRowIds);
247
248 SAFE_POINT(removedRowIds.size() == rowIds.size(), "Failed to remove some rows", );
249 }
250
removeRows(const QList<int> & rowIndexes)251 void MultipleAlignmentObject::removeRows(const QList<int> &rowIndexes) {
252 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
253 CHECK(!rowIndexes.isEmpty(), );
254
255 const MultipleAlignment &ma = getMultipleAlignment();
256 QList<qint64> rowIdsToRemove;
257 foreach (int rowIdx, rowIndexes) {
258 SAFE_POINT(rowIdx >= 0 && rowIdx < ma->getNumRows(), "Invalid row index", );
259 qint64 rowId = ma->getRow(rowIdx)->getRowId();
260 rowIdsToRemove << rowId;
261 }
262
263 removeRowsById(rowIdsToRemove);
264 }
265
renameRow(int rowIdx,const QString & newName)266 void MultipleAlignmentObject::renameRow(int rowIdx, const QString &newName) {
267 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
268
269 const MultipleAlignment &ma = getMultipleAlignment();
270 SAFE_POINT(rowIdx >= 0 && rowIdx < ma->getNumRows(), "Invalid row index", );
271 qint64 rowId = ma->getRow(rowIdx)->getRowId();
272
273 U2OpStatus2Log os;
274 MaDbiUtils::renameRow(entityRef, rowId, newName, os);
275 SAFE_POINT_OP(os, );
276
277 MaModificationInfo mi;
278 mi.alignmentLengthChanged = false;
279 updateCachedMultipleAlignment(mi);
280 }
281
isRegionEmpty(int startPos,int startRow,int numChars,int numRows) const282 bool MultipleAlignmentObject::isRegionEmpty(int startPos, int startRow, int numChars, int numRows) const {
283 const MultipleAlignment &ma = getMultipleAlignment();
284 bool isEmpty = true;
285 for (int row = startRow; row < startRow + numRows && isEmpty; ++row) {
286 for (int pos = startPos; pos < startPos + numChars && isEmpty; ++pos) {
287 isEmpty = ma->isGap(row, pos);
288 }
289 }
290 return isEmpty;
291 }
292
isRegionEmpty(const QList<int> & rowIndexes,int x,int width) const293 bool MultipleAlignmentObject::isRegionEmpty(const QList<int> &rowIndexes, int x, int width) const {
294 const MultipleAlignment &ma = getMultipleAlignment();
295 bool isEmpty = true;
296 for (int i = 0; i < rowIndexes.size() && isEmpty; i++) {
297 int rowIndex = rowIndexes[i];
298 for (int pos = x; pos < x + width && isEmpty; pos++) {
299 isEmpty = ma->isGap(rowIndex, pos);
300 }
301 }
302 return isEmpty;
303 }
304
moveRowsBlock(int firstRow,int numRows,int shift)305 void MultipleAlignmentObject::moveRowsBlock(int firstRow, int numRows, int shift) {
306 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
307
308 QList<qint64> rowIds = getMultipleAlignment()->getRowsIds();
309 QList<qint64> rowsToMove;
310
311 for (int i = 0; i < numRows; ++i) {
312 rowsToMove << rowIds[firstRow + i];
313 }
314
315 U2OpStatusImpl os;
316 MaDbiUtils::moveRows(entityRef, rowsToMove, shift, os);
317 CHECK_OP(os, );
318
319 updateCachedMultipleAlignment();
320 }
321
getRowIds() const322 QList<qint64> MultipleAlignmentObject::getRowIds() const {
323 return getMultipleAlignment()->getRowsIds();
324 }
325
getRowIdsByRowIndexes(const QList<int> & rowIndexes) const326 QList<qint64> MultipleAlignmentObject::getRowIdsByRowIndexes(const QList<int> &rowIndexes) const {
327 QList<qint64> allRowIds = getRowIds();
328 QList<qint64> resultRowIds;
329 int rowCount = getNumRows();
330 for (int rowIndex : qAsConst(rowIndexes)) {
331 SAFE_POINT(rowIndex >= 0 && rowIndex < rowCount, "Invalid row index: " + QString::number(rowIndex), {});
332 resultRowIds << allRowIds[rowIndex];
333 }
334 return resultRowIds;
335 }
336
updateRowsOrder(U2OpStatus & os,const QList<qint64> & rowIds)337 void MultipleAlignmentObject::updateRowsOrder(U2OpStatus &os, const QList<qint64> &rowIds) {
338 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
339 if (rowIds == getRowIds()) {
340 return;
341 }
342 MaDbiUtils::updateRowsOrder(entityRef, rowIds, os);
343 CHECK_OP(os, );
344
345 MaModificationInfo mi;
346 mi.alignmentLengthChanged = false;
347 updateCachedMultipleAlignment(mi);
348 }
349
changeLength(U2OpStatus & os,qint64 newLength)350 void MultipleAlignmentObject::changeLength(U2OpStatus &os, qint64 newLength) {
351 qint64 length = getLength();
352 CHECK(length != newLength, );
353
354 MaDbiUtils::updateMaLength(getEntityRef(), newLength, os);
355 CHECK_OP(os, );
356
357 bool rowContentChangeStatus = false;
358 if (newLength < length) {
359 qint64 numRows = getNumRows();
360 for (int i = 0; i < numRows; i++) {
361 MultipleAlignmentRow row = getRow(i);
362 qint64 rowLengthWithoutTrailing = row->getRowLengthWithoutTrailing();
363 if (rowLengthWithoutTrailing > newLength) {
364 row->crop(os, 0, newLength);
365 CHECK_OP(os, );
366 rowContentChangeStatus = true;
367 }
368 }
369 }
370
371 MaModificationInfo modificationInfo;
372 modificationInfo.rowContentChanged = rowContentChangeStatus;
373 modificationInfo.rowListChanged = false;
374 updateCachedMultipleAlignment(modificationInfo);
375 }
376
updateCachedMultipleAlignment(const MaModificationInfo & mi,const QList<qint64> & removedRowIds)377 void MultipleAlignmentObject::updateCachedMultipleAlignment(const MaModificationInfo &mi, const QList<qint64> &removedRowIds) {
378 ensureDataLoaded();
379 emit si_startMaUpdating();
380
381 MultipleAlignment oldCachedMa = cachedMa->getCopy();
382
383 // Undo-Redo Framework does not provide detailed change info.
384 // In this case or if we do not know which rows are changed we reload the whole object.
385 bool isUnknownChange = mi.type == MaModificationType_Undo || mi.type == MaModificationType_Redo || (mi.modifiedRowIds.isEmpty() && removedRowIds.isEmpty());
386 U2OpStatus2Log os;
387 if (isUnknownChange) {
388 loadAlignment(os); // Reload 'cachedMa'.
389 SAFE_POINT_OP(os, );
390 } else {
391 if (mi.alignmentLengthChanged) {
392 qint64 msaLength = MaDbiUtils::getMaLength(entityRef, os);
393 SAFE_POINT_OP(os, );
394 if (msaLength != cachedMa->getLength()) {
395 cachedMa->setLength(msaLength);
396 }
397 }
398
399 if (mi.alphabetChanged) {
400 U2AlphabetId alphabet = MaDbiUtils::getMaAlphabet(entityRef, os);
401 SAFE_POINT_OP(os, );
402 if (alphabet.id != cachedMa->getAlphabet()->getId() && !alphabet.id.isEmpty()) {
403 const DNAAlphabet *newAlphabet = U2AlphabetUtils::getById(alphabet);
404 cachedMa->setAlphabet(newAlphabet);
405 }
406 }
407
408 if (!removedRowIds.isEmpty()) {
409 foreach (qint64 rowId, removedRowIds) {
410 const int rowIndex = cachedMa->getRowIndexByRowId(rowId, os);
411 SAFE_POINT_OP(os, );
412 cachedMa->removeRow(rowIndex, os);
413 SAFE_POINT_OP(os, );
414 }
415 }
416 if (!mi.modifiedRowIds.isEmpty()) {
417 updateCachedRows(os, mi.modifiedRowIds);
418 }
419 }
420
421 setModified(true);
422 if (!mi.middleState) {
423 emit si_alignmentChanged(oldCachedMa, mi);
424
425 if (cachedMa->isEmpty() && !oldCachedMa->isEmpty()) {
426 emit si_alignmentBecomesEmpty(true);
427 } else if (!cachedMa->isEmpty() && oldCachedMa->isEmpty()) {
428 emit si_alignmentBecomesEmpty(false);
429 }
430
431 QString newName = cachedMa->getName();
432 if (oldCachedMa->getName() != newName) {
433 setGObjectNameNotDbi(newName);
434 }
435 }
436 if (!removedRowIds.isEmpty()) {
437 emit si_rowsRemoved(removedRowIds);
438 }
439 if (cachedMa->getAlphabet()->getId() != oldCachedMa->getAlphabet()->getId()) {
440 emit si_alphabetChanged(mi, oldCachedMa->getAlphabet());
441 }
442 }
443
sortRowsByList(const QStringList & order)444 void MultipleAlignmentObject::sortRowsByList(const QStringList &order) {
445 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
446
447 MultipleSequenceAlignment ma = getMultipleAlignment()->getCopy();
448 ma->sortRowsByList(order);
449 CHECK(ma->getRowsIds() != cachedMa->getRowsIds(), );
450
451 U2OpStatusImpl os;
452 MaDbiUtils::updateRowsOrder(entityRef, ma->getRowsIds(), os);
453 SAFE_POINT_OP(os, );
454
455 MaModificationInfo mi;
456 mi.alignmentLengthChanged = false;
457 mi.rowContentChanged = false;
458 mi.rowListChanged = false;
459 updateCachedMultipleAlignment(mi);
460 }
461
insertGap(const U2Region & rows,int pos,int nGaps,bool collapseTrailingGaps)462 void MultipleAlignmentObject::insertGap(const U2Region &rows, int pos, int nGaps, bool collapseTrailingGaps) {
463 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
464 const MultipleAlignment &ma = getMultipleAlignment();
465 int startSeq = rows.startPos;
466 int endSeq = startSeq + rows.length;
467
468 QList<qint64> rowIds;
469 for (int i = startSeq; i < endSeq; ++i) {
470 qint64 rowId = ma->getRow(i)->getRowId();
471 rowIds.append(rowId);
472 }
473 insertGapByRowIdList(rowIds, pos, nGaps, collapseTrailingGaps);
474 }
475
insertGapByRowIndexList(const QList<int> & rowIndexes,int pos,int nGaps,bool collapseTrailingGaps)476 void MultipleAlignmentObject::insertGapByRowIndexList(const QList<int> &rowIndexes, int pos, int nGaps, bool collapseTrailingGaps) {
477 const MultipleAlignment &ma = getMultipleAlignment();
478 QList<qint64> rowIds;
479 for (int i = 0; i < rowIndexes.size(); i++) {
480 int rowIndex = rowIndexes[i];
481 qint64 rowId = ma->getRow(rowIndex)->getRowId();
482 rowIds.append(rowId);
483 }
484 insertGapByRowIdList(rowIds, pos, nGaps, collapseTrailingGaps);
485 }
486
insertGapByRowIdList(const QList<qint64> & rowIds,int pos,int nGaps,bool collapseTrailingGaps)487 void MultipleAlignmentObject::insertGapByRowIdList(const QList<qint64> &rowIds, int pos, int nGaps, bool collapseTrailingGaps) {
488 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
489 U2OpStatus2Log os;
490 MsaDbiUtils::insertGaps(entityRef, rowIds, pos, nGaps, os, collapseTrailingGaps);
491 SAFE_POINT_OP(os, );
492
493 MaModificationInfo mi;
494 mi.rowListChanged = false;
495 mi.modifiedRowIds = rowIds;
496 updateCachedMultipleAlignment(mi);
497 }
498
499 namespace {
500
501 template<typename T>
mergeLists(const QList<T> & first,const QList<T> & second)502 inline QList<T> mergeLists(const QList<T> &first, const QList<T> &second) {
503 QList<T> result = first;
504 foreach (const T &item, second) {
505 if (!result.contains(item)) {
506 result.append(item);
507 }
508 }
509 return result;
510 }
511
getRowsAffectedByDeletion(const MultipleAlignment & ma,const QList<qint64> & removedRowIds)512 QList<qint64> getRowsAffectedByDeletion(const MultipleAlignment &ma, const QList<qint64> &removedRowIds) {
513 QList<qint64> rowIdsAffectedByDeletion;
514 U2OpStatus2Log os;
515 const QList<qint64> maRows = ma->getRowsIds();
516 int previousRemovedRowIndex = -1;
517 foreach (qint64 removedRowId, removedRowIds) {
518 if (-1 != previousRemovedRowIndex) {
519 const int currentRemovedRowIndex = ma->getRowIndexByRowId(removedRowId, os);
520 SAFE_POINT_OP(os, QList<qint64>());
521 SAFE_POINT(currentRemovedRowIndex > previousRemovedRowIndex, "Rows order violation", QList<qint64>());
522 const int countOfUnchangedRowsBetween = currentRemovedRowIndex - previousRemovedRowIndex - 1;
523 if (0 < countOfUnchangedRowsBetween) {
524 for (int middleRowIndex = previousRemovedRowIndex + 1; middleRowIndex < currentRemovedRowIndex; ++middleRowIndex) {
525 rowIdsAffectedByDeletion += maRows[middleRowIndex];
526 }
527 }
528 }
529 previousRemovedRowIndex = ma->getRowIndexByRowId(removedRowId, os);
530 SAFE_POINT_OP(os, QList<qint64>());
531 }
532 const int lastDeletedRowIndex = ma->getRowIndexByRowId(removedRowIds.last(), os);
533 SAFE_POINT_OP(os, QList<qint64>());
534 if (lastDeletedRowIndex < maRows.size() - 1) { // if the last removed row was not in the bottom of the msa
535 rowIdsAffectedByDeletion += maRows.mid(lastDeletedRowIndex + 1);
536 }
537 return rowIdsAffectedByDeletion;
538 }
539
540 } // namespace
541
removeRegion(const QList<int> & rowIndexes,int x,int width,bool removeEmptyRows)542 void MultipleAlignmentObject::removeRegion(const QList<int> &rowIndexes, int x, int width, bool removeEmptyRows) {
543 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
544
545 const MultipleAlignment &ma = getMultipleAlignment();
546 QList<qint64> modifiedRowIds = convertMaRowIndexesToMaRowIds(rowIndexes);
547 U2OpStatus2Log os;
548 removeRegionPrivate(os, entityRef, modifiedRowIds, x, width);
549 SAFE_POINT_OP(os, );
550
551 QList<qint64> removedRowIds;
552 if (removeEmptyRows) {
553 removedRowIds = MsaDbiUtils::removeEmptyRows(entityRef, modifiedRowIds, os);
554 SAFE_POINT_OP(os, );
555 if (!removedRowIds.isEmpty()) {
556 // suppose that if at least one row in msa was removed then all the rows below it were changed
557 QList<qint64> rowIdsAffectedByDeletion = getRowsAffectedByDeletion(ma, removedRowIds);
558 foreach (qint64 removedRowId, removedRowIds) { // removed rows ain't need to be update
559 modifiedRowIds.removeAll(removedRowId);
560 }
561 modifiedRowIds = mergeLists(modifiedRowIds, rowIdsAffectedByDeletion);
562 }
563 }
564
565 MaModificationInfo mi;
566 mi.modifiedRowIds = modifiedRowIds;
567 updateCachedMultipleAlignment(mi, removedRowIds);
568
569 if (!removedRowIds.isEmpty()) {
570 emit si_rowsRemoved(removedRowIds);
571 }
572 }
573
removeRegion(int startPos,int startRow,int nBases,int nRows,bool removeEmptyRows,bool track)574 void MultipleAlignmentObject::removeRegion(int startPos, int startRow, int nBases, int nRows, bool removeEmptyRows, bool track) {
575 SAFE_POINT(!isStateLocked(), "Alignment state is locked", );
576
577 QList<qint64> modifiedRowIds;
578 const MultipleAlignment &ma = getMultipleAlignment();
579 const QList<MultipleAlignmentRow> &maRows = ma->getRows();
580 SAFE_POINT(nRows > 0 && startRow >= 0 && startRow + nRows <= maRows.size() && startPos + nBases <= ma->getLength(), "Invalid parameters", );
581 QList<MultipleAlignmentRow>::ConstIterator it = maRows.begin() + startRow;
582 QList<MultipleAlignmentRow>::ConstIterator end = it + nRows;
583 for (; it != end; it++) {
584 modifiedRowIds << (*it)->getRowId();
585 }
586
587 U2OpStatus2Log os;
588 removeRegionPrivate(os, entityRef, modifiedRowIds, startPos, nBases);
589 SAFE_POINT_OP(os, );
590
591 QList<qint64> removedRows;
592 if (removeEmptyRows) {
593 removedRows = MsaDbiUtils::removeEmptyRows(entityRef, modifiedRowIds, os);
594 SAFE_POINT_OP(os, );
595 if (!removedRows.isEmpty()) { // suppose that if at least one row in msa was removed then
596 // all the rows below it were changed
597 const QList<qint64> rowIdsAffectedByDeletion = getRowsAffectedByDeletion(ma, removedRows);
598 foreach (qint64 removedRowId, removedRows) { // removed rows ain't need to be update
599 modifiedRowIds.removeAll(removedRowId);
600 }
601 modifiedRowIds = mergeLists(modifiedRowIds, rowIdsAffectedByDeletion);
602 }
603 }
604
605 if (track || !removedRows.isEmpty()) {
606 MaModificationInfo mi;
607 mi.modifiedRowIds = modifiedRowIds;
608 updateCachedMultipleAlignment(mi, removedRows);
609 }
610
611 if (!removedRows.isEmpty()) {
612 emit si_rowsRemoved(removedRows);
613 }
614 }
615
deleteGap(U2OpStatus & os,const U2Region & rows,int pos,int maxGaps)616 int MultipleAlignmentObject::deleteGap(U2OpStatus &os, const U2Region &rows, int pos, int maxGaps) {
617 QList<int> rowIndexes;
618 for (int i = (int)rows.startPos; i < (int)rows.endPos(); i++) {
619 rowIndexes << i;
620 }
621 return deleteGapByRowIndexList(os, rowIndexes, pos, maxGaps);
622 }
623
toUniqueRowIndexes(const QList<int> & rowIndexes,int numRows)624 static QList<int> toUniqueRowIndexes(const QList<int> &rowIndexes, int numRows) {
625 QSet<int> uniqueRowIndexes;
626 for (int i = 0; i < rowIndexes.size(); i++) {
627 int rowIndex = rowIndexes[i];
628 if (rowIndex >= 0 && rowIndex < numRows) {
629 uniqueRowIndexes << rowIndex;
630 }
631 }
632 return uniqueRowIndexes.toList();
633 }
634
deleteGapByRowIndexList(U2OpStatus & os,const QList<int> & rowIndexes,int pos,int maxGaps)635 int MultipleAlignmentObject::deleteGapByRowIndexList(U2OpStatus &os, const QList<int> &rowIndexes, int pos, int maxGaps) {
636 SAFE_POINT(!isStateLocked(), "Alignment state is locked", 0);
637
638 int removingGapColumnCount = getMaxWidthOfGapRegion(os, rowIndexes, pos, maxGaps);
639 SAFE_POINT_OP(os, 0);
640
641 if (removingGapColumnCount == 0) {
642 return 0;
643 } else if (removingGapColumnCount < maxGaps) {
644 pos += maxGaps - removingGapColumnCount;
645 }
646 QList<qint64> modifiedRowIds;
647 MultipleAlignment msa = getMultipleAlignmentCopy();
648 // iterate through given rows to update each of them in DB
649 QList<int> uniqueRowIndexes = toUniqueRowIndexes(rowIndexes, getNumRows());
650 for (int i = 0; i < rowIndexes.size(); i++) {
651 int rowIndex = uniqueRowIndexes[i];
652 msa->removeChars(rowIndex, pos, removingGapColumnCount, os);
653 CHECK_OP(os, 0);
654
655 MultipleAlignmentRow row = msa->getRow(rowIndex);
656 MaDbiUtils::updateRowGapModel(entityRef, row->getRowId(), row->getGapModel(), os);
657 CHECK_OP(os, 0);
658 modifiedRowIds << row->getRowId();
659 }
660
661 if (uniqueRowIndexes.size() == getNumRows()) {
662 // delete columns
663 MaDbiUtils::updateMaLength(entityRef, getLength() - removingGapColumnCount, os);
664 CHECK_OP(os, 0);
665 }
666
667 MaModificationInfo mi;
668 mi.rowListChanged = false;
669 mi.modifiedRowIds = modifiedRowIds;
670 updateCachedMultipleAlignment(mi);
671 return removingGapColumnCount;
672 }
673
shiftRegion(int startPos,int startRow,int nBases,int nRows,int shift)674 int MultipleAlignmentObject::shiftRegion(int startPos, int startRow, int nBases, int nRows, int shift) {
675 SAFE_POINT(!isStateLocked(), "Alignment state is locked", 0);
676 SAFE_POINT(!isRegionEmpty(startPos, startRow, nBases, nRows), "Region is empty", 0);
677 SAFE_POINT(0 <= startPos && 0 <= startRow && 0 < nBases && 0 < nRows, "Invalid parameters of selected region encountered", 0);
678 U2OpStatusImpl os;
679
680 int n = 0;
681 if (shift > 0) {
682 // if last symbol selected - do not add gaps at the end
683 if (startPos + nBases != getLength()) {
684 // if some trailing gaps are selected --> save them!
685 if (startPos + nBases + shift > getLength()) {
686 bool increaseAlignmentLen = true;
687 for (int i = startRow; i < startRow + nRows; i++) {
688 int rowLen = getRow(i)->getRowLengthWithoutTrailing();
689 if (rowLen >= startPos + nBases + shift) {
690 increaseAlignmentLen = false;
691 break;
692 }
693 }
694 if (increaseAlignmentLen) {
695 MaDbiUtils::updateMaLength(entityRef, startPos + nBases + shift, os);
696 SAFE_POINT_OP(os, 0);
697 updateCachedMultipleAlignment();
698 }
699 }
700 }
701
702 insertGap(U2Region(startRow, nRows), startPos, shift);
703 n = shift;
704 } else if (0 < startPos) {
705 if (0 > startPos + shift) {
706 shift = -startPos;
707 }
708 n = -deleteGap(os, U2Region(startRow, nRows), startPos + shift, -shift);
709 SAFE_POINT_OP(os, 0);
710 }
711 return n;
712 }
713
saveState()714 void MultipleAlignmentObject::saveState() {
715 const MultipleAlignment &ma = getMultipleAlignment();
716 emit si_completeStateChanged(false);
717 savedState.setState(ma);
718 }
719
releaseState()720 void MultipleAlignmentObject::releaseState() {
721 if (!isStateLocked()) {
722 emit si_completeStateChanged(true);
723
724 CHECK(savedState.hasState(), );
725 MultipleAlignment maBefore = savedState.takeState();
726 CHECK(*maBefore != *getMultipleAlignment(), );
727 setModified(true);
728
729 MaModificationInfo mi;
730 emit si_alignmentChanged(maBefore, mi);
731
732 if (cachedMa->isEmpty() && !maBefore->isEmpty()) {
733 emit si_alignmentBecomesEmpty(true);
734 } else if (!cachedMa->isEmpty() && maBefore->isEmpty()) {
735 emit si_alignmentBecomesEmpty(false);
736 }
737 }
738 }
739
loadDataCore(U2OpStatus & os)740 void MultipleAlignmentObject::loadDataCore(U2OpStatus &os) {
741 DbiConnection con(entityRef.dbiRef, os);
742 CHECK_OP(os, );
743 loadAlignment(os);
744 }
745
getMaxWidthOfGapRegion(U2OpStatus & os,const QList<int> & rowIndexes,int pos,int maxGaps)746 int MultipleAlignmentObject::getMaxWidthOfGapRegion(U2OpStatus &os, const QList<int> &rowIndexes, int pos, int maxGaps) {
747 const MultipleAlignment &ma = getMultipleAlignment();
748 SAFE_POINT_EXT(pos >= 0 && maxGaps >= 0 && pos < ma->getLength(), os.setError("Illegal parameters of the gap region"), 0);
749
750 // check if there is nothing to remove
751 int maxRemovedGaps = qBound(0, maxGaps, ma->getLength() - pos);
752 if (maxRemovedGaps == 0) {
753 return 0;
754 }
755 QList<int> uniqueRowIndexes = toUniqueRowIndexes(rowIndexes, getNumRows());
756 int removingGapColumnCount = maxRemovedGaps;
757 bool isRegionInRowTrailingGaps = true;
758 // iterate through given rows to determine the width of the continuous gap region
759 for (int i = 0; i < uniqueRowIndexes.size(); i++) {
760 int rowIndex = uniqueRowIndexes[i];
761 int gapCountInCurrentRow = 0;
762 // iterate through current row bases to determine gap count
763 while (gapCountInCurrentRow < maxRemovedGaps) {
764 if (!ma->isGap(rowIndex, pos + maxGaps - gapCountInCurrentRow - 1)) {
765 break;
766 }
767 gapCountInCurrentRow++;
768 }
769
770 // determine if the given area intersects a row in the area of trailing gaps
771 if (gapCountInCurrentRow != 0 && isRegionInRowTrailingGaps) {
772 int trailingPosition = pos + maxRemovedGaps - gapCountInCurrentRow;
773 if (trailingPosition != ma->getLength()) {
774 while (ma->getLength() > trailingPosition && isRegionInRowTrailingGaps) {
775 isRegionInRowTrailingGaps = isRegionInRowTrailingGaps && ma->isGap(rowIndex, trailingPosition);
776 trailingPosition++;
777 }
778 }
779 } else if (isRegionInRowTrailingGaps) {
780 isRegionInRowTrailingGaps = false;
781 }
782
783 if (gapCountInCurrentRow == 0) {
784 // don't do anything if there is a row without gaps
785 return 0;
786 }
787 removingGapColumnCount = qMin(removingGapColumnCount, gapCountInCurrentRow);
788 }
789
790 if (isRegionInRowTrailingGaps) {
791 if (uniqueRowIndexes.size() == getNumRows()) {
792 return qMin((int)getLength() - pos, maxGaps);
793 } else {
794 return 0;
795 }
796 }
797
798 return removingGapColumnCount;
799 }
800
convertMaRowIndexesToMaRowIds(const QList<int> & maRowIndexes,bool excludeErrors)801 QList<qint64> MultipleAlignmentObject::convertMaRowIndexesToMaRowIds(const QList<int> &maRowIndexes, bool excludeErrors) {
802 QList<qint64> ids;
803 const QList<MultipleAlignmentRow> &rows = getMultipleAlignment()->getRows();
804 for (int i = 0; i < maRowIndexes.length(); i++) {
805 int index = maRowIndexes[i];
806 bool isValid = index >= 0 && index <= rows.size() - 1;
807 if (isValid) {
808 ids << rows[index]->getRowId();
809 } else if (!excludeErrors) {
810 ids << -1;
811 }
812 }
813 return ids;
814 }
815
convertMaRowIdsToMaRowIndexes(const QList<qint64> & maRowIds,bool excludeErrors)816 QList<int> MultipleAlignmentObject::convertMaRowIdsToMaRowIndexes(const QList<qint64> &maRowIds, bool excludeErrors) {
817 QList<int> indexes;
818 const QList<MultipleAlignmentRow> &rows = getMultipleAlignment()->getRows();
819 for (int i = 0; i < maRowIds.length(); i++) {
820 int rowId = maRowIds[i];
821 int index = -1;
822 for (int j = 0; j < rows.size(); j++) {
823 const MultipleAlignmentRow &row = rows[j];
824 if (row->getRowId() == rowId) {
825 index = j;
826 break;
827 }
828 }
829 bool isValid = index >= 0;
830 if (isValid) {
831 indexes << index;
832 } else if (!excludeErrors) {
833 indexes << -1;
834 }
835 }
836 return indexes;
837 }
838
839 } // namespace U2
840