4b7e9209400a631fc79f9edd377f49aecee4b4d9
[jalview.git] / forester / java / src / org / forester / io / parsers / GeneralMsaParser.java
1 // $Id:
2 //
3 // forester -- software libraries and applications
4 // for genomics and evolutionary biology research.
5 //
6 // Copyright (C) 2010 Christian M Zmasek
7 // Copyright (C) 2010 Sanford-Burnham Medical Research Institute
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: www.phylosoft.org/forester
26
27 package org.forester.io.parsers;
28
29 import java.io.BufferedReader;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40 import org.forester.msa.BasicMsa;
41 import org.forester.msa.Msa;
42 import org.forester.msa.MsaFormatException;
43 import org.forester.sequence.BasicSequence;
44 import org.forester.sequence.Sequence;
45
46 public final class GeneralMsaParser {
47
48     private static final Pattern NAME_SEQ_PATTERN          = Pattern.compile( "(\\S+)\\s+(\\S+)\\s*" );
49     private static final Pattern INDENTED_SEQ_PATTERN      = Pattern.compile( "\\s+(\\S+)\\s*" );
50     private static final Pattern NON_INDENTED_SEQ_PATTERN  = Pattern.compile( "(\\S+).*" );
51     private static final Pattern PROBCONS_REGEX            = Pattern.compile( "^CLUSTAL\\s" );
52     private static final Pattern MUSCLE_REGEX              = Pattern.compile( "^MUSCLE\\s\\(" );
53     private static final Pattern CLUSTAL_REGEX             = Pattern.compile( "^PROBCONS\\s" );
54     private static final Pattern ANYTHING_REGEX            = Pattern.compile( "[\\d\\s]+" );
55     private static final Pattern SELEX_SPECIAL_LINES_REGEX = Pattern.compile( "\\s+[*\\.:\\s]+" );
56     private static final Pattern SPECIAL_LINES_REGEX       = Pattern.compile( "^\\s*(#|%|//|!!)" );
57     private static final Pattern ERROR_REGEX               = Pattern.compile( "\\S+\\s+\\S+\\s+\\S+" );
58
59     static private boolean canIgnore( final String line ) {
60         if ( ( line.length() < 1 ) || ANYTHING_REGEX.matcher( line ).matches() ) {
61             return true;
62         }
63         return ( SELEX_SPECIAL_LINES_REGEX.matcher( line ).matches() || SPECIAL_LINES_REGEX.matcher( line ).lookingAt() );
64     }
65
66     static private boolean isProgramNameLine( final String line ) {
67         return ( PROBCONS_REGEX.matcher( line ).lookingAt() || CLUSTAL_REGEX.matcher( line ).lookingAt() || MUSCLE_REGEX
68                 .matcher( line ).lookingAt() );
69     }
70
71     static public Msa parse( final InputStream is ) throws IOException {
72         int block = -1;
73         int current_seq_index_per_block = -1;
74         String current_name = null;
75         boolean saw_ignorable = true;
76         boolean is_first = true;
77         final Map<String, StringBuilder> temp_msa = new HashMap<String, StringBuilder>();
78         final List<String> names_in_order = new ArrayList<String>();
79         final BufferedReader reader = new BufferedReader( new InputStreamReader( is, "UTF-8" ) );
80         String line = null;
81         int line_counter = 0;
82         while ( ( line = reader.readLine() ) != null ) {
83             ++line_counter;
84             if ( canIgnore( line ) ) {
85                 saw_ignorable = true;
86             }
87             else if ( !( is_first && isProgramNameLine( line ) ) ) {
88                 if ( ERROR_REGEX.matcher( line ).lookingAt() ) {
89                     throw new MsaFormatException( "unrecognized msa format (line: " + line_counter + "):\n\""
90                             + trim( line ) + "\"" );
91                 }
92                 if ( canIgnore( line ) ) {
93                     saw_ignorable = true;
94                 }
95                 final Matcher name_seq_m = NAME_SEQ_PATTERN.matcher( line );
96                 Matcher ind_seq_m = null;
97                 Matcher non_ind_seq_m = null;
98                 boolean ind_seq_m_matches = false;
99                 boolean non_ind_seq_m_matches = false;
100                 final boolean name_seq_m_matches = name_seq_m.matches();
101                 if ( !name_seq_m_matches ) {
102                     ind_seq_m = INDENTED_SEQ_PATTERN.matcher( line );
103                     ind_seq_m_matches = ind_seq_m.matches();
104                     if ( !ind_seq_m_matches ) {
105                         non_ind_seq_m = NON_INDENTED_SEQ_PATTERN.matcher( line );
106                         non_ind_seq_m_matches = non_ind_seq_m.lookingAt();
107                     }
108                 }
109                 if ( name_seq_m_matches || ind_seq_m_matches || non_ind_seq_m_matches ) {
110                     if ( saw_ignorable ) {
111                         ++block;
112                         current_seq_index_per_block = -1;
113                         saw_ignorable = false;
114                     }
115                     ++current_seq_index_per_block;
116                     if ( name_seq_m_matches ) {
117                         final String name = name_seq_m.group( 1 );
118                         final String seq = name_seq_m.group( 2 );
119                         if ( temp_msa.containsKey( name ) ) {
120                             temp_msa.get( name ).append( seq );
121                         }
122                         else {
123                             temp_msa.put( name, new StringBuilder( seq ) );
124                             names_in_order.add( name );
125                         }
126                         current_name = name;
127                     }
128                     else if ( ind_seq_m_matches ) {
129                         if ( temp_msa.containsKey( current_name ) ) {
130                             temp_msa.get( current_name ).append( ind_seq_m.group( 1 ) );
131                         }
132                         else {
133                             throw new MsaFormatException( "illegal msa format (line: " + line_counter + "):\n\""
134                                     + trim( line ) + "\"" );
135                         }
136                     }
137                     else if ( non_ind_seq_m_matches ) {
138                         if ( block == 0 ) {
139                             throw new MsaFormatException( "illegal msa format: first block cannot contain un-named sequence (line: "
140                                     + line_counter + "):\n\"" + trim( line ) + "\"" );
141                         }
142                         else {
143                             String name = "";
144                             try {
145                                 name = names_in_order.get( current_seq_index_per_block );
146                             }
147                             catch ( final IndexOutOfBoundsException e ) {
148                                 throw new MsaFormatException( "illegalmsa format (line: " + line_counter + "):\n\""
149                                         + trim( line ) + "\"" );
150                             }
151                             if ( temp_msa.containsKey( name ) ) {
152                                 temp_msa.get( name ).append( non_ind_seq_m.group( 1 ) );
153                             }
154                             else {
155                                 throw new MsaFormatException( "illegal msa format (line: " + line_counter + "):\n\""
156                                         + trim( line ) + "\"" );
157                             }
158                         }
159                         current_name = null;
160                     }
161                 }
162                 else {
163                     throw new MsaFormatException( "illegal msa format (line: " + line_counter + "):\n\"" + trim( line )
164                             + "\"" );
165                 }
166                 if ( is_first ) {
167                     is_first = false;
168                 }
169             }
170         } // while ( ( line = reader.readLine() ) != null )
171         final List<Sequence> seqs = new ArrayList<Sequence>();
172         for( int i = 0; i < names_in_order.size(); ++i ) {
173             seqs.add( BasicSequence.createAaSequence( names_in_order.get( i ), temp_msa.get( names_in_order.get( i ) )
174                     .toString() ) );
175         }
176         final Msa msa = BasicMsa.createInstance( seqs );
177         return msa;
178     }
179
180     private static String trim( final String line ) {
181         if ( line.length() > 100 ) {
182             return line.substring( 0, 100 ) + " ...";
183         }
184         return line;
185     }
186 }