0532035809c53a2a0383c4b84f614d24755ff9e6
[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,lowest=al.getHeight(), highest=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 (lowest>p)
109           {
110             lowest=p;
111           }
112           if (highest<p)
113           {
114             highest=p;
115           }
116           if (se == null)
117           {
118             se = new int[]
119             { p, p };
120           }
121           else
122           {
123             if (p < se[0])
124               se[0] = p;
125             if (p > se[1])
126               se[1] = p;
127           }
128         }
129         if (se != null)
130         {
131           gl.add(se);
132         }
133       }
134     }
135     // are there any more sequences ungrouped that should be added as a single
136     // remaining group ? - these might be at the start or the end
137     if (gl.size() > 0)
138     {
139       if (lowest-1>minsize)
140       {
141         gl.add(0, new int[]
142           { 0, lowest-2});
143       }
144       if ((al.getHeight()-1-highest)>minsize)
145       {
146         gl.add(new int[] { highest+1, al.getHeight()-1});
147       }
148     }
149     else
150     {
151       gl.add(new int[]
152       { 0, al.getHeight() - 1 });
153     }
154     if (min >= 0 && gl.size() < min)
155     {
156       throw new NoValidInputDataException(
157               "Not enough sequence groups for input. Need at least " + min
158                       + " groups (including ungrouped regions).");
159     }
160     if (max > 0 && gl.size() > max)
161     {
162       throw new NoValidInputDataException(
163               "Too many sequence groups for input. Need at most " + max
164                       + " groups (including ungrouped regions).");
165     }
166     int[][] vals = gl.toArray(new int[gl.size()][]);
167     int[] srt = new int[gl.size()];
168     for (int i = 0; i < vals.length; i++)
169       srt[i] = vals[i][0];
170     jalview.util.QuickSort.sort(srt, vals);
171     list = false;
172     int last = vals[0][0] - 1;
173     for (int[] range : vals)
174     {
175       if (range[1] > last)
176       {
177         if (list)
178         {
179           idvector.append(sep);
180         }
181         idvector.append(range[1] - last);
182         last = range[1];
183         list = true;
184       }
185     }
186     return new StringBody(idvector.toString());
187   }
188
189   /**
190    * set minimum number of sequences allowed in a partition. Default is 1
191    * sequence.
192    * 
193    * @param i
194    *          (number greater than 1)
195    */
196   public void setMinsize(int i)
197   {
198     if (minsize >= 1)
199     {
200       minsize = i;
201     }
202     else
203     {
204       minsize = 1;
205     }
206   }
207
208   @Override
209   public List<String> getURLEncodedParameter()
210   {
211     ArrayList<String> prms = new ArrayList<String>();
212     super.addBaseParams(prms);
213     prms.add("minsize='" + minsize + "'");
214     prms.add("sep='" + sep + "'");
215     if (type != null)
216     {
217       prms.add("type='" + type + "'");
218     }
219     return prms;
220   }
221
222   @Override
223   public String getURLtokenPrefix()
224   {
225     return "PARTITION";
226   }
227
228   @Override
229   public boolean configureProperty(String tok, String val,
230           StringBuffer warnings)
231   {
232
233     if (tok.startsWith("sep"))
234     {
235       sep = val;
236       return true;
237     }
238     if (tok.startsWith("minsize"))
239     {
240       try
241       {
242         minsize = Integer.valueOf(val);
243         if (minsize >= 0)
244           return true;
245       } catch (Exception x)
246       {
247
248       }
249       warnings.append("Invalid minsize value '" + val
250               + "'. Must be a positive integer.\n");
251     }
252     if (tok.startsWith("type"))
253     {
254       try
255       {
256         type = molType.valueOf(val);
257         return true;
258       } catch (Exception x)
259       {
260         warnings.append("Invalid molecule type '" + val
261                 + "'. Must be one of (");
262         for (molType v : molType.values())
263         {
264           warnings.append(" " + v);
265         }
266         warnings.append(")\n");
267       }
268     }
269     return false;
270   }
271
272   @Override
273   public List<OptionI> getOptions()
274   {
275     List<OptionI> lst = getBaseOptions();
276     lst.add(new Option("sep",
277             "Separator character between elements of vector", true, ",",
278             sep, Arrays.asList(new String[]
279             { " ", ",", ";", "\t", "|" }), null));
280     lst.add(new IntegerParameter("minsize",
281             "Minimum size of partition allowed by service", true, 1,
282             minsize, 1, 0));
283     lst.add(createMolTypeOption("type", "Sequence type", false, type,
284             molType.MIX));
285     return lst;
286   }
287
288 }