JAL-633, JAL-591 - refactored ArgumentI.getValue from jabaws .getDefaultValue
[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.rest.AlignmentProcessor;
7 import jalview.ws.rest.InputType;
8 import jalview.ws.rest.NoValidInputDataException;
9 import jalview.ws.rest.RestJob;
10 import jalview.ws.rest.RestServiceDescription;
11 import jalview.ws.rest.InputType.molType;
12
13 import java.io.UnsupportedEncodingException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Vector;
17
18 import org.apache.http.entity.mime.content.ContentBody;
19 import org.apache.http.entity.mime.content.StringBody;
20
21 /**
22  * Represents the partitions defined on the alignment as indices e.g. for a
23  * partition (A,B,C),(D,E),(F) The indices would be 3,2,1. Note, the alignment
24  * must be ordered so groups are contiguous before this input type can be used.
25  * 
26  * @author JimP
27  * 
28  */
29 public class SeqGroupIndexVector extends InputType implements
30         AlignmentProcessor
31 {
32   public SeqGroupIndexVector()
33   {
34     super(new Class[]
35     { AlignmentI.class });
36   }
37
38   /**
39    * separator for list of sequence Indices - default is ','
40    */
41   public String sep = ",";
42
43   /**
44    * min size of each partition
45    */
46   public int minsize = 1;
47
48   molType type;
49
50   /**
51    * prepare the context alignment for this input
52    * 
53    * @param al
54    *          - alignment to be processed
55    * @return al or a new alignment with appropriate attributes/order for input
56    */
57   public AlignmentI prepareAlignment(AlignmentI al)
58   {
59     jalview.analysis.AlignmentSorter.sortByGroup(al);
60     return al;
61   }
62
63   @Override
64   public ContentBody formatForInput(RestJob rj)
65           throws UnsupportedEncodingException, NoValidInputDataException
66   {
67     StringBuffer idvector = new StringBuffer();
68     boolean list = false;
69     AlignmentI al = rj.getAlignmentForInput(token, type);
70     // assume that alignment is properly ordered so groups form consecutive
71     // blocks
72     ArrayList<int[]> gl = new ArrayList<int[]>();
73     int p=0;
74     for (SequenceGroup sg : (Vector<SequenceGroup>) al.getGroups())
75     {
76       if (sg.getSize()<minsize)
77       {
78         throw new NoValidInputDataException("Group contains less than "+minsize+" sequences.");
79       }
80       // TODO: refactor to sequenceGroup for efficiency -
81       // getAlignmentRowInterval(AlignmentI al)
82       int[] se = null;
83       for (SequenceI sq : sg.getSequencesInOrder(al))
84       {
85         p = al.findIndex(sq);
86         if (se == null)
87         {
88           se = new int[]
89           { p, p };
90         }
91         else
92         {
93           if (p < se[0])
94             se[0] = p;
95           if (p > se[1])
96             se[1] = p;
97         }
98       }
99       if (se != null)
100       {
101         gl.add(se);
102       }
103     }
104     // are there any more sequences ungrouped that should be added as a single remaining group ? - these might be at the start or the end
105     if (gl.size()>0)
106     {
107       int[] tail=gl.get(0);
108       if (tail[0]>0) {
109         if (1+tail[0]>minsize)
110       {
111         gl.add(0,new int[] { 0,tail[0]-1});
112       } else {
113         // lets be intelligent here - if the remaining sequences aren't enough to make a final group, then don't make one.
114         // throw new NoValidInputDataException("Group from remaining ungrouped sequences in input contains less than "+minsize+" sequences.");       
115       }
116       } else {
117         tail=gl.get(gl.size()-1);
118         if (1+tail[1]<al.getHeight())
119         {
120           if (al.getHeight()-(1+tail[1])>minsize) {
121             gl.add(new int[] { tail[1]+1, al.getHeight()-1});            
122           } else {
123             // lets be intelligent here - if the remaining sequences aren't enough to make a final group, then don't make one.
124             //  throw new NoValidInputDataException("Group from remaining ungrouped sequences in input contains less than "+minsize+" sequences.");       
125           }
126         }
127       }
128     } else {
129       gl.add(new int[] { 0, al.getHeight()-1});
130     }
131     if (min>=0 && gl.size()<min)
132     {
133       throw new NoValidInputDataException("Not enough sequence groups for input. Need at least "+min+" groups (including ungrouped regions).");
134     }
135     if (max>0 && gl.size()>max)
136     {
137       throw new NoValidInputDataException("Too many sequence groups for input. Need at most "+max+" groups (including ungrouped regions).");
138     }
139     int[][] vals = gl.toArray(new int[gl.size()][]);
140     int[] srt = new int[gl.size()];
141     for (int i = 0; i < vals.length; i++)
142       srt[i] = vals[i][0];
143     jalview.util.QuickSort.sort(srt, vals);
144     list = false;
145     int last = vals[0][0] - 1;
146     for (int[] range : vals)
147     {
148       if (range[1] > last)
149       {
150         if (list)
151         {
152           idvector.append(sep);
153         }
154         idvector.append(range[1] - last);
155         last = range[1];
156         list = true;
157       }
158     }
159     return new StringBody(idvector.toString());
160   }
161
162   /**
163    * set minimum number of sequences allowed in a partition. Default is 1 sequence.
164    * @param i (number greater than 1)
165    */
166   public void setMinsize(int i)
167   {
168     if (minsize>=1)
169       {
170       minsize=i;
171       } else {
172         minsize=1;
173       }
174   }
175   @Override
176   public List<String> getURLEncodedParameter()
177   {
178     ArrayList<String> prms = new ArrayList<String>();
179     super.addBaseParams(prms);
180     prms.add("minsize='"+ minsize+"'");
181     prms.add("sep='"+ sep+"'");
182     if (type!=null)
183     {
184       prms.add("type='"+type+"'");
185     }
186     return prms;
187   }
188
189   @Override
190   public String getURLtokenPrefix()
191   {
192     return "PARTITION";
193   }
194
195   @Override
196   public boolean configureProperty(String tok, String val,
197           StringBuffer warnings)
198   {
199
200     if (tok.startsWith("sep"))
201     {
202       sep=val;
203       return true;
204     }
205     if (tok.startsWith("minsize"))
206     {
207       try {
208         minsize=Integer.valueOf(val);
209         if (minsize>=0)
210         return true;
211       } catch (Exception x)
212       {
213         
214       }
215       warnings.append("Invalid minsize value '"+val+"'. Must be a positive integer.\n");
216     }
217     if (tok.startsWith("type"))
218     {
219       try {
220         type=molType.valueOf(val);
221         return true;
222       } catch (Exception x)
223       {
224         warnings.append("Invalid molecule type '"+val+"'. Must be one of (");
225         for (molType v:molType.values())
226         {
227           warnings.append(" "+v);
228         }
229         warnings.append(")\n");
230       }
231     }
232     return false;
233   }
234
235 }