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