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