2340283ea2e31f0f538a1b4561f411ca3fb95e6f
[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         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 Hashtable 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 && properties.size() > 0)
255     {
256       Enumeration keys = properties.keys();
257       Enumeration vals = properties.elements();
258       while (keys.hasMoreElements())
259       {
260         al.setProperty(keys.nextElement(), vals.nextElement());
261       }
262     }
263   }
264
265   /**
266    * Store a non-null key-value pair in a hashtable used to set alignment
267    * properties note: null keys will raise an error, null values will result in
268    * the key/value pair being silently ignored.
269    * 
270    * @param key
271    *          - non-null key object
272    * @param value
273    *          - non-null value
274    */
275   protected void setAlignmentProperty(Object key, Object value)
276   {
277     if (key == null)
278     {
279       throw new Error(MessageManager.getString(
280               "error.implementation_error_cannot_have_null_alignment"));
281     }
282     if (value == null)
283     {
284       return; // null properties are ignored.
285     }
286     if (properties == null)
287     {
288       properties = new Hashtable();
289     }
290     properties.put(key, value);
291   }
292
293   protected Object getAlignmentProperty(Object key)
294   {
295     if (properties != null && key != null)
296     {
297       return properties.get(key);
298     }
299     return null;
300   }
301
302   /**
303    * Initialise objects to store sequence data in.
304    */
305   protected void initData()
306   {
307     seqs = new Vector<SequenceI>();
308     annotations = new Vector<AlignmentAnnotation>();
309     seqGroups = new ArrayList<SequenceGroup>();
310     parseCalled = false;
311   }
312
313   /**
314    * DOCUMENT ME!
315    * 
316    * @param s
317    *          DOCUMENT ME!
318    */
319   @Override
320   public void setSeqs(SequenceI[] s)
321   {
322     seqs = new Vector<SequenceI>();
323
324     for (int i = 0; i < s.length; i++)
325     {
326       seqs.addElement(s[i]);
327     }
328   }
329
330   /**
331    * This method must be implemented to parse the contents of the file.
332    */
333   public abstract void parse() throws IOException;
334
335   /**
336    * A general parser for ids.
337    * 
338    * @String id Id to be parsed
339    */
340   Sequence parseId(String id)
341   {
342     Sequence seq = null;
343     id = id.trim();
344     int space = id.indexOf(" ");
345     if (space > -1)
346     {
347       seq = new Sequence(id.substring(0, space), "");
348       String desc = id.substring(space + 1);
349       seq.setDescription(desc);
350
351       /*
352        * it is tempting to parse Ensembl style gene description e.g.
353        * chromosome:GRCh38:7:140696688:140721955:1 and set the
354        * start position of the sequence, but this causes much confusion
355        * for reverse strand feature locations
356        */
357     }
358     else
359     {
360       seq = new Sequence(id, "");
361     }
362
363     return seq;
364   }
365
366   /**
367    * Creates the output id. Adds prefix Uniprot format source|id and optionally
368    * suffix Jalview /start-end
369    * 
370    * @param jvsuffix
371    * 
372    * @String id Id to be parsed
373    */
374   String printId(SequenceI seq, boolean jvsuffix)
375   {
376     return seq.getDisplayId(jvsuffix);
377   }
378
379   String printId(SequenceI seq)
380   {
381     return printId(seq, true);
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   @Override
404   public void addGroups(AlignmentI al)
405   {
406
407     for (SequenceGroup sg : getSeqGroups())
408     {
409       al.addGroup(sg);
410     }
411   }
412
413   protected void addSequence(SequenceI seq)
414   {
415     seqs.add(seq);
416   }
417 }