* If there are hidden columns, and option {@ignoreHidden} is selected, then * only visible positions of the sequence are included, and a mapping is also * constructed from the returned string positions to the true sequence * positions. *
* Note we have to do this each time {@code findNext} or {@code findAll} is * called, in case the alignment, selection group or hidden columns have * changed. In particular, if the sequence at offset {@code sequenceIndex} in * the alignment is (no longer) in the selection group, search is advanced to * the next sequence that is. *
* Sets sequence string to the empty string if there are no more sequences (in * selection group if any) at or after {@code sequenceIndex}. *
* Returns true if a sequence could be found, false if end of alignment was * reached * * @param ignoreHidden * @return */ private boolean getSequence(boolean ignoreHidden) { AlignmentI alignment = viewport.getAlignment(); if (sequenceIndex >= alignment.getHeight()) { seqToSearch = ""; return false; } SequenceI seq = alignment.getSequenceAt(sequenceIndex); SequenceGroup selection = viewport.getSelectionGroup(); if (selection != null && !selection.contains(seq)) { if (!nextSequence(ignoreHidden)) { return false; } seq = alignment.getSequenceAt(sequenceIndex); } String seqString = null; if (ignoreHidden) { seqString = getVisibleSequence(seq); this.searchedSequenceStartPosition = 1; } else { int startCol = 0; int endCol = seq.getLength() - 1; this.searchedSequenceStartPosition = seq.getStart(); if (selection != null) { startCol = selection.getStartRes(); endCol = Math.min(endCol, selection.getEndRes()); this.searchedSequenceStartPosition = seq.findPosition(startCol); } seqString = seq.getSequenceAsString(startCol, endCol + 1); } /* * remove gaps; note that even if this leaves an empty string, we 'search' * the sequence anyway (for possible match on name or description) */ String ungapped = AlignSeq.extractGaps(Comparison.GapChars, seqString); this.seqToSearch = ungapped; return true; } /** * 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)
{
/*
* get start / end columns of sequence and convert to base 0
* (so as to match the visible column ranges)
*/
int seqStartCol = seq.findIndex(seq.getStart()) - 1;
int seqEndCol = seq.findIndex(seq.getStart() + seq.getLength() - 1) - 1;
Iterator
* Returns true if a sequence could be found, false if end of alignment was
* reached
*
* @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(String searchString, Regex searchPattern,
boolean matchDescription, boolean ignoreHidden)
{
if (residueIndex < 0)
{
/*
* at start of sequence; try find by residue number, in sequence id,
* or (optionally) in sequence description
*/
if (doNonMotifSearches(searchString, searchPattern, matchDescription))
{
return true;
}
}
/*
* search for next match in sequence string
*/
int end = seqToSearch.length();
while (residueIndex < end)
{
boolean matched = searchPattern.searchFrom(seqToSearch, residueIndex);
if (matched)
{
if (recordMatch(searchPattern, ignoreHidden))
{
return true;
}
}
else
{
residueIndex = Integer.MAX_VALUE;
}
}
nextSequence(ignoreHidden);
return false;
}
/**
* Adds the match held in the
* Matches that lie entirely within hidden regions of the alignment are not
* added.
*
* @param searchPattern
* @param ignoreHidden
* @return
*/
protected boolean recordMatch(Regex searchPattern, boolean ignoreHidden)
{
SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex);
/*
* convert start/end of the match to sequence coordinates
*/
int offset = searchPattern.matchedFrom();
int matchStartPosition = this.searchedSequenceStartPosition + offset;
int matchEndPosition = matchStartPosition + searchPattern.charsMatched()
- 1;
/*
* update residueIndex to next position after the start of the match
* (findIndex returns a value base 1, columnIndex is held base 0)
*/
residueIndex = searchPattern.matchedFrom() + 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)
*/
ListsearchPattern
Regex to the
* searchResults
, unless it is a subregion of the last match
* 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.
*
*
* Answers true if a match is found, else false.
*
* @param searchString
* @param searchPattern
* @param includeDescription
* @return
*/
protected boolean doNonMotifSearches(String searchString,
Regex searchPattern, boolean includeDescription)
{
SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex);
/*
* position sequence search to start of sequence
*/
residueIndex = 0;
try
{
int res = Integer.parseInt(searchString);
return searchForResidueNumber(seq, res);
} catch (NumberFormatException ex)
{
// search pattern is not a number
}
if (searchSequenceName(seq, searchPattern))
{
return true;
}
if (includeDescription && searchSequenceDescription(seq, searchPattern))
{
return true;
}
return 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 searchPattern
* @return
*/
protected boolean searchSequenceDescription(SequenceI seq,
Regex searchPattern)
{
String desc = seq.getDescription();
if (desc != null && searchPattern.search(desc)
&& !idMatches.contains(seq))
{
idMatches.add(seq);
return true;
}
return false;
}
/**
* Searches for a match with the sequence name, 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 searchPattern
* @return
*/
protected boolean searchSequenceName(SequenceI seq, Regex searchPattern)
{
if (searchPattern.search(seq.getName()) && !idMatches.contains(seq))
{
idMatches.add(seq);
return true;
}
return 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, int resNo)
{
if (seq.getStart() <= resNo && seq.getEnd() >= resNo)
{
if (isVisible(seq, resNo))
{
searchResults.addResult(seq, resNo, resNo);
return true;
}
}
return false;
}
/**
* 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 List