SerialDatastoreViewer.java
001 /*
002  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *
010  *  Valentin Tablan 23/01/2001
011  *
012  *  $Id: SerialDatastoreViewer.java 17662 2014-03-14 16:19:05Z markagreenwood $
013  *
014  */
015 package gate.gui;
016 
017 import gate.CreoleRegister;
018 import gate.DataStore;
019 import gate.Factory;
020 import gate.FeatureMap;
021 import gate.Gate;
022 import gate.Resource;
023 import gate.VisualResource;
024 import gate.creole.AbstractResource;
025 import gate.creole.ResourceData;
026 import gate.creole.ResourceInstantiationException;
027 import gate.creole.metadata.CreoleResource;
028 import gate.creole.metadata.GuiType;
029 import gate.event.DatastoreEvent;
030 import gate.event.DatastoreListener;
031 import gate.persist.PersistenceException;
032 import gate.util.Err;
033 import gate.util.GateRuntimeException;
034 import gate.util.Strings;
035 
036 import java.awt.event.ActionEvent;
037 import java.awt.event.MouseAdapter;
038 import java.awt.event.MouseEvent;
039 import java.beans.BeanInfo;
040 import java.beans.Introspector;
041 import java.text.NumberFormat;
042 import java.util.Enumeration;
043 import java.util.Iterator;
044 
045 import javax.swing.AbstractAction;
046 import javax.swing.JOptionPane;
047 import javax.swing.JPopupMenu;
048 import javax.swing.JScrollPane;
049 import javax.swing.JTree;
050 import javax.swing.SwingUtilities;
051 import javax.swing.event.TreeExpansionEvent;
052 import javax.swing.event.TreeWillExpandListener;
053 import javax.swing.tree.DefaultMutableTreeNode;
054 import javax.swing.tree.DefaultTreeModel;
055 import javax.swing.tree.DefaultTreeSelectionModel;
056 import javax.swing.tree.TreePath;
057 
058 @SuppressWarnings("serial")
059 @CreoleResource(name = "Serial Datastore Viewer", guiType = GuiType.LARGE,
060     resourceDisplayed = "gate.persist.SerialDataStore", mainViewer = true)
061 public class SerialDatastoreViewer extends JScrollPane implements
062                                                       VisualResource,
063                                                       DatastoreListener {
064 
065   public SerialDatastoreViewer() {
066   }
067 
068   @Override
069   public void cleanup() {
070     datastore.removeDatastoreListener(this);
071     myHandle = null;
072     datastore = null;
073   }
074 
075   /** Accessor for features. */
076   @Override
077   public FeatureMap getFeatures() {
078     return features;
079   }// getFeatures()
080 
081   /** Mutator for features */
082   @Override
083   public void setFeatures(FeatureMap features) {
084     this.features = features;
085   }// setFeatures()
086 
087   // Parameters utility methods
088   /**
089    * Gets the value of a parameter of this resource.
090    
091    @param paramaterName the name of the parameter
092    @return the current value of the parameter
093    */
094   @Override
095   public Object getParameterValue(String paramaterName)
096           throws ResourceInstantiationException {
097     return AbstractResource.getParameterValue(this, paramaterName);
098   }
099 
100   /**
101    * Sets the value for a specified parameter.
102    
103    @param paramaterName the name for the parameteer
104    @param parameterValue the value the parameter will receive
105    */
106   @Override
107   public void setParameterValue(String paramaterName, Object parameterValue)
108           throws ResourceInstantiationException {
109     // get the beaninfo for the resource bean, excluding data about
110     // Object
111     BeanInfo resBeanInf = null;
112     try {
113       resBeanInf = Introspector.getBeanInfo(this.getClass(), Object.class);
114     }
115     catch(Exception e) {
116       throw new ResourceInstantiationException(
117               "Couldn't get bean info for resource "
118                       this.getClass().getName() + Strings.getNl()
119                       "Introspector exception was: " + e);
120     }
121     AbstractResource.setParameterValue(this, resBeanInf, paramaterName,
122             parameterValue);
123   }
124 
125   /**
126    * Sets the values for more parameters in one step.
127    
128    @param parameters a feature map that has paramete names as keys and
129    *          parameter values as values.
130    */
131   @Override
132   public void setParameterValues(FeatureMap parameters)
133           throws ResourceInstantiationException {
134     AbstractResource.setParameterValues(this, parameters);
135   }
136 
137   /** Initialise this resource, and return it. */
138   @Override
139   public Resource init() throws ResourceInstantiationException {
140     return this;
141   }// init()
142 
143   public void clear() {
144   }
145 
146   @Override
147   public void setTarget(Object target) {
148     if(target == null) {
149       datastore = null;
150       return;
151     }
152     if(target instanceof DataStore) {
153       datastore = (DataStore)target;
154       initLocalData();
155       initGuiComponents();
156       initListeners();
157     }
158     else {
159       throw new IllegalArgumentException(
160               "SerialDatastoreViewers can only be used with GATE serial datastores!\n"
161                       + target.getClass().toString()
162                       " is not a GATE serial datastore!");
163     }
164   }
165 
166   @Override
167   public void setHandle(Handle handle) {
168     if(handle instanceof NameBearerHandle) {
169       myHandle = (NameBearerHandle)handle;
170     }
171   }
172 
173   protected void fireProgressChanged(int e) {
174     myHandle.fireProgressChanged(e);
175   }// protected void fireProgressChanged(int e)
176 
177   protected void fireProcessFinished() {
178     myHandle.fireProcessFinished();
179   }// protected void fireProcessFinished()
180 
181   protected void fireStatusChanged(String e) {
182     myHandle.fireStatusChanged(e);
183   }
184 
185   protected void initLocalData() {
186   }
187 
188   protected void initGuiComponents() {
189     treeRoot = new DefaultMutableTreeNode(datastore.getName()true);
190     treeModel = new DefaultTreeModel(treeRoot, true);
191     mainTree = new JTree();
192     mainTree.setModel(treeModel);
193     mainTree.setExpandsSelectedPaths(true);
194     mainTree.expandPath(new TreePath(treeRoot));
195     
196     mainTree.addTreeWillExpandListener(new TreeWillExpandListener() {
197       @Override
198       public void treeWillCollapse(TreeExpansionEvent e) {
199         //ignore these events as we don't care about collapsing trees, timmmmmmmmmber!
200       }
201 
202       @Override
203       public void treeWillExpand(TreeExpansionEvent e) {
204         TreePath path = e.getPath();
205         
206         DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
207 
208         if(node.getChildCount() == && node.getUserObject() instanceof DSType) {
209           DSType dsType = (DSType)node.getUserObject();
210           if (dsType.expandedreturn;
211           node.removeAllChildren();
212           try {
213             Iterator<String> lrIDsIter = datastore.getLrIds(dsType.type).iterator();
214             while(lrIDsIter.hasNext()) {
215               String id = lrIDsIter.next();
216               DSEntry entry =
217                   new DSEntry(datastore.getLrName(id), id, dsType.type);
218               DefaultMutableTreeNode lrNode =
219                   new DefaultMutableTreeNode(entry, false);
220               treeModel.insertNodeInto(lrNode, node, node.getChildCount());
221               node.add(lrNode);
222             }
223             dsType.expanded = true;
224           catch(PersistenceException pe) {
225             throw new GateRuntimeException(pe.toString());
226           }
227         }
228       }
229     });
230     
231     try {
232       Iterator<String> lrTypesIter = datastore.getLrTypes().iterator();
233       CreoleRegister cReg = Gate.getCreoleRegister();
234       while(lrTypesIter.hasNext()) {
235         String type = lrTypesIter.next();
236         ResourceData rData = cReg.get(type);
237         DSType dsType = new DSType(rData.getName(), type);
238         DefaultMutableTreeNode node = new DefaultMutableTreeNode(dsType);
239                
240         treeModel.insertNodeInto(node, treeRoot, treeRoot.getChildCount());
241       }
242     }
243     catch(PersistenceException pe) {
244       throw new GateRuntimeException(pe.toString());
245     }
246     DefaultTreeSelectionModel selectionModel = new DefaultTreeSelectionModel();
247     selectionModel
248             .setSelectionMode(DefaultTreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
249     mainTree.setSelectionModel(selectionModel);
250     getViewport().setView(mainTree);
251 
252     popup = new JPopupMenu();
253     deleteAction = new DeleteAction();
254     loadAction = new LoadAction();
255     popup.add(deleteAction);
256     popup.add(loadAction);
257   }// protected void initGuiComponents()
258 
259   protected void initListeners() {
260     datastore.addDatastoreListener(this);
261     mainTree.addMouseListener(new MouseAdapter() {
262       @Override
263       public void mouseClicked(MouseEvent e) {
264         if(SwingUtilities.isLeftMouseButton(e&& e.getClickCount() == 2) {
265           // double click -> just load the resource
266           TreePath path = mainTree.getPathForLocation(e.getX(), e.getY());
267           Object value = null;
268           if(path != null)
269             value = ((DefaultMutableTreeNode)path.getLastPathComponent())
270                     .getUserObject();
271           if(value != null && value instanceof DSEntry) {
272             loadAction.ignoreSelection = true;
273             loadAction.setLocation(path);
274             loadAction.actionPerformed(null);
275           }
276         }
277       }// public void mouseClicked(MouseEvent e)
278 
279       @Override
280       public void mousePressed(MouseEvent e) {
281         if(e.isPopupTrigger()) {
282           // where inside the tree?
283           TreePath path = mainTree.getPathForLocation(e.getX(), e.getY());
284           deleteAction.setLocation(path);
285           loadAction.setLocation(path);
286           popup.show(SerialDatastoreViewer.this, e.getX(), e.getY());
287         }
288       }
289 
290       @Override
291       public void mouseReleased(MouseEvent e) {
292         if(e.isPopupTrigger()) {
293           // where inside the tree?
294           TreePath path = mainTree.getPathForLocation(e.getX(), e.getY());
295           deleteAction.setLocation(path);
296           loadAction.setLocation(path);
297           popup.show(SerialDatastoreViewer.this, e.getX(), e.getY());
298         }
299       }
300     });
301   }// protected void initListeners()
302 
303   /**
304    * ACtion to delete all selected resources.
305    */
306   class DeleteAction extends AbstractAction {
307     public DeleteAction() {
308       super("Delete");
309     }
310 
311     @Override
312     public void actionPerformed(ActionEvent e) {
313       // delete all selected resources
314       TreePath[] selectedPaths = mainTree.getSelectionPaths();
315       // if no selection -> delete path under cursor
316       if(selectedPaths == null && location != null) {
317         selectedPaths = new TreePath[] {location};
318         location = null;
319       }
320       if(selectedPaths != null) {
321         for(TreePath aPath : selectedPaths) {
322           Object value = ((DefaultMutableTreeNode)aPath.getLastPathComponent())
323                   .getUserObject();
324           if(value instanceof DSEntry) {
325             DSEntry entry = (DSEntry)value;
326             try {
327               datastore.delete(entry.type, entry.id);
328               // project.frame.resourcesTreeModel.treeChanged();
329             }
330             catch(gate.persist.PersistenceException pe) {
331               JOptionPane.showMessageDialog(SerialDatastoreViewer.this,
332                       "Error!\n" + pe.toString()"GATE",
333                       JOptionPane.ERROR_MESSAGE);
334               pe.printStackTrace(Err.getPrintWriter());
335             }
336             catch(SecurityException se) {
337               JOptionPane.showMessageDialog(SerialDatastoreViewer.this,
338                       "Error!\n" + se.toString()"GATE",
339                       JOptionPane.ERROR_MESSAGE);
340               se.printStackTrace(Err.getPrintWriter());
341             }
342           }
343         }
344       }
345     }
346 
347     /**
348      * The path where the mouse click occurred.
349      */
350     TreePath location;
351 
352     public TreePath getLocation() {
353       return location;
354     }
355 
356     public void setLocation(TreePath location) {
357       this.location = location;
358     }
359   }
360 
361   /**
362    * Action to load all selected resources.
363    */
364   class LoadAction extends AbstractAction {
365     public LoadAction() {
366       super("Load");
367     }
368 
369     @Override
370     public void actionPerformed(ActionEvent e) {
371       Runnable runner = new Runnable(){
372         @Override
373         public void run(){
374           // load all selected resources
375           TreePath[] selectedPaths = mainTree.getSelectionPaths();
376           if(ignoreSelection){
377             ignoreSelection = false;
378            selectedPaths = null;
379           }
380           // if no selection -> load path under cursor
381           if(selectedPaths == null && location != null) {
382             selectedPaths = new TreePath[] {location};
383             location = null;
384           }
385           if(selectedPaths != null) {
386             for(TreePath aPath : selectedPaths) {
387               Object value = ((DefaultMutableTreeNode)aPath.getLastPathComponent())
388                       .getUserObject();
389               if(value instanceof DSEntry) {
390                 DSEntry entry = (DSEntry)value;
391                 try {
392                   MainFrame.lockGUI("Loading " + entry.name);
393                   long start = System.currentTimeMillis();
394                   fireStatusChanged("Loading " + entry.name);
395                   fireProgressChanged(0);
396                   FeatureMap params = Factory.newFeatureMap();
397                   params.put(DataStore.DATASTORE_FEATURE_NAME, datastore);
398                   params.put(DataStore.LR_ID_FEATURE_NAME, entry.id);
399                   FeatureMap features = Factory.newFeatureMap();
400                   Factory.createResource(entry.type, params, features,
401                           entry.name);
402                   // project.frame.resourcesTreeModel.treeChanged();
403                   fireProgressChanged(0);
404                   fireProcessFinished();
405                   long end = System.currentTimeMillis();
406                   fireStatusChanged(entry.name
407                           " loaded in "
408                           + NumberFormat.getInstance().format(
409                                   (double)(end - start1000" seconds");
410                 }
411                 catch(ResourceInstantiationException rie) {
412                   MainFrame.unlockGUI();
413                   JOptionPane.showMessageDialog(SerialDatastoreViewer.this,
414                           "Error!\n" + rie.toString()"GATE",
415                           JOptionPane.ERROR_MESSAGE);
416                   rie.printStackTrace(Err.getPrintWriter());
417                   fireProgressChanged(0);
418                   fireProcessFinished();
419                 }
420                 finally {
421                   MainFrame.unlockGUI();
422                 }            
423               }
424             }
425           }  
426         }
427       };
428       Thread thread = new Thread(runner, 
429               SerialDatastoreViewer.this.getClass().getCanonicalName() +  
430               " DS Loader");
431       thread.setPriority(Thread.MIN_PRIORITY);
432       thread.start();
433     }
434 
435     /**
436      * The path where the mouse click occurred.
437      */
438     protected TreePath location;
439     
440     protected boolean ignoreSelection = false;
441 
442     public TreePath getLocation() {
443       return location;
444     }
445 
446     public void setLocation(TreePath location) {
447       this.location = location;
448     }
449   }
450 
451   static class DSEntry {
452     DSEntry(String name, String id, String type) {
453       this.name = name;
454       this.type = type;
455       this.id = id;
456     }// DSEntry
457 
458     @Override
459     public String toString() {
460       return name;
461     }
462 
463 
464     String name;
465 
466     String type;
467 
468     String id;
469 
470   }// class DSEntry
471   
472   static class DSType {
473     String name, type;
474     boolean expanded = false;
475     
476     DSType(String name, String type) {
477       this.name = name;
478       this.type = type;
479     }
480     
481     @Override
482     public String toString() {
483       return name;
484     }
485   }
486 
487   DefaultMutableTreeNode treeRoot;
488 
489   DefaultTreeModel treeModel;
490 
491   JTree mainTree;
492 
493   DataStore datastore;
494 
495   NameBearerHandle myHandle;
496 
497   /**
498    * Action used to delete selected resources
499    */
500   protected DeleteAction deleteAction;
501 
502   /**
503    * Action object for loading resources.
504    */
505   protected LoadAction loadAction;
506 
507   /**
508    * The popup used for actions.
509    */
510   protected JPopupMenu popup;
511 
512   protected FeatureMap features;
513   
514   @Override
515   public void resourceAdopted(DatastoreEvent e) {
516     // do nothing; SerialDataStore does actually nothing on adopt()
517     // we'll have to listen for RESOURE_WROTE events
518   }
519 
520   @Override
521   public void resourceDeleted(DatastoreEvent e) {
522     String resID = (String)e.getResourceID();
523     DefaultMutableTreeNode node = null;
524     Enumeration<?> nodesEnum = treeRoot.depthFirstEnumeration();
525     boolean found = false;
526     while(nodesEnum.hasMoreElements() && !found) {
527       node = (DefaultMutableTreeNode)nodesEnum.nextElement();
528       Object userObject = node.getUserObject();
529       found = userObject instanceof DSEntry
530               && ((DSEntry)userObject).id.equals(resID);
531     }
532     if(found) {
533       DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
534       treeModel.removeNodeFromParent(node);
535       if(parent.getChildCount() == 0treeModel.removeNodeFromParent(parent);
536     }
537   }
538 
539   @Override
540   public void resourceWritten(DatastoreEvent e) {
541     Resource res = e.getResource();
542     String resID = (String)e.getResourceID();
543     String resType = Gate.getCreoleRegister().get(
544             res.getClass().getName()).getName();
545     DefaultMutableTreeNode parent = treeRoot;
546     DefaultMutableTreeNode node = null;
547     // first look for the type node
548     Enumeration<?> childrenEnum = parent.children();
549     boolean found = false;
550     while(childrenEnum.hasMoreElements() && !found) {
551       node = (DefaultMutableTreeNode)childrenEnum.nextElement();
552       if (node.getUserObject() instanceof DSType) {
553         found = ((DSType)node.getUserObject()).name.equals(resType);
554       }
555     }
556     if(!found) {
557       // exhausted the children without finding the node -> new type
558       node = new DefaultMutableTreeNode(new DSType(resType, res.getClass().getName()));
559       treeModel.insertNodeInto(node, parent, parent.getChildCount());
560     }
561     //mainTree.expandPath(new TreePath(new Object[] {parent, node}));
562 
563     if (node.getUserObject() instanceof DSType) {
564       if (!((DSType)node.getUserObject()).expandedreturn;
565     }
566     
567     // now look for the resource node
568     parent = node;
569     childrenEnum = parent.children();
570     found = false;
571     while(childrenEnum.hasMoreElements() && !found) {
572       node = (DefaultMutableTreeNode)childrenEnum.nextElement();
573       found = ((DSEntry)node.getUserObject()).id.equals(resID);
574     }
575     if(!found) {
576       // exhausted the children without finding the node -> new resource
577       try {
578         DSEntry entry = new DSEntry(datastore.getLrName(resID), resID, res
579                 .getClass().getName());
580         node = new DefaultMutableTreeNode(entry, false);
581         treeModel.insertNodeInto(node, parent, parent.getChildCount());
582       }
583       catch(PersistenceException pe) {
584         pe.printStackTrace(Err.getPrintWriter());
585       }
586     }
587   }// public void resourceWritten(DatastoreEvent e)
588 
589 }// public class DSHandle