JAL-629 implementation of --tempfac options
[jalview.git] / src / jalview / io / AlignFile.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 package jalview.io;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.Hashtable;
27 import java.util.List;
28 import java.util.Vector;
29
30 import jalview.datamodel.AlignmentAnnotation;
31 import jalview.datamodel.AlignmentAnnotation.TFType;
32 import jalview.datamodel.AlignmentI;
33 import jalview.datamodel.Sequence;
34 import jalview.datamodel.SequenceGroup;
35 import jalview.datamodel.SequenceI;
36 import jalview.util.MessageManager;
37
38 /**
39  * DOCUMENT ME!
40  * 
41  * @author $author$
42  * @version $Revision$
43  */
44 public abstract class AlignFile extends FileParse
45         implements AlignmentFileReaderI, AlignmentFileWriterI
46 {
47   int noSeqs = 0;
48
49   int maxLength = 0;
50
51   /**
52    * Sequences to be added to form a new alignment. TODO: remove vector in this
53    * class
54    */
55   protected Vector<SequenceI> seqs;
56
57   /**
58    * annotation to be added to generated alignment object
59    */
60   protected Vector<AlignmentAnnotation> annotations;
61
62   /**
63    * SequenceGroups to be added to the alignment object
64    */
65   protected List<SequenceGroup> seqGroups;
66
67   /**
68    * Properties to be added to generated alignment object
69    */
70   private Hashtable properties;
71
72   long start;
73
74   long end;
75
76   /**
77    * true if parse() has been called
78    */
79   private boolean parseCalled = false;
80
81   private boolean parseImmediately = true;
82
83   private boolean dataClosed = false;
84
85   private AlignmentAnnotation.TFType temperatureFactorType = null;
86
87   public AlignmentAnnotation.TFType getTemperatureFactorType()
88   {
89     return this.temperatureFactorType;
90   }
91
92   /**
93    * @return if doParse() was called at construction time
94    */
95   protected boolean isParseImmediately()
96   {
97     return parseImmediately;
98   }
99
100   /**
101    * Creates a new AlignFile object.
102    */
103   public AlignFile()
104   {
105     // Shouldn't we init data structures (JBPNote: not sure - initData is for
106     // initialising the structures used for reading from a datasource, and the
107     // bare constructor hasn't got any datasource)
108     initData();
109   }
110
111   public AlignFile(SequenceI[] seqs)
112   {
113     this();
114     setSeqs(seqs);
115   }
116
117   /**
118    * Constructor which parses the data from a file of some specified type.
119    * 
120    * @param dataObject
121    *          Filename, URL or Pasted String to read from.
122    * @param sourceType
123    *          What type of file to read from (File, URL, Pasted String)
124    */
125   public AlignFile(Object dataObject, DataSourceType sourceType)
126           throws IOException
127   {
128     this(dataObject, sourceType, null);
129   }
130
131   public AlignFile(Object dataObject, DataSourceType sourceType,
132           AlignmentAnnotation.TFType tempfacType) throws IOException
133   {
134     this(true, dataObject, sourceType, tempfacType);
135   }
136
137   /**
138    * Constructor which (optionally delays) parsing of data from a file of some
139    * specified type.
140    * 
141    * @param parseImmediately
142    *          if false, need to call 'doParse()' to begin parsing data
143    * @param dataObject
144    *          Filename, URL or Pasted String to read from.
145    * @param sourceType
146    *          What type of file to read from (File, URL)
147    * @throws IOException
148    */
149   public AlignFile(boolean parseImmediately, Object dataObject,
150           DataSourceType sourceType) throws IOException
151   {
152     this(parseImmediately, dataObject, sourceType, null);
153   }
154
155   public AlignFile(boolean parseImmediately, Object dataObject,
156           DataSourceType sourceType, AlignmentAnnotation.TFType tempfacType) throws IOException
157   {
158     // BH allows File or String
159     super(dataObject, sourceType);
160     initData();
161     this.temperatureFactorType = tempfacType;
162     if (parseImmediately)
163     {
164       doParse();
165     }
166   }
167
168   /**
169    * Attempt to read from the position where some other parsing process left
170    * off.
171    * 
172    * @param source
173    * @throws IOException
174    */
175   public AlignFile(FileParse source) throws IOException
176   {
177     this(true, source);
178   }
179
180   /**
181    * Construct a new parser to read from the position where some other parsing
182    * process left
183    * 
184    * @param parseImmediately
185    *          if false, need to call 'doParse()' to begin parsing data
186    * @param source
187    */
188   public AlignFile(boolean parseImmediately, FileParse source)
189           throws IOException
190   {
191     this(parseImmediately, source, true);
192   }
193
194   public AlignFile(boolean parseImmediately, FileParse source,
195           boolean closeData) throws IOException
196   {
197     super(source);
198     initData();
199
200     // stash flag in case parse needs to know if it has to autoconfigure or was
201     // configured after construction
202     this.parseImmediately = parseImmediately;
203
204     if (parseImmediately)
205     {
206       doParse(closeData);
207     }
208   }
209
210   /**
211    * called if parsing was delayed till after parser was constructed
212    * 
213    * @throws IOException
214    */
215   public void doParse() throws IOException
216   {
217     doParse(true);
218   }
219
220   public void doParse(boolean closeData) throws IOException
221   {
222     if (parseCalled)
223     {
224       throw new IOException(
225               "Implementation error: Parser called twice for same data.\n"
226                       + "Need to call initData() again before parsing can be reattempted.");
227     }
228     parseCalled = true;
229     parse();
230     if (closeData && !dataClosed)
231     {
232       dataIn.close();
233       dataClosed = true;
234     }
235   }
236
237   /**
238    * Return the seqs Vector
239    */
240   public Vector<SequenceI> getSeqs()
241   {
242     return seqs;
243   }
244
245   public List<SequenceGroup> getSeqGroups()
246   {
247     return seqGroups;
248   }
249
250   /**
251    * Return the Sequences in the seqs Vector as an array of Sequences
252    */
253   @Override
254   public SequenceI[] getSeqsAsArray()
255   {
256     SequenceI[] s = new SequenceI[seqs.size()];
257
258     for (int i = 0; i < seqs.size(); i++)
259     {
260       s[i] = seqs.elementAt(i);
261     }
262
263     return s;
264   }
265
266   /**
267    * called by AppletFormatAdapter to generate an annotated alignment, rather
268    * than bare sequences.
269    * 
270    * @param al
271    */
272   @Override
273   public void addAnnotations(AlignmentI al)
274   {
275     addProperties(al);
276     for (int i = 0; i < annotations.size(); i++)
277     {
278       // detect if annotations.elementAt(i) rna secondary structure
279       // if so then do:
280       /*
281        * SequenceFeature[] pairArray =
282        * Rna.GetBasePairsFromAlignmentAnnotation(annotations.elementAt(i));
283        * Rna.HelixMap(pairArray);
284        */
285       AlignmentAnnotation an = annotations.elementAt(i);
286       an.validateRangeAndDisplay();
287       al.addAnnotation(an);
288     }
289
290   }
291
292   /**
293    * register sequence groups on the alignment for **output**
294    * 
295    * @param al
296    */
297   public void addSeqGroups(AlignmentI al)
298   {
299     this.seqGroups = al.getGroups();
300
301   }
302
303   /**
304    * Add any additional information extracted from the file to the alignment
305    * properties.
306    * 
307    * @note implicitly called by addAnnotations()
308    * @param al
309    */
310   public void addProperties(AlignmentI al)
311   {
312     if (properties != null && properties.size() > 0)
313     {
314       Enumeration keys = properties.keys();
315       Enumeration vals = properties.elements();
316       while (keys.hasMoreElements())
317       {
318         al.setProperty(keys.nextElement(), vals.nextElement());
319       }
320     }
321   }
322
323   /**
324    * Store a non-null key-value pair in a hashtable used to set alignment
325    * properties note: null keys will raise an error, null values will result in
326    * the key/value pair being silently ignored.
327    * 
328    * @param key
329    *          - non-null key object
330    * @param value
331    *          - non-null value
332    */
333   protected void setAlignmentProperty(Object key, Object value)
334   {
335     if (key == null)
336     {
337       throw new Error(MessageManager.getString(
338               "error.implementation_error_cannot_have_null_alignment"));
339     }
340     if (value == null)
341     {
342       return; // null properties are ignored.
343     }
344     if (properties == null)
345     {
346       properties = new Hashtable();
347     }
348     properties.put(key, value);
349   }
350
351   protected Object getAlignmentProperty(Object key)
352   {
353     if (properties != null && key != null)
354     {
355       return properties.get(key);
356     }
357     return null;
358   }
359
360   /**
361    * Initialise objects to store sequence data in.
362    */
363   protected void initData()
364   {
365     seqs = new Vector<SequenceI>();
366     annotations = new Vector<AlignmentAnnotation>();
367     seqGroups = new ArrayList<SequenceGroup>();
368     parseCalled = false;
369   }
370
371   /**
372    * DOCUMENT ME!
373    * 
374    * @param s
375    *          DOCUMENT ME!
376    */
377   @Override
378   public void setSeqs(SequenceI[] s)
379   {
380     seqs = new Vector<SequenceI>();
381
382     for (int i = 0; i < s.length; i++)
383     {
384       seqs.addElement(s[i]);
385     }
386   }
387
388   /**
389    * This method must be implemented to parse the contents of the file.
390    */
391   public abstract void parse() throws IOException;
392
393   /**
394    * A general parser for ids.
395    * 
396    * @String id Id to be parsed
397    */
398   Sequence parseId(String id)
399   {
400     Sequence seq = null;
401     id = id.trim();
402     int space = id.indexOf(" ");
403     if (space > -1)
404     {
405       seq = new Sequence(id.substring(0, space), "");
406       String desc = id.substring(space + 1);
407       seq.setDescription(desc);
408
409       /*
410        * it is tempting to parse Ensembl style gene description e.g.
411        * chromosome:GRCh38:7:140696688:140721955:1 and set the
412        * start position of the sequence, but this causes much confusion
413        * for reverse strand feature locations
414        */
415     }
416     else
417     {
418       seq = new Sequence(id, "");
419     }
420
421     return seq;
422   }
423
424   /**
425    * Creates the output id. Adds prefix Uniprot format source|id and optionally
426    * suffix Jalview /start-end
427    * 
428    * @param jvsuffix
429    * 
430    * @String id Id to be parsed
431    */
432   String printId(SequenceI seq, boolean jvsuffix)
433   {
434     return seq.getDisplayId(jvsuffix);
435   }
436
437   String printId(SequenceI seq)
438   {
439     return printId(seq, true);
440   }
441
442   /**
443    * vector of String[] treeName, newickString pairs
444    */
445   Vector<String[]> newickStrings = null;
446
447   protected void addNewickTree(String treeName, String newickString)
448   {
449     if (newickStrings == null)
450     {
451       newickStrings = new Vector<String[]>();
452     }
453     newickStrings.addElement(new String[] { treeName, newickString });
454   }
455
456   protected int getTreeCount()
457   {
458     return newickStrings == null ? 0 : newickStrings.size();
459   }
460
461   @Override
462   public void addGroups(AlignmentI al)
463   {
464
465     for (SequenceGroup sg : getSeqGroups())
466     {
467       al.addGroup(sg);
468     }
469   }
470
471   protected void addSequence(SequenceI seq)
472   {
473     seqs.add(seq);
474   }
475 }