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