JAL-1738 JAL-345 fix for JAL-2302 only report Matches that are not contained by the...
[jalview.git] / src / jalview / analysis / Finder.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.analysis;
22
23 import jalview.datamodel.AlignmentI;
24 import jalview.datamodel.SearchResultMatchI;
25 import jalview.datamodel.SearchResults;
26 import jalview.datamodel.SearchResultsI;
27 import jalview.datamodel.Sequence;
28 import jalview.datamodel.SequenceGroup;
29
30 import java.util.Vector;
31
32 public class Finder
33 {
34   /**
35    * Implements the search algorithms for the Find dialog box.
36    */
37   SearchResultsI searchResults;
38
39   AlignmentI alignment;
40
41   jalview.datamodel.SequenceGroup selection = null;
42
43   Vector idMatch = null;
44
45   boolean caseSensitive = false;
46
47   private boolean includeDescription = false;
48
49   boolean findAll = false;
50
51   com.stevesoft.pat.Regex regex = null;
52
53   /**
54    * hold's last-searched position between calles to find(false)
55    */
56   int seqIndex = 0, resIndex = -1;
57
58   public Finder(AlignmentI alignment, SequenceGroup selection)
59   {
60     this.alignment = alignment;
61     this.selection = selection;
62   }
63
64   /**
65    * restart search at given sequence and residue on alignment and (optionally)
66    * contained in selection
67    * 
68    * @param alignment
69    * @param selectionGroup
70    * @param seqIndex
71    * @param resIndex
72    */
73   public Finder(AlignmentI alignment, SequenceGroup selectionGroup,
74           int seqIndex, int resIndex)
75   {
76     this(alignment, selectionGroup);
77     this.seqIndex = seqIndex;
78     this.resIndex = resIndex;
79   }
80
81   public boolean find(String searchString)
82   {
83     boolean hasResults = false;
84     if (!caseSensitive)
85     {
86       searchString = searchString.toUpperCase();
87     }
88     regex = new com.stevesoft.pat.Regex(searchString);
89     regex.setIgnoreCase(!caseSensitive);
90     searchResults = new SearchResults();
91     idMatch = new Vector();
92     Sequence seq;
93     String item = null;
94     boolean found = false;
95     int end = alignment.getHeight();
96
97     // /////////////////////////////////////////////
98
99     if (selection != null)
100     {
101       if ((selection.getSize() < 1)
102               || ((selection.getEndRes() - selection.getStartRes()) < 2))
103       {
104         selection = null;
105       }
106     }
107     SearchResultMatchI lastm = null;
108
109     while (!found && (seqIndex < end))
110     {
111       seq = (Sequence) alignment.getSequenceAt(seqIndex);
112
113       if ((selection != null && selection.getSize() > 0)
114               && !selection.getSequences(null).contains(seq))
115       {
116         seqIndex++;
117         resIndex = -1;
118
119         continue;
120       }
121       if (resIndex < 0)
122       {
123         resIndex = 0;
124         // test for one off matches - sequence position and sequence ID
125         // //// is the searchString a residue number?
126         try
127         {
128           int res = Integer.parseInt(searchString);
129           // possibly a residue number - check if valid for seq
130           if (seq.getEnd() >= res)
131           {
132             searchResults.addResult(seq, res, res);
133             hasResults = true;
134             // resIndex=seq.getLength();
135             // seqIndex++;
136             if (!findAll)
137             {
138               found = true;
139               break;
140             }
141           }
142         } catch (NumberFormatException ex)
143         {
144         }
145
146         if (regex.search(seq.getName()))
147         {
148           idMatch.addElement(seq);
149           hasResults = true;
150           if (!findAll)
151           {
152             // stop and return the match
153             found = true;
154             break;
155           }
156         }
157
158         if (isIncludeDescription() && seq.getDescription() != null
159                 && regex.search(seq.getDescription()))
160         {
161           idMatch.addElement(seq);
162           hasResults = true;
163           if (!findAll)
164           {
165             // stop and return the match
166             found = true;
167             break;
168           }
169         }
170       }
171       item = seq.getSequenceAsString();
172
173       if ((selection != null)
174               && (selection.getEndRes() < alignment.getWidth() - 1))
175       {
176         item = item.substring(0, selection.getEndRes() + 1);
177       }
178
179       // /Shall we ignore gaps???? - JBPNote: Add Flag for forcing this or not
180       StringBuffer noGapsSB = new StringBuffer();
181       int insertCount = 0;
182       Vector spaces = new Vector();
183
184       for (int j = 0; j < item.length(); j++)
185       {
186         if (!jalview.util.Comparison.isGap(item.charAt(j)))
187         {
188           noGapsSB.append(item.charAt(j));
189           spaces.addElement(new Integer(insertCount));
190         }
191         else
192         {
193           insertCount++;
194         }
195       }
196
197       String noGaps = noGapsSB.toString();
198       for (int r = resIndex; r < noGaps.length(); r++)
199       {
200
201         if (regex.searchFrom(noGaps, r))
202         {
203           resIndex = regex.matchedFrom();
204
205           if ((selection != null && selection.getSize() > 0)
206                   && ((resIndex + Integer.parseInt(spaces.elementAt(
207                           resIndex).toString())) < selection.getStartRes()))
208           {
209             continue;
210           }
211           // if invalid string used, then regex has no matched to/from
212           int sres = seq
213                   .findPosition(resIndex
214                           + Integer.parseInt(spaces.elementAt(resIndex)
215                                   .toString()));
216           int eres = seq.findPosition(regex.matchedTo()
217                   - 1
218                   + Integer.parseInt(spaces
219                           .elementAt(regex.matchedTo() - 1).toString()));
220           // only add result if not contained in previous result
221           if (lastm == null
222                   || (lastm.getSequence() != seq || (!(lastm.getStart() <= sres && lastm
223                           .getEnd() >= eres))))
224           {
225             lastm = searchResults.addResult(seq, sres, eres);
226           }
227           hasResults = true;
228           if (!findAll)
229           {
230             // thats enough, break and display the result
231             found = true;
232             resIndex++;
233
234             break;
235           }
236
237           r = resIndex;
238         }
239         else
240         {
241           break;
242         }
243       }
244
245       if (!found)
246       {
247         seqIndex++;
248         resIndex = -1;
249       }
250     }
251
252     /**
253      * We now search the Id string in the main search loop. for (int id = 0; id
254      * < alignment.getHeight(); id++) { if
255      * (regex.search(alignment.getSequenceAt(id).getName())) {
256      * idMatch.addElement(alignment.getSequenceAt(id)); hasResults = true; } }
257      */
258     return hasResults;
259   }
260
261   /**
262    * @return the alignment
263    */
264   public AlignmentI getAlignment()
265   {
266     return alignment;
267   }
268
269   /**
270    * @param alignment
271    *          the alignment to set
272    */
273   public void setAlignment(AlignmentI alignment)
274   {
275     this.alignment = alignment;
276   }
277
278   /**
279    * @return the caseSensitive
280    */
281   public boolean isCaseSensitive()
282   {
283     return caseSensitive;
284   }
285
286   /**
287    * @param caseSensitive
288    *          the caseSensitive to set
289    */
290   public void setCaseSensitive(boolean caseSensitive)
291   {
292     this.caseSensitive = caseSensitive;
293   }
294
295   /**
296    * @return the findAll
297    */
298   public boolean isFindAll()
299   {
300     return findAll;
301   }
302
303   /**
304    * @param findAll
305    *          the findAll to set
306    */
307   public void setFindAll(boolean findAll)
308   {
309     this.findAll = findAll;
310   }
311
312   /**
313    * @return the selection
314    */
315   public jalview.datamodel.SequenceGroup getSelection()
316   {
317     return selection;
318   }
319
320   /**
321    * @param selection
322    *          the selection to set
323    */
324   public void setSelection(jalview.datamodel.SequenceGroup selection)
325   {
326     this.selection = selection;
327   }
328
329   /**
330    * @return the idMatch
331    */
332   public Vector getIdMatch()
333   {
334     return idMatch;
335   }
336
337   /**
338    * @return the regex
339    */
340   public com.stevesoft.pat.Regex getRegex()
341   {
342     return regex;
343   }
344
345   /**
346    * @return the searchResults
347    */
348   public SearchResultsI getSearchResults()
349   {
350     return searchResults;
351   }
352
353   /**
354    * @return the resIndex
355    */
356   public int getResIndex()
357   {
358     return resIndex;
359   }
360
361   /**
362    * @param resIndex
363    *          the resIndex to set
364    */
365   public void setResIndex(int resIndex)
366   {
367     this.resIndex = resIndex;
368   }
369
370   /**
371    * @return the seqIndex
372    */
373   public int getSeqIndex()
374   {
375     return seqIndex;
376   }
377
378   /**
379    * @param seqIndex
380    *          the seqIndex to set
381    */
382   public void setSeqIndex(int seqIndex)
383   {
384     this.seqIndex = seqIndex;
385   }
386
387   public boolean isIncludeDescription()
388   {
389     return includeDescription;
390   }
391
392   public void setIncludeDescription(boolean includeDescription)
393   {
394     this.includeDescription = includeDescription;
395   }
396 }