JAL-1432 updated copyright notices
[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
28 public class FileLoader implements Runnable
29 {
30   String file;
31
32   String protocol;
33
34   String format;
35
36   FileParse source = null; // alternative specification of where data comes
37
38   // from
39
40   AlignViewport viewport;
41
42   AlignFrame alignFrame;
43
44   long loadtime;
45
46   long memused;
47
48   boolean raiseGUI = true;
49
50   /**
51    * default constructor always raised errors in GUI dialog boxes
52    */
53   public FileLoader()
54   {
55     this(true);
56   }
57
58   /**
59    * construct a Fileloader that may raise errors non-interactively
60    * 
61    * @param raiseGUI
62    *          true if errors are to be raised as GUI dialog boxes
63    */
64   public FileLoader(boolean raiseGUI)
65   {
66     this.raiseGUI = raiseGUI;
67   }
68
69   public void LoadFile(AlignViewport viewport, String file,
70           String protocol, String format)
71   {
72     this.viewport = viewport;
73     LoadFile(file, protocol, format);
74   }
75
76   public void LoadFile(String file, String protocol, String format)
77   {
78     this.file = file;
79     this.protocol = protocol;
80     this.format = format;
81
82     final Thread loader = new Thread(this);
83
84     SwingUtilities.invokeLater(new Runnable()
85     {
86       public void run()
87       {
88         loader.start();
89       }
90     });
91   }
92
93   /**
94    * Load a (file, protocol) source of unknown type
95    * 
96    * @param file
97    * @param protocol
98    */
99   public void LoadFile(String file, String protocol)
100   {
101     LoadFile(file, protocol, null);
102   }
103
104   /**
105    * Load alignment from (file, protocol) and wait till loaded
106    * 
107    * @param file
108    * @param protocol
109    * @return alignFrame constructed from file contents
110    */
111   public AlignFrame LoadFileWaitTillLoaded(String file, String protocol)
112   {
113     return LoadFileWaitTillLoaded(file, protocol, null);
114   }
115
116   /**
117    * Load alignment from (file, protocol) of type format and wait till loaded
118    * 
119    * @param file
120    * @param protocol
121    * @param format
122    * @return alignFrame constructed from file contents
123    */
124   public AlignFrame LoadFileWaitTillLoaded(String file, String protocol,
125           String format)
126   {
127     this.file = file;
128     this.protocol = protocol;
129     this.format = format;
130     return _LoadFileWaitTillLoaded();
131   }
132
133   /**
134    * Load alignment from FileParse source of type format and wait till loaded
135    * 
136    * @param source
137    * @param format
138    * @return alignFrame constructed from file contents
139    */
140   public AlignFrame LoadFileWaitTillLoaded(FileParse source, String format)
141   {
142     this.source = source;
143     file = source.getInFile();
144     protocol = source.type;
145     this.format = format;
146     return _LoadFileWaitTillLoaded();
147   }
148
149   /**
150    * start thread and wait until finished, then return the alignFrame that's
151    * (hopefully) been read.
152    * 
153    * @return
154    */
155   protected AlignFrame _LoadFileWaitTillLoaded()
156   {
157     Thread loader = new Thread(this);
158     loader.start();
159
160     while (loader.isAlive())
161     {
162       try
163       {
164         Thread.sleep(500);
165       } catch (Exception ex)
166       {
167       }
168     }
169
170     return alignFrame;
171   }
172
173   public void updateRecentlyOpened()
174   {
175     Vector recent = new Vector();
176     if (protocol.equals(FormatAdapter.PASTE))
177     {
178       // do nothing if the file was pasted in as text... there is no filename to
179       // refer to it as.
180       return;
181     }
182     String type = protocol.equals(FormatAdapter.FILE) ? "RECENT_FILE"
183             : "RECENT_URL";
184
185     String historyItems = jalview.bin.Cache.getProperty(type);
186
187     StringTokenizer st;
188
189     if (historyItems != null)
190     {
191       st = new StringTokenizer(historyItems, "\t");
192
193       while (st.hasMoreTokens())
194       {
195         recent.addElement(st.nextElement().toString().trim());
196       }
197     }
198
199     if (recent.contains(file))
200     {
201       recent.remove(file);
202     }
203
204     StringBuffer newHistory = new StringBuffer(file);
205     for (int i = 0; i < recent.size() && i < 10; i++)
206     {
207       newHistory.append("\t");
208       newHistory.append(recent.elementAt(i));
209     }
210
211     jalview.bin.Cache.setProperty(type, newHistory.toString());
212
213     if (protocol.equals(FormatAdapter.FILE))
214     {
215       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT", format);
216     }
217   }
218
219   public void run()
220   {
221     String title = protocol.equals(AppletFormatAdapter.PASTE) ? "Copied From Clipboard"
222             : file;
223     Runtime rt = Runtime.getRuntime();
224     try
225     {
226       if (Desktop.instance != null)
227       {
228         Desktop.instance.startLoading(file);
229       }
230       if (format == null)
231       {
232         // just in case the caller didn't identify the file for us
233         if (source != null)
234         {
235           format = new IdentifyFile().Identify(source, false); // identify
236           // stream and
237           // rewind rather
238           // than close
239         }
240         else
241         {
242           format = new IdentifyFile().Identify(file, protocol);
243         }
244       }
245       // TODO: cache any stream datasources as a temporary file (eg. PDBs
246       // retrieved via URL)
247       if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
248       {
249         System.gc();
250         memused = (rt.maxMemory() - rt.totalMemory() + rt.freeMemory()); // free
251         // memory
252         // before
253         // load
254       }
255       loadtime = -System.currentTimeMillis();
256       Alignment al = null;
257
258       if (format.equalsIgnoreCase("Jalview"))
259       {
260         if (source != null)
261         {
262           // Tell the user (developer?) that this is going to cause a problem
263           System.err
264                   .println("IMPLEMENTATION ERROR: Cannot read consecutive Jalview XML projects from a stream.");
265           // We read the data anyway - it might make sense.
266         }
267         alignFrame = new Jalview2XML(raiseGUI).LoadJalviewAlign(file);
268       }
269       else
270       {
271         String error = AppletFormatAdapter.SUPPORTED_FORMATS;
272         if (FormatAdapter.isValidFormat(format))
273         {
274           try
275           {
276             if (source != null)
277             {
278               // read from the provided source
279               al = new FormatAdapter().readFromFile(source, format);
280             }
281             else
282             {
283
284               // open a new source and read from it
285               FormatAdapter fa = new FormatAdapter();
286               al = fa.readFile(file, protocol, format);
287               source = fa.afile; // keep reference for later if necessary.
288             }
289           } catch (java.io.IOException ex)
290           {
291             error = ex.getMessage();
292           }
293         }
294         else
295         {
296           if (format != null && format.length() > 7)
297           {
298             // ad hoc message in format.
299             error = format + "\n" + error;
300           }
301         }
302
303         if ((al != null) && (al.getHeight() > 0))
304         {
305           if (viewport != null)
306           {
307             // TODO: create undo object for this JAL-1101
308             for (int i = 0; i < al.getHeight(); i++)
309             {
310               viewport.getAlignment().addSequence(al.getSequenceAt(i));
311             }
312             viewport.firePropertyChange("alignment", null, viewport
313                     .getAlignment().getSequences());
314
315           }
316           else
317           {
318             alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
319                     AlignFrame.DEFAULT_HEIGHT);
320
321             alignFrame.statusBar.setText("Successfully loaded file "
322                     + 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, "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, "Encountered problems opening " + file
386                             + "!!", "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 }