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