JAL-2629 storage and lifecycle of alignment/group HMM annotations revised
[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)
143           throws IOException
144   {
145     if (seqs != null)
146     {
147       AlignmentI newAl = new Alignment(seqs);
148       if (toFile != null && annotated != null)
149       {
150         for (AlignmentAnnotation annot : annotated.getAlignmentAnnotation())
151         {
152           if (annot.label.contains("Reference") || "RF".equals(annot.label))
153           {
154             AlignmentAnnotation newRF;
155             if (annot.annotations.length > newAl.getWidth())
156             {
157               Annotation[] rfAnnots = new Annotation[newAl.getWidth()];
158               System.arraycopy(annot.annotations, 0, rfAnnots, 0,
159                       rfAnnots.length);
160               newRF = new AlignmentAnnotation("RF", "Reference Positions",
161                       rfAnnots);
162             }
163             else
164             {
165               newRF = new AlignmentAnnotation(annot);
166             }
167             newAl.addAnnotation(newRF);
168           }
169         }
170       }
171
172       StockholmFile file = new StockholmFile(newAl);
173       String output = file.print(seqs, false);
174       PrintWriter writer = new PrintWriter(toFile);
175       writer.println(output);
176       writer.close();
177     }
178   }
179
180   /**
181    * Answers the full path to the given hmmer executable, or null if file cannot
182    * be found or is not executable
183    * 
184    * @param cmd
185    *          command short name e.g. hmmalign
186    * @return
187    */
188   protected String getCommandPath(String cmd)
189   {
190     String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
191     File file = getExecutable(cmd, binariesFolder);
192     if (file == null && af != null)
193     {
194         JvOptionPane.showInternalMessageDialog(af,
195                 MessageManager.getString("warn.hmm_command_failed"));
196     }
197
198     return file == null ? null : file.getAbsolutePath();
199   }
200
201   /**
202    * Answers the executable file for the given hmmer command, or null if not
203    * found or not executable. The path to the executable is the command name
204    * prefixed by the hmmer binaries folder path, optionally with .exe appended.
205    * 
206    * @param cmd
207    *          hmmer command short name, for example hmmbuild
208    * @param binaryPath
209    *          parent folder containing hmmer executables
210    * @return
211    */
212   public static File getExecutable(String cmd, String binaryPath)
213   {
214     File file = new File(binaryPath, cmd);
215     if (!file.canExecute())
216     {
217       file = new File(binaryPath, cmd + ".exe");
218       {
219         if (!file.canExecute())
220         {
221           file = null;
222         }
223       }
224     }
225     return file;
226   }
227
228   /**
229    * A convenience method to create a temporary file that is deleted on exit of
230    * the JVM
231    * 
232    * @param prefix
233    * @param suffix
234    * @return
235    * @throws IOException
236    */
237   protected File createTempFile(String prefix, String suffix)
238           throws IOException
239   {
240     File f = File.createTempFile(prefix, suffix);
241     f.deleteOnExit();
242     return f;
243
244   }
245
246   /**
247    * Exports an HMM to the specified file
248    * 
249    * @param hmm
250    * @param hmmFile
251    * @throws IOException
252    */
253   public void exportHmm(HiddenMarkovModel hmm, File hmmFile)
254           throws IOException
255   {
256     if (hmm != null)
257     {
258       HMMFile file = new HMMFile(hmm);
259       PrintWriter writer = new PrintWriter(hmmFile);
260       writer.print(file.print());
261       writer.close();
262     }
263   }
264 }