From 3ba65b98445f192453664357a1df25128fe07c84 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 13 Mar 2018 15:13:11 +0000 Subject: [PATCH] JAL-2839 refactored analysis.Finder and tests --- src/jalview/analysis/Finder.java | 342 ++++++++++++++++++++----------- src/jalview/appletgui/Finder.java | 26 +-- src/jalview/appletgui/IdPanel.java | 2 +- src/jalview/gui/Finder.java | 59 +++--- src/jalview/jbgui/GFinder.java | 88 ++++---- test/jalview/analysis/AlignSeqTest.java | 1 + test/jalview/analysis/FinderTest.java | 281 +++++++++++++++++++------ 7 files changed, 527 insertions(+), 272 deletions(-) diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java index d7bf0a2..de57d69 100644 --- a/src/jalview/analysis/Finder.java +++ b/src/jalview/analysis/Finder.java @@ -26,8 +26,10 @@ 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 java.util.List; import java.util.Vector; import com.stevesoft.pat.Regex; @@ -38,7 +40,7 @@ import com.stevesoft.pat.Regex; public class Finder { /* - * match residue locations + * matched residue locations */ private SearchResultsI searchResults; @@ -75,13 +77,14 @@ public class Finder /* * sequence index in alignment to search from */ - private int seqIndex; + private int sequenceIndex; /* - * residue position in sequence to search from, base 1 + * column position in sequence to search from, base 0 + * - absolute column number including any hidden columns * (position of last match for a repeat search) */ - private int resIndex; + private int columnIndex; /** * Constructor to start searching an alignment, optionally restricting results @@ -102,15 +105,15 @@ public class Finder * @param al * @param sel * @param seqindex - * @param resindex + * @param colindex */ public Finder(AlignmentI al, SequenceGroup sel, int seqindex, - int resindex) + int colindex) { this.alignment = al; this.selection = sel; - this.seqIndex = seqindex; - this.resIndex = resindex; + this.sequenceIndex = seqindex; + this.columnIndex = colindex; } /** @@ -124,10 +127,16 @@ public class Finder */ public void find(String theSearchString) { - String searchString = caseSensitive ? theSearchString.toUpperCase() - : theSearchString; - Regex regex = new Regex(searchString); - regex.setIgnoreCase(!caseSensitive); + if (findAll) + { + sequenceIndex = 0; + columnIndex = -1; + } + + String searchString = caseSensitive ? theSearchString + : theSearchString.toUpperCase(); + Regex searchPattern = new Regex(searchString); + searchPattern.setIgnoreCase(!caseSensitive); searchResults = new SearchResults(); idMatch = new Vector<>(); @@ -136,115 +145,206 @@ public class Finder selection = null; // ? ignore column-only selection } - boolean finished = false; int end = alignment.getHeight(); - while (!finished && (seqIndex < end)) + while (sequenceIndex < end) { - SequenceI seq = alignment.getSequenceAt(seqIndex); - - if ((selection != null) && !selection.contains(seq)) + SequenceI seq = alignment.getSequenceAt(sequenceIndex); + boolean found = findNext(seq, searchString, searchPattern); + if (found && !findAll) { - // this sequence is not in the selection - skip to next sequence - seqIndex++; - resIndex = -1; - continue; + return; } - - if (resIndex < 0) + if (!found) { - /* - * at start of sequence; try find by residue number, in sequence id, - * or (optionally) in sequence description - */ - resIndex = 0; - if (doNonMotifSearches(seq, searchString, regex)) - { - return; - } + sequenceIndex++; + columnIndex = -1; } + } + } + + /** + * Answers the start-end column range of the visible region starting at or + * after the given column. if there are no hidden columns, this just returns + * the remaining width of the alignment. Answers null if there are no visible + * columns at or after column. + */ + protected int[] getNextVisibleRegion(int column) + { + VisibleContigsIterator visibleRegions = alignment.getHiddenColumns() + .getVisContigsIterator(column, alignment.getWidth(), + false); + return visibleRegions.hasNext() ? visibleRegions.next() : null; + } + + /** + * 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. + * + * @param seq + * @param searchString + * @param searchPattern + * @return + */ + protected boolean findNext(SequenceI seq, String searchString, + Regex searchPattern) + { + if (selection != null && !selection.contains(seq)) + { + /* + * this sequence is not in the selection - advance to next sequence + */ + return false; + } - finished = searchSequenceString(seq, regex) && !findAll; + if (columnIndex < 0) + { + /* + * at start of sequence; try find by residue number, in sequence id, + * or (optionally) in sequence description + */ + if (doNonMotifSearches(seq, searchString, searchPattern)) + { + return true; + } + } - if (!finished) + /* + * search for next match in sequence string + */ + int end = seq.getLength(); + while (columnIndex < end) + { + if (searchNextVisibleRegion(seq, searchPattern)) { - seqIndex++; - resIndex = -1; + return true; } } + return false; } /** - * Searches the sequence, starting from resIndex (base 1), and - * adds matches to searchResults. The search is restricted to the - * selection region if there is one. Answers true if any match is - * added, else 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 regex + * @param searchPattern * @return */ - protected boolean searchSequenceString(SequenceI seq, Regex regex) + protected boolean searchNextVisibleRegion(SequenceI seq, Regex searchPattern) { /* - * Restrict search to selected region if there is one + * sequence columns to search (working in absolute column + * positions, base 0, including any hidden columns) */ - int seqColStart = 0; + int seqColStart = columnIndex; int seqColEnd = seq.getLength() - 1; - int residueOffset = 0; + + /* + * restrict search to (next) visible column region, + * in case there are hidden columns + */ + int[] visible = getNextVisibleRegion(columnIndex); + if (visible != null) + { + seqColStart = Math.max(seqColStart, visible[0]); + seqColEnd = Math.min(seqColEnd, visible[1]); + } + else + { + columnIndex = seqColEnd + 1; + return false; + } + + /* + * restrict search to selected region if there is one + */ if (selection != null) { - int selColEnd = selection.getEndRes(); - int selColStart = selection.getStartRes(); - if (selColStart > seqColEnd) + int selectionStart = selection.getStartRes(); + int selectionEnd = selection.getEndRes(); + if (selectionStart > seqColEnd || selectionEnd < seqColStart) { - return false; // sequence doesn't reach selection region + /* + * sequence region doesn't overlap selection region - + * no match, advance to next visible region + */ + columnIndex = seqColEnd + 1; + return false; } - seqColStart = selColStart; - seqColEnd = Math.min(seqColEnd, selColEnd); - residueOffset = seq.findPosition(selection.getStartRes()) - - seq.getStart(); + seqColStart = Math.max(seqColStart, selectionStart); + seqColEnd = Math.min(seqColEnd, selectionEnd); } - String seqString = seq.getSequenceAsString(seqColStart, seqColEnd + 1); + String seqString = seq.getSequenceAsString(seqColStart, seqColEnd + 1); String noGaps = AlignSeq.extractGaps(Comparison.GapChars, seqString); - SearchResultMatchI lastMatch = null; - boolean found = false; - - for (int r = resIndex; r < noGaps.length(); r++) + if (searchPattern.search(noGaps)) + { + int sequenceStartPosition = seq.findPosition(seqColStart); + recordMatch(seq, searchPattern, sequenceStartPosition); + return true; + } + else { /* - * searchFrom position is base 0, r is base 1, - * so search is from the position after the r'th residue + * no match - advance columnIndex past this visible region + * so the next visible region (if any) is searched next */ - if (regex.searchFrom(noGaps, r)) - { - resIndex = regex.matchedFrom(); - resIndex += residueOffset; // add back #residues before selection region - int matchStartPosition = resIndex + seq.getStart(); - int matchEndPosition = matchStartPosition + regex.charsMatched() - - 1; - if (lastMatch == null || !lastMatch.contains(seq, - matchStartPosition, matchEndPosition)) - { - lastMatch = searchResults.addResult(seq, matchStartPosition, - matchEndPosition); - found = true; - } - if (!findAll) - { - resIndex++; - return true; - } - r = resIndex; - } - else - { - break; - } + columnIndex = seqColEnd + 1; + } + + 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 + * start of the matched region, ready for the next search. Answers true if a + * match was added, else false. + * + * @param seq + * @param searchPattern + * @param firstResiduePosition + * @return + */ + protected boolean recordMatch(SequenceI seq, Regex searchPattern, + int firstResiduePosition) + { + /* + * get start/end of the match in sequence coordinates + */ + int offset = searchPattern.matchedFrom(); + int matchStartPosition = firstResiduePosition + offset; + int matchEndPosition = matchStartPosition + + searchPattern.charsMatched() - 1; + + /* + * update columnIndex to next column after the start of the match + * (findIndex returns a value base 1, columnIndex is held base 0) + */ + columnIndex = seq.findIndex(matchStartPosition); + + /* + * check that this match is not a subset of the previous one (JAL-2302) + */ + List matches = searchResults.getResults(); + SearchResultMatchI lastMatch = matches.isEmpty() ? null + : matches.get(matches.size() - 1); + + if (lastMatch == null || !lastMatch.contains(seq, matchStartPosition, + matchEndPosition)) + { + searchResults.addResult(seq, matchStartPosition, matchEndPosition); + return true; } - return found; + + return false; } /** @@ -254,26 +354,30 @@ public class Finder *
  • match search string to sequence id
  • *
  • match search string to sequence description (optional)
  • * - * Answers true if a match is found and we are not doing 'find all' (so this - * search action is complete), else false. + * Answers true if a match is found, else false. * * @param seq * @param searchString - * @param regex + * @param searchPattern * @return */ protected boolean doNonMotifSearches(SequenceI seq, String searchString, - Regex regex) + Regex searchPattern) { - if (searchForResidueNumber(seq, searchString) && !findAll) + /* + * position sequence search to start of sequence + */ + columnIndex = 0; + + if (searchForResidueNumber(seq, searchString)) { return true; } - if (searchSequenceName(seq, regex) && !findAll) + if (searchSequenceName(seq, searchPattern)) { return true; } - if (searchSequenceDescription(seq, regex) && !findAll) + if (includeDescription && searchSequenceDescription(seq, searchPattern)) { return true; } @@ -281,22 +385,18 @@ public class Finder } /** - * Searches for a match with the sequence description, if that option was - * requested, and if found, adds the sequence to the list of match ids (but - * not as a duplicate). Answers true if a match was added, else false. + * Searches for a match with the sequence description, and if found, adds the + * sequence to the list of match ids (but not as a duplicate). Answers true if + * a match was added, else false. * * @param seq - * @param regex + * @param searchPattern * @return */ - protected boolean searchSequenceDescription(SequenceI seq, Regex regex) + protected boolean searchSequenceDescription(SequenceI seq, Regex searchPattern) { - if (!includeDescription) - { - return false; - } String desc = seq.getDescription(); - if (desc != null && regex.search(desc) && !idMatch.contains(seq)) + if (desc != null && searchPattern.search(desc) && !idMatch.contains(seq)) { idMatch.addElement(seq); return true; @@ -310,12 +410,12 @@ public class Finder * a match was added, else false. * * @param seq - * @param regex + * @param searchPattern * @return */ - protected boolean searchSequenceName(SequenceI seq, Regex regex) + protected boolean searchSequenceName(SequenceI seq, Regex searchPattern) { - if (regex.search(seq.getName()) && !idMatch.contains(seq)) + if (searchPattern.search(seq.getName()) && !idMatch.contains(seq)) { idMatch.addElement(seq); return true; @@ -325,7 +425,8 @@ public class Finder /** * Tries to interpret the search string as a residue position, and if valid, - * adds the position to the search results + * adds the position to the search results and returns true, else answers + * false */ protected boolean searchForResidueNumber(SequenceI seq, String searchString) { @@ -365,8 +466,8 @@ public class Finder } /** - * Returns the (possibly empty) list of matching sequences (when search - * includes searching sequence names) + * Returns the (possibly empty) list of sequences matched on sequence name or + * description * * @return */ @@ -376,7 +477,9 @@ public class Finder } /** - * @return the searchResults + * Answers the search results (possibly empty) from the last search + * + * @return */ public SearchResultsI getSearchResults() { @@ -384,19 +487,26 @@ public class Finder } /** - * @return the resIndex + * Answers the absolute column position (base 0, including any hidden columns) + * of the start of the last sequence motif (residue pattern) match found. A + * 'Find next' will search from the next position. + * + * @return */ - public int getResIndex() + public int getColumnIndex() { - return resIndex; + return columnIndex; } /** - * @return the seqIndex + * Answers the offset in the alignment (0..) of the sequence in which the last + * match was found (if any) + * + * @return */ - public int getSeqIndex() + public int getSequenceIndex() { - return seqIndex; + return sequenceIndex; } /** diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java index f44387f..286cd5f 100644 --- a/src/jalview/appletgui/Finder.java +++ b/src/jalview/appletgui/Finder.java @@ -68,21 +68,21 @@ public class Finder extends Panel implements ActionListener private SearchResultsI searchResults; /* - * sequence index and residue position of last match, + * sequence index and column position of last match, * for current search and per viewport */ private int seqIndex = 0; - private int resIndex = -1; + private int colIndex = -1; Map seqIndices; - Map resIndices; + Map colIndices; public Finder(final AlignmentPanel ap) { seqIndices = new HashMap<>(); - resIndices = new HashMap<>(); + colIndices = new HashMap<>(); try { @@ -126,7 +126,7 @@ public class Finder extends Panel implements ActionListener else if (evt.getSource() == findAll) { - resIndex = -1; + colIndex = -1; seqIndex = 0; doSearch(true); } @@ -165,19 +165,19 @@ public class Finder extends Panel implements ActionListener ap = ap.av.applet.currentAlignFrame.alignPanel; av = ap.av; seqIndex = 0; - resIndex = -1; + colIndex = -1; if (seqIndices.containsKey(av)) { seqIndex = seqIndices.get(av).intValue(); } - if (resIndices.containsKey(av)) + if (colIndices.containsKey(av)) { - resIndex = resIndices.get(av).intValue(); + colIndex = colIndices.get(av).intValue(); } } createFeatures.setEnabled(false); jalview.analysis.Finder finder = new jalview.analysis.Finder( - av.getAlignment(), av.getSelectionGroup(), seqIndex, resIndex); + av.getAlignment(), av.getSelectionGroup(), seqIndex, colIndex); finder.setCaseSensitive(caseSensitive.getState()); finder.setIncludeDescription(searchDescription.getState()); finder.setFindAll(doFindAll); @@ -186,10 +186,10 @@ public class Finder extends Panel implements ActionListener finder.find(searchString); - seqIndex = finder.getSeqIndex(); - resIndex = finder.getResIndex(); + seqIndex = finder.getSequenceIndex(); + colIndex = finder.getColumnIndex(); seqIndices.put(av, seqIndex); - resIndices.put(av, resIndex); + colIndices.put(av, colIndex); searchResults = finder.getSearchResults(); Vector idMatch = finder.getIdMatch(); @@ -213,7 +213,7 @@ public class Finder extends Panel implements ActionListener { ap.alignFrame.statusBar.setText( MessageManager.getString("label.finished_searching")); - resIndex = -1; + colIndex = -1; seqIndex = 0; } else diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java index 15e269c..af1c47b 100755 --- a/src/jalview/appletgui/IdPanel.java +++ b/src/jalview/appletgui/IdPanel.java @@ -395,7 +395,7 @@ public class IdPanel extends Panel { idCanvas.setHighlighted(list); - if (list == null) + if (list == null || list.isEmpty()) { return; } diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index 1cbe6ab..c5b25bf 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -45,6 +45,7 @@ import javax.swing.JComponent; import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; import javax.swing.KeyStroke; +import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; /** @@ -80,21 +81,26 @@ public class Finder extends GFinder */ private int seqIndex = 0; - private int resIndex = -1; + private int colIndex = -1; - Map seqIndices; + private Map seqIndices; - Map resIndices; + private Map colIndices; 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. + * 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); - focusfixed = false; } /** @@ -109,13 +115,13 @@ public class Finder extends GFinder av = viewport; ap = alignPanel; seqIndices = new HashMap<>(); - resIndices = new HashMap<>(); - focusfixed = true; + colIndices = new HashMap<>(); + focusfixed = viewport != null; frame = new JInternalFrame(); frame.setContentPane(this); frame.setLayer(JLayeredPane.PALETTE_LAYER); frame.addInternalFrameListener( - new javax.swing.event.InternalFrameAdapter() + new InternalFrameAdapter() { @Override public void internalFrameClosing(InternalFrameEvent e) @@ -148,12 +154,10 @@ public class Finder extends GFinder } /** - * Performs the 'Find Next' action. - * - * @param e + * Performs the 'Find Next' action on the alignment panel with focus */ @Override - public void findNext_actionPerformed(ActionEvent e) + public void findNext_actionPerformed() { if (getFocusedViewport()) { @@ -162,27 +166,20 @@ public class Finder extends GFinder } /** - * Performs the 'Find All' action. - * - * @param e + * Performs the 'Find All' action on the alignment panel with focus */ @Override - public void findAll_actionPerformed(ActionEvent e) + public void findAll_actionPerformed() { if (getFocusedViewport()) { - resIndex = -1; + colIndex = -1; seqIndex = 0; doSearch(true); } } /** - * do we only search a given alignment view ? - */ - private boolean focusfixed; - - /** * 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 @@ -217,14 +214,14 @@ public class Finder extends GFinder * panel where we have previously searched */ seqIndex = 0; - resIndex = -1; + colIndex = -1; if (seqIndices.containsKey(av)) { seqIndex = seqIndices.get(av).intValue(); } - if (resIndices.containsKey(av)) + if (colIndices.containsKey(av)) { - resIndex = resIndices.get(av).intValue(); + colIndex = colIndices.get(av).intValue(); } return true; } @@ -291,7 +288,7 @@ public class Finder extends GFinder // TODO: add switches to control what is searched - sequences, IDS, // descriptions, features jalview.analysis.Finder finder = new jalview.analysis.Finder( - av.getAlignment(), av.getSelectionGroup(), seqIndex, resIndex); + av.getAlignment(), av.getSelectionGroup(), seqIndex, colIndex); finder.setCaseSensitive(caseSensitive.isSelected()); finder.setIncludeDescription(searchDescription.isSelected()); @@ -299,10 +296,10 @@ public class Finder extends GFinder finder.find(searchString); - seqIndex = finder.getSeqIndex(); - resIndex = finder.getResIndex(); + seqIndex = finder.getSequenceIndex(); + colIndex = finder.getColumnIndex(); seqIndices.put(av, seqIndex); - resIndices.put(av, resIndex); + colIndices.put(av, colIndex); searchResults = finder.getSearchResults(); Vector idMatch = finder.getIdMatch(); @@ -325,7 +322,7 @@ public class Finder extends GFinder JvOptionPane.showInternalMessageDialog(this, MessageManager.getString("label.finished_searching"), null, JvOptionPane.INFORMATION_MESSAGE); - resIndex = -1; + colIndex = -1; seqIndex = 0; } else @@ -346,7 +343,7 @@ public class Finder extends GFinder } JvOptionPane.showInternalMessageDialog(this, message, null, JvOptionPane.INFORMATION_MESSAGE); - resIndex = -1; + colIndex = -1; seqIndex = 0; } } diff --git a/src/jalview/jbgui/GFinder.java b/src/jalview/jbgui/GFinder.java index 1ea4ab5..b433570 100755 --- a/src/jalview/jbgui/GFinder.java +++ b/src/jalview/jbgui/GFinder.java @@ -47,41 +47,20 @@ import javax.swing.text.JTextComponent; public class GFinder extends JPanel { - JLabel jLabelFind = new JLabel(); + private static final java.awt.Font VERDANA_12 = new java.awt.Font("Verdana", 0, + 12); - protected JButton findAll = new JButton(); - - protected JButton findNext = new JButton(); - - JPanel actionsPanel = new JPanel(); - - GridLayout gridLayout1 = new GridLayout(); + private static final String FINDER_CACHE_KEY = "CACHE.FINDER"; protected JButton createFeatures = new JButton(); - protected JvCacheableInputBox searchBox = new JvCacheableInputBox( + protected JvCacheableInputBox searchBox = new JvCacheableInputBox<>( getCacheKey()); - BorderLayout mainBorderLayout = new BorderLayout(); - - JPanel jPanel2 = new JPanel(); - - JPanel jPanel3 = new JPanel(); - - JPanel jPanel4 = new JPanel(); - - BorderLayout borderLayout2 = new BorderLayout(); - - JPanel jPanel6 = new JPanel(); - protected JCheckBox caseSensitive = new JCheckBox(); protected JCheckBox searchDescription = new JCheckBox(); - GridLayout optionsGridLayout = new GridLayout(); - - private static final String FINDER_CACHE_KEY = "CACHE.FINDER"; - public GFinder() { try @@ -95,35 +74,47 @@ public class GFinder extends JPanel private void jbInit() throws Exception { - jLabelFind.setFont(new java.awt.Font("Verdana", 0, 12)); - jLabelFind.setText(MessageManager.getString("label.find")); + BorderLayout mainBorderLayout = new BorderLayout(); this.setLayout(mainBorderLayout); - findAll.setFont(new java.awt.Font("Verdana", 0, 12)); - findAll.setText(MessageManager.getString("action.find_all")); + mainBorderLayout.setHgap(5); + mainBorderLayout.setVgap(5); + + JLabel jLabelFind = new JLabel(MessageManager.getString("label.find")); + jLabelFind.setFont(VERDANA_12); + + JButton findAll = new JButton( + MessageManager.getString("action.find_all")); + findAll.setFont(VERDANA_12); findAll.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(ActionEvent e) { - findAll_actionPerformed(e); + findAll_actionPerformed(); } }); - findNext.setFont(new java.awt.Font("Verdana", 0, 12)); - findNext.setText(MessageManager.getString("action.find_next")); + + JButton findNext = new JButton( + MessageManager.getString("action.find_next")); + findNext.setFont(VERDANA_12); findNext.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(ActionEvent e) { - findNext_actionPerformed(e); + findNext_actionPerformed(); } }); + + JPanel actionsPanel = new JPanel(); + GridLayout gridLayout1 = new GridLayout(); actionsPanel.setLayout(gridLayout1); gridLayout1.setHgap(0); gridLayout1.setRows(3); gridLayout1.setVgap(2); + createFeatures.setEnabled(false); - createFeatures.setFont(new java.awt.Font("Verdana", 0, 12)); + 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() @@ -141,7 +132,7 @@ public class GFinder extends JPanel @Override public void caretUpdate(CaretEvent e) { - textfield_caretUpdate(e); + textfield_caretUpdate(); } }); searchBox.getEditor().getEditorComponent() @@ -153,11 +144,7 @@ public class GFinder extends JPanel textfield_keyPressed(e); } }); - mainBorderLayout.setHgap(5); - mainBorderLayout.setVgap(5); - jPanel4.setLayout(borderLayout2); - jPanel2.setPreferredSize(new Dimension(10, 1)); - jPanel3.setPreferredSize(new Dimension(10, 1)); + caseSensitive.setHorizontalAlignment(SwingConstants.LEFT); caseSensitive.setText(MessageManager.getString("label.match_case")); @@ -169,6 +156,13 @@ public class GFinder extends JPanel 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); @@ -176,6 +170,7 @@ public class GFinder extends JPanel JPanel optionsPanel = new JPanel(); + GridLayout optionsGridLayout = new GridLayout(); optionsGridLayout.setHgap(0); optionsGridLayout.setRows(2); optionsGridLayout.setVgap(2); @@ -193,16 +188,16 @@ public class GFinder extends JPanel if (!searchBox.isPopupVisible()) { e.consume(); - findNext_actionPerformed(null); + findNext_actionPerformed(); } } } - protected void findNext_actionPerformed(ActionEvent e) + protected void findNext_actionPerformed() { } - protected void findAll_actionPerformed(ActionEvent e) + protected void findAll_actionPerformed() { } @@ -210,9 +205,10 @@ public class GFinder extends JPanel { } - public void textfield_caretUpdate(CaretEvent e) + public void textfield_caretUpdate() { - if (searchBox.getUserInput().indexOf(">") > -1) + // disabled as appears to be running a non-functional + if (false && searchBox.getUserInput().indexOf(">") > -1) { SwingUtilities.invokeLater(new Runnable() { @@ -233,7 +229,7 @@ public class GFinder extends JPanel str = jalview.analysis.AlignSeq.extractGaps( jalview.util.Comparison.GapChars, al.getSequenceAt(0).getSequenceAsString()); - + // todo and what? set str as searchBox text? } } }); diff --git a/test/jalview/analysis/AlignSeqTest.java b/test/jalview/analysis/AlignSeqTest.java index 837e970..bd827f9 100644 --- a/test/jalview/analysis/AlignSeqTest.java +++ b/test/jalview/analysis/AlignSeqTest.java @@ -52,6 +52,7 @@ public class AlignSeqTest assertEquals(AlignSeq.extractGaps(" -", " AC-G.T"), "ACG.T"); assertEquals(AlignSeq.extractGaps(" -.", " AC-G.T ."), "ACGT"); assertEquals(AlignSeq.extractGaps("-", " AC-G.T"), " ACG.T"); + assertEquals(AlignSeq.extractGaps("-. ", " -. .-"), ""); } @Test(groups = { "Functional" }) diff --git a/test/jalview/analysis/FinderTest.java b/test/jalview/analysis/FinderTest.java index 771712d..e959e62 100644 --- a/test/jalview/analysis/FinderTest.java +++ b/test/jalview/analysis/FinderTest.java @@ -27,6 +27,8 @@ import static org.testng.Assert.assertTrue; import jalview.bin.Cache; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.HiddenColumns; import jalview.datamodel.SearchResultMatchI; import jalview.datamodel.SearchResultsI; import jalview.datamodel.Sequence; @@ -61,7 +63,7 @@ public class FinderTest Cache.applicationProperties.setProperty("PAD_GAPS", Boolean.FALSE.toString()); - String seqData = "seq1seq1/8-16 ABCD--EF-GHI\n" + "seq2 A--BCDefHI\n" + String seqData = "seq1seq1/8-18 ABCD--EF-GHIJI\n" + "seq2 A--BCDefHI\n" + "seq3 --bcdEFH\n" + "seq4 aa---aMMMMMaaa\n"; af = new FileLoader().LoadFileWaitTillLoaded(seqData, DataSourceType.PASTE); @@ -83,7 +85,7 @@ public class FinderTest SearchResultsI sr = f.getSearchResults(); assertEquals(sr.getSize(), 1); List matches = sr.getResults(); - assertSame(al.getSequenceAt(1), matches.get(0).getSequence()); + assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); assertEquals(matches.get(0).getStart(), 5); assertEquals(matches.get(0).getEnd(), 7); @@ -94,8 +96,8 @@ public class FinderTest sr = f.getSearchResults(); assertEquals(sr.getSize(), 2); matches = sr.getResults(); - assertSame(al.getSequenceAt(1), matches.get(0).getSequence()); - assertSame(al.getSequenceAt(2), matches.get(1).getSequence()); + assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); + assertSame(matches.get(1).getSequence(), al.getSequenceAt(2)); assertEquals(matches.get(0).getStart(), 5); assertEquals(matches.get(0).getEnd(), 7); assertEquals(matches.get(1).getStart(), 4); @@ -117,7 +119,7 @@ public class FinderTest SearchResultsI sr = f.getSearchResults(); assertEquals(sr.getSize(), 1); List matches = sr.getResults(); - assertSame(al.getSequenceAt(0), matches.get(0).getSequence()); + assertSame(matches.get(0).getSequence(), al.getSequenceAt(0)); assertEquals(matches.get(0).getStart(), 9); assertEquals(matches.get(0).getEnd(), 9); @@ -130,8 +132,8 @@ public class FinderTest sr = f.getSearchResults(); assertEquals(sr.getSize(), 2); matches = sr.getResults(); - assertSame(al.getSequenceAt(0), matches.get(0).getSequence()); - assertSame(al.getSequenceAt(3), matches.get(1).getSequence()); + assertSame(matches.get(0).getSequence(), al.getSequenceAt(0)); + assertSame(matches.get(1).getSequence(), al.getSequenceAt(3)); assertEquals(matches.get(0).getStart(), 9); assertEquals(matches.get(0).getEnd(), 9); assertEquals(matches.get(1).getStart(), 9); @@ -152,7 +154,7 @@ public class FinderTest public void testFindNext() { /* - * start at second sequence; resIndex of -1 + * start at second sequence; colIndex of -1 * means sequence id / description is searched */ Finder f = new Finder(al, null, 1, -1); @@ -162,8 +164,9 @@ public class FinderTest assertEquals(f.getIdMatch().size(), 1); assertSame(f.getIdMatch().get(0), al.getSequenceAt(1)); - // resIndex is now 0 - for use in next find next - assertEquals(f.getResIndex(), 0); + // colIndex is now 0 - for use in next find next + // searching A--BCDefHI + assertEquals(f.getColumnIndex(), 0); f = new Finder(al, null, 1, 0); f.find("e"); // matches in sequence assertTrue(f.getIdMatch().isEmpty()); @@ -173,16 +176,16 @@ public class FinderTest assertEquals(matches.get(0).getEnd(), 5); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); // still in the second sequence - assertEquals(f.getSeqIndex(), 1); - // next residue position to search from is 5 - // (used as base 0 by RegEx so the same as 6 if base 1) - assertEquals(f.getResIndex(), 5); + assertEquals(f.getSequenceIndex(), 1); + // next column position to search from is 7 + assertEquals(f.getColumnIndex(), 7); // find next from end of sequence - finds next sequence id - f = new Finder(al, null, 1, 5); + f = new Finder(al, null, 1, 7); f.find("e"); assertEquals(f.getIdMatch().size(), 1); assertSame(f.getIdMatch().get(0), al.getSequenceAt(2)); + assertTrue(f.getSearchResults().isEmpty()); } /** @@ -250,13 +253,13 @@ public class FinderTest assertEquals(f.getSearchResults().getSize(), 2); SearchResultMatchI match = f.getSearchResults().getResults().get(0); - assertSame(al2.getSequenceAt(1), match.getSequence()); - assertEquals(5, match.getStart()); - assertEquals(7, match.getEnd()); + assertSame(match.getSequence(), al2.getSequenceAt(1)); + assertEquals(match.getStart(), 5); + assertEquals(match.getEnd(), 7); match = f.getSearchResults().getResults().get(1); - assertSame(al2.getSequenceAt(2), match.getSequence()); - assertEquals(4, match.getStart()); - assertEquals(6, match.getEnd()); + assertSame(match.getSequence(), al2.getSequenceAt(2)); + assertEquals(match.getStart(), 4); + assertEquals(match.getEnd(), 6); } /** @@ -275,7 +278,8 @@ public class FinderTest f.find("SEQ1"); assertEquals(f.getIdMatch().size(), 1); assertSame(f.getIdMatch().get(0), al.getSequenceAt(0)); - assertTrue(f.getSearchResults().isEmpty()); + SearchResultsI searchResults = f.getSearchResults(); + assertTrue(searchResults.isEmpty()); /* * case sensitive @@ -284,7 +288,8 @@ public class FinderTest f.setFindAll(true); f.setCaseSensitive(true); f.find("SEQ1"); - assertTrue(f.getSearchResults().isEmpty()); + searchResults = f.getSearchResults(); + assertTrue(searchResults.isEmpty()); /* * match both sequence id and sequence @@ -296,31 +301,66 @@ public class FinderTest f.find("ABZ"); assertEquals(f.getIdMatch().size(), 1); assertSame(f.getIdMatch().get(0), al2.getSequenceAt(4)); - assertEquals(f.getSearchResults().getSize(), 2); - SearchResultMatchI match = f.getSearchResults().getResults().get(0); - assertSame(al2.getSequenceAt(4), match.getSequence()); - assertEquals(4, match.getStart()); - assertEquals(6, match.getEnd()); - match = f.getSearchResults().getResults().get(1); - assertSame(al2.getSequenceAt(4), match.getSequence()); - assertEquals(10, match.getStart()); - assertEquals(12, match.getEnd()); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 2); + SearchResultMatchI match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al2.getSequenceAt(4)); + assertEquals(match.getStart(), 4); + assertEquals(match.getEnd(), 6); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al2.getSequenceAt(4)); + assertEquals(match.getStart(), 10); + assertEquals(match.getEnd(), 12); } /** * Test finding next match of a sequence pattern in an alignment */ @Test(groups = "Functional") - public void testFind() + public void testFind_findNext() { + /* + * efh should be matched in seq2 only + */ Finder f = new Finder(al, null); f.find("EfH"); SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(1), match.getSequence()); - assertEquals(5, match.getStart()); - assertEquals(7, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 5); + assertEquals(match.getEnd(), 7); + + /* + * I should be found in seq1 (twice) and seq2 (once) + */ + f = new Finder(al, null); + f.find("I"); // find next: seq1/16 + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 16); + assertEquals(match.getEnd(), 16); + + f.find("I"); // find next: seq1/18 + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 18); + assertEquals(match.getEnd(), 18); + + f.find("I"); // find next: seq2/8 + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 8); + assertEquals(match.getEnd(), 8); + + f.find("I"); + assertTrue(f.getSearchResults().isEmpty()); } /** @@ -336,9 +376,9 @@ public class FinderTest SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(3), match.getSequence()); - assertEquals(4, match.getStart()); // dataset sequence positions - assertEquals(8, match.getEnd()); // base 1 + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 4); // dataset sequence positions + assertEquals(match.getEnd(), 8); // base 1 } /** @@ -353,13 +393,32 @@ public class FinderTest SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 2); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(1), match.getSequence()); - assertEquals(5, match.getStart()); - assertEquals(7, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 5); + assertEquals(match.getEnd(), 7); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(2)); + assertEquals(match.getStart(), 4); + assertEquals(match.getEnd(), 6); + + /* + * find all I should find 2 positions in seq1, 1 in seq2 + */ + f.find("I"); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 3); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 16); + assertEquals(match.getEnd(), 16); match = searchResults.getResults().get(1); - assertSame(al.getSequenceAt(2), match.getSequence()); - assertEquals(4, match.getStart()); - assertEquals(6, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 18); + assertEquals(match.getEnd(), 18); + match = searchResults.getResults().get(2); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 8); + assertEquals(match.getEnd(), 8); } /** @@ -371,17 +430,38 @@ public class FinderTest Finder f = new Finder(al, null); f.setCaseSensitive(true); f.setFindAll(true); + + /* + * BC should match seq1/9-10 and seq2/2-3 + */ f.find("BC"); SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 2); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(0), match.getSequence()); + assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 9); assertEquals(match.getEnd(), 10); match = searchResults.getResults().get(1); - assertSame(al.getSequenceAt(1), match.getSequence()); + assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 2); assertEquals(match.getEnd(), 3); + + /* + * bc should match seq3/1-2 + */ + f = new Finder(al, null); + f.setCaseSensitive(true); + f.setFindAll(true); + f.find("bc"); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(2)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 2); + + f.find("bC"); + assertTrue(f.getSearchResults().isEmpty()); } /** @@ -407,9 +487,9 @@ public class FinderTest SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(1), match.getSequence()); - assertEquals(2, match.getStart()); - assertEquals(2, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 2); + assertEquals(match.getEnd(), 2); /* * a second Find should not return the 'b' in seq3 as outside the selection @@ -424,17 +504,17 @@ public class FinderTest searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(1), match.getSequence()); - assertEquals(4, match.getStart()); - assertEquals(4, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 4); + assertEquals(match.getEnd(), 4); f.find("d"); assertTrue(f.getIdMatch().isEmpty()); searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(2), match.getSequence()); - assertEquals(3, match.getStart()); - assertEquals(3, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(2)); + assertEquals(match.getStart(), 3); + assertEquals(match.getEnd(), 3); } /** @@ -466,9 +546,9 @@ public class FinderTest SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 1); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(2), match.getSequence()); - assertEquals(4, match.getStart()); - assertEquals(4, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(2)); + assertEquals(match.getStart(), 4); + assertEquals(match.getEnd(), 4); /* * search for 'Q' should match two sequence ids only @@ -511,12 +591,83 @@ public class FinderTest SearchResultsI searchResults = f.getSearchResults(); assertEquals(searchResults.getSize(), 2); SearchResultMatchI match = searchResults.getResults().get(0); - assertSame(al.getSequenceAt(0), match.getSequence()); - assertEquals(16, match.getStart()); - assertEquals(16, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 16); + assertEquals(match.getEnd(), 16); match = searchResults.getResults().get(1); - assertSame(al.getSequenceAt(1), match.getSequence()); - assertEquals(8, match.getStart()); - assertEquals(8, match.getEnd()); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 8); + assertEquals(match.getEnd(), 8); + } + + /** + * Test that find does not report hidden positions + */ + @Test(groups = "Functional") + public void testFind_withHiddenColumns() + { + /* + * 0 5 9 + * ABCD--EF-GHI + * A--BCDefHI + * --bcdEFH + * aa---aMMMMMaaa + */ + + /* + * hide 2-4 (CD- -BC bcd ---) + */ + HiddenColumns hc = new HiddenColumns(); + hc.hideColumns(2, 4); + al.setHiddenColumns(hc); + + /* + * find all search for D should ignore hidden positions in seq1 and seq3, + * find the visible D in seq2 + */ + Finder f = new Finder(al, null); + f.setFindAll(true); + f.find("D"); + SearchResultsI searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + SearchResultMatchI match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 4); + assertEquals(match.getEnd(), 4); + + /* + * hide columns 2-5: + * find all 'aaa' should find end of seq4 only + */ + hc.hideColumns(2, 5); + f = new Finder(al, null); + f.find("aaa"); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + match = searchResults.getResults().get(0); + 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.find("BE"); + assertTrue(f.getSearchResults().isEmpty()); + + /* + * boundary case: hide columns at end of alignment + * search for H should match seq3/6 only + */ + hc.revealAllHiddenColumns(new ColumnSelection()); + hc.hideColumns(8, 13); + f = new Finder(al, null); + f.find("H"); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getSize(), 1); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(2)); + assertEquals(match.getStart(), 6); + assertEquals(match.getEnd(), 6); } } -- 1.7.10.2