c6b16b152eca0eb3b6f2fe42abdc053f4c3d924a
[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           names[i]=((DbSourceProxy)child.getUserObject()).getDbName().toLowerCase();
189         } else {
190           names[i] = ((String) child.getUserObject()).toLowerCase();
191           sortTreeNodes(child);
192         }
193       } else {
194         throw new Error("Implementation Error: Can't reorder this tree. Not DefaultMutableTreeNode.");
195       }
196     }
197     jalview.util.QuickSort.sort(names,  nodes);
198     root.removeAllChildren();
199     for (int i=count-1;i>=0;i--)
200     {
201       root.add(nodes[i]);
202     }
203   }
204
205   private class DbTreeRenderer extends DefaultTreeCellRenderer implements
206           TreeCellRenderer
207   {
208     JDatabaseTree us;
209
210     public DbTreeRenderer(JDatabaseTree me)
211     {
212       us = me;
213     }
214
215     private Component returnLabel(String txt)
216     {
217       JLabel jl = new JLabel(txt);
218       jl.setFont(JvSwingUtils.getLabelFont());
219       return jl;
220     }
221
222     @Override
223     public Component getTreeCellRendererComponent(JTree tree, Object value,
224             boolean selected, boolean expanded, boolean leaf, int row,
225             boolean hasFocus)
226     {
227       String val = "";
228       if (value != null && value instanceof DefaultMutableTreeNode)
229       {
230         DefaultMutableTreeNode vl = (DefaultMutableTreeNode) value;
231         value = vl.getUserObject();
232         if (value instanceof DbSourceProxy)
233         {
234           val = (((DbSourceProxy) value).getDbName());
235         }
236         else
237         {
238           if (value instanceof String)
239           {
240             val = ((String) value);
241           }
242         }
243       }
244       if (value == null)
245       {
246         val = ("");
247       }
248       return super.getTreeCellRendererComponent(tree, val, selected,
249               expanded, leaf, row, hasFocus);
250
251     }
252   }
253
254   List<DbSourceProxy> oldselection, selection = null;
255
256   TreePath[] tsel = null, oldtsel = null;
257
258   @Override
259   protected void raiseClosed()
260   {
261     for (ActionListener al : lstners)
262     {
263       al.actionPerformed(null);
264     }
265   }
266
267   @Override
268   protected void okPressed()
269   {
270     _setSelectionState();
271     closeDialog();
272   }
273
274   @Override
275   protected void cancelPressed()
276   {
277     selection = oldselection;
278     tsel = oldtsel;
279     _revertSelectionState();
280     closeDialog();
281   }
282
283   private void showDialog(Container parent)
284   {
285     oldselection = selection;
286     oldtsel = tsel;
287     validate();
288     waitForInput();
289   }
290
291   public boolean hasSelection()
292   {
293     return selection == null ? false : selection.size() == 0 ? false : true;
294   }
295
296   public List<DbSourceProxy> getSelectedSources()
297   {
298     return selection;
299   }
300
301   /**
302    * disable or enable selection handler
303    */
304   boolean handleSelections = true;
305
306   private void _setSelectionState()
307   {
308     if (!handleSelections)
309     {
310       return;
311     }
312     if (dbviews.getSelectionCount() == 0)
313     {
314       selection = null;
315     }
316     tsel = dbviews.getSelectionPaths();
317     boolean forcedFirstChild = false;
318     List<DbSourceProxy> srcs = new ArrayList<DbSourceProxy>();
319     if (tsel != null)
320     {
321       for (TreePath tp : tsel)
322       {
323         DefaultMutableTreeNode admt, dmt = (DefaultMutableTreeNode) tp
324                 .getLastPathComponent();
325         if (dmt.getUserObject() != null)
326         {
327           if (dmt.getUserObject() instanceof DbSourceProxy)
328           {
329             srcs.add((DbSourceProxy) dmt.getUserObject());
330           }
331           else
332           {
333             if (allowMultiSelections)
334             {
335               srcs.addAll(sfetcher.getSourceProxy((String) dmt
336                       .getUserObject()));
337             }
338             else
339             {
340               if ((admt = dmt.getFirstLeaf()) != null
341                       && admt.getUserObject() != null)
342               {
343                 // modify db selection to just first leaf.
344                 if (admt.getUserObject() instanceof DbSourceProxy)
345                 {
346                   srcs.add((DbSourceProxy) admt.getUserObject());
347                 }
348                 else
349                 {
350                   srcs.add(sfetcher.getSourceProxy(
351                           (String) admt.getUserObject()).get(0));
352                 }
353                 forcedFirstChild = true;
354               }
355             }
356           }
357         }
358       }
359     }
360     updateDbStatus(srcs, forcedFirstChild);
361     selection = srcs;
362   }
363
364   private void _revertSelectionState()
365   {
366     handleSelections = false;
367     if (selection == null || selection.size() == 0)
368     {
369       dbviews.clearSelection();
370     }
371     else
372     {
373       dbviews.setSelectionPaths(tsel);
374     }
375     handleSelections = true;
376   }
377
378   private void updateDbStatus(List<DbSourceProxy> srcs,
379           boolean forcedFirstChild)
380   {
381     int x = 0;
382     String nm = "", qr = "";
383     for (DbSourceProxy dbs : srcs)
384     {
385       String tq = dbs.getTestQuery();
386       nm = dbs.getDbName();
387       if (tq != null && tq.trim().length() > 0 && dbs.isValidReference(tq))
388       {
389         qr = tq;
390         x++;
391       }
392     }
393
394     if (allowMultiSelections)
395     {
396       dbstatus.setText("Selected "
397               + srcs.size()
398               + " database"
399               + (srcs.size() == 1 ? "" : "s")
400               + " to fetch from"
401               + (srcs.size() > 0 ? " with " + x + " test quer"
402                       + (x == 1 ? "y" : "ies") : "."));
403       dbstatex.setText(" ");
404     }
405     else
406     {
407       if (nm.length() > 0)
408       {
409         dbstatus.setText("Database: " + nm);
410         if (qr.length() > 0)
411         {
412           dbstatex.setText("Example: " + qr);
413         }
414         else
415         {
416           dbstatex.setText(" ");
417         }
418       }
419       else
420       {
421         dbstatus.setText(" ");
422       }
423     }
424     dbstatus.invalidate();
425     dbstatex.invalidate();
426   }
427
428   public String getSelectedItem()
429   {
430     if (hasSelection())
431     {
432       return getSelectedSources().get(0).getDbName();
433     }
434     return null;
435   }
436
437   public String getExampleQueries()
438   {
439     if (!hasSelection())
440     {
441       return null;
442     }
443     StringBuffer sb = new StringBuffer();
444     HashSet<String> hs = new HashSet<String>();
445     for (DbSourceProxy dbs : getSelectedSources())
446     {
447       String tq = dbs.getTestQuery();
448       ;
449       if (hs.add(tq))
450       {
451         if (sb.length() > 0)
452         {
453           sb.append(";");
454         }
455         sb.append(tq);
456       }
457     }
458     return sb.toString();
459   }
460
461   List<ActionListener> lstners = new Vector<ActionListener>();
462
463   public void addActionListener(ActionListener actionListener)
464   {
465     lstners.add(actionListener);
466   }
467
468   public void removeActionListener(ActionListener actionListener)
469   {
470     lstners.remove(actionListener);
471   }
472
473   public static void main(String args[])
474   {
475     Cache.getDasSourceRegistry();
476     JDatabaseTree jdt = new JDatabaseTree(new jalview.ws.SequenceFetcher());
477     JFrame foo = new JFrame();
478     foo.setLayout(new BorderLayout());
479     foo.add(jdt.getDatabaseSelectorButton(), BorderLayout.CENTER);
480     foo.pack();
481     foo.setVisible(true);
482     int nultimes = 5;
483     final Thread us = Thread.currentThread();
484     jdt.addActionListener(new ActionListener()
485     {
486
487       @Override
488       public void actionPerformed(ActionEvent e)
489       {
490         us.interrupt();
491       }
492     });
493     do
494     {
495       try
496       {
497         Thread.sleep(50);
498       } catch (InterruptedException x)
499       {
500         nultimes--;
501         if (!jdt.hasSelection())
502         {
503           System.out.println("No Selection");
504         }
505         else
506         {
507           System.out.println("Selection: " + jdt.getSelectedItem());
508           int s = 1;
509           for (DbSourceProxy pr : jdt.getSelectedSources())
510           {
511             System.out.println("Source " + s++ + ": " + pr.getDbName()
512                     + " (" + pr.getDbSource() + ") Version "
513                     + pr.getDbVersion() + ". Test:\t" + pr.getTestQuery());
514           }
515           System.out.println("Test queries: " + jdt.getExampleQueries());
516         }
517       }
518     } while (nultimes > 0 && foo.isVisible());
519     foo.setVisible(false);
520   }
521
522   @Override
523   public void keyPressed(KeyEvent arg0)
524   {
525     if (!arg0.isConsumed() && arg0.getKeyCode() == KeyEvent.VK_ENTER)
526     {
527       okPressed();
528     }
529     if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE)
530     {
531       cancelPressed();
532     }
533   }
534
535   @Override
536   public void keyReleased(KeyEvent arg0)
537   {
538     // TODO Auto-generated method stub
539
540   }
541
542   @Override
543   public void keyTyped(KeyEvent arg0)
544   {
545     // TODO Auto-generated method stub
546
547   }
548 }