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