formatting
[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       Alignment 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
332             Desktop.addInternalFrame(alignFrame, title,
333                     AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
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 }