1   /*
2    *  Copyright (c) 1998-2005, 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 15 May 2002
10   *
11   *  $Id: SearchPRViewer.java,v 1.5 2005/01/11 13:51:34 ian Exp $
12   */
13  package gate.gui;
14  
15  import java.awt.*;
16  import java.awt.event.MouseAdapter;
17  import java.awt.event.MouseEvent;
18  import java.text.NumberFormat;
19  import java.util.*;
20  import java.util.List;
21  
22  import javax.swing.*;
23  import javax.swing.table.AbstractTableModel;
24  import javax.swing.table.TableCellRenderer;
25  
26  import gate.*;
27  import gate.creole.AbstractVisualResource;
28  import gate.creole.ir.*;
29  import gate.event.ProgressListener;
30  import gate.swing.XJTable;
31  
32  
33  /**
34   * Shows the results of a IR query. This VR is associated to
35   * {@link gate.creole.ir.SearchPR}.
36   */
37  public class SearchPRViewer extends AbstractVisualResource
38                              implements ProgressListener{
39  
40    public Resource init(){
41      initLocalData();
42      initGuiComponents();
43      initListeners();
44      return this;
45    }
46  
47    protected void initLocalData(){
48      results = new ArrayList();
49    }
50  
51    protected void initGuiComponents(){
52      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
53      resultsTableModel = new ResultsTableModel();
54      resultsTable = new XJTable(resultsTableModel);
55      resultsTable.getColumnModel().getColumn(1).
56                   setCellRenderer(new FloatRenderer());
57      add(new JScrollPane(resultsTable));
58  //    add(Box.createHorizontalGlue());
59    }
60  
61    protected void initListeners(){
62      resultsTable.addMouseListener(new MouseAdapter() {
63        public void mouseClicked(MouseEvent e) {
64          if(e.getClickCount() == 2){
65            //where inside the table
66            selectDocument((String)resultsTable.getValueAt(
67                              resultsTable.rowAtPoint(e.getPoint()), 0));
68          }
69        }
70      });
71    }
72  
73    /**
74     * Tries to load (if necessary) and select (i.e. bring to front) a document
75     * based on its name.
76     */
77    protected void selectDocument(String documentName){
78      Corpus corpus = target.getCorpus();
79      int i = 0;
80      for(;
81          i < corpus.size() &&
82          (!corpus.getDocumentName(i).equals(documentName));
83          i++);
84      if(corpus.getDocumentName(i).equals(documentName)){
85        //trigger document loading if needed
86        Document doc = (Document)corpus.get(i);
87        //try to select the document
88        Component root = SwingUtilities.getRoot(this);
89        if(root instanceof MainFrame){
90          MainFrame mainFrame = (MainFrame)root;
91          mainFrame.select(doc);
92        }
93      }
94    }
95  
96    /**
97     * Called by the GUI when this viewer/editor has to initialise itself for a
98     * specific object.
99     * @param target the object (be it a {@link gate.Resource},
100    * {@link gate.DataStore} or whatever) this viewer has to display
101    */
102   public void setTarget(Object target){
103     if(!(target instanceof SearchPR)){
104       throw new IllegalArgumentException(
105         "The GATE IR results viewer can only be used with a GATE search PR!\n" +
106         target.getClass().toString() + " is not a GATE search PR!");
107     }
108     this.target = (SearchPR)target;
109     this.target.addProgressListener(this);
110   }
111 
112   /**
113    * Does nothing.
114    * @param i
115    */
116   public void progressChanged(int i){}
117 
118   /**
119    * Called when the process is finished, fires a refresh for this VR.
120    */
121   public void processFinished(){
122     updateDisplay();
123   }
124 
125   protected void updateDisplay(){
126     results.clear();
127     if(target != null){
128       QueryResultList resultsList = target.getResult();
129       Iterator resIter = resultsList.getQueryResults();
130       while(resIter.hasNext()){
131         results.add(resIter.next());
132       }
133       SwingUtilities.invokeLater(new Runnable(){
134         public void run(){
135           resultsTableModel.fireTableDataChanged();
136         }
137       });
138     }
139   }
140 
141   protected class ResultsTableModel extends AbstractTableModel{
142     public int getRowCount(){
143       return results.size();
144     }
145 
146     public int getColumnCount(){
147       return 2;
148     }
149 
150     public String getColumnName(int columnIndex){
151       switch(columnIndex){
152         case DOC_NAME_COLUMN: return "Document";
153         case DOC_SCORE_COLUMN: return "Score";
154         default: return "?";
155       }
156     }
157 
158     public Class getColumnClass(int columnIndex){
159       switch(columnIndex){
160         case DOC_NAME_COLUMN: return String.class;
161         case DOC_SCORE_COLUMN: return Float.class;
162         default: return Object.class;
163       }
164     }
165 
166     public boolean isCellEditable(int rowIndex, int columnIndex){
167       return false;
168     }
169 
170     public Object getValueAt(int rowIndex, int columnIndex){
171       QueryResult aResult = (QueryResult)results.get(rowIndex);
172       switch(columnIndex){
173         case DOC_NAME_COLUMN: return guessDocName(aResult.getDocumentID()).
174                                      toString();
175         case DOC_SCORE_COLUMN: return new Float(aResult.getScore());
176         default: return null;
177       }
178     }
179 
180     /**
181      * Attempts to guess the document name from the ID returned by the searcher
182      */
183     protected Object guessDocName(Object docID){
184       //the doc ID is most likely the persistence ID for the doc
185       Corpus corpus = target.getCorpus();
186       DataStore ds = corpus.getDataStore();
187       if(ds != null){
188         try{
189           return ds.getLrName(docID);
190         }catch(Exception e){}
191       }
192       //we couldn't guess anything
193       return docID;
194     }
195 
196     static private final int DOC_NAME_COLUMN = 0;
197     static private final int DOC_SCORE_COLUMN = 1;
198   }
199 
200   protected class FloatRenderer extends JProgressBar
201                                 implements TableCellRenderer{
202     public FloatRenderer(){
203       setStringPainted(true);
204       setForeground(new Color(150, 75, 150));
205       setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
206       setMinimum(0);
207       //we'll use 3 decimal digits
208       setMaximum(1000);
209       numberFormat = NumberFormat.getInstance(Locale.getDefault());
210       numberFormat.setMaximumFractionDigits(3);
211     }
212 
213 
214     public Component getTableCellRendererComponent(JTable table,
215                                                    Object value,
216                                                    boolean isSelected,
217                                                    boolean hasFocus,
218                                                    int row,
219                                                    int column){
220 
221       float fValue = ((Float)value).floatValue();
222       setValue((int)(fValue * 1000));
223       setBackground(table.getBackground());
224 
225       setString(numberFormat.format(value));
226       return this;
227     }
228 
229     /*
230      * The following methods are overridden as a performance measure to
231      * to prune code-paths are often called in the case of renders
232      * but which we know are unnecessary.
233      */
234 
235     /**
236      * Overridden for performance reasons.
237      */
238     public boolean isOpaque() {
239       Color back = getBackground();
240       Component p = getParent();
241       if (p != null) {
242         p = p.getParent();
243       }
244       // p should now be the JTable.
245       boolean colorMatch = (back != null) && (p != null) &&
246       back.equals(p.getBackground()) &&
247       p.isOpaque();
248       return !colorMatch && super.isOpaque();
249     }
250 
251     /**
252      * Overridden for performance reasons.
253      */
254     public void validate() {}
255 
256     /**
257      * Overridden for performance reasons.
258      */
259     public void revalidate() {}
260 
261     /**
262      * Overridden for performance reasons.
263      */
264     public void repaint(long tm, int x, int y, int width, int height) {}
265 
266     /**
267      * Overridden for performance reasons.
268      */
269     public void repaint(Rectangle r) { }
270 
271     /**
272      * Overridden for performance reasons.
273      */
274     protected void firePropertyChange(String propertyName, Object oldValue,
275                                       Object newValue) {
276       // Strings get interned...
277       if (propertyName=="text") {
278         super.firePropertyChange(propertyName, oldValue, newValue);
279       }
280     }
281 
282     /**
283      * Overridden for performance reasons.
284      */
285     public void firePropertyChange(String propertyName, boolean oldValue,
286                                    boolean newValue) { }
287 
288     NumberFormat numberFormat;
289   }
290 
291   /**
292    * The search PR this VR is associated to.
293    */
294   SearchPR target;
295 
296   /**
297    * The table displaying the results
298    */
299   XJTable resultsTable;
300 
301   /**
302    * The model for the results table.
303    */
304   ResultsTableModel resultsTableModel;
305 
306   /**
307    * Contains the {@link gate.creole.ir.QueryResult} objects returned by the
308    * search.
309    */
310   List results;
311 
312 }