1   /*
2    *  Copyright (c) 1998-2004, The University of Sheffield.
3    *
4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
5    *  software, licenced under the GNU Library General Public License,
6    *  Version 2, June 1991 (in the distribution as file licence.html,
7    *  and also available at http://gate.ac.uk/gate/licence.html).
8    *
9    *  PluginManagerUI.java
10   *
11   *  Valentin Tablan, 21-Jul-2004
12   *
13   *  $Id: PluginManagerUI.java,v 1.5 2004/08/06 16:08:26 valyt Exp $
14   */
15  
16  package gate.gui;
17  
18  import java.awt.*;
19  import java.awt.GridBagConstraints;
20  import java.awt.GridBagLayout;
21  import java.awt.event.ActionEvent;
22  import java.awt.event.ActionListener;
23  import java.io.File;
24  import java.io.IOException;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.util.*;
28  import java.util.ArrayList;
29  import java.util.List;
30  import javax.swing.*;
31  import javax.swing.event.ListSelectionEvent;
32  import javax.swing.event.ListSelectionListener;
33  import javax.swing.table.*;
34  import javax.swing.table.AbstractTableModel;
35  import javax.swing.table.TableCellRenderer;
36  import org.jdom.*;
37  import org.jdom.Element;
38  import org.jdom.JDOMException;
39  import org.jdom.input.SAXBuilder;
40  import gate.Gate;
41  import gate.GateConstants;
42  import gate.swing.XJTable;
43  import gate.util.*;
44  import gate.util.Err;
45  import gate.util.GateRuntimeException;
46  
47  /**
48   * This is the user interface used for plugin management 
49   */
50  public class PluginManagerUI extends JDialog implements GateConstants{
51    
52    public PluginManagerUI(Frame owner){
53      super(owner);
54      initLocalData();
55      initGUI();
56      initListeners();
57    }
58    
59    
60    protected void initLocalData(){
61      loadNowByURL = new HashMap();
62      loadAlwaysByURL = new HashMap();
63    }
64    
65    protected void initGUI(){
66      setTitle("Plugin Management Console");
67      mainTableModel = new MainTableModel();
68      mainTable = new XJTable();
69      mainTable.setSortable(false);
70      mainTable.setModel(mainTableModel);
71      mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
72      mainTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
73      DeleteColumnCellRendererEditor rendererEditor = new DeleteColumnCellRendererEditor();
74      mainTable.getColumnModel().getColumn(DELETE_COLUMN).
75        setCellEditor(rendererEditor);
76      mainTable.getColumnModel().getColumn(DELETE_COLUMN).
77        setCellRenderer(rendererEditor);
78      
79      resourcesListModel = new ResourcesListModel();
80      resourcesList = new JList(resourcesListModel);
81      resourcesList.setCellRenderer(new ResourcesListCellRenderer());
82      resourcesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
83      //enable tooltips
84      ToolTipManager.sharedInstance().registerComponent(resourcesList);
85      getContentPane().setLayout(new GridBagLayout());
86      GridBagConstraints constraints = new GridBagConstraints();
87      constraints.insets = new Insets(2, 2, 2, 2);
88      constraints.fill = GridBagConstraints.BOTH;
89      constraints.anchor = GridBagConstraints.WEST;
90      constraints.gridy = 0;
91      constraints.weightx = 1;
92      getContentPane().add(new JLabel("Known CREOLE directories"), constraints);
93      getContentPane().add(new JLabel("CREOLE resources in directory"), constraints);
94      
95      constraints.gridy = 1;
96      constraints.weighty = 1;
97      getContentPane().add(new JScrollPane(mainTable), constraints);
98      getContentPane().add(new JScrollPane(resourcesList), constraints);
99      
100     constraints.gridy = 2;
101     constraints.weighty = 0;
102     constraints.gridwidth = 2;
103     Box hBox = Box.createHorizontalBox();
104     hBox.add(new JLabel("You can also "));
105     hBox.add(new JButton(new AddCreoleRepositoryAction()));
106     hBox.add(Box.createHorizontalGlue());
107     getContentPane().add(hBox, constraints);
108     
109     constraints.gridy = 3;
110     constraints.gridwidth = 2;
111     constraints.anchor = GridBagConstraints.CENTER;
112     constraints.fill = GridBagConstraints.NONE;
113     hBox = Box.createHorizontalBox();
114     hBox.add(new JButton(new OkAction()));
115     hBox.add(Box.createHorizontalStrut(20));
116     hBox.add(new JButton(new CancelAction()));
117     getContentPane().add(hBox, constraints);
118   }
119   
120   protected void initListeners(){
121     mainTable.getSelectionModel().addListSelectionListener(
122       new ListSelectionListener(){
123      public void valueChanged(ListSelectionEvent e){
124        resourcesListModel.dataChanged();
125      }
126     });
127   }
128   
129   protected Boolean getLoadNow(URL url){
130     Boolean res = (Boolean)loadNowByURL.get(url);
131     if(res == null){
132       res = new Boolean(Gate.getCreoleRegister().getDirectories().contains(url));
133       loadNowByURL.put(url, res);
134     }
135     return res;
136   }
137   
138   protected Boolean getLoadAlways(URL url){
139     Boolean res = (Boolean)loadAlwaysByURL.get(url);
140     if(res == null){
141       res = new Boolean(Gate.getAutoloadPlugins().contains(url));
142       loadAlwaysByURL.put(url, res);
143     }
144     return res;
145   }
146   
147   protected class MainTableModel extends AbstractTableModel{
148     public MainTableModel(){
149       localIcon = MainFrame.getIcon("loadFile.gif");
150       remoteIcon = MainFrame.getIcon("internet.gif");
151       invalidIcon = MainFrame.getIcon("param.gif");
152     }
153     public int getRowCount(){
154       return Gate.getKnownPlugins().size();
155     }
156     
157     public int getColumnCount(){
158       return 6;
159     }
160     
161     public String getColumnName(int column){
162       switch (column){
163         case NAME_COLUMN: return "Name";
164         case ICON_COLUMN: return "";
165         case URL_COLUMN: return "URL";
166         case LOAD_NOW_COLUMN: return "Load now";
167         case LOAD_ALWAYS_COLUMN: return "Load always";
168         case DELETE_COLUMN: return "Delete";
169         default: return "?";
170       }
171     }
172     
173     public Class getColumnClass(int columnIndex){
174       switch (columnIndex){
175         case NAME_COLUMN: return String.class;
176         case ICON_COLUMN: return Icon.class;
177         case URL_COLUMN: return String.class;
178         case LOAD_NOW_COLUMN: return Boolean.class;
179         case LOAD_ALWAYS_COLUMN: return Boolean.class;
180         case DELETE_COLUMN: return Object.class;
181         default: return Object.class;
182       }
183     }
184     
185     public Object getValueAt(int row, int column){
186       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(
187               (URL)Gate.getKnownPlugins().get(row));
188       switch (column){
189         case NAME_COLUMN: return new File(dInfo.getUrl().getFile()).getName();
190         case ICON_COLUMN: return
191           dInfo.isValid() ? (
192             dInfo.getUrl().getProtocol().equalsIgnoreCase("file") ? 
193             localIcon : remoteIcon) :
194           invalidIcon;
195         case URL_COLUMN: return dInfo.getUrl().toString();
196         case LOAD_NOW_COLUMN: return  getLoadNow(dInfo.getUrl());
197         case LOAD_ALWAYS_COLUMN: return getLoadAlways(dInfo.getUrl());
198         case DELETE_COLUMN: return null;
199         default: return null;
200       }
201     }
202     
203     public boolean isCellEditable(int rowIndex, int columnIndex){
204       return columnIndex == LOAD_NOW_COLUMN || 
205         columnIndex == LOAD_ALWAYS_COLUMN ||
206         columnIndex == DELETE_COLUMN;
207     }
208     
209     public void setValueAt(Object aValue, int rowIndex, int columnIndex){
210       Boolean valueBoolean = (Boolean)aValue;
211       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(
212               (URL)Gate.getKnownPlugins().get(rowIndex));
213       switch(columnIndex){
214         case LOAD_NOW_COLUMN: 
215           loadNowByURL.put(dInfo.getUrl(), valueBoolean);
216           break;
217         case LOAD_ALWAYS_COLUMN:
218           loadAlwaysByURL.put(dInfo.getUrl(), valueBoolean);
219           break;
220       }
221     }
222     
223     protected Icon localIcon;
224     protected Icon remoteIcon;
225     protected Icon invalidIcon;
226   }
227   
228   protected class ResourcesListModel extends AbstractListModel{
229     public Object getElementAt(int index){
230       int row = mainTable.getSelectedRow();
231       if(row == -1) return null;
232       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(
233               (URL)Gate.getKnownPlugins().get(row));
234       return (Gate.ResourceInfo)dInfo.getResourceInfoList().get(index);
235     }
236     
237     public int getSize(){
238       int row = mainTable.getSelectedRow();
239       if(row == -1) return 0;
240       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(
241               (URL)Gate.getKnownPlugins().get(row));
242       return dInfo.getResourceInfoList().size();
243     }
244     
245     public void dataChanged(){
246       fireIntervalRemoved(this, 0, getSize());
247       fireContentsChanged(this, 0, getSize());
248     }
249   }
250   
251   /**
252    * This class acts both as cell renderer  and editor for all the cells in the 
253    * delete column.
254    */
255   protected class DeleteColumnCellRendererEditor extends AbstractCellEditor 
256     implements TableCellRenderer, TableCellEditor{
257     
258     public DeleteColumnCellRendererEditor(){
259       label = new JLabel();
260       rendererDeleteButton = new JButton(MainFrame.getIcon("delete.gif"));
261       rendererDeleteButton.setMaximumSize(rendererDeleteButton.getPreferredSize());
262       editorDeleteButton = new JButton(MainFrame.getIcon("delete.gif"));
263       editorDeleteButton.addActionListener(new ActionListener(){
264         public void actionPerformed(ActionEvent evt){
265           int row = mainTable.getEditingRow();
266           Gate.removeKnownDirectory((URL)Gate.getKnownPlugins().
267                   get(row));
268           mainTableModel.fireTableDataChanged();
269         }
270       });
271     }
272     
273     public Component getTableCellRendererComponent(JTable table,
274             Object value,
275             boolean isSelected,
276             boolean hasFocus,
277             int row,
278             int column){
279 //      editorDeleteButton.setSelected(false);
280       switch(column){
281         case DELETE_COLUMN:
282           return rendererDeleteButton;
283         default: return null;
284       }
285     }
286     
287     public Component getTableCellEditorComponent(JTable table,
288             Object value,
289             boolean isSelected,
290             int row,
291             int column){
292       switch(column){
293         case DELETE_COLUMN:
294           return editorDeleteButton;
295         default: return null;
296       }
297     }
298     
299     public Object getCellEditorValue(){
300       return null;
301     }
302     
303     JButton editorDeleteButton;
304     JButton rendererDeleteButton;
305     JLabel label;
306   }
307   
308   protected class ResourcesListCellRenderer extends DefaultListCellRenderer{
309     public Component getListCellRendererComponent(JList list,
310             Object value,
311             int index,
312             boolean isSelected,
313             boolean cellHasFocus){
314       Gate.ResourceInfo rInfo = (Gate.ResourceInfo)value;
315       //prepare the renderer
316       super.getListCellRendererComponent(list, 
317               rInfo.getResourceName(), index, isSelected, cellHasFocus);
318       //add tooltip text
319       setToolTipText(rInfo.getResourceComment());
320       return this;
321     }
322   }
323   
324   
325   protected class OkAction extends AbstractAction {
326     public OkAction(){
327       super("OK");
328     }
329     public void actionPerformed(ActionEvent evt){
330       hide();
331       //update the data structures to reflect the user's choices
332       Iterator pluginIter = loadNowByURL.keySet().iterator();
333       while(pluginIter.hasNext()){
334         URL aPluginURL = (URL)pluginIter.next();
335         boolean load = ((Boolean)loadNowByURL.get(aPluginURL)).booleanValue();
336         boolean loaded = Gate.getCreoleRegister().
337             getDirectories().contains(aPluginURL); 
338         if(load && !loaded){
339           //load the directory
340           try{
341             Gate.getCreoleRegister().registerDirectories(aPluginURL);
342           }catch(GateException ge){
343             throw new GateRuntimeException(ge);
344           }
345         }
346         if(!load && loaded){
347           //remove the directory
348           Gate.getCreoleRegister().removeDirectory(aPluginURL);
349         }
350       }
351       
352       
353       pluginIter = loadAlwaysByURL.keySet().iterator();
354       while(pluginIter.hasNext()){
355         URL aPluginURL = (URL)pluginIter.next();
356         boolean load = ((Boolean)loadAlwaysByURL.get(aPluginURL)).booleanValue();
357         boolean loaded = Gate.getAutoloadPlugins().contains(aPluginURL); 
358         if(load && !loaded){
359           //set autoload top true
360           Gate.addAutoloadPlugin(aPluginURL);
361         }
362         if(!load && loaded){
363           //set autoload to false
364           Gate.getAutoloadPlugins().remove(aPluginURL);
365         }
366       }
367       loadNowByURL.clear();
368       loadAlwaysByURL.clear();
369     }
370   }
371   
372   protected class CancelAction extends AbstractAction {
373     public CancelAction(){
374       super("Cancel");
375     }
376     
377     public void actionPerformed(ActionEvent evt){
378       hide();
379       loadNowByURL.clear();
380       loadAlwaysByURL.clear();      
381     }
382   }
383 
384   protected class AddCreoleRepositoryAction extends AbstractAction {
385     public AddCreoleRepositoryAction(){
386       super("Add a new CREOLE repository");
387       putValue(SHORT_DESCRIPTION,"Load a new CREOLE repository");
388     }
389 
390     public void actionPerformed(ActionEvent e) {
391       Box messageBox = Box.createHorizontalBox();
392       Box leftBox = Box.createVerticalBox();
393       JTextField urlTextField = new JTextField(20);
394       leftBox.add(new JLabel("Type an URL"));
395       leftBox.add(urlTextField);
396       messageBox.add(leftBox);
397 
398       messageBox.add(Box.createHorizontalStrut(10));
399       messageBox.add(new JLabel("or"));
400       messageBox.add(Box.createHorizontalStrut(10));
401 
402       class URLfromFileAction extends AbstractAction{
403         URLfromFileAction(JTextField textField){
404           super(null, MainFrame.getIcon("loadFile.gif"));
405           putValue(SHORT_DESCRIPTION,"Click to select a directory");
406           this.textField = textField;
407         }
408 
409         public void actionPerformed(ActionEvent e){
410           JFileChooser fileChooser = MainFrame.getFileChooser(); 
411           fileChooser.setMultiSelectionEnabled(false);
412           fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
413           fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());
414           int result = fileChooser.showOpenDialog(PluginManagerUI.this);
415           if(result == JFileChooser.APPROVE_OPTION){
416             try{
417               textField.setText(fileChooser.getSelectedFile().
418                                             toURL().toExternalForm());
419             }catch(MalformedURLException mue){
420               throw new GateRuntimeException(mue.toString());
421             }
422           }
423         }
424         JTextField textField;
425       };//class URLfromFileAction extends AbstractAction
426 
427       Box rightBox = Box.createVerticalBox();
428       rightBox.add(new JLabel("Select a directory"));
429       JButton fileBtn = new JButton(new URLfromFileAction(urlTextField));
430       rightBox.add(fileBtn);
431       messageBox.add(rightBox);
432 
433       int res = JOptionPane.showOptionDialog(
434                             PluginManagerUI.this, messageBox,
435                             "Enter an URL to the directory containig the " +
436                             "\"creole.xml\" file", JOptionPane.OK_CANCEL_OPTION,
437                             JOptionPane.QUESTION_MESSAGE, null, null, null);
438       if(res == JOptionPane.OK_OPTION){
439         try{
440           URL creoleURL = new URL(urlTextField.getText());
441           Gate.addKnownPlugin(creoleURL);
442           mainTableModel.fireTableDataChanged();
443         }catch(Exception ex){
444           JOptionPane.showMessageDialog(
445               PluginManagerUI.this,
446               "There was a problem with your selection:\n" +
447               ex.toString() ,
448               "GATE", JOptionPane.ERROR_MESSAGE);
449           ex.printStackTrace(Err.getPrintWriter());
450         }
451       }
452     }
453   }//class LoadCreoleRepositoryAction extends AbstractAction
454 
455   protected XJTable mainTable;
456   protected MainTableModel mainTableModel;
457   protected ResourcesListModel resourcesListModel;
458   protected JList resourcesList; 
459   
460   /**
461    * Map from URL to Boolean. Stores temporary values for the loadNow options.
462    */
463   protected Map loadNowByURL;
464   /**
465    * Map from URL to Boolean. Stores temporary values for the loadAlways 
466    * options.
467    */
468   protected Map loadAlwaysByURL;
469  
470   protected static final int ICON_COLUMN = 0;
471   protected static final int NAME_COLUMN = 1;
472   protected static final int URL_COLUMN = 2;
473   protected static final int LOAD_NOW_COLUMN = 3;
474   protected static final int LOAD_ALWAYS_COLUMN = 4;
475   protected static final int DELETE_COLUMN = 5;
476   
477 }
478