1 /* 2 * Jalview - A Sequence Alignment Editor and Viewer (2.11.1.4) 3 * Copyright (C) 2021 The Jalview Authors 4 * 5 * This file is part of Jalview. 6 * 7 * Jalview is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation, either version 3 10 * of the License, or (at your option) any later version. 11 * 12 * Jalview is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty 14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 15 * PURPOSE. See the GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>. 19 * The Jalview Authors are detailed in the 'AUTHORS' file. 20 */ 21 package jalview.datamodel; 22 23 import jalview.util.MessageManager; 24 import jalview.util.ShiftList; 25 26 import java.io.PrintStream; 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Transient object compactly representing a 'view' of an alignment - with 32 * discontinuities marked. Extended in Jalview 2.7 to optionally record sequence 33 * groups and specific selected regions on the alignment. 34 */ 35 public class AlignmentView 36 { 37 private SeqCigar[] sequences = null; 38 39 private int[] contigs = null; 40 41 private int width = 0; 42 43 private int firstCol = 0; 44 45 /** 46 * one or more ScGroup objects, which are referenced by each seqCigar's group 47 * membership 48 */ 49 private List<ScGroup> scGroups = null; 50 51 private boolean isNa = false; 52 53 /** 54 * reference to the complementary CDS/Protein alignment for this alignment, if available 55 */ 56 private AlignmentView complementView=null; 57 58 /** 59 * setter for 60 * @param complementView 61 */ setComplement(AlignmentView complementView)62 public void setComplement(AlignmentView complementView) 63 { 64 this.complementView = complementView; 65 66 } 67 /** 68 * 69 * @return true if a complement is available 70 */ hasComplementView()71 public boolean hasComplementView() 72 { 73 return complementView!=null; 74 } 75 /** 76 * 77 * @return the complement view or null 78 */ getComplementView()79 public AlignmentView getComplementView() 80 { 81 return complementView; 82 } 83 84 /** 85 * false if the view concerns peptides 86 * 87 * @return 88 */ isNa()89 public boolean isNa() 90 { 91 return isNa; 92 } 93 94 /** 95 * Group defined over SeqCigars. Unlike AlignmentI associated groups, each 96 * SequenceGroup hold just the essential properties for the group, but no 97 * references to the sequences involved. SeqCigars hold references to the 98 * seuqenceGroup entities themselves. 99 */ 100 private class ScGroup 101 { 102 public List<SeqCigar> seqs; 103 104 public SequenceGroup sg; 105 ScGroup()106 ScGroup() 107 { 108 seqs = new ArrayList<>(); 109 } 110 111 /** 112 * @param seq 113 * @return true if seq was not a member before and was added to group 114 */ add(SeqCigar seq)115 public boolean add(SeqCigar seq) 116 { 117 if (!seq.isMemberOf(this)) 118 { 119 seqs.add(seq); 120 seq.setGroupMembership(this); 121 return true; 122 } 123 else 124 { 125 return false; 126 } 127 } 128 129 /** 130 * 131 * @param seq 132 * @return true if seq was a member and was removed from group 133 */ remove(SeqCigar seq)134 public boolean remove(SeqCigar seq) 135 { 136 if (seq.removeGroupMembership(this)) 137 { 138 seqs.remove(seq); 139 return true; 140 } 141 return false; 142 } 143 size()144 public int size() 145 { 146 return seqs.size(); 147 } 148 } 149 150 /** 151 * vector of selected seqCigars. This vector is also referenced by each 152 * seqCigar contained in it. 153 */ 154 private ScGroup selected; 155 156 /** 157 * Construct an alignmentView from a live jalview alignment view. Note - 158 * hidden rows will be excluded from alignmentView Note: JAL-1179 159 * 160 * @param alignment 161 * - alignment as referenced by an AlignViewport 162 * @param columnSelection 163 * - 164 * @param selection 165 * @param hasHiddenColumns 166 * - mark the hidden columns in columnSelection as hidden in the view 167 * @param selectedRegionOnly 168 * - when set, only include the selected region in the view, 169 * otherwise just mark the selected region on the constructed view. 170 * @param recordGroups 171 * - when set, any groups on the given alignment will be marked on 172 * the view 173 */ AlignmentView(AlignmentI alignment, HiddenColumns hidden, SequenceGroup selection, boolean hasHiddenColumns, boolean selectedRegionOnly, boolean recordGroups)174 public AlignmentView(AlignmentI alignment, HiddenColumns hidden, 175 SequenceGroup selection, boolean hasHiddenColumns, 176 boolean selectedRegionOnly, boolean recordGroups) 177 { 178 // refactored from AlignViewport.getAlignmentView(selectedOnly); 179 this(new jalview.datamodel.CigarArray(alignment, 180 (hasHiddenColumns ? hidden : null), 181 (selectedRegionOnly ? selection : null)), 182 (selectedRegionOnly && selection != null) 183 ? selection.getStartRes() 184 : 0); 185 isNa = alignment.isNucleotide(); 186 // walk down SeqCigar array and Alignment Array - optionally restricted by 187 // selected region. 188 // test group membership for each sequence in each group, store membership 189 // and record non-empty groups in group list. 190 // record / sub-select selected region on the alignment view 191 SequenceI[] selseqs; 192 if (selection != null && selection.getSize() > 0) 193 { 194 this.selected = new ScGroup(); 195 selseqs = selection.getSequencesInOrder(alignment, 196 selectedRegionOnly); 197 } 198 else 199 { 200 selseqs = alignment.getSequencesArray(); 201 } 202 203 List<List<SequenceI>> seqsets = new ArrayList<>(); 204 // get the alignment's group list and make a copy 205 List<SequenceGroup> grps = new ArrayList<>(); 206 List<SequenceGroup> gg = alignment.getGroups(); 207 grps.addAll(gg); 208 ScGroup[] sgrps = null; 209 boolean addedgps[] = null; 210 if (grps != null) 211 { 212 if (selection != null && selectedRegionOnly) 213 { 214 // trim annotation to the region being stored. 215 // strip out any groups that do not actually intersect with the 216 // visible and selected region 217 int ssel = selection.getStartRes(), esel = selection.getEndRes(); 218 List<SequenceGroup> isg = new ArrayList<>(); 219 for (SequenceGroup sg : grps) 220 { 221 if (!(sg.getStartRes() > esel || sg.getEndRes() < ssel)) 222 { 223 // adjust bounds of new group, if necessary. 224 if (sg.getStartRes() < ssel) 225 { 226 sg.setStartRes(ssel); 227 } 228 if (sg.getEndRes() > esel) 229 { 230 sg.setEndRes(esel); 231 } 232 sg.setStartRes(sg.getStartRes() - ssel + 1); 233 sg.setEndRes(sg.getEndRes() - ssel + 1); 234 235 isg.add(sg); 236 } 237 } 238 grps = isg; 239 } 240 241 sgrps = new ScGroup[grps.size()]; 242 addedgps = new boolean[grps.size()]; 243 for (int g = 0; g < sgrps.length; g++) 244 { 245 SequenceGroup sg = grps.get(g); 246 sgrps[g] = new ScGroup(); 247 sgrps[g].sg = new SequenceGroup(sg); 248 addedgps[g] = false; 249 // can't set entry 0 in an empty list 250 // seqsets.set(g, sg.getSequences(null)); 251 seqsets.add(sg.getSequences()); 252 } 253 // seqsets now contains vectors (should be sets) for each group, so we can 254 // track when we've done with the group 255 } 256 int csi = 0; 257 for (int i = 0; i < selseqs.length; i++) 258 { 259 if (selseqs[i] != null) 260 { 261 if (selection != null && selection.getSize() > 0 262 && !selectedRegionOnly) 263 { 264 selected.add(sequences[csi]); 265 } 266 if (seqsets != null) 267 { 268 for (int sg = 0; sg < sgrps.length; sg++) 269 { 270 if ((seqsets.get(sg)).contains(selseqs[i])) 271 { 272 sgrps[sg].sg.deleteSequence(selseqs[i], false); 273 sgrps[sg].add(sequences[csi]); 274 if (!addedgps[sg]) 275 { 276 if (scGroups == null) 277 { 278 scGroups = new ArrayList<>(); 279 } 280 addedgps[sg] = true; 281 scGroups.add(sgrps[sg]); 282 } 283 } 284 } 285 } 286 csi++; 287 } 288 } 289 // finally, delete the remaining sequences (if any) not selected 290 for (int sg = 0; sg < sgrps.length; sg++) 291 { 292 SequenceI[] sqs = sgrps[sg].sg.getSequencesAsArray(null); 293 for (int si = 0; si < sqs.length; si++) 294 { 295 sgrps[sg].sg.deleteSequence(sqs[si], false); 296 } 297 sgrps[sg] = null; 298 } 299 } 300 301 /** 302 * construct an alignmentView from a SeqCigarArray. Errors are thrown if the 303 * seqcigararray.isSeqCigarArray() flag is not set. 304 */ AlignmentView(CigarArray seqcigararray)305 public AlignmentView(CigarArray seqcigararray) 306 { 307 if (!seqcigararray.isSeqCigarArray()) 308 { 309 throw new Error( 310 "Implementation Error - can only make an alignment view from a CigarArray of sequences."); 311 } 312 // contigs = seqcigararray.applyDeletions(); 313 contigs = seqcigararray.getDeletedRegions(); 314 sequences = seqcigararray.getSeqCigarArray(); 315 width = seqcigararray.getWidth(); // visible width 316 } 317 318 /** 319 * Create an alignmentView where the first column corresponds with the 320 * 'firstcol' column of some reference alignment 321 * 322 * @param sdata 323 * @param firstcol 324 */ AlignmentView(CigarArray sdata, int firstcol)325 public AlignmentView(CigarArray sdata, int firstcol) 326 { 327 this(sdata); 328 firstCol = firstcol; 329 } 330 setSequences(SeqCigar[] sequences)331 public void setSequences(SeqCigar[] sequences) 332 { 333 this.sequences = sequences; 334 } 335 setContigs(int[] contigs)336 public void setContigs(int[] contigs) 337 { 338 this.contigs = contigs; 339 } 340 getSequences()341 public SeqCigar[] getSequences() 342 { 343 return sequences; 344 } 345 346 /** 347 * @see CigarArray.getDeletedRegions 348 * @return int[] { vis_start, sym_start, length } 349 */ getContigs()350 public int[] getContigs() 351 { 352 return contigs; 353 } 354 355 /** 356 * get the full alignment and a columnselection object marking the hidden 357 * regions 358 * 359 * @param gapCharacter 360 * char 361 * @return Object[] { SequenceI[], ColumnSelection} 362 */ getAlignmentAndHiddenColumns(char gapCharacter)363 public Object[] getAlignmentAndHiddenColumns(char gapCharacter) 364 { 365 HiddenColumns hidden = new HiddenColumns(); 366 367 return new Object[] { SeqCigar.createAlignmentSequences(sequences, 368 gapCharacter, hidden, contigs), 369 hidden }; 370 } 371 372 /** 373 * return the visible alignment corresponding to this view. Sequences in this 374 * alignment are edited versions of the parent sequences - where hidden 375 * regions have been removed. NOTE: the sequence data in this alignment is not 376 * complete! 377 * 378 * @param c 379 * @return 380 */ getVisibleAlignment(char c)381 public AlignmentI getVisibleAlignment(char c) 382 { 383 SequenceI[] aln = getVisibleSeqs(c); 384 385 AlignmentI vcal = new Alignment(aln); 386 addPrunedGroupsInOrder(vcal, -1, -1, true); 387 return vcal; 388 } 389 390 /** 391 * add groups from view to the given alignment 392 * 393 * @param vcal 394 * @param gstart 395 * -1 or 0 to width-1 396 * @param gend 397 * -1 or gstart to width-1 398 * @param viscontigs 399 * - true if vcal is alignment of the visible regions of the view 400 * (e.g. as returned from getVisibleAlignment) 401 */ addPrunedGroupsInOrder(AlignmentI vcal, int gstart, int gend, boolean viscontigs)402 private void addPrunedGroupsInOrder(AlignmentI vcal, int gstart, int gend, 403 boolean viscontigs) 404 { 405 boolean r = false; 406 if (gstart > -1 && gstart <= gend) 407 { 408 r = true; 409 } 410 411 SequenceI[] aln = vcal.getSequencesArray(); 412 { 413 /** 414 * prune any groups to the visible coordinates of the alignment. 415 */ 416 { 417 int nvg = (scGroups != null) ? scGroups.size() : 0; 418 if (nvg > 0) 419 { 420 SequenceGroup[] nsg = new SequenceGroup[nvg]; 421 for (int g = 0; g < nvg; g++) 422 { 423 SequenceGroup sg = scGroups.get(g).sg; 424 if (r) 425 { 426 if (sg.getStartRes() > gend || sg.getEndRes() < gstart) 427 { 428 // Skip this group 429 nsg[g] = null; 430 continue; 431 } 432 } 433 434 // clone group properties 435 nsg[g] = new SequenceGroup(sg); 436 437 // may need to shift/trim start and end ? 438 if (r && !viscontigs) 439 { 440 // Not fully tested code - routine not yet called with 441 // viscontigs==false 442 if (nsg[g].getStartRes() < gstart) 443 { 444 nsg[g].setStartRes(0); 445 } 446 else 447 { 448 nsg[g].setStartRes(nsg[g].getStartRes() - gstart); 449 nsg[g].setEndRes(nsg[g].getEndRes() - gstart); 450 } 451 if (nsg[g].getEndRes() > (gend - gstart)) 452 { 453 nsg[g].setEndRes(gend - gstart); 454 } 455 } 456 } 457 if (viscontigs) 458 { 459 // prune groups to cover just the visible positions between 460 // gstart/gend. 461 if (contigs != null) 462 { 463 int p = 0; 464 ShiftList prune = new ShiftList(); 465 if (r) 466 { 467 // adjust for start of alignment within visible window. 468 prune.addShift(gstart, -gstart); // 469 } 470 for (int h = 0; h < contigs.length; h += 3) 471 { 472 { 473 prune.addShift(p + contigs[h + 1], 474 contigs[h + 2] - contigs[h + 1]); 475 } 476 p = contigs[h + 1] + contigs[h + 2]; 477 } 478 for (int g = 0; g < nsg.length; g++) 479 { 480 if (nsg[g] != null) 481 { 482 int s = nsg[g].getStartRes(), t = nsg[g].getEndRes(); 483 int w = 1 + t - s; 484 if (r) 485 { 486 if (s < gstart) 487 { 488 s = gstart; 489 } 490 if (t > gend) 491 { 492 t = gend; 493 } 494 } 495 s = prune.shift(s); 496 t = prune.shift(t); 497 nsg[g].setStartRes(s); 498 nsg[g].setEndRes(t); 499 } 500 } 501 } 502 } 503 504 for (int nsq = 0; nsq < aln.length; nsq++) 505 { 506 for (int g = 0; g < nvg; g++) 507 { 508 if (nsg[g] != null 509 && sequences[nsq].isMemberOf(scGroups.get(g))) 510 { 511 nsg[g].addSequence(aln[nsq], false); 512 } 513 } 514 } 515 for (int g = 0; g < nvg; g++) 516 { 517 if (nsg[g] != null && nsg[g].getSize() > 0) 518 { 519 vcal.addGroup(nsg[g]); 520 } 521 nsg[g] = null; 522 } 523 } 524 } 525 } 526 } 527 528 /** 529 * generate sequence array corresponding to the visible parts of the 530 * alignment. 531 * 532 * @param c 533 * gap character to use to recreate the alignment 534 * @return 535 */ getVisibleSeqs(char c)536 private SequenceI[] getVisibleSeqs(char c) 537 { 538 SequenceI[] aln = new SequenceI[sequences.length]; 539 for (int i = 0, j = sequences.length; i < j; i++) 540 { 541 aln[i] = sequences[i].getSeq(c); 542 // Remove hidden regions from sequence 543 aln[i].setSequence(getASequenceString(c, i)); 544 } 545 return aln; 546 } 547 548 /** 549 * creates new alignment objects for all contiguous visible segments 550 * 551 * @param c 552 * @param start 553 * @param end 554 * @param regionOfInterest 555 * specify which sequences to include (or null to include all 556 * sequences) 557 * @return AlignmentI[] - all alignments where each sequence is a subsequence 558 * constructed from visible contig regions of view 559 */ getVisibleContigAlignments(char c)560 public AlignmentI[] getVisibleContigAlignments(char c) 561 { 562 int nvc = 0; 563 int[] vcontigs = getVisibleContigs(); 564 SequenceI[][] contigviews = getVisibleContigs(c); 565 AlignmentI[] vcals = new AlignmentI[contigviews.length]; 566 for (nvc = 0; nvc < contigviews.length; nvc++) 567 { 568 vcals[nvc] = new Alignment(contigviews[nvc]); 569 if (scGroups != null && scGroups.size() > 0) 570 { 571 addPrunedGroupsInOrder(vcals[nvc], vcontigs[nvc * 2], 572 vcontigs[nvc * 2 + 1], true); 573 } 574 } 575 return vcals; 576 } 577 578 /** 579 * build a string excluding hidden regions from a particular sequence in the 580 * view 581 * 582 * @param c 583 * @param n 584 * @return 585 */ getASequenceString(char c, int n)586 private String getASequenceString(char c, int n) 587 { 588 String sqn; 589 String fullseq = sequences[n].getSequenceString(c); 590 if (contigs != null) 591 { 592 sqn = ""; 593 int p = 0; 594 for (int h = 0; h < contigs.length; h += 3) 595 { 596 sqn += fullseq.substring(p, contigs[h + 1]); 597 p = contigs[h + 1] + contigs[h + 2]; 598 } 599 sqn += fullseq.substring(p); 600 } 601 else 602 { 603 sqn = fullseq; 604 } 605 return sqn; 606 } 607 608 /** 609 * get an array of visible sequence strings for a view on an alignment using 610 * the given gap character uses getASequenceString 611 * 612 * @param c 613 * char 614 * @return String[] 615 */ getSequenceStrings(char c)616 public String[] getSequenceStrings(char c) 617 { 618 String[] seqs = new String[sequences.length]; 619 for (int n = 0; n < sequences.length; n++) 620 { 621 seqs[n] = getASequenceString(c, n); 622 } 623 return seqs; 624 } 625 626 /** 627 * 628 * @return visible number of columns in alignment view 629 */ getWidth()630 public int getWidth() 631 { 632 return width; 633 } 634 setWidth(int width)635 protected void setWidth(int width) 636 { 637 this.width = width; 638 } 639 640 /** 641 * get the contiguous subalignments in an alignment view. 642 * 643 * @param gapCharacter 644 * char 645 * @return SequenceI[][] 646 */ getVisibleContigs(char gapCharacter)647 public SequenceI[][] getVisibleContigs(char gapCharacter) 648 { 649 SequenceI[][] smsa; 650 int njobs = 1; 651 if (sequences == null || width <= 0) 652 { 653 return null; 654 } 655 if (contigs != null && contigs.length > 0) 656 { 657 int start = 0; 658 njobs = 0; 659 int fwidth = width; 660 for (int contig = 0; contig < contigs.length; contig += 3) 661 { 662 if ((contigs[contig + 1] - start) > 0) 663 { 664 njobs++; 665 } 666 fwidth += contigs[contig + 2]; // end up with full region width 667 // (including hidden regions) 668 start = contigs[contig + 1] + contigs[contig + 2]; 669 } 670 if (start < fwidth) 671 { 672 njobs++; 673 } 674 smsa = new SequenceI[njobs][]; 675 start = 0; 676 int j = 0; 677 for (int contig = 0; contig < contigs.length; contig += 3) 678 { 679 if (contigs[contig + 1] - start > 0) 680 { 681 SequenceI mseq[] = new SequenceI[sequences.length]; 682 for (int s = 0; s < mseq.length; s++) 683 { 684 mseq[s] = sequences[s].getSeq(gapCharacter) 685 .getSubSequence(start, contigs[contig + 1]); 686 } 687 smsa[j] = mseq; 688 j++; 689 } 690 start = contigs[contig + 1] + contigs[contig + 2]; 691 } 692 if (start < fwidth) 693 { 694 SequenceI mseq[] = new SequenceI[sequences.length]; 695 for (int s = 0; s < mseq.length; s++) 696 { 697 mseq[s] = sequences[s].getSeq(gapCharacter).getSubSequence(start, 698 fwidth + 1); 699 } 700 smsa[j] = mseq; 701 j++; 702 } 703 } 704 else 705 { 706 smsa = new SequenceI[1][]; 707 smsa[0] = new SequenceI[sequences.length]; 708 for (int s = 0; s < sequences.length; s++) 709 { 710 smsa[0][s] = sequences[s].getSeq(gapCharacter); 711 } 712 } 713 return smsa; 714 } 715 716 /** 717 * return full msa and hidden regions with visible blocks replaced with new 718 * sub alignments 719 * 720 * @param nvismsa 721 * SequenceI[][] 722 * @param orders 723 * AlignmentOrder[] corresponding to each SequenceI[] block. 724 * @return Object[] 725 */ getUpdatedView(SequenceI[][] nvismsa, AlignmentOrder[] orders, char gapCharacter)726 public Object[] getUpdatedView(SequenceI[][] nvismsa, 727 AlignmentOrder[] orders, char gapCharacter) 728 { 729 if (sequences == null || width <= 0) 730 { 731 throw new Error(MessageManager 732 .getString("error.empty_view_cannot_be_updated")); 733 } 734 if (nvismsa == null) 735 { 736 throw new Error( 737 "nvismsa==null. use getAlignmentAndColumnSelection() instead."); 738 } 739 if (contigs != null && contigs.length > 0) 740 { 741 SequenceI[] alignment = new SequenceI[sequences.length]; 742 // ColumnSelection columnselection = new ColumnSelection(); 743 HiddenColumns hidden = new HiddenColumns(); 744 if (contigs != null && contigs.length > 0) 745 { 746 int start = 0; 747 int nwidth = 0; 748 int owidth = width; 749 int j = 0; 750 for (int contig = 0; contig < contigs.length; contig += 3) 751 { 752 owidth += contigs[contig + 2]; // recover final column width 753 if (contigs[contig + 1] - start > 0) 754 { 755 int swidth = 0; // subalignment width 756 if (nvismsa[j] != null) 757 { 758 SequenceI mseq[] = nvismsa[j]; 759 AlignmentOrder order = (orders == null) ? null : orders[j]; 760 j++; 761 if (mseq.length != sequences.length) 762 { 763 throw new Error(MessageManager.formatMessage( 764 "error.mismatch_between_number_of_sequences_in_block", 765 new String[] 766 { Integer.valueOf(j).toString(), 767 Integer.valueOf(mseq.length).toString(), 768 Integer.valueOf(sequences.length) 769 .toString() })); 770 } 771 swidth = mseq[0].getLength(); // JBPNote: could ensure padded 772 // here. 773 for (int s = 0; s < mseq.length; s++) 774 { 775 if (alignment[s] == null) 776 { 777 alignment[s] = mseq[s]; 778 } 779 else 780 { 781 alignment[s] 782 .setSequence(alignment[s].getSequenceAsString() 783 + mseq[s].getSequenceAsString()); 784 if (mseq[s].getStart() <= mseq[s].getEnd()) 785 { 786 alignment[s].setEnd(mseq[s].getEnd()); 787 } 788 if (order != null) 789 { 790 order.updateSequence(mseq[s], alignment[s]); 791 } 792 } 793 } 794 } 795 else 796 { 797 // recover original alignment block or place gaps 798 if (true) 799 { 800 // recover input data 801 for (int s = 0; s < sequences.length; s++) 802 { 803 SequenceI oseq = sequences[s].getSeq(gapCharacter) 804 .getSubSequence(start, contigs[contig + 1]); 805 if (swidth < oseq.getLength()) 806 { 807 swidth = oseq.getLength(); 808 } 809 if (alignment[s] == null) 810 { 811 alignment[s] = oseq; 812 } 813 else 814 { 815 alignment[s] 816 .setSequence(alignment[s].getSequenceAsString() 817 + oseq.getSequenceAsString()); 818 if (oseq.getEnd() >= oseq.getStart()) 819 { 820 alignment[s].setEnd(oseq.getEnd()); 821 } 822 } 823 } 824 825 } 826 j++; 827 } 828 nwidth += swidth; 829 } 830 // advance to begining of visible region 831 start = contigs[contig + 1] + contigs[contig + 2]; 832 // add hidden segment to right of next region 833 for (int s = 0; s < sequences.length; s++) 834 { 835 SequenceI hseq = sequences[s].getSeq(gapCharacter) 836 .getSubSequence(contigs[contig + 1], start); 837 if (alignment[s] == null) 838 { 839 alignment[s] = hseq; 840 } 841 else 842 { 843 alignment[s].setSequence(alignment[s].getSequenceAsString() 844 + hseq.getSequenceAsString()); 845 if (hseq.getEnd() >= hseq.getStart()) 846 { 847 alignment[s].setEnd(hseq.getEnd()); 848 } 849 } 850 } 851 // mark hidden segment as hidden in the new alignment 852 hidden.hideColumns(nwidth, nwidth + contigs[contig + 2] - 1); 853 nwidth += contigs[contig + 2]; 854 } 855 // Do final segment - if it exists 856 if (j < nvismsa.length) 857 { 858 int swidth = 0; 859 if (nvismsa[j] != null) 860 { 861 SequenceI mseq[] = nvismsa[j]; 862 AlignmentOrder order = (orders != null) ? orders[j] : null; 863 swidth = mseq[0].getLength(); 864 for (int s = 0; s < mseq.length; s++) 865 { 866 if (alignment[s] == null) 867 { 868 alignment[s] = mseq[s]; 869 } 870 else 871 { 872 alignment[s].setSequence(alignment[s].getSequenceAsString() 873 + mseq[s].getSequenceAsString()); 874 if (mseq[s].getEnd() >= mseq[s].getStart()) 875 { 876 alignment[s].setEnd(mseq[s].getEnd()); 877 } 878 if (order != null) 879 { 880 order.updateSequence(mseq[s], alignment[s]); 881 } 882 } 883 } 884 } 885 else 886 { 887 if (start < owidth) 888 { 889 // recover input data or place gaps 890 if (true) 891 { 892 // recover input data 893 for (int s = 0; s < sequences.length; s++) 894 { 895 SequenceI oseq = sequences[s].getSeq(gapCharacter) 896 .getSubSequence(start, owidth + 1); 897 if (swidth < oseq.getLength()) 898 { 899 swidth = oseq.getLength(); 900 } 901 if (alignment[s] == null) 902 { 903 alignment[s] = oseq; 904 } 905 else 906 { 907 alignment[s] 908 .setSequence(alignment[s].getSequenceAsString() 909 + oseq.getSequenceAsString()); 910 if (oseq.getEnd() >= oseq.getStart()) 911 { 912 alignment[s].setEnd(oseq.getEnd()); 913 } 914 } 915 } 916 nwidth += swidth; 917 } 918 else 919 { 920 // place gaps. 921 throw new Error(MessageManager 922 .getString("error.padding_not_yet_implemented")); 923 } 924 } 925 } 926 } 927 } 928 return new Object[] { alignment, hidden }; 929 } 930 else 931 { 932 if (nvismsa.length != 1) 933 { 934 throw new Error(MessageManager.formatMessage( 935 "error.mismatch_between_visible_blocks_to_update_and_number_of_contigs_in_view", 936 new String[] 937 { Integer.valueOf(nvismsa.length).toString() })); 938 } 939 if (nvismsa[0] != null) 940 { 941 return new Object[] { nvismsa[0], new HiddenColumns() }; 942 } 943 else 944 { 945 return getAlignmentAndHiddenColumns(gapCharacter); 946 } 947 } 948 } 949 950 /** 951 * returns simple array of start end positions of visible range on alignment. 952 * vis_start and vis_end are inclusive - use 953 * SequenceI.getSubSequence(vis_start, vis_end+1) to recover visible sequence 954 * from underlying alignment. 955 * 956 * @return int[] { start_i, end_i } for 1<i<n visible regions. 957 */ getVisibleContigs()958 public int[] getVisibleContigs() 959 { 960 if (contigs != null && contigs.length > 0) 961 { 962 int start = 0; 963 int nvis = 0; 964 int fwidth = width; 965 for (int contig = 0; contig < contigs.length; contig += 3) 966 { 967 if ((contigs[contig + 1] - start) > 0) 968 { 969 nvis++; 970 } 971 fwidth += contigs[contig + 2]; // end up with full region width 972 // (including hidden regions) 973 start = contigs[contig + 1] + contigs[contig + 2]; 974 } 975 if (start < fwidth) 976 { 977 nvis++; 978 } 979 int viscontigs[] = new int[nvis * 2]; 980 nvis = 0; 981 start = 0; 982 for (int contig = 0; contig < contigs.length; contig += 3) 983 { 984 if ((contigs[contig + 1] - start) > 0) 985 { 986 viscontigs[nvis] = start; 987 viscontigs[nvis + 1] = contigs[contig + 1] - 1; // end is inclusive 988 nvis += 2; 989 } 990 start = contigs[contig + 1] + contigs[contig + 2]; 991 } 992 if (start < fwidth) 993 { 994 viscontigs[nvis] = start; 995 viscontigs[nvis + 1] = fwidth - 1; // end is inclusive 996 nvis += 2; 997 } 998 return viscontigs; 999 } 1000 else 1001 { 1002 return new int[] { 0, width - 1 }; 1003 } 1004 } 1005 1006 /** 1007 * 1008 * @return position of first visible column of AlignmentView within its 1009 * parent's alignment reference frame 1010 */ getAlignmentOrigin()1011 public int getAlignmentOrigin() 1012 { 1013 return firstCol; 1014 } 1015 1016 /** 1017 * compute a deletion map for the current view according to the given 1018 * gap/match map 1019 * 1020 * @param gapMap 1021 * (as returned from SequenceI.gapMap()) 1022 * @return int[] {intersection of visible regions with gapMap) 1023 */ getVisibleContigMapFor(int[] gapMap)1024 public int[] getVisibleContigMapFor(int[] gapMap) 1025 { 1026 int[] delMap = null; 1027 int[] viscontigs = getVisibleContigs(); 1028 int spos = 0; 1029 int i = 0; 1030 if (viscontigs != null) 1031 { 1032 // viscontigs maps from a subset of the gapMap to the gapMap, so it will 1033 // always be equal to or shorter than gapMap 1034 delMap = new int[gapMap.length]; 1035 for (int contig = 0; contig < viscontigs.length; contig += 2) 1036 { 1037 1038 while (spos < gapMap.length && gapMap[spos] < viscontigs[contig]) 1039 { 1040 spos++; 1041 } 1042 while (spos < gapMap.length 1043 && gapMap[spos] <= viscontigs[contig + 1]) 1044 { 1045 delMap[i++] = spos++; 1046 } 1047 } 1048 int tmap[] = new int[i]; 1049 System.arraycopy(delMap, 0, tmap, 0, i); 1050 delMap = tmap; 1051 } 1052 return delMap; 1053 } 1054 1055 /** 1056 * apply the getSeq(gc) method to each sequence cigar, and return the array of 1057 * edited sequences, optionally with hidden regions removed. 1058 * 1059 * @param gc 1060 * gap character to use for insertions 1061 * @param delete 1062 * remove hidden regions from sequences. Note: currently implemented 1063 * in a memory inefficient way - space needed is 2*result set for 1064 * deletion 1065 * 1066 * @return SequenceI[] 1067 */ getEditedSequences(char gc, boolean delete)1068 public SequenceI[] getEditedSequences(char gc, boolean delete) 1069 { 1070 SeqCigar[] msf = getSequences(); 1071 SequenceI[] aln = new SequenceI[msf.length]; 1072 for (int i = 0, j = msf.length; i < j; i++) 1073 { 1074 aln[i] = msf[i].getSeq(gc); 1075 } 1076 if (delete) 1077 { 1078 String[] sqs = getSequenceStrings(gc); 1079 for (int i = 0; i < sqs.length; i++) 1080 { 1081 aln[i].setSequence(sqs[i]); 1082 sqs[i] = null; 1083 } 1084 } 1085 return aln; 1086 } 1087 summariseAlignmentView(AlignmentView view, PrintStream os)1088 public static void summariseAlignmentView(AlignmentView view, 1089 PrintStream os) 1090 { 1091 os.print("View has " + view.sequences.length + " of which "); 1092 if (view.selected == null) 1093 { 1094 os.print("None"); 1095 } 1096 else 1097 { 1098 os.print(" " + view.selected.size()); 1099 } 1100 os.println(" are selected."); 1101 os.print("View is " + view.getWidth() + " columns wide"); 1102 int viswid = 0; 1103 int[] contigs = view.getContigs(); 1104 if (contigs != null) 1105 { 1106 viswid = view.width; 1107 for (int i = 0; i < contigs.length; i += 3) 1108 { 1109 viswid += contigs[i + 2]; 1110 } 1111 os.println("with " + viswid + " visible columns spread over " 1112 + contigs.length / 3 + " regions."); 1113 } 1114 else 1115 { 1116 viswid = view.width; 1117 os.println("."); 1118 } 1119 if (view.scGroups != null) 1120 { 1121 os.println("There are " + view.scGroups.size() 1122 + " groups defined on the view."); 1123 for (int g = 0; g < view.scGroups.size(); g++) 1124 { 1125 ScGroup sgr = view.scGroups.get(g); 1126 os.println("Group " + g + ": Name = " + sgr.sg.getName() 1127 + " Contains " + sgr.seqs.size() + " Seqs."); 1128 os.println("This group runs from " + sgr.sg.getStartRes() + " to " 1129 + sgr.sg.getEndRes()); 1130 for (int s = 0; s < sgr.seqs.size(); s++) 1131 { 1132 // JBPnote this should be a unit test for ScGroup 1133 if (!sgr.seqs.get(s).isMemberOf(sgr)) 1134 { 1135 os.println("** WARNING: sequence " + sgr.seqs.get(s).toString() 1136 + " is not marked as member of group."); 1137 } 1138 } 1139 } 1140 AlignmentI visal = view.getVisibleAlignment('-'); 1141 if (visal != null) 1142 { 1143 os.println("Vis. alignment is " + visal.getWidth() 1144 + " wide and has " + visal.getHeight() + " seqs."); 1145 if (visal.getGroups() != null && visal.getGroups().size() > 0) 1146 { 1147 1148 int i = 1; 1149 for (SequenceGroup sg : visal.getGroups()) 1150 { 1151 os.println("Group " + (i++) + " begins at column " 1152 + sg.getStartRes() + " and ends at " + sg.getEndRes()); 1153 } 1154 } 1155 } 1156 } 1157 } 1158 testSelectionViews(AlignmentI alignment, HiddenColumns hidden, SequenceGroup selection)1159 public static void testSelectionViews(AlignmentI alignment, 1160 HiddenColumns hidden, SequenceGroup selection) 1161 { 1162 System.out.println("Testing standard view creation:\n"); 1163 AlignmentView view = null; 1164 try 1165 { 1166 System.out.println( 1167 "View with no hidden columns, no limit to selection, no groups to be collected:"); 1168 view = new AlignmentView(alignment, hidden, selection, false, false, 1169 false); 1170 summariseAlignmentView(view, System.out); 1171 1172 } catch (Exception e) 1173 { 1174 e.printStackTrace(); 1175 System.err.println( 1176 "Failed to generate alignment with selection but no groups marked."); 1177 } 1178 try 1179 { 1180 System.out.println( 1181 "View with no hidden columns, no limit to selection, and all groups to be collected:"); 1182 view = new AlignmentView(alignment, hidden, selection, false, false, 1183 true); 1184 summariseAlignmentView(view, System.out); 1185 } catch (Exception e) 1186 { 1187 e.printStackTrace(); 1188 System.err.println( 1189 "Failed to generate alignment with selection marked but no groups marked."); 1190 } 1191 try 1192 { 1193 System.out.println( 1194 "View with no hidden columns, limited to selection and no groups to be collected:"); 1195 view = new AlignmentView(alignment, hidden, selection, false, true, 1196 false); 1197 summariseAlignmentView(view, System.out); 1198 } catch (Exception e) 1199 { 1200 e.printStackTrace(); 1201 System.err.println( 1202 "Failed to generate alignment with selection restricted but no groups marked."); 1203 } 1204 try 1205 { 1206 System.out.println( 1207 "View with no hidden columns, limited to selection, and all groups to be collected:"); 1208 view = new AlignmentView(alignment, hidden, selection, false, true, 1209 true); 1210 summariseAlignmentView(view, System.out); 1211 } catch (Exception e) 1212 { 1213 e.printStackTrace(); 1214 System.err.println( 1215 "Failed to generate alignment with selection restricted and groups marked."); 1216 } 1217 try 1218 { 1219 System.out.println( 1220 "View *with* hidden columns, no limit to selection, no groups to be collected:"); 1221 view = new AlignmentView(alignment, hidden, selection, true, false, 1222 false); 1223 summariseAlignmentView(view, System.out); 1224 } catch (Exception e) 1225 { 1226 e.printStackTrace(); 1227 System.err.println( 1228 "Failed to generate alignment with selection but no groups marked."); 1229 } 1230 try 1231 { 1232 System.out.println( 1233 "View *with* hidden columns, no limit to selection, and all groups to be collected:"); 1234 view = new AlignmentView(alignment, hidden, selection, true, false, 1235 true); 1236 summariseAlignmentView(view, System.out); 1237 } catch (Exception e) 1238 { 1239 e.printStackTrace(); 1240 System.err.println( 1241 "Failed to generate alignment with selection marked but no groups marked."); 1242 } 1243 try 1244 { 1245 System.out.println( 1246 "View *with* hidden columns, limited to selection and no groups to be collected:"); 1247 view = new AlignmentView(alignment, hidden, selection, true, true, 1248 false); 1249 summariseAlignmentView(view, System.out); 1250 } catch (Exception e) 1251 { 1252 e.printStackTrace(); 1253 System.err.println( 1254 "Failed to generate alignment with selection restricted but no groups marked."); 1255 } 1256 try 1257 { 1258 System.out.println( 1259 "View *with* hidden columns, limited to selection, and all groups to be collected:"); 1260 view = new AlignmentView(alignment, hidden, selection, true, true, 1261 true); 1262 summariseAlignmentView(view, System.out); 1263 } catch (Exception e) 1264 { 1265 e.printStackTrace(); 1266 System.err.println( 1267 "Failed to generate alignment with selection restricted and groups marked."); 1268 } 1269 1270 } 1271 } 1272