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