JAL-1452 nucleotide/protein default colour; JAL-845 SplitFrame refactor
[jalview.git] / src / jalview / datamodel / SearchResults.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.datamodel;
22
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.List;
26
27 /**
28  * Holds a list of search result matches, where each match is a contiguous
29  * stretch of a single sequence.
30  * 
31  * @author gmcarstairs
32  *
33  */
34 public class SearchResults
35 {
36
37   private List<Match> matches = new ArrayList<Match>();
38
39   public class Match
40   {
41     SequenceI sequence;
42
43     /*
44      * Start position of match in sequence (base 1)
45      */
46     int start;
47
48     /*
49      * End position (inclusive) (base 1)
50      */
51     int end;
52
53     public Match(SequenceI seq, int start, int end)
54     {
55       sequence = seq;
56       this.start = start;
57       this.end = end;
58     }
59
60     public SequenceI getSequence()
61     {
62       return sequence;
63     }
64
65     public int getStart()
66     {
67       return start;
68     }
69
70     public int getEnd()
71     {
72       return end;
73     }
74
75     /**
76      * Returns the string of characters in the matched region.
77      */
78     @Override
79     public String toString()
80     {
81       char[] chars = sequence.getSequence();
82       return String.valueOf(Arrays.copyOfRange(chars, start - 1, end));
83     }
84   }
85
86   /**
87    * This method replaces the old search results which merely held an alignment
88    * index of search matches. This broke when sequences were moved around the
89    * alignment
90    * 
91    * @param seq
92    *          Sequence
93    * @param start
94    *          int
95    * @param end
96    *          int
97    */
98   public void addResult(SequenceI seq, int start, int end)
99   {
100     matches.add(new Match(seq, start, end));
101   }
102
103   /**
104    * Quickly check if the given sequence is referred to in the search results
105    * 
106    * @param sequence
107    *          (specific alignment sequence or a dataset sequence)
108    * @return true if the results involve sequence
109    */
110   public boolean involvesSequence(SequenceI sequence)
111   {
112     SequenceI ds = sequence.getDatasetSequence();
113     for (Match m : matches)
114     {
115       if (m.sequence != null
116               && (m.sequence == sequence || m.sequence == ds))
117       {
118         return true;
119       }
120     }
121     return false;
122   }
123
124   /**
125    * This Method returns the search matches which lie between the start and end
126    * points of the sequence in question. It is optimised for returning objects
127    * for drawing on SequenceCanvas
128    */
129   public int[] getResults(SequenceI sequence, int start, int end)
130   {
131     if (matches.isEmpty())
132     {
133       return null;
134     }
135
136     int[] result = null;
137     int[] tmp = null;
138     int resultLength, matchStart = 0, matchEnd = 0;
139     boolean mfound;
140     for (Match m : matches)
141     {
142       mfound = false;
143       if (m.sequence == sequence)
144       {
145         mfound = true;
146         // locate aligned position
147         matchStart = sequence.findIndex(m.start) - 1;
148         matchEnd = sequence.findIndex(m.end) - 1;
149       }
150       else if (m.sequence == sequence.getDatasetSequence())
151       {
152         mfound = true;
153         // locate region in local context
154         matchStart = sequence.findIndex(m.start) - 1;
155         matchEnd = sequence.findIndex(m.end) - 1;
156       }
157       if (mfound)
158       {
159         if (matchStart <= end && matchEnd >= start)
160         {
161           if (matchStart < start)
162           {
163             matchStart = start;
164           }
165
166           if (matchEnd > end)
167           {
168             matchEnd = end;
169           }
170
171           if (result == null)
172           {
173             result = new int[]
174             { matchStart, matchEnd };
175           }
176           else
177           {
178             resultLength = result.length;
179             tmp = new int[resultLength + 2];
180             System.arraycopy(result, 0, tmp, 0, resultLength);
181             result = tmp;
182             result[resultLength] = matchStart;
183             result[resultLength + 1] = matchEnd;
184           }
185         }
186         else
187         {
188           // debug
189           // System.err.println("Outwith bounds!" + matchStart+">"+end +"  or "
190           // + matchEnd+"<"+start);
191         }
192       }
193     }
194     return result;
195   }
196
197   public int getSize()
198   {
199     return matches.size();
200   }
201
202   public SequenceI getResultSequence(int index)
203   {
204     return matches.get(index).sequence;
205   }
206
207   /**
208    * Returns the start position of the i'th match in the search results.
209    * 
210    * @param i
211    * @return
212    */
213   public int getResultStart(int i)
214   {
215     return matches.get(i).start;
216   }
217
218   /**
219    * Returns the end position of the i'th match in the search results.
220    * 
221    * @param i
222    * @return
223    */
224   public int getResultEnd(int i)
225   {
226     return matches.get(i).end;
227   }
228
229   /**
230    * Returns true if no search result matches are held.
231    * 
232    * @return
233    */
234   public boolean isEmpty()
235   {
236     return matches.isEmpty();
237   }
238
239   /**
240    * Returns the list of matches.
241    * 
242    * @return
243    */
244   public List<Match> getResults()
245   {
246     return matches;
247   }
248
249   /**
250    * Return the results as a string of characters. Meant for use when the
251    * context ensures that all matches are to regions of the same sequence
252    * (otherwise the result is meaningless).
253    * 
254    * @return
255    */
256   @Override
257   public String toString()
258   {
259     StringBuilder result = new StringBuilder(256);
260     for (Match m : matches)
261     {
262       result.append(m.toString());
263     }
264     return result.toString();
265   }
266 }