JAL-1723 ellipsis in tooltip if xrefs to more than 40 db sources
[jalview.git] / src / jalview / io / BioJsHTMLOutput.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io;
22
23 import jalview.api.AlignExportSettingI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.datamodel.AlignmentExportData;
26 import jalview.exceptions.NoFileSelectedException;
27 import jalview.gui.IProgressIndicator;
28 import jalview.gui.OOMWarning;
29 import jalview.json.binding.biojs.BioJSReleasePojo;
30 import jalview.json.binding.biojs.BioJSRepositoryPojo;
31 import jalview.util.MessageManager;
32
33 import java.io.BufferedInputStream;
34 import java.io.BufferedReader;
35 import java.io.File;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.InputStreamReader;
39 import java.io.PrintWriter;
40 import java.net.URISyntaxException;
41 import java.net.URL;
42 import java.util.Objects;
43 import java.util.TreeMap;
44
45 public class BioJsHTMLOutput
46 {
47   private AlignmentViewPanel ap;
48
49   private long pSessionId;
50
51   private IProgressIndicator pIndicator;
52
53   private boolean headless;
54
55   private static File currentBJSTemplateFile;
56
57   private static TreeMap<String, File> bioJsMSAVersions;
58
59   public static final String DEFAULT_DIR = System.getProperty("user.home")
60           + File.separatorChar + ".biojs_templates" + File.separatorChar;
61
62   public static final String BJS_TEMPLATES_LOCAL_DIRECTORY = jalview.bin.Cache
63           .getDefault("biojs_template_directory", DEFAULT_DIR);
64
65   public static final String BJS_TEMPLATE_GIT_REPO = jalview.bin.Cache
66           .getDefault(
67                   "biojs_template_git_repo",
68                   "https://raw.githubusercontent.com/jalview/exporter-templates/master/biojs/package.json");
69
70   public BioJsHTMLOutput(AlignmentViewPanel ap,
71           IProgressIndicator pIndicator)
72   {
73     if (ap != null)
74     {
75       this.ap = ap;
76       this.pSessionId = System.currentTimeMillis();
77       this.pIndicator = pIndicator;
78       this.headless = (System.getProperty("java.awt.headless") != null && System
79               .getProperty("java.awt.headless").equals("true"));
80     }
81   }
82
83   public void exportJalviewAlignmentAsBioJsHtmlFile(String outputFile)
84   {
85     // String outputFile = null;
86     try
87     {
88       if (outputFile == null)
89       {
90         outputFile = getOutputFile();
91       }
92       AlignExportSettingI exportSettings = new AlignExportSettingI()
93       {
94         @Override
95         public boolean isExportHiddenSequences()
96         {
97           return true;
98         }
99
100         @Override
101         public boolean isExportHiddenColumns()
102         {
103           return true;
104         }
105
106         @Override
107         public boolean isExportAnnotations()
108         {
109           return true;
110         }
111
112         @Override
113         public boolean isExportFeatures()
114         {
115           return true;
116         }
117
118         @Override
119         public boolean isExportGroups()
120         {
121           return true;
122         }
123
124         @Override
125         public boolean isCancelled()
126         {
127           return false;
128         }
129
130       };
131       AlignmentExportData exportData = jalview.gui.AlignFrame
132               .getAlignmentForExport(JSONFile.FILE_DESC,
133                       ap.getAlignViewport(), exportSettings);
134       String bioJSON = new FormatAdapter(ap, exportData.getSettings())
135               .formatSequences(JSONFile.FILE_DESC, exportData
136                       .getAlignment(), exportData.getOmitHidden(),
137                       exportData.getStartEndPostions(), ap
138                               .getAlignViewport().getColumnSelection());
139
140       String bioJSTemplateString = getBioJsTemplateAsString();
141       String generatedBioJsWithJalviewAlignmentAsJson = bioJSTemplateString
142               .replaceAll("#sequenceData#", bioJSON).toString();
143
144       PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
145               outputFile));
146       out.print(generatedBioJsWithJalviewAlignmentAsJson);
147       out.flush();
148       out.close();
149       jalview.util.BrowserLauncher.openURL("file:///" + outputFile);
150       if (pIndicator != null && !headless)
151       {
152         pIndicator.setProgressBar(MessageManager.formatMessage(
153                 "status.export_complete", "BioJS"), pSessionId);
154       }
155     } catch (NoFileSelectedException ex)
156     {
157       // do noting if no file was selected
158     } catch (OutOfMemoryError err)
159     {
160       System.out.println("########################\n" + "OUT OF MEMORY "
161               + outputFile + "\n" + "########################");
162       new OOMWarning("Creating Image for " + outputFile, err);
163     } catch (Exception e)
164     {
165       if (pIndicator != null && !headless)
166       {
167       pIndicator.setProgressBar(MessageManager.formatMessage(
168               "info.error_creating_file", "HTML"), pSessionId);
169       }
170       e.printStackTrace();
171     }
172   }
173
174   public String getOutputFile() throws NoFileSelectedException
175   {
176     String selectedFile = null;
177     if (pIndicator != null && !headless)
178     {
179       pIndicator.setProgressBar(MessageManager.formatMessage(
180               "status.waiting_for_user_to_select_output_file", "HTML"),
181               pSessionId);
182     }
183
184     JalviewFileChooser jvFileChooser = new JalviewFileChooser(
185             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
186             new String[] { "html" }, new String[] { "HTML files" },
187             "HTML files");
188     jvFileChooser.setFileView(new JalviewFileView());
189
190     jvFileChooser.setDialogTitle(MessageManager
191             .getString("label.save_as_biojs_html"));
192     jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
193
194     int fileChooserOpt = jvFileChooser.showSaveDialog(null);
195     if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
196     {
197       jalview.bin.Cache.setProperty("LAST_DIRECTORY", jvFileChooser
198               .getSelectedFile().getParent());
199       selectedFile = jvFileChooser.getSelectedFile().getPath();
200     }
201     else
202     {
203       pIndicator.setProgressBar(MessageManager.formatMessage(
204               "status.cancelled_image_export_operation", "BioJS"),
205               pSessionId);
206       throw new NoFileSelectedException("No file was selected.");
207     }
208     return selectedFile;
209   }
210
211   public static String getBioJsTemplateAsString() throws IOException
212   {
213     InputStreamReader isReader = null;
214     BufferedReader buffReader = null;
215     StringBuilder sb = new StringBuilder();
216     Objects.requireNonNull(getCurrentBJSTemplateFile(),
217             "BioJsTemplate File not initialized!");
218     @SuppressWarnings("deprecation")
219     URL url = getCurrentBJSTemplateFile().toURL();
220     if (url != null)
221     {
222       try
223       {
224         isReader = new InputStreamReader(url.openStream());
225         buffReader = new BufferedReader(isReader);
226         String line;
227         String lineSeparator = System.getProperty("line.separator");
228         while ((line = buffReader.readLine()) != null)
229         {
230           sb.append(line).append(lineSeparator);
231         }
232
233       } catch (Exception ex)
234       {
235         ex.printStackTrace();
236       } finally
237       {
238         if (isReader != null)
239         {
240           isReader.close();
241         }
242
243         if (buffReader != null)
244         {
245           buffReader.close();
246         }
247       }
248     }
249     return sb.toString();
250   }
251
252   public static void refreshBioJSVersionsInfo(String dirName)
253           throws URISyntaxException
254   {
255     File directory = new File(BJS_TEMPLATES_LOCAL_DIRECTORY);
256     Objects.requireNonNull(dirName, "dirName MUST not be null!");
257     Objects.requireNonNull(directory, "directory MUST not be null!");
258     TreeMap<String, File> versionFileMap = new TreeMap<String, File>();
259
260     for (File file : directory.listFiles())
261     {
262       if (file.isFile())
263       {
264         String fileName = file.getName().substring(0,
265                 file.getName().lastIndexOf("."));
266         String fileMeta[] = fileName.split("_");
267         if (fileMeta.length > 2)
268         {
269           setCurrentBJSTemplateFile(file);
270           versionFileMap.put(fileMeta[2], file);
271         }
272         else if (fileMeta.length > 1)
273         {
274           versionFileMap.put(fileMeta[1], file);
275         }
276       }
277     }
278     if (getCurrentBJSTemplateFile() == null && versionFileMap.size() > 0)
279     {
280       setCurrentBJSTemplateFile(versionFileMap.lastEntry().getValue());
281     }
282     setBioJsMSAVersions(versionFileMap);
283   }
284
285   public static void updateBioJS()
286   {
287     Thread updateThread = new Thread()
288     {
289       @Override
290       public void run()
291       {
292         try
293         {
294           String gitRepoPkgJson = getURLContentAsString(BJS_TEMPLATE_GIT_REPO);
295           if (gitRepoPkgJson != null)
296           {
297             BioJSRepositoryPojo release = new BioJSRepositoryPojo(
298                     gitRepoPkgJson);
299             syncUpdates(BJS_TEMPLATES_LOCAL_DIRECTORY, release);
300             refreshBioJSVersionsInfo(BJS_TEMPLATES_LOCAL_DIRECTORY);
301           }
302         } catch (URISyntaxException e)
303         {
304           e.printStackTrace();
305         }
306       }
307     };
308     updateThread.start();
309
310   }
311
312   public static void syncUpdates(String localDir, BioJSRepositoryPojo repo)
313   {
314     for (BioJSReleasePojo bjsRelease : repo.getReleases())
315     {
316       String releaseUrl = bjsRelease.getUrl();
317       String releaseVersion = bjsRelease.getVersion();
318       String releaseFile = "BioJsMSA_" + releaseVersion + ".txt";
319       if (releaseVersion.equals(repo.getLatestReleaseVersion()))
320       {
321         releaseFile = "Latest_BioJsMSA_" + releaseVersion + ".txt";
322       }
323
324       File biojsDirectory = new File(BJS_TEMPLATES_LOCAL_DIRECTORY);
325       if (!biojsDirectory.exists())
326       {
327         if (!biojsDirectory.mkdirs())
328         {
329           System.out.println("Couldn't create local directory : "
330                   + BJS_TEMPLATES_LOCAL_DIRECTORY);
331           return;
332         }
333       }
334
335       File file = new File(BJS_TEMPLATES_LOCAL_DIRECTORY + releaseFile);
336       if (!file.exists())
337       {
338
339         PrintWriter out = null;
340         try
341         {
342           out = new java.io.PrintWriter(new java.io.FileWriter(file));
343           out.print(getURLContentAsString(releaseUrl));
344         } catch (IOException e)
345         {
346           e.printStackTrace();
347         } finally
348         {
349           if (out != null)
350           {
351             out.flush();
352             out.close();
353           }
354         }
355       }
356     }
357
358   }
359
360   public static String getURLContentAsString(String url)
361           throws OutOfMemoryError
362   {
363     StringBuilder responseStrBuilder = null;
364     InputStream is = null;
365     try
366     {
367       URL resourceUrl = new URL(url);
368       is = new BufferedInputStream(resourceUrl.openStream());
369       BufferedReader br = new BufferedReader(new InputStreamReader(is));
370       responseStrBuilder = new StringBuilder();
371       String lineContent;
372
373       while ((lineContent = br.readLine()) != null)
374       {
375         responseStrBuilder.append(lineContent).append("\n");
376       }
377     } catch (OutOfMemoryError er)
378     {
379       er.printStackTrace();
380     } catch (Exception ex)
381     {
382       ex.printStackTrace();
383     } finally
384     {
385       if (is != null)
386       {
387         try
388         {
389           is.close();
390         } catch (IOException e)
391         {
392           e.printStackTrace();
393         }
394       }
395     }
396     return responseStrBuilder == null ? null : responseStrBuilder
397             .toString();
398   }
399
400   public static File getCurrentBJSTemplateFile()
401   {
402     return currentBJSTemplateFile;
403   }
404
405   public static void setCurrentBJSTemplateFile(File currentBJSTemplateFile)
406   {
407     BioJsHTMLOutput.currentBJSTemplateFile = currentBJSTemplateFile;
408   }
409
410   public static TreeMap<String, File> getBioJsMSAVersions()
411   {
412     return bioJsMSAVersions;
413   }
414
415   public static void setBioJsMSAVersions(
416           TreeMap<String, File> bioJsMSAVersions)
417   {
418     BioJsHTMLOutput.bioJsMSAVersions = bioJsMSAVersions;
419   }
420
421 }