tidy imports
[jalview.git] / src / jalview / ws / rest / params / SeqGroupIndexVector.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.ws.rest.params;
19
20 import jalview.datamodel.AlignmentI;
21 import jalview.datamodel.SequenceGroup;
22 import jalview.datamodel.SequenceI;
23 import jalview.ws.params.OptionI;
24 import jalview.ws.params.simple.IntegerParameter;
25 import jalview.ws.params.simple.Option;
26 import jalview.ws.rest.AlignmentProcessor;
27 import jalview.ws.rest.InputType;
28 import jalview.ws.rest.NoValidInputDataException;
29 import jalview.ws.rest.RestJob;
30
31 import java.io.UnsupportedEncodingException;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35
36 import org.apache.http.entity.mime.content.ContentBody;
37 import org.apache.http.entity.mime.content.StringBody;
38
39 /**
40  * Represents the partitions defined on the alignment as indices e.g. for a
41  * partition (A,B,C),(D,E),(F) The indices would be 3,2,1. Note, the alignment
42  * must be ordered so groups are contiguous before this input type can be used.
43  * 
44  * @author JimP
45  * 
46  */
47 public class SeqGroupIndexVector extends InputType implements
48         AlignmentProcessor
49 {
50   public SeqGroupIndexVector()
51   {
52     super(new Class[]
53     { AlignmentI.class });
54   }
55
56   /**
57    * separator for list of sequence Indices - default is ','
58    */
59   public String sep = ",";
60
61   /**
62    * min size of each partition
63    */
64   public int minsize = 1;
65
66   molType type;
67
68   /**
69    * prepare the context alignment for this input
70    * 
71    * @param al
72    *          - alignment to be processed
73    * @return al or a new alignment with appropriate attributes/order for input
74    */
75   public AlignmentI prepareAlignment(AlignmentI al)
76   {
77     jalview.analysis.AlignmentSorter.sortByGroup(al);
78     return al;
79   }
80
81   @Override
82   public ContentBody formatForInput(RestJob rj)
83           throws UnsupportedEncodingException, NoValidInputDataException
84   {
85     StringBuffer idvector = new StringBuffer();
86     boolean list = false;
87     AlignmentI al = rj.getAlignmentForInput(token, type);
88     // assume that alignment is properly ordered so groups form consecutive
89     // blocks
90     ArrayList<int[]> gl = new ArrayList<int[]>();
91     int p = 0;
92     List<SequenceGroup> sgs;
93     synchronized (sgs = al.getGroups())
94     {
95       for (SequenceGroup sg : sgs)
96       {
97         if (sg.getSize() < minsize)
98         {
99           throw new NoValidInputDataException("Group contains less than "
100                   + minsize + " sequences.");
101         }
102         // TODO: refactor to sequenceGroup for efficiency -
103         // getAlignmentRowInterval(AlignmentI al)
104         int[] se = null;
105         for (SequenceI sq : sg.getSequencesInOrder(al))
106         {
107           p = al.findIndex(sq);
108           if (se == null)
109           {
110             se = new int[]
111             { p, p };
112           }
113           else
114           {
115             if (p < se[0])
116               se[0] = p;
117             if (p > se[1])
118               se[1] = p;
119           }
120         }
121         if (se != null)
122         {
123           gl.add(se);
124         }
125       }
126     }
127     // are there any more sequences ungrouped that should be added as a single
128     // remaining group ? - these might be at the start or the end
129     if (gl.size() > 0)
130     {
131       int[] tail = gl.get(0);
132       if (tail[0] > 0)
133       {
134         if (1 + tail[0] > minsize)
135         {
136           gl.add(0, new int[]
137           { 0, tail[0] - 1 });
138         }
139         else
140         {
141           // lets be intelligent here - if the remaining sequences aren't enough
142           // to make a final group, then don't make one.
143           // throw new
144           // NoValidInputDataException("Group from remaining ungrouped sequences in input contains less than "+minsize+" sequences.");
145         }
146       }
147       else
148       {
149         tail = gl.get(gl.size() - 1);
150         if (1 + tail[1] < al.getHeight())
151         {
152           if (al.getHeight() - (1 + tail[1]) > minsize)
153           {
154             gl.add(new int[]
155             { tail[1] + 1, al.getHeight() - 1 });
156           }
157           else
158           {
159             // lets be intelligent here - if the remaining sequences aren't
160             // enough to make a final group, then don't make one.
161             // throw new
162             // NoValidInputDataException("Group from remaining ungrouped sequences in input contains less than "+minsize+" sequences.");
163           }
164         }
165       }
166     }
167     else
168     {
169       gl.add(new int[]
170       { 0, al.getHeight() - 1 });
171     }
172     if (min >= 0 && gl.size() < min)
173     {
174       throw new NoValidInputDataException(
175               "Not enough sequence groups for input. Need at least " + min
176                       + " groups (including ungrouped regions).");
177     }
178     if (max > 0 && gl.size() > max)
179     {
180       throw new NoValidInputDataException(
181               "Too many sequence groups for input. Need at most " + max
182                       + " groups (including ungrouped regions).");
183     }
184     int[][] vals = gl.toArray(new int[gl.size()][]);
185     int[] srt = new int[gl.size()];
186     for (int i = 0; i < vals.length; i++)
187       srt[i] = vals[i][0];
188     jalview.util.QuickSort.sort(srt, vals);
189     list = false;
190     int last = vals[0][0] - 1;
191     for (int[] range : vals)
192     {
193       if (range[1] > last)
194       {
195         if (list)
196         {
197           idvector.append(sep);
198         }
199         idvector.append(range[1] - last);
200         last = range[1];
201         list = true;
202       }
203     }
204     return new StringBody(idvector.toString());
205   }
206
207   /**
208    * set minimum number of sequences allowed in a partition. Default is 1
209    * sequence.
210    * 
211    * @param i
212    *          (number greater than 1)
213    */
214   public void setMinsize(int i)
215   {
216     if (minsize >= 1)
217     {
218       minsize = i;
219     }
220     else
221     {
222       minsize = 1;
223     }
224   }
225
226   @Override
227   public List<String> getURLEncodedParameter()
228   {
229     ArrayList<String> prms = new ArrayList<String>();
230     super.addBaseParams(prms);
231     prms.add("minsize='" + minsize + "'");
232     prms.add("sep='" + sep + "'");
233     if (type != null)
234     {
235       prms.add("type='" + type + "'");
236     }
237     return prms;
238   }
239
240   @Override
241   public String getURLtokenPrefix()
242   {
243     return "PARTITION";
244   }
245
246   @Override
247   public boolean configureProperty(String tok, String val,
248           StringBuffer warnings)
249   {
250
251     if (tok.startsWith("sep"))
252     {
253       sep = val;
254       return true;
255     }
256     if (tok.startsWith("minsize"))
257     {
258       try
259       {
260         minsize = Integer.valueOf(val);
261         if (minsize >= 0)
262           return true;
263       } catch (Exception x)
264       {
265
266       }
267       warnings.append("Invalid minsize value '" + val
268               + "'. Must be a positive integer.\n");
269     }
270     if (tok.startsWith("type"))
271     {
272       try
273       {
274         type = molType.valueOf(val);
275         return true;
276       } catch (Exception x)
277       {
278         warnings.append("Invalid molecule type '" + val
279                 + "'. Must be one of (");
280         for (molType v : molType.values())
281         {
282           warnings.append(" " + v);
283         }
284         warnings.append(")\n");
285       }
286     }
287     return false;
288   }
289
290   @Override
291   public List<OptionI> getOptions()
292   {
293     List<OptionI> lst = getBaseOptions();
294     lst.add(new Option("sep",
295             "Separator character between elements of vector", true, ",",
296             sep, Arrays.asList(new String[]
297             { " ", ",", ";", "\t", "|" }), null));
298     lst.add(new IntegerParameter("minsize",
299             "Minimum size of partition allowed by service", true, 1,
300             minsize, 1, 0));
301     lst.add(createMolTypeOption("type", "Sequence type", false, type,
302             molType.MIX));
303     return lst;
304   }
305
306 }