1   /*
2    *  Copyright (c) 1998-2001, 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    *  Valentin Tablan 23/01/2001
10   *
11   *  $Id: NameBearerHandle.java,v 1.39 2001/12/03 09:33:22 valyt Exp $
12   *
13   */
14  
15  package gate.gui;
16  
17  import javax.swing.*;
18  import java.util.*;
19  import java.net.*;
20  import java.awt.Component;
21  import java.awt.Window;
22  import java.awt.event.*;
23  import java.text.NumberFormat;
24  import java.io.*;
25  import javax.swing.filechooser.FileFilter;
26  
27  import gate.*;
28  import gate.util.*;
29  import gate.swing.*;
30  import gate.creole.*;
31  import gate.persist.*;
32  import gate.event.*;
33  import gate.security.*;
34  import gate.security.SecurityException;
35  
36  /**
37   * Class used to store the GUI information about an open entity (resource,
38   * controller, datastore).
39   * Such information will include icon to be used for tree components,
40   * popup menu for right click events, large and small views, etc.
41   */
42  public class NameBearerHandle implements Handle,
43                                           StatusListener,
44                                           ProgressListener, CreoleListener {
45  
46    public NameBearerHandle(NameBearer target, Window window) {
47      this.target = target;
48      this.window = window;
49      sListenerProxy = new ProxyStatusListener();
50      String iconName = null;
51      if(target instanceof Resource){
52        rData = (ResourceData)Gate.getCreoleRegister().
53                                                get(target.getClass().getName());
54        if(rData != null){
55          iconName = rData.getIcon();
56          if(iconName == null){
57            if(target instanceof LanguageResource) iconName = "lr.gif";
58            else if(target instanceof ProcessingResource) iconName = "pr.gif";
59            else if(target instanceof Controller) iconName = "controller.gif";
60          }
61          tooltipText = "<HTML> <b>" + rData.getComment() + "</b><br>(<i>" +
62                        rData.getClassName() + "</i>)</HTML>";
63        } else {
64          this.icon = MainFrame.getIcon("lr.gif");
65        }
66      }else if(target instanceof DataStore){
67        iconName = ((DataStore)target).getIconName();
68        tooltipText = ((DataStore)target).getComment();
69      }
70  
71      popup = null;
72      title = (String)target.getName();
73      this.icon = MainFrame.getIcon(iconName);
74  
75      Gate.getCreoleRegister().addCreoleListener(this);
76      buildViews();
77      // Add the CTRL +F4 key & action combination to the resource
78      JComponent largeView = this.getLargeView();
79      if (largeView != null){
80        largeView.getActionMap().put("Close resource",
81                          new CloseAction());
82        if (target instanceof gate.corpora.DocumentImpl){
83          largeView.getActionMap().put("Save As XML", new SaveAsXmlAction());
84        }// End if
85      }// End if
86    }//public DefaultResourceHandle(FeatureBearer res)
87  
88    public Icon getIcon(){
89      return icon;
90    }
91  
92    public void setIcon(Icon icon){
93      this.icon = icon;
94    }
95  
96    public String getTitle(){
97      return title;
98    }
99  
100   public void setTitle(String newTitle){
101     this.title = newTitle;
102   }
103 
104   /**
105    * Returns a GUI component to be used as a small viewer/editor, e.g. below
106    * the main tree in the Gate GUI for the selected resource
107    */
108   public JComponent getSmallView() {
109     return smallView;
110   }
111 
112   /**
113    * Returns the large view for this resource. This view will go into the main
114    * display area.
115    */
116   public JComponent getLargeView() {
117     return largeView;
118   }
119 
120   public JPopupMenu getPopup() {
121     return popup;
122   }
123 
124   public void setPopup(JPopupMenu popup) {
125     this.popup = popup;
126   }
127 
128   public String getTooltipText() {
129     return tooltipText;
130   }
131 
132   public void setTooltipText(String text) {
133     this.tooltipText = text;
134   }
135 
136   public Object getTarget() {
137     return target;
138   }
139 
140   public Action getCloseAction(){
141     return new CloseAction();
142   }
143   protected void buildViews() {
144     //build the popup
145     popup = new JPopupMenu();
146     XJMenuItem closeItem = new XJMenuItem(new CloseAction(), sListenerProxy);
147     closeItem.setAccelerator(KeyStroke.getKeyStroke(
148                                 KeyEvent.VK_F4, ActionEvent.CTRL_MASK));
149     popup.add(closeItem);
150 
151     if(target instanceof ProcessingResource){
152       popup.addSeparator();
153       popup.add(new XJMenuItem(new ReloadAction(), sListenerProxy));
154     }else if(target instanceof LanguageResource) {
155       //Language Resources
156       popup.addSeparator();
157 //      popup.add(new XJMenuItem(new RenameAction(), sListenerProxy));
158       popup.add(new XJMenuItem(new SaveAction(), sListenerProxy));
159       popup.add(new XJMenuItem(new SaveToAction(), sListenerProxy));
160       if(target instanceof gate.corpora.DocumentImpl){
161         XJMenuItem saveAsXmlItem =
162                          new XJMenuItem(new SaveAsXmlAction(), sListenerProxy);
163         saveAsXmlItem.setAccelerator(KeyStroke.getKeyStroke(
164                                         KeyEvent.VK_X, ActionEvent.CTRL_MASK));
165 
166         popup.add(saveAsXmlItem);
167       }else if(target instanceof Corpus){
168         popup.addSeparator();
169         corpusFiller = new CorpusFillerComponent();
170         popup.add(new XJMenuItem(new PopulateCorpusAction(), sListenerProxy));
171         popup.addSeparator();
172         popup.add(new XJMenuItem(new SaveCorpusAsXmlAction(), sListenerProxy));
173       }
174     }else if(target instanceof Controller){
175       //Applications
176       popup.addSeparator();
177       popup.add(new XJMenuItem(new DumpToFileAction(), sListenerProxy));
178     }
179 //    if(target instanceof Resource){
180 //      popup.addSeparator();
181 //      popup.add(new XJMenuItem(new ReloadClassAction(), sListenerProxy));
182 //    }
183 
184 
185     fireStatusChanged("Building views...");
186 
187     //build the large views
188     List largeViewNames = Gate.getCreoleRegister().
189                           getLargeVRsForResource(target.getClass().getName());
190     if(largeViewNames != null && !largeViewNames.isEmpty()){
191       largeView = new JTabbedPane(JTabbedPane.BOTTOM);
192       Iterator classNameIter = largeViewNames.iterator();
193       while(classNameIter.hasNext()){
194         try{
195           String className = (String)classNameIter.next();
196           ResourceData rData = (ResourceData)Gate.getCreoleRegister().
197                                                   get(className);
198           FeatureMap params = Factory.newFeatureMap();
199           FeatureMap features = Factory.newFeatureMap();
200           Gate.setHiddenAttribute(features, true);
201           VisualResource view = (VisualResource)
202                                 Factory.createResource(className,
203                                                        params,
204                                                        features);
205           view.setTarget(target);
206           view.setHandle(this);
207           ((JTabbedPane)largeView).add((Component)view, rData.getName());
208         }catch(ResourceInstantiationException rie){
209           rie.printStackTrace(Err.getPrintWriter());
210         }
211       }
212       if(largeViewNames.size() == 1){
213         largeView = (JComponent)((JTabbedPane)largeView).getComponentAt(0);
214       }else{
215         ((JTabbedPane)largeView).setSelectedIndex(0);
216       }
217     }
218 
219     //build the small views
220     List smallViewNames = Gate.getCreoleRegister().
221                           getSmallVRsForResource(target.getClass().getName());
222     if(smallViewNames != null && !smallViewNames.isEmpty()){
223       smallView = new JTabbedPane(JTabbedPane.BOTTOM);
224       Iterator classNameIter = smallViewNames.iterator();
225       while(classNameIter.hasNext()){
226         try{
227           String className = (String)classNameIter.next();
228           ResourceData rData = (ResourceData)Gate.getCreoleRegister().
229                                                   get(className);
230           FeatureMap params = Factory.newFeatureMap();
231           FeatureMap features = Factory.newFeatureMap();
232           Gate.setHiddenAttribute(features, true);
233           VisualResource view = (VisualResource)
234                                 Factory.createResource(className,
235                                                        params,
236                                                        features);
237           view.setTarget(target);
238           view.setHandle(this);
239           ((JTabbedPane)smallView).add((Component)view, rData.getName());
240         }catch(ResourceInstantiationException rie){
241           rie.printStackTrace(Err.getPrintWriter());
242         }
243       }
244       if(smallViewNames.size() == 1){
245         smallView = (JComponent)((JTabbedPane)smallView).getComponentAt(0);
246       }else{
247         ((JTabbedPane)smallView).setSelectedIndex(0);
248       }
249     }
250     fireStatusChanged("Views built!");
251   }//protected void buildViews
252 
253   public String toString(){ return title;}
254 
255   public synchronized void removeProgressListener(ProgressListener l) {
256     if (progressListeners != null && progressListeners.contains(l)) {
257       Vector v = (Vector) progressListeners.clone();
258       v.removeElement(l);
259       progressListeners = v;
260     }
261   }//public synchronized void removeProgressListener(ProgressListener l)
262 
263   public synchronized void addProgressListener(ProgressListener l) {
264     Vector v = progressListeners == null ? new Vector(2) : (Vector) progressListeners.clone();
265     if (!v.contains(l)) {
266       v.addElement(l);
267       progressListeners = v;
268     }
269   }//public synchronized void addProgressListener(ProgressListener l)
270 
271   JPopupMenu popup;
272   String title;
273   String tooltipText;
274   NameBearer target;
275   /**
276    * The top level GUI component this hadle belongs to.
277    */
278   Window window;
279   ResourceData rData;
280   Icon icon;
281   JComponent smallView;
282   JComponent largeView;
283 
284   /**
285    * Component used to select the options for corpus populating
286    */
287   CorpusFillerComponent corpusFiller;
288 
289   StatusListener sListenerProxy;
290 
291 //  File currentDir = null;
292   private transient Vector progressListeners;
293   private transient Vector statusListeners;
294 
295   class CloseAction extends AbstractAction {
296     public CloseAction() {
297       super("Close");
298       putValue(SHORT_DESCRIPTION, "Removes this resource from the system");
299     }
300 
301     public void actionPerformed(ActionEvent e){
302       if(target instanceof Resource){
303         Factory.deleteResource((Resource)target);
304       }else if(target instanceof DataStore){
305         try{
306           ((DataStore)target).close();
307         } catch(PersistenceException pe){
308           JOptionPane.showMessageDialog(largeView != null ?
309                                                      largeView : smallView,
310                                         "Error!\n" + pe.toString(),
311                                         "Gate", JOptionPane.ERROR_MESSAGE);
312         }
313         statusListeners.clear();
314         progressListeners.clear();
315         //delete the viewers
316         if(largeView instanceof VisualResource){
317           Factory.deleteResource((VisualResource)largeView);
318         }else if(largeView instanceof JTabbedPane){
319           Component[] comps = ((JTabbedPane)largeView).getComponents();
320           for(int i = 0; i < comps.length; i++){
321             if(comps[i] instanceof VisualResource)
322               Factory.deleteResource((VisualResource)comps[i]);
323           }
324         }
325         if(smallView instanceof VisualResource){
326           Factory.deleteResource((VisualResource)smallView);
327         }else if(smallView instanceof JTabbedPane){
328           Component[] comps = ((JTabbedPane)smallView).getComponents();
329           for(int i = 0; i < comps.length; i++){
330             if(comps[i] instanceof VisualResource)
331               Factory.deleteResource((VisualResource)comps[i]);
332           }
333         }
334       }
335     }//public void actionPerformed(ActionEvent e)
336   }//class CloseAction
337 
338   /**
339    * Used to save a document as XML
340    */
341   class SaveAsXmlAction extends AbstractAction {
342     public SaveAsXmlAction(){
343       super("Save As Xml...");
344       putValue(SHORT_DESCRIPTION, "Saves this resource in XML");
345     }// SaveAsXmlAction()
346 
347     public void actionPerformed(ActionEvent e) {
348       Runnable runableAction = new Runnable(){
349         public void run(){
350           JFileChooser fileChooser = MainFrame.getFileChooser();
351           File selectedFile = null;
352 
353           List filters = Arrays.asList(fileChooser.getChoosableFileFilters());
354           Iterator filtersIter = filters.iterator();
355           FileFilter filter = null;
356           if(filtersIter.hasNext()){
357             filter = (FileFilter)filtersIter.next();
358             while(filtersIter.hasNext() &&
359                   filter.getDescription().indexOf("XML") == -1){
360               filter = (FileFilter)filtersIter.next();
361             }
362           }
363           if(filter == null || filter.getDescription().indexOf("XML") == -1){
364             //no suitable filter found, create a new one
365             ExtensionFileFilter xmlFilter = new ExtensionFileFilter();
366             xmlFilter.setDescription("XML files");
367             xmlFilter.addExtension("xml");
368             xmlFilter.addExtension("gml");
369             fileChooser.addChoosableFileFilter(xmlFilter);
370             filter = xmlFilter;
371           }
372           fileChooser.setFileFilter(filter);
373 
374           fileChooser.setMultiSelectionEnabled(false);
375           fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
376           fileChooser.setDialogTitle("Select document to save ...");
377           fileChooser.setSelectedFiles(null);
378 
379           int res = (getLargeView() != null) ?
380                                   fileChooser.showDialog(getLargeView(), "Save"):
381                     (getSmallView() != null) ?
382                                   fileChooser.showDialog(getSmallView(), "Save") :
383                                               fileChooser.showDialog(null, "Save");
384           if(res == JFileChooser.APPROVE_OPTION){
385             selectedFile = fileChooser.getSelectedFile();
386             File currentDir = fileChooser.getCurrentDirectory();
387             if(selectedFile == null) return;
388             NameBearerHandle.this.statusChanged("Saving as XML to " +
389              selectedFile.toString() + "...");
390             try{
391               // Prepare to write into the xmlFile using UTF-8 encoding
392               OutputStreamWriter writer = new OutputStreamWriter(
393                               new FileOutputStream(selectedFile),"UTF-8");
394 
395               // Write (test the toXml() method)
396               // This Action is added only when a gate.Document is created.
397               // So, is for sure that the resource is a gate.Document
398               writer.write(((gate.Document)target).toXml());
399               writer.flush();
400               writer.close();
401             } catch (Exception ex){
402               ex.printStackTrace(Out.getPrintWriter());
403             }// End try
404             NameBearerHandle.this.statusChanged("Finished saving as xml into "+
405              " the file : "+ selectedFile.toString());
406           }// End if
407         }// End run()
408       };// End Runnable
409       Thread thread = new Thread(runableAction, "");
410       thread.setPriority(Thread.MIN_PRIORITY);
411       thread.start();
412     }// actionPerformed()
413   }// SaveAsXmlAction
414 
415   /**
416    * Saves a corpus as a set of xml files in a directory.
417    */
418   class SaveCorpusAsXmlAction extends AbstractAction {
419     public SaveCorpusAsXmlAction(){
420       super("Save As Xml...");
421       putValue(SHORT_DESCRIPTION, "Saves this corpus in XML");
422     }// SaveAsXmlAction()
423 
424     public void actionPerformed(ActionEvent e) {
425       Runnable runnable = new Runnable(){
426         public void run(){
427           //we need a directory
428           JFileChooser filer = MainFrame.getFileChooser();
429           filer.setDialogTitle(
430               "Select the directory that will contain the corpus");
431           filer.setFileSelectionMode(filer.DIRECTORIES_ONLY);
432 
433           if (filer.showDialog(getLargeView() != null ?
434                                    getLargeView() :
435                                    getSmallView(),
436                                    "Select") == filer.APPROVE_OPTION){
437 
438             File dir = filer.getSelectedFile();
439             //create the top directory if needed
440             if(!dir.exists()){
441               if(!dir.mkdirs()){
442                 JOptionPane.showMessageDialog(
443                   largeView != null ?largeView : smallView,
444                   "Could not create top directory!",
445                   "Gate", JOptionPane.ERROR_MESSAGE);
446                 return;
447               }
448             }
449             //iterate through all the docs and save each of them as xml
450             Corpus corpus = (Corpus)target;
451             Iterator docIter = corpus.iterator();
452             boolean overwriteAll = false;
453             int docCnt = corpus.size();
454             int currentDocIndex = 0;
455             while(docIter.hasNext()){
456               Document currentDoc = (Document)docIter.next();
457               URL sourceURL = currentDoc.getSourceUrl();
458               String fileName = null;
459               if(sourceURL != null){
460                 fileName = sourceURL.getFile();
461                 fileName = Files.getLastPathComponent(fileName);
462               }
463               if(fileName == null || fileName.length() == 0){
464                 fileName = currentDoc.getName();
465               }
466               if(!fileName.toLowerCase().endsWith(".xml")) fileName += ".xml";
467               File docFile = null;
468               boolean nameOK = false;
469               do{
470                 docFile = new File(dir, fileName);
471                 if(docFile.exists() && !overwriteAll){
472                   //ask the user if we can ovewrite the file
473                   Object[] options = new Object[] {"Yes", "All",
474                                                    "No", "Cancel"};
475                   int answer = JOptionPane.showOptionDialog(
476                     largeView != null ? largeView : smallView,
477                     "File " + docFile.getName() + " already exists!\n" +
478                     "Overwrite?" ,
479                     "Gate", JOptionPane.DEFAULT_OPTION,
480                     JOptionPane.WARNING_MESSAGE, null, options, options[2]);
481                   switch(answer){
482                     case 0: {
483                       nameOK = true;
484                       break;
485                     }
486                     case 1: {
487                       nameOK = true;
488                       overwriteAll = true;
489                       break;
490                     }
491                     case 2: {
492                       //user said NO, allow them to provide an alternative name;
493                       fileName = (String)JOptionPane.showInputDialog(
494                           largeView != null ? largeView : smallView,
495                           "Please provide an alternative file name",
496                           "Gate", JOptionPane.QUESTION_MESSAGE,
497                           null, null, fileName);
498                       if(fileName == null) return;
499                       break;
500                     }
501                     case 3: {
502                       //user gave up; return
503                       return;
504                     }
505                   }
506 
507                 }else{
508                   nameOK = true;
509                 }
510               }while(!nameOK);
511               //save the file
512               try{
513                 OutputStreamWriter writer = new OutputStreamWriter(
514                               new FileOutputStream(docFile),"UTF-8");
515                 writer.write(currentDoc.toXml());
516                 writer.flush();
517                 writer.close();
518               }catch(IOException ioe){
519                 JOptionPane.showMessageDialog(
520                   largeView != null ? largeView : smallView,
521                   "Could not create write file:" +
522                   ioe.toString(),
523                   "Gate", JOptionPane.ERROR_MESSAGE);
524                 ioe.printStackTrace(Err.getPrintWriter());
525                 return;
526               }
527 
528               fireStatusChanged(currentDoc.getName() + " saved");
529               fireProgressChanged(100 * currentDocIndex++ / docCnt);
530             }//while(docIter.hasNext())
531             fireStatusChanged("Corpus saved");
532             fireProcessFinished();
533           }//select directory
534         }//public void run(){
535       };//Runnable runnable = new Runnable()
536       Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
537                                  runnable, "Corpus XML dumper");
538       thread.setPriority(Thread.MIN_PRIORITY);
539       thread.start();
540 
541     }//public void actionPerformed(ActionEvent e)
542   }//class SaveCorpusAsXmlAction extends AbstractAction
543 
544   /**
545    * Saves a corpus as a set of xml files in a directory.
546    */
547   class ReloadClassAction extends AbstractAction {
548     public ReloadClassAction(){
549       super("Reload resource class");
550       putValue(SHORT_DESCRIPTION, "Reloads the java class for this resource");
551     }// SaveAsXmlAction()
552 
553     public void actionPerformed(ActionEvent e) {
554       int answer = JOptionPane.showOptionDialog(
555                 largeView != null ? largeView : smallView,
556                 "This is an advanced option!\n" +
557                 "You should not use this unless your name is Hamish.\n" +
558                 "Are you sure you want to do this?" ,
559                 "Gate", JOptionPane.YES_NO_OPTION,
560                 JOptionPane.WARNING_MESSAGE, null, null, null);
561       if(answer == JOptionPane.OK_OPTION){
562         try{
563           String className = target.getClass().getName();
564           Gate.getClassLoader().reloadClass(className);
565           fireStatusChanged("Class " + className + " reloaded!");
566         }catch(Exception ex){
567           JOptionPane.showMessageDialog(largeView != null ?
568                                         largeView : smallView,
569                                         "Look what you've done: \n" +
570                                         ex.toString() +
571                                         "\nI told you not to do it...",
572                                         "Gate", JOptionPane.ERROR_MESSAGE);
573           ex.printStackTrace(Err.getPrintWriter());
574         }
575       }
576     }
577   }
578 
579   class RenameAction extends AbstractAction {
580     public RenameAction(){
581       super("Rename");
582       putValue(SHORT_DESCRIPTION, "Rename this language resource");
583     }
584     public void actionPerformed(ActionEvent e){
585       LanguageResource lr = (LanguageResource)target;
586 
587       String newName = JOptionPane.showInputDialog(
588                                      getLargeView(),
589                                     "Please enter the LR's new name");
590 
591       //don't change if nothing selected
592       if (newName == null || newName.equals(""))
593         return;
594       lr.setName(newName);
595 
596     }//public void actionPerformed(ActionEvent e)
597   }//class RenameAction
598 
599 
600 
601   class SaveAction extends AbstractAction {
602     public SaveAction(){
603       super("Save");
604       putValue(SHORT_DESCRIPTION, "Save back to the datastore");
605     }
606     public void actionPerformed(ActionEvent e){
607       DataStore ds = ((LanguageResource)target).getDataStore();
608       if(ds != null){
609         try {
610           StatusListener sListener = (StatusListener)
611                                      gate.gui.MainFrame.getListeners().
612                                      get("gate.event.StatusListener");
613           if(sListener != null) sListener.statusChanged(
614             "Saving: " + ((LanguageResource)target).getName());
615           double timeBefore = System.currentTimeMillis();
616           ((LanguageResource)
617                     target).getDataStore().sync((LanguageResource)target);
618           double timeAfter = System.currentTimeMillis();
619           if(sListener != null) sListener.statusChanged(
620             ((LanguageResource)target).getName() + " saved in " +
621             NumberFormat.getInstance().format((timeAfter-timeBefore)/1000)
622             + " seconds");
623         } catch(PersistenceException pe) {
624           JOptionPane.showMessageDialog(getLargeView(),
625                                         "Save failed!\n " +
626                                         pe.toString(),
627                                         "Gate", JOptionPane.ERROR_MESSAGE);
628         } catch(SecurityException se) {
629           JOptionPane.showMessageDialog(getLargeView(),
630                                         "Save failed!\n " +
631                                         se.toString(),
632                                         "Gate", JOptionPane.ERROR_MESSAGE);
633         }
634       } else {
635         JOptionPane.showMessageDialog(getLargeView(),
636                         "This resource has not been loaded from a datastore.\n"+
637                          "Please use the \"Save to\" option!\n",
638                          "Gate", JOptionPane.ERROR_MESSAGE);
639 
640       }
641     }//public void actionPerformed(ActionEvent e)
642   }//class SaveAction
643 
644   class DumpToFileAction extends AbstractAction {
645     public DumpToFileAction(){
646       super("Save application state");
647       putValue(SHORT_DESCRIPTION,
648                "Saves the data needed to recreate this application");
649     }
650 
651     public void actionPerformed(ActionEvent ae){
652       JFileChooser fileChooser = MainFrame.getFileChooser();
653 
654       fileChooser.setDialogTitle("Select a file for this resource");
655       fileChooser.setFileSelectionMode(fileChooser.FILES_AND_DIRECTORIES);
656       if (fileChooser.showSaveDialog(largeView) ==
657                                             fileChooser.APPROVE_OPTION){
658         final File file = fileChooser.getSelectedFile();
659           Runnable runnable = new Runnable(){
660             public void run(){
661               try{
662                 gate.util.persistence.PersistenceManager.
663                                       saveObjectToFile((Resource)target, file);
664               }catch(Exception e){
665                 JOptionPane.showMessageDialog(getLargeView(),
666                                 "Error!\n"+
667                                  e.toString(),
668                                  "Gate", JOptionPane.ERROR_MESSAGE);
669                 e.printStackTrace(Err.getPrintWriter());
670               }
671             }
672           };
673           Thread thread = new Thread(runnable);
674           thread.setPriority(Thread.MIN_PRIORITY);
675           thread.start();
676       }
677     }
678 
679   }
680 
681   class SaveToAction extends AbstractAction {
682     public SaveToAction(){
683       super("Save to...");
684       putValue(SHORT_DESCRIPTION, "Save this resource to a datastore");
685     }
686 
687     public void actionPerformed(ActionEvent e) {
688       try {
689         DataStoreRegister dsReg = Gate.getDataStoreRegister();
690         Map dsByName =new HashMap();
691         Iterator dsIter = dsReg.iterator();
692         while(dsIter.hasNext()){
693           DataStore oneDS = (DataStore)dsIter.next();
694           String name;
695           if((name = (String)oneDS.getName()) != null){
696           } else {
697             name  = oneDS.getStorageUrl();
698             try {
699               URL tempURL = new URL(name);
700               name = tempURL.getFile();
701             } catch (java.net.MalformedURLException ex) {
702               throw new GateRuntimeException(
703                         );
704             }
705           }
706           dsByName.put(name, oneDS);
707         }
708         List dsNames = new ArrayList(dsByName.keySet());
709         if(dsNames.isEmpty()){
710           JOptionPane.showMessageDialog(getLargeView(),
711                                         "There are no open datastores!\n " +
712                                         "Please open a datastore first!",
713                                         "Gate", JOptionPane.ERROR_MESSAGE);
714 
715         } else {
716           Object answer = JOptionPane.showInputDialog(
717                               getLargeView(),
718                               "Select the datastore",
719                               "Gate", JOptionPane.QUESTION_MESSAGE,
720                               null, dsNames.toArray(),
721                               dsNames.get(0));
722           if(answer == null) return;
723           DataStore ds = (DataStore)dsByName.get(answer);
724           if (ds == null){
725             Err.prln("The datastore does not exists. Saving procedure" +
726                               " has FAILED! This should never happen again!");
727             return;
728           }// End if
729           DataStore ownDS = ((LanguageResource)target).getDataStore();
730           if(ds == ownDS){
731             StatusListener sListener = (StatusListener)
732                                        gate.gui.MainFrame.getListeners().
733                                        get("gate.event.StatusListener");
734             if(sListener != null) sListener.statusChanged(
735               "Saving: " + ((LanguageResource)target).getName());
736             double timeBefore = System.currentTimeMillis();
737             ds.sync((LanguageResource)target);
738             double timeAfter = System.currentTimeMillis();
739             if(sListener != null) sListener.statusChanged(
740               ((LanguageResource)target).getName() + " saved in " +
741               NumberFormat.getInstance().format((timeAfter-timeBefore)/1000)
742               + " seconds");
743           }else{
744             FeatureMap securityData = (FeatureMap)
745                          Gate.getDataStoreRegister().getSecurityData(ds);
746             SecurityInfo si = null;
747             //check whether the datastore supports security data
748             //serial ones do not for example
749             if (securityData != null) {
750               //first get the type of access from the user
751               if(!AccessRightsDialog.showDialog(window))
752                 return;
753               int accessType = AccessRightsDialog.getSelectedMode();
754               if(accessType < 0)
755                 return;
756               si = new SecurityInfo(accessType,
757                                     (User) securityData.get("user"),
758                                     (Group) securityData.get("group"));
759             }//if security info
760             StatusListener sListener = (StatusListener)
761                                        gate.gui.MainFrame.getListeners().
762                                        get("gate.event.StatusListener");
763             if(sListener != null) sListener.statusChanged(
764               "Saving: " + ((LanguageResource)target).getName());
765             double timeBefore = System.currentTimeMillis();
766             LanguageResource lr = ds.adopt((LanguageResource)target,si);
767             ds.sync(lr);
768             double timeAfter = System.currentTimeMillis();
769             if(sListener != null) sListener.statusChanged(
770               ((LanguageResource)target).getName() + " saved in " +
771               NumberFormat.getInstance().format((timeAfter-timeBefore)/1000)
772               + " seconds");
773 
774             //check whether the new LR is different from the transient one and
775             //if so, unload the transient LR, so the user realises
776             //it is no longer valid. Don't do this in the adopt() code itself
777             //because the batch code might wish to keep the transient
778             //resource for some purpose.
779             if (lr != target) {
780               Factory.deleteResource((LanguageResource)target);
781             }
782           }
783         }
784       } catch(PersistenceException pe) {
785         JOptionPane.showMessageDialog(getLargeView(),
786                                       "Save failed!\n " +
787                                       pe.toString(),
788                                       "Gate", JOptionPane.ERROR_MESSAGE);
789       }catch(gate.security.SecurityException se) {
790         JOptionPane.showMessageDialog(getLargeView(),
791                                       "Save failed!\n " +
792                                       se.toString(),
793                                       "Gate", JOptionPane.ERROR_MESSAGE);
794       }
795     }
796   }//class SaveToAction extends AbstractAction
797 
798   class ReloadAction extends AbstractAction {
799     ReloadAction() {
800       super("Reinitialise");
801       putValue(SHORT_DESCRIPTION, "Reloads this resource");
802     }
803 
804     public void actionPerformed(ActionEvent e) {
805       Runnable runnable = new Runnable(){
806         public void run(){
807           if(!(target instanceof ProcessingResource)) return;
808           try{
809             long startTime = System.currentTimeMillis();
810             fireStatusChanged("Reinitialising " +
811                                target.getName());
812             Map listeners = new HashMap();
813             StatusListener sListener = new StatusListener(){
814                                         public void statusChanged(String text){
815                                           fireStatusChanged(text);
816                                         }
817                                        };
818             listeners.put("gate.event.StatusListener", sListener);
819 
820             ProgressListener pListener =
821                 new ProgressListener(){
822                   public void progressChanged(int value){
823                     fireProgressChanged(value);
824                   }
825                   public void processFinished(){
826                     fireProcessFinished();
827                   }
828                 };
829             listeners.put("gate.event.ProgressListener", pListener);
830 
831             ProcessingResource res = (ProcessingResource)target;
832             try{
833               AbstractResource.setResourceListeners(res, listeners);
834             }catch (Exception e){
835               e.printStackTrace(Err.getPrintWriter());
836             }
837             //show the progress indicator
838             fireProgressChanged(0);
839             //the actual reinitialisation
840             res.reInit();
841             try{
842               AbstractResource.removeResourceListeners(res, listeners);
843             }catch (Exception e){
844               e.printStackTrace(Err.getPrintWriter());
845             }
846             long endTime = System.currentTimeMillis();
847             fireStatusChanged(target.getName() +
848                               " reinitialised in " +
849                               NumberFormat.getInstance().format(
850                               (double)(endTime - startTime) / 1000) + " seconds");
851             fireProcessFinished();
852           }catch(ResourceInstantiationException rie){
853             fireStatusChanged("reinitialisation failed");
854             rie.printStackTrace(Err.getPrintWriter());
855             JOptionPane.showMessageDialog(getLargeView(),
856                                           "Reload failed!\n " +
857                                           "See \"Messages\" tab for details!",
858                                           "Gate", JOptionPane.ERROR_MESSAGE);
859             fireProcessFinished();
860           }
861         }//public void run()
862       };
863       Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
864                                  runnable,
865                                  "DefaultResourceHandle1");
866       thread.setPriority(Thread.MIN_PRIORITY);
867       thread.start();
868     }//public void actionPerformed(ActionEvent e)
869 
870   }//class ReloadAction
871 
872   class PopulateCorpusAction extends AbstractAction {
873     PopulateCorpusAction() {
874       super("Populate");
875       putValue(SHORT_DESCRIPTION,
876                "Fills this corpus with documents from a directory");
877     }
878 
879     public void actionPerformed(ActionEvent e) {
880       Runnable runnable = new Runnable(){
881         public void run(){
882           corpusFiller.setExtensions(new ArrayList());
883           corpusFiller.setEncoding("");
884           boolean answer = OkCancelDialog.showDialog(
885                                   getLargeView(),
886                                   corpusFiller,
887                                   "Select a directory and allowed extensions");
888           if(answer){
889             URL url = null;
890             try{
891               url = new URL(corpusFiller.getUrlString());
892               java.util.List extensions = corpusFiller.getExtensions();
893               ExtensionFileFilter filter = null;
894               if(extensions == null || extensions.isEmpty()) filter = null;
895               else{
896                 filter = new ExtensionFileFilter();
897                 Iterator extIter = corpusFiller.getExtensions().iterator();
898                 while(extIter.hasNext()){
899                   filter.addExtension((String)extIter.next());
900                 }
901               }
902               ((Corpus)target).populate(url, filter,
903                                         corpusFiller.getEncoding(),
904                                         corpusFiller.isRecurseDirectories());
905               fireStatusChanged("Corpus populated!");
906 
907             }catch(MalformedURLException mue){
908               JOptionPane.showMessageDialog(getLargeView(),
909                                             "Invalid URL!\n " +
910                                             "See \"Messages\" tab for details!",
911                                             "Gate", JOptionPane.ERROR_MESSAGE);
912               mue.printStackTrace(Err.getPrintWriter());
913             }catch(IOException ioe){
914               JOptionPane.showMessageDialog(getLargeView(),
915                                             "I/O error!\n " +
916                                             "See \"Messages\" tab for details!",
917                                             "Gate", JOptionPane.ERROR_MESSAGE);
918               ioe.printStackTrace(Err.getPrintWriter());
919             }catch(ResourceInstantiationException rie){
920               JOptionPane.showMessageDialog(getLargeView(),
921                                             "Could not create document!\n " +
922                                             "See \"Messages\" tab for details!",
923                                             "Gate", JOptionPane.ERROR_MESSAGE);
924               rie.printStackTrace(Err.getPrintWriter());
925             }
926           }
927         }
928       };
929       Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
930                                  runnable);
931       thread.setPriority(Thread.MIN_PRIORITY);
932       thread.start();
933     }
934   }
935 
936   /**
937    * Releases the memory, removes the listeners, cleans up.
938    * Will get called when the target resource is unloaded from the system
939    */
940   protected void cleanup(){
941     //delete all the VRs that were created
942     if(largeView != null){
943       if(largeView instanceof VisualResource){
944         //we only had a view so no tabbed pane was used
945         ((VisualResource)largeView).setTarget(null);
946         ((VisualResource)largeView).setHandle(null);
947         Factory.deleteResource((VisualResource)largeView);
948       }else{
949         Component vrs[] = ((JTabbedPane)largeView).getComponents();
950         for(int i = 0; i < vrs.length; i++){
951           if(vrs[i] instanceof VisualResource){
952             Factory.deleteResource((VisualResource)vrs[i]);
953           }
954         }
955       }
956     }
957 
958     if(smallView != null){
959       if(smallView instanceof VisualResource){
960         //we only had a view so no tabbed pane was used
961         Factory.deleteResource((VisualResource)smallView);
962       }else{
963         Component vrs[] = ((JTabbedPane)smallView).getComponents();
964         for(int i = 0; i < vrs.length; i++){
965           if(vrs[i] instanceof VisualResource){
966             ((VisualResource)vrs[i]).setTarget(null);
967             ((VisualResource)vrs[i]).setHandle(null);
968             Factory.deleteResource((VisualResource)vrs[i]);
969           }
970         }
971       }
972     }
973 
974     Gate.getCreoleRegister().removeCreoleListener(this);
975     target = null;
976   }
977 
978   class ProxyStatusListener implements StatusListener{
979     public void statusChanged(String text){
980       fireStatusChanged(text);
981     }
982   }
983 
984   protected void fireProgressChanged(int e) {
985     if (progressListeners != null) {
986       Vector listeners = progressListeners;
987       int count = listeners.size();
988       for (int i = 0; i < count; i++) {
989         ((ProgressListener) listeners.elementAt(i)).progressChanged(e);
990       }
991     }
992   }//protected void fireProgressChanged(int e)
993 
994   protected void fireProcessFinished() {
995     if (progressListeners != null) {
996       Vector listeners = progressListeners;
997       int count = listeners.size();
998       for (int i = 0; i < count; i++) {
999         ((ProgressListener) listeners.elementAt(i)).processFinished();
1000      }
1001    }
1002  }//protected void fireProcessFinished()
1003
1004  public synchronized void removeStatusListener(StatusListener l) {
1005    if (statusListeners != null && statusListeners.contains(l)) {
1006      Vector v = (Vector) statusListeners.clone();
1007      v.removeElement(l);
1008      statusListeners = v;
1009    }
1010  }//public synchronized void removeStatusListener(StatusListener l)
1011
1012  public synchronized void addStatusListener(StatusListener l) {
1013    Vector v = statusListeners == null ? new Vector(2) : (Vector) statusListeners.clone();
1014    if (!v.contains(l)) {
1015      v.addElement(l);
1016      statusListeners = v;
1017    }
1018  }//public synchronized void addStatusListener(StatusListener l)
1019
1020  protected void fireStatusChanged(String e) {
1021    if (statusListeners != null) {
1022      Vector listeners = statusListeners;
1023      int count = listeners.size();
1024      for (int i = 0; i < count; i++) {
1025        ((StatusListener) listeners.elementAt(i)).statusChanged(e);
1026      }
1027    }
1028  }
1029
1030  public void statusChanged(String e) {
1031    fireStatusChanged(e);
1032  }
1033  public void progressChanged(int e) {
1034    fireProgressChanged(e);
1035  }
1036  public void processFinished() {
1037    fireProcessFinished();
1038  }
1039  public Window getWindow() {
1040    return window;
1041  }
1042
1043  public void resourceLoaded(CreoleEvent e) {
1044  }
1045
1046  public void resourceUnloaded(CreoleEvent e) {
1047    if(getTarget() == e.getResource()) cleanup();
1048  }
1049
1050  public void datastoreOpened(CreoleEvent e) {
1051  }
1052
1053  public void datastoreCreated(CreoleEvent e) {
1054  }
1055
1056  public void datastoreClosed(CreoleEvent e) {
1057    if(getTarget() == e.getDatastore()) cleanup();
1058  }
1059}//class DefaultResourceHandle
1060