JAL-629 Add --renderer arg/subval for vector output and fixed annotation renderer...
[jalview.git] / src / jalview / io / HTMLOutput.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 java.io.BufferedReader;
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStreamReader;
27 import java.net.URL;
28 import java.util.Objects;
29
30 import jalview.api.AlignExportSettingsI;
31 import jalview.bin.Cache;
32 import jalview.bin.Jalview;
33 import jalview.datamodel.AlignExportSettingsAdapter;
34 import jalview.datamodel.AlignmentExportData;
35 import jalview.gui.AlignmentPanel;
36 import jalview.gui.IProgressIndicator;
37 import jalview.util.MessageManager;
38
39 public abstract class HTMLOutput implements Runnable
40 {
41   protected AlignmentPanel ap;
42
43   /*
44    * key for progress or status messages
45    */
46   protected long pSessionId;
47
48   /*
49    * (optional) place to write progress messages to
50    */
51   protected IProgressIndicator pIndicator;
52
53   protected File generatedFile;
54
55   String _bioJson = null;
56
57   private String description;
58
59   /**
60    * Constructor given an alignment panel (which should not be null)
61    * 
62    * @param ap
63    * @param desc
64    */
65   public HTMLOutput(AlignmentPanel ap, String desc)
66   {
67     this.ap = ap;
68     this.pIndicator = ap.alignFrame;
69     this.description = desc;
70     this.pSessionId = System.currentTimeMillis();
71   }
72
73   /**
74    * Gets the BioJSON data as a string, with lazy evaluation (first time called
75    * only). If the output format is configured not to embed BioJSON, returns
76    * null.
77    * 
78    * @return
79    */
80   public String getBioJSONData()
81   {
82     if (!isEmbedData())
83     {
84       return null;
85     }
86     if (_bioJson == null)
87     {
88       AlignExportSettingsI options = new AlignExportSettingsAdapter(true);
89       AlignmentExportData exportData = ap.getAlignViewport()
90               .getAlignExportData(options);
91       _bioJson = new FormatAdapter(ap, options).formatSequences(
92               FileFormat.Json, exportData.getAlignment(),
93               exportData.getOmitHidden(), exportData.getStartEndPostions(),
94               ap.getAlignViewport().getAlignment().getHiddenColumns());
95     }
96
97     return _bioJson;
98   }
99
100   /**
101    * Read a template file content as string
102    * 
103    * @param file
104    *          - the file to be read
105    * @return File content as String
106    * @throws IOException
107    */
108   public static String readFileAsString(File file) throws IOException
109   {
110     InputStreamReader isReader = null;
111     BufferedReader buffReader = null;
112     StringBuilder sb = new StringBuilder();
113     Objects.requireNonNull(file, "File must not be null!");
114     @SuppressWarnings("deprecation")
115     URL url = file.toURL();
116     if (url != null)
117     {
118       try
119       {
120         isReader = new InputStreamReader(url.openStream());
121         buffReader = new BufferedReader(isReader);
122         String line;
123         String lineSeparator = System.getProperty("line.separator");
124         while ((line = buffReader.readLine()) != null)
125         {
126           sb.append(line).append(lineSeparator);
127         }
128
129       } catch (Exception ex)
130       {
131         ex.printStackTrace();
132       } finally
133       {
134         if (isReader != null)
135         {
136           isReader.close();
137         }
138
139         if (buffReader != null)
140         {
141           buffReader.close();
142         }
143       }
144     }
145     return sb.toString();
146   }
147
148   public static String getImageMapHTML()
149   {
150     return new String("<html>\n" + "<head>\n"
151             + "<script language=\"JavaScript\">\n"
152             + "var ns4 = document.layers;\n"
153             + "var ns6 = document.getElementById && !document.all;\n"
154             + "var ie4 = document.all;\n" + "offsetX = 0;\n"
155             + "offsetY = 20;\n" + "var toolTipSTYLE=\"\";\n"
156             + "function initToolTips()\n" + "{\n" + "  if(ns4||ns6||ie4)\n"
157             + "  {\n"
158             + "    if(ns4) toolTipSTYLE = document.toolTipLayer;\n"
159             + "    else if(ns6) toolTipSTYLE = document.getElementById(\"toolTipLayer\").style;\n"
160             + "    else if(ie4) toolTipSTYLE = document.all.toolTipLayer.style;\n"
161             + "    if(ns4) document.captureEvents(Event.MOUSEMOVE);\n"
162             + "    else\n" + "    {\n"
163             + "      toolTipSTYLE.visibility = \"visible\";\n"
164             + "      toolTipSTYLE.display = \"none\";\n" + "    }\n"
165             + "    document.onmousemove = moveToMouseLoc;\n" + "  }\n"
166             + "}\n" + "function toolTip(msg, fg, bg)\n" + "{\n"
167             + "  if(toolTip.arguments.length < 1) // hide\n" + "  {\n"
168             + "    if(ns4) toolTipSTYLE.visibility = \"hidden\";\n"
169             + "    else toolTipSTYLE.display = \"none\";\n" + "  }\n"
170             + "  else // show\n" + "  {\n"
171             + "    if(!fg) fg = \"#555555\";\n"
172             + "    if(!bg) bg = \"#FFFFFF\";\n" + "    var content =\n"
173             + "    '<table border=\"0\" cellspacing=\"0\" cellpadding=\"1\" bgcolor=\"' + fg + '\"><td>' +\n"
174             + "    '<table border=\"0\" cellspacing=\"0\" cellpadding=\"1\" bgcolor=\"' + bg + \n"
175             + "    '\"><td align=\"center\"><font face=\"sans-serif\" color=\"' + fg +\n"
176             + "    '\" size=\"-2\">&nbsp;' + msg +\n"
177             + "    '&nbsp;</font></td></table></td></table>';\n"
178             + "    if(ns4)\n" + "    {\n"
179             + "      toolTipSTYLE.document.write(content);\n"
180             + "      toolTipSTYLE.document.close();\n"
181             + "      toolTipSTYLE.visibility = \"visible\";\n" + "    }\n"
182             + "    if(ns6)\n" + "    {\n"
183             + "      document.getElementById(\"toolTipLayer\").innerHTML = content;\n"
184             + "      toolTipSTYLE.display='block'\n" + "    }\n"
185             + "    if(ie4)\n" + "    {\n"
186             + "      document.all(\"toolTipLayer\").innerHTML=content;\n"
187             + "      toolTipSTYLE.display='block'\n" + "    }\n" + "  }\n"
188             + "}\n" + "function moveToMouseLoc(e)\n" + "{\n"
189             + "  if(ns4||ns6)\n" + "  {\n" + "    x = e.pageX;\n"
190             + "    y = e.pageY;\n" + "  }\n" + "  else\n" + "  {\n"
191             + "    x = event.x + document.body.scrollLeft;\n"
192             + "    y = event.y + document.body.scrollTop;\n" + "  }\n"
193             + "  toolTipSTYLE.left = x + offsetX;\n"
194             + "  toolTipSTYLE.top = y + offsetY;\n" + "  return true;\n"
195             + "}\n" + "</script>\n" + "</head>\n" + "<body>\n"
196             + "<div id=\"toolTipLayer\" style=\"position:absolute; visibility: hidden\"></div>\n"
197             + "<script language=\"JavaScript\"><!--\n"
198             + "initToolTips(); //--></script>\n");
199
200   }
201
202   /**
203    * Prompts the user to choose an output file and returns the file path, or
204    * null on Cancel
205    * 
206    * @return
207    */
208   public String getOutputFile()
209   {
210     String selectedFile = null;
211
212     // TODO: JAL-3048 generate html rendered view (requires SvgGraphics and/or
213     // Jalview HTML rendering system- probably not required for Jalview-JS)
214     JalviewFileChooser jvFileChooser = new JalviewFileChooser("html",
215             "HTML files");
216     jvFileChooser.setFileView(new JalviewFileView());
217
218     jvFileChooser
219             .setDialogTitle(MessageManager.getString("label.save_as_html"));
220     jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
221
222     int fileChooserOpt = jvFileChooser.showSaveDialog(null);
223     if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
224     {
225       Cache.setProperty("LAST_DIRECTORY",
226               jvFileChooser.getSelectedFile().getParent());
227       selectedFile = jvFileChooser.getSelectedFile().getPath();
228     }
229
230     return selectedFile;
231   }
232
233   protected void setProgressMessage(String message)
234   {
235     if (pIndicator != null && !isHeadless())
236     {
237       pIndicator.setProgressBar(message, pSessionId);
238     }
239     else
240     {
241       System.out.println(message);
242     }
243   }
244
245   /**
246    * Answers true if HTML export is invoke in headless mode or false otherwise
247    * 
248    * @return
249    */
250   protected boolean isHeadless()
251   {
252     return System.getProperty("java.awt.headless") != null
253             && System.getProperty("java.awt.headless").equals("true");
254   }
255
256   /**
257    * This method provides implementation of consistent behaviour which should
258    * occur after a HTML file export. It MUST be called at the end of the
259    * exportHTML() method implementation.
260    */
261   protected void exportCompleted()
262   {
263     if (isLaunchInBrowserAfterExport() && !isHeadless())
264     {
265       /*
266       try
267       {
268       */
269       jalview.util.BrowserLauncher.openURL("file:///" + getExportedFile());
270       /*
271       } catch (IOException e)
272       {
273         e.printStackTrace();
274       }
275       */
276     }
277   }
278
279   /**
280    * if this answers true then BioJSON data will be embedded to the exported
281    * HTML file otherwise it won't be embedded.
282    * 
283    * @return
284    */
285   public abstract boolean isEmbedData();
286
287   /**
288    * if this answers true then the generated HTML file is opened for viewing in
289    * a browser after its generation otherwise it won't be opened in a browser
290    * 
291    * @return
292    */
293   public abstract boolean isLaunchInBrowserAfterExport();
294
295   /**
296    * handle to the generated HTML file
297    * 
298    * @return
299    */
300   public File getExportedFile()
301   {
302     return generatedFile;
303   }
304
305   public void exportHTML(String outputFile)
306   {
307     exportHTML(outputFile, null);
308   }
309
310   public void exportHTML(String outputFile, String renderer)
311   {
312     setProgressMessage(MessageManager.formatMessage(
313             "status.exporting_alignment_as_x_file", getDescription()));
314     try
315     {
316       if (outputFile == null)
317       {
318         /*
319          * prompt for output file
320          */
321         outputFile = getOutputFile();
322         if (outputFile == null)
323         {
324           setProgressMessage(MessageManager.formatMessage(
325                   "status.cancelled_image_export_operation",
326                   getDescription()));
327           return;
328         }
329       }
330       generatedFile = new File(outputFile);
331     } catch (Exception e)
332     {
333       setProgressMessage(MessageManager
334               .formatMessage("info.error_creating_file", getDescription()));
335       e.printStackTrace();
336       return;
337     }
338     if (Jalview.isHeadlessMode())
339     {
340       this.run(renderer);
341     }
342     else
343     {
344       new Thread(this).start();
345     }
346
347   }
348
349   /**
350    * Answers a short description of the image format suitable for display in
351    * messages
352    * 
353    * @return
354    */
355   protected final String getDescription()
356   {
357     return description;
358   }
359
360   // used to pass an option such as render to run
361   public abstract void run(String string);
362 }