From b7014049e421026d0f2d57179af9688472cd78f2 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 11 Dec 2019 12:55:37 +0000 Subject: [PATCH] JAL-3490 revised layout and search algorithm (more tests to be added) --- resources/lang/Messages.properties | 4 +- resources/lang/Messages_es.properties | 4 +- src/jalview/analysis/Finder.java | 487 +++++++++++++------- src/jalview/api/FinderI.java | 29 +- src/jalview/appletgui/Finder.java | 6 +- src/jalview/fts/core/GFTSPanel.java | 2 +- src/jalview/gui/AlignFrame.java | 5 +- src/jalview/gui/AnnotationColumnChooser.java | 3 +- src/jalview/gui/Finder.java | 53 +-- src/jalview/gui/SplitFrame.java | 2 +- src/jalview/io/cache/JvCacheableInputBox.java | 36 +- src/jalview/jbgui/GFinder.java | 194 ++++---- test/jalview/analysis/FinderTest.java | 125 ++--- .../controller/AlignViewControllerTest.java | 2 +- test/jalview/io/cache/JvCacheableInputBoxTest.java | 4 +- 15 files changed, 571 insertions(+), 385 deletions(-) diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index fdd15cd..6515367 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1403,4 +1403,6 @@ label.by_annotation_tooltip = Annotation Colour is configured from the main Colo label.show_linked_features = Show {0} features label.on_top = on top label.include_linked_features = Include {0} features -label.include_linked_tooltip = Include visible {0} features
converted to local sequence coordinates \ No newline at end of file +label.include_linked_tooltip = Include visible {0} features
converted to local sequence coordinates +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 daa4418..90625fe 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1404,4 +1404,6 @@ label.by_annotation_tooltip = El color de anotaci label.show_linked_features = Características de {0} label.on_top = encima label.include_linked_features = Incluir características de {0} -label.include_linked_tooltip = Incluir características de {0}
convertidas a coordenadas de secuencia local \ No newline at end of file +label.include_linked_tooltip = Incluir características de {0}
convertidas a coordenadas de secuencia local +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 ab71894..fc65379 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,127 +161,201 @@ public class Finder implements FinderI AlignmentI alignment = viewport.getAlignment(); int end = alignment.getHeight(); - while (sequenceIndex < end) + getSequence(ignoreHidden); + + boolean found = false; + while (!found || findAll) { - SequenceI seq = alignment.getSequenceAt(sequenceIndex); - boolean found = findNextMatch(seq, searchString, searchPattern, - searchDescription); - if (found && !findAll) + found = findNextMatch(searchString, searchPattern, searchDescription, + ignoreHidden); + if (sequenceIndex >= end) { - return; - } - if (!found) - { - sequenceIndex++; - columnIndex = -1; + break; } } } /** - * Answers the start-end column range of the contiguous visible regions of - * {@code sequence} starting at or after the given {@code column}. If there are - * no hidden columns, this just returns the remaining width of the sequence. - * Otherwise, visible columns are added as long as they are contiguous on the - * sequence (hidden regions only contain gaps). The range is restricted to the - * current {@code selection} if there is one. Answers null if there are no - * visible columns at or after {@code column}. + * 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 sequence - * @param column + * @param ignoreHidden * @return */ - protected Range getNextVisibleSequenceRegion(SequenceI sequence, - final int column) + private boolean getSequence(boolean ignoreHidden) { AlignmentI alignment = viewport.getAlignment(); - VisibleContigsIterator visibleRegions = alignment.getHiddenColumns() - .getVisContigsIterator(column, alignment.getWidth(), - false); - if (!visibleRegions.hasNext()) + if (sequenceIndex >= alignment.getHeight()) + { + seqToSearch = ""; + return false; + } + SequenceI seq = alignment.getSequenceAt(sequenceIndex); + SequenceGroup selection = viewport.getSelectionGroup(); + if (selection != null && !selection.contains(seq)) { - // off the end of the sequence - force search to next sequence - columnIndex = sequence.getLength(); - return null; + if (!nextSequence(ignoreHidden)) + { + return false; + } + seq = alignment.getSequenceAt(sequenceIndex); } - int[] visible = visibleRegions.next(); - int seqColStart = Math.max(column, visible[0]); - int seqColEnd = visible[1]; - // end residue of region (next residue if end position is gapped) - int endSeqPos = sequence.findPosition(visible[1]); - if (Comparison.isGap(sequence.getCharAt(visible[1]))) + String seqString = null; + if (ignoreHidden) { - endSeqPos--; + seqString = getVisibleSequence(seq); } - while (visibleRegions.hasNext()) + else { - visible = visibleRegions.next(); - int startSeqPos = sequence.findPosition(visible[0]); - if (startSeqPos - endSeqPos > 1) + int startCol = 0; + int endCol = seq.getLength() - 1; + this.searchedSequenceStartPosition = seq.getStart(); + if (selection != null) { - // this visible region is not contiguous - ignore it - break; + startCol = selection.getStartRes(); + endCol = Math.min(endCol, selection.getEndRes()); + this.searchedSequenceStartPosition = seq.findPosition(startCol); } - endSeqPos = sequence.findPosition(visible[1]); - seqColEnd = visible[1]; + seqString = seq.getSequenceAsString(startCol, endCol + 1); } - seqColEnd = Math.min(sequence.getLength() - 1, seqColEnd); /* - * restrict search to selected region if there is one + * remove gaps; note that even if this leaves an empty string, we 'search' + * the sequence anyway (for possible match on name or description) */ - SequenceGroup selection = viewport.getSelectionGroup(); - if (selection != null) + String ungapped = AlignSeq.extractGaps(Comparison.GapChars, seqString); + this.seqToSearch = ungapped; + + return true; + } + + /** + * 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 + */ + private String getVisibleSequence(SequenceI seq) + { + int seqStartCol = seq.findIndex(seq.getStart()); + int seqEndCol = seq.findIndex(seq.getStart() + seq.getLength() - 1); + Iterator visibleColumns = viewport.getViewAsVisibleContigs(true); + StringBuilder visibleSeq = new StringBuilder(seqEndCol - seqStartCol); + List fromRanges = new ArrayList<>(); + + 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 = seq.findPosition(range[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; @@ -274,83 +365,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) @@ -362,7 +436,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; } @@ -370,6 +444,63 @@ 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); + for (int i = 0; i < truePositions.length - 1; i += 2) + { + searchResults.addResult(seq, truePositions[i], truePositions[i + 1]); + } + } + + /** + * 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)
  • @@ -378,24 +509,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; @@ -421,7 +557,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; @@ -440,45 +576,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/FinderI.java b/src/jalview/api/FinderI.java index 19f6136..d74d879 100644 --- a/src/jalview/api/FinderI.java +++ b/src/jalview/api/FinderI.java @@ -14,35 +14,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/Finder.java b/src/jalview/appletgui/Finder.java index 2fc3441..4772580 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(); diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java index 232561c..54b2989 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -296,7 +296,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 fcb6572..09cff8e 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -2683,15 +2683,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/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index 5adecad..1b633c4 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -720,7 +720,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) { @@ -730,7 +730,6 @@ public class AnnotationColumnChooser extends AnnotationRowFilter this.setBorder(new TitledBorder( MessageManager.getString("label.search_filter"))); - searchBox.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXX"); searchBox.setToolTipText( MessageManager.getString("info.enter_search_text_here")); searchBox.getEditor().getEditorComponent() diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index a4d7ad0..217a390 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -28,9 +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; @@ -65,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; @@ -82,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); @@ -122,6 +106,7 @@ public class Finder extends GFinder } }); addEscapeHandler(); + Desktop.addInternalFrame(frame, MessageManager.getString("label.find"), MY_WIDTH, MY_HEIGHT); frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); @@ -172,13 +157,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) { @@ -198,6 +184,7 @@ public class Finder extends GFinder { av = ((AlignFrame) alignFrame).viewport; ap = ((AlignFrame) alignFrame).alignPanel; + ignoreHidden.setEnabled(av.hasHiddenColumns()); return true; } } @@ -274,13 +261,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(); @@ -388,4 +378,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/SplitFrame.java b/src/jalview/gui/SplitFrame.java index 25dedc5..5889489 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -784,7 +784,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 beef3e7..71e7c62 100644 --- a/src/jalview/io/cache/JvCacheableInputBox.java +++ b/src/jalview/io/cache/JvCacheableInputBox.java @@ -25,8 +25,8 @@ import jalview.util.MessageManager; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -64,12 +64,19 @@ public class JvCacheableInputBox extends JComboBox return enterWasPressed; } - public JvCacheableInputBox(String newCacheKey) + /** + * 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, int length) { super(); this.cacheKey = newCacheKey; setEditable(true); - addKeyListener(new KeyListener() + addKeyListener(new KeyAdapter() { @Override @@ -82,23 +89,16 @@ public class JvCacheableInputBox extends JComboBox } // let event bubble up } - - @Override - public void keyReleased(KeyEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void keyPressed(KeyEvent e) + }); + if (length > 0) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { - // TODO Auto-generated method stub - + sb.append("X"); } - }); - setPrototypeDisplayValue( - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + setPrototypeDisplayValue(sb.toString()); + } appCache = AppCache.getInstance(); initCachePopupMenu(); initCache(newCacheKey); diff --git a/src/jalview/jbgui/GFinder.java b/src/jalview/jbgui/GFinder.java index b433570..57beaee 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; @@ -47,19 +47,20 @@ import javax.swing.text.JTextComponent; 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(); + 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() { @@ -72,20 +73,98 @@ 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(4, 1)); + this.add(eastPanel, BorderLayout.EAST); + JPanel centrePanel = new JPanel(); + centrePanel.setLayout(new GridLayout(4, 1)); + this.add(centrePanel, BorderLayout.CENTER); + JPanel westPanel = new JPanel(); + westPanel.setLayout(new GridLayout(4, 1)); + this.add(westPanel, BorderLayout.WEST); + + /* + * '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.setFont(VERDANA_12); + ((JTextComponent) searchBox.getEditor().getEditorComponent()) + .addCaretListener(new CaretListener() + { + @Override + public void caretUpdate(CaretEvent e) + { + textfield_caretUpdate(); + } + }); + searchBox.getEditor().getEditorComponent() + .addKeyListener(new KeyAdapter() + { + @Override + public void keyPressed(KeyEvent e) + { + textfield_keyPressed(e); + } + }); + centrePanel.add(searchBox); - JLabel jLabelFind = new JLabel(MessageManager.getString("label.find")); - jLabelFind.setFont(VERDANA_12); + /* + * 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) @@ -93,11 +172,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) @@ -105,19 +183,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) @@ -125,60 +195,9 @@ public class GFinder extends JPanel createFeatures_actionPerformed(); } }); - searchBox.setFont(new java.awt.Font("Verdana", Font.PLAIN, 12)); - ((JTextComponent) searchBox.getEditor().getEditorComponent()) - .addCaretListener(new CaretListener() - { - @Override - public void caretUpdate(CaretEvent e) - { - textfield_caretUpdate(); - } - }); - searchBox.getEditor().getEditorComponent() - .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, 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) @@ -236,15 +255,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/test/jalview/analysis/FinderTest.java b/test/jalview/analysis/FinderTest.java index f3ae69f..43c48d0 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 = "seq1/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,7 +100,7 @@ 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); @@ -105,7 +110,7 @@ public class FinderTest 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); @@ -129,7 +134,7 @@ 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); List matches = sr.getResults(); @@ -141,7 +146,7 @@ public class FinderTest * find all matches should return seq1 and seq4 (others are too short) */ f = new Finder(av); - f.findAll("9", false, false); + f.findAll("9", false, false, false); sr = f.getSearchResults(); assertEquals(sr.getSize(), 2); matches = sr.getResults(); @@ -156,7 +161,7 @@ public class FinderTest * 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,25 +172,25 @@ 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); List matches = f.getSearchResults().getResults(); @@ -194,14 +199,14 @@ public class FinderTest 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 +228,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 +237,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 +247,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,7 +263,7 @@ 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)); @@ -286,7 +291,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 +301,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,7 +312,7 @@ 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(); @@ -328,11 +333,15 @@ 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); SearchResultMatchI match = searchResults.getResults().get(0); @@ -344,7 +353,7 @@ 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); match = searchResults.getResults().get(0); @@ -352,7 +361,7 @@ public class FinderTest 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); match = searchResults.getResults().get(0); @@ -360,7 +369,7 @@ public class FinderTest 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); match = searchResults.getResults().get(0); @@ -368,13 +377,13 @@ public class FinderTest 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); match = searchResults.getResults().get(0); @@ -391,7 +400,7 @@ public class FinderTest public void testFind_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); SearchResultMatchI match = searchResults.getResults().get(0); @@ -404,10 +413,10 @@ 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); SearchResultMatchI match = searchResults.getResults().get(0); @@ -422,7 +431,7 @@ 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); match = searchResults.getResults().get(0); @@ -450,7 +459,7 @@ public class FinderTest /* * 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); SearchResultMatchI match = searchResults.getResults().get(0); @@ -466,7 +475,7 @@ 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); match = searchResults.getResults().get(0); @@ -474,7 +483,7 @@ public class FinderTest assertEquals(match.getStart(), 1); assertEquals(match.getEnd(), 2); - f.findAll("bC", true, false); + f.findAll("bC", true, false, false); assertTrue(f.getSearchResults().isEmpty()); } @@ -497,7 +506,7 @@ 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); @@ -509,12 +518,12 @@ 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); @@ -522,7 +531,7 @@ public class FinderTest 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); @@ -554,7 +563,7 @@ 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)); @@ -569,7 +578,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,7 +609,7 @@ 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); @@ -637,7 +646,7 @@ public class FinderTest hc.hideColumns(3, 3); al.setHiddenColumns(hc); Finder f = new Finder(av); - f.findAll("aaa", false, false); + f.findAll("aaa", false, false, false); SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 2); SearchResultMatchI match = searchResults.getResults().get(0); @@ -659,7 +668,7 @@ public class FinderTest * find the visible D in seq2 */ f = new Finder(av); - f.findAll("D", false, false); + f.findAll("D", false, false, false); searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); match = searchResults.getResults().get(0); @@ -672,7 +681,7 @@ public class FinderTest * consecutive in the visible columns */ f = new Finder(av); - f.findAll("AD", false, false); + f.findAll("AD", false, false, false); searchResults = f.getSearchResults(); assertTrue(searchResults.isEmpty()); @@ -681,7 +690,7 @@ public class FinderTest * (first run includes hidden gaps) */ f = new Finder(av); - f.findAll("aaa", false, false); + f.findAll("aaa", false, false, false); searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 2); match = searchResults.getResults().get(0); @@ -695,23 +704,27 @@ public class FinderTest /* * hide columns 2-5: - * find all 'aaa' should find end of seq4 only - * (hidden columns not all gapped) + * 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.getSize(), 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()); /* @@ -721,7 +734,7 @@ 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); match = searchResults.getResults().get(0); @@ -764,7 +777,7 @@ 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); SearchResultMatchI match = searchResults.getResults().get(0); 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/io/cache/JvCacheableInputBoxTest.java b/test/jalview/io/cache/JvCacheableInputBoxTest.java index 010a4b2..9b899e2 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() -- 1.7.10.2