JAL-1807 explicit imports (jalview.analysis)
[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 import jalview.util.Comparison;
28
29 import java.util.Vector;
30
31 import com.stevesoft.pat.Regex;
32
33 public class Finder
34 {
35   /**
36    * Implements the search algorithms for the Find dialog box.
37    */
38   SearchResults searchResults;
39
40   AlignmentI alignment;
41
42   SequenceGroup selection = null;
43
44   Vector idMatch = null;
45
46   boolean caseSensitive = false;
47
48   private boolean includeDescription = false;
49
50   boolean findAll = false;
51
52   Regex regex = null;
53
54   /**
55    * hold's last-searched position between calles to find(false)
56    */
57   int seqIndex = 0, resIndex = -1;
58
59   public Finder(AlignmentI alignment, SequenceGroup selection)
60   {
61     this.alignment = alignment;
62     this.selection = selection;
63   }
64
65   /**
66    * restart search at given sequence and residue on alignment and (optionally)
67    * contained in selection
68    * 
69    * @param alignment
70    * @param selectionGroup
71    * @param seqIndex
72    * @param resIndex
73    */
74   public Finder(AlignmentI alignment, SequenceGroup selectionGroup,
75           int seqIndex, int resIndex)
76   {
77     this(alignment, selectionGroup);
78     this.seqIndex = seqIndex;
79     this.resIndex = resIndex;
80   }
81
82   public boolean find(String searchString)
83   {
84     boolean hasResults = false;
85     if (!caseSensitive)
86     {
87       searchString = searchString.toUpperCase();
88     }
89     regex = new Regex(searchString);
90     regex.setIgnoreCase(!caseSensitive);
91     searchResults = new SearchResults();
92     idMatch = new Vector();
93     Sequence seq;
94     String item = null;
95     boolean found = false;
96     int end = alignment.getHeight();
97
98     // /////////////////////////////////////////////
99
100     if (selection != null)
101     {
102       if ((selection.getSize() < 1)
103               || ((selection.getEndRes() - selection.getStartRes()) < 2))
104       {
105         selection = null;
106       }
107     }
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 (!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
199       for (int r = resIndex; r < noGaps.length(); r++)
200       {
201
202         if (regex.searchFrom(noGaps, r))
203         {
204           resIndex = regex.matchedFrom();
205
206           if ((selection != null && selection.getSize() > 0)
207                   && ((resIndex + Integer.parseInt(spaces.elementAt(
208                           resIndex).toString())) < selection.getStartRes()))
209           {
210             continue;
211           }
212 // if invalid string used, then regex has no matched to/from
213           int sres = seq
214                   .findPosition(resIndex
215                           + Integer.parseInt(spaces.elementAt(resIndex)
216                                   .toString()));
217           int eres = seq.findPosition(regex.matchedTo()
218                   - 1
219                   + Integer.parseInt(spaces
220                           .elementAt(regex.matchedTo() - 1).toString()));
221
222           searchResults.addResult(seq, sres, eres);
223           hasResults = true;
224           if (!findAll)
225           {
226             // thats enough, break and display the result
227             found = true;
228             resIndex++;
229
230             break;
231           }
232
233           r = resIndex;
234         }
235         else
236         {
237           break;
238         }
239       }
240
241       if (!found)
242       {
243         seqIndex++;
244         resIndex = -1;
245       }
246     }
247
248     /**
249      * We now search the Id string in the main search loop. for (int id = 0; id
250      * < alignment.getHeight(); id++) { if
251      * (regex.search(alignment.getSequenceAt(id).getName())) {
252      * idMatch.addElement(alignment.getSequenceAt(id)); hasResults = true; } }
253      */
254     return hasResults;
255   }
256
257   /**
258    * @return the alignment
259    */
260   public AlignmentI getAlignment()
261   {
262     return alignment;
263   }
264
265   /**
266    * @param alignment
267    *          the alignment to set
268    */
269   public void setAlignment(AlignmentI alignment)
270   {
271     this.alignment = alignment;
272   }
273
274   /**
275    * @return the caseSensitive
276    */
277   public boolean isCaseSensitive()
278   {
279     return caseSensitive;
280   }
281
282   /**
283    * @param caseSensitive
284    *          the caseSensitive to set
285    */
286   public void setCaseSensitive(boolean caseSensitive)
287   {
288     this.caseSensitive = caseSensitive;
289   }
290
291   /**
292    * @return the findAll
293    */
294   public boolean isFindAll()
295   {
296     return findAll;
297   }
298
299   /**
300    * @param findAll
301    *          the findAll to set
302    */
303   public void setFindAll(boolean findAll)
304   {
305     this.findAll = findAll;
306   }
307
308   /**
309    * @return the selection
310    */
311   public SequenceGroup getSelection()
312   {
313     return selection;
314   }
315
316   /**
317    * @param selection
318    *          the selection to set
319    */
320   public void setSelection(SequenceGroup selection)
321   {
322     this.selection = selection;
323   }
324
325   /**
326    * @return the idMatch
327    */
328   public Vector getIdMatch()
329   {
330     return idMatch;
331   }
332
333   /**
334    * @return the regex
335    */
336   public Regex getRegex()
337   {
338     return regex;
339   }
340
341   /**
342    * @return the searchResults
343    */
344   public SearchResults getSearchResults()
345   {
346     return searchResults;
347   }
348
349   /**
350    * @return the resIndex
351    */
352   public int getResIndex()
353   {
354     return resIndex;
355   }
356
357   /**
358    * @param resIndex
359    *          the resIndex to set
360    */
361   public void setResIndex(int resIndex)
362   {
363     this.resIndex = resIndex;
364   }
365
366   /**
367    * @return the seqIndex
368    */
369   public int getSeqIndex()
370   {
371     return seqIndex;
372   }
373
374   /**
375    * @param seqIndex
376    *          the seqIndex to set
377    */
378   public void setSeqIndex(int seqIndex)
379   {
380     this.seqIndex = seqIndex;
381   }
382
383   public boolean isIncludeDescription()
384   {
385     return includeDescription;
386   }
387
388   public void setIncludeDescription(boolean includeDescription)
389   {
390     this.includeDescription = includeDescription;
391   }
392 }