From: Jim Procter Date: Mon, 21 Sep 2020 11:01:51 +0000 (+0100) Subject: Merge branch 'merge_review/JAL-3490_2_11_2_develop' into develop X-Git-Tag: Develop-2_11_2_0-d20201215~24^2~2^2 X-Git-Url: http://source.jalview.org/gitweb/?p=jalview.git;a=commitdiff_plain;h=490bea038483dcc853766de08a8971668930f43a;hp=02d251a913dfe940a7d3e86dce7f37fee06bd916 Merge branch 'merge_review/JAL-3490_2_11_2_develop' into develop --- diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 308876b..dbf54bf 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1375,3 +1375,5 @@ label.include_linked_features = Include {0} features label.include_linked_tooltip = Include visible {0} features
converted to local sequence coordinates label.features_not_shown = {0} feature(s) not shown label.no_features_to_sort_by = No features to sort by +label.ignore_hidden = Ignore hidden columns +label.ignore_hidden_tooltip = Ignore any characters in hidden columns when matching diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index ef40e97..7383a0e 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1376,3 +1376,5 @@ label.include_linked_features = Incluir caracter label.include_linked_tooltip = Incluir características de {0}
convertidas a coordenadas de secuencia local label.features_not_shown = {0} característica(s) no mostradas label.no_features_to_sort_by = No hay características para ordenar +label.ignore_hidden = Ignorar columnas ocultas +label.ignore_hidden_tooltip = Ignorar caracteres en columnas ocultas diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java index 3cbef6d..c545c7f 100644 --- a/src/jalview/analysis/Finder.java +++ b/src/jalview/analysis/Finder.java @@ -23,17 +23,18 @@ package jalview.analysis; import jalview.api.AlignViewportI; import jalview.api.FinderI; import jalview.datamodel.AlignmentI; -import jalview.datamodel.Range; import jalview.datamodel.SearchResultMatchI; import jalview.datamodel.SearchResults; import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import jalview.datamodel.VisibleContigsIterator; import jalview.util.Comparison; +import jalview.util.MapList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; -import java.util.Vector; import com.stevesoft.pat.Regex; @@ -50,7 +51,7 @@ public class Finder implements FinderI /* * sequences matched by id or description */ - private Vector idMatches; + private List idMatches; /* * the viewport to search over @@ -63,11 +64,24 @@ public class Finder implements FinderI private int sequenceIndex; /* - * column position in sequence to search from, base 0 - * - absolute column number including any hidden columns - * (position after start of last match for a repeat search) + * position offset in sequence to search from, base 0 + * (position after start of last match for a 'find next') */ - private int columnIndex; + private int residueIndex; + + /* + * the true sequence position of the start of the + * last sequence searched (when 'ignore hidden regions' does not apply) + */ + private int searchedSequenceStartPosition; + + /* + * when 'ignore hidden regions' applies, this holds the mapping from + * the visible sequence positions (1, 2, ...) to true sequence positions + */ + private MapList searchedSequenceMap; + + private String seqToSearch; /** * Constructor for searching a viewport @@ -78,33 +92,35 @@ public class Finder implements FinderI { this.viewport = av; this.sequenceIndex = 0; - this.columnIndex = -1; + this.residueIndex = -1; } @Override public void findAll(String theSearchString, boolean matchCase, - boolean searchDescription) + boolean searchDescription, boolean ignoreHidden) { /* * search from the start */ sequenceIndex = 0; - columnIndex = -1; + residueIndex = -1; - doFind(theSearchString, matchCase, searchDescription, true); + doFind(theSearchString, matchCase, searchDescription, true, + ignoreHidden); /* * reset to start for next search */ sequenceIndex = 0; - columnIndex = -1; + residueIndex = -1; } @Override public void findNext(String theSearchString, boolean matchCase, - boolean searchDescription) + boolean searchDescription, boolean ignoreHidden) { - doFind(theSearchString, matchCase, searchDescription, false); + doFind(theSearchString, matchCase, searchDescription, false, + ignoreHidden); if (searchResults.isEmpty() && idMatches.isEmpty()) { @@ -112,7 +128,7 @@ public class Finder implements FinderI * search failed - reset to start for next search */ sequenceIndex = 0; - columnIndex = -1; + residueIndex = -1; } } @@ -123,18 +139,19 @@ public class Finder implements FinderI * @param matchCase * @param searchDescription * @param findAll + * @param ignoreHidden */ protected void doFind(String theSearchString, boolean matchCase, - boolean searchDescription, boolean findAll) + boolean searchDescription, boolean findAll, boolean ignoreHidden) { + searchResults = new SearchResults(); + idMatches = new ArrayList<>(); + String searchString = matchCase ? theSearchString : theSearchString.toUpperCase(); Regex searchPattern = new Regex(searchString); searchPattern.setIgnoreCase(!matchCase); - searchResults = new SearchResults(); - idMatches = new Vector<>(); - SequenceGroup selection = viewport.getSelectionGroup(); if (selection != null && selection.getSize() < 1) { @@ -144,108 +161,202 @@ public class Finder implements FinderI AlignmentI alignment = viewport.getAlignment(); int end = alignment.getHeight(); - while (sequenceIndex < end) + getSequence(ignoreHidden); + + boolean found = false; + while ((!found || findAll) && sequenceIndex < end) { - SequenceI seq = alignment.getSequenceAt(sequenceIndex); - boolean found = findNextMatch(seq, searchString, searchPattern, - searchDescription); - if (found && !findAll) + found = findNextMatch(searchString, searchPattern, searchDescription, + ignoreHidden); + } + } + + /** + * Calculates and saves the sequence string to search. The string is restricted + * to the current selection region if there is one, and is saved with all gaps + * removed. + *

+ * If there are hidden columns, and option {@ignoreHidden} is selected, then + * only visible positions of the sequence are included, and a mapping is also + * constructed from the returned string positions to the true sequence + * positions. + *

+ * Note we have to do this each time {@code findNext} or {@code findAll} is + * called, in case the alignment, selection group or hidden columns have + * changed. In particular, if the sequence at offset {@code sequenceIndex} in + * the alignment is (no longer) in the selection group, search is advanced to + * the next sequence that is. + *

+ * Sets sequence string to the empty string if there are no more sequences (in + * selection group if any) at or after {@code sequenceIndex}. + *

+ * Returns true if a sequence could be found, false if end of alignment was + * reached + * + * @param ignoreHidden + * @return + */ + private boolean getSequence(boolean ignoreHidden) + { + AlignmentI alignment = viewport.getAlignment(); + if (sequenceIndex >= alignment.getHeight()) + { + seqToSearch = ""; + return false; + } + SequenceI seq = alignment.getSequenceAt(sequenceIndex); + SequenceGroup selection = viewport.getSelectionGroup(); + if (selection != null && !selection.contains(seq)) + { + if (!nextSequence(ignoreHidden)) { - return; + return false; } - if (!found) + seq = alignment.getSequenceAt(sequenceIndex); + } + + String seqString = null; + if (ignoreHidden) + { + seqString = getVisibleSequence(seq); + this.searchedSequenceStartPosition = 1; + } + else + { + int startCol = 0; + int endCol = seq.getLength() - 1; + this.searchedSequenceStartPosition = seq.getStart(); + if (selection != null) { - sequenceIndex++; - columnIndex = -1; + startCol = selection.getStartRes(); + endCol = Math.min(endCol, selection.getEndRes()); + this.searchedSequenceStartPosition = seq.findPosition(startCol); } + seqString = seq.getSequenceAsString(startCol, endCol + 1); } + + /* + * remove gaps; note that even if this leaves an empty string, we 'search' + * the sequence anyway (for possible match on name or description) + */ + String ungapped = AlignSeq.extractGaps(Comparison.GapChars, seqString); + this.seqToSearch = ungapped; + + return true; } /** - * Answers the start-end column range of the visible region of - * sequence starting at or after the given column. - * If there are no hidden columns, this just returns the remaining width of - * the sequence. The range is restricted to the current selection - * if there is one. Answers null if there are no visible columns at or after - * column. + * Returns a string consisting of only the visible residues of {@code seq} from + * alignment column {@ fromColumn}, restricted to the current selection region + * if there is one. + *

+ * As a side-effect, also computes the mapping from the true sequence positions + * to the positions (1, 2, ...) of the returned sequence. This is to allow + * search matches in the visible sequence to be converted to sequence positions. + * + * @param seq + * @return */ - protected Range getNextVisibleSequenceRegion(SequenceI sequence, - int column) + private String getVisibleSequence(SequenceI seq) { - int seqColStart = column; - int seqColEnd = sequence.getLength() - 1; - /* - * restrict search to (next) visible column region, - * in case there are hidden columns + * get start / end columns of sequence and convert to base 0 + * (so as to match the visible column ranges) */ - AlignmentI alignment = viewport.getAlignment(); - VisibleContigsIterator visibleRegions = alignment.getHiddenColumns() - .getVisContigsIterator(column, alignment.getWidth(), - false); - int[] visible = visibleRegions.hasNext() ? visibleRegions.next() : null; - if (visible == null) - { - columnIndex = seqColEnd + 1; - return null; - } - seqColStart = Math.max(seqColStart, visible[0]); - seqColEnd = Math.min(seqColEnd, visible[1]); + int seqStartCol = seq.findIndex(seq.getStart()) - 1; + int seqEndCol = seq.findIndex(seq.getStart() + seq.getLength() - 1) - 1; + Iterator visibleColumns = viewport.getViewAsVisibleContigs(true); + StringBuilder visibleSeq = new StringBuilder(seqEndCol - seqStartCol); + List fromRanges = new ArrayList<>(); - /* - * restrict search to selected region if there is one - */ - SequenceGroup selection = viewport.getSelectionGroup(); - if (selection != null) + while (visibleColumns.hasNext()) { - int selectionStart = selection.getStartRes(); - int selectionEnd = selection.getEndRes(); - if (selectionStart > seqColEnd || selectionEnd < seqColStart) + int[] range = visibleColumns.next(); + if (range[0] > seqEndCol) + { + // beyond the end of the sequence + break; + } + if (range[1] < seqStartCol) + { + // before the start of the sequence + continue; + } + String subseq = seq.getSequenceAsString(range[0], range[1] + 1); + String ungapped = AlignSeq.extractGaps(Comparison.GapChars, subseq); + visibleSeq.append(ungapped); + if (!ungapped.isEmpty()) { /* - * sequence region doesn't overlap selection region + * visible region includes at least one non-gap character, + * so add the range to the mapping being constructed */ - columnIndex = seqColEnd + 1; - return null; + int seqResFrom = seq.findPosition(range[0]); + int seqResTo = seqResFrom + ungapped.length() - 1; + fromRanges.add(new int[] { seqResFrom, seqResTo }); } - seqColStart = Math.max(seqColStart, selectionStart); - seqColEnd = Math.min(seqColEnd, selectionEnd); } - return new Range(seqColStart, seqColEnd); + /* + * construct the mapping + * from: visible sequence positions 1..length + * to: true residue positions of the alignment sequence + */ + List toRange = Arrays + .asList(new int[] + { 1, visibleSeq.length() }); + searchedSequenceMap = new MapList(fromRanges, toRange, 1, 1); + + return visibleSeq.toString(); } /** - * Finds the next match in the given sequence, starting at column at - * columnIndex. Answers true if a match is found, else false. If - * a match is found, columnIndex is advanced to the column after - * the start of the matched region, ready for a search from the next position. + * Advances the search to the next sequence in the alignment. Sequences not in + * the current selection group (if there is one) are skipped. The (sub-)sequence + * to be searched is extracted, gaps removed, and saved, or set to null if there + * are no more sequences to search. + *

+ * Returns true if a sequence could be found, false if end of alignment was + * reached * - * @param seq + * @param ignoreHidden + */ + private boolean nextSequence(boolean ignoreHidden) + { + sequenceIndex++; + residueIndex = -1; + + return getSequence(ignoreHidden); + } + + /** + * Finds the next match in the given sequence, starting at offset + * {@code residueIndex}. Answers true if a match is found, else false. + *

+ * If a match is found, {@code residueIndex} is advanced to the position after + * the start of the matched region, ready for the next search. + *

+ * If no match is found, {@code sequenceIndex} is advanced ready to search the + * next sequence. + * + * @param seqToSearch * @param searchString * @param searchPattern * @param matchDescription + * @param ignoreHidden * @return */ - protected boolean findNextMatch(SequenceI seq, String searchString, - Regex searchPattern, boolean matchDescription) + protected boolean findNextMatch(String searchString, + Regex searchPattern, boolean matchDescription, + boolean ignoreHidden) { - SequenceGroup selection = viewport.getSelectionGroup(); - if (selection != null && !selection.contains(seq)) - { - /* - * this sequence is not in the selection - advance to next sequence - */ - return false; - } - - if (columnIndex < 0) + if (residueIndex < 0) { /* * at start of sequence; try find by residue number, in sequence id, * or (optionally) in sequence description */ - if (doNonMotifSearches(seq, searchString, searchPattern, + if (doNonMotifSearches(searchString, searchPattern, matchDescription)) { return true; @@ -255,83 +366,66 @@ public class Finder implements FinderI /* * search for next match in sequence string */ - int end = seq.getLength(); - while (columnIndex < end) + int end = seqToSearch.length(); + while (residueIndex < end) { - if (searchNextVisibleRegion(seq, searchPattern)) + boolean matched = searchPattern.searchFrom(seqToSearch, residueIndex); + if (matched) { - return true; + if (recordMatch(searchPattern, ignoreHidden)) + { + return true; + } + } + else + { + residueIndex = Integer.MAX_VALUE; } - } - return false; - } - - /** - * Searches the sequence, starting from columnIndex, and adds the - * next match (if any) to searchResults. The search is restricted - * to the next visible column region, and to the selection region - * if there is one. Answers true if a match is added, else false. - * - * @param seq - * @param searchPattern - * @return - */ - protected boolean searchNextVisibleRegion(SequenceI seq, Regex searchPattern) - { - Range visible = getNextVisibleSequenceRegion(seq, columnIndex); - if (visible == null) - { - return false; - } - String seqString = seq.getSequenceAsString(visible.start, visible.end + 1); - String noGaps = AlignSeq.extractGaps(Comparison.GapChars, seqString); - - if (searchPattern.search(noGaps)) - { - int sequenceStartPosition = seq.findPosition(visible.start); - recordMatch(seq, searchPattern, sequenceStartPosition); - return true; - } - else - { - /* - * no match - advance columnIndex past this visible region - * so the next visible region (if any) is searched next - */ - columnIndex = visible.end + 1; } + nextSequence(ignoreHidden); return false; } /** * Adds the match held in the searchPattern Regex to the * searchResults, unless it is a subregion of the last match - * recorded. columnIndex is advanced to the position after the + * recorded. residueIndex is advanced to the position after the * start of the matched region, ready for the next search. Answers true if a * match was added, else false. + *

+ * Matches that lie entirely within hidden regions of the alignment are not + * added. * - * @param seq * @param searchPattern - * @param firstResiduePosition + * @param ignoreHidden * @return */ - protected boolean recordMatch(SequenceI seq, Regex searchPattern, - int firstResiduePosition) + protected boolean recordMatch(Regex searchPattern, boolean ignoreHidden) { + SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex); + /* - * get start/end of the match in sequence coordinates + * convert start/end of the match to sequence coordinates */ int offset = searchPattern.matchedFrom(); - int matchStartPosition = firstResiduePosition + offset; + int matchStartPosition = this.searchedSequenceStartPosition + offset; int matchEndPosition = matchStartPosition + searchPattern.charsMatched() - 1; /* - * update columnIndex to next column after the start of the match + * update residueIndex to next position after the start of the match * (findIndex returns a value base 1, columnIndex is held base 0) */ - columnIndex = seq.findIndex(matchStartPosition); + residueIndex += offset + 1; + + /* + * return false if the match is entirely in a hidden region + */ + if (allHidden(seq, matchStartPosition, matchEndPosition)) + { + return false; + } /* * check that this match is not a subset of the previous one (JAL-2302) @@ -343,7 +437,7 @@ public class Finder implements FinderI if (lastMatch == null || !lastMatch.contains(seq, matchStartPosition, matchEndPosition)) { - searchResults.addResult(seq, matchStartPosition, matchEndPosition); + addMatch(seq, matchStartPosition, matchEndPosition, ignoreHidden); return true; } @@ -351,6 +445,60 @@ public class Finder implements FinderI } /** + * Adds one match to the stored list. If hidden residues are being skipped, then + * the match may need to be split into contiguous positions of the sequence (so + * it does not include skipped residues). + * + * @param seq + * @param matchStartPosition + * @param matchEndPosition + * @param ignoreHidden + */ + private void addMatch(SequenceI seq, int matchStartPosition, + int matchEndPosition, boolean ignoreHidden) + { + if (!ignoreHidden) + { + /* + * simple case + */ + searchResults.addResult(seq, matchStartPosition, matchEndPosition); + return; + } + + /* + * get start-end contiguous ranges in underlying sequence + */ + int[] truePositions = searchedSequenceMap + .locateInFrom(matchStartPosition, matchEndPosition); + searchResults.addResult(seq, truePositions); + } + + /** + * Returns true if all residues are hidden, else false + * + * @param seq + * @param fromPos + * @param toPos + * @return + */ + private boolean allHidden(SequenceI seq, int fromPos, int toPos) + { + if (!viewport.hasHiddenColumns()) + { + return false; + } + for (int res = fromPos; res <= toPos; res++) + { + if (isVisible(seq, res)) + { + return false; + } + } + return true; + } + + /** * Does searches other than for residue patterns. Currently this includes *

    *
  • find residue by position (if search string is a number)
  • @@ -359,24 +507,29 @@ public class Finder implements FinderI *
* Answers true if a match is found, else false. * - * @param seq * @param searchString * @param searchPattern * @param includeDescription * @return */ - protected boolean doNonMotifSearches(SequenceI seq, String searchString, + protected boolean doNonMotifSearches(String searchString, Regex searchPattern, boolean includeDescription) { + SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex); + /* * position sequence search to start of sequence */ - columnIndex = 0; - - if (searchForResidueNumber(seq, searchString)) + residueIndex = 0; + try { - return true; + int res = Integer.parseInt(searchString); + return searchForResidueNumber(seq, res); + } catch (NumberFormatException ex) + { + // search pattern is not a number } + if (searchSequenceName(seq, searchPattern)) { return true; @@ -402,7 +555,7 @@ public class Finder implements FinderI String desc = seq.getDescription(); if (desc != null && searchPattern.search(desc) && !idMatches.contains(seq)) { - idMatches.addElement(seq); + idMatches.add(seq); return true; } return false; @@ -421,45 +574,56 @@ public class Finder implements FinderI { if (searchPattern.search(seq.getName()) && !idMatches.contains(seq)) { - idMatches.addElement(seq); + idMatches.add(seq); return true; } return false; } /** - * Tries to interpret the search string as a residue position, and if valid, - * adds the position to the search results and returns true, else answers - * false + * If the residue position is valid for the sequence, and in a visible column, + * adds the position to the search results and returns true, else answers false. + * + * @param seq + * @param resNo + * @return */ - protected boolean searchForResidueNumber(SequenceI seq, String searchString) + protected boolean searchForResidueNumber(SequenceI seq, int resNo) { - try + if (seq.getStart() <= resNo && seq.getEnd() >= resNo) { - int res = Integer.parseInt(searchString); - if (seq.getStart() <= res && seq.getEnd() >= res) + if (isVisible(seq, resNo)) { - searchResults.addResult(seq, res, res); + searchResults.addResult(seq, resNo, resNo); return true; } - } catch (NumberFormatException ex) - { } return false; } - /* (non-Javadoc) - * @see jalview.analysis.FinderI#getIdMatch() + /** + * Returns true if the residue is in a visible column, else false + * + * @param seq + * @param res + * @return */ + private boolean isVisible(SequenceI seq, int res) + { + if (!viewport.hasHiddenColumns()) + { + return true; + } + int col = seq.findIndex(res); // base 1 + return viewport.getAlignment().getHiddenColumns().isVisible(col - 1); // base 0 + } + @Override - public Vector getIdMatches() + public List getIdMatches() { return idMatches; } - /* (non-Javadoc) - * @see jalview.analysis.FinderI#getSearchResults() - */ @Override public SearchResultsI getSearchResults() { diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 065be75..93a5101 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -39,6 +39,7 @@ import jalview.viewmodel.ViewportRanges; import java.awt.Color; import java.awt.Font; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -539,4 +540,16 @@ public interface AlignViewportI extends ViewStyleI * - a group defined on sequences in the alignment held by the view */ void addSequenceGroup(SequenceGroup sequenceGroup); + + /** + * Returns an interator over the [start, end] column positions of the visible + * regions of the alignment + * + * @param selectedRegionOnly + * if true, and the view has a selection region, + * then only the intersection of visible columns + * with the selection region is returned + * @return + */ + Iterator getViewAsVisibleContigs(boolean selectedRegionOnly); } diff --git a/src/jalview/api/FinderI.java b/src/jalview/api/FinderI.java index 71b35ef..8f6232e 100644 --- a/src/jalview/api/FinderI.java +++ b/src/jalview/api/FinderI.java @@ -34,35 +34,48 @@ public interface FinderI /** * Performs a find for the given search string (interpreted as a regular * expression). Search may optionally be case-sensitive, and may optionally - * including match in sequence description (sequence id is always searched). - * If the viewport has an active selection, then the find is restricted to the - * selection region. Sequences matched by id or description can be retrieved - * by getIdMatches(), and matched residue patterns by getSearchResults(). + * including match in sequence description (sequence id is always searched). If + * the viewport has an active selection, then the find is restricted to the + * selection region. Sequences matched by id or description can be retrieved by + * getIdMatches(), and matched residue patterns by getSearchResults(). + *

+ * If {@code ignoreHidden} is true, then any residues in hidden columns are + * ignored (skipped) when matching, so for example pattern {@code KRT} would + * match sequence {@code KRqmT} (where {@code qm} are in hidden columns). + *

+ * Matches of entirely hidden patterns are not returned. Matches that span + * hidden regions on one or both sides may be returned. * * @param theSearchString * @param caseSensitive * @param searchDescription + * @param ignoreHidden * @return */ void findAll(String theSearchString, boolean caseSensitive, - boolean searchDescription); + boolean searchDescription, boolean ignoreHidden); /** * Finds the next match for the given search string (interpreted as a regular * expression), starting from the position after the last match found. Search * may optionally be case-sensitive, and may optionally including match in - * sequence description (sequence id is always searched). If the viewport has - * an active selection, then the find is restricted to the selection region. + * sequence description (sequence id is always searched). If the viewport has an + * active selection, then the find is restricted to the selection region. * Sequences matched by id or description can be retrieved by getIdMatches(), * and matched residue patterns by getSearchResults(). + *

+ * If {@code ignoreHidden} is true, any hidden residues are skipped (matches may + * span them). If false, they are included for matching purposes. In either + * cases, entirely hidden matches are not returned. * * @param theSearchString * @param caseSensitive * @param searchDescription + * @param ignoreHidden * @return */ void findNext(String theSearchString, boolean caseSensitive, - boolean searchDescription); + boolean searchDescription, boolean ignoreHidden); /** * Returns the (possibly empty) list of sequences matched on sequence name or diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index 58569cd..0ed54e7 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -345,7 +345,7 @@ public class AlignmentPanel extends Panel int verticalOffset, boolean redrawOverview, boolean centre) { // do we need to scroll the panel? - if (results != null && results.getSize() > 0) + if (results != null && results.getCount() > 0) { AlignmentI alignment = av.getAlignment(); int seqIndex = alignment.findIndex(results); diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java index 2fc3441..a78e41c 100644 --- a/src/jalview/appletgui/Finder.java +++ b/src/jalview/appletgui/Finder.java @@ -171,11 +171,13 @@ public class Finder extends Panel implements ActionListener boolean doSearchDescription = searchDescription.getState(); if (doFindAll) { - finder.findAll(searchString, isCaseSensitive, doSearchDescription); + finder.findAll(searchString, isCaseSensitive, doSearchDescription, + false); } else { - finder.findNext(searchString, isCaseSensitive, doSearchDescription); + finder.findNext(searchString, isCaseSensitive, doSearchDescription, + false); } searchResults = finder.getSearchResults(); @@ -209,13 +211,13 @@ public class Finder extends Panel implements ActionListener String message = (idMatches.size() > 0) ? "" + idMatches.size() + " IDs" : ""; if (idMatches.size() > 0 && searchResults != null - && searchResults.getSize() > 0) + && searchResults.getCount() > 0) { message += " and "; } if (searchResults != null) { - message += searchResults.getSize() + " subsequence matches."; + message += searchResults.getCount() + " subsequence matches."; } ap.alignFrame.statusBar.setText(MessageManager .formatMessage("label.search_results", new String[] diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index 0074d2a..7c3bba7 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -33,6 +33,7 @@ import java.util.List; */ public class SearchResults implements SearchResultsI { + private int count; private List matches = new ArrayList<>(); @@ -168,11 +169,30 @@ public class SearchResults implements SearchResultsI if (!matches.contains(m)) { matches.add(m); + count++; } return m; } @Override + public void addResult(SequenceI seq, int[] positions) + { + /* + * we only increment the match count by 1 - or not at all, + * if the matches are all duplicates of existing + */ + int beforeCount = count; + for (int i = 0; i < positions.length - 1; i += 2) + { + addResult(seq, positions[i], positions[i + 1]); + } + if (count > beforeCount) + { + count = beforeCount + 1; + } + } + + @Override public boolean involvesSequence(SequenceI sequence) { final int start = sequence.getStart(); @@ -286,9 +306,9 @@ public class SearchResults implements SearchResultsI } @Override - public int getSize() + public int getCount() { - return matches.size(); + return count; } @Override diff --git a/src/jalview/datamodel/SearchResultsI.java b/src/jalview/datamodel/SearchResultsI.java index 3c3ad4e..c89f363 100644 --- a/src/jalview/datamodel/SearchResultsI.java +++ b/src/jalview/datamodel/SearchResultsI.java @@ -41,6 +41,17 @@ public interface SearchResultsI SearchResultMatchI addResult(SequenceI seq, int start, int end); /** + * Adds one ore more [start, end] ranges to the results (unless already added + * to avoid duplicates). This method only increments the match count by 1. + * This is for the case where a match spans ignored hidden residues - it is + * formally two or more contiguous matches, but only counted as one match. + * + * @param seq + * @param positions + */ + void addResult(SequenceI seq, int[] positions); + + /** * adds all match results in the argument to this set * * @param toAdd @@ -74,11 +85,13 @@ public interface SearchResultsI int[] getResults(SequenceI sequence, int start, int end); /** - * Returns the number of matches found + * Returns the number of matches found. Note that if a match straddles ignored + * hidden residues, it is counted as one match, although formally recorded as + * two (or more) contiguous matched sequence regions * * @return */ - int getSize(); + int getCount(); /** * Returns true if no search result matches are held. diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java index 7ff0f75..0dccc0c 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -297,7 +297,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI private void jbInit() throws Exception { - txt_search = new JvCacheableInputBox<>(getCacheKey()); + txt_search = new JvCacheableInputBox<>(getCacheKey(), 45); populateCmbSearchTargetOptions(); Integer width = getTempUserPrefs().get("FTSPanel.width") == null ? 800 : getTempUserPrefs().get("FTSPanel.width"); diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 8e7820f..1f3f409 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -2764,15 +2764,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * DOCUMENT ME! + * Opens a Finder dialog * * @param e - * DOCUMENT ME! */ @Override public void findMenuItem_actionPerformed(ActionEvent e) { - new Finder(); + new Finder(alignPanel); } /** diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 140f77e..0125f0d 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -59,7 +59,6 @@ import java.awt.FontMetrics; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Hashtable; -import java.util.Iterator; import java.util.List; import javax.swing.JInternalFrame; @@ -446,31 +445,6 @@ public class AlignViewport extends AlignmentViewport } /** - * returns the visible column regions of the alignment - * - * @param selectedRegionOnly - * true to just return the contigs intersecting with the selected - * area - * @return - */ - public Iterator getViewAsVisibleContigs(boolean selectedRegionOnly) - { - int start = 0; - int end = 0; - if (selectedRegionOnly && selectionGroup != null) - { - start = selectionGroup.getStartRes(); - end = selectionGroup.getEndRes() + 1; - } - else - { - end = alignment.getWidth(); - } - return (alignment.getHiddenColumns().getVisContigsIterator(start, end, - false)); - } - - /** * get hash of undo and redo list for the alignment * * @return long[] { historyList.hashCode, redoList.hashCode }; diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index 589f4bd..8b66b8e 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -719,7 +719,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter private static final String FILTER_BY_ANN_CACHE_KEY = "CACHE.SELECT_FILTER_BY_ANNOT"; public JvCacheableInputBox searchBox = new JvCacheableInputBox<>( - FILTER_BY_ANN_CACHE_KEY); + FILTER_BY_ANN_CACHE_KEY, 23); public SearchPanel(AnnotationColumnChooser aColChooser) { @@ -729,7 +729,6 @@ public class AnnotationColumnChooser extends AnnotationRowFilter this.setBorder(new TitledBorder( MessageManager.getString("label.search_filter"))); - searchBox.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXX"); searchBox.getComponent().setToolTipText( MessageManager.getString("info.enter_search_text_here")); searchBox.addKeyListener(new java.awt.event.KeyAdapter() diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index a1693f7..d328452 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -28,8 +28,9 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.jbgui.GFinder; import jalview.util.MessageManager; -import jalview.viewmodel.AlignmentViewport; +import java.awt.Dimension; +import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; @@ -64,7 +65,7 @@ public class Finder extends GFinder private static final int MIN_HEIGHT = 120; - private static final int MY_HEIGHT = 120; + private static final int MY_HEIGHT = 150; private static final int MY_WIDTH = 400; @@ -81,33 +82,17 @@ public class Finder extends GFinder private SearchResultsI searchResults; - /* - * true if we only search a given alignment view - */ - private boolean focusfixed; - - /** - * Creates a new Finder object with no associated viewport or panel. Each Find - * or Find Next action will act on whichever viewport has focus at the time. - */ - public Finder() - { - this(null, null); - } - /** - * Constructor given an associated viewport and alignment panel. Constructs - * and displays an internal frame where the user can enter a search string. + * Constructor given an associated alignment panel. Constructs and displays an + * internal frame where the user can enter a search string. * - * @param viewport * @param alignPanel */ - public Finder(AlignmentViewport viewport, AlignmentPanel alignPanel) + public Finder(AlignmentPanel alignPanel) { - av = viewport; + av = alignPanel.getAlignViewport(); ap = alignPanel; finders = new HashMap<>(); - focusfixed = viewport != null; frame = new JInternalFrame(); frame.setContentPane(this); frame.setLayer(JLayeredPane.PALETTE_LAYER); @@ -121,6 +106,7 @@ public class Finder extends GFinder } }); addEscapeHandler(); + Desktop.addInternalFrame(frame, MessageManager.getString("label.find"), true, MY_WIDTH, MY_HEIGHT, true, true); searchBox.getComponent().requestFocus(); @@ -170,13 +156,14 @@ public class Finder extends GFinder /** * if !focusfixed and not in a desktop environment, checks that av and ap are * valid. Otherwise, gets the topmost alignment window and sets av and ap - * accordingly + * accordingly. Also sets the 'ignore hidden' checkbox disabled if the viewport + * has no hidden columns. * * @return false if no alignment window was found */ boolean getFocusedViewport() { - if (focusfixed || Desktop.desktop == null) + if (Desktop.desktop == null) { if (ap != null && av != null) { @@ -196,6 +183,7 @@ public class Finder extends GFinder { av = ((AlignFrame) alignFrame).viewport; ap = ((AlignFrame) alignFrame).alignPanel; + ignoreHidden.setEnabled(av.hasHiddenColumns()); return true; } } @@ -265,13 +253,16 @@ public class Finder extends GFinder boolean isCaseSensitive = caseSensitive.isSelected(); boolean doSearchDescription = searchDescription.isSelected(); + boolean skipHidden = ignoreHidden.isSelected(); if (doFindAll) { - finder.findAll(searchString, isCaseSensitive, doSearchDescription); + finder.findAll(searchString, isCaseSensitive, doSearchDescription, + skipHidden); } else { - finder.findNext(searchString, isCaseSensitive, doSearchDescription); + finder.findNext(searchString, isCaseSensitive, doSearchDescription, + skipHidden); } searchResults = finder.getSearchResults(); @@ -307,11 +298,11 @@ public class Finder extends GFinder : ""; if (searchResults != null) { - if (idMatch.size() > 0 && searchResults.getSize() > 0) + if (idMatch.size() > 0 && searchResults.getCount() > 0) { message += " and "; } - message += searchResults.getSize() + message += searchResults.getCount() + " subsequence matches found."; } JvOptionPane.showInternalMessageDialog(this, message, null, @@ -380,4 +371,15 @@ public class Finder extends GFinder ap.alignFrame.requestFocus(); } } + + @Override + protected void paintComponent(Graphics g) + { + /* + * enable 'hidden regions' option only if + * 'top' viewport has hidden columns + */ + getFocusedViewport(); + super.paintComponent(g); + } } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 8a49092..4ffefba 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -909,11 +909,12 @@ public class SeqPanel extends JPanel AlignFrame af = Desktop.getAlignFrameFor(complement); FeatureRendererModel fr2 = af.getFeatureRenderer(); - int j = results.getSize(); + List matches = results.getResults(); + int j = matches.size(); List infos = new ArrayList<>(); for (int i = 0; i < j; i++) { - SearchResultMatchI match = results.getResults().get(i); + SearchResultMatchI match = matches.get(i); int pos = match.getStart(); if (pos == match.getEnd()) { diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java index e8f30b7..7ade797 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -814,7 +814,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI if (c != null && c instanceof AlignFrame) { AlignFrame af = (AlignFrame) c; - new Finder(af.viewport, af.alignPanel); + new Finder(af.alignPanel); } } }; diff --git a/src/jalview/io/cache/JvCacheableInputBox.java b/src/jalview/io/cache/JvCacheableInputBox.java index 9ec3a9f..eedf09a 100644 --- a/src/jalview/io/cache/JvCacheableInputBox.java +++ b/src/jalview/io/cache/JvCacheableInputBox.java @@ -87,15 +87,26 @@ public class JvCacheableInputBox } /** - * Constructor + * Constructor given the key to cached values, and the (approximate) length in + * characters of the input field * * @param newCacheKey + * @param length */ - public JvCacheableInputBox(String newCacheKey) + public JvCacheableInputBox(String newCacheKey, int length) { // super(); cacheKey = newCacheKey; prototypeDisplayValue = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + if (length > 0) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) + { + sb.append("X"); + } + setPrototypeDisplayValue(sb.toString()); + } boolean useTextField = Platform.isJS(); // BH 2019.03 only switch for JavaScript here // SwingJS TODO implement editable combo box @@ -134,10 +145,10 @@ public class JvCacheableInputBox } }); comboBox.setPrototypeDisplayValue(prototypeDisplayValue); + initCachePopupMenu(); + initCache(newCacheKey); + updateCache(); } - initCachePopupMenu(); - initCache(newCacheKey); - updateCache(); } /** diff --git a/src/jalview/jbgui/GFinder.java b/src/jalview/jbgui/GFinder.java index 0da40cd..9788fba 100755 --- a/src/jalview/jbgui/GFinder.java +++ b/src/jalview/jbgui/GFinder.java @@ -28,11 +28,11 @@ import jalview.io.cache.JvCacheableInputBox; import jalview.util.MessageManager; import java.awt.BorderLayout; -import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; -import java.awt.Insets; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JButton; @@ -46,19 +46,26 @@ import javax.swing.event.CaretListener; public class GFinder extends JPanel { - private static final java.awt.Font VERDANA_12 = new java.awt.Font("Verdana", 0, - 12); + private static final java.awt.Font VERDANA_12 = new Font("Verdana", + Font.PLAIN, 12); private static final String FINDER_CACHE_KEY = "CACHE.FINDER"; - protected JButton createFeatures = new JButton(); + /* + * if more checkboxes are wanted, increase this value + * and add to centrePanel in jbInit() + */ + private static final int PANEL_ROWS = 4; + + protected JButton createFeatures; - protected JvCacheableInputBox searchBox = new JvCacheableInputBox<>( - getCacheKey()); + protected JvCacheableInputBox searchBox; - protected JCheckBox caseSensitive = new JCheckBox(); + protected JCheckBox caseSensitive; - protected JCheckBox searchDescription = new JCheckBox(); + protected JCheckBox searchDescription; + + protected JCheckBox ignoreHidden; public GFinder() { @@ -71,20 +78,96 @@ public class GFinder extends JPanel } } + /** + * Constructs the widgets and adds them to the layout + */ private void jbInit() throws Exception { - BorderLayout mainBorderLayout = new BorderLayout(); - this.setLayout(mainBorderLayout); - mainBorderLayout.setHgap(5); - mainBorderLayout.setVgap(5); + /* + * border layout + * West: 4 rows + * first row 'Find' + * remaining rows empty + * Center: 4 rows + * first row search box + * second row 'match case' checkbox + * third row 'include description' checkbox + * fourth row 'ignore hidden' checkbox + * East: four rows + * first row 'find next' button + * second row 'find all' button + * third row 'new feature' button + * fourth row empty + */ + this.setLayout(new BorderLayout()); + JPanel eastPanel = new JPanel(); + eastPanel.setLayout(new GridLayout(PANEL_ROWS, 1)); + this.add(eastPanel, BorderLayout.EAST); + JPanel centrePanel = new JPanel(); + centrePanel.setLayout(new GridLayout(PANEL_ROWS, 1)); + this.add(centrePanel, BorderLayout.CENTER); + JPanel westPanel = new JPanel(); + westPanel.setLayout(new GridLayout(PANEL_ROWS, 1)); + this.add(westPanel, BorderLayout.WEST); - JLabel jLabelFind = new JLabel(MessageManager.getString("label.find")); - jLabelFind.setFont(VERDANA_12); + /* + * 'Find' prompt goes top left + */ + JLabel findLabel = new JLabel( + " " + MessageManager.getString("label.find") + " "); + findLabel.setFont(VERDANA_12); + westPanel.add(findLabel); + /* + * search box + */ + searchBox = new JvCacheableInputBox<>(FINDER_CACHE_KEY, 25); + searchBox.getComponent().setFont(VERDANA_12); + searchBox.addCaretListener(new CaretListener() + { + @Override + public void caretUpdate(CaretEvent e) + { + textfield_caretUpdate(); + } + }); + searchBox.addKeyListener(new KeyAdapter() + { + @Override + public void keyPressed(KeyEvent e) + { + textfield_keyPressed(e); + } + }); + centrePanel.add(searchBox.getComponent()); + + /* + * search options checkboxes + */ + caseSensitive = new JCheckBox(); + caseSensitive.setHorizontalAlignment(SwingConstants.LEFT); + caseSensitive.setText(MessageManager.getString("label.match_case")); + + searchDescription = new JCheckBox(); + searchDescription + .setText(MessageManager.getString("label.include_description")); + + ignoreHidden = new JCheckBox(); + ignoreHidden.setText(MessageManager.getString("label.ignore_hidden")); + ignoreHidden.setToolTipText( + MessageManager.getString("label.ignore_hidden_tooltip")); + + centrePanel.add(caseSensitive); + centrePanel.add(searchDescription); + centrePanel.add(ignoreHidden); + + /* + * action buttons + */ JButton findAll = new JButton( MessageManager.getString("action.find_all")); findAll.setFont(VERDANA_12); - findAll.addActionListener(new java.awt.event.ActionListener() + findAll.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -92,11 +175,10 @@ public class GFinder extends JPanel findAll_actionPerformed(); } }); - JButton findNext = new JButton( MessageManager.getString("action.find_next")); findNext.setFont(VERDANA_12); - findNext.addActionListener(new java.awt.event.ActionListener() + findNext.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -104,19 +186,11 @@ public class GFinder extends JPanel findNext_actionPerformed(); } }); - - JPanel actionsPanel = new JPanel(); - GridLayout gridLayout1 = new GridLayout(); - actionsPanel.setLayout(gridLayout1); - gridLayout1.setHgap(0); - gridLayout1.setRows(3); - gridLayout1.setVgap(2); - + createFeatures = new JButton(); createFeatures.setEnabled(false); createFeatures.setFont(VERDANA_12); - createFeatures.setMargin(new Insets(0, 0, 0, 0)); createFeatures.setText(MessageManager.getString("label.new_feature")); - createFeatures.addActionListener(new java.awt.event.ActionListener() + createFeatures.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -124,59 +198,9 @@ public class GFinder extends JPanel createFeatures_actionPerformed(); } }); - searchBox.getComponent() - .setFont(new java.awt.Font("Verdana", Font.PLAIN, 12)); - searchBox.addCaretListener(new CaretListener() - { - @Override - public void caretUpdate(CaretEvent e) - { - textfield_caretUpdate(); - } - }); - searchBox.addKeyListener(new java.awt.event.KeyAdapter() - { - @Override - public void keyPressed(KeyEvent e) - { - textfield_keyPressed(e); - } - }); - - caseSensitive.setHorizontalAlignment(SwingConstants.LEFT); - caseSensitive.setText(MessageManager.getString("label.match_case")); - - searchDescription - .setText(MessageManager.getString("label.include_description")); - - actionsPanel.add(findNext, null); - actionsPanel.add(findAll, null); - actionsPanel.add(createFeatures, null); - this.add(jLabelFind, java.awt.BorderLayout.WEST); - this.add(actionsPanel, java.awt.BorderLayout.EAST); - - JPanel jPanel2 = new JPanel(); - jPanel2.setPreferredSize(new Dimension(10, 1)); - JPanel jPanel3 = new JPanel(); - jPanel3.setPreferredSize(new Dimension(10, 1)); - JPanel jPanel4 = new JPanel(); - jPanel4.setLayout(new BorderLayout()); - this.add(jPanel2, java.awt.BorderLayout.SOUTH); - this.add(jPanel3, java.awt.BorderLayout.NORTH); - this.add(jPanel4, java.awt.BorderLayout.CENTER); - jPanel4.add(searchBox.getComponent(), java.awt.BorderLayout.NORTH); - - JPanel optionsPanel = new JPanel(); - - GridLayout optionsGridLayout = new GridLayout(); - optionsGridLayout.setHgap(0); - optionsGridLayout.setRows(2); - optionsGridLayout.setVgap(2); - optionsPanel.setLayout(optionsGridLayout); - optionsPanel.add(caseSensitive, null); - optionsPanel.add(searchDescription, null); - - jPanel4.add(optionsPanel, java.awt.BorderLayout.WEST); + eastPanel.add(findNext); + eastPanel.add(findAll); + eastPanel.add(createFeatures); } protected void textfield_keyPressed(KeyEvent e) @@ -234,15 +258,4 @@ public class GFinder extends JPanel } } - /** - * Returns unique key used for storing Finder cache items in the cache data - * structure - * - * @return - */ - public String getCacheKey() - { - return FINDER_CACHE_KEY; - } - } diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 0d26db1..75cb45b 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -3079,4 +3079,22 @@ public abstract class AlignmentViewport codingComplement.setUpdateStructures(needToUpdateStructureViews); } } + + @Override + public Iterator getViewAsVisibleContigs(boolean selectedRegionOnly) + { + int start = 0; + int end = 0; + if (selectedRegionOnly && selectionGroup != null) + { + start = selectionGroup.getStartRes(); + end = selectionGroup.getEndRes() + 1; + } + else + { + end = alignment.getWidth(); + } + return (alignment.getHiddenColumns().getVisContigsIterator(start, end, + false)); + } } diff --git a/test/jalview/analysis/FinderTest.java b/test/jalview/analysis/FinderTest.java index 5f64b28..95e6c0d 100644 --- a/test/jalview/analysis/FinderTest.java +++ b/test/jalview/analysis/FinderTest.java @@ -71,8 +71,13 @@ public class FinderTest Cache.applicationProperties.setProperty("PAD_GAPS", Boolean.FALSE.toString()); - String seqData = "seq1seq1/8-18 ABCD--EF-GHIJI\n" + "seq2 A--BCDefHI\n" - + "seq3 --bcdEFH\n" + "seq4 aa---aMMMMMaaa\n"; + //@formatter:off + String seqData = + "seq1/8-18 ABCD--EF-GHIJI\n" + + "seq2 A--BCDefHI\n" + + "seq3 --bcdEFH\n" + + "seq4 aa---aMMMMMaaa\n"; + //@formatter:on af = new FileLoader().LoadFileWaitTillLoaded(seqData, DataSourceType.PASTE); av = af.getViewport(); @@ -95,20 +100,20 @@ public class FinderTest * find next match only */ Finder f = new Finder(av); - f.findNext("E.H", false, false); // 'E, any character, H' + f.findNext("E.H", false, false, false); // 'E, any character, H' // should match seq2 efH only SearchResultsI sr = f.getSearchResults(); - assertEquals(sr.getSize(), 1); + assertEquals(sr.getCount(), 1); List matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); assertEquals(matches.get(0).getStart(), 5); assertEquals(matches.get(0).getEnd(), 7); f = new Finder(av); - f.findAll("E.H", false, false); // 'E, any character, H' + f.findAll("E.H", false, false, false); // 'E, any character, H' // should match seq2 efH and seq3 EFH sr = f.getSearchResults(); - assertEquals(sr.getSize(), 2); + assertEquals(sr.getCount(), 2); matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); assertSame(matches.get(1).getSequence(), al.getSequenceAt(2)); @@ -129,9 +134,9 @@ public class FinderTest /* * find first match should return seq1 residue 9 */ - f.findNext("9", false, false); + f.findNext("9", false, false, false); SearchResultsI sr = f.getSearchResults(); - assertEquals(sr.getSize(), 1); + assertEquals(sr.getCount(), 1); List matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(0)); assertEquals(matches.get(0).getStart(), 9); @@ -139,11 +144,14 @@ public class FinderTest /* * find all matches should return seq1 and seq4 (others are too short) + * (and not matches in sequence ids) */ f = new Finder(av); - f.findAll("9", false, false); + String name = al.getSequenceAt(0).getName(); + al.getSequenceAt(0).setName("Q9XA0"); + f.findAll("9", false, false, false); sr = f.getSearchResults(); - assertEquals(sr.getSize(), 2); + assertEquals(sr.getCount(), 2); matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(0)); assertSame(matches.get(1).getSequence(), al.getSequenceAt(3)); @@ -151,12 +159,13 @@ public class FinderTest assertEquals(matches.get(0).getEnd(), 9); assertEquals(matches.get(1).getStart(), 9); assertEquals(matches.get(1).getEnd(), 9); + al.getSequenceAt(0).setName(name); /* * parsing of search string as integer is strict */ f = new Finder(av); - f.findNext(" 9", false, false); + f.findNext(" 9", false, false, false); assertTrue(f.getSearchResults().isEmpty()); } @@ -167,41 +176,41 @@ public class FinderTest public void testFindNext() { /* - * start at second sequence; colIndex of -1 + * start at second sequence; residueIndex of -1 * means sequence id / description is searched */ Finder f = new Finder(av); PA.setValue(f, "sequenceIndex", 1); - PA.setValue(f, "columnIndex", -1); - f.findNext("e", false, false); // matches id + PA.setValue(f, "residueIndex", -1); + f.findNext("e", false, false, false); // matches id assertTrue(f.getSearchResults().isEmpty()); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al.getSequenceAt(1)); - // colIndex is now 0 - for use in next find next + // residueIndex is now 0 - for use in next find next // searching A--BCDefHI - assertEquals(PA.getValue(f, "columnIndex"), 0); + assertEquals(PA.getValue(f, "residueIndex"), 0); f = new Finder(av); PA.setValue(f, "sequenceIndex", 1); - PA.setValue(f, "columnIndex", 0); - f.findNext("e", false, false); // matches in sequence + PA.setValue(f, "residueIndex", 0); + f.findNext("e", false, false, false); // matches in sequence assertTrue(f.getIdMatches().isEmpty()); - assertEquals(f.getSearchResults().getSize(), 1); + assertEquals(f.getSearchResults().getCount(), 1); List matches = f.getSearchResults().getResults(); assertEquals(matches.get(0).getStart(), 5); assertEquals(matches.get(0).getEnd(), 5); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); // still in the second sequence assertEquals(PA.getValue(f, "sequenceIndex"), 1); - // next column position to search from is 7 - assertEquals(PA.getValue(f, "columnIndex"), 7); + // next residue offset to search from is 5 + assertEquals(PA.getValue(f, "residueIndex"), 5); // find next from end of sequence - finds next sequence id f = new Finder(av); PA.setValue(f, "sequenceIndex", 1); - PA.setValue(f, "columnIndex", 7); - f.findNext("e", false, false); + PA.setValue(f, "residueIndex", 7); + f.findNext("e", false, false, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al.getSequenceAt(2)); assertTrue(f.getSearchResults().isEmpty()); @@ -223,7 +232,7 @@ public class FinderTest * find first match only */ Finder f = new Finder(av2); - f.findNext("rAF", false, true); + f.findNext("rAF", false, true, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertTrue(f.getSearchResults().isEmpty()); @@ -232,7 +241,7 @@ public class FinderTest * find all matches */ f = new Finder(av2); - f.findAll("rAF", false, true); + f.findAll("rAF", false, true, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertSame(f.getIdMatches().get(1), al2.getSequenceAt(1)); @@ -242,7 +251,7 @@ public class FinderTest * case sensitive */ f = new Finder(av2); - f.findAll("RAF", true, true); + f.findAll("RAF", true, true, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertTrue(f.getSearchResults().isEmpty()); @@ -258,12 +267,12 @@ public class FinderTest /* * sequence matches should have no duplicates */ - f.findAll("EFH", false, true); + f.findAll("EFH", false, true, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertSame(f.getIdMatches().get(1), al2.getSequenceAt(1)); - assertEquals(f.getSearchResults().getSize(), 2); + assertEquals(f.getSearchResults().getCount(), 2); SearchResultMatchI match = f.getSearchResults().getResults().get(0); assertSame(match.getSequence(), al2.getSequenceAt(1)); assertEquals(match.getStart(), 5); @@ -286,7 +295,7 @@ public class FinderTest * case insensitive; seq1 occurs twice in sequence id but * only one match should be returned */ - f.findAll("SEQ1", false, false); + f.findAll("SEQ1", false, false, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al.getSequenceAt(0)); SearchResultsI searchResults = f.getSearchResults(); @@ -296,7 +305,7 @@ public class FinderTest * case sensitive */ f = new Finder(av); - f.findAll("SEQ1", true, false); + f.findAll("SEQ1", true, false, false); searchResults = f.getSearchResults(); assertTrue(searchResults.isEmpty()); @@ -307,11 +316,11 @@ public class FinderTest AlignViewportI av2 = new AlignViewport(al2); al2.addSequence(new Sequence("aBz", "xyzabZpqrAbZ")); f = new Finder(av2); - f.findAll("ABZ", false, false); + f.findAll("ABZ", false, false, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(4)); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al2.getSequenceAt(4)); assertEquals(match.getStart(), 4); @@ -328,13 +337,17 @@ public class FinderTest @Test(groups = "Functional") public void testFind_findNext() { + // "seq1/8-18 ABCD--EF-GHIJI\n" + + // "seq2 A--BCDefHI\n" + + // "seq3 --bcdEFH\n" + + // "seq4 aa---aMMMMMaaa\n"; /* * efh should be matched in seq2 only */ FinderI f = new Finder(av); - f.findNext("EfH", false, false); + f.findNext("EfH", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 5); @@ -344,39 +357,39 @@ public class FinderTest * I should be found in seq1 (twice) and seq2 (once) */ f = new Finder(av); - f.findNext("I", false, false); // find next: seq1/16 + f.findNext("I", false, false, false); // find next: seq1/16 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); assertEquals(match.getEnd(), 16); - f.findNext("I", false, false); // find next: seq1/18 + f.findNext("I", false, false, false); // find next: seq1/18 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 18); assertEquals(match.getEnd(), 18); - f.findNext("I", false, false); // find next: seq2/8 + f.findNext("I", false, false, false); // find next: seq2/8 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 8); assertEquals(match.getEnd(), 8); - f.findNext("I", false, false); + f.findNext("I", false, false, false); assertTrue(f.getSearchResults().isEmpty()); /* * find should reset to start of alignment after a failed search */ - f.findNext("I", false, false); // find next: seq1/16 + f.findNext("I", false, false, false); // find next: seq1/16 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); @@ -388,12 +401,12 @@ public class FinderTest * result */ @Test(groups = "Functional") - public void testFind_maximalResultOnly() + public void testFindAll_maximalResultOnly() { Finder f = new Finder(av); - f.findAll("M+", false, false); + f.findAll("M+", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(3)); assertEquals(match.getStart(), 4); // dataset sequence positions @@ -404,12 +417,12 @@ public class FinderTest * Test finding all matches of a sequence pattern in an alignment */ @Test(groups = "Functional") - public void testFind_findAll() + public void testFindAll() { Finder f = new Finder(av); - f.findAll("EfH", false, false); + f.findAll("EfH", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 5); @@ -422,9 +435,9 @@ public class FinderTest /* * find all I should find 2 positions in seq1, 1 in seq2 */ - f.findAll("I", false, false); + f.findAll("I", false, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 3); + assertEquals(searchResults.getCount(), 3); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); @@ -443,16 +456,16 @@ public class FinderTest * Test finding all matches, case-sensitive */ @Test(groups = "Functional") - public void testFind_findAllCaseSensitive() + public void testFindAll_caseSensitive() { Finder f = new Finder(av); /* * BC should match seq1/9-10 and seq2/2-3 */ - f.findAll("BC", true, false); + f.findAll("BC", true, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 9); @@ -466,15 +479,15 @@ public class FinderTest * bc should match seq3/1-2 */ f = new Finder(av); - f.findAll("bc", true, false); + f.findAll("bc", true, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 1); assertEquals(match.getEnd(), 2); - f.findAll("bC", true, false); + f.findAll("bC", true, false, false); assertTrue(f.getSearchResults().isEmpty()); } @@ -482,7 +495,7 @@ public class FinderTest * Test finding next match of a sequence pattern in a selection group */ @Test(groups = "Functional") - public void testFind_inSelection() + public void testFindNext_inSelection() { /* * select sequences 2 and 3, columns 4-6 which contains @@ -497,10 +510,10 @@ public class FinderTest av.setSelectionGroup(sg); FinderI f = new Finder(av); - f.findNext("b", false, false); + f.findNext("b", false, false, false); assertTrue(f.getIdMatches().isEmpty()); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 2); @@ -509,23 +522,23 @@ public class FinderTest /* * a second Find should not return the 'b' in seq3 as outside the selection */ - f.findNext("b", false, false); + f.findNext("b", false, false, false); assertTrue(f.getSearchResults().isEmpty()); assertTrue(f.getIdMatches().isEmpty()); f = new Finder(av); - f.findNext("d", false, false); + f.findNext("d", false, false, false); assertTrue(f.getIdMatches().isEmpty()); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 4); assertEquals(match.getEnd(), 4); - f.findNext("d", false, false); + f.findNext("d", false, false, false); assertTrue(f.getIdMatches().isEmpty()); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 3); @@ -536,7 +549,7 @@ public class FinderTest * Test finding all matches of a search pattern in a selection group */ @Test(groups = "Functional") - public void testFind_findAllInSelection() + public void testFindAll_inSelection() { /* * select sequences 2 and 3, columns 4-6 which contains @@ -554,12 +567,12 @@ public class FinderTest * search for 'e' should match two sequence ids and one residue */ Finder f = new Finder(av); - f.findAll("e", false, false); + f.findAll("e", false, false, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al.getSequenceAt(1)); assertSame(f.getIdMatches().get(1), al.getSequenceAt(2)); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 4); @@ -569,7 +582,7 @@ public class FinderTest * search for 'Q' should match two sequence ids only */ f = new Finder(av); - f.findAll("Q", false, false); + f.findAll("Q", false, false, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al.getSequenceAt(1)); assertSame(f.getIdMatches().get(1), al.getSequenceAt(2)); @@ -600,10 +613,10 @@ public class FinderTest * search for 'I' should match two sequence positions */ Finder f = new Finder(av); - f.findAll("I", false, false); + f.findAll("I", false, false, false); assertTrue(f.getIdMatches().isEmpty()); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); @@ -615,7 +628,8 @@ public class FinderTest } /** - * Test that find does not report hidden positions + * Test that find does not report hidden positions, but does report matches that + * span hidden gaps */ @Test(groups = "Functional") public void testFind_withHiddenColumns() @@ -629,43 +643,92 @@ public class FinderTest */ /* - * hide 2-4 (CD- -BC bcd ---) + * hide column 3 only, search for aaa + * should find two matches: aa-[-]-aa and trailing aaa */ HiddenColumns hc = new HiddenColumns(); - hc.hideColumns(2, 4); + hc.hideColumns(3, 3); al.setHiddenColumns(hc); + Finder f = new Finder(av); + f.findAll("aaa", false, false, false); + SearchResultsI searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 2); + SearchResultMatchI match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 3); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 9); + assertEquals(match.getEnd(), 11); + + /* + * hide 2-4 (CD- -BC bcd ---) + */ + hc.hideColumns(2, 4); /* * find all search for D should ignore hidden positions in seq1 and seq3, * find the visible D in seq2 */ - Finder f = new Finder(av); - f.findAll("D", false, false); - SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); - SearchResultMatchI match = searchResults.getResults().get(0); + f = new Finder(av); + f.findAll("D", false, false, false); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 4); assertEquals(match.getEnd(), 4); /* + * search for AD should fail although these are now + * consecutive in the visible columns + */ + f = new Finder(av); + f.findAll("AD", false, false, false); + searchResults = f.getSearchResults(); + assertTrue(searchResults.isEmpty()); + + /* + * find all 'aaa' should find both start and end of seq4 + * (first run includes hidden gaps) + */ + f = new Finder(av); + f.findAll("aaa", false, false, false); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 2); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 3); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 9); + assertEquals(match.getEnd(), 11); + + /* * hide columns 2-5: - * find all 'aaa' should find end of seq4 only + * find all 'aaa' should match twice in seq4 + * (first match partly hidden, second all visible) */ hc.hideColumns(2, 5); f = new Finder(av); - f.findAll("aaa", false, false); + f.findAll("aaa", false, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 2); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 3); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(3)); assertEquals(match.getStart(), 9); assertEquals(match.getEnd(), 11); /* * find all 'BE' should not match across hidden columns in seq1 */ - f.findAll("BE", false, false); + f.findAll("BE", false, false, false); assertTrue(f.getSearchResults().isEmpty()); /* @@ -675,9 +738,9 @@ public class FinderTest hc.revealAllHiddenColumns(new ColumnSelection()); hc.hideColumns(8, 13); f = new Finder(av); - f.findNext("H", false, false); + f.findNext("H", false, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 6); @@ -718,9 +781,9 @@ public class FinderTest * should match seq2/1, seq2/7, not seq3/6 */ Finder f = new Finder(av); - f.findAll("[AH]", false, false); + f.findAll("[AH]", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 1); @@ -730,4 +793,123 @@ public class FinderTest assertEquals(match.getStart(), 7); assertEquals(match.getEnd(), 7); } + + @Test(groups = "Functional") + public void testFind_ignoreHiddenColumns() + { + /* + * 0 5 9 + * ABCD--EF-GHI + * A--BCDefHI + * --bcdEFH + * aa---aMMMMMaaa + */ + HiddenColumns hc = new HiddenColumns(); + hc.hideColumns(2, 4); + hc.hideColumns(7, 7); + al.setHiddenColumns(hc); + + /* + * now have + * 015689 + * AB-E-GHI + * A-DeHI + * --EF + * aaaMMMMaaa + */ + Finder f = new Finder(av); + f.findAll("abe", false, false, true); // true = ignore hidden + SearchResultsI searchResults = f.getSearchResults(); + + /* + * match of seq1 ABE made up of AB and E + * note only one match is counted + */ + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 2); + SearchResultMatchI match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 8); // A + assertEquals(match.getEnd(), 9); // B + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 12); // E + assertEquals(match.getEnd(), 12); + + f = new Finder(av); + f.findNext("a.E", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 2); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 8); // A + assertEquals(match.getEnd(), 9); // B + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 12); // E + assertEquals(match.getEnd(), 12); + + f.findNext("a.E", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 2); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 1); // a + assertEquals(match.getEnd(), 1); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 4); // D + assertEquals(match.getEnd(), 5); // e + + /* + * find all matching across two hidden column regions + * note one 'match' is returned as three contiguous matches + */ + f.findAll("BEG", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 3); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 9); // B + assertEquals(match.getEnd(), 9); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 12); // E + assertEquals(match.getEnd(), 12); + match = searchResults.getResults().get(2); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 14); // G + assertEquals(match.getEnd(), 14); + + /* + * now select columns 0-9 and search for A.*H + * this should match in the second sequence (split as 3 matches) + * but not the first (as H is outside the selection) + */ + SequenceGroup selection = new SequenceGroup(); + selection.setStartRes(0); + selection.setEndRes(9); + al.getSequences().forEach(seq -> selection.addSequence(seq, false)); + av.setSelectionGroup(selection); + f.findAll("A.*H", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 3); + // match made of contiguous matches A, DE, H + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 1); // A + assertEquals(match.getEnd(), 1); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 4); // D + assertEquals(match.getEnd(), 5); // E + match = searchResults.getResults().get(2); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 7); // H (there is no G) + assertEquals(match.getEnd(), 7); + } } diff --git a/test/jalview/controller/AlignViewControllerTest.java b/test/jalview/controller/AlignViewControllerTest.java index 7990d21..fde4da1 100644 --- a/test/jalview/controller/AlignViewControllerTest.java +++ b/test/jalview/controller/AlignViewControllerTest.java @@ -225,7 +225,7 @@ public class AlignViewControllerTest * test Match/Find works first */ FinderI f = new Finder(af.getViewport()); - f.findAll("M+", true, false); + f.findAll("M+", true, false, false); assertEquals( "Finder found different set of results to manually created SearchResults", sr, f.getSearchResults()); diff --git a/test/jalview/datamodel/SearchResultsTest.java b/test/jalview/datamodel/SearchResultsTest.java index a302d6e..838b259 100644 --- a/test/jalview/datamodel/SearchResultsTest.java +++ b/test/jalview/datamodel/SearchResultsTest.java @@ -297,11 +297,11 @@ public class SearchResultsTest SequenceI seq1 = new Sequence("", "abcdefghijklm"); SearchResultsI sr = new SearchResults(); sr.addResult(seq1, 3, 5); - assertEquals(1, sr.getSize()); + assertEquals(1, sr.getCount()); sr.addResult(seq1, 3, 5); - assertEquals(1, sr.getSize()); + assertEquals(1, sr.getCount()); sr.addResult(seq1, 3, 6); - assertEquals(2, sr.getSize()); + assertEquals(2, sr.getCount()); } /** diff --git a/test/jalview/io/cache/JvCacheableInputBoxTest.java b/test/jalview/io/cache/JvCacheableInputBoxTest.java index 4d25cb5..84a8be6 100644 --- a/test/jalview/io/cache/JvCacheableInputBoxTest.java +++ b/test/jalview/io/cache/JvCacheableInputBoxTest.java @@ -13,8 +13,8 @@ public class JvCacheableInputBoxTest private static final String TEST_CACHE_KEY = "CACHE.UNIT_TEST"; - private JvCacheableInputBox cacheBox = new JvCacheableInputBox( - TEST_CACHE_KEY); + private JvCacheableInputBox cacheBox = new JvCacheableInputBox<>( + TEST_CACHE_KEY, 20); @BeforeClass(alwaysRun = true) private void setUpCache()