in progress...
[jalview.git] / forester / ruby / evoruby / lib / evo / util / util.rb
1 #
2 # = lib/evo/util/util.rb - Util class
3 #
4 # Copyright::    Copyright (C) 2017 Christian M. Zmasek
5 # License::      GNU Lesser General Public License (LGPL)
6 #
7 # Last modified: 2017/04/27
8
9 require 'lib/evo/util/constants'
10
11 module Evoruby
12   class Util
13     def Util.get_matching_files( files, prefix_pattern, suffix_pattern )
14       matching_files = Array.new
15       files.each { | file |
16         if ( !File.directory?( file ) &&
17         file !~ /^\./ &&
18         file =~ /^#{prefix_pattern}.*#{suffix_pattern}$/ )
19           matching_files << file
20         end
21       }
22       matching_files
23     end
24
25     def Util.get_matching_file( dir_name, prefix, suffix )
26       unless Dir.exist? dir_name
27         raise IOError, "directory [#{dir_name}] does not exist"
28       end
29
30       all_files = Dir.entries(dir_name)
31
32       if ( all_files.size <= 2 )
33         raise IOError, "directory [#{dir_name}] is empty"
34       end
35       matching_files = Array.new
36       all_files.each { | file |
37         if  (( !File.directory?( file )) && (file.end_with? suffix))
38           matching_files << file
39         end
40       }
41
42       if ( matching_files.size == 0 )
43         raise IOError, "no files ending with \"" + suffix + "\" found in [" + dir_name + "]"
44       end
45       my_prefix = prefix
46       done = false
47       more_than_one = false
48       the_one = nil;
49
50       loop do
51         puts my_prefix #TODO remove me
52         matches = 0
53         matching_files.each { | file |
54           if file.start_with?( my_prefix )
55             matches += 1
56             if matches > 1
57               the_one = nil
58               break
59             end
60             the_one = file
61           end
62         }
63         if matches > 1
64           more_than_one = true
65           done = true
66         end
67         if matches == 1
68           done = true
69         else
70           if my_prefix.length <= 1
71             raise IOError, "no file matching \"" + removeFileExtension( prefix )
72             + "\" and ending with \"" + suffix + "\" found in [" + dir_name + "]"
73           end
74           my_prefix = my_prefix[ 0 ... ( my_prefix.length - 1 ) ]
75
76         end
77         break if done
78       end
79
80       if more_than_one
81         raise IOError, "multiple files matching \"" +  prefix  +
82         "\" and ending with \"" + suffix + "\" found in [" + dir_name + "]"
83       elsif the_one != nil
84       else
85         raise IOError, "no file matching \"" + prefix  + "\" and ending with \"" +
86         suffix + "\" found in [" + dir_name + "]"
87       end
88       the_one
89     end
90
91     def Util.normalize_seq_name( name, length, exception_if_too_long = false )
92       if name.length > length
93         if exception_if_too_long
94           error_msg = "sequence name \"#{name}\" is too long (>#{length})"
95           raise StandardError, error_msg
96         end
97         name = name[ 0, length ]
98       elsif name.length < length
99         t = length - name.length
100         t.times do
101           name = name + " "
102         end
103       end
104       name
105     end
106
107     # Returns true if char_code corresponds to: space * - . _
108     def Util.is_aa_gap_character?( char_code )
109       return ( char_code <= 32  || char_code == 42 || char_code == 45 || char_code == 46 ||char_code == 95  )
110     end
111
112     # Deletes *, digits, and whitespace, replaces BJOUZ? with X, and replaces non-(letters, -) with -
113     def Util.clean_seq_str( seq_str )
114       seq_str = seq_str.upcase
115       seq_str = seq_str.gsub( /\s+/, '' )
116       seq_str = seq_str.gsub( /\d+/, '' )
117       seq_str = seq_str.gsub( '*', '' )
118       seq_str = seq_str.gsub( /[BJOUZ?]/, 'X' )
119       seq_str = seq_str.gsub( /[^A-Z\-]/, '-' )
120       seq_str
121     end
122
123     # raises ArgumentError
124     def Util.check_file_for_readability( path )
125       unless ( File.exist?( path ) )
126         error_msg = "file [#{path}] does not exist"
127         raise IOError, error_msg
128       end
129       unless ( File.file?( path ) )
130         error_msg = "file [#{path}] is not a regular file"
131         raise IOError, error_msg
132       end
133       unless ( File.readable?( path ) )
134         error_msg = "file [#{path}] is not a readable file"
135         raise IOError, error_msg
136       end
137       if ( File.zero?( path ) )
138         error_msg = "file [#{path}] is empty"
139         raise IOError, error_msg
140       end
141     end
142
143     # raises ArgumentError
144     def Util.check_file_for_writability( path )
145       if File.directory?( path )
146         error_msg = "file [#{path}] is an existing directory"
147         raise IOError, error_msg
148       elsif File.exist?( path )
149         error_msg = "file [#{path}] already exists"
150         raise IOError, error_msg
151       elsif File.writable?( path )
152         error_msg = "file [#{path}] is not writeable"
153         raise IOError, error_msg
154       end
155     end
156
157     def Util.fatal_error_if_not_writable( prg_name, path )
158       begin
159         Util.check_file_for_writability( path )
160       rescue IOError => e
161         Util.fatal_error( prg_name, e.to_s )
162       end
163     end
164
165     def Util.fatal_error_if_not_readable( prg_name, path )
166       begin
167         Util.check_file_for_readability( path )
168       rescue IOError => e
169         Util.fatal_error( prg_name, e.to_s )
170       end
171     end
172
173     def Util.get_env_variable_value( env_variable )
174       value = ENV[env_variable]
175       if value == nil || value.empty?
176         error_msg = "apparently environment variable #{env_variable} has not been set"
177         raise StandardError, error_msg
178       end
179       value
180     end
181
182     # raises ArgumentError
183     def Util.file2array( path, split_by_semicolon )
184       Util.check_file_for_readability( path )
185       a = Array.new()
186       c = 0
187       File.open( path ) do | file |
188         while line = file.gets
189           if ( line =~ /^\s*(\S.*?)\s*$/ )
190             s = $1
191             if ( split_by_semicolon && s =~/;/ )
192               sa = s.split( /;/ )
193               for i in 0 ... sa.length()
194                 a[ c ] = sa[ i ].strip!
195               end
196             else
197               a[ c ] = s
198             end
199             c += 1
200           end
201         end
202       end
203       return a
204     end
205
206     def Util.print_program_information( prg_name,
207       prg_version,
208       prg_desc,
209       date,
210       www,
211       io = STDOUT )
212
213       ruby_version = RUBY_VERSION
214       l = prg_name.length + prg_version.length + date.length + ruby_version.length + 12
215       io.print( Evoruby::Constants::LINE_DELIMITER )
216       io.print( prg_name + " " + prg_version + " [" + date + "] [ruby " + ruby_version + "]")
217       io.print( Evoruby::Constants::LINE_DELIMITER )
218       l.times {
219         io.print( "_" )
220       }
221       io.print( Constants::LINE_DELIMITER )
222       io.print( Constants::LINE_DELIMITER )
223       io.print( prg_desc )
224       io.print( Constants::LINE_DELIMITER )
225       io.print( Constants::LINE_DELIMITER )
226       io.print( "Website: " + www )
227       io.print( Constants::LINE_DELIMITER )
228       io.print( Constants::LINE_DELIMITER )
229     end
230
231     def Util.fatal_error( prg_name, message, io = STDOUT )
232       io.print( Constants::LINE_DELIMITER )
233       if ( !Util.is_string_empty?( prg_name ) )
234         io.print( "[" + prg_name + "] > " + message )
235       else
236         io.print( " > " + message )
237       end
238       io.print( Constants::LINE_DELIMITER )
239       io.print( Constants::LINE_DELIMITER )
240       exit( -1 )
241     end
242
243     def Util.print_message( prg_name, message, io = STDOUT )
244       if ( !Util.is_string_empty?( prg_name ) )
245         io.print( "[" + prg_name + "] > " + message )
246       else
247         io.print( " > " + message )
248       end
249       io.print( Constants::LINE_DELIMITER )
250     end
251
252     def Util.print_warning_message( prg_name, message, io = STDOUT )
253       if ( !Util.is_string_empty?( prg_name ) )
254         io.print( "[" + prg_name + "] > WARNING: " + message )
255       else
256         io.print( " > " + message )
257       end
258       io.print( Constants::LINE_DELIMITER )
259     end
260
261     def Util.is_string_empty?( s )
262       return ( s == nil || s.length < 1 )
263     end
264
265     # From "Ruby Cookbook"
266     # counts_hash: key is a "name", value is the count (integer)
267     def Util.draw_histogram( counts_hash, char = "#" )
268       pairs = counts_hash.keys.collect { |x| [ x.to_s, counts_hash[ x ] ] }.sort
269       largest_key_size = pairs.max { |x, y| x[ 0 ].size <=> y[ 0 ].size }[ 0 ].size
270       pairs.inject( "" ) do | s, kv |
271         s << "#{ kv[ 0 ].ljust( largest_key_size ) }  | #{ char*kv[ 1 ] }" + Constants::LINE_DELIMITER
272       end
273     end
274
275     def Util.looks_like_fasta?( path )
276       Util.check_file_for_readability( path )
277       File.open( path ) do | file |
278         while line = file.gets
279           if ( line !~ /\S/ || line =~ /^\s*#/ )
280           elsif line =~ /^\s*>\s*(.+)/
281             return true
282           else
283             return false
284           end
285         end
286       end
287       error_msg = "unexpected format"
288       raise IOError, error_msg
289     end
290
291   end # class Util
292
293 end # module Evoruby