Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / io / ClustalFile.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.Sequence;
25 import jalview.datamodel.SequenceI;
26 import jalview.util.Format;
27
28 import java.io.IOException;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.StringTokenizer;
32 import java.util.Vector;
33
34 public class ClustalFile extends AlignFile
35 {
36
37   public ClustalFile()
38   {
39   }
40
41   public ClustalFile(String inFile, DataSourceType sourceType)
42           throws IOException
43   {
44     super(inFile, sourceType);
45   }
46
47   public ClustalFile(FileParse source) throws IOException
48   {
49     super(source);
50   }
51
52   @Override
53   public void initData()
54   {
55     super.initData();
56   }
57
58   @Override
59   public void parse() throws IOException
60   {
61     int i = 0;
62     boolean flag = false;
63     boolean top = false;
64     StringBuffer pssecstr = new StringBuffer();
65     StringBuffer consstr = new StringBuffer();
66     Vector<String> headers = new Vector<>();
67     Map<String, StringBuffer> seqhash = new HashMap<>();
68     StringBuffer tempseq;
69     String line, id;
70     StringTokenizer str;
71
72     try
73     {
74       while ((line = nextLine()) != null)
75       {
76         if (line.length() == 0)
77         {
78           top = true;
79         }
80         boolean isConservation = line.startsWith(SPACE)
81                 || line.startsWith(TAB);
82         if (!isConservation)
83         {
84           str = new StringTokenizer(line);
85
86           if (str.hasMoreTokens())
87           {
88             id = str.nextToken();
89
90             if (id.equalsIgnoreCase("CLUSTAL"))
91             {
92               flag = true;
93             }
94             else
95             {
96               if (flag)
97               {
98                 if (seqhash.containsKey(id))
99                 {
100                   tempseq = seqhash.get(id);
101                 }
102                 else
103                 {
104                   tempseq = new StringBuffer();
105                   seqhash.put(id, tempseq);
106                 }
107
108                 if (!(headers.contains(id)))
109                 {
110                   headers.addElement(id);
111                 }
112
113                 if (str.hasMoreTokens())
114                 {
115                   tempseq.append(str.nextToken());
116                 }
117                 top = false;
118               }
119             }
120           }
121           else
122           {
123             flag = true;
124           }
125         }
126         else
127         {
128           if (line.matches("\\s+(-|\\.|\\(|\\[|\\]|\\))+"))
129           {
130             if (top)
131             {
132               pssecstr.append(line.trim());
133             }
134             else
135             {
136               consstr.append(line.trim());
137             }
138           }
139         }
140       }
141     } catch (IOException e)
142     {
143       jalview.bin.Console.errPrintln("Exception parsing clustal file " + e);
144       e.printStackTrace();
145     }
146
147     if (flag)
148     {
149       this.noSeqs = headers.size();
150
151       // Add sequences to the hash
152       for (i = 0; i < headers.size(); i++)
153       {
154         if (seqhash.get(headers.elementAt(i)) != null)
155         {
156           if (maxLength < seqhash.get(headers.elementAt(i)).toString()
157                   .length())
158           {
159             maxLength = seqhash.get(headers.elementAt(i)).toString()
160                     .length();
161           }
162
163           Sequence newSeq = parseId(headers.elementAt(i).toString());
164           newSeq.setSequence(
165                   seqhash.get(headers.elementAt(i).toString()).toString());
166
167           seqs.addElement(newSeq);
168         }
169         else
170         {
171           jalview.bin.Console.errPrintln(
172                   "Clustal File Reader: Can't find sequence for "
173                           + headers.elementAt(i));
174         }
175       }
176       AlignmentAnnotation lastssa = null;
177       if (pssecstr.length() == maxLength)
178       {
179         Vector<AlignmentAnnotation> ss = new Vector<>();
180         AlignmentAnnotation ssa = lastssa = StockholmFile
181                 .parseAnnotationRow(ss, "secondary structure",
182                         pssecstr.toString());
183         ssa.label = "Secondary Structure";
184         annotations.addElement(ssa);
185       }
186       if (consstr.length() == maxLength)
187       {
188         Vector<AlignmentAnnotation> ss = new Vector<>();
189         AlignmentAnnotation ssa = StockholmFile.parseAnnotationRow(ss,
190                 "secondary structure", consstr.toString());
191         ssa.label = "Consensus Secondary Structure";
192         if (lastssa == null || !lastssa.getRNAStruc()
193                 .equals(ssa.getRNAStruc().replace('-', '.')))
194         {
195           annotations.addElement(ssa);
196         }
197       }
198     }
199   }
200
201   @Override
202   public String print(SequenceI[] s, boolean jvsuffix)
203   {
204     StringBuffer out = new StringBuffer("CLUSTAL" + newline + newline);
205
206     int max = 0;
207     int maxid = 0;
208
209     int i = 0;
210
211     while ((i < s.length) && (s[i] != null))
212     {
213       String tmp = printId(s[i], jvsuffix);
214
215       max = Math.max(max, s[i].getLength());
216
217       if (tmp.length() > maxid)
218       {
219         maxid = tmp.length();
220       }
221
222       i++;
223     }
224
225     if (maxid < 15)
226     {
227       maxid = 15;
228     }
229
230     maxid++;
231
232     int len = 60;
233     int nochunks = (max / len) + (max % len > 0 ? 1 : 0);
234
235     for (i = 0; i < nochunks; i++)
236     {
237       int j = 0;
238
239       while ((j < s.length) && (s[j] != null))
240       {
241         out.append(new Format("%-" + maxid + "s")
242                 .form(printId(s[j], jvsuffix) + " "));
243
244         int chunkStart = i * len;
245         int chunkEnd = chunkStart + len;
246
247         int length = s[j].getLength();
248         if ((chunkEnd < length) && (chunkStart < length))
249         {
250           out.append(s[j].getSequenceAsString(chunkStart, chunkEnd));
251         }
252         else
253         {
254           if (chunkStart < length)
255           {
256             out.append(s[j].getSequenceAsString().substring(chunkStart));
257           }
258         }
259
260         out.append(newline);
261         j++;
262       }
263
264       out.append(newline);
265     }
266
267     return out.toString();
268   }
269 }