moved to: https://sites.google.com/site/cmzmasek/home/software/forester
[jalview.git] / forester / java / src / org / forester / io / parsers / SymmetricalDistanceMatrixParser.java
1 // $Id:
2 // Exp $
3 // FORESTER -- software libraries and applications
4 // for evolutionary biology research and applications.
5 //
6 // Copyright (C) 2008-2009 Christian M. Zmasek
7 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
8 // All rights reserved
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 //
24 // Contact: phylosoft @ gmail . com
25 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
26
27 package org.forester.io.parsers;
28
29 import java.io.IOException;
30 import java.util.List;
31
32 import org.forester.evoinference.matrix.distance.BasicSymmetricalDistanceMatrix;
33 import org.forester.evoinference.matrix.distance.DistanceMatrix;
34 import org.forester.util.BasicTable;
35 import org.forester.util.BasicTableParser;
36 import org.forester.util.ForesterUtil;
37
38 /*
39  * This can read full, lower triangular, and upper triangular distance matrices.
40  * In the case of a full matrix, the lower triangular values are used. Format
41  * (by example): id1 0 id2 0.3 0 id3 0.4 0.4 0
42  * 
43  * OR
44  * 
45  * id1 id2 0.3 id3 0.4 0.4
46  * 
47  * Numbers before are after the data are ignored.
48  * 
49  * 
50  * 
51  * 
52  * @author Christian M Zmasek
53  */
54 public class SymmetricalDistanceMatrixParser {
55
56     private final static InputMatrixType INPUT_MATRIX_TYPE_DEFAULT = InputMatrixType.LOWER_TRIANGLE;
57     private final static String          COMMENT                   = "#";
58     private final static String          VALUE_SEPARATOR           = " ";
59     private int                          _matrix_size;
60     private InputMatrixType              _input_matrix_type;
61
62     private SymmetricalDistanceMatrixParser() {
63         init();
64     }
65
66     private void checkValueIsZero( final BasicTable<String> table, final int row, final int i, final int start_row )
67             throws IOException {
68         double d = 0.0;
69         final String table_value = table.getValue( i, row + start_row );
70         if ( ForesterUtil.isEmpty( table_value ) ) {
71             throw new IOException( "value is null or empty at [" + ( i - 1 ) + ", " + row + "]" );
72         }
73         try {
74             d = Double.parseDouble( table_value );
75         }
76         catch ( final NumberFormatException e ) {
77             throw new IOException( "illegal format for distance [" + table_value + "] at [" + ( i - 1 ) + ", " + row
78                     + "]" );
79         }
80         if ( !ForesterUtil.isEqual( 0.0, d ) ) {
81             throw new IOException( "attempt to use non-zero diagonal value [" + table_value + "] at [" + ( i - 1 )
82                     + ", " + row + "]" );
83         }
84     }
85
86     private InputMatrixType getInputMatrixType() {
87         return _input_matrix_type;
88     }
89
90     private int getMatrixSize() {
91         return _matrix_size;
92     }
93
94     private void init() {
95         setInputMatrixType( INPUT_MATRIX_TYPE_DEFAULT );
96         reset();
97     }
98
99     public DistanceMatrix[] parse( final Object source ) throws IOException {
100         reset();
101         final List<BasicTable<String>> tables = BasicTableParser.parse( source,
102                                                                         VALUE_SEPARATOR,
103                                                                         false,
104                                                                         false,
105                                                                         COMMENT,
106                                                                         true );
107         final DistanceMatrix[] distance_matrices = new DistanceMatrix[ tables.size() ];
108         int i = 0;
109         for( final BasicTable<String> table : tables ) {
110             distance_matrices[ i++ ] = transform( table );
111         }
112         return distance_matrices;
113     }
114
115     private void reset() {
116         setMatrixSize( -1 );
117     }
118
119     public void setInputMatrixType( final InputMatrixType input_matrix_type ) {
120         _input_matrix_type = input_matrix_type;
121     }
122
123     private void setMatrixSize( final int matrix_size ) {
124         _matrix_size = matrix_size;
125     }
126
127     private void transferValue( final BasicTable<String> table,
128                                 final DistanceMatrix distance_matrix,
129                                 final int row,
130                                 final int col,
131                                 final int start_row,
132                                 final int col_offset ) throws IOException {
133         double d = 0.0;
134         final String table_value = table.getValue( col, row + start_row );
135         if ( ForesterUtil.isEmpty( table_value ) ) {
136             throw new IOException( "value is null or empty at [" + ( col - 1 ) + ", " + row + "]" );
137         }
138         try {
139             d = Double.parseDouble( table_value );
140         }
141         catch ( final NumberFormatException e ) {
142             throw new IOException( "illegal format for distance [" + table_value + "] at [" + ( col - 1 ) + ", " + row
143                     + "]" );
144         }
145         distance_matrix.setValue( ( col - 1 ) + col_offset, row, d );
146     }
147
148     private DistanceMatrix transform( final BasicTable<String> table ) throws IllegalArgumentException, IOException {
149         boolean first_line_is_size = false;
150         if ( table.getNumberOfColumns() < 3 ) {
151             throw new IllegalArgumentException( "attempt to create distance matrix with with less than 3 columns [columns: "
152                     + table.getNumberOfColumns() + ", rows: " + table.getNumberOfRows() + "]" );
153         }
154         if ( table.getNumberOfColumns() == table.getNumberOfRows() ) {
155             first_line_is_size = true;
156         }
157         else if ( table.getNumberOfColumns() != ( table.getNumberOfRows() + 1 ) ) {
158             throw new IllegalArgumentException( "attempt to create distance matrix with illegal dimensions [columns: "
159                     + table.getNumberOfColumns() + ", rows: " + table.getNumberOfRows() + "]" );
160         }
161         final DistanceMatrix distance_matrix = new BasicSymmetricalDistanceMatrix( table.getNumberOfColumns() - 1 );
162         int start_row = 0;
163         if ( first_line_is_size ) {
164             start_row = 1;
165         }
166         for( int row = 0; row < ( table.getNumberOfRows() - start_row ); row++ ) {
167             distance_matrix.setIdentifier( row, table.getValue( 0, row + start_row ) );
168             switch ( getInputMatrixType() ) {
169                 case LOWER_TRIANGLE:
170                     for( int col = 1; col <= row; ++col ) {
171                         transferValue( table, distance_matrix, row, col, start_row, 0 );
172                     }
173                     checkValueIsZero( table, row, row + 1, start_row );
174                     break;
175                 case UPPER_TRIANGLE:
176                     for( int col = 1; col < ( table.getNumberOfColumns() - row ); ++col ) {
177                         transferValue( table, distance_matrix, row, col, start_row, row );
178                     }
179                     break;
180                 default:
181                     throw new AssertionError( "unkwnown input matrix type [" + getInputMatrixType() + "]" );
182             }
183         }
184         if ( getMatrixSize() < 1 ) {
185             setMatrixSize( distance_matrix.getSize() );
186         }
187         else if ( getMatrixSize() != distance_matrix.getSize() ) {
188             throw new IOException( "attempt to use matrices of unequal size: [" + getMatrixSize() + "] vs ["
189                     + distance_matrix.getSize() + "]" );
190         }
191         return distance_matrix;
192     }
193
194     public static SymmetricalDistanceMatrixParser createInstance() {
195         return new SymmetricalDistanceMatrixParser();
196     }
197
198     public enum InputMatrixType {
199         UPPER_TRIANGLE, LOWER_TRIANGLE
200     }
201 }