Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 GMT
  2. Package jalview.hmmer

File HmmerCommand.java

 

Coverage histogram

../../img/srcFileCovDistChart1.png
57% of files have more coverage

Code metrics

60
136
16
1
539
349
56
0.41
8.5
16
3.5

Classes

Class Line # Actions
HmmerCommand 43 136 56
0.0141509441.4%
 

Contributing tests

This file is covered by 190 tests. .

Source view

1    package jalview.hmmer;
2   
3    import jalview.analysis.SeqsetUtils;
4    import jalview.analysis.SeqsetUtils.SequenceInfo;
5    import jalview.bin.Cache;
6    import jalview.bin.Console;
7    import jalview.datamodel.Alignment;
8    import jalview.datamodel.AlignmentAnnotation;
9    import jalview.datamodel.AlignmentI;
10    import jalview.datamodel.AnnotatedCollectionI;
11    import jalview.datamodel.Annotation;
12    import jalview.datamodel.HiddenMarkovModel;
13    import jalview.datamodel.SequenceGroup;
14    import jalview.datamodel.SequenceI;
15    import jalview.gui.AlignFrame;
16    import jalview.gui.JvOptionPane;
17    import jalview.gui.Preferences;
18    import jalview.io.FastaFile;
19    import jalview.io.HMMFile;
20    import jalview.io.StockholmFile;
21    import jalview.util.FileUtils;
22    import jalview.util.MessageManager;
23    import jalview.util.Platform;
24    import jalview.ws.params.ArgumentI;
25   
26    import java.io.BufferedReader;
27    import java.io.File;
28    import java.io.IOException;
29    import java.io.InputStreamReader;
30    import java.io.PrintWriter;
31    import java.nio.file.Paths;
32    import java.util.ArrayList;
33    import java.util.Hashtable;
34    import java.util.List;
35    import java.util.Map;
36   
37    /**
38    * Base class for hmmbuild, hmmalign and hmmsearch
39    *
40    * @author TZVanaalten
41    *
42    */
 
43    public abstract class HmmerCommand implements Runnable
44    {
45    public static final String HMMBUILD = "hmmbuild";
46   
47    protected final AlignFrame af;
48   
49    protected final AlignmentI alignment;
50   
51    protected final List<ArgumentI> params;
52   
53    /*
54    * constants for i18n lookup of passed parameter names
55    */
56    static final String DATABASE_KEY = "label.database";
57   
58    static final String THIS_ALIGNMENT_KEY = "label.this_alignment";
59   
60    static final String USE_ACCESSIONS_KEY = "label.use_accessions";
61   
62    static final String AUTO_ALIGN_SEQS_KEY = "label.auto_align_seqs";
63   
64    static final String NUMBER_OF_RESULTS_KEY = "label.number_of_results";
65   
66    static final String NUMBER_OF_ITERATIONS = "label.number_of_iterations";
67   
68    static final String TRIM_TERMINI_KEY = "label.trim_termini";
69   
70    static final String RETURN_N_NEW_SEQ = "label.check_for_new_sequences";
71   
72    static final String REPORTING_CUTOFF_KEY = "label.reporting_cutoff";
73   
74    static final String CUTOFF_NONE = "label.default";
75   
76    static final String CUTOFF_SCORE = "label.score";
77   
78    static final String CUTOFF_EVALUE = "label.evalue";
79   
80    static final String REPORTING_SEQ_EVALUE_KEY = "label.reporting_seq_evalue";
81   
82    static final String REPORTING_DOM_EVALUE_KEY = "label.reporting_dom_evalue";
83   
84    static final String REPORTING_SEQ_SCORE_KEY = "label.reporting_seq_score";
85   
86    static final String REPORTING_DOM_SCORE_KEY = "label.reporting_dom_score";
87   
88    static final String INCLUSION_SEQ_EVALUE_KEY = "label.inclusion_seq_evalue";
89   
90    static final String INCLUSION_DOM_EVALUE_KEY = "label.inclusion_dom_evalue";
91   
92    static final String INCLUSION_SEQ_SCORE_KEY = "label.inclusion_seq_score";
93   
94    static final String INCLUSION_DOM_SCORE_KEY = "label.inclusion_dom_score";
95   
96    static final String ARG_TRIM = "--trim";
97   
98    static final String INCLUSION_THRESHOLD_KEY = "label.inclusion_threshold";
99   
100    /**
101    * Constructor
102    *
103    * @param alignFrame
104    * @param args
105    */
 
106  0 toggle public HmmerCommand(AlignFrame alignFrame, List<ArgumentI> args)
107    {
108  0 af = alignFrame;
109  0 alignment = af.getViewport().getAlignment();
110  0 params = args;
111    }
112   
113    /**
114    * Answers true if preference HMMER_PATH is set, and its value is the path to
115    * a directory that contains an executable <code>hmmbuild</code> or
116    * <code>hmmbuild.exe</code>, else false
117    *
118    * @return
119    */
 
120  487 toggle public static boolean isHmmerAvailable()
121    {
122  487 File exec = FileUtils.getExecutable(HMMBUILD,
123    Cache.getProperty(Preferences.HMMER_PATH));
124  487 return exec != null;
125    }
126   
127    /**
128    * Uniquifies the sequences when exporting and stores their details in a
129    * hashtable
130    *
131    * @param seqs
132    */
 
133  0 toggle protected Map<String, SequenceInfo> stashSequences(SequenceI[] seqs)
134    {
135  0 return SeqsetUtils.uniquify(seqs, true);
136    }
137   
138    /**
139    * Restores the sequence data lost by uniquifying
140    *
141    * @param sequencesHash
142    * @param seqs
143    */
 
144  0 toggle protected void recoverSequences(Map<String, SequenceInfo> sequencesHash, SequenceI[] seqs)
145    {
146  0 SeqsetUtils.deuniquify(sequencesHash, seqs);
147    }
148   
149    /**
150    * Runs a command as a separate process and waits for it to complete. Answers
151    * true if the process return status is zero, else false.
152    *
153    * @param commands
154    * the executable command and any arguments to it
155    * @throws IOException
156    */
 
157  0 toggle public boolean runCommand(List<String> commands)
158    throws IOException
159    {
160  0 List<String> args = Platform.isWindowsAndNotJS() ? wrapWithCygwin(commands)
161    : commands;
162   
163  0 try
164    {
165  0 ProcessBuilder pb = new ProcessBuilder(args);
166  0 pb.redirectErrorStream(true); // merge syserr to sysout
167  0 if (Platform.isWindowsAndNotJS())
168    {
169  0 String path = pb.environment().get("Path");
170  0 path = jalview.bin.Cache.getProperty("CYGWIN_PATH") + ";" + path;
171  0 pb.environment().put("Path", path);
172    }
173  0 final Process p = pb.start();
174  0 new Thread(new Runnable()
175    {
 
176  0 toggle @Override
177    public void run()
178    {
179  0 BufferedReader input = new BufferedReader(
180    new InputStreamReader(p.getInputStream()));
181  0 try
182    {
183  0 String line = input.readLine();
184  0 while (line != null)
185    {
186  0 System.out.println(line);
187  0 line = input.readLine();
188    }
189    } catch (IOException e)
190    {
191  0 e.printStackTrace();
192    }
193    }
194    }).start();
195   
196  0 p.waitFor();
197  0 int exitValue = p.exitValue();
198  0 if (exitValue != 0)
199    {
200  0 Console.error("Command failed, return code = " + exitValue);
201  0 Console.error("Command/args were: " + args.toString());
202    }
203  0 return exitValue == 0; // 0 is success, by convention
204    } catch (Exception e)
205    {
206  0 e.printStackTrace();
207  0 return false;
208    }
209    }
210   
211    /**
212    * Converts the given command to a Cygwin "bash" command wrapper. The hmmer
213    * command and any arguments to it are converted into a single parameter to the
214    * bash command.
215    *
216    * @param commands
217    */
 
218  0 toggle protected List<String> wrapWithCygwin(List<String> commands)
219    {
220  0 File bash = FileUtils.getExecutable("bash",
221    Cache.getProperty(Preferences.CYGWIN_PATH));
222  0 if (bash == null)
223    {
224  0 Console.error("Cygwin shell not found");
225  0 return commands;
226    }
227   
228  0 List<String> wrapped = new ArrayList<>();
229    // wrapped.add("C:\Users\tva\run");
230  0 wrapped.add(bash.getAbsolutePath());
231  0 wrapped.add("-c");
232   
233    /*
234    * combine hmmbuild/search/align and arguments to a single string
235    */
236  0 StringBuilder sb = new StringBuilder();
237  0 for (String cmd : commands)
238    {
239  0 sb.append(" ").append(cmd);
240    }
241  0 wrapped.add(sb.toString());
242   
243  0 return wrapped;
244    }
245   
246    /**
247    * Exports an alignment, and reference (RF) annotation if present, to the
248    * specified file, in Stockholm format, removing all HMM sequences
249    *
250    * @param seqs
251    * @param toFile
252    * @param annotated
253    * @throws IOException
254    */
 
255  0 toggle public void exportStockholm(SequenceI[] seqs, File toFile,
256    AnnotatedCollectionI annotated)
257    throws IOException
258    {
259  0 if (seqs == null)
260    {
261  0 return;
262    }
263  0 AlignmentI newAl = new Alignment(seqs);
264   
265  0 if (!newAl.isAligned())
266    {
267  0 newAl.padGaps();
268    }
269   
270  0 if (toFile != null && annotated != null)
271    {
272  0 AlignmentAnnotation[] annots = annotated.getAlignmentAnnotation();
273  0 if (annots != null)
274    {
275  0 for (AlignmentAnnotation annot : annots)
276    {
277  0 if (annot.label.contains("Reference") || "RF".equals(annot.label))
278    {
279  0 AlignmentAnnotation newRF;
280  0 if (annot.annotations.length > newAl.getWidth())
281    {
282  0 Annotation[] rfAnnots = new Annotation[newAl.getWidth()];
283  0 System.arraycopy(annot.annotations, 0, rfAnnots, 0,
284    rfAnnots.length);
285  0 newRF = new AlignmentAnnotation("RF", "Reference Positions",
286    rfAnnots);
287    }
288    else
289    {
290  0 newRF = new AlignmentAnnotation(annot);
291    }
292  0 newAl.addAnnotation(newRF);
293    }
294    }
295    }
296    }
297   
298  0 for (SequenceI seq : newAl.getSequencesArray())
299    {
300  0 if (seq.getAnnotation() != null)
301    {
302  0 for (AlignmentAnnotation ann : seq.getAnnotation())
303    {
304  0 seq.removeAlignmentAnnotation(ann);
305    }
306    }
307    }
308   
309  0 StockholmFile file = new StockholmFile(newAl);
310  0 String output = file.print(seqs, false);
311  0 PrintWriter writer = new PrintWriter(toFile);
312  0 writer.println(output);
313  0 writer.close();
314    }
315   
316    /**
317    * Answers the full path to the given hmmer executable, or null if file cannot
318    * be found or is not executable
319    *
320    * @param cmd
321    * command short name e.g. hmmalign
322    * @return
323    * @throws IOException
324    */
 
325  0 toggle protected String getCommandPath(String cmd)
326    throws IOException
327    {
328  0 String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
329    // ensure any symlink to the directory is resolved:
330  0 binariesFolder = Paths.get(binariesFolder).toRealPath().toString();
331  0 File file = FileUtils.getExecutable(cmd, binariesFolder);
332  0 if (file == null && af != null)
333    {
334  0 JvOptionPane.showInternalMessageDialog(af, MessageManager
335    .formatMessage("label.executable_not_found", cmd));
336    }
337   
338  0 return file == null ? null : getFilePath(file, true);
339    }
340   
341    /**
342    * Exports an HMM to the specified file
343    *
344    * @param hmm
345    * @param hmmFile
346    * @throws IOException
347    */
 
348  0 toggle public void exportHmm(HiddenMarkovModel hmm, File hmmFile)
349    throws IOException
350    {
351  0 if (hmm != null)
352    {
353  0 HMMFile file = new HMMFile(hmm);
354  0 PrintWriter writer = new PrintWriter(hmmFile);
355  0 writer.print(file.print());
356  0 writer.close();
357    }
358    }
359   
360    // TODO is needed?
361    /**
362    * Exports a sequence to the specified file
363    *
364    * @param hmm
365    * @param hmmFile
366    * @throws IOException
367    */
 
368  0 toggle public void exportSequence(SequenceI seq, File seqFile) throws IOException
369    {
370  0 if (seq != null)
371    {
372  0 FastaFile file = new FastaFile();
373  0 PrintWriter writer = new PrintWriter(seqFile);
374  0 writer.print(file.print(new SequenceI[] { seq }, false));
375  0 writer.close();
376    }
377    }
378   
379    /**
380    * Answers the HMM profile for the profile sequence the user selected (default
381    * is just the first HMM sequence in the alignment)
382    *
383    * @return
384    */
 
385  0 toggle protected HiddenMarkovModel getHmmProfile()
386    {
387  0 String alignToParamName = MessageManager.getString("label.use_hmm");
388  0 for (ArgumentI arg : params)
389    {
390  0 String name = arg.getName();
391  0 if (name.equals(alignToParamName))
392    {
393  0 String seqName = arg.getValue();
394  0 SequenceI hmmSeq = alignment.findName(seqName);
395  0 if (hmmSeq.hasHMMProfile())
396    {
397  0 return hmmSeq.getHMM();
398    }
399    }
400    }
401  0 return null;
402    }
403   
404    /**
405    * Answers the query sequence the user selected (default is just the first
406    * sequence in the alignment)
407    *
408    * @return
409    */
 
410  0 toggle protected SequenceI getSequence()
411    {
412  0 String alignToParamName = MessageManager
413    .getString("label.use_sequence");
414  0 for (ArgumentI arg : params)
415    {
416  0 String name = arg.getName();
417  0 if (name.equals(alignToParamName))
418    {
419  0 String seqName = arg.getValue();
420  0 SequenceI seq = alignment.findName(seqName);
421  0 return seq;
422    }
423    }
424  0 return null;
425    }
426   
427    /**
428    * Answers an absolute path to the given file, in a format suitable for
429    * processing by a hmmer command. On a Windows platform, the native Windows file
430    * path is converted to Cygwin format, by replacing '\'with '/' and drive letter
431    * X with /cygdrive/x.
432    *
433    * @param resultFile
434    * @param isInCygwin
435    * True if file is to be read/written from within the Cygwin
436    * shell. Should be false for any imports.
437    * @return
438    */
 
439  0 toggle protected String getFilePath(File resultFile, boolean isInCygwin)
440    {
441  0 String path = resultFile.getAbsolutePath();
442  0 if (Platform.isWindowsAndNotJS() && isInCygwin)
443    {
444    // the first backslash escapes '\' for the regular expression argument
445  0 path = path.replaceAll("\\" + File.separator, "/");
446  0 int colon = path.indexOf(':');
447  0 if (colon > 0)
448    {
449  0 String drive = path.substring(0, colon);
450  0 path = path.replaceAll(drive + ":", "/cygdrive/" + drive);
451    }
452    }
453   
454  0 return path;
455    }
456   
457    /**
458    * A helper method that deletes any HMM consensus sequence from the given
459    * collection, and from the parent alignment if <code>ac</code> is a subgroup
460    *
461    * @param ac
462    */
 
463  0 toggle void deleteHmmSequences(AnnotatedCollectionI ac)
464    {
465  0 List<SequenceI> hmmSeqs = ac.getHmmSequences();
466  0 for (SequenceI hmmSeq : hmmSeqs)
467    {
468  0 if (ac instanceof SequenceGroup)
469    {
470  0 ((SequenceGroup) ac).deleteSequence(hmmSeq, false);
471  0 AnnotatedCollectionI context = ac.getContext();
472  0 if (context != null && context instanceof AlignmentI)
473    {
474  0 ((AlignmentI) context).deleteSequence(hmmSeq);
475    }
476    }
477    else
478    {
479  0 ((AlignmentI) ac).deleteSequence(hmmSeq);
480    }
481    }
482    }
483   
484    /**
485    * Sets the names of any duplicates within the given sequences to include their
486    * respective lengths. Deletes any duplicates that have the same name after this
487    * step
488    *
489    * @param seqs
490    */
 
491  0 toggle void renameDuplicates(AlignmentI al)
492    {
493   
494  0 SequenceI[] seqs = al.getSequencesArray();
495  0 List<Boolean> wasRenamed = new ArrayList<>();
496   
497  0 for (SequenceI seq : seqs)
498    {
499  0 wasRenamed.add(false);
500    }
501   
502  0 for (int i = 0; i < seqs.length; i++)
503    {
504  0 for (int j = 0; j < seqs.length; j++)
505    {
506  0 if (seqs[i].getName().equals(seqs[j].getName()) && i != j
507    && !wasRenamed.get(j))
508    {
509   
510  0 wasRenamed.set(i, true);
511  0 String range = "/" + seqs[j].getStart() + "-" + seqs[j].getEnd();
512    // setting sequence name to include range - to differentiate between
513    // sequences of the same name. Currently have to include the range twice
514    // because the range is removed (once) when setting the name
515    // TODO come up with a better way of doing this
516  0 seqs[j].setName(seqs[j].getName() + range + range);
517    }
518   
519    }
520  0 if (wasRenamed.get(i))
521    {
522  0 String range = "/" + seqs[i].getStart() + "-" + seqs[i].getEnd();
523  0 seqs[i].setName(seqs[i].getName() + range + range);
524    }
525    }
526   
527  0 for (int i = 0; i < seqs.length; i++)
528    {
529  0 for (int j = 0; j < seqs.length; j++)
530    {
531  0 if (seqs[i].getName().equals(seqs[j].getName()) && i != j)
532    {
533  0 al.deleteSequence(j);
534    }
535    }
536    }
537    }
538   
539    }