javatidy
[jalview.git] / src / jalview / gui / SequenceFetcher.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.gui;
19
20 import java.util.*;
21 import java.util.List;
22
23 import java.awt.*;
24 import java.awt.event.*;
25
26 import javax.swing.*;
27 import javax.swing.tree.DefaultMutableTreeNode;
28 import jalview.datamodel.*;
29 import jalview.io.*;
30 import jalview.util.DBRefUtils;
31 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
32 import jalview.ws.seqfetcher.DbSourceProxy;
33 import java.awt.BorderLayout;
34
35 public class SequenceFetcher extends JPanel implements Runnable
36 {
37   // ASequenceFetcher sfetch;
38   JInternalFrame frame;
39
40   IProgressIndicator guiWindow;
41
42   AlignFrame alignFrame;
43
44   StringBuffer result;
45
46   final String noDbSelected = "-- Select Database --";
47
48   private static jalview.ws.SequenceFetcher sfetch = null;
49
50   private static long lastDasSourceRegistry = -3;
51
52   private static DasSourceRegistryI dasRegistry = null;
53
54   private static boolean _initingFetcher = false;
55
56   private static Thread initingThread = null;
57
58   /**
59    * Blocking method that initialises and returns the shared instance of the
60    * SequenceFetcher client
61    *
62    * @param guiWindow
63    *          - where the initialisation delay message should be shown
64    * @return the singleton instance of the sequence fetcher client
65    */
66   public static jalview.ws.SequenceFetcher getSequenceFetcherSingleton(
67           final IProgressIndicator guiWindow)
68   {
69     if (_initingFetcher && initingThread != null && initingThread.isAlive())
70     {
71       if (guiWindow != null)
72       {
73         guiWindow.setProgressBar(
74                 "Waiting for Sequence Database Fetchers to initialise",
75                 Thread.currentThread().hashCode());
76       }
77       // initting happening on another thread - so wait around to see if it
78       // finishes.
79       while (_initingFetcher && initingThread != null
80               && initingThread.isAlive())
81       {
82         try
83         {
84           Thread.sleep(10);
85         } catch (Exception e)
86         {
87         }
88         ;
89       }
90       if (guiWindow != null)
91       {
92         guiWindow.setProgressBar(
93                 "Waiting for Sequence Database Fetchers to initialise",
94                 Thread.currentThread().hashCode());
95       }
96     }
97     if (sfetch == null
98             || dasRegistry != jalview.bin.Cache.getDasSourceRegistry()
99             || lastDasSourceRegistry != (jalview.bin.Cache
100                     .getDasSourceRegistry().getDasRegistryURL() + jalview.bin.Cache
101                     .getDasSourceRegistry().getLocalSourceString())
102                     .hashCode())
103     {
104       _initingFetcher = true;
105       initingThread = Thread.currentThread();
106       /**
107        * give a visual indication that sequence fetcher construction is occuring
108        */
109       if (guiWindow != null)
110       {
111         guiWindow.setProgressBar("Initialising Sequence Database Fetchers",
112                 Thread.currentThread().hashCode());
113       }
114       dasRegistry = jalview.bin.Cache.getDasSourceRegistry();
115       dasRegistry.refreshSources();
116
117       jalview.ws.SequenceFetcher sf = new jalview.ws.SequenceFetcher();
118       if (guiWindow != null)
119       {
120         guiWindow.setProgressBar("Initialising Sequence Database Fetchers",
121                 Thread.currentThread().hashCode());
122       }
123       lastDasSourceRegistry = (dasRegistry.getDasRegistryURL() + dasRegistry
124               .getLocalSourceString()).hashCode();
125       sfetch = sf;
126       _initingFetcher = false;
127       initingThread = null;
128     }
129     return sfetch;
130   }
131
132   public SequenceFetcher(IProgressIndicator guiIndic)
133   {
134     final IProgressIndicator guiWindow = guiIndic;
135     final SequenceFetcher us = this;
136     // launch initialiser thread
137     Thread sf = new Thread(new Runnable()
138     {
139
140       @Override
141       public void run()
142       {
143         if (getSequenceFetcherSingleton(guiWindow) != null)
144         {
145           us.initGui(guiWindow);
146         }
147         else
148         {
149           javax.swing.SwingUtilities.invokeLater(new Runnable()
150           {
151             @Override
152             public void run()
153             {
154               JOptionPane
155                       .showInternalMessageDialog(
156                               Desktop.desktop,
157                               "Could not create the sequence fetcher client. Check error logs for details.",
158                               "Couldn't create SequenceFetcher",
159                               JOptionPane.ERROR_MESSAGE);
160             }
161           });
162
163           // raise warning dialog
164         }
165       }
166     });
167     sf.start();
168   }
169
170   private class DatabaseAuthority extends DefaultMutableTreeNode
171   {
172
173   };
174
175   private class DatabaseSource extends DefaultMutableTreeNode
176   {
177
178   };
179
180   /**
181    * called by thread spawned by constructor
182    *
183    * @param guiWindow
184    */
185   private void initGui(IProgressIndicator guiWindow)
186   {
187     this.guiWindow = guiWindow;
188     if (guiWindow instanceof AlignFrame)
189     {
190       alignFrame = (AlignFrame) guiWindow;
191     }
192     database = new JDatabaseTree(sfetch);
193     try
194     {
195       jbInit();
196     } catch (Exception ex)
197     {
198       ex.printStackTrace();
199     }
200
201     frame = new JInternalFrame();
202     frame.setContentPane(this);
203     if (new jalview.util.Platform().isAMac())
204     {
205       Desktop.addInternalFrame(frame, getFrameTitle(), 400, 240);
206     }
207     else
208     {
209       Desktop.addInternalFrame(frame, getFrameTitle(), 400, 180);
210     }
211   }
212
213   private String getFrameTitle()
214   {
215     return ((alignFrame == null) ? "New " : "Additional ")
216             + "Sequence Fetcher";
217   }
218
219   private void jbInit() throws Exception
220   {
221     this.setLayout(borderLayout2);
222
223     database.setFont(JvSwingUtils.getLabelFont());
224     dbeg.setFont(new java.awt.Font("Verdana", Font.BOLD, 11));
225     jLabel1.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
226     jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
227     jLabel1.setText("Separate multiple accession ids with semi colon \";\"");
228
229     replacePunctuation.setHorizontalAlignment(SwingConstants.CENTER);
230     replacePunctuation
231             .setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
232     replacePunctuation.setText("Replace commas with semi-colons");
233     ok.setText("OK");
234     ok.addActionListener(new ActionListener()
235     {
236       @Override
237       public void actionPerformed(ActionEvent e)
238       {
239         ok_actionPerformed();
240       }
241     });
242     clear.setText("Clear");
243     clear.addActionListener(new ActionListener()
244     {
245       @Override
246       public void actionPerformed(ActionEvent e)
247       {
248         clear_actionPerformed();
249       }
250     });
251
252     example.setText("Example");
253     example.addActionListener(new ActionListener()
254     {
255       @Override
256       public void actionPerformed(ActionEvent e)
257       {
258         example_actionPerformed();
259       }
260     });
261     close.setText("Close");
262     close.addActionListener(new ActionListener()
263     {
264       @Override
265       public void actionPerformed(ActionEvent e)
266       {
267         close_actionPerformed(e);
268       }
269     });
270     textArea.setFont(JvSwingUtils.getLabelFont());
271     textArea.setLineWrap(true);
272     textArea.addKeyListener(new KeyAdapter()
273     {
274       @Override
275       public void keyPressed(KeyEvent e)
276       {
277         if (e.getKeyCode() == KeyEvent.VK_ENTER)
278           ok_actionPerformed();
279       }
280     });
281     jPanel3.setLayout(borderLayout1);
282     borderLayout1.setVgap(5);
283     jPanel1.add(ok);
284     jPanel1.add(example);
285     jPanel1.add(clear);
286     jPanel1.add(close);
287     jPanel3.add(jPanel2, java.awt.BorderLayout.CENTER);
288     jPanel2.setLayout(borderLayout3);
289     databaseButt = database.getDatabaseSelectorButton();
290     databaseButt.setFont(JvSwingUtils.getLabelFont());
291     database.addActionListener(new ActionListener()
292     {
293
294       @Override
295       public void actionPerformed(ActionEvent e)
296       {
297         try
298         {
299           databaseButt.setText(database.getSelectedItem()
300                   + (database.getSelectedSources().size() > 1 ? " (and "
301                           + database.getSelectedSources().size()
302                           + " others)" : ""));
303           String eq = database.getExampleQueries();
304           dbeg.setText("Example query: " + eq);
305           boolean enablePunct=!(eq != null && eq.indexOf(",") > -1);
306           for (DbSourceProxy dbs:database.getSelectedSources()) {
307             if (dbs instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
308             {
309               enablePunct = false;
310               break;
311             }
312           }
313           replacePunctuation.setEnabled(enablePunct);
314
315         } catch (Exception ex)
316         {
317           dbeg.setText("");
318           replacePunctuation.setEnabled(true);
319         }
320         jPanel2.repaint();
321       }
322     });
323     dbeg.setText("");
324     jPanel2.add(databaseButt, java.awt.BorderLayout.NORTH);
325     jPanel2.add(dbeg, java.awt.BorderLayout.CENTER);
326     JPanel jPanel2a = new JPanel(new BorderLayout());
327     jPanel2a.add(jLabel1, java.awt.BorderLayout.NORTH);
328     jPanel2a.add(replacePunctuation, java.awt.BorderLayout.SOUTH);
329     jPanel2.add(jPanel2a, java.awt.BorderLayout.SOUTH);
330     // jPanel2.setPreferredSize(new Dimension())
331     jPanel3.add(jScrollPane1, java.awt.BorderLayout.CENTER);
332     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
333     this.add(jPanel3, java.awt.BorderLayout.CENTER);
334     this.add(jPanel2, java.awt.BorderLayout.NORTH);
335     jScrollPane1.getViewport().add(textArea);
336
337   }
338
339   protected void example_actionPerformed()
340   {
341     DbSourceProxy db = null;
342     try
343     {
344       textArea.setText(database.getExampleQueries());
345     } catch (Exception ex)
346     {
347     }
348     jPanel3.repaint();
349   }
350
351   protected void clear_actionPerformed()
352   {
353     textArea.setText("");
354     jPanel3.repaint();
355   }
356
357   JLabel dbeg = new JLabel();
358
359   JDatabaseTree database;
360
361   JButton databaseButt;
362
363   JLabel jLabel1 = new JLabel();
364
365   JCheckBox replacePunctuation = new JCheckBox();
366
367   JButton ok = new JButton();
368
369   JButton clear = new JButton();
370
371   JButton example = new JButton();
372
373   JButton close = new JButton();
374
375   JPanel jPanel1 = new JPanel();
376
377   JTextArea textArea = new JTextArea();
378
379   JScrollPane jScrollPane1 = new JScrollPane();
380
381   JPanel jPanel2 = new JPanel();
382
383   JPanel jPanel3 = new JPanel();
384
385   JPanel jPanel4 = new JPanel();
386
387   BorderLayout borderLayout1 = new BorderLayout();
388
389   BorderLayout borderLayout2 = new BorderLayout();
390
391   BorderLayout borderLayout3 = new BorderLayout();
392
393   public void close_actionPerformed(ActionEvent e)
394   {
395     try
396     {
397       frame.setClosed(true);
398     } catch (Exception ex)
399     {
400     }
401   }
402
403   public void ok_actionPerformed()
404   {
405     database.setEnabled(false);
406     textArea.setEnabled(false);
407     ok.setEnabled(false);
408     close.setEnabled(false);
409
410     Thread worker = new Thread(this);
411     worker.start();
412   }
413
414   private void resetDialog()
415   {
416     database.setEnabled(true);
417     textArea.setEnabled(true);
418     ok.setEnabled(true);
419     close.setEnabled(true);
420   }
421
422   @Override
423   public void run()
424   {
425     String error = "";
426     if (!database.hasSelection())
427     {
428       error += "Please select the source database\n";
429     }
430     // TODO: make this transformation more configurable
431     com.stevesoft.pat.Regex empty;
432     if (replacePunctuation.isEnabled() && replacePunctuation.isSelected())
433     {
434       empty = new com.stevesoft.pat.Regex(
435       // replace commas and spaces with a semicolon
436               "(\\s|[,; ])+", ";");
437     }
438     else
439     {
440       // just turn spaces and semicolons into single semicolons
441       empty = new com.stevesoft.pat.Regex("(\\s|[; ])+", ";");
442     }
443     textArea.setText(empty.replaceAll(textArea.getText()));
444     // see if there's anthing to search with
445     if (!new com.stevesoft.pat.Regex("[A-Za-z0-9_.]").search(textArea
446             .getText()))
447     {
448       error += "Please enter a (semi-colon separated list of) database id(s)";
449     }
450     if (error.length() > 0)
451     {
452       showErrorMessage(error);
453       resetDialog();
454       return;
455     }
456     // indicate if successive sources should be merged into one alignment.
457     boolean addToLast=false;
458     ArrayList<String> aresultq = new ArrayList<String>(), presultTitle = new ArrayList<String>();
459     ArrayList<AlignmentI> presult = new ArrayList<AlignmentI>(), aresult = new ArrayList<AlignmentI>();
460     Iterator<DbSourceProxy> proxies = database.getSelectedSources()
461             .iterator();
462     String[] qries;
463     List<String> nextfetch = Arrays.asList(qries = textArea.getText()
464             .split(";"));
465     Iterator<String> en = Arrays.asList(new String[0]).iterator();
466     int nqueries = qries.length;
467     while (proxies.hasNext() && (en.hasNext() || nextfetch.size() > 0))
468     {
469       if (!en.hasNext() && nextfetch.size() > 0)
470       {
471         en = nextfetch.iterator();
472         nqueries = nextfetch.size();
473         // save the remaining queries in the original array
474         qries = nextfetch.toArray(new String[nqueries]);
475         nextfetch = new ArrayList<String>();
476       }
477
478       DbSourceProxy proxy = proxies.next();
479       boolean isAliSource = false;
480       try
481       {
482         // update status
483         guiWindow.setProgressBar("Fetching " + nqueries
484                 + " sequence queries from " + proxy.getDbName(), Thread
485                 .currentThread().hashCode());
486         isAliSource = proxy.isA(DBRefSource.ALIGNMENTDB);
487         if (proxy.getAccessionSeparator() == null)
488         {
489           while (en.hasNext())
490           {
491             String item = en.next();
492             try
493             {
494               if (aresult != null)
495               {
496                 try
497                 {
498                   // give the server a chance to breathe
499                   Thread.sleep(5);
500                 } catch (Exception e)
501                 {
502                   //
503                 }
504
505               }
506
507               AlignmentI indres = null;
508               try
509               {
510                 indres = proxy.getSequenceRecords(item);
511               } catch (OutOfMemoryError oome)
512               {
513                 new OOMWarning("fetching " + item + " from "
514                         + proxy.getDbName(), oome, this);
515               }
516               if (indres != null)
517               {
518                 aresultq.add(item);
519                 aresult.add(indres);
520               }
521               else
522               {
523                 nextfetch.add(item);
524               }
525             } catch (Exception e)
526             {
527               jalview.bin.Cache.log.info("Error retrieving " + item
528                       + " from " + proxy.getDbName(), e);
529               nextfetch.add(item);
530             }
531           }
532         }
533         else
534         {
535           StringBuffer multiacc = new StringBuffer();
536           ArrayList<String> tosend = new ArrayList<String>();
537           while (en.hasNext())
538           {
539             String nel = en.next();
540             tosend.add(nel);
541             multiacc.append(nel);
542             if (en.hasNext())
543             {
544               multiacc.append(proxy.getAccessionSeparator());
545             }
546           }
547           try
548           {
549             AlignmentI rslt;
550             SequenceI[] rs;
551             List<String> nores = new ArrayList<String>();
552             rslt = proxy.getSequenceRecords(multiacc.toString());
553             if (rslt == null || rslt.getHeight() == 0)
554             {
555               // no results - pass on all queries to next source
556               nextfetch.addAll(tosend);
557             }
558             else
559             {
560               aresultq.add(multiacc.toString());
561               aresult.add(rslt);
562
563               rs = rslt.getSequencesArray();
564               // search for each query in the dbrefs associated with each
565               // sequence
566               // returned.
567               // ones we do not find will be used to query next source (if any)
568               for (String q : tosend)
569               {
570                 DBRefEntry dbr = new DBRefEntry(), found[] = null;
571                 dbr.setSource(proxy.getDbSource());
572                 boolean rfound = false;
573                 for (int r = 0; r < rs.length; r++)
574                 {
575                   if (rs[r] != null
576                           && (found = DBRefUtils.searchRefs(
577                                   rs[r].getDBRef(), dbr)) != null
578                           && found.length > 0)
579                   {
580                     rfound = true;
581                     rs[r] = null;
582                     continue;
583                   }
584                 }
585                 if (!rfound)
586                 {
587                   nextfetch.add(q);
588                 }
589               }
590             }
591           } catch (OutOfMemoryError oome)
592           {
593             new OOMWarning("fetching " + multiacc + " from "
594                     + database.getSelectedItem(), oome, this);
595           }
596         }
597
598       } catch (Exception e)
599       {
600         showErrorMessage("Error retrieving " + textArea.getText()
601                 + " from " + database.getSelectedItem());
602         // error
603         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
604         System.err.println("Retrieval failed for source ='"
605                 + database.getSelectedItem() + "' and query\n'"
606                 + textArea.getText() + "'\n");
607         e.printStackTrace();
608       } catch (OutOfMemoryError e)
609       {
610         // resets dialog box - so we don't use OOMwarning here.
611         showErrorMessage("Out of Memory when retrieving "
612                 + textArea.getText()
613                 + " from "
614                 + database.getSelectedItem()
615                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
616         e.printStackTrace();
617       } catch (Error e)
618       {
619         showErrorMessage("Serious Error retrieving " + textArea.getText()
620                 + " from " + database.getSelectedItem());
621         e.printStackTrace();
622       }
623       // Stack results ready for opening in alignment windows
624       if (aresult != null && aresult.size() > 0)
625       {
626         AlignmentI ar = null;
627         if (isAliSource)
628         {
629           addToLast=false;
630           // new window for each result
631           while (aresult.size() > 0)
632           {
633             presult.add(aresult.remove(0));
634             presultTitle.add(aresultq.remove(0) + " "
635                     + getDefaultRetrievalTitle());
636           }
637         }
638         else
639         {
640           String titl=null;
641           if (addToLast && presult.size()>0)
642           {
643             ar=presult.remove(presult.size()-1);
644             titl=presultTitle.remove(presultTitle.size()-1);
645           }
646           // concatenate all results in one window
647           while (aresult.size() > 0)
648           {
649             if (ar == null)
650             {
651               ar = aresult.remove(0);
652             }
653             else
654             {
655               ar.append(aresult.remove(0));
656             }
657             ;
658           }
659           addToLast=true;
660           presult.add(ar);
661           presultTitle.add(titl);
662         }
663       }
664       guiWindow.setProgressBar("Finished querying", Thread.currentThread().hashCode());
665     }
666     guiWindow.setProgressBar((presult.size()>0) ? "Parsing results.":"Processing ..", Thread.currentThread().hashCode());
667     // process results
668     while (presult.size() > 0)
669     {
670       parseResult(presult.remove(0), presultTitle.remove(0), null);
671     }
672     // only remove visual delay after we finished parsing.
673     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
674     if (nextfetch.size() > 0)
675     {
676       StringBuffer sb = new StringBuffer();
677       sb.append("Didn't retrieve the following "
678               + (nextfetch.size() == 1 ? "query" : nextfetch.size()
679                       + " queries") + ": \n");
680       int l = sb.length(),lr=0;
681       for (String s : nextfetch)
682       {
683         if (l != sb.length())
684         {
685           sb.append("; ");
686         }
687         if (lr-sb.length()>40)
688         {
689           sb.append("\n");
690         }
691         sb.append(s);
692       }
693       showErrorMessage(sb.toString());
694     }
695     resetDialog();
696   }
697
698   AlignmentI parseResult(String result, String title)
699   {
700     String format = new IdentifyFile().Identify(result, "Paste");
701     Alignment sequences = null;
702     if (FormatAdapter.isValidFormat(format))
703     {
704       sequences = null;
705       try
706       {
707         sequences = new FormatAdapter().readFile(result.toString(),
708                 "Paste", format);
709       } catch (Exception ex)
710       {
711       }
712
713       if (sequences != null)
714       {
715         return parseResult(sequences, title, format);
716       }
717     }
718     else
719     {
720       showErrorMessage("Error retrieving " + textArea.getText() + " from "
721               + database.getSelectedItem());
722     }
723
724     return null;
725   }
726
727   /**
728    *
729    * @return a standard title for any results retrieved using the currently
730    *         selected source and settings
731    */
732   public String getDefaultRetrievalTitle()
733   {
734     return "Retrieved from " + database.getSelectedItem();
735   }
736
737   AlignmentI parseResult(AlignmentI al, String title,
738           String currentFileFormat)
739   {
740
741     if (al != null && al.getHeight() > 0)
742     {
743       if (alignFrame == null)
744       {
745         AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
746                 AlignFrame.DEFAULT_HEIGHT);
747         if (currentFileFormat != null)
748         {
749           af.currentFileFormat = currentFileFormat; // WHAT IS THE DEFAULT
750           // FORMAT FOR
751           // NON-FormatAdapter Sourced
752           // Alignments?
753         }
754
755         if (title == null)
756         {
757           title = getDefaultRetrievalTitle();
758         }
759         SequenceFeature[] sfs = null;
760         List<SequenceI> alsqs;
761         synchronized (alsqs = al.getSequences())
762         {
763           for (SequenceI sq : alsqs)
764           {
765             if ((sfs = (sq).getDatasetSequence().getSequenceFeatures()) != null)
766             {
767               if (sfs.length > 0)
768               {
769                 af.setShowSeqFeatures(true);
770                 break;
771               }
772             }
773
774           }
775         }
776         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
777                 AlignFrame.DEFAULT_HEIGHT);
778
779         af.statusBar.setText("Successfully pasted alignment file");
780
781         try
782         {
783           af.setMaximum(jalview.bin.Cache.getDefault("SHOW_FULLSCREEN",
784                   false));
785         } catch (Exception ex)
786         {
787         }
788       }
789       else
790       {
791         for (int i = 0; i < al.getHeight(); i++)
792         {
793           alignFrame.viewport.getAlignment().addSequence(
794                   al.getSequenceAt(i)); // this
795           // also
796           // creates
797           // dataset
798           // sequence
799           // entries
800         }
801         alignFrame.viewport.setEndSeq(alignFrame.viewport.getAlignment()
802                 .getHeight());
803         alignFrame.viewport.getAlignment().getWidth();
804         alignFrame.viewport.firePropertyChange("alignment", null,
805                 alignFrame.viewport.getAlignment().getSequences());
806       }
807     }
808     return al;
809   }
810
811   void showErrorMessage(final String error)
812   {
813     resetDialog();
814     javax.swing.SwingUtilities.invokeLater(new Runnable()
815     {
816       @Override
817       public void run()
818       {
819         JOptionPane.showInternalMessageDialog(Desktop.desktop, error,
820                 "Error Retrieving Data", JOptionPane.WARNING_MESSAGE);
821       }
822     });
823   }
824 }