in progress....
[jalview.git] / forester / java / src / org / forester / evoinference / matrix / character / BasicCharacterStateMatrix.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.evoinference.matrix.character;
27
28 import java.io.IOException;
29 import java.io.StringWriter;
30 import java.io.Writer;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.forester.io.parsers.nexus.NexusConstants;
36 import org.forester.util.ForesterUtil;
37 import org.forester.util.IllegalFormatUseException;
38
39 public class BasicCharacterStateMatrix<S> implements CharacterStateMatrix<S> {
40
41     final Object[][]           _states;
42     final String[]             _identifiers;
43     final String[]             _characters;
44     final Map<String, Integer> _identifier_index_map;
45     final Map<String, Integer> _character_index_map;
46
47     public BasicCharacterStateMatrix( final int number_of_identifiers, final int number_of_characters ) {
48         _states = new Object[ number_of_identifiers ][ number_of_characters ];
49         _identifiers = new String[ number_of_identifiers ];
50         _characters = new String[ number_of_characters ];
51         _identifier_index_map = new HashMap<String, Integer>( number_of_identifiers );
52         _character_index_map = new HashMap<String, Integer>( number_of_characters );
53     }
54
55     public BasicCharacterStateMatrix( final int number_of_identifiers,
56                                       final int number_of_characters,
57                                       final S default_state ) {
58         this( number_of_identifiers, number_of_identifiers );
59         for( int identifier = 0; identifier < number_of_identifiers; ++identifier ) {
60             for( int character = 0; character < number_of_characters; ++character ) {
61                 setState( identifier, character, default_state );
62             }
63         }
64     }
65
66     public BasicCharacterStateMatrix( final List<List<S>> states ) {
67         if ( ( states == null ) || ( states.size() < 1 ) || ( states.get( 0 ) == null ) ) {
68             throw new IllegalArgumentException( "attempt to create character state matrix from empty list" );
69         }
70         final int number_of_characters = states.get( 0 ).size();
71         final int number_of_identifiers = states.size();
72         _states = new Object[ number_of_identifiers ][ number_of_characters ];
73         _identifiers = new String[ number_of_identifiers ];
74         _characters = new String[ number_of_characters ];
75         _identifier_index_map = new HashMap<String, Integer>( number_of_identifiers );
76         _character_index_map = new HashMap<String, Integer>( number_of_characters );
77         for( int identifier = 0; identifier < number_of_identifiers; ++identifier ) {
78             for( int character = 0; character < number_of_characters; ++character ) {
79                 setState( identifier, character, states.get( identifier ).get( character ) );
80             }
81         }
82     }
83
84     public BasicCharacterStateMatrix( final S[][] states ) {
85         this( states.length, states[ 0 ].length );
86         for( int identifier = 0; identifier < states.length; ++identifier ) {
87             for( int character = 0; character < states[ 0 ].length; ++character ) {
88                 setState( identifier, character, states[ identifier ][ character ] );
89             }
90         }
91     }
92
93     @Override
94     public boolean containsCharacter( final String character ) {
95         return _character_index_map.containsKey( character );
96     }
97
98     @Override
99     public boolean containsIdentifier( final String identifier ) {
100         return _identifier_index_map.containsKey( identifier );
101     }
102
103     @Override
104     public CharacterStateMatrix<S> copy() {
105         final CharacterStateMatrix<S> new_matrix = new BasicCharacterStateMatrix<S>( getNumberOfIdentifiers(),
106                 getNumberOfCharacters() );
107         for( int character = 0; character < getNumberOfCharacters(); ++character ) {
108             if ( getCharacter( character ) != null ) {
109                 new_matrix.setCharacter( character, getCharacter( character ) );
110             }
111         }
112         for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
113             if ( getIdentifier( identifier ) != null ) {
114                 new_matrix.setIdentifier( identifier, getIdentifier( identifier ) );
115             }
116             for( int character = 0; character < getNumberOfCharacters(); ++character ) {
117                 new_matrix.setState( identifier, character, getState( identifier, character ) );
118             }
119         }
120         return new_matrix;
121     }
122
123     @Override
124     @SuppressWarnings("unchecked")
125     public boolean equals( final Object o ) {
126         if ( this == o ) {
127             return true;
128         }
129         else if ( o == null ) {
130             throw new IllegalArgumentException( "attempt to check character state matrix equality to null" );
131         }
132         else if ( o.getClass() != this.getClass() ) {
133             throw new IllegalArgumentException( "attempt to check character state matrix to " + o + " [" + o.getClass()
134                                                 + "]" );
135         }
136         else {
137             final CharacterStateMatrix<S> other = ( CharacterStateMatrix<S> ) o;
138             if ( ( getNumberOfIdentifiers() != other.getNumberOfIdentifiers() )
139                     || ( getNumberOfCharacters() != other.getNumberOfCharacters() ) ) {
140             }
141             for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
142                 for( int character = 0; character < getNumberOfCharacters(); ++character ) {
143                     final S s = getState( identifier, character );
144                     final S os = other.getState( identifier, character );
145                     if ( s == os ) {
146                         continue;
147                     }
148                     else if ( ( s == null ) && ( os != null ) ) {
149                         return false;
150                     }
151                     else if ( ( s != null ) && ( os == null ) ) {
152                         return false;
153                     }
154                     else if ( !s.equals( other.getState( identifier, character ) ) ) {
155                         return false;
156                     }
157                 }
158             }
159             return true;
160         }
161     }
162
163     @Override
164     public String getCharacter( final int character_index ) {
165         return _characters[ character_index ];
166     }
167
168     @Override
169     public int getCharacterIndex( final String character ) {
170         if ( !_character_index_map.containsKey( character ) ) {
171             throw new IllegalArgumentException( "character [" + character + "] not found" );
172         }
173         return _character_index_map.get( character );
174     }
175
176     @Override
177     public String getIdentifier( final int identifier_index ) {
178         return _identifiers[ identifier_index ];
179     }
180
181     @Override
182     public int getIdentifierIndex( final String identifier ) {
183         if ( !_identifier_index_map.containsKey( identifier ) ) {
184             throw new IllegalArgumentException( "indentifier [" + identifier + "] not found" );
185         }
186         return _identifier_index_map.get( identifier );
187     }
188
189     private int getLengthOfLongestState() {
190         int longest = 0;
191         for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
192             for( int character = 0; character < getNumberOfCharacters(); ++character ) {
193                 final S s = getState( identifier, character );
194                 if ( s != null ) {
195                     final int l = getState( identifier, character ).toString().length();
196                     if ( l > longest ) {
197                         longest = l;
198                     }
199                 }
200             }
201         }
202         return longest;
203     }
204
205     @Override
206     public int getNumberOfCharacters() {
207         if ( !isEmpty() ) {
208             return _states[ 0 ].length;
209         }
210         else {
211             return 0;
212         }
213     }
214
215     @Override
216     public int getNumberOfIdentifiers() {
217         return _states.length;
218     }
219
220     @Override
221     @SuppressWarnings("unchecked")
222     public S getState( final int identifier_index, final int character_index ) {
223         return ( S ) _states[ identifier_index ][ character_index ];
224     }
225
226     @Override
227     public S getState( final String identifier, final int character_index ) {
228         if ( !containsIdentifier( identifier ) ) {
229             throw new IllegalArgumentException( "identifier [" + identifier + "] not found" );
230         }
231         return getState( _identifier_index_map.get( identifier ), character_index );
232     }
233
234     @Override
235     public S getState( final String identifier, final String character ) {
236         if ( !containsIdentifier( identifier ) ) {
237             throw new IllegalArgumentException( "identifier [" + identifier + "] not found" );
238         }
239         if ( !containsCharacter( character ) ) {
240             throw new IllegalArgumentException( "character [" + character + "] not found" );
241         }
242         return getState( _identifier_index_map.get( identifier ), _character_index_map.get( character ) );
243     }
244
245     @Override
246     public boolean isEmpty() {
247         return getNumberOfIdentifiers() <= 0;
248     }
249
250     @Override
251     public CharacterStateMatrix<S> pivot() {
252         final CharacterStateMatrix<S> new_matrix = new BasicCharacterStateMatrix<S>( getNumberOfCharacters(),
253                 getNumberOfIdentifiers() );
254         for( int character = 0; character < getNumberOfCharacters(); ++character ) {
255             if ( getCharacter( character ) != null ) {
256                 new_matrix.setIdentifier( character, getCharacter( character ) );
257             }
258         }
259         for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
260             if ( getIdentifier( identifier ) != null ) {
261                 new_matrix.setCharacter( identifier, getIdentifier( identifier ) );
262             }
263             for( int character = 0; character < getNumberOfCharacters(); ++character ) {
264                 new_matrix.setState( character, identifier, getState( identifier, character ) );
265             }
266         }
267         return new_matrix;
268     }
269
270     @Override
271     public void setCharacter( final int character_index, final String character ) {
272         if ( character == null ) {
273             throw new IllegalArgumentException( "attempt to use null character" );
274         }
275         _characters[ character_index ] = character;
276         if ( _character_index_map.containsKey( character ) ) {
277             throw new IllegalArgumentException( "character [" + character + "] is not unique" );
278         }
279         _character_index_map.put( character, character_index );
280     }
281
282     @Override
283     public void setIdentifier( final int identifier_index, final String identifier ) {
284         if ( identifier == null ) {
285             throw new IllegalArgumentException( "attempt to use null identifier" );
286         }
287         _identifiers[ identifier_index ] = identifier;
288         if ( _identifier_index_map.containsKey( identifier ) ) {
289             throw new IllegalArgumentException( "identifier [" + identifier + "] is not unique" );
290         }
291         _identifier_index_map.put( identifier, identifier_index );
292     }
293
294     @Override
295     public void setState( final int identifier_index, final int character_index, final S state ) {
296         _states[ identifier_index ][ character_index ] = state;
297     }
298
299     @Override
300     public void setState( final String identifier, final int character_index, final S state ) {
301         if ( !_identifier_index_map.containsKey( identifier ) ) {
302             throw new IllegalArgumentException( "identifier [" + identifier + "] not found" );
303         }
304         setState( _identifier_index_map.get( identifier ), character_index, state );
305     }
306
307     @Override
308     public void setState( final String identifier, final String character, final S state ) {
309         if ( !containsIdentifier( identifier ) ) {
310             throw new IllegalArgumentException( "identifier [" + identifier + "] not found" );
311         }
312         if ( !containsCharacter( character ) ) {
313             throw new IllegalArgumentException( "character [" + character + "] not found" );
314         }
315         setState( _identifier_index_map.get( identifier ), _character_index_map.get( character ), state );
316     }
317     
318     
319     public String toString() {
320         StringWriter w = new StringWriter();
321         try {
322             toForester( w );
323         }
324         catch ( IOException e ) {
325             // TODO Auto-generated catch block
326             e.printStackTrace();
327         }
328         return w.toString();
329     }
330
331     private void toForester( final Writer writer ) throws IOException {
332         final int longest = getLengthOfLongestState() + 5;
333         writer.write( "Identifiers: " );
334         writer.write( String.valueOf( getNumberOfIdentifiers() ) );
335         writer.write( ForesterUtil.LINE_SEPARATOR );
336         writer.write( "Characters : " );
337         writer.write( String.valueOf( getNumberOfCharacters() ) );
338         writer.write( ForesterUtil.LINE_SEPARATOR );
339         writer.write( ForesterUtil.pad( "", 20, ' ', false ).toString() );
340         writer.write( ' ' );
341         for( int character = 0; character < getNumberOfCharacters(); ++character ) {
342             final String c = getCharacter( character );
343             writer.write( c != null ? ForesterUtil.pad( c, longest, ' ', false ).toString() : ForesterUtil
344                     .pad( "", longest, ' ', false ).toString() );
345             if ( character < ( getNumberOfCharacters() - 1 ) ) {
346                 writer.write( ' ' );
347             }
348         }
349         writer.write( ForesterUtil.LINE_SEPARATOR );
350         for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
351             if ( getIdentifier( identifier ) != null ) {
352                 writer.write( ForesterUtil.pad( getIdentifier( identifier ), 20, ' ', false ).toString() );
353                 writer.write( ' ' );
354             }
355             for( int character = 0; character < getNumberOfCharacters(); ++character ) {
356                 final S state = getState( identifier, character );
357                 writer.write( state != null ? ForesterUtil.pad( state.toString(), longest, ' ', false ).toString()
358                         : ForesterUtil.pad( "", longest, ' ', false ).toString() );
359                 if ( character < ( getNumberOfCharacters() - 1 ) ) {
360                     writer.write( ' ' );
361                 }
362             }
363             if ( identifier < ( getNumberOfIdentifiers() - 1 ) ) {
364                 writer.write( ForesterUtil.LINE_SEPARATOR );
365             }
366         }
367     }
368
369     private void toNexus( final Writer writer ) throws IOException {
370         if ( isEmpty() ) {
371             return;
372         }
373         writer.write( NexusConstants.NEXUS );
374         writer.write( ForesterUtil.LINE_SEPARATOR );
375         writeNexusTaxaBlock( writer );
376         writeNexusBinaryChractersBlock( writer );
377     }
378
379     private void toPhylip( final Writer writer ) throws IOException {
380         final int pad = 6;
381         writer.write( ' ' );
382         writer.write( ' ' );
383         writer.write( ' ' );
384         writer.write( ' ' );
385         writer.write( getNumberOfIdentifiers() );
386         writer.write( ' ' );
387         writer.write( getNumberOfCharacters() );
388         writer.write( ForesterUtil.LINE_SEPARATOR );
389         for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
390             if ( !ForesterUtil.isEmpty( getIdentifier( identifier ) ) ) {
391                 writer.write( ForesterUtil.pad( getIdentifier( identifier ), pad, ' ', false ).toString() );
392                 writer.write( ' ' );
393                 writer.write( ' ' );
394             }
395             else {
396                 throw new IllegalFormatUseException( "Phylip format does not allow empty identifiers" );
397             }
398             writer.write( "" );
399             for( int character = 0; character < getNumberOfCharacters(); ++character ) {
400                 final String state = getState( identifier, character ).toString();
401                 writer.write( state != null ? ForesterUtil.pad( state, pad, ' ', false ).toString() : ForesterUtil
402                         .pad( "", pad, ' ', false ).toString() );
403                 if ( character < ( getNumberOfCharacters() - 1 ) ) {
404                     writer.write( ' ' );
405                     writer.write( ' ' );
406                 }
407             }
408             if ( identifier < ( getNumberOfIdentifiers() - 1 ) ) {
409                 writer.write( ForesterUtil.LINE_SEPARATOR );
410             }
411         }
412     }
413
414    
415     @Override
416     public void toWriter( final Writer writer ) throws IOException {
417         toForester( writer );
418     }
419
420     @Override
421     public void toWriter( final Writer writer, final Format format ) throws IOException {
422         switch ( format ) {
423             case PHYLIP:
424                 toPhylip( writer );
425                 break;
426             case FORESTER:
427                 toForester( writer );
428                 break;
429             case NEXUS_BINARY:
430                 toNexus( writer );
431                 break;
432             default:
433                 throw new IllegalArgumentException( "Unknown format:" + format );
434         }
435     }
436
437     public void writeNexusBinaryChractersBlock( final Writer w ) throws IOException {
438         //BEGIN CHARACTERS;
439         // DIMENSIONS NCHAR=x;
440         //BEGIN CHARSTATELABELS
441         // 1 bcl,
442         // 2 tir,
443         //END;
444         // FORMAT DATATYPE=STANDARD SYMBOLS=;
445         // MATRIX
446         //  fish d d f
447         //  frog s d f f
448         //  snake x x x x;
449         // END;
450         w.write( NexusConstants.BEGIN_CHARACTERS );
451         w.write( ForesterUtil.LINE_SEPARATOR );
452         w.write( " " );
453         w.write( NexusConstants.DIMENSIONS );
454         w.write( " " );
455         w.write( NexusConstants.NCHAR );
456         w.write( "=" );
457         w.write( String.valueOf( getNumberOfCharacters() ) );
458         w.write( ";" );
459         w.write( ForesterUtil.LINE_SEPARATOR );
460         writeNexusCharstatelabels( w );
461         w.write( " " );
462         w.write( NexusConstants.FORMAT );
463         w.write( " " );
464         w.write( NexusConstants.DATATYPE );
465         w.write( "=" );
466         w.write( NexusConstants.STANDARD );
467         w.write( " " );
468         w.write( NexusConstants.SYMBOLS );
469         w.write( "=\"" );
470         w.write( String.valueOf( BinaryStates.ABSENT ) );
471         w.write( String.valueOf( BinaryStates.PRESENT ) );
472         w.write( "\";" );
473         w.write( ForesterUtil.LINE_SEPARATOR );
474         writeNexusMatrix( w );
475         w.write( ForesterUtil.LINE_SEPARATOR );
476         w.write( NexusConstants.END );
477         w.write( ForesterUtil.LINE_SEPARATOR );
478     }
479
480     public void writeNexusCharstatelabels( final Writer w ) throws IOException {
481         w.write( " " );
482         w.write( NexusConstants.CHARSTATELABELS );
483         w.write( ForesterUtil.LINE_SEPARATOR );
484         for( int i = 0; i < getNumberOfCharacters(); ++i ) {
485             w.write( "  " + ( i + 1 ) + " '" );
486             w.write( getCharacter( i ) );
487             w.write( "'" );
488             if ( i < ( getNumberOfCharacters() - 1 ) ) {
489                 w.write( "," );
490                 w.write( ForesterUtil.LINE_SEPARATOR );
491             }
492         }
493         w.write( ";" );
494         w.write( ForesterUtil.LINE_SEPARATOR );
495     }
496
497     public void writeNexusMatrix( final Writer w ) throws IOException {
498         w.write( " " );
499         w.write( NexusConstants.MATRIX );
500         w.write( ForesterUtil.LINE_SEPARATOR );
501         for( int identifier = 0; identifier < getNumberOfIdentifiers(); ++identifier ) {
502             if ( getIdentifier( identifier ) != null ) {
503                 w.write( "  " );
504                 w.write( ForesterUtil.pad( getIdentifier( identifier ), 20, ' ', false ).toString() );
505                 w.write( ' ' );
506             }
507             for( int character = 0; character < getNumberOfCharacters(); ++character ) {
508                 final S state = getState( identifier, character );
509                 if ( state == null ) {
510                     throw new IllegalFormatUseException( "character state matrix cannot contain null if to be represented in nexus format" );
511                 }
512                 if ( !( state instanceof BinaryStates ) ) {
513                     throw new IllegalFormatUseException( "nexus format representation expects binary character data - got ["
514                             + getState( 0, 0 ).getClass() + "] instead" );
515                 }
516                 if ( state == BinaryStates.UNKNOWN ) {
517                     throw new IllegalFormatUseException( "character state matrix cannot contain unknown states if to be represented in nexus format" );
518                 }
519                 w.write( state.toString() );
520             }
521             if ( identifier < ( getNumberOfIdentifiers() - 1 ) ) {
522                 w.write( ForesterUtil.LINE_SEPARATOR );
523             }
524         }
525         w.write( ";" );
526     }
527
528     public void writeNexusTaxaBlock( final Writer w ) throws IOException {
529         //BEGIN TAXA;
530         // DIMENSIONS NTAX=n;
531         // TAXLABELS fish frog snake;
532         //END;
533         w.write( NexusConstants.BEGIN_TAXA );
534         w.write( ForesterUtil.LINE_SEPARATOR );
535         w.write( " " );
536         w.write( NexusConstants.DIMENSIONS );
537         w.write( " " );
538         w.write( NexusConstants.NTAX );
539         w.write( "=" );
540         w.write( String.valueOf( getNumberOfIdentifiers() ) );
541         w.write( ";" );
542         w.write( ForesterUtil.LINE_SEPARATOR );
543         w.write( " " );
544         w.write( NexusConstants.TAXLABELS );
545         for( int i = 0; i < getNumberOfIdentifiers(); ++i ) {
546             w.write( " " );
547             w.write( getIdentifier( i ) );
548         }
549         w.write( ";" );
550         w.write( ForesterUtil.LINE_SEPARATOR );
551         w.write( NexusConstants.END );
552         w.write( ForesterUtil.LINE_SEPARATOR );
553     }
554 }