JAL-3060 entry points for createFeatures, amendFeatures
[jalview.git] / src / jalview / io / SimpleBlastFile.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.Sequence;
24 import jalview.datamodel.SequenceI;
25
26 import java.io.IOException;
27 import java.util.Enumeration;
28 import java.util.Hashtable;
29 import java.util.Vector;
30
31 /**
32  * parse a simple blast report. Attempt to cope with query anchored and pairwise
33  * alignments only.
34  * 
35  * @author Jim Procter
36  */
37
38 public class SimpleBlastFile extends AlignFile
39 {
40   /**
41    * header and footer info goes into alignment annotation.
42    */
43   StringBuffer headerLines, footerLines;
44
45   /**
46    * hold sequence ids in order of appearance in file
47    */
48   Vector seqids;
49
50   public SimpleBlastFile()
51   {
52   }
53
54   public SimpleBlastFile(String inFile, DataSourceType sourceType)
55           throws IOException
56   {
57     super(inFile, sourceType);
58   }
59
60   public SimpleBlastFile(FileParse source) throws IOException
61   {
62     super(source);
63   }
64
65   @Override
66   public void initData()
67   {
68     super.initData();
69     headerLines = new StringBuffer();
70     footerLines = new StringBuffer();
71     seqids = new Vector();
72   }
73
74   @Override
75   public void parse() throws IOException
76   {
77     String line;
78     char gapc = ' '; // nominal gap character
79     Hashtable seqhash = new Hashtable();
80     boolean inAlignments = false;
81     int padding = -1, numcol = -1, aligcol = -1, lastcol = -1;
82     long qlen = 0, rstart, rend; // total number of query bases so far
83     boolean padseq = false;
84     while ((line = nextLine()) != null)
85     {
86       if (line.indexOf("ALIGNMENTS") == 0)
87       {
88         inAlignments = true;
89       }
90       else
91       {
92         if (inAlignments)
93         {
94           if (line.trim().length() == 0)
95           {
96             continue;
97           }
98           // parse out the sequences
99           // query anchored means that we use the query sequence as the
100           // alignment ruler
101           if (line.indexOf("Query") == 0)
102           {
103             padding = -1;
104             // reset column markers for this block
105             numcol = -1;
106             aligcol = -1;
107             lastcol = -1;
108             // init or reset the column positions
109             for (int p = 5, mLen = line.length(); p < mLen; p++)
110             {
111               char c = line.charAt(p);
112               if (c >= '0' && c <= '9')
113               {
114                 if (numcol == -1)
115                 {
116                   numcol = p;
117                 }
118                 else if (aligcol != -1 && lastcol == -1)
119                 {
120                   lastcol = p;
121                 }
122               }
123               else
124               {
125                 if (c >= 'A' && c <= 'z')
126                 {
127                   if (aligcol == -1)
128                   {
129                     aligcol = p;
130                     padding = -1;
131                   }
132                 }
133                 else
134                 {
135                   if (padding == -1)
136                   {
137                     padding = p; // beginning of last stretch of whitespace
138                   }
139                 }
140               }
141             }
142             if (padding == -1)
143             {
144               padding = aligcol;
145             }
146           }
147           if (line.indexOf("Database:") > -1
148                   || (aligcol == -1 || numcol == -1 || lastcol == -1)
149                   || line.length() < lastcol)
150           {
151             inAlignments = false;
152           }
153           else
154           {
155             // now extract the alignment.
156             String sqid = line.substring(0, numcol).trim();
157             String stindx = line.substring(numcol, aligcol).trim();
158             String aligseg = line.substring(aligcol, padding);
159             String endindx = line.substring(lastcol).trim();
160             // init start/end prior to parsing
161             rstart = 1; // best guess we have
162             rend = 0; // if zero at end of parsing, then we count non-gaps
163             try
164             {
165               rstart = Long.parseLong(stindx);
166             } catch (Exception e)
167             {
168               System.err.println(
169                       "Couldn't parse '" + stindx + "' as start of row");
170               // inAlignments = false;
171               // warn for this line
172             }
173             try
174             {
175               rend = Long.parseLong(endindx);
176             } catch (Exception e)
177             {
178               System.err.println(
179                       "Couldn't parse '" + endindx + "' as end of row");
180               // inAlignments = false;
181
182               // warn for this line
183             }
184             Vector seqentries = (Vector) seqhash.get(sqid);
185             if (seqentries == null)
186             {
187               seqentries = new Vector();
188               seqhash.put(sqid, seqentries);
189               seqids.addElement(sqid);
190             }
191
192             Object[] seqentry = null;
193             Enumeration sqent = seqentries.elements();
194             while (seqentry == null && sqent.hasMoreElements())
195             {
196               seqentry = (Object[]) sqent.nextElement();
197               if (((long[]) seqentry[1])[1] + 1 != rstart)
198               {
199                 seqentry = null;
200               }
201             }
202             padseq = false;
203             if (seqentry == null)
204             {
205               padseq = true; // prepend gaps to new sequences in this block
206               seqentry = new Object[] { new StringBuffer(),
207                   new long[]
208                   { rstart, rend } };
209               seqentries.addElement(seqentry);
210               seqhash.put(sqid, seqentry);
211
212             }
213             if (sqid.equals("Query"))
214             {
215               // update current block length in case we need to pad
216               qlen = ((StringBuffer) seqentry[0]).length();
217             }
218             StringBuffer sqs = ((StringBuffer) seqentry[0]);
219             if (padseq)
220             {
221               for (long c = sqs.length(); c < qlen; c++)
222               {
223                 sqs.append(gapc);
224               }
225             }
226             sqs.append(aligseg);
227             if (rend > 0)
228             {
229               ((long[]) seqentry[1])[1] = rend;
230             }
231           }
232           // end of parsing out the sequences
233         }
234         // if we haven't parsed the line as an alignment, then
235         // add to the sequence header
236         if (!inAlignments)
237         {
238           String ln = line.trim();
239           // save any header stuff for the user
240           if (ln.length() > 0)
241           {
242             StringBuffer addto = (seqhash.size() > 0) ? footerLines
243                     : headerLines;
244             addto.append(line);
245             addto.append("\n");
246           }
247         }
248       }
249     }
250     if (seqhash.size() > 0)
251     {
252       // make the sequence vector
253       Enumeration seqid = seqids.elements();
254       while (seqid.hasMoreElements())
255       {
256         String idstring = (String) seqid.nextElement();
257         Object[] seqentry = (Object[]) seqhash.get(idstring);
258         try
259         {
260           Sequence newseq = new Sequence(idstring,
261
262                   ((StringBuffer) seqentry[0]).toString(),
263                   (int) ((long[]) seqentry[1])[0],
264                   (int) ((long[]) seqentry[1])[1]);
265           if (newseq.getEnd() == 0)
266           {
267             // assume there are no deletions in the sequence.
268             newseq.setEnd(newseq.findPosition(newseq.getLength()));
269           }
270           seqs.addElement(newseq);
271         } catch (Exception e)
272         {
273           if (warningMessage == null)
274           {
275             warningMessage = "";
276           }
277           warningMessage += "Couldn't add Sequence - ID is '" + idstring
278                   + "' : Exception was " + e.toString() + "\n";
279         }
280       }
281       // add any annotation
282       if (headerLines.length() > 1)
283       {
284         setAlignmentProperty("HEADER", headerLines.toString());
285       }
286       if (footerLines.length() > 1)
287       {
288         setAlignmentProperty("FOOTER", footerLines.toString());
289       }
290     }
291   }
292
293   @Override
294   public String print(SequenceI[] sqs, boolean jvsuffix)
295   {
296     return new String("Not Implemented.");
297   }
298 }