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