From: kiramt Date: Fri, 19 Jan 2018 12:36:12 +0000 (+0000) Subject: JAL-2759 Moved propagateInsertions out of HiddenColumns X-Git-Tag: Release_2_10_4~55^2~1^2~27 X-Git-Url: http://source.jalview.org/gitweb/?p=jalview.git;a=commitdiff_plain;h=cfed9d8a9e10be992877d6097260758ee5563630 JAL-2759 Moved propagateInsertions out of HiddenColumns --- diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 7cc2125..b6a5e0f 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -28,10 +28,12 @@ import jalview.util.LinkedIdentityHashSet; import jalview.util.MessageManager; import java.util.ArrayList; +import java.util.BitSet; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -1923,4 +1925,107 @@ public class Alignment implements AlignmentI cs.hideList(repseq.getInsertions()); setHiddenColumns(cs); } + + @Override + public HiddenColumns propagateInsertions(SequenceI profileseq, + AlignmentView input) + { + int profsqpos = 0; + + char gc = getGapCharacter(); + Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc); + HiddenColumns nview = (HiddenColumns) alandhidden[1]; + SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos]; + return propagateInsertions(profileseq, origseq, nview); + } + + /** + * + * @param profileseq + * sequence in al which corresponds to origseq + * @param al + * alignment which is to have gaps inserted into it + * @param origseq + * sequence corresponding to profileseq which defines gap map for + * modifying al + */ + private HiddenColumns propagateInsertions(SequenceI profileseq, + SequenceI origseq, HiddenColumns hc) + { + // take the set of hidden columns, and the set of gaps in origseq, + // and remove all the hidden gaps from hiddenColumns + + // first get the gaps as a Bitset + // then calculate hidden ^ not(gap) + BitSet gaps = origseq.gapBitset(); + hc.andNot(gaps); + + // for each sequence in the alignment, except the profile sequence, + // insert gaps corresponding to each hidden region but where each hidden + // column region is shifted backwards by the number of preceding visible + // gaps update hidden columns at the same time + HiddenColumns newhidden = new HiddenColumns(); + + int numGapsBefore = 0; + int gapPosition = 0; + Iterator it = hc.iterator(); + while (it.hasNext()) + { + int[] region = it.next(); + + // get region coordinates accounting for gaps + // we can rely on gaps not being *in* hidden regions because we already + // removed those + while (gapPosition < region[0]) + { + gapPosition++; + if (gaps.get(gapPosition)) + { + numGapsBefore++; + } + } + + int left = region[0] - numGapsBefore; + int right = region[1] - numGapsBefore; + + newhidden.hideColumns(left, right); + padGaps(left, right, profileseq); + } + return newhidden; + } + + /** + * Pad gaps in all sequences in alignment except profileseq + * + * @param left + * position of first gap to insert + * @param right + * position of last gap to insert + * @param profileseq + * sequence not to pad + */ + private void padGaps(int left, int right, SequenceI profileseq) + { + char gc = getGapCharacter(); + + // make a string with number of gaps = length of hidden region + StringBuilder sb = new StringBuilder(); + for (int g = 0; g < right - left + 1; g++) + { + sb.append(gc); + } + + // loop over the sequences and pad with gaps where required + for (int s = 0, ns = getHeight(); s < ns; s++) + { + SequenceI sqobj = getSequenceAt(s); + if ((sqobj != profileseq) && (sqobj.getLength() >= left)) + { + String sq = sqobj.getSequenceAsString(); + sqobj.setSequence( + sq.substring(0, left) + sb.toString() + sq.substring(left)); + } + } + } + } diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index bdc48d0..5fb16d6 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -593,4 +593,19 @@ public interface AlignmentI extends AnnotatedCollectionI */ public void setupJPredAlignment(); + /** + * Add gaps into the sequences aligned to profileseq under the given + * AlignmentView + * + * @param profileseq + * sequence in al which sequences are aligned to + * @param input + * alignment view where sequence corresponding to profileseq is first + * entry + * @return new HiddenColumns for new alignment view, with insertions into + * profileseq marked as hidden. + */ + public HiddenColumns propagateInsertions(SequenceI profileseq, + AlignmentView input); + } diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java index 77d1c65..6e594bd 100644 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@ -153,19 +153,9 @@ public class HiddenColumns */ public void hideColumns(int start, int end) { - boolean wasAlreadyLocked = false; try { - // check if the write lock was already locked by this thread, - // as this method can be called internally in loops within HiddenColumns - if (!LOCK.isWriteLockedByCurrentThread()) - { - LOCK.writeLock().lock(); - } - else - { - wasAlreadyLocked = true; - } + LOCK.writeLock().lock(); int previndex = 0; int prevHiddenCount = 0; @@ -220,10 +210,7 @@ public class HiddenColumns } finally { - if (!wasAlreadyLocked) - { - LOCK.writeLock().unlock(); - } + LOCK.writeLock().unlock(); } } @@ -396,158 +383,8 @@ public class HiddenColumns } } - /** - * Add gaps into the sequences aligned to profileseq under the given - * AlignmentView - * - * @param profileseq - * sequence in al which sequences are aligned to - * @param al - * alignment to have gaps inserted into it - * @param input - * alignment view where sequence corresponding to profileseq is first - * entry - * @return new HiddenColumns for new alignment view, with insertions into - * profileseq marked as hidden. - */ - public static HiddenColumns propagateInsertions(SequenceI profileseq, - AlignmentI al, AlignmentView input) - { - int profsqpos = 0; - - char gc = al.getGapCharacter(); - Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc); - HiddenColumns nview = (HiddenColumns) alandhidden[1]; - SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos]; - nview.propagateInsertions(profileseq, al, origseq); - return nview; - } - - /** - * - * @param profileseq - * sequence in al which corresponds to origseq - * @param al - * alignment which is to have gaps inserted into it - * @param origseq - * sequence corresponding to profileseq which defines gap map for - * modifying al - */ - private void propagateInsertions(SequenceI profileseq, AlignmentI al, - SequenceI origseq) - { - try - { - LOCK.writeLock().lock(); - - char gc = al.getGapCharacter(); - - // take the set of hidden columns, and the set of gaps in origseq, - // and remove all the hidden gaps from hiddenColumns - - // first get the non-gaps as a Bitset - // then calculate hidden ^ not(gap) - BitSet gaps = origseq.gapBitset(); - this.andNot(gaps); - - // for each sequence in the alignment, except the profile sequence, - // insert gaps corresponding to each hidden region but where each hidden - // column region is shifted backwards by the number of preceding visible - // gaps update hidden columns at the same time - List newhidden = new ArrayList<>(); - - int numGapsBefore = 0; - int gapPosition = 0; - for (int[] region : hiddenColumns) - { - // get region coordinates accounting for gaps - // we can rely on gaps not being *in* hidden regions because we already - // removed those - while (gapPosition < region[0]) - { - gapPosition++; - if (gaps.get(gapPosition)) - { - numGapsBefore++; - } - } - - int left = region[0] - numGapsBefore; - int right = region[1] - numGapsBefore; - newhidden.add(new int[] { left, right }); - // make a string with number of gaps = length of hidden region - StringBuilder sb = new StringBuilder(); - for (int s = 0; s < right - left + 1; s++) - { - sb.append(gc); - } - padGaps(sb, left, profileseq, al); - } - hiddenColumns = newhidden; - cursor.resetCursor(hiddenColumns); - numColumns = 0; - } finally - { - LOCK.writeLock().unlock(); - } - } - - /** - * Pad gaps in all sequences in alignment except profileseq - * - * @param sb - * gap string to insert - * @param left - * position to insert at - * @param profileseq - * sequence not to pad - * @param al - * alignment to pad sequences in - */ - private void padGaps(StringBuilder sb, int pos, SequenceI profileseq, - AlignmentI al) - { - // loop over the sequences and pad with gaps where required - for (int s = 0, ns = al.getHeight(); s < ns; s++) - { - SequenceI sqobj = al.getSequenceAt(s); - if (sqobj != profileseq) - { - String sq = al.getSequenceAt(s).getSequenceAsString(); - if (sq.length() <= pos) - { - // pad sequence - int diff = pos - sq.length() - 1; - if (diff > 0) - { - // pad gaps - sq = sq + sb; - while ((diff = pos - sq.length() - 1) > 0) - { - if (diff >= sb.length()) - { - sq += sb.toString(); - } - else - { - char[] buf = new char[diff]; - sb.getChars(0, diff, buf, 0); - sq += buf.toString(); - } - } - } - sq += sb.toString(); - } - else - { - al.getSequenceAt(s).setSequence( - sq.substring(0, pos) + sb.toString() + sq.substring(pos)); - } - } - } - } /* * Methods which only need read access to the hidden columns collection. @@ -1199,14 +1036,14 @@ public class HiddenColumns /** * - * @param inserts + * @param updates * BitSet where hidden columns will be marked */ - private void andNot(BitSet updates) + protected void andNot(BitSet updates) { try { - LOCK.readLock().lock(); + LOCK.writeLock().lock(); BitSet hiddenBitSet = new BitSet(); for (int[] range : hiddenColumns) @@ -1218,7 +1055,7 @@ public class HiddenColumns hideColumns(hiddenBitSet); } finally { - LOCK.readLock().unlock(); + LOCK.writeLock().unlock(); } } diff --git a/src/jalview/ws/jws1/JPredThread.java b/src/jalview/ws/jws1/JPredThread.java index a239625..23d9eb0 100644 --- a/src/jalview/ws/jws1/JPredThread.java +++ b/src/jalview/ws/jws1/JPredThread.java @@ -235,8 +235,7 @@ class JPredThread extends JWS1Thread implements WSClientI { // Adjust input view for gaps // propagate insertions into profile - alhidden = HiddenColumns.propagateInsertions(profileseq, al, - input); + alhidden = al.propagateInsertions(profileseq, input); } } } diff --git a/test/jalview/datamodel/AlignmentTest.java b/test/jalview/datamodel/AlignmentTest.java index 4b5d096..2fed815 100644 --- a/test/jalview/datamodel/AlignmentTest.java +++ b/test/jalview/datamodel/AlignmentTest.java @@ -34,6 +34,7 @@ import jalview.io.DataSourceType; import jalview.io.FileFormat; import jalview.io.FileFormatI; import jalview.io.FormatAdapter; +import jalview.util.Comparison; import jalview.util.MapList; import java.io.IOException; @@ -1321,4 +1322,119 @@ public class AlignmentTest // todo test coverage for annotations, mappings, groups, // hidden sequences, properties } + + @Test(groups = "Functional") + public void testPropagateInsertions() + { + // create an alignment with no gaps - this will be the profile seq and other + // JPRED seqs + AlignmentGenerator gen = new AlignmentGenerator(false); + AlignmentI al = gen.generate(25, 10, 1234, 0, 0); + + // get the profileseq + SequenceI profileseq = al.getSequenceAt(0); + SequenceI gappedseq = new Sequence(profileseq); + gappedseq.insertCharAt(5, al.getGapCharacter()); + gappedseq.insertCharAt(6, al.getGapCharacter()); + gappedseq.insertCharAt(7, al.getGapCharacter()); + gappedseq.insertCharAt(8, al.getGapCharacter()); + + // force different kinds of padding + al.getSequenceAt(3).deleteChars(2, 23); + al.getSequenceAt(4).deleteChars(2, 27); + al.getSequenceAt(5).deleteChars(10, 27); + + // create an alignment view with the gapped sequence + SequenceI[] seqs = new SequenceI[1]; + seqs[0] = gappedseq; + AlignmentI newal = new Alignment(seqs); + HiddenColumns hidden = new HiddenColumns(); + hidden.hideColumns(15, 17); + + AlignmentView view = new AlignmentView(newal, hidden, null, true, false, + false); + + // confirm that original contigs are as expected + Iterator visible = hidden.getVisContigsIterator(0, 25, false); + int[] region = visible.next(); + assertEquals("[0, 14]", Arrays.toString(region)); + region = visible.next(); + assertEquals("[18, 24]", Arrays.toString(region)); + + // propagate insertions + HiddenColumns result = al.propagateInsertions(profileseq, view); + + // confirm that the contigs have changed to account for the gaps + visible = result.getVisContigsIterator(0, 25, false); + region = visible.next(); + assertEquals("[0, 10]", Arrays.toString(region)); + region = visible.next(); + assertEquals("[14, 24]", Arrays.toString(region)); + + // confirm the alignment has been changed so that the other sequences have + // gaps inserted where the columns are hidden + assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[10])); + assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[11])); + assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[12])); + assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[13])); + assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[14])); + + } + + @Test(groups = "Functional") + public void testPropagateInsertionsOverlap() + { + // test propagateInsertions where gaps and hiddenColumns overlap + + // create an alignment with no gaps - this will be the profile seq and other + // JPRED seqs + AlignmentGenerator gen = new AlignmentGenerator(false); + AlignmentI al = gen.generate(20, 10, 1234, 0, 0); + + // get the profileseq + SequenceI profileseq = al.getSequenceAt(0); + SequenceI gappedseq = new Sequence(profileseq); + gappedseq.insertCharAt(5, al.getGapCharacter()); + gappedseq.insertCharAt(6, al.getGapCharacter()); + gappedseq.insertCharAt(7, al.getGapCharacter()); + gappedseq.insertCharAt(8, al.getGapCharacter()); + + // create an alignment view with the gapped sequence + SequenceI[] seqs = new SequenceI[1]; + seqs[0] = gappedseq; + AlignmentI newal = new Alignment(seqs); + + // hide columns so that some overlap with the gaps + HiddenColumns hidden = new HiddenColumns(); + hidden.hideColumns(7, 10); + + AlignmentView view = new AlignmentView(newal, hidden, null, true, false, + false); + + // confirm that original contigs are as expected + Iterator visible = hidden.getVisContigsIterator(0, 20, false); + int[] region = visible.next(); + assertEquals("[0, 6]", Arrays.toString(region)); + region = visible.next(); + assertEquals("[11, 19]", Arrays.toString(region)); + assertFalse(visible.hasNext()); + + // propagate insertions + HiddenColumns result = al.propagateInsertions(profileseq, view); + + // confirm that the contigs have changed to account for the gaps + visible = result.getVisContigsIterator(0, 20, false); + region = visible.next(); + assertEquals("[0, 4]", Arrays.toString(region)); + region = visible.next(); + assertEquals("[7, 19]", Arrays.toString(region)); + assertFalse(visible.hasNext()); + + // confirm the alignment has been changed so that the other sequences have + // gaps inserted where the columns are hidden + assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[4])); + assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[5])); + assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[6])); + assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[7])); + } } diff --git a/test/jalview/datamodel/HiddenColumnsTest.java b/test/jalview/datamodel/HiddenColumnsTest.java index 151e31d..07a4810 100644 --- a/test/jalview/datamodel/HiddenColumnsTest.java +++ b/test/jalview/datamodel/HiddenColumnsTest.java @@ -26,7 +26,6 @@ import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import jalview.analysis.AlignmentGenerator; -import jalview.util.Comparison; import java.util.Arrays; import java.util.BitSet; @@ -859,123 +858,6 @@ public class HiddenColumnsTest } @Test(groups = "Functional") - public void testPropagateInsertions() - { - // create an alignment with no gaps - this will be the profile seq and other - // JPRED seqs - AlignmentGenerator gen = new AlignmentGenerator(false); - AlignmentI al = gen.generate(25, 10, 1234, 0, 0); - - // get the profileseq - SequenceI profileseq = al.getSequenceAt(0); - SequenceI gappedseq = new Sequence(profileseq); - gappedseq.insertCharAt(5, al.getGapCharacter()); - gappedseq.insertCharAt(6, al.getGapCharacter()); - gappedseq.insertCharAt(7, al.getGapCharacter()); - gappedseq.insertCharAt(8, al.getGapCharacter()); - - // force different kinds of padding - al.getSequenceAt(3).deleteChars(2, 23); - al.getSequenceAt(4).deleteChars(2, 27); - al.getSequenceAt(5).deleteChars(10, 27); - - // create an alignment view with the gapped sequence - SequenceI[] seqs = new SequenceI[1]; - seqs[0] = gappedseq; - AlignmentI newal = new Alignment(seqs); - HiddenColumns hidden = new HiddenColumns(); - hidden.hideColumns(15, 17); - - AlignmentView view = new AlignmentView(newal, hidden, null, true, false, - false); - - // confirm that original contigs are as expected - Iterator visible = hidden.getVisContigsIterator(0, 25, false); - int[] region = visible.next(); - assertEquals("[0, 14]", Arrays.toString(region)); - region = visible.next(); - assertEquals("[18, 24]", Arrays.toString(region)); - - // propagate insertions - HiddenColumns result = HiddenColumns.propagateInsertions(profileseq, al, - view); - - // confirm that the contigs have changed to account for the gaps - visible = result.getVisContigsIterator(0, 25, false); - region = visible.next(); - assertEquals("[0, 10]", Arrays.toString(region)); - region = visible.next(); - assertEquals("[14, 24]", Arrays.toString(region)); - - // confirm the alignment has been changed so that the other sequences have - // gaps inserted where the columns are hidden - assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[10])); - assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[11])); - assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[12])); - assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[13])); - assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[14])); - - } - - @Test(groups = "Functional") - public void testPropagateInsertionsOverlap() - { - // test propagateInsertions where gaps and hiddenColumns overlap - - // create an alignment with no gaps - this will be the profile seq and other - // JPRED seqs - AlignmentGenerator gen = new AlignmentGenerator(false); - AlignmentI al = gen.generate(20, 10, 1234, 0, 0); - - // get the profileseq - SequenceI profileseq = al.getSequenceAt(0); - SequenceI gappedseq = new Sequence(profileseq); - gappedseq.insertCharAt(5, al.getGapCharacter()); - gappedseq.insertCharAt(6, al.getGapCharacter()); - gappedseq.insertCharAt(7, al.getGapCharacter()); - gappedseq.insertCharAt(8, al.getGapCharacter()); - - // create an alignment view with the gapped sequence - SequenceI[] seqs = new SequenceI[1]; - seqs[0] = gappedseq; - AlignmentI newal = new Alignment(seqs); - - // hide columns so that some overlap with the gaps - HiddenColumns hidden = new HiddenColumns(); - hidden.hideColumns(7, 10); - - AlignmentView view = new AlignmentView(newal, hidden, null, true, false, - false); - - // confirm that original contigs are as expected - Iterator visible = hidden.getVisContigsIterator(0, 20, false); - int[] region = visible.next(); - assertEquals("[0, 6]", Arrays.toString(region)); - region = visible.next(); - assertEquals("[11, 19]", Arrays.toString(region)); - assertFalse(visible.hasNext()); - - // propagate insertions - HiddenColumns result = HiddenColumns.propagateInsertions(profileseq, al, - view); - - // confirm that the contigs have changed to account for the gaps - visible = result.getVisContigsIterator(0, 20, false); - region = visible.next(); - assertEquals("[0, 4]", Arrays.toString(region)); - region = visible.next(); - assertEquals("[7, 19]", Arrays.toString(region)); - assertFalse(visible.hasNext()); - - // confirm the alignment has been changed so that the other sequences have - // gaps inserted where the columns are hidden - assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[4])); - assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[5])); - assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[6])); - assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[7])); - } - - @Test(groups = "Functional") public void testHasHiddenColumns() { HiddenColumns h = new HiddenColumns();