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    *  AnnotatioListView.java
10   *
11   *  Valentin Tablan, May 25, 2004
12   *
13   *  $Id: AnnotationListView.java,v 1.15 2004/07/21 17:10:07 akshay Exp $
14   */
15  
16  package gate.gui.docview;
17  
18  import gate.*;
19  import gate.Annotation;
20  import gate.AnnotationSet;
21  import gate.event.AnnotationEvent;
22  import gate.event.AnnotationListener;
23  import gate.swing.XJTable;
24  import gate.util.GateRuntimeException;
25  import java.awt.*;
26  import java.awt.Component;
27  import java.awt.GridBagLayout;
28  import java.util.*;
29  import java.util.List;
30  import java.util.Map;
31  import javax.swing.*;
32  import javax.swing.JScrollPane;
33  import javax.swing.SwingUtilities;
34  import javax.swing.event.*;
35  import javax.swing.event.TableModelEvent;
36  import javax.swing.event.TableModelListener;
37  import javax.swing.table.AbstractTableModel;
38  
39  /**
40   * A tabular view for a list of annotations.
41   * Used as part of the document viewer to display all the annotation currently 
42   * highlighted.
43   */
44  public class AnnotationListView extends AbstractDocumentView 
45      implements AnnotationListener{
46    public AnnotationListView(){
47      tagList = new ArrayList();
48      annotationHandlerByTag = new HashMap();
49    }
50    
51    /* (non-Javadoc)
52     * @see gate.gui.docview.AbstractDocumentView#initGUI()
53     */
54    protected void initGUI() {
55      tableModel = new AnnotationTableModel();
56      table = new XJTable(tableModel);
57      table.setAutoResizeMode(XJTable.AUTO_RESIZE_OFF);
58      table.setSortable(true);
59      table.setSortedColumn(START_COL);
60      table.setIntercellSpacing(new Dimension(2, 0));
61      scroller = new JScrollPane(table);
62      
63      mainPanel = new JPanel();
64      mainPanel.setLayout(new GridBagLayout());
65      GridBagConstraints constraints = new GridBagConstraints();
66      
67      constraints.gridx = 0;
68      constraints.gridy = 0;
69      constraints.weightx = 1;
70      constraints.weighty = 1;
71      constraints.fill= GridBagConstraints.BOTH;
72      mainPanel.add(scroller, constraints);
73      
74      constraints.gridy = 1;
75      constraints.weightx = 0;
76      constraints.weighty = 0;
77      constraints.fill= GridBagConstraints.NONE;
78      constraints.anchor = GridBagConstraints.WEST;
79      statusLabel = new JLabel();
80      mainPanel.add(statusLabel, constraints);
81      
82      //get a pointer to the text view used to display
83      //the selected annotations 
84      Iterator centralViewsIter = owner.getCentralViews().iterator();
85      while(textView == null && centralViewsIter.hasNext()){
86        DocumentView aView = (DocumentView)centralViewsIter.next();
87        if(aView instanceof TextualDocumentView)  
88          textView = (TextualDocumentView)aView;
89      }
90      
91      initListeners();
92    }
93    
94    public Component getGUI(){
95      return mainPanel;
96    }
97    protected void initListeners(){
98  //    table.addComponentListener(new ComponentAdapter(){
99  //      public void componentShown(ComponentEvent e){
100 //        //trigger a resize for the columns
101 //        table.adjustSizes();
102 //      }
103 //    });
104 
105     tableModel.addTableModelListener(new TableModelListener(){
106       public void tableChanged(TableModelEvent e){
107         statusLabel.setText(
108                 Integer.toString(tableModel.getRowCount()) + 
109                 " Annotations (" +
110                 Integer.toString(table.getSelectedRowCount()) +
111                 " selected)");
112       }
113     });
114     
115     table.getSelectionModel().
116       addListSelectionListener(new ListSelectionListener(){
117         public void valueChanged(ListSelectionEvent e){
118           statusLabel.setText(
119                   Integer.toString(tableModel.getRowCount()) + 
120                   " Annotations (" +
121                   Integer.toString(table.getSelectedRowCount()) +
122                   "selected)");
123           //blink the selected annotations
124           textView.removeAllBlinkingHighlights();
125           int[] rows = table.getSelectedRows();
126           for(int i = 0; i < rows.length; i++){
127             Object tag = tagList.get(table.rowViewToModel(rows[i]));
128             AnnotationHandler aHandler = (AnnotationHandler)
129               annotationHandlerByTag.get(tag);
130             textView.addBlinkingHighlight(aHandler.ann);
131           }
132         }
133     });
134     
135   }
136   /* (non-Javadoc)
137    * @see gate.gui.docview.AbstractDocumentView#registerHooks()
138    */
139   protected void registerHooks() {
140     //this view is a slave only view so it has no hooks to register
141   }
142   
143   /* (non-Javadoc)
144    * @see gate.gui.docview.AbstractDocumentView#unregisterHooks()
145    */
146   protected void unregisterHooks() {
147     //this view is a slave only view so it has no hooks to register  
148   }
149     
150   /* (non-Javadoc)
151    * @see gate.gui.docview.DocumentView#getType()
152    */
153   public int getType() {
154     return HORIZONTAL;
155   }
156   protected void guiShown(){
157     tableModel.fireTableDataChanged();
158   }
159   
160   
161   public void addAnnotation(Object tag, Annotation ann, AnnotationSet set){
162     AnnotationHandler aHandler = new AnnotationHandler(set, ann);
163     Object oldValue = annotationHandlerByTag.put(tag, aHandler);
164     if(oldValue == null){
165       //new value
166       tagList.add(tag);
167       int row = tagList.size() -1;
168       if(tableModel != null) tableModel.fireTableRowsInserted(row, row);
169     }else{
170       //update old value
171       int row = tagList.indexOf(tag);
172       if(tableModel != null) tableModel.fireTableRowsUpdated(row,row);
173     }
174     //listen for the new annotation's events
175     ann.addAnnotationListener(this);
176   }
177   
178   public void removeAnnotation(Object tag){
179     int row = tagList.indexOf(tag);
180     if(row >= 0){
181       tagList.remove(row);
182       AnnotationHandler aHandler = (AnnotationHandler)
183           annotationHandlerByTag.remove(tag);
184       if(aHandler != null)aHandler.ann.removeAnnotationListener(this);
185       if(tableModel != null) tableModel.fireTableRowsDeleted(row, row);
186     }
187   }
188   
189   /**
190    * Adds a batch of annotations in one go. The tags and annotations collections
191    * are accessed through their iterators which are expected to return the
192    * corresponding tag for the right annotation.
193    * This method does not assume it was called from the UI Thread.
194    * @param tags a collection of tags
195    * @param annotations a collection of annotations
196    * @param set the annotation set to which all the annotations belong.
197    */
198   public void addAnnotations(Collection tags, Collection annotations, 
199           AnnotationSet set){
200     if(tags.size() != annotations.size()) throw new GateRuntimeException(
201             "Invalid invocation - different numbers of annotations and tags!");
202     Iterator tagIter = tags.iterator();
203     Iterator annIter = annotations.iterator();
204     while(tagIter.hasNext()){
205       Object tag = tagIter.next();
206       Annotation ann = (Annotation)annIter.next();
207       AnnotationHandler aHandler = new AnnotationHandler(set, ann);
208       Object oldValue = annotationHandlerByTag.put(tag, aHandler);
209       if(oldValue == null){
210         //new value
211         tagList.add(tag);
212         int row = tagList.size() -1;
213       }else{
214         //update old value
215         int row = tagList.indexOf(tag);
216       }
217       //listen for the new annotation's events
218       ann.addAnnotationListener(this);
219     }
220     SwingUtilities.invokeLater(new Runnable(){
221       public void run(){
222         if(tableModel != null) tableModel.fireTableDataChanged();
223       }
224     });
225   }
226   
227   public void removeAnnotations(Collection tags){
228     Iterator tagIter = tags.iterator();
229     while(tagIter.hasNext()){
230       Object tag = tagIter.next();
231       int row = tagList.indexOf(tag);
232       if(row >= 0){
233         tagList.remove(row);
234         AnnotationHandler aHandler = (AnnotationHandler)
235             annotationHandlerByTag.remove(tag);
236         if(aHandler != null)aHandler.ann.removeAnnotationListener(this);
237       }
238     }
239     SwingUtilities.invokeLater(new Runnable(){
240       public void run(){
241         if(tableModel != null) tableModel.fireTableDataChanged();
242       }
243     });
244   }
245   
246   public void annotationUpdated(AnnotationEvent e){
247     //update all occurrences of this annotation
248     Annotation ann = (Annotation)e.getSource();
249     for(int i = 0; i < tagList.size(); i++){
250       Object tag = tagList.get(i);
251       if(((AnnotationHandler)annotationHandlerByTag.get(tag)).ann == ann){
252         if(tableModel != null)tableModel.fireTableRowsUpdated(i, i);
253       }
254     }
255   }
256   
257   class AnnotationTableModel extends AbstractTableModel{
258     public int getRowCount(){
259       return tagList.size();
260     }
261     
262     public int getColumnCount(){
263       return 5;
264     }
265     
266     public String getColumnName(int column){
267       switch(column){
268         case TYPE_COL: return "Type";
269         case SET_COL: return "Set";
270         case START_COL: return "Start";
271         case END_COL: return "End";
272         case FEATURES_COL: return "Features";
273         default: return "?";
274       }
275     }
276       
277     public Class getColumnClass(int column){
278       switch(column){
279         case TYPE_COL: return String.class;
280         case SET_COL: return String.class;
281         case START_COL: return Long.class;
282         case END_COL: return Long.class;
283         case FEATURES_COL: return String.class;
284         default: return String.class;
285       }
286     }
287     
288     public boolean isCellEditable(int rowIndex, int columnIndex){
289       return false;
290     }
291     
292     public Object getValueAt(int row, int column){
293       AnnotationHandler aHandler = (AnnotationHandler)annotationHandlerByTag.
294         get(tagList.get(row));
295       switch(column){
296         case TYPE_COL: return aHandler.ann.getType();
297         case SET_COL: return aHandler.set.getName();
298         case START_COL: return aHandler.ann.getStartNode().getOffset();
299         case END_COL: return aHandler.ann.getEndNode().getOffset();
300         case FEATURES_COL:
301           //sort the features by name
302           FeatureMap features = aHandler.ann.getFeatures();
303           List keyList = new ArrayList(features.keySet());
304           Collections.sort(keyList);
305           StringBuffer strBuf = new StringBuffer("{");
306           Iterator keyIter = keyList.iterator();
307           boolean first = true;
308           while(keyIter.hasNext()){
309             Object key = keyIter.next();
310             Object value = features.get(key);
311             if(first){
312               first = false;
313             }else{
314               strBuf.append(", ");
315             }
316             strBuf.append(key.toString());
317             strBuf.append("=");
318             strBuf.append(value == null ? "[null]" : value.toString());
319           }
320           strBuf.append("}");
321           return strBuf.toString();
322         default: return "?";
323       }
324     }
325     
326   }
327   
328   protected static class AnnotationHandler{
329     public AnnotationHandler(AnnotationSet set, Annotation ann){
330       this.ann = ann;
331       this.set = set;
332     }
333     Annotation ann;
334     AnnotationSet set;
335   }
336 
337   protected XJTable table;
338   protected AnnotationTableModel tableModel;
339   protected JScrollPane scroller;
340   protected Map annotationHandlerByTag;
341   protected List tagList;
342   protected JPanel mainPanel;
343   protected JLabel statusLabel;
344   protected TextualDocumentView textView;
345   
346   private static final int TYPE_COL = 0;
347   private static final int SET_COL = 1;
348   private static final int START_COL = 2;
349   private static final int END_COL = 3;
350   private static final int FEATURES_COL = 4;
351   
352 }
353