* @return
*/
boolean contains(SequenceI seq, int start, int end);
+
+ /**
+ *
+ * @param seq
+ * @param from - first position to highlight
+ * @param to - last position to highlight (assumed higher than from)
+ * @return true iff from-to intersects or marks positions either side of start/end
+ */
+ boolean adjacent(SequenceI seq, int from, int to);
}
\ No newline at end of file
import java.util.ArrayList;
import java.util.BitSet;
+import java.util.Collections;
import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import com.google.common.collect.Lists;
/**
* Holds a list of search result matches, where each match is a contiguous
{
private int count;
- private List<SearchResultMatchI> matches = new ArrayList<>();
+ private SortedSet<SearchResultMatchI> matches = new TreeSet<>();
/**
* 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 implements SearchResultMatchI
+ public class Match implements SearchResultMatchI, Comparable<SearchResultMatchI>
{
final SequenceI sequence;
{
return (sequence == seq && start <= from && end >= to);
}
+ @Override
+ public boolean adjacent(SequenceI seq, int from, int to)
+ {
+ return (sequence == seq && ((start <= from && end >= to) || (from<=(end+1) && to >=(end+1)) || (from<=(start-1) && to>=(start-1))));
+ }
+
+ @Override
+ public int compareTo(SearchResultMatchI o)
+ {
+ if (start<o.getStart())
+ {
+ return -1;
+ }
+ if (start > o.getStart())
+ {
+ return +1;
+ }
+ if (end < o.getEnd())
+ {
+ return -1;
+ }
+ if (end > o.getEnd())
+ {
+ return +1;
+ }
+ if (sequence!=o.getSequence())
+ {
+ int hashc =sequence.hashCode(),oseq=o.getSequence().hashCode();
+ return (hashc < oseq) ? -1 : 1;
+ }
+ return 0;
+ }
+
}
@Override
count = beforeCount + 1;
}
}
+
@Override
+ public boolean appendResult(SequenceI sequence, int start, int end)
+ {
+
+ Match m = new Match(sequence, start, end);
+ Match toAdd=null;
+
+ if (matches.contains(m))
+ {
+ return false;
+ }
+ boolean appending=false;
+
+ // we dynamically maintain an interval to add as we test each range in the list
+
+ int cstart=start,cend=end;
+ List<SearchResultMatchI> toRemove=new ArrayList<>();
+ for (SearchResultMatchI thatm:matches)
+ {
+ if (thatm.getSequence()==sequence)
+ {
+ if (thatm.adjacent(sequence, cstart, cend))
+ {
+ // update the match to add with the adjacent start/end
+ start = Math.min(m.start, thatm.getStart());
+ end = Math.max(m.end, thatm.getEnd());
+ // and check if we keep or remove the old one
+ if (thatm.getStart()!=start || thatm.getEnd()!=end)
+ {
+ toRemove.add(thatm);
+ count--;
+ cstart = start;
+ cend = end;
+ appending=true;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ matches.removeAll(toRemove);
+ {
+ matches.add(new Match(sequence,cstart,cend));
+ count++;
+ }
+ return appending;
+ }
+ @Override
public boolean involvesSequence(SequenceI sequence)
{
final int start = sequence.getStart();
@Override
public List<SearchResultMatchI> getResults()
{
- return matches;
+ return List.copyOf(matches);
}
/**
}
return seqs;
}
+
}
*/
void addResult(SequenceI seq, int[] positions);
+
+ /**
+ * Adds the given start/end region to this search result. If sequence already
+ * has a search result and the range is adjacent to already highlighted
+ * positions, they will be merged
+ *
+ * @param sequence
+ * @param start
+ * @param end
+ * @return true if an existing range was updated with this one
+ */
+ boolean appendResult(SequenceI sequence, int start, int end);
+
/**
* adds all match results in the argument to this set
*
int indexpos = sm.getSeqPos(atom.getPdbResNum());
if (lastipos != indexpos || lastseq != sm.sequence)
{
- results.addResult(sm.sequence, indexpos, indexpos);
+ results.appendResult(sm.sequence, indexpos, indexpos);
lastipos = indexpos;
lastseq = sm.sequence;
// construct highlighted sequence list
assertFalse(m.contains(null, 3, 3));
}
+ @Test(groups = { "Functional" })
+ public void testMatchAdjacent()
+ {
+ SequenceI seq1 = new Sequence("", "abcdefghijklm");
+ SequenceI seq2 = new Sequence("", "abcdefghijklm");
+ SearchResultMatchI m = new SearchResults().new Match(seq1, 2, 5);
+
+ assertTrue(m.adjacent(seq1, 2, 5));
+ assertTrue(m.adjacent(seq1, 3, 5));
+ assertTrue(m.adjacent(seq1, 2, 4));
+ assertTrue(m.adjacent(seq1, 3, 3));
+
+ assertTrue(m.adjacent(seq1, 2, 6));
+ assertTrue(m.adjacent(seq1, 1, 5));
+ assertTrue(m.adjacent(seq1, 1, 8));
+ assertFalse(m.adjacent(seq1, 0, 0));
+ assertFalse(m.adjacent(seq1, 7, 8));
+ assertTrue(m.adjacent(seq1, 6, 8));
+ assertTrue(m.adjacent(seq1, 5, 8));
+ assertTrue(m.adjacent(seq1, 0, 1));
+
+
+ assertFalse(m.adjacent(seq2, 3, 3));
+ assertFalse(m.adjacent(null, 3, 3));
+ }
+
/**
* test markColumns for creating column selections
*/
}
/**
+ * Test to verify appending creates a minimal set of results
+ */
+ @Test(groups = { "Functional" })
+ public void testAppendResult()
+ {
+ SequenceI seq1 = new Sequence("", "abcdefghijklm");
+ SearchResultsI sr = new SearchResults();
+ sr.appendResult(seq1, 3, 5);
+ assertEquals(1, sr.getCount());
+ sr.appendResult(seq1, 3, 6);
+ assertEquals(1, sr.getCount());
+ sr.appendResult(seq1, 8, 8);
+ assertEquals(2, sr.getCount());
+ sr.appendResult(seq1, 7, 7);
+ assertEquals(1, sr.getCount());
+ }
+ /**
* Test for method that checks if search results matches a sequence region
*/
@Test(groups = { "Functional" })