JAL-2629 pad gaps when exporting alignment as 'database to search'
[jalview.git] / src / jalview / hmmer / HmmerCommand.java
1 package jalview.hmmer;
2
3 import jalview.analysis.SeqsetUtils;
4 import jalview.bin.Cache;
5 import jalview.datamodel.Alignment;
6 import jalview.datamodel.AlignmentAnnotation;
7 import jalview.datamodel.AlignmentI;
8 import jalview.datamodel.AnnotatedCollectionI;
9 import jalview.datamodel.Annotation;
10 import jalview.datamodel.HiddenMarkovModel;
11 import jalview.datamodel.SequenceI;
12 import jalview.gui.AlignFrame;
13 import jalview.gui.JvOptionPane;
14 import jalview.gui.Preferences;
15 import jalview.io.HMMFile;
16 import jalview.io.StockholmFile;
17 import jalview.util.MessageManager;
18 import jalview.ws.params.ArgumentI;
19
20 import java.io.BufferedReader;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.io.PrintWriter;
25 import java.util.Hashtable;
26 import java.util.List;
27
28 /**
29  * Base class for hmmbuild, hmmalign and hmmsearch
30  * 
31  * @author TZVanaalten
32  *
33  */
34 public abstract class HmmerCommand implements Runnable
35 {
36   public static final String HMMBUILD = "hmmbuild";
37
38   protected final AlignFrame af;
39
40   protected final AlignmentI alignment;
41
42   protected final List<ArgumentI> params;
43
44   /**
45    * Constructor
46    * 
47    * @param alignFrame
48    * @param args
49    */
50   public HmmerCommand(AlignFrame alignFrame, List<ArgumentI> args)
51   {
52     af = alignFrame;
53     alignment = af.getViewport().getAlignment();
54     params = args;
55   }
56
57   public static boolean isHmmerAvailable()
58   {
59     File exec = getExecutable(HMMBUILD, Cache.getProperty(Preferences.HMMER_PATH));
60     return exec != null;
61   }
62
63   /**
64    * Uniquifies the sequences when exporting and stores their details in a
65    * hashtable
66    * 
67    * @param seqs
68    */
69   protected Hashtable stashSequences(SequenceI[] seqs)
70   {
71     return SeqsetUtils.uniquify(seqs, true);
72   }
73
74   /**
75    * Restores the sequence data lost by uniquifying
76    * 
77    * @param hashtable
78    * @param seqs
79    */
80   protected void recoverSequences(Hashtable hashtable, SequenceI[] seqs)
81   {
82     SeqsetUtils.deuniquify(hashtable, seqs);
83   }
84
85   /**
86    * Runs a command as a separate process and waits for it to complete. Answers
87    * true if the process return status is zero, else false.
88    * 
89    * @param command
90    *          the executable command and any arguments to it
91    * @throws IOException
92    */
93   public boolean runCommand(List<String> command)
94           throws IOException
95   {
96     try
97     {
98       ProcessBuilder pb = new ProcessBuilder(command);
99       pb.redirectErrorStream(true); // merge syserr to sysout
100       final Process p = pb.start();
101       new Thread(new Runnable()
102       {
103         @Override
104         public void run()
105         {
106           BufferedReader input = new BufferedReader(
107                   new InputStreamReader(p.getInputStream()));
108           try
109           {
110             String line = input.readLine();
111             while (line != null)
112             {
113               System.out.println(line);
114               line = input.readLine();
115             }
116           } catch (IOException e)
117           {
118             e.printStackTrace();
119           }
120         }
121       }).start();
122
123       p.waitFor();
124       return p.exitValue() == 0; // 0 is success, by convention
125     } catch (Exception e)
126     {
127       e.printStackTrace();
128       return false;
129     }
130   }
131
132   /**
133    * Exports an alignment (and possibly annotation) to the specified file, in
134    * Stockholm format
135    * 
136    * @param seqs
137    * @param toFile
138    * @param annotated
139    * @throws IOException
140    */
141   public void exportStockholm(SequenceI[] seqs, File toFile,
142           AnnotatedCollectionI annotated) throws IOException
143   {
144     if (seqs == null)
145     {
146       return;
147     }
148     AlignmentI newAl = new Alignment(seqs);
149     if (!newAl.isAligned())
150     {
151       newAl.padGaps();
152     }
153
154     if (toFile != null && annotated != null)
155     {
156       for (AlignmentAnnotation annot : annotated.getAlignmentAnnotation())
157       {
158         if (annot.label.contains("Reference") || "RF".equals(annot.label))
159         {
160           AlignmentAnnotation newRF;
161           if (annot.annotations.length > newAl.getWidth())
162           {
163             Annotation[] rfAnnots = new Annotation[newAl.getWidth()];
164             System.arraycopy(annot.annotations, 0, rfAnnots, 0,
165                     rfAnnots.length);
166             newRF = new AlignmentAnnotation("RF", "Reference Positions",
167                     rfAnnots);
168           }
169           else
170           {
171             newRF = new AlignmentAnnotation(annot);
172           }
173           newAl.addAnnotation(newRF);
174         }
175       }
176     }
177
178     StockholmFile file = new StockholmFile(newAl);
179     String output = file.print(seqs, false);
180     PrintWriter writer = new PrintWriter(toFile);
181     writer.println(output);
182     writer.close();
183   }
184
185   /**
186    * Answers the full path to the given hmmer executable, or null if file cannot
187    * be found or is not executable
188    * 
189    * @param cmd
190    *          command short name e.g. hmmalign
191    * @return
192    */
193   protected String getCommandPath(String cmd)
194   {
195     String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
196     File file = getExecutable(cmd, binariesFolder);
197     if (file == null && af != null)
198     {
199         JvOptionPane.showInternalMessageDialog(af,
200                 MessageManager.getString("warn.hmm_command_failed"));
201     }
202
203     return file == null ? null : file.getAbsolutePath();
204   }
205
206   /**
207    * Answers the executable file for the given hmmer command, or null if not
208    * found or not executable. The path to the executable is the command name
209    * prefixed by the hmmer binaries folder path, optionally with .exe appended.
210    * 
211    * @param cmd
212    *          hmmer command short name, for example hmmbuild
213    * @param binaryPath
214    *          parent folder containing hmmer executables
215    * @return
216    */
217   public static File getExecutable(String cmd, String binaryPath)
218   {
219     File file = new File(binaryPath, cmd);
220     if (!file.canExecute())
221     {
222       file = new File(binaryPath, cmd + ".exe");
223       {
224         if (!file.canExecute())
225         {
226           file = null;
227         }
228       }
229     }
230     return file;
231   }
232
233   /**
234    * A convenience method to create a temporary file that is deleted on exit of
235    * the JVM
236    * 
237    * @param prefix
238    * @param suffix
239    * @return
240    * @throws IOException
241    */
242   protected File createTempFile(String prefix, String suffix)
243           throws IOException
244   {
245     File f = File.createTempFile(prefix, suffix);
246     f.deleteOnExit();
247     return f;
248
249   }
250
251   /**
252    * Exports an HMM to the specified file
253    * 
254    * @param hmm
255    * @param hmmFile
256    * @throws IOException
257    */
258   public void exportHmm(HiddenMarkovModel hmm, File hmmFile)
259           throws IOException
260   {
261     if (hmm != null)
262     {
263       HMMFile file = new HMMFile(hmm);
264       PrintWriter writer = new PrintWriter(hmmFile);
265       writer.print(file.print());
266       writer.close();
267     }
268   }
269 }