JAL-1517 fix copyright for 2.8.2
[jalview.git] / src / jalview / gui / JDatabaseTree.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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(MessageManager.getString("action.select_ddbb"));
66     viewdbs.addActionListener(new ActionListener()
67     {
68
69       @Override
70       public void actionPerformed(ActionEvent arg0)
71       {
72         showDialog(null);
73       }
74     });
75     return viewdbs;
76   }
77
78   JScrollPane svp;
79
80   JTree dbviews;
81
82   private jalview.ws.SequenceFetcher sfetcher;
83
84   private JLabel dbstatus, dbstatex;
85
86   public JDatabaseTree(jalview.ws.SequenceFetcher sfetch)
87   {
88     initDialogFrame(this, true, false, "Select Database Retrieval Source",
89             650, 490);
90     /*
91      * Dynamically generated database list will need a translation function from
92      * internal source to externally distinct names. UNIPROT and UP_NAME are
93      * identical DB sources, and should be collapsed.
94      */
95     DefaultMutableTreeNode tn = null, root = new DefaultMutableTreeNode();
96     Hashtable<String, DefaultMutableTreeNode> source = new Hashtable<String, DefaultMutableTreeNode>();
97     sfetcher = sfetch;
98     String dbs[] = sfetch.getSupportedDb();
99     Hashtable<String, String> ht = new Hashtable<String, String>();
100     for (int i = 0; i < dbs.length; i++)
101     {
102       tn = source.get(dbs[i]);
103       List<DbSourceProxy> srcs = sfetch.getSourceProxy(dbs[i]);
104       if (tn == null)
105       {
106         source.put(dbs[i], tn = new DefaultMutableTreeNode(dbs[i], true));
107       }
108       for (DbSourceProxy dbp : srcs)
109       {
110         if (ht.get(dbp.getDbName()) == null)
111         {
112           tn.add(new DefaultMutableTreeNode(dbp, false));
113           ht.put(dbp.getDbName(), dbp.getDbName());
114         }
115         else
116         {
117           System.err.println("dupe ig for : " + dbs[i] + " \t"
118                   + dbp.getDbName() + " (" + dbp.getDbSource() + ")");
119           source.remove(tn);
120         }
121       }
122     }
123     for (int i = 0; i < dbs.length; i++)
124     {
125       tn = source.get(dbs[i]);
126       if (tn == null)
127       {
128         continue;
129       }
130       if (tn.getChildCount() == 1)
131       {
132         DefaultMutableTreeNode ttn = (DefaultMutableTreeNode) tn
133                 .getChildAt(0);
134         // remove nodes with only one child
135         tn.setUserObject(ttn.getUserObject());
136         tn.removeAllChildren();
137         source.put(dbs[i], tn);
138         tn.setAllowsChildren(false);
139       }
140       root.add(tn);
141     }
142     // and sort the tree
143     sortTreeNodes(root);
144     svp = new JScrollPane();
145     // svp.setAutoscrolls(true);
146     dbviews = new JTree(new DefaultTreeModel(root, false));
147     dbviews.setCellRenderer(new DbTreeRenderer(this));
148
149     dbviews.getSelectionModel().setSelectionMode(
150             TreeSelectionModel.SINGLE_TREE_SELECTION);
151     svp.getViewport().setView(dbviews);
152     // svp.getViewport().setMinimumSize(new Dimension(300,200));
153     // svp.setSize(300,250);
154     // JPanel panel=new JPanel();
155     // panel.setSize(new Dimension(350,220));
156     // panel.add(svp);
157     dbviews.addTreeSelectionListener(new TreeSelectionListener()
158     {
159
160       @Override
161       public void valueChanged(TreeSelectionEvent arg0)
162       {
163         _setSelectionState();
164       }
165     });
166     JPanel jc = new JPanel(new BorderLayout()), j = new JPanel(
167             new FlowLayout());
168     jc.add(svp, BorderLayout.CENTER);
169
170     java.awt.Font f;
171     // TODO: make the panel stay a fixed size for longest dbname+example set.
172     JPanel dbstat = new JPanel(new GridLayout(2, 1));
173     dbstatus = new JLabel(" "); // set the height correctly for layout
174     dbstatus.setFont(f = JvSwingUtils.getLabelFont(false, true));
175     dbstatus.setSize(new Dimension(290, 50));
176     dbstatex = new JLabel(" ");
177     dbstatex.setFont(f);
178     dbstatex.setSize(new Dimension(290, 50));
179     dbstat.add(dbstatus);
180     dbstat.add(dbstatex);
181     jc.add(dbstat, BorderLayout.SOUTH);
182     jc.validate();
183     // j.setPreferredSize(new Dimension(300,50));
184     add(jc, BorderLayout.CENTER);
185     j.add(ok);
186     j.add(cancel);
187     add(j, BorderLayout.SOUTH);
188     dbviews.addKeyListener(this);
189     validate();
190   }
191
192   private void sortTreeNodes(DefaultMutableTreeNode root)
193   {
194     if (root.getChildCount() == 0)
195     {
196       return;
197     }
198     int count = root.getChildCount();
199     String[] names = new String[count];
200     DefaultMutableTreeNode[] nodes = new DefaultMutableTreeNode[count];
201     for (int i = 0; i < count; i++)
202     {
203       TreeNode node = root.getChildAt(i);
204       if (node instanceof DefaultMutableTreeNode)
205       {
206         DefaultMutableTreeNode child = (DefaultMutableTreeNode) node;
207         nodes[i] = child;
208         if (child.getUserObject() instanceof DbSourceProxy)
209         {
210           names[i] = ((DbSourceProxy) child.getUserObject()).getDbName()
211                   .toLowerCase();
212         }
213         else
214         {
215           names[i] = ((String) child.getUserObject()).toLowerCase();
216           sortTreeNodes(child);
217         }
218       }
219       else
220       {
221         throw new Error(
222                 "Implementation Error: Can't reorder this tree. Not DefaultMutableTreeNode.");
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.getSourceProxy((String) dmt
369                       .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("label.selected_database_to_fetch_from", new String[]{Integer.valueOf(srcs.size()).toString(),(srcs.size() == 1 ? "" : "s"),(srcs.size() > 0 ? " with " + x + " test quer" + (x == 1 ? "y" : "ies") : ".")}));
413       dbstatex.setText(" ");
414     }
415     else
416     {
417       if (nm.length() > 0)
418       {
419         dbstatus.setText(MessageManager.formatMessage("label.database_param",new String[]{nm}));
420         if (qr.length() > 0)
421         {
422           dbstatex.setText(MessageManager.formatMessage("label.example_param", new String[]{qr}));
423         }
424         else
425         {
426           dbstatex.setText(" ");
427         }
428       }
429       else
430       {
431         dbstatus.setText(" ");
432       }
433     }
434     dbstatus.invalidate();
435     dbstatex.invalidate();
436   }
437
438   public String getSelectedItem()
439   {
440     if (hasSelection())
441     {
442       return getSelectedSources().get(0).getDbName();
443     }
444     return null;
445   }
446
447   public String getExampleQueries()
448   {
449     if (!hasSelection())
450     {
451       return null;
452     }
453     StringBuffer sb = new StringBuffer();
454     HashSet<String> hs = new HashSet<String>();
455     for (DbSourceProxy dbs : getSelectedSources())
456     {
457       String tq = dbs.getTestQuery();
458       ;
459       if (hs.add(tq))
460       {
461         if (sb.length() > 0)
462         {
463           sb.append(";");
464         }
465         sb.append(tq);
466       }
467     }
468     return sb.toString();
469   }
470
471   List<ActionListener> lstners = new Vector<ActionListener>();
472
473   public void addActionListener(ActionListener actionListener)
474   {
475     lstners.add(actionListener);
476   }
477
478   public void removeActionListener(ActionListener actionListener)
479   {
480     lstners.remove(actionListener);
481   }
482
483   public static void main(String args[])
484   {
485     Cache.getDasSourceRegistry();
486     JDatabaseTree jdt = new JDatabaseTree(new jalview.ws.SequenceFetcher());
487     JFrame foo = new JFrame();
488     foo.setLayout(new BorderLayout());
489     foo.add(jdt.getDatabaseSelectorButton(), BorderLayout.CENTER);
490     foo.pack();
491     foo.setVisible(true);
492     int nultimes = 5;
493     final Thread us = Thread.currentThread();
494     jdt.addActionListener(new ActionListener()
495     {
496
497       @Override
498       public void actionPerformed(ActionEvent e)
499       {
500         us.interrupt();
501       }
502     });
503     do
504     {
505       try
506       {
507         Thread.sleep(50);
508       } catch (InterruptedException x)
509       {
510         nultimes--;
511         if (!jdt.hasSelection())
512         {
513           System.out.println("No Selection");
514         }
515         else
516         {
517           System.out.println("Selection: " + jdt.getSelectedItem());
518           int s = 1;
519           for (DbSourceProxy pr : jdt.getSelectedSources())
520           {
521             System.out.println("Source " + s++ + ": " + pr.getDbName()
522                     + " (" + pr.getDbSource() + ") Version "
523                     + pr.getDbVersion() + ". Test:\t" + pr.getTestQuery());
524           }
525           System.out.println("Test queries: " + jdt.getExampleQueries());
526         }
527       }
528     } while (nultimes > 0 && foo.isVisible());
529     foo.setVisible(false);
530   }
531
532   @Override
533   public void keyPressed(KeyEvent arg0)
534   {
535     if (!arg0.isConsumed() && arg0.getKeyCode() == KeyEvent.VK_ENTER)
536     {
537       okPressed();
538     }
539     if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE)
540     {
541       cancelPressed();
542     }
543   }
544
545   @Override
546   public void keyReleased(KeyEvent arg0)
547   {
548     // TODO Auto-generated method stub
549
550   }
551
552   @Override
553   public void keyTyped(KeyEvent arg0)
554   {
555     // TODO Auto-generated method stub
556
557   }
558 }