X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2FSearchResults.java;h=63a87a83ac7ef825b5bc926d81a108d5ec4d6bef;hb=dfc18d64376ae95cfdbf3c112825587a8891179c;hp=f8e1c5af7a3794d2a847cac4c951f37e0213e646;hpb=7bc226b58110fa26d9dbd3f0c78095d06909ffc3;p=jalview.git diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index f8e1c5a..63a87a8 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -1,147 +1,359 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer - * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ -package jalview.datamodel; - -public class SearchResults -{ - - Match[] matches; - - /** - * This method replaces the old search results which merely - * held an alignment index of search matches. This broke - * when sequences were moved around the alignment - * @param seq Sequence - * @param start int - * @param end int - */ - public void addResult(SequenceI seq, int start, int end) - { - if (matches == null) - { - matches = new Match[] - { - new Match(seq, start, end)}; - return; - } - - int mSize = matches.length; - - Match[] tmp = new Match[mSize + 1]; - int m; - for (m = 0; m < mSize; m++) - { - tmp[m] = matches[m]; - } - - tmp[m] = new Match(seq, start, end); - - matches = tmp; - } - - /** - * This Method returns the search matches which lie between the - * start and end points of the sequence in question. It is - * optimised for returning objects for drawing on SequenceCanvas - */ - public int[] getResults(SequenceI sequence, int start, int end) - { - if (matches == null) - { - return null; - } - - int[] result = null; - int[] tmp = null; - int resultLength; - - for (int m = 0; m < matches.length; m++) - { - if (matches[m].sequence == sequence) - { - int matchStart = matches[m].sequence.findIndex(matches[m].start) - 1; - int matchEnd = matches[m].sequence.findIndex(matches[m].end) - 1; - - if (matchStart <= end && matchEnd >= start) - { - if (matchStart < start) - { - matchStart = start; - } - - if (matchEnd > end) - { - matchEnd = end; - } - - if (result == null) - { - result = new int[] - { - matchStart, matchEnd}; - } - else - { - resultLength = result.length; - tmp = new int[resultLength + 2]; - System.arraycopy(result, 0, tmp, 0, resultLength); - result = tmp; - result[resultLength] = matchStart; - result[resultLength + 1] = matchEnd; - } - } - } - } - return result; - } - - public int getSize() - { - return matches == null ? 0 : matches.length; - } - - public SequenceI getResultSequence(int index) - { - return matches[index].sequence; - } - - public int getResultStart(int index) - { - return matches[index].start; - } - - public int getResultEnd(int index) - { - return matches[index].end; - } - - class Match - { - SequenceI sequence; - int start; - int end; - - public Match(SequenceI seq, int start, int end) - { - sequence = seq; - this.start = start; - this.end = end; - } - } -} +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.datamodel; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +/** + * Holds a list of search result matches, where each match is a contiguous + * stretch of a single sequence. + * + * @author gmcarstairs amwaterhouse + * + */ +public class SearchResults implements SearchResultsI +{ + + 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 implements SearchResultMatchI + { + final SequenceI sequence; + + /** + * Start position of match in sequence (base 1) + */ + final int start; + + /** + * End position (inclusive) (base 1) + */ + final int end; + + /** + * create a Match on a range of sequence. Match always holds region in + * forwards order, even if given in reverse order (such as from a mapping to + * a reverse strand); this avoids trouble for routines that highlight search + * results etc + * + * @param seq + * a sequence + * @param start + * start position of matched range (base 1) + * @param end + * end of matched range (inclusive, base 1) + */ + public Match(SequenceI seq, int start, int end) + { + sequence = seq; + + /* + * always hold in forwards order, even if given in reverse order + * (such as from a mapping to a reverse strand); this avoids + * trouble for routines that highlight search results etc + */ + if (start <= end) + { + this.start = start; + this.end = end; + } + else + { + // TODO: JBP could mark match as being specified in reverse direction + // for use + // by caller ? e.g. visualizing reverse strand highlight + this.start = end; + this.end = start; + } + } + + @Override + public SequenceI getSequence() + { + return sequence; + } + + @Override + public int getStart() + { + return start; + } + + @Override + public int getEnd() + { + return end; + } + + /** + * Returns a representation as "seqid/start-end" + */ + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + if (sequence != null) + { + sb.append(sequence.getName()).append("/"); + } + sb.append(start).append("-").append(end); + return sb.toString(); + } + + /** + * 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 SearchResultMatchI)) + { + return false; + } + SearchResultMatchI m = (SearchResultMatchI) obj; + return (sequence == m.getSequence() && start == m.getStart() + && end == m.getEnd()); + } + + @Override + public boolean contains(SequenceI seq, int from, int to) + { + return (sequence == seq && start <= from && end >= to); + } + } + + /* (non-Javadoc) + * @see jalview.datamodel.SearchResultsI#addResult(jalview.datamodel.SequenceI, int, int) + */ + @Override + public SearchResultMatchI addResult(SequenceI seq, int start, int end) + { + Match m = new Match(seq, start, end); + matches.add(m); + return m; + } + + /* (non-Javadoc) + * @see jalview.datamodel.SearchResultsI#involvesSequence(jalview.datamodel.SequenceI) + */ + @Override + public boolean involvesSequence(SequenceI sequence) + { + SequenceI ds = sequence.getDatasetSequence(); + for (SearchResultMatchI _m : matches) + { + SequenceI matched = _m.getSequence(); + if (matched != null && (matched == sequence || matched == ds)) + { + return true; + } + } + return false; + } + + /* (non-Javadoc) + * @see jalview.datamodel.SearchResultsI#getResults(jalview.datamodel.SequenceI, int, int) + */ + @Override + public int[] getResults(SequenceI sequence, int start, int end) + { + if (matches.isEmpty()) + { + return null; + } + + int[] result = null; + int[] tmp = null; + int resultLength, matchStart = 0, matchEnd = 0; + boolean mfound; + Match m; + for (SearchResultMatchI _m : matches) + { + m = (Match) _m; + + mfound = false; + if (m.sequence == sequence + || m.sequence == sequence.getDatasetSequence()) + { + mfound = true; + matchStart = sequence.findIndex(m.start) - 1; + matchEnd = m.start == m.end ? matchStart : sequence + .findIndex(m.end) - 1; + } + + if (mfound) + { + if (matchStart <= end && matchEnd >= start) + { + if (matchStart < start) + { + matchStart = start; + } + + if (matchEnd > end) + { + matchEnd = end; + } + + if (result == null) + { + result = new int[] { matchStart, matchEnd }; + } + else + { + resultLength = result.length; + tmp = new int[resultLength + 2]; + System.arraycopy(result, 0, tmp, 0, resultLength); + result = tmp; + result[resultLength] = matchStart; + result[resultLength + 1] = matchEnd; + } + } + else + { + // debug + // System.err.println("Outwith bounds!" + matchStart+">"+end +" or " + // + matchEnd+"<"+start); + } + } + } + return result; + } + + @Override + public int markColumns(SequenceCollectionI sqcol, BitSet bs) + { + int count = 0; + BitSet mask = new BitSet(); + for (SequenceI s : sqcol.getSequences()) + { + int[] cols = getResults(s, sqcol.getStartRes(), sqcol.getEndRes()); + if (cols != null) + { + for (int pair = 0; pair < cols.length; pair += 2) + { + mask.set(cols[pair], cols[pair + 1] + 1); + } + } + } + // compute columns that were newly selected + BitSet original = (BitSet) bs.clone(); + original.and(mask); + count = mask.cardinality() - original.cardinality(); + // and mark ranges not already marked + bs.or(mask); + return count; + } + + /* (non-Javadoc) + * @see jalview.datamodel.SearchResultsI#getSize() + */ + @Override + public int getSize() + { + return matches.size(); + } + + /* (non-Javadoc) + * @see jalview.datamodel.SearchResultsI#isEmpty() + */ + @Override + public boolean isEmpty() + { + return matches.isEmpty(); + } + + /* (non-Javadoc) + * @see jalview.datamodel.SearchResultsI#getResults() + */ + @Override + public List getResults() + { + return matches; + } + + /** + * Return the results as a list of matches [seq1/from-to, seq2/from-to, ...] + * + * @return + */ + @Override + public String toString() + { + return matches == null ? "" : matches.toString(); + } + + /** + * Hashcode is derived from the list of matches. This ensures that when two + * SearchResults objects satisfy the test for equals(), then they have the + * same hashcode. + * + * @see Match#hashCode() + * @see java.util.AbstractList#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 SearchResultsI)) + { + return false; + } + SearchResultsI sr = (SearchResultsI) obj; + return matches.equals(sr.getResults()); + } + + @Override + public void addSearchResults(SearchResultsI toAdd) + { + matches.addAll(toAdd.getResults()); + } +}