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