Merge commit
[jalview.git] / src / jalview / analysis / Finder.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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() && regex.search(seq.getDescription()))
156         {
157           idMatch.addElement(seq);
158           hasResults = true;
159           if (!findAll)
160           {
161             // stop and return the match
162             found = true;
163             break;
164           }
165         }
166       }
167       item = seq.getSequenceAsString();
168
169       if ((selection != null)
170               && (selection.getEndRes() < alignment.getWidth() - 1))
171       {
172         item = item.substring(0, selection.getEndRes() + 1);
173       }
174
175       // /Shall we ignore gaps???? - JBPNote: Add Flag for forcing this or not
176       StringBuffer noGapsSB = new StringBuffer();
177       int insertCount = 0;
178       Vector spaces = new Vector();
179
180       for (int j = 0; j < item.length(); j++)
181       {
182         if (!jalview.util.Comparison.isGap(item.charAt(j)))
183         {
184           noGapsSB.append(item.charAt(j));
185           spaces.addElement(new Integer(insertCount));
186         }
187         else
188         {
189           insertCount++;
190         }
191       }
192
193       String noGaps = noGapsSB.toString();
194
195       for (int r = resIndex; r < noGaps.length(); r++)
196       {
197
198         if (regex.searchFrom(noGaps, r))
199         {
200           resIndex = regex.matchedFrom();
201
202           if ((selection != null && selection.getSize() > 0)
203                   && ((resIndex + Integer.parseInt(spaces.elementAt(
204                           resIndex).toString())) < selection.getStartRes()))
205           {
206             continue;
207           }
208 // if invalid string used, then regex has no matched to/from
209           int sres = seq
210                   .findPosition(resIndex
211                           + Integer.parseInt(spaces.elementAt(resIndex)
212                                   .toString()));
213           int eres = seq.findPosition(regex.matchedTo()
214                   - 1
215                   + Integer.parseInt(spaces
216                           .elementAt(regex.matchedTo() - 1).toString()));
217
218           searchResults.addResult(seq, sres, eres);
219           hasResults = true;
220           if (!findAll)
221           {
222             // thats enough, break and display the result
223             found = true;
224             resIndex++;
225
226             break;
227           }
228
229           r = resIndex;
230         }
231         else
232         {
233           break;
234         }
235       }
236
237       if (!found)
238       {
239         seqIndex++;
240         resIndex = -1;
241       }
242     }
243
244     /**
245      * We now search the Id string in the main search loop. for (int id = 0; id
246      * < alignment.getHeight(); id++) { if
247      * (regex.search(alignment.getSequenceAt(id).getName())) {
248      * idMatch.addElement(alignment.getSequenceAt(id)); hasResults = true; } }
249      */
250     return hasResults;
251   }
252
253   /**
254    * @return the alignment
255    */
256   public AlignmentI getAlignment()
257   {
258     return alignment;
259   }
260
261   /**
262    * @param alignment
263    *          the alignment to set
264    */
265   public void setAlignment(AlignmentI alignment)
266   {
267     this.alignment = alignment;
268   }
269
270   /**
271    * @return the caseSensitive
272    */
273   public boolean isCaseSensitive()
274   {
275     return caseSensitive;
276   }
277
278   /**
279    * @param caseSensitive
280    *          the caseSensitive to set
281    */
282   public void setCaseSensitive(boolean caseSensitive)
283   {
284     this.caseSensitive = caseSensitive;
285   }
286
287   /**
288    * @return the findAll
289    */
290   public boolean isFindAll()
291   {
292     return findAll;
293   }
294
295   /**
296    * @param findAll
297    *          the findAll to set
298    */
299   public void setFindAll(boolean findAll)
300   {
301     this.findAll = findAll;
302   }
303
304   /**
305    * @return the selection
306    */
307   public jalview.datamodel.SequenceGroup getSelection()
308   {
309     return selection;
310   }
311
312   /**
313    * @param selection
314    *          the selection to set
315    */
316   public void setSelection(jalview.datamodel.SequenceGroup selection)
317   {
318     this.selection = selection;
319   }
320
321   /**
322    * @return the idMatch
323    */
324   public Vector getIdMatch()
325   {
326     return idMatch;
327   }
328
329   /**
330    * @return the regex
331    */
332   public com.stevesoft.pat.Regex getRegex()
333   {
334     return regex;
335   }
336
337   /**
338    * @return the searchResults
339    */
340   public SearchResults getSearchResults()
341   {
342     return searchResults;
343   }
344
345   /**
346    * @return the resIndex
347    */
348   public int getResIndex()
349   {
350     return resIndex;
351   }
352
353   /**
354    * @param resIndex
355    *          the resIndex to set
356    */
357   public void setResIndex(int resIndex)
358   {
359     this.resIndex = resIndex;
360   }
361
362   /**
363    * @return the seqIndex
364    */
365   public int getSeqIndex()
366   {
367     return seqIndex;
368   }
369
370   /**
371    * @param seqIndex
372    *          the seqIndex to set
373    */
374   public void setSeqIndex(int seqIndex)
375   {
376     this.seqIndex = seqIndex;
377   }
378
379   public boolean isIncludeDescription()
380   {
381     return includeDescription;
382   }
383
384   public void setIncludeDescription(boolean includeDescription)
385   {
386     this.includeDescription = includeDescription;
387   }
388 }