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    * @param inFile
73    *          DOCUMENT ME!
74    * @param sourceType
75    *          DOCUMENT ME!
76    * 
77    * @throws IOException
78    *           DOCUMENT ME!
79    */
80   public JPredFile(String inFile, DataSourceType sourceType)
81           throws IOException
82   {
83     super(inFile, sourceType);
84   }
85
86   public JPredFile(FileParse source) throws IOException
87   {
88     super(source);
89   }
90
91   /**
92    * DOCUMENT ME!
93    * 
94    * @param QuerySeqPosition
95    *          DOCUMENT ME!
96    */
97   public void setQuerySeqPosition(int QuerySeqPosition)
98   {
99     this.QuerySeqPosition = QuerySeqPosition;
100   }
101
102   /**
103    * DOCUMENT ME!
104    * 
105    * @return DOCUMENT ME!
106    */
107   public int getQuerySeqPosition()
108   {
109     return QuerySeqPosition;
110   }
111
112   /**
113    * DOCUMENT ME!
114    * 
115    * @return DOCUMENT ME!
116    */
117   public Hashtable getScores()
118   {
119     return Scores;
120   }
121
122   /**
123    * DOCUMENT ME!
124    * 
125    * @return DOCUMENT ME!
126    */
127   public Hashtable getSymscores()
128   {
129     return Symscores;
130   }
131
132   /**
133    * DOCUMENT ME!
134    */
135   @Override
136   public void initData()
137   {
138     super.initData();
139     Scores = new Hashtable();
140     ids = null;
141     conf = null;
142   }
143
144   /**
145    * parse a JPred concise file into a sequence-alignment like object.
146    */
147   @Override
148   public void parse() throws IOException
149   {
150     // JBPNote log.System.out.println("all read in ");
151     String line;
152     QuerySeqPosition = -1;
153     noSeqs = 0;
154
155     Vector seq_entries = new Vector();
156     Vector ids = new Vector();
157     Hashtable Symscores = new Hashtable();
158
159     while ((line = nextLine()) != null)
160     {
161       // Concise format allows no comments or non comma-formatted data
162       StringTokenizer str = new StringTokenizer(line, ":");
163       String id = "";
164
165       if (!str.hasMoreTokens())
166       {
167         continue;
168       }
169
170       id = str.nextToken();
171
172       String seqsym = str.nextToken();
173       StringTokenizer symbols = new StringTokenizer(seqsym, ",");
174
175       // decide if we have more than just alphanumeric symbols
176       int numSymbols = symbols.countTokens();
177
178       if (numSymbols == 0)
179       {
180         continue;
181       }
182
183       if (seqsym.length() != (2 * numSymbols))
184       {
185         // Set of scalars for some property
186         if (Scores.containsKey(id))
187         {
188           int i = 1;
189
190           while (Scores.containsKey(id + "_" + i))
191           {
192             i++;
193           }
194
195           id = id + "_" + i;
196         }
197
198         Vector scores = new Vector();
199
200         // Typecheck from first entry
201         int i = 0;
202         String ascore = "dead";
203
204         try
205         {
206           // store elements as floats...
207           while (symbols.hasMoreTokens())
208           {
209             ascore = symbols.nextToken();
210
211             Float score = Float.valueOf(ascore);
212             scores.addElement(score);
213           }
214
215           Scores.put(id, scores);
216         } catch (Exception e)
217         {
218           // or just keep them as strings
219           i = scores.size();
220
221           for (int j = 0; j < i; j++)
222           {
223             scores.setElementAt(((Float) scores.elementAt(j)).toString(),
224                     j);
225           }
226
227           scores.addElement(ascore);
228
229           while (symbols.hasMoreTokens())
230           {
231             ascore = symbols.nextToken();
232             scores.addElement(ascore);
233           }
234
235           Scores.put(id, scores);
236         }
237       }
238       else if (id.equals("jnetconf"))
239       {
240         // log.debug System.out.println("here");
241         id = "Prediction Confidence";
242         this.conf = new Vector(numSymbols);
243
244         for (int i = 0; i < numSymbols; i++)
245         {
246           conf.setElementAt(symbols.nextToken(), i);
247         }
248       }
249       else
250       {
251         // Sequence or a prediction string (rendered as sequence)
252         StringBuffer newseq = new StringBuffer();
253
254         for (int i = 0; i < numSymbols; i++)
255         {
256           newseq.append(symbols.nextToken());
257         }
258
259         if (id.indexOf(";") > -1)
260         {
261           seq_entries.addElement(newseq);
262
263           int i = 1;
264           String name = id.substring(id.indexOf(";") + 1);
265
266           while (ids.lastIndexOf(name) > -1)
267           {
268             name = id.substring(id.indexOf(";") + 1) + "_" + ++i;
269           }
270
271           if (QuerySeqPosition == -1)
272           {
273             QuerySeqPosition = ids.size();
274           }
275           ids.addElement(name);
276           noSeqs++;
277         }
278         else
279         {
280           if (id.equals("JNETPRED"))
281           {
282             id = "Predicted Secondary Structure";
283           }
284
285           seq_entries.addElement(newseq.toString());
286           ids.addElement(id);
287           Symscores.put(id, Integer.valueOf(ids.size() - 1));
288         }
289       }
290     }
291     /*
292      * leave it to the parser user to actually check this. if (noSeqs < 1) {
293      * throw new IOException( "JpredFile Parser: No sequence in the
294      * prediction!"); }
295      */
296
297     maxLength = seq_entries.elementAt(0).toString().length();
298
299     for (int i = 0; i < ids.size(); i++)
300     {
301       // Add all sequence like objects
302       Sequence newSeq = new Sequence(ids.elementAt(i).toString(),
303               seq_entries.elementAt(i).toString(), 1,
304               seq_entries.elementAt(i).toString().length());
305
306       if (maxLength != seq_entries.elementAt(i).toString().length())
307       {
308         throw new IOException(MessageManager.formatMessage(
309                 "exception.jpredconcide_entry_has_unexpected_number_of_columns",
310                 new String[]
311                 { ids.elementAt(i).toString() }));
312       }
313
314       if ((newSeq.getName().startsWith("QUERY")
315               || newSeq.getName().startsWith("align;"))
316               && (QuerySeqPosition == -1))
317       {
318         QuerySeqPosition = seqs.size();
319       }
320
321       seqs.addElement(newSeq);
322     }
323     if (seqs.size() > 0 && QuerySeqPosition > -1)
324     {
325       // try to make annotation for a prediction only input (default if no
326       // alignment is given and prediction contains a QUERY or align;sequence_id
327       // line)
328       Alignment tal = new Alignment(this.getSeqsAsArray());
329       try
330       {
331         JnetAnnotationMaker.add_annotation(this, tal, QuerySeqPosition,
332                 true);
333       } catch (Exception e)
334       {
335         tal = null;
336         IOException ex = new IOException(MessageManager.formatMessage(
337                 "exception.couldnt_parse_concise_annotation_for_prediction",
338                 new String[]
339                 { e.getMessage() }));
340         e.printStackTrace(); // java 1.1 does not have :
341                              // ex.setStackTrace(e.getStackTrace());
342         throw ex;
343       }
344       this.annotations = new Vector();
345       AlignmentAnnotation[] aan = tal.getAlignmentAnnotation();
346       for (int aai = 0; aan != null && aai < aan.length; aai++)
347       {
348         annotations.addElement(aan[aai]);
349       }
350     }
351   }
352
353   /**
354    * print
355    * 
356    * @return String
357    */
358   @Override
359   public String print(SequenceI[] sqs, boolean jvsuffix)
360   {
361     return "Not Supported";
362   }
363
364   /**
365    * DOCUMENT ME!
366    * 
367    * @param args
368    *          DOCUMENT ME!
369    */
370   public static void main(String[] args)
371   {
372     try
373     {
374       JPredFile jpred = new JPredFile(args[0], DataSourceType.FILE);
375
376       for (int i = 0; i < jpred.seqs.size(); i++)
377       {
378         System.out.println(((Sequence) jpred.seqs.elementAt(i)).getName()
379                 + "\n"
380                 + ((Sequence) jpred.seqs.elementAt(i)).getSequenceAsString()
381                 + "\n");
382       }
383     } catch (java.io.IOException e)
384     {
385       System.err.println("Exception " + e);
386       // e.printStackTrace(); not java 1.1 compatible!
387     }
388   }
389
390   Vector annotSeqs = null;
391
392   /**
393    * removeNonSequences
394    */
395   public void removeNonSequences()
396   {
397     if (annotSeqs != null)
398     {
399       return;
400     }
401     annotSeqs = new Vector();
402     Vector newseqs = new Vector();
403     int i = 0;
404     int j = seqs.size();
405     for (; i < QuerySeqPosition; i++)
406     {
407       annotSeqs.addElement(seqs.elementAt(i));
408     }
409     // check that no stray annotations have been added at the end.
410     {
411       SequenceI sq = seqs.elementAt(j - 1);
412       if (sq.getName().toUpperCase(Locale.ROOT).startsWith("JPRED"))
413       {
414         annotSeqs.addElement(sq);
415         seqs.removeElementAt(--j);
416       }
417     }
418     for (; i < j; i++)
419     {
420       newseqs.addElement(seqs.elementAt(i));
421     }
422
423     seqs.removeAllElements();
424     seqs = newseqs;
425   }
426 }
427
428 /*
429  * StringBuffer out = new StringBuffer();
430  * 
431  * out.append("START PRED\n"); for (int i = 0; i < s[0].sequence.length(); i++)
432  * { out.append(s[0].sequence.substring(i, i + 1) + " ");
433  * out.append(s[1].sequence.substring(i, i + 1) + " ");
434  * out.append(s[1].score[0].elementAt(i) + " ");
435  * out.append(s[1].score[1].elementAt(i) + " ");
436  * out.append(s[1].score[2].elementAt(i) + " ");
437  * out.append(s[1].score[3].elementAt(i) + " ");
438  * 
439  * out.append("\n"); } out.append("END PRED\n"); return out.toString(); }
440  * 
441  * public static void main(String[] args) { try { BLCFile blc = new
442  * BLCFile(args[0], "File"); DrawableSequence[] s = new
443  * DrawableSequence[blc.seqs.size()]; for (int i = 0; i < blc.seqs.size(); i++)
444  * { s[i] = new DrawableSequence( (Sequence) blc.seqs.elementAt(i)); } String
445  * out = BLCFile.print(s);
446  * 
447  * AlignFrame af = new AlignFrame(null, s); af.resize(700, 500); af.show();
448  * System.out.println(out); } catch (java.io.IOException e) {
449  * System.out.println("Exception " + e); } } }
450  */