ba2c1b3185539a6f8bbda19f1f36a0693d1702e5
[jalview.git] / src / jalview / ws / rest / params / SeqGroupIndexVector.java
1 package jalview.ws.rest.params;
2
3 import jalview.datamodel.AlignmentI;
4 import jalview.datamodel.SequenceGroup;
5 import jalview.datamodel.SequenceI;
6 import jalview.ws.params.OptionI;
7 import jalview.ws.params.simple.IntegerParameter;
8 import jalview.ws.params.simple.Option;
9 import jalview.ws.rest.AlignmentProcessor;
10 import jalview.ws.rest.InputType;
11 import jalview.ws.rest.NoValidInputDataException;
12 import jalview.ws.rest.RestJob;
13 import jalview.ws.rest.RestServiceDescription;
14 import jalview.ws.rest.InputType.molType;
15
16 import java.io.UnsupportedEncodingException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.Vector;
21
22 import org.apache.http.entity.mime.content.ContentBody;
23 import org.apache.http.entity.mime.content.StringBody;
24
25 /**
26  * Represents the partitions defined on the alignment as indices e.g. for a
27  * partition (A,B,C),(D,E),(F) The indices would be 3,2,1. Note, the alignment
28  * must be ordered so groups are contiguous before this input type can be used.
29  * 
30  * @author JimP
31  * 
32  */
33 public class SeqGroupIndexVector extends InputType implements
34         AlignmentProcessor
35 {
36   public SeqGroupIndexVector()
37   {
38     super(new Class[]
39     { AlignmentI.class });
40   }
41
42   /**
43    * separator for list of sequence Indices - default is ','
44    */
45   public String sep = ",";
46
47   /**
48    * min size of each partition
49    */
50   public int minsize = 1;
51
52   molType type;
53
54   /**
55    * prepare the context alignment for this input
56    * 
57    * @param al
58    *          - alignment to be processed
59    * @return al or a new alignment with appropriate attributes/order for input
60    */
61   public AlignmentI prepareAlignment(AlignmentI al)
62   {
63     jalview.analysis.AlignmentSorter.sortByGroup(al);
64     return al;
65   }
66
67   @Override
68   public ContentBody formatForInput(RestJob rj)
69           throws UnsupportedEncodingException, NoValidInputDataException
70   {
71     StringBuffer idvector = new StringBuffer();
72     boolean list = false;
73     AlignmentI al = rj.getAlignmentForInput(token, type);
74     // assume that alignment is properly ordered so groups form consecutive
75     // blocks
76     ArrayList<int[]> gl = new ArrayList<int[]>();
77     int p = 0;
78     for (SequenceGroup sg : (Vector<SequenceGroup>) al.getGroups())
79     {
80       if (sg.getSize() < minsize)
81       {
82         throw new NoValidInputDataException("Group contains less than "
83                 + minsize + " sequences.");
84       }
85       // TODO: refactor to sequenceGroup for efficiency -
86       // getAlignmentRowInterval(AlignmentI al)
87       int[] se = null;
88       for (SequenceI sq : sg.getSequencesInOrder(al))
89       {
90         p = al.findIndex(sq);
91         if (se == null)
92         {
93           se = new int[]
94           { p, p };
95         }
96         else
97         {
98           if (p < se[0])
99             se[0] = p;
100           if (p > se[1])
101             se[1] = p;
102         }
103       }
104       if (se != null)
105       {
106         gl.add(se);
107       }
108     }
109     // are there any more sequences ungrouped that should be added as a single
110     // remaining group ? - these might be at the start or the end
111     if (gl.size() > 0)
112     {
113       int[] tail = gl.get(0);
114       if (tail[0] > 0)
115       {
116         if (1 + tail[0] > minsize)
117         {
118           gl.add(0, new int[]
119           { 0, tail[0] - 1 });
120         }
121         else
122         {
123           // lets be intelligent here - if the remaining sequences aren't enough
124           // to make a final group, then don't make one.
125           // throw new
126           // NoValidInputDataException("Group from remaining ungrouped sequences in input contains less than "+minsize+" sequences.");
127         }
128       }
129       else
130       {
131         tail = gl.get(gl.size() - 1);
132         if (1 + tail[1] < al.getHeight())
133         {
134           if (al.getHeight() - (1 + tail[1]) > minsize)
135           {
136             gl.add(new int[]
137             { tail[1] + 1, al.getHeight() - 1 });
138           }
139           else
140           {
141             // lets be intelligent here - if the remaining sequences aren't
142             // enough 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       }
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", "Minimum size of partition allowed by service", true, 1, minsize, 1,0));
281     lst.add(createMolTypeOption("type", "Sequence type", false, type, molType.MIX));
282     return lst;
283   }
284
285 }