JAL-3691 toUpperCase(Locale.ROOT) for all standard file format operations
[jalview.git] / src / jalview / io / JPredFile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 /**
22  * PredFile.java
23  * JalviewX / Vamsas Project
24  * JPred.seq.concise reader
25  */
26 package jalview.io;
27
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentAnnotation;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceI;
32 import jalview.util.MessageManager;
33
34 import java.io.IOException;
35 import java.util.Hashtable;
36 import java.util.Locale;
37 import java.util.StringTokenizer;
38 import java.util.Vector;
39
40 /**
41  * Parser for the JPred/JNet concise format. This is a series of CSV lines, each
42  * line is either a sequence (QUERY), a sequence profile (align;), or jnet
43  * prediction annotation (anything else). Automagic translation happens for
44  * annotation called 'JNETPRED' (translated to Secondary Structure Prediction),
45  * or 'JNETCONF' (translates to 'Prediction Confidence'). Numeric scores are
46  * differentiated from symbolic by being parseable into a float vector. They are
47  * put in Scores. Symscores gets the others. JNetAnnotationMaker translates the
48  * data parsed by this object into annotation on an alignment. It is
49  * automatically called but can be used to transfer the annotation onto a
50  * sequence in another alignment (and insert gaps where necessary)
51  * 
52  * @author jprocter
53  * @version $Revision$
54  */
55 public class JPredFile extends AlignFile
56 {
57   Vector ids;
58
59   Vector conf;
60
61   Hashtable Scores; // Hash of names and score vectors
62
63   Hashtable Symscores; // indexes of symbol annotation properties in sequenceI
64
65   // vector
66
67   private int QuerySeqPosition;
68
69   /**
70    * Creates a new JPredFile object.
71    * 
72    * BH allows File or String
73    * 
74    * @param inFile
75    *          DOCUMENT ME!
76    * @param sourceType
77    *          DOCUMENT ME!
78    * 
79    * @throws IOException
80    *           DOCUMENT ME!
81    */
82   public JPredFile(Object inFile, DataSourceType sourceType)
83           throws IOException
84   {
85     super(inFile, sourceType);
86   }
87
88   public JPredFile(FileParse source) throws IOException
89   {
90     super(source);
91   }
92
93   /**
94    * DOCUMENT ME!
95    * 
96    * @param QuerySeqPosition
97    *          DOCUMENT ME!
98    */
99   public void setQuerySeqPosition(int QuerySeqPosition)
100   {
101     this.QuerySeqPosition = QuerySeqPosition;
102   }
103
104   /**
105    * DOCUMENT ME!
106    * 
107    * @return DOCUMENT ME!
108    */
109   public int getQuerySeqPosition()
110   {
111     return QuerySeqPosition;
112   }
113
114   /**
115    * DOCUMENT ME!
116    * 
117    * @return DOCUMENT ME!
118    */
119   public Hashtable getScores()
120   {
121     return Scores;
122   }
123
124   /**
125    * DOCUMENT ME!
126    * 
127    * @return DOCUMENT ME!
128    */
129   public Hashtable getSymscores()
130   {
131     return Symscores;
132   }
133
134   /**
135    * DOCUMENT ME!
136    */
137   @Override
138   public void initData()
139   {
140     super.initData();
141     Scores = new Hashtable();
142     ids = null;
143     conf = null;
144   }
145
146   /**
147    * parse a JPred concise file into a sequence-alignment like object.
148    */
149   @Override
150   public void parse() throws IOException
151   {
152     // JBPNote log.System.out.println("all read in ");
153     String line;
154     QuerySeqPosition = -1;
155     noSeqs = 0;
156
157     Vector seq_entries = new Vector();
158     Vector ids = new Vector();
159     Hashtable Symscores = new Hashtable();
160
161     while ((line = nextLine()) != null)
162     {
163       // Concise format allows no comments or non comma-formatted data
164       StringTokenizer str = new StringTokenizer(line, ":");
165       String id = "";
166
167       if (!str.hasMoreTokens())
168       {
169         continue;
170       }
171
172       id = str.nextToken();
173
174       String seqsym = str.nextToken();
175       StringTokenizer symbols = new StringTokenizer(seqsym, ",");
176
177       // decide if we have more than just alphanumeric symbols
178       int numSymbols = symbols.countTokens();
179
180       if (numSymbols == 0)
181       {
182         continue;
183       }
184
185       if (seqsym.length() != (2 * numSymbols))
186       {
187         // Set of scalars for some property
188         if (Scores.containsKey(id))
189         {
190           int i = 1;
191
192           while (Scores.containsKey(id + "_" + i))
193           {
194             i++;
195           }
196
197           id = id + "_" + i;
198         }
199
200         Vector scores = new Vector();
201
202         // Typecheck from first entry
203         int i = 0;
204         String ascore = "dead";
205
206         try
207         {
208           // store elements as floats...
209           while (symbols.hasMoreTokens())
210           {
211             ascore = symbols.nextToken();
212
213             Float score = Float.valueOf(ascore);
214             scores.addElement(score);
215           }
216
217           Scores.put(id, scores);
218         } catch (Exception e)
219         {
220           // or just keep them as strings
221           i = scores.size();
222
223           for (int j = 0; j < i; j++)
224           {
225             scores.setElementAt(((Float) scores.elementAt(j)).toString(),
226                     j);
227           }
228
229           scores.addElement(ascore);
230
231           while (symbols.hasMoreTokens())
232           {
233             ascore = symbols.nextToken();
234             scores.addElement(ascore);
235           }
236
237           Scores.put(id, scores);
238         }
239       }
240       else if (id.equals("jnetconf"))
241       {
242         // log.debug System.out.println("here");
243         id = "Prediction Confidence";
244         this.conf = new Vector(numSymbols);
245
246         for (int i = 0; i < numSymbols; i++)
247         {
248           conf.setElementAt(symbols.nextToken(), i);
249         }
250       }
251       else
252       {
253         // Sequence or a prediction string (rendered as sequence)
254         StringBuffer newseq = new StringBuffer();
255
256         for (int i = 0; i < numSymbols; i++)
257         {
258           newseq.append(symbols.nextToken());
259         }
260
261         if (id.indexOf(";") > -1)
262         {
263           seq_entries.addElement(newseq);
264
265           int i = 1;
266           String name = id.substring(id.indexOf(";") + 1);
267
268           while (ids.lastIndexOf(name) > -1)
269           {
270             name = id.substring(id.indexOf(";") + 1) + "_" + ++i;
271           }
272
273           if (QuerySeqPosition == -1)
274           {
275             QuerySeqPosition = ids.size();
276           }
277           ids.addElement(name);
278           noSeqs++;
279         }
280         else
281         {
282           if (id.equals("JNETPRED"))
283           {
284             id = "Predicted Secondary Structure";
285           }
286
287           seq_entries.addElement(newseq.toString());
288           ids.addElement(id);
289           Symscores.put(id, Integer.valueOf(ids.size() - 1));
290         }
291       }
292     }
293     /*
294      * leave it to the parser user to actually check this. if (noSeqs < 1) {
295      * throw new IOException( "JpredFile Parser: No sequence in the
296      * prediction!"); }
297      */
298
299     maxLength = seq_entries.elementAt(0).toString().length();
300
301     for (int i = 0; i < ids.size(); i++)
302     {
303       // Add all sequence like objects
304       Sequence newSeq = new Sequence(ids.elementAt(i).toString(),
305               seq_entries.elementAt(i).toString(), 1,
306               seq_entries.elementAt(i).toString().length());
307
308       if (maxLength != seq_entries.elementAt(i).toString().length())
309       {
310         throw new IOException(MessageManager.formatMessage(
311                 "exception.jpredconcide_entry_has_unexpected_number_of_columns",
312                 new String[]
313                 { ids.elementAt(i).toString() }));
314       }
315
316       if ((newSeq.getName().startsWith("QUERY")
317               || newSeq.getName().startsWith("align;"))
318               && (QuerySeqPosition == -1))
319       {
320         QuerySeqPosition = seqs.size();
321       }
322
323       seqs.addElement(newSeq);
324     }
325     if (seqs.size() > 0 && QuerySeqPosition > -1)
326     {
327       // try to make annotation for a prediction only input (default if no
328       // alignment is given and prediction contains a QUERY or align;sequence_id
329       // line)
330       Alignment tal = new Alignment(this.getSeqsAsArray());
331       try
332       {
333         JnetAnnotationMaker.add_annotation(this, tal, QuerySeqPosition,
334                 true);
335       } catch (Exception e)
336       {
337         tal = null;
338         IOException ex = new IOException(MessageManager.formatMessage(
339                 "exception.couldnt_parse_concise_annotation_for_prediction",
340                 new String[]
341                 { e.getMessage() }));
342         e.printStackTrace(); // java 1.1 does not have :
343                              // ex.setStackTrace(e.getStackTrace());
344         throw ex;
345       }
346       this.annotations = new Vector();
347       AlignmentAnnotation[] aan = tal.getAlignmentAnnotation();
348       for (int aai = 0; aan != null && aai < aan.length; aai++)
349       {
350         annotations.addElement(aan[aai]);
351       }
352     }
353   }
354
355   /**
356    * print
357    * 
358    * @return String
359    */
360   @Override
361   public String print(SequenceI[] sqs, boolean jvsuffix)
362   {
363     return "Not Supported";
364   }
365
366   /**
367    * 
368    * @param args
369    * @j2sIgnore
370    */
371   public static void main(String[] args)
372   {
373     try
374     {
375       JPredFile jpred = new JPredFile(args[0], DataSourceType.FILE);
376
377       for (int i = 0; i < jpred.seqs.size(); i++)
378       {
379         System.out.println(((Sequence) jpred.seqs.elementAt(i)).getName()
380                 + "\n"
381                 + ((Sequence) jpred.seqs.elementAt(i)).getSequenceAsString()
382                 + "\n");
383       }
384     } catch (java.io.IOException e)
385     {
386       System.err.println("Exception " + e);
387       // e.printStackTrace(); not java 1.1 compatible!
388     }
389   }
390
391   Vector annotSeqs = null;
392
393   /**
394    * removeNonSequences
395    */
396   public void removeNonSequences()
397   {
398     if (annotSeqs != null)
399     {
400       return;
401     }
402     annotSeqs = new Vector();
403     Vector newseqs = new Vector();
404     int i = 0;
405     int j = seqs.size();
406     for (; i < QuerySeqPosition; i++)
407     {
408       annotSeqs.addElement(seqs.elementAt(i));
409     }
410     // check that no stray annotations have been added at the end.
411     {
412       SequenceI sq = seqs.elementAt(j - 1);
413       if (sq.getName().toUpperCase(Locale.ROOT).startsWith("JPRED"))
414       {
415         annotSeqs.addElement(sq);
416         seqs.removeElementAt(--j);
417       }
418     }
419     for (; i < j; i++)
420     {
421       newseqs.addElement(seqs.elementAt(i));
422     }
423
424     seqs.removeAllElements();
425     seqs = newseqs;
426   }
427 }
428
429 /*
430  * StringBuffer out = new StringBuffer();
431  * 
432  * out.append("START PRED\n"); for (int i = 0; i < s[0].sequence.length(); i++)
433  * { out.append(s[0].sequence.substring(i, i + 1) + " ");
434  * out.append(s[1].sequence.substring(i, i + 1) + " ");
435  * out.append(s[1].score[0].elementAt(i) + " ");
436  * out.append(s[1].score[1].elementAt(i) + " ");
437  * out.append(s[1].score[2].elementAt(i) + " ");
438  * out.append(s[1].score[3].elementAt(i) + " ");
439  * 
440  * out.append("\n"); } out.append("END PRED\n"); return out.toString(); }
441  * 
442  * public static void main(String[] args) { try { BLCFile blc = new
443  * BLCFile(args[0], "File"); DrawableSequence[] s = new
444  * DrawableSequence[blc.seqs.size()]; for (int i = 0; i < blc.seqs.size(); i++)
445  * { s[i] = new DrawableSequence( (Sequence) blc.seqs.elementAt(i)); } String
446  * out = BLCFile.print(s);
447  * 
448  * AlignFrame af = new AlignFrame(null, s); af.resize(700, 500); af.show();
449  * System.out.println(out); } catch (java.io.IOException e) {
450  * System.out.println("Exception " + e); } } }
451  */