From 0c37e7cea9a7028130b6f2164961af24c3c48ec2 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Mon, 17 Aug 2015 11:16:52 +0100 Subject: [PATCH] JAL-1800 SearchResults.equals() to allow smarter mouseover of codons --- src/jalview/datamodel/SearchResults.java | 63 +++++++++++++- test/jalview/datamodel/MatchTest.java | 67 ++++++++++++++ test/jalview/datamodel/SearchResultsTest.java | 116 ++++++++++++++++++++++++- 3 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 test/jalview/datamodel/MatchTest.java diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index 2a33f6c..3948ba0 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -36,6 +36,10 @@ public class SearchResults private List matches = new ArrayList(); + /** + * One match consists of a sequence reference, start and end positions. + * Discontiguous ranges in a sequence require two or more Match objects. + */ public class Match { SequenceI sequence; @@ -92,13 +96,44 @@ public class SearchResults // convert start/end to base 0 (with bounds check) final int from = Math.max(start - 1, 0); final int to = Math.min(end, chars.length + 1); - return String.valueOf(Arrays.copyOfRange(chars, from, to)); + // return String.valueOf(Arrays.copyOfRange(chars, from, to)); + return String.valueOf(from) + + String.valueOf(Arrays.copyOfRange(chars, from, to)); } public void setSequence(SequenceI seq) { this.sequence = seq; } + + /** + * Hashcode is the hashcode of the matched sequence plus a hash of start and + * end positions. Match objects that pass the test for equals are guaranteed + * to have the same hashcode. + */ + @Override + public int hashCode() + { + int hash = sequence == null ? 0 : sequence.hashCode(); + hash += 31 * start; + hash += 67 * end; + return hash; + } + + /** + * Two Match objects are equal if they are for the same sequence, start and + * end positions + */ + @Override + public boolean equals(Object obj) + { + if (obj == null || !(obj instanceof Match)) + { + return false; + } + Match m = (Match) obj; + return (this.sequence == m.sequence && this.start == m.start && this.end == m.end); + } } /** @@ -281,4 +316,30 @@ public class SearchResults } return result.toString(); } + + /** + * Hashcode is has derived from the list of matches. This ensures that when + * two SearchResults objects satisfy the test for equals(), then they have the + * same hashcode. + */ + @Override + public int hashCode() + { + return matches.hashCode(); + } + + /** + * Two SearchResults are considered equal if they contain the same matches in + * the same order. + */ + @Override + public boolean equals(Object obj) + { + if (obj == null || !(obj instanceof SearchResults)) + { + return false; + } + SearchResults sr = (SearchResults) obj; + return ((ArrayList) this.matches).equals(sr.matches); + } } diff --git a/test/jalview/datamodel/MatchTest.java b/test/jalview/datamodel/MatchTest.java new file mode 100644 index 0000000..9b5e97b --- /dev/null +++ b/test/jalview/datamodel/MatchTest.java @@ -0,0 +1,67 @@ +package jalview.datamodel; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertTrue; + +import jalview.datamodel.SearchResults.Match; + +import org.testng.annotations.Test; + +public class MatchTest +{ + + @Test(groups = { "Functional" }) + public void testToString() + { + SequenceI seq = new Sequence("", "abcdefghijklm"); + Match m = new SearchResults().new Match(seq, 3, 5); + assertEquals("2cde", m.toString()); + } + + @Test(groups = { "Functional" }) + public void testEquals() + { + SequenceI seq1 = new Sequence("", "abcdefghijklm"); + SequenceI seq2 = new Sequence("", "abcdefghijklm"); + SearchResults sr1 = new SearchResults(); + SearchResults sr2 = new SearchResults(); + + assertFalse(sr1.equals(null)); + assertFalse(sr1.equals(seq1)); + assertTrue(sr1.equals(sr1)); + assertTrue(sr1.equals(sr2)); + assertTrue(sr2.equals(sr1)); + + sr1.addResult(seq1, 1, 1); + assertFalse(sr1.equals(sr2)); + assertFalse(sr2.equals(sr1)); + + sr2.addResult(seq1, 1, 1); + assertTrue(sr1.equals(sr2)); + assertTrue(sr2.equals(sr1)); + + /* + * same match but on different sequences - not equal + */ + SearchResults sr3 = new SearchResults(); + sr3.addResult(seq2, 1, 1); + assertFalse(sr1.equals(sr3)); + assertFalse(sr3.equals(sr1)); + + /* + * same sequence but different end position - not equal + */ + sr1.addResult(seq1, 3, 4); + sr2.addResult(seq1, 3, 5); + assertFalse(sr1.equals(sr2)); + + /* + * same sequence but different start position - not equal + */ + sr1 = new SearchResults(); + sr2 = new SearchResults(); + sr1.addResult(seq1, 3, 4); + sr2.addResult(seq1, 2, 4); + assertFalse(sr1.equals(sr2)); + } +} diff --git a/test/jalview/datamodel/SearchResultsTest.java b/test/jalview/datamodel/SearchResultsTest.java index b576569..983bd34 100644 --- a/test/jalview/datamodel/SearchResultsTest.java +++ b/test/jalview/datamodel/SearchResultsTest.java @@ -1,6 +1,8 @@ package jalview.datamodel; import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; @@ -13,12 +15,120 @@ public class SearchResultsTest SequenceI seq = new Sequence("", "abcdefghijklm"); SearchResults sr = new SearchResults(); sr.addResult(seq, 1, 1); - assertEquals("a", sr.toString()); + assertEquals("0a", sr.toString()); sr.addResult(seq, 3, 5); - assertEquals("acde", sr.toString()); + assertEquals("0a2cde", sr.toString()); seq = new Sequence("", "pqrstuvwxy"); sr.addResult(seq, 6, 7); - assertEquals("acdeuv", sr.toString()); + assertEquals("0a2cde5uv", sr.toString()); + } + + @Test(groups = { "Functional" }) + public void testEquals() + { + SequenceI seq1 = new Sequence("", "abcdefghijklm"); + SearchResults sr1 = new SearchResults(); + SearchResults sr2 = new SearchResults(); + + assertFalse(sr1.equals(null)); // null object + assertFalse(sr1.equals(seq1)); // wrong type + assertTrue(sr1.equals(sr1)); // self + assertTrue(sr1.equals(sr2)); // empty + assertTrue(sr2.equals(sr1)); // reflexive + + /* + * only one result is not empty + */ + sr1.addResult(seq1, 1, 1); + assertTrue(sr1.equals(sr1)); + assertFalse(sr1.equals(sr2)); + assertFalse(sr2.equals(sr1)); + + /* + * both the same + */ + sr2.addResult(seq1, 1, 1); + assertTrue(sr1.equals(sr2)); + assertTrue(sr2.equals(sr1)); + + /* + * both have three matches + */ + sr1.addResult(seq1, 3, 4); + sr1.addResult(seq1, 6, 8); + sr2.addResult(seq1, 3, 4); + sr2.addResult(seq1, 6, 8); + assertTrue(sr1.equals(sr1)); + assertTrue(sr2.equals(sr2)); + assertTrue(sr1.equals(sr2)); + assertTrue(sr2.equals(sr1)); + } + + /** + * Matches that are similar but for distinct sequences are not equal + */ + @Test(groups = { "Functional" }) + public void testEquals_distinctSequences() + { + SequenceI seq1 = new Sequence("", "abcdefghijklm"); + SequenceI seq2 = new Sequence("", "abcdefghijklm"); + SearchResults sr1 = new SearchResults(); + SearchResults sr2 = new SearchResults(); + + sr1.addResult(seq1, 1, 1); + sr2.addResult(seq2, 1, 1); + assertFalse(sr1.equals(sr2)); + assertFalse(sr2.equals(sr1)); + } + + /** + * Matches that are the same except for ordering are not equal + */ + @Test(groups = { "Functional" }) + public void testEquals_orderDiffers() + { + SequenceI seq1 = new Sequence("", "abcdefghijklm"); + SearchResults sr1 = new SearchResults(); + SearchResults sr2 = new SearchResults(); + + sr1.addResult(seq1, 1, 1); + sr1.addResult(seq1, 2, 2); + sr2.addResult(seq1, 2, 2); + sr2.addResult(seq1, 1, 1); + assertFalse(sr1.equals(sr2)); + assertFalse(sr2.equals(sr1)); + } + + /** + * Verify that hashCode matches for equal objects + */ + @Test(groups = { "Functional" }) + public void testHashcode() + { + SequenceI seq1 = new Sequence("", "abcdefghijklm"); + SearchResults sr1 = new SearchResults(); + SearchResults sr2 = new SearchResults(); + + /* + * both empty + */ + assertEquals(sr1.hashCode(), sr2.hashCode()); + + /* + * both one match + */ + sr1.addResult(seq1, 1, 1); + sr2.addResult(seq1, 1, 1); + assertEquals(sr1.hashCode(), sr2.hashCode()); + + /* + * both three matches + */ + sr1.addResult(seq1, 3, 4); + sr1.addResult(seq1, 6, 8); + sr2.addResult(seq1, 3, 4); + sr2.addResult(seq1, 6, 8); + assertEquals(sr1.hashCode(), sr2.hashCode()); } } -- 1.7.10.2