todo: blocked by JAL-1104
[jalview.git] / src / jalview / io / FileLoader.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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
326             Desktop.addInternalFrame(alignFrame, title,
327                     AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
328
329             try
330             {
331               alignFrame.setMaximum(jalview.bin.Cache.getDefault(
332                       "SHOW_FULLSCREEN", false));
333             } catch (java.beans.PropertyVetoException ex)
334             {
335             }
336           }
337         }
338         else
339         {
340           if (Desktop.instance != null)
341           {
342             Desktop.instance.stopLoading();
343           }
344
345           final String errorMessage = "Couldn't load file " + title + "\n"
346                   + error;
347           if (raiseGUI)
348           {
349             javax.swing.SwingUtilities.invokeLater(new Runnable()
350             {
351               public void run()
352               {
353                 JOptionPane.showInternalMessageDialog(Desktop.desktop,
354                         errorMessage, "Error loading file",
355                         JOptionPane.WARNING_MESSAGE);
356               }
357             });
358           }
359           else
360           {
361             System.err.println(errorMessage);
362           }
363         }
364       }
365
366       updateRecentlyOpened();
367
368     } catch (Exception er)
369     {
370       System.err.println("Exception whilst opening file '" + file);
371       er.printStackTrace();
372       if (raiseGUI)
373       {
374         javax.swing.SwingUtilities.invokeLater(new Runnable()
375         {
376           public void run()
377           {
378             javax.swing.JOptionPane.showInternalMessageDialog(
379                     Desktop.desktop, "Encountered problems opening " + file
380                             + "!!", "File open error",
381                     javax.swing.JOptionPane.WARNING_MESSAGE);
382           }
383         });
384       }
385       alignFrame = null;
386     } catch (OutOfMemoryError er)
387     {
388
389       er.printStackTrace();
390       alignFrame = null;
391       if (raiseGUI)
392       {
393         javax.swing.SwingUtilities.invokeLater(new Runnable()
394         {
395           public void run()
396           {
397             javax.swing.JOptionPane
398                     .showInternalMessageDialog(
399                             Desktop.desktop,
400                             "Out of memory loading file "
401                                     + file
402                                     + "!!"
403                                     + "\nSee help files for increasing Java Virtual Machine memory.",
404                             "Out of memory",
405                             javax.swing.JOptionPane.WARNING_MESSAGE);
406           }
407         });
408       }
409       System.err.println("Out of memory loading file " + file + "!!");
410
411     }
412     loadtime += System.currentTimeMillis();
413     // TODO: Estimate percentage of memory used by a newly loaded alignment -
414     // warn if more memory will be needed to work with it
415     // System.gc();
416     memused = memused
417             - (rt.maxMemory() - rt.totalMemory() + rt.freeMemory()); // difference
418     // in free
419     // memory
420     // after
421     // load
422     if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
423     {
424       if (alignFrame != null)
425       {
426         AlignmentI al = alignFrame.getViewport().getAlignment();
427
428         System.out.println("Loaded '" + title + "' in "
429                 + (loadtime / 1000.0) + "s, took an additional "
430                 + (1.0 * memused / (1024.0 * 1024.0)) + " MB ("
431                 + al.getHeight() + " seqs by " + al.getWidth() + " cols)");
432       }
433       else
434       {
435         // report that we didn't load anything probably due to an out of memory
436         // error
437         System.out.println("Failed to load '" + title + "' in "
438                 + (loadtime / 1000.0) + "s, took an additional "
439                 + (1.0 * memused / (1024.0 * 1024.0))
440                 + " MB (alignment is null)");
441       }
442     }
443     // remove the visual delay indicator
444     if (Desktop.instance != null)
445     {
446       Desktop.instance.stopLoading();
447     }
448
449   }
450
451   /*
452    * (non-Javadoc)
453    * 
454    * @see java.lang.Object#finalize()
455    */
456   protected void finalize() throws Throwable
457   {
458     source = null;
459     alignFrame = null;
460     viewport = null;
461     super.finalize();
462   }
463
464 }