Parsing moved to (new) ScoreMatrixFile, drag and drop to alignment now
[jalview.git] / src / jalview / io / ScoreMatrixFile.java
1 package jalview.io;
2
3 import jalview.analysis.scoremodels.ScoreMatrix;
4 import jalview.analysis.scoremodels.ScoreModels;
5 import jalview.datamodel.SequenceI;
6
7 import java.io.IOException;
8 import java.util.StringTokenizer;
9
10 /**
11  * A class that can parse a file containing a substitution matrix and register
12  * it for use in Jalview
13  * 
14  * @author gmcarstairs
15  *
16  */
17 // TODO modify the AlignFile / IdentifyFile pattern so that non-alignment files
18 // like this are handled more naturally
19 public class ScoreMatrixFile extends AlignFile implements
20         AlignmentFileReaderI
21 {
22   // first non-comment line identifier - also checked in IdentifyFile
23   public static final String SCOREMATRIX = "SCOREMATRIX";
24
25   private static final String DELIMITERS = " ,\t";
26
27   private static final String COMMENT_CHAR = "#";
28
29   private String matrixName;
30
31   /**
32    * Constructor
33    * 
34    * @param source
35    * @throws IOException
36    */
37   public ScoreMatrixFile(FileParse source) throws IOException
38   {
39     super(false, source);
40   }
41
42   @Override
43   public String print(SequenceI[] sqs, boolean jvsuffix)
44   {
45     return null;
46   }
47
48   /**
49    * Parses the score matrix file, and if successful registers the matrix so it
50    * will be shown in Jalview menus.
51    */
52   @Override
53   public void parse() throws IOException
54   {
55     ScoreMatrix sm = parseMatrix();
56
57     ScoreModels.getInstance().registerScoreModel(sm);
58   }
59
60   /**
61    * Parses the score matrix file and constructs a ScoreMatrix object. If an
62    * error is found in parsing, it is thrown as FileFormatException. Any
63    * warnings are written to syserr.
64    * 
65    * @return
66    * @throws IOException
67    */
68   public ScoreMatrix parseMatrix() throws IOException
69   {
70     ScoreMatrix sm = null;
71     int lineNo = 0;
72     String name = null;
73     String alphabet = null;
74     float[][] scores = null;
75     int size = 0;
76     int row = 0;
77     String err = null;
78     String data;
79
80     while ((data = nextLine()) != null)
81     {
82       lineNo++;
83       data = data.trim();
84       if (data.startsWith(COMMENT_CHAR) || data.length() == 0)
85       {
86         continue;
87       }
88       if (data.toUpperCase().startsWith(SCOREMATRIX))
89       {
90         /*
91          * Parse name from ScoreMatrix <name>
92          */
93         if (name != null)
94         {
95           System.err
96                   .println("Warning: 'ScoreMatrix' repeated in file at line "
97                           + lineNo);
98         }
99         StringTokenizer nameLine = new StringTokenizer(data, DELIMITERS);
100         if (nameLine.countTokens() != 2)
101         {
102           err = "Format error: expected 'ScoreMatrix <name>', found '"
103                   + data + "' at line " + lineNo;
104           throw new FileFormatException(err);
105         }
106         nameLine.nextToken();
107         name = nameLine.nextToken();
108         continue;
109       }
110       else if (name == null)
111       {
112         err = "Format error: 'ScoreMatrix <name>' should be the first non-comment line";
113         throw new FileFormatException(err);
114       }
115
116       /*
117        * next line after ScoreMatrix should be the alphabet of scored symbols
118        */
119       if (alphabet == null)
120       {
121         alphabet = data;
122         size = alphabet.length();
123         scores = new float[size][];
124         continue;
125       }
126
127       /*
128        * too much information
129        */
130       if (row >= size)
131       {
132         err = "Unexpected extra input line in score model file: '" + data
133                 + "'";
134         throw new FileFormatException(err);
135       }
136
137       /*
138        * subsequent lines should be the symbol scores
139        * optionally with the symbol as the first column for readability
140        */
141       StringTokenizer scoreLine = new StringTokenizer(data, DELIMITERS);
142       if (scoreLine.countTokens() == size + 1)
143       {
144         /*
145          * check 'guide' symbol is the row'th letter of the alphabet
146          */
147         String symbol = scoreLine.nextToken();
148         if (symbol.length() > 1 || symbol.charAt(0) != alphabet.charAt(row))
149         {
150           err = String
151                   .format("Error parsing score matrix at line %d, expected '%s' but found '%s'",
152                           lineNo, alphabet.charAt(row), symbol);
153           throw new FileFormatException(err);
154         }
155       }
156       if (scoreLine.countTokens() != size)
157       {
158         err = String.format("Expected %d scores at line %d but found %d",
159                 size, lineNo, scoreLine.countTokens());
160         throw new FileFormatException(err);
161       }
162       scores[row] = new float[size];
163       int col = 0;
164       String value = null;
165       while (scoreLine.hasMoreTokens())
166       {
167         try
168         {
169           value = scoreLine.nextToken();
170           scores[row][col] = Float.valueOf(value);
171           col++;
172         } catch (NumberFormatException e)
173         {
174           err = String.format(
175                   "Invalid score value '%s' at line %d column %d", value,
176                   lineNo, col);
177           throw new FileFormatException(err);
178         }
179       }
180       row++;
181     }
182
183     /*
184      * out of data - check we found enough
185      */
186     if (row < size)
187     {
188       err = String
189               .format("Expected %d rows of score data in score matrix but only found %d",
190                       size, row);
191       throw new FileFormatException(err);
192     }
193
194     /*
195      * If we get here, then name, alphabet and scores have been parsed successfully
196      */
197     sm = new ScoreMatrix(name, alphabet.toCharArray(), scores);
198     matrixName = name;
199
200     return sm;
201   }
202
203   public String getMatrixName()
204   {
205     return matrixName;
206   }
207 }