JAL-1620 version bump and release notes
[jalview.git] / src / jalview / gui / JDatabaseTree.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.util.MessageManager;
25 import jalview.ws.seqfetcher.DbSourceProxy;
26
27 import java.awt.BorderLayout;
28 import java.awt.Component;
29 import java.awt.Container;
30 import java.awt.Dimension;
31 import java.awt.FlowLayout;
32 import java.awt.GridLayout;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.KeyEvent;
36 import java.awt.event.KeyListener;
37 import java.util.ArrayList;
38 import java.util.HashSet;
39 import java.util.Hashtable;
40 import java.util.List;
41 import java.util.Vector;
42
43 import javax.swing.JButton;
44 import javax.swing.JFrame;
45 import javax.swing.JLabel;
46 import javax.swing.JPanel;
47 import javax.swing.JScrollPane;
48 import javax.swing.JTree;
49 import javax.swing.event.TreeSelectionEvent;
50 import javax.swing.event.TreeSelectionListener;
51 import javax.swing.tree.DefaultMutableTreeNode;
52 import javax.swing.tree.DefaultTreeCellRenderer;
53 import javax.swing.tree.DefaultTreeModel;
54 import javax.swing.tree.TreeCellRenderer;
55 import javax.swing.tree.TreeNode;
56 import javax.swing.tree.TreePath;
57 import javax.swing.tree.TreeSelectionModel;
58
59 public class JDatabaseTree extends JalviewDialog implements KeyListener
60 {
61   boolean allowMultiSelections = false;
62
63   JButton getDatabaseSelectorButton()
64   {
65     final JButton viewdbs = new JButton(
66             MessageManager.getString("action.select_ddbb"));
67     viewdbs.addActionListener(new ActionListener()
68     {
69
70       @Override
71       public void actionPerformed(ActionEvent arg0)
72       {
73         showDialog(null);
74       }
75     });
76     return viewdbs;
77   }
78
79   JScrollPane svp;
80
81   JTree dbviews;
82
83   private jalview.ws.SequenceFetcher sfetcher;
84
85   private JLabel dbstatus, dbstatex;
86
87   public JDatabaseTree(jalview.ws.SequenceFetcher sfetch)
88   {
89     initDialogFrame(this, true, false, MessageManager.getString("label.select_database_retrieval_source"),
90             650, 490);
91     /*
92      * Dynamically generated database list will need a translation function from
93      * internal source to externally distinct names. UNIPROT and UP_NAME are
94      * identical DB sources, and should be collapsed.
95      */
96     DefaultMutableTreeNode tn = null, root = new DefaultMutableTreeNode();
97     Hashtable<String, DefaultMutableTreeNode> source = new Hashtable<String, DefaultMutableTreeNode>();
98     sfetcher = sfetch;
99     String dbs[] = sfetch.getSupportedDb();
100     Hashtable<String, String> ht = new Hashtable<String, String>();
101     for (int i = 0; i < dbs.length; i++)
102     {
103       tn = source.get(dbs[i]);
104       List<DbSourceProxy> srcs = sfetch.getSourceProxy(dbs[i]);
105       if (tn == null)
106       {
107         source.put(dbs[i], tn = new DefaultMutableTreeNode(dbs[i], true));
108       }
109       for (DbSourceProxy dbp : srcs)
110       {
111         if (ht.get(dbp.getDbName()) == null)
112         {
113           tn.add(new DefaultMutableTreeNode(dbp, false));
114           ht.put(dbp.getDbName(), dbp.getDbName());
115         }
116         else
117         {
118           System.err.println("dupe ig for : " + dbs[i] + " \t"
119                   + dbp.getDbName() + " (" + dbp.getDbSource() + ")");
120           source.remove(tn);
121         }
122       }
123     }
124     for (int i = 0; i < dbs.length; i++)
125     {
126       tn = source.get(dbs[i]);
127       if (tn == null)
128       {
129         continue;
130       }
131       if (tn.getChildCount() == 1)
132       {
133         DefaultMutableTreeNode ttn = (DefaultMutableTreeNode) tn
134                 .getChildAt(0);
135         // remove nodes with only one child
136         tn.setUserObject(ttn.getUserObject());
137         tn.removeAllChildren();
138         source.put(dbs[i], tn);
139         tn.setAllowsChildren(false);
140       }
141       root.add(tn);
142     }
143     // and sort the tree
144     sortTreeNodes(root);
145     svp = new JScrollPane();
146     // svp.setAutoscrolls(true);
147     dbviews = new JTree(new DefaultTreeModel(root, false));
148     dbviews.setCellRenderer(new DbTreeRenderer(this));
149
150     dbviews.getSelectionModel().setSelectionMode(
151             TreeSelectionModel.SINGLE_TREE_SELECTION);
152     svp.getViewport().setView(dbviews);
153     // svp.getViewport().setMinimumSize(new Dimension(300,200));
154     // svp.setSize(300,250);
155     // JPanel panel=new JPanel();
156     // panel.setSize(new Dimension(350,220));
157     // panel.add(svp);
158     dbviews.addTreeSelectionListener(new TreeSelectionListener()
159     {
160
161       @Override
162       public void valueChanged(TreeSelectionEvent arg0)
163       {
164         _setSelectionState();
165       }
166     });
167     JPanel jc = new JPanel(new BorderLayout()), j = new JPanel(
168             new FlowLayout());
169     jc.add(svp, BorderLayout.CENTER);
170
171     java.awt.Font f;
172     // TODO: make the panel stay a fixed size for longest dbname+example set.
173     JPanel dbstat = new JPanel(new GridLayout(2, 1));
174     dbstatus = new JLabel(" "); // set the height correctly for layout
175     dbstatus.setFont(f = JvSwingUtils.getLabelFont(false, true));
176     dbstatus.setSize(new Dimension(290, 50));
177     dbstatex = new JLabel(" ");
178     dbstatex.setFont(f);
179     dbstatex.setSize(new Dimension(290, 50));
180     dbstat.add(dbstatus);
181     dbstat.add(dbstatex);
182     jc.add(dbstat, BorderLayout.SOUTH);
183     jc.validate();
184     // j.setPreferredSize(new Dimension(300,50));
185     add(jc, BorderLayout.CENTER);
186     j.add(ok);
187     j.add(cancel);
188     add(j, BorderLayout.SOUTH);
189     dbviews.addKeyListener(this);
190     validate();
191   }
192
193   private void sortTreeNodes(DefaultMutableTreeNode root)
194   {
195     if (root.getChildCount() == 0)
196     {
197       return;
198     }
199     int count = root.getChildCount();
200     String[] names = new String[count];
201     DefaultMutableTreeNode[] nodes = new DefaultMutableTreeNode[count];
202     for (int i = 0; i < count; i++)
203     {
204       TreeNode node = root.getChildAt(i);
205       if (node instanceof DefaultMutableTreeNode)
206       {
207         DefaultMutableTreeNode child = (DefaultMutableTreeNode) node;
208         nodes[i] = child;
209         if (child.getUserObject() instanceof DbSourceProxy)
210         {
211           names[i] = ((DbSourceProxy) child.getUserObject()).getDbName()
212                   .toLowerCase();
213         }
214         else
215         {
216           names[i] = ((String) child.getUserObject()).toLowerCase();
217           sortTreeNodes(child);
218         }
219       }
220       else
221       {
222         throw new Error(MessageManager.getString("error.implementation_error_cant_reorder_tree"));
223       }
224     }
225     jalview.util.QuickSort.sort(names, nodes);
226     root.removeAllChildren();
227     for (int i = count - 1; i >= 0; i--)
228     {
229       root.add(nodes[i]);
230     }
231   }
232
233   private class DbTreeRenderer extends DefaultTreeCellRenderer implements
234           TreeCellRenderer
235   {
236     JDatabaseTree us;
237
238     public DbTreeRenderer(JDatabaseTree me)
239     {
240       us = me;
241     }
242
243     private Component returnLabel(String txt)
244     {
245       JLabel jl = new JLabel(txt);
246       jl.setFont(JvSwingUtils.getLabelFont());
247       return jl;
248     }
249
250     @Override
251     public Component getTreeCellRendererComponent(JTree tree, Object value,
252             boolean selected, boolean expanded, boolean leaf, int row,
253             boolean hasFocus)
254     {
255       String val = "";
256       if (value != null && value instanceof DefaultMutableTreeNode)
257       {
258         DefaultMutableTreeNode vl = (DefaultMutableTreeNode) value;
259         value = vl.getUserObject();
260         if (value instanceof DbSourceProxy)
261         {
262           val = (((DbSourceProxy) value).getDbName());
263         }
264         else
265         {
266           if (value instanceof String)
267           {
268             val = ((String) value);
269           }
270         }
271       }
272       if (value == null)
273       {
274         val = ("");
275       }
276       return super.getTreeCellRendererComponent(tree, val, selected,
277               expanded, leaf, row, hasFocus);
278
279     }
280   }
281
282   List<DbSourceProxy> oldselection, selection = null;
283
284   TreePath[] tsel = null, oldtsel = null;
285
286   @Override
287   protected void raiseClosed()
288   {
289     for (ActionListener al : lstners)
290     {
291       al.actionPerformed(null);
292     }
293   }
294
295   @Override
296   protected void okPressed()
297   {
298     _setSelectionState();
299     closeDialog();
300   }
301
302   @Override
303   protected void cancelPressed()
304   {
305     selection = oldselection;
306     tsel = oldtsel;
307     _revertSelectionState();
308     closeDialog();
309   }
310
311   private void showDialog(Container parent)
312   {
313     oldselection = selection;
314     oldtsel = tsel;
315     validate();
316     waitForInput();
317   }
318
319   public boolean hasSelection()
320   {
321     return selection == null ? false : selection.size() == 0 ? false : true;
322   }
323
324   public List<DbSourceProxy> getSelectedSources()
325   {
326     return selection;
327   }
328
329   /**
330    * disable or enable selection handler
331    */
332   boolean handleSelections = true;
333
334   private void _setSelectionState()
335   {
336     if (!handleSelections)
337     {
338       return;
339     }
340     if (dbviews.getSelectionCount() == 0)
341     {
342       selection = null;
343     }
344     tsel = dbviews.getSelectionPaths();
345     boolean forcedFirstChild = false;
346     List<DbSourceProxy> srcs = new ArrayList<DbSourceProxy>();
347     if (tsel != null)
348     {
349       for (TreePath tp : tsel)
350       {
351         DefaultMutableTreeNode admt, dmt = (DefaultMutableTreeNode) tp
352                 .getLastPathComponent();
353         if (dmt.getUserObject() != null)
354         {
355           if (dmt.getUserObject() instanceof DbSourceProxy)
356           {
357             srcs.add((DbSourceProxy) dmt.getUserObject());
358           }
359           else
360           {
361             if (allowMultiSelections)
362             {
363               srcs.addAll(sfetcher.getSourceProxy((String) dmt
364                       .getUserObject()));
365             }
366             else
367             {
368               srcs.add(sfetcher
369                       .getSourceProxy((String) dmt.getUserObject()).get(0));
370               forcedFirstChild = true;
371             }
372           }
373         }
374       }
375     }
376     updateDbStatus(srcs, forcedFirstChild);
377     selection = srcs;
378   }
379
380   private void _revertSelectionState()
381   {
382     handleSelections = false;
383     if (selection == null || selection.size() == 0)
384     {
385       dbviews.clearSelection();
386     }
387     else
388     {
389       dbviews.setSelectionPaths(tsel);
390     }
391     handleSelections = true;
392   }
393
394   private void updateDbStatus(List<DbSourceProxy> srcs,
395           boolean forcedFirstChild)
396   {
397     int x = 0;
398     String nm = "", qr = "";
399     for (DbSourceProxy dbs : srcs)
400     {
401       String tq = dbs.getTestQuery();
402       nm = dbs.getDbName();
403       if (tq != null && tq.trim().length() > 0 && dbs.isValidReference(tq))
404       {
405         qr = tq;
406         x++;
407       }
408     }
409
410     if (allowMultiSelections)
411     {
412       dbstatus.setText(MessageManager.formatMessage(
413               "label.selected_database_to_fetch_from", new String[]
414               {
415                   Integer.valueOf(srcs.size()).toString(),
416                   (srcs.size() == 1 ? "" : "s"),
417                   (srcs.size() > 0 ? " with " + x + " test quer"
418                           + (x == 1 ? "y" : "ies") : ".") }));
419       dbstatex.setText(" ");
420     }
421     else
422     {
423       if (nm.length() > 0)
424       {
425         dbstatus.setText(MessageManager.formatMessage(
426                 "label.database_param", new String[]
427                 { nm }));
428         if (qr.length() > 0)
429         {
430           dbstatex.setText(MessageManager.formatMessage(
431                   "label.example_param", new String[]
432                   { qr }));
433         }
434         else
435         {
436           dbstatex.setText(" ");
437         }
438       }
439       else
440       {
441         dbstatus.setText(" ");
442       }
443     }
444     dbstatus.invalidate();
445     dbstatex.invalidate();
446   }
447
448   public String getSelectedItem()
449   {
450     if (hasSelection())
451     {
452       return getSelectedSources().get(0).getDbName();
453     }
454     return null;
455   }
456
457   public String getExampleQueries()
458   {
459     if (!hasSelection())
460     {
461       return null;
462     }
463     StringBuffer sb = new StringBuffer();
464     HashSet<String> hs = new HashSet<String>();
465     for (DbSourceProxy dbs : getSelectedSources())
466     {
467       String tq = dbs.getTestQuery();
468       ;
469       if (hs.add(tq))
470       {
471         if (sb.length() > 0)
472         {
473           sb.append(";");
474         }
475         sb.append(tq);
476       }
477     }
478     return sb.toString();
479   }
480
481   List<ActionListener> lstners = new Vector<ActionListener>();
482
483   public void addActionListener(ActionListener actionListener)
484   {
485     lstners.add(actionListener);
486   }
487
488   public void removeActionListener(ActionListener actionListener)
489   {
490     lstners.remove(actionListener);
491   }
492
493   public static void main(String args[])
494   {
495     Cache.getDasSourceRegistry();
496     JDatabaseTree jdt = new JDatabaseTree(new jalview.ws.SequenceFetcher());
497     JFrame foo = new JFrame();
498     foo.setLayout(new BorderLayout());
499     foo.add(jdt.getDatabaseSelectorButton(), BorderLayout.CENTER);
500     foo.pack();
501     foo.setVisible(true);
502     int nultimes = 5;
503     final Thread us = Thread.currentThread();
504     jdt.addActionListener(new ActionListener()
505     {
506
507       @Override
508       public void actionPerformed(ActionEvent e)
509       {
510         us.interrupt();
511       }
512     });
513     do
514     {
515       try
516       {
517         Thread.sleep(50);
518       } catch (InterruptedException x)
519       {
520         nultimes--;
521         if (!jdt.hasSelection())
522         {
523           System.out.println("No Selection");
524         }
525         else
526         {
527           System.out.println("Selection: " + jdt.getSelectedItem());
528           int s = 1;
529           for (DbSourceProxy pr : jdt.getSelectedSources())
530           {
531             System.out.println("Source " + s++ + ": " + pr.getDbName()
532                     + " (" + pr.getDbSource() + ") Version "
533                     + pr.getDbVersion() + ". Test:\t" + pr.getTestQuery());
534           }
535           System.out.println("Test queries: " + jdt.getExampleQueries());
536         }
537       }
538     } while (nultimes > 0 && foo.isVisible());
539     foo.setVisible(false);
540   }
541
542   @Override
543   public void keyPressed(KeyEvent arg0)
544   {
545     if (!arg0.isConsumed() && arg0.getKeyCode() == KeyEvent.VK_ENTER)
546     {
547       okPressed();
548     }
549     if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE)
550     {
551       cancelPressed();
552     }
553   }
554
555   @Override
556   public void keyReleased(KeyEvent arg0)
557   {
558     // TODO Auto-generated method stub
559
560   }
561
562   @Override
563   public void keyTyped(KeyEvent arg0)
564   {
565     // TODO Auto-generated method stub
566
567   }
568 }