Merge branch 'develop' into features/filetypeEnum
[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.bin.Cache;
26 import jalview.datamodel.AlignmentExportData;
27 import jalview.exceptions.NoFileSelectedException;
28 import jalview.gui.AlignFrame;
29 import jalview.gui.IProgressIndicator;
30 import jalview.gui.OOMWarning;
31 import jalview.json.binding.biojs.BioJSReleasePojo;
32 import jalview.json.binding.biojs.BioJSRepositoryPojo;
33 import jalview.util.ImageMaker;
34 import jalview.util.MessageManager;
35
36 import java.io.BufferedInputStream;
37 import java.io.BufferedReader;
38 import java.io.File;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.InputStreamReader;
42 import java.io.PrintWriter;
43 import java.net.URISyntaxException;
44 import java.net.URL;
45 import java.util.Objects;
46 import java.util.TreeMap;
47
48 public class BioJsHTMLOutput
49 {
50   private AlignmentViewPanel ap;
51
52   private long pSessionId;
53
54   private IProgressIndicator pIndicator;
55
56   private boolean headless;
57
58   private static File currentBJSTemplateFile;
59
60   private static TreeMap<String, File> bioJsMSAVersions;
61
62   public static final String DEFAULT_DIR = System.getProperty("user.home")
63           + File.separatorChar + ".biojs_templates" + File.separatorChar;
64
65   public static final String BJS_TEMPLATES_LOCAL_DIRECTORY = jalview.bin.Cache
66           .getDefault("biojs_template_directory", DEFAULT_DIR);
67
68   public static final String BJS_TEMPLATE_GIT_REPO = jalview.bin.Cache
69           .getDefault(
70                   "biojs_template_git_repo",
71                   "https://raw.githubusercontent.com/jalview/exporter-templates/master/biojs/package.json");
72
73   public BioJsHTMLOutput(AlignmentViewPanel ap,
74           IProgressIndicator pIndicator)
75   {
76     if (ap != null)
77     {
78       this.ap = ap;
79       this.pSessionId = System.currentTimeMillis();
80       this.pIndicator = pIndicator;
81       this.headless = (System.getProperty("java.awt.headless") != null && System
82               .getProperty("java.awt.headless").equals("true"));
83     }
84   }
85
86   public void exportJalviewAlignmentAsBioJsHtmlFile()
87   {
88     String outputFile = null;
89     try
90     {
91       outputFile = getOutputFile();
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 = AlignFrame
132               .getAlignmentForExport(FileFormat.Json,
133                       ap.getAlignViewport(), exportSettings);
134       String bioJSON = new FormatAdapter(ap, exportData.getSettings())
135               .formatSequences(FileFormat.Json, 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       pIndicator.setProgressBar(MessageManager.formatMessage(
166               "info.error_creating_file", "HTML"), pSessionId);
167       e.printStackTrace();
168     }
169   }
170
171   public String getOutputFile() throws NoFileSelectedException
172   {
173     String selectedFile = null;
174     if (pIndicator != null && !headless)
175     {
176       pIndicator.setProgressBar(MessageManager.formatMessage(
177               "status.waiting_for_user_to_select_output_file", "HTML"),
178               pSessionId);
179     }
180
181     JalviewFileChooser jvFileChooser = new JalviewFileChooser(
182             Cache.getProperty("LAST_DIRECTORY"), ImageMaker.HTML_EXTENSION,
183             ImageMaker.HTML_EXTENSION, ImageMaker.HTML_EXTENSION);
184     jvFileChooser.setFileView(new JalviewFileView());
185
186     jvFileChooser.setDialogTitle(MessageManager
187             .getString("label.save_as_biojs_html"));
188     jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
189
190     int fileChooserOpt = jvFileChooser.showSaveDialog(null);
191     if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
192     {
193       Cache.setProperty("LAST_DIRECTORY", jvFileChooser
194               .getSelectedFile().getParent());
195       selectedFile = jvFileChooser.getSelectedFile().getPath();
196     }
197     else
198     {
199       pIndicator.setProgressBar(MessageManager.formatMessage(
200               "status.cancelled_image_export_operation", "BioJS"),
201               pSessionId);
202       throw new NoFileSelectedException("No file was selected.");
203     }
204     return selectedFile;
205   }
206
207   public static String getBioJsTemplateAsString() throws IOException
208   {
209     InputStreamReader isReader = null;
210     BufferedReader buffReader = null;
211     StringBuilder sb = new StringBuilder();
212     Objects.requireNonNull(getCurrentBJSTemplateFile(),
213             "BioJsTemplate File not initialized!");
214     @SuppressWarnings("deprecation")
215     URL url = getCurrentBJSTemplateFile().toURL();
216     if (url != null)
217     {
218       try
219       {
220         isReader = new InputStreamReader(url.openStream());
221         buffReader = new BufferedReader(isReader);
222         String line;
223         String lineSeparator = System.getProperty("line.separator");
224         while ((line = buffReader.readLine()) != null)
225         {
226           sb.append(line).append(lineSeparator);
227         }
228
229       } catch (Exception ex)
230       {
231         ex.printStackTrace();
232       } finally
233       {
234         if (isReader != null)
235         {
236           isReader.close();
237         }
238
239         if (buffReader != null)
240         {
241           buffReader.close();
242         }
243       }
244     }
245     return sb.toString();
246   }
247
248   public static void refreshBioJSVersionsInfo(String dirName)
249           throws URISyntaxException
250   {
251     File directory = new File(BJS_TEMPLATES_LOCAL_DIRECTORY);
252     Objects.requireNonNull(dirName, "dirName MUST not be null!");
253     Objects.requireNonNull(directory, "directory MUST not be null!");
254     TreeMap<String, File> versionFileMap = new TreeMap<String, File>();
255
256     for (File file : directory.listFiles())
257     {
258       if (file.isFile())
259       {
260         String fileName = file.getName().substring(0,
261                 file.getName().lastIndexOf("."));
262         String fileMeta[] = fileName.split("_");
263         if (fileMeta.length > 2)
264         {
265           setCurrentBJSTemplateFile(file);
266           versionFileMap.put(fileMeta[2], file);
267         }
268         else if (fileMeta.length > 1)
269         {
270           versionFileMap.put(fileMeta[1], file);
271         }
272       }
273     }
274     if (getCurrentBJSTemplateFile() == null && versionFileMap.size() > 0)
275     {
276       setCurrentBJSTemplateFile(versionFileMap.lastEntry().getValue());
277     }
278     setBioJsMSAVersions(versionFileMap);
279   }
280
281   public static void updateBioJS()
282   {
283     Thread updateThread = new Thread()
284     {
285       @Override
286       public void run()
287       {
288         try
289         {
290           String gitRepoPkgJson = getURLContentAsString(BJS_TEMPLATE_GIT_REPO);
291           if (gitRepoPkgJson != null)
292           {
293             BioJSRepositoryPojo release = new BioJSRepositoryPojo(
294                     gitRepoPkgJson);
295             syncUpdates(BJS_TEMPLATES_LOCAL_DIRECTORY, release);
296             refreshBioJSVersionsInfo(BJS_TEMPLATES_LOCAL_DIRECTORY);
297           }
298         } catch (URISyntaxException e)
299         {
300           e.printStackTrace();
301         }
302       }
303     };
304     updateThread.start();
305
306   }
307
308   public static void syncUpdates(String localDir, BioJSRepositoryPojo repo)
309   {
310     for (BioJSReleasePojo bjsRelease : repo.getReleases())
311     {
312       String releaseUrl = bjsRelease.getUrl();
313       String releaseVersion = bjsRelease.getVersion();
314       String releaseFile = "BioJsMSA_" + releaseVersion + ".txt";
315       if (releaseVersion.equals(repo.getLatestReleaseVersion()))
316       {
317         releaseFile = "Latest_BioJsMSA_" + releaseVersion + ".txt";
318       }
319
320       File biojsDirectory = new File(BJS_TEMPLATES_LOCAL_DIRECTORY);
321       if (!biojsDirectory.exists())
322       {
323         if (!biojsDirectory.mkdirs())
324         {
325           System.out.println("Couldn't create local directory : "
326                   + BJS_TEMPLATES_LOCAL_DIRECTORY);
327           return;
328         }
329       }
330
331       File file = new File(BJS_TEMPLATES_LOCAL_DIRECTORY + releaseFile);
332       if (!file.exists())
333       {
334
335         PrintWriter out = null;
336         try
337         {
338           out = new java.io.PrintWriter(new java.io.FileWriter(file));
339           out.print(getURLContentAsString(releaseUrl));
340         } catch (IOException e)
341         {
342           e.printStackTrace();
343         } finally
344         {
345           if (out != null)
346           {
347             out.flush();
348             out.close();
349           }
350         }
351       }
352     }
353
354   }
355
356   public static String getURLContentAsString(String url)
357           throws OutOfMemoryError
358   {
359     StringBuilder responseStrBuilder = null;
360     InputStream is = null;
361     try
362     {
363       URL resourceUrl = new URL(url);
364       is = new BufferedInputStream(resourceUrl.openStream());
365       BufferedReader br = new BufferedReader(new InputStreamReader(is));
366       responseStrBuilder = new StringBuilder();
367       String lineContent;
368
369       while ((lineContent = br.readLine()) != null)
370       {
371         responseStrBuilder.append(lineContent).append("\n");
372       }
373     } catch (OutOfMemoryError er)
374     {
375       er.printStackTrace();
376     } catch (Exception ex)
377     {
378       ex.printStackTrace();
379     } finally
380     {
381       if (is != null)
382       {
383         try
384         {
385           is.close();
386         } catch (IOException e)
387         {
388           e.printStackTrace();
389         }
390       }
391     }
392     return responseStrBuilder == null ? null : responseStrBuilder
393             .toString();
394   }
395
396   public static File getCurrentBJSTemplateFile()
397   {
398     return currentBJSTemplateFile;
399   }
400
401   public static void setCurrentBJSTemplateFile(File currentBJSTemplateFile)
402   {
403     BioJsHTMLOutput.currentBJSTemplateFile = currentBJSTemplateFile;
404   }
405
406   public static TreeMap<String, File> getBioJsMSAVersions()
407   {
408     return bioJsMSAVersions;
409   }
410
411   public static void setBioJsMSAVersions(
412           TreeMap<String, File> bioJsMSAVersions)
413   {
414     BioJsHTMLOutput.bioJsMSAVersions = bioJsMSAVersions;
415   }
416
417 }