JAL-2103 patch routine for inserting gaps for hidden regions in a prediction
[jalview.git] / src / jalview / ws / jws1 / JPredWSUtils.java
1 package jalview.ws.jws1;
2
3 import jalview.datamodel.Alignment;
4 import jalview.datamodel.AlignmentAnnotation;
5 import jalview.datamodel.AlignmentI;
6 import jalview.datamodel.AlignmentView;
7 import jalview.datamodel.ColumnSelection;
8 import jalview.datamodel.SequenceI;
9 import jalview.io.FileParse;
10 import jalview.io.FormatAdapter;
11 import jalview.util.MessageManager;
12
13 import java.io.IOException;
14 import java.util.Hashtable;
15 import java.util.List;
16
17 /**
18  * extraction of processing routines to allow mocking
19  * 
20  * @author jprocter
21  *
22  */
23 public class JPredWSUtils
24 {
25   /**
26    * Process data extracted from service result set to generate a JPred result
27    * view.
28    * 
29    * @param currentView
30    * 
31    * @param input
32    *          - original input alignment
33    * @param gapChar
34    *          - character to use for reconstructing alignment used for
35    *          prediction
36    * @param SequenceInfo
37    *          - from SeqHash
38    * @param msaPred
39    *          - true if a prediction based on existing MSA
40    * @param predMap
41    *          - position
42    * @param result_PredFile
43    * @param result_Aligfile
44    * @param full_alignment
45    * @return { Alignment, ColumnSelection }
46    * @throws Exception
47    */
48   public static Object[] processJnetResult(AlignmentI currentView,
49           AlignmentView input, char gapChar, Hashtable SequenceInfo,
50           boolean msaPred, int[] predMap, String result_PredFile,
51           String result_Aligfile, FileParse full_alignment)
52           throws Exception
53   {
54
55     AlignmentI al = null;
56     ColumnSelection alcsel = null;
57
58     // the position of the query sequence in Alignment al
59     int FirstSeq = -1;
60
61     // the position of the original sequence in the array of
62     // Sequences in the input object that this job holds a
63     // prediction for
64     int msaIndex = 0;
65
66     // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt",
67     // "File");
68     jalview.io.JPredFile prediction = new jalview.io.JPredFile(
69             result_PredFile, "Paste");
70     SequenceI[] preds = prediction.getSeqsAsArray();
71     jalview.bin.Cache.log.debug("Got prediction profile.");
72
73     if (msaPred && (result_Aligfile != null))
74     {
75       jalview.bin.Cache.log.debug("Getting associated alignment.");
76       // we ignore the returned alignment if we only predicted on a single
77       // sequence
78       String format = new jalview.io.IdentifyFile().identify(
79               result_Aligfile, "Paste");
80
81       if (jalview.io.FormatAdapter.isValidFormat(format))
82       {
83         SequenceI sqs[];
84         if (predMap != null)
85         {
86           Object[] alandcolsel = input
87                   .getAlignmentAndColumnSelection(gapChar);
88           sqs = (SequenceI[]) alandcolsel[0];
89           al = new Alignment(sqs);
90           alcsel = (ColumnSelection) alandcolsel[1];
91         }
92         else
93         {
94           al = new FormatAdapter().readFile(result_Aligfile, "Paste",
95                   format);
96           sqs = new SequenceI[al.getHeight()];
97
98           for (int i = 0, j = al.getHeight(); i < j; i++)
99           {
100             sqs[i] = al.getSequenceAt(i);
101           }
102           if (!jalview.analysis.SeqsetUtils.deuniquify(SequenceInfo, sqs))
103           {
104             throw (new Exception(
105                     MessageManager
106                             .getString("exception.couldnt_recover_sequence_properties_for_alignment")));
107           }
108         }
109         FirstSeq = 0;
110         if (currentView.getDataset() != null)
111         {
112           al.setDataset(currentView.getDataset());
113
114         }
115         else
116         {
117           al.setDataset(null);
118         }
119         jalview.io.JnetAnnotationMaker.add_annotation(prediction, al,
120                 FirstSeq, false, predMap);
121
122       }
123       else
124       {
125         throw (new Exception(MessageManager.formatMessage(
126                 "exception.unknown_format_for_file", new String[] { format,
127                     result_Aligfile })));
128       }
129     }
130     else
131     {
132       AlignmentI fullAlignment = null;
133       try
134       {
135         // read full alignment if present.
136         if (!msaPred && full_alignment != null)
137         {
138           fullAlignment = new FormatAdapter().readFromFile(full_alignment,
139                   "FASTA");
140         }
141       } catch (IOException q)
142       {
143
144       }
145       {
146         if (fullAlignment != null)
147         {
148           al = fullAlignment;
149           FirstSeq = 0;
150         }
151         else
152         {
153           al = new Alignment(preds);
154           FirstSeq = prediction.getQuerySeqPosition();
155         }
156       }
157
158       if (predMap != null)
159       {
160         // map the prediction onto the query sequence, excluding positions
161         // corresponding to hidden regions in the original input.
162         char gc = gapChar;
163         SequenceI[] sqs = (SequenceI[]) input
164                 .getAlignmentAndColumnSelection(gc)[0];
165         if (msaIndex >= sqs.length)
166         {
167           throw new Error(
168                   MessageManager
169                           .getString("error.implementation_error_invalid_msa_index_for_job"));
170         }
171         if (fullAlignment == null)
172         {
173           // //
174           // Uses RemoveGapsCommand
175           // //
176           new jalview.commands.RemoveGapsCommand(
177                   MessageManager.getString("label.remove_gaps"),
178                   new SequenceI[] { sqs[msaIndex] }, currentView);
179
180           SequenceI profileseq = al.getSequenceAt(FirstSeq);
181           profileseq.setSequence(sqs[msaIndex].getSequenceAsString());
182         }
183       }
184
185       if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash(
186               al.getSequenceAt(FirstSeq), SequenceInfo))
187       {
188         throw (new Exception(
189                 MessageManager
190                         .getString("exception.couldnt_recover_sequence_props_for_jnet_query")));
191       }
192       else
193       {
194         if (currentView.getDataset() != null)
195         {
196           al.setDataset(currentView.getDataset());
197
198         }
199         else
200         {
201           al.setDataset(null);
202         }
203         if (fullAlignment != null)
204         {
205           // map gapMap from positions in visible sequence to positions in
206           // original sequence
207           if (predMap != null)
208           {
209
210           }
211         }
212         jalview.io.JnetAnnotationMaker.add_annotation(prediction, al,
213                 FirstSeq, true, predMap);
214         SequenceI profileseq = al.getSequenceAt(0); // this includes any gaps.
215         if (fullAlignment == null)
216         {
217           alignToProfileSeq(al, profileseq);
218         }
219         if (fullAlignment == null && predMap != null)
220         {
221           // Adjust input view for gaps
222           // propagate insertions into profile
223           alcsel = ColumnSelection.propagateInsertions(profileseq, al,
224                   input);
225         }
226       }
227     }
228
229     // transfer to dataset
230     for (AlignmentAnnotation alant : al.getAlignmentAnnotation())
231     {
232       if (alant.sequenceRef != null)
233       {
234         replaceAnnotationOnAlignmentWith(alant, alant.label,
235                 "jalview.jws1.Jpred" + (msaPred ? "MSA" : ""),
236                 alant.sequenceRef);
237       }
238     }
239
240     return new Object[] { al, alcsel }; // , FirstSeq, noMsa};
241   }
242
243   /**
244    * copied from JabawsCalcWorker
245    * 
246    * @param newAnnot
247    * @param typeName
248    * @param calcId
249    * @param aSeq
250    */
251   public static void replaceAnnotationOnAlignmentWith(
252           AlignmentAnnotation newAnnot, String typeName, String calcId,
253           SequenceI aSeq)
254   {
255     SequenceI dsseq = aSeq.getDatasetSequence();
256     while (dsseq.getDatasetSequence() != null)
257     {
258       dsseq = dsseq.getDatasetSequence();
259     }
260     // look for same annotation on dataset and lift this one over
261     List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
262             typeName);
263     if (dsan != null && dsan.size() > 0)
264     {
265       for (AlignmentAnnotation dssan : dsan)
266       {
267         dsseq.removeAlignmentAnnotation(dssan);
268       }
269     }
270     AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
271     dsseq.addAlignmentAnnotation(dssan);
272     dssan.adjustForAlignment();
273   }
274
275   /**
276    * Given an alignment where all other sequences except profileseq are aligned
277    * to the ungapped profileseq, insert gaps in the other sequences to realign
278    * them with the residues in profileseq
279    * 
280    * @param al
281    * @param profileseq
282    */
283   public static void alignToProfileSeq(AlignmentI al, SequenceI profileseq)
284   {
285     char gc = al.getGapCharacter();
286     int[] gapMap = profileseq.gapMap();
287     insertGapsInto(al, gc, gapMap);
288   }
289
290   /**
291    * Given an original sequence, and an alignment involving just the visible
292    * region insert gaps into the alignment and add in the missing residues from
293    * the original sequence
294    * 
295    * @param al
296    * @param c
297    * @param profileseq
298    */
299   public static void insertHiddenResidues(AlignmentI al, char gc,
300           int[] predMap, SequenceI origseq)
301   {
302     // orig: asdfPPPPPPPasdfPPPPasdf
303     // pred: PPPPPPPPPPP
304     // al: -----P-P-P---P---P----P---P-P--PP---P---
305     // s1: SSSSSSS-SS---S---SSSSSS---S-S--SSSSSSSSS
306     // s2: SSSSSSS-SSSSSSSSSSS----SSS-S-SSS-----SSS
307     //
308     // result:
309     //
310     // al: asdf-----P-P-P---P---P----P---Pasdf-P--PP---P---asdf
311     // s1: ....SSSSSSS-SS---S---SSSSSS---S....-S--SSSSSSSSS....
312     // s2: ....SSSSSSS-SSSSSSSSSSS----SSS-....S-SSS-----SSS....
313
314     // iteration 0: add asdf, append -----P
315     // iteration 1: append -P
316     // iteration 2: append -P
317     // iteration 3: append ---P
318     // iteration 4: append ---P
319     // iteration 5: append ----P
320     // iteration 6: append ---P
321     // iteration 7: append -P
322     // iteration 8: append P
323     // iteration 9: append P
324     // iteration 10: append ---P
325     // tail: append: ---, add asdf
326
327     String alseq = "";
328     SequenceI predseq = al.getSequenceAt(0);
329     int predIdx = 0; // next column of prediction to preserve
330     // positions in original and prediction sequence
331     int lp = origseq.getStart() - 1, predPos = predseq.getStart();
332     for (int r = 0; r < predMap.length; r++)
333     {
334       // also need to keep track of trimmed prediction sequence numbering
335       if (predMap[r] - lp > 1)
336       {
337         // hidden region insert from origseq
338         String insert = origseq.getSequenceAsString(
339                 origseq.findIndex(lp + 1) - 1,
340                 origseq.findIndex(predMap[r]) - 1);
341
342         insertGapsAt(al, gc, alseq.length(), insert.length());
343         alseq += insert;
344       }
345       // Now update prediction sequence for next position.
346       {
347         int predIdxNext = predseq.findIndex(predPos); // everything up
348                                                       // to the current
349                                                       // position in the
350                                                       // prediction
351                                                       // sequence
352                                                       // alignment
353         if (predIdxNext <= predIdx)
354         {
355           predIdxNext = predseq.getLength();
356         }
357         // just add in next segment of predseq
358         String predsert = predseq.getSequenceAsString(predIdx, predIdxNext);
359         alseq += predsert;
360         predIdx = predIdxNext;
361       }
362       lp = predMap[r];
363       predPos++;
364     }
365     // append final bits
366     // add any remaining gaps
367     {
368       int predIdxNext = predseq.findIndex(predPos); // everything up
369                                                     // to the current
370                                                     // position in the
371                                                     // prediction
372                                                     // sequence
373                                                     // alignment
374       if (predIdxNext <= predIdx)
375       {
376         predIdxNext = predseq.getLength();
377       }
378       // just add in next segment of predseq
379       String predsert = predseq.getSequenceAsString(predIdx, predIdxNext);
380       alseq += predsert;
381       predIdx = predIdxNext;
382     }
383
384     if (lp < origseq.getEnd())
385     {
386       String insert = origseq.getSequenceAsString(
387               origseq.findIndex(lp + 1) - 1, origseq.getLength());
388       insertGapsAt(al, gc, alseq.length(), insert.length());
389       alseq += insert;
390     }
391     // then add in origseq data.
392     predseq.setSequence(alseq);
393   }
394
395   public static void insertGapsInto(AlignmentI al, char gc, int[] gapMap)
396   {
397     // insert gaps into profile
398     for (int lp = 0, r = 0; r < gapMap.length; r++)
399     {
400       if (gapMap[r] - lp > 1)
401       {
402         insertGapsAt(al, gc, gapMap[r], gapMap[r] - lp);
403       }
404       lp = gapMap[r];
405     }
406   }
407
408   private static void insertGapsAt(AlignmentI al, char gc, int i, int lp)
409   {
410
411     StringBuffer sb = new StringBuffer();
412     for (int s = 0, ns = lp; s < ns; s++)
413     {
414       sb.append(gc);
415     }
416     for (int s = 1, ns = al.getHeight(); s < ns; s++)
417     {
418       String sq = al.getSequenceAt(s).getSequenceAsString();
419       int diff = i - sq.length();
420       if (diff > 0)
421       {
422         // pad gaps
423         sq = sq + sb;
424         while ((diff = i - sq.length()) > 0)
425         {
426           sq = sq
427                   + ((diff >= sb.length()) ? sb.toString() : sb.substring(
428                           0, diff));
429         }
430         al.getSequenceAt(s).setSequence(sq);
431       }
432       else
433       {
434         al.getSequenceAt(s).setSequence(
435                 sq.substring(0, i) + sb.toString() + sq.substring(i));
436       }
437     }
438   }
439
440 }