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