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