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