9cc1e1664eb27b8da2bce9a138605a17a778b27e
[jalview.git] / src / jalview / io / FileLoader.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3  * Copyright (C) 2014 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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.io;
20
21 import java.util.*;
22
23 import javax.swing.*;
24
25 import jalview.datamodel.*;
26 import jalview.gui.*;
27 import jalview.util.MessageManager;
28
29 public class FileLoader implements Runnable
30 {
31   String file;
32
33   String protocol;
34
35   String format;
36
37   FileParse source = null; // alternative specification of where data comes
38
39   // from
40
41   AlignViewport viewport;
42
43   AlignFrame alignFrame;
44
45   long loadtime;
46
47   long memused;
48
49   boolean raiseGUI = true;
50
51   /**
52    * default constructor always raised errors in GUI dialog boxes
53    */
54   public FileLoader()
55   {
56     this(true);
57   }
58
59   /**
60    * construct a Fileloader that may raise errors non-interactively
61    * 
62    * @param raiseGUI
63    *          true if errors are to be raised as GUI dialog boxes
64    */
65   public FileLoader(boolean raiseGUI)
66   {
67     this.raiseGUI = raiseGUI;
68   }
69
70   public void LoadFile(AlignViewport viewport, String file,
71           String protocol, String format)
72   {
73     this.viewport = viewport;
74     LoadFile(file, protocol, format);
75   }
76
77   public void LoadFile(String file, String protocol, String format)
78   {
79     this.file = file;
80     this.protocol = protocol;
81     this.format = format;
82
83     final Thread loader = new Thread(this);
84
85     SwingUtilities.invokeLater(new Runnable()
86     {
87       public void run()
88       {
89         loader.start();
90       }
91     });
92   }
93
94   /**
95    * Load a (file, protocol) source of unknown type
96    * 
97    * @param file
98    * @param protocol
99    */
100   public void LoadFile(String file, String protocol)
101   {
102     LoadFile(file, protocol, null);
103   }
104
105   /**
106    * Load alignment from (file, protocol) and wait till loaded
107    * 
108    * @param file
109    * @param protocol
110    * @return alignFrame constructed from file contents
111    */
112   public AlignFrame LoadFileWaitTillLoaded(String file, String protocol)
113   {
114     return LoadFileWaitTillLoaded(file, protocol, null);
115   }
116
117   /**
118    * Load alignment from (file, protocol) of type format and wait till loaded
119    * 
120    * @param file
121    * @param protocol
122    * @param format
123    * @return alignFrame constructed from file contents
124    */
125   public AlignFrame LoadFileWaitTillLoaded(String file, String protocol,
126           String format)
127   {
128     this.file = file;
129     this.protocol = protocol;
130     this.format = format;
131     return _LoadFileWaitTillLoaded();
132   }
133
134   /**
135    * Load alignment from FileParse source of type format and wait till loaded
136    * 
137    * @param source
138    * @param format
139    * @return alignFrame constructed from file contents
140    */
141   public AlignFrame LoadFileWaitTillLoaded(FileParse source, String format)
142   {
143     this.source = source;
144     file = source.getInFile();
145     protocol = source.type;
146     this.format = format;
147     return _LoadFileWaitTillLoaded();
148   }
149
150   /**
151    * start thread and wait until finished, then return the alignFrame that's
152    * (hopefully) been read.
153    * 
154    * @return
155    */
156   protected AlignFrame _LoadFileWaitTillLoaded()
157   {
158     Thread loader = new Thread(this);
159     loader.start();
160
161     while (loader.isAlive())
162     {
163       try
164       {
165         Thread.sleep(500);
166       } catch (Exception ex)
167       {
168       }
169     }
170
171     return alignFrame;
172   }
173
174   public void updateRecentlyOpened()
175   {
176     Vector recent = new Vector();
177     if (protocol.equals(FormatAdapter.PASTE))
178     {
179       // do nothing if the file was pasted in as text... there is no filename to
180       // refer to it as.
181       return;
182     }
183     String type = protocol.equals(FormatAdapter.FILE) ? "RECENT_FILE"
184             : "RECENT_URL";
185
186     String historyItems = jalview.bin.Cache.getProperty(type);
187
188     StringTokenizer st;
189
190     if (historyItems != null)
191     {
192       st = new StringTokenizer(historyItems, "\t");
193
194       while (st.hasMoreTokens())
195       {
196         recent.addElement(st.nextElement().toString().trim());
197       }
198     }
199
200     if (recent.contains(file))
201     {
202       recent.remove(file);
203     }
204
205     StringBuffer newHistory = new StringBuffer(file);
206     for (int i = 0; i < recent.size() && i < 10; i++)
207     {
208       newHistory.append("\t");
209       newHistory.append(recent.elementAt(i));
210     }
211
212     jalview.bin.Cache.setProperty(type, newHistory.toString());
213
214     if (protocol.equals(FormatAdapter.FILE))
215     {
216       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT", format);
217     }
218   }
219
220   public void run()
221   {
222     String title = protocol.equals(AppletFormatAdapter.PASTE) ? "Copied From Clipboard"
223             : file;
224     Runtime rt = Runtime.getRuntime();
225     try
226     {
227       if (Desktop.instance != null)
228       {
229         Desktop.instance.startLoading(file);
230       }
231       if (format == null)
232       {
233         // just in case the caller didn't identify the file for us
234         if (source != null)
235         {
236           format = new IdentifyFile().Identify(source, false); // identify
237           // stream and
238           // rewind rather
239           // than close
240         }
241         else
242         {
243           format = new IdentifyFile().Identify(file, protocol);
244         }
245       }
246       // TODO: cache any stream datasources as a temporary file (eg. PDBs
247       // retrieved via URL)
248       if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
249       {
250         System.gc();
251         memused = (rt.maxMemory() - rt.totalMemory() + rt.freeMemory()); // free
252         // memory
253         // before
254         // load
255       }
256       loadtime = -System.currentTimeMillis();
257       Alignment al = null;
258
259       if (format.equalsIgnoreCase("Jalview"))
260       {
261         if (source != null)
262         {
263           // Tell the user (developer?) that this is going to cause a problem
264           System.err
265                   .println("IMPLEMENTATION ERROR: Cannot read consecutive Jalview XML projects from a stream.");
266           // We read the data anyway - it might make sense.
267         }
268         alignFrame = new Jalview2XML(raiseGUI).LoadJalviewAlign(file);
269       }
270       else
271       {
272         String error = AppletFormatAdapter.SUPPORTED_FORMATS;
273         if (FormatAdapter.isValidFormat(format))
274         {
275           try
276           {
277             if (source != null)
278             {
279               // read from the provided source
280               al = new FormatAdapter().readFromFile(source, format);
281             }
282             else
283             {
284
285               // open a new source and read from it
286               FormatAdapter fa = new FormatAdapter();
287               al = fa.readFile(file, protocol, format);
288               source = fa.afile; // keep reference for later if necessary.
289             }
290           } catch (java.io.IOException ex)
291           {
292             error = ex.getMessage();
293           }
294         }
295         else
296         {
297           if (format != null && format.length() > 7)
298           {
299             // ad hoc message in format.
300             error = format + "\n" + error;
301           }
302         }
303
304         if ((al != null) && (al.getHeight() > 0))
305         {
306           if (viewport != null)
307           {
308             // TODO: create undo object for this JAL-1101
309             for (int i = 0; i < al.getHeight(); i++)
310             {
311               viewport.getAlignment().addSequence(al.getSequenceAt(i));
312             }
313             viewport.firePropertyChange("alignment", null, viewport
314                     .getAlignment().getSequences());
315
316           }
317           else
318           {
319             alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
320                     AlignFrame.DEFAULT_HEIGHT);
321
322             alignFrame.statusBar.setText(MessageManager.formatMessage("label.successfully_loaded_file", new String[]{title}));
323
324             if (!protocol.equals(AppletFormatAdapter.PASTE))
325               alignFrame.setFileName(file, format);
326             if (raiseGUI)
327             {
328               // add the window to the GUI
329               // note - this actually should happen regardless of raiseGUI status in Jalview 3
330               // TODO: define 'virtual desktop' for benefit of headless scripts that perform queries to find the 'current working alignment'
331               Desktop.addInternalFrame(alignFrame, title,
332                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
333             }
334
335             try
336             {
337               alignFrame.setMaximum(jalview.bin.Cache.getDefault(
338                       "SHOW_FULLSCREEN", false));
339             } catch (java.beans.PropertyVetoException ex)
340             {
341             }
342           }
343         }
344         else
345         {
346           if (Desktop.instance != null)
347           {
348             Desktop.instance.stopLoading();
349           }
350
351           final String errorMessage = "Couldn't load file " + title + "\n"
352                   + error;
353           if (raiseGUI)
354           {
355             javax.swing.SwingUtilities.invokeLater(new Runnable()
356             {
357               public void run()
358               {
359                 JOptionPane.showInternalMessageDialog(Desktop.desktop,
360                         errorMessage, MessageManager.getString("label.error_loading_file"),
361                         JOptionPane.WARNING_MESSAGE);
362               }
363             });
364           }
365           else
366           {
367             System.err.println(errorMessage);
368           }
369         }
370       }
371
372       updateRecentlyOpened();
373
374     } catch (Exception er)
375     {
376       System.err.println("Exception whilst opening file '" + file);
377       er.printStackTrace();
378       if (raiseGUI)
379       {
380         javax.swing.SwingUtilities.invokeLater(new Runnable()
381         {
382           public void run()
383           {
384             javax.swing.JOptionPane.showInternalMessageDialog(
385                     Desktop.desktop, MessageManager.formatMessage("label.problems_opening_file", new String[]{file}),
386                     MessageManager.getString("label.file_open_error"),
387                     javax.swing.JOptionPane.WARNING_MESSAGE);
388           }
389         });
390       }
391       alignFrame = null;
392     } catch (OutOfMemoryError er)
393     {
394
395       er.printStackTrace();
396       alignFrame = null;
397       if (raiseGUI)
398       {
399         javax.swing.SwingUtilities.invokeLater(new Runnable()
400         {
401           public void run()
402           {
403             javax.swing.JOptionPane
404                     .showInternalMessageDialog(
405                             Desktop.desktop,
406                             "Out of memory loading file "
407                                     + file
408                                     + "!!"
409                                     + "\nSee help files for increasing Java Virtual Machine memory.",
410                             "Out of memory",
411                             javax.swing.JOptionPane.WARNING_MESSAGE);
412           }
413         });
414       }
415       System.err.println("Out of memory loading file " + file + "!!");
416
417     }
418     loadtime += System.currentTimeMillis();
419     // TODO: Estimate percentage of memory used by a newly loaded alignment -
420     // warn if more memory will be needed to work with it
421     // System.gc();
422     memused = memused
423             - (rt.maxMemory() - rt.totalMemory() + rt.freeMemory()); // difference
424     // in free
425     // memory
426     // after
427     // load
428     if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
429     {
430       if (alignFrame != null)
431       {
432         AlignmentI al = alignFrame.getViewport().getAlignment();
433
434         System.out.println("Loaded '" + title + "' in "
435                 + (loadtime / 1000.0) + "s, took an additional "
436                 + (1.0 * memused / (1024.0 * 1024.0)) + " MB ("
437                 + al.getHeight() + " seqs by " + al.getWidth() + " cols)");
438       }
439       else
440       {
441         // report that we didn't load anything probably due to an out of memory
442         // error
443         System.out.println("Failed to load '" + title + "' in "
444                 + (loadtime / 1000.0) + "s, took an additional "
445                 + (1.0 * memused / (1024.0 * 1024.0))
446                 + " MB (alignment is null)");
447       }
448     }
449     // remove the visual delay indicator
450     if (Desktop.instance != null)
451     {
452       Desktop.instance.stopLoading();
453     }
454
455   }
456
457   /*
458    * (non-Javadoc)
459    * 
460    * @see java.lang.Object#finalize()
461    */
462   protected void finalize() throws Throwable
463   {
464     source = null;
465     alignFrame = null;
466     viewport = null;
467     super.finalize();
468   }
469
470 }