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