SearchPRViewer.java
001 /*
002  *  Copyright (c) 1995-2010, 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 15 May 2002
011  *
012  *  $Id: SearchPRViewer.java 12006 2009-12-01 17:24:28Z thomas_heitz $
013  */
014 package gate.gui;
015 
016 import java.awt.*;
017 import java.awt.event.MouseAdapter;
018 import java.awt.event.MouseEvent;
019 import java.text.NumberFormat;
020 import java.util.*;
021 import java.util.List;
022 
023 import javax.swing.*;
024 import javax.swing.table.AbstractTableModel;
025 import javax.swing.table.TableCellRenderer;
026 
027 import gate.*;
028 import gate.creole.AbstractVisualResource;
029 import gate.creole.ir.*;
030 import gate.event.ProgressListener;
031 import gate.swing.XJTable;
032 
033 
034 /**
035  * Shows the results of a IR query. This VR is associated to
036  {@link gate.creole.ir.SearchPR}.
037  */
038 public class SearchPRViewer extends AbstractVisualResource
039                             implements ProgressListener{
040 
041   public Resource init(){
042     initLocalData();
043     initGuiComponents();
044     initListeners();
045     return this;
046   }
047 
048   protected void initLocalData(){
049     results = new ArrayList();
050   }
051 
052   protected void initGuiComponents(){
053     setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
054     resultsTableModel = new ResultsTableModel();
055     resultsTable = new XJTable(resultsTableModel);
056     resultsTable.getColumnModel().getColumn(1).
057                  setCellRenderer(new FloatRenderer());
058     add(new JScrollPane(resultsTable));
059 //    add(Box.createHorizontalGlue());
060   }
061 
062   protected void initListeners(){
063     resultsTable.addMouseListener(new MouseAdapter() {
064       public void mouseClicked(MouseEvent e) {
065         if(e.getClickCount() == 2){
066           //where inside the table
067           selectDocument((String)resultsTable.getValueAt(
068                             resultsTable.rowAtPoint(e.getPoint())0));
069         }
070       }
071     });
072   }
073 
074   /**
075    * Tries to load (if necessary) and select (i.e. bring to front) a document
076    * based on its name.
077    */
078   protected void selectDocument(String documentName){
079     Corpus corpus = target.getCorpus();
080     int i = 0;
081     for(;
082         i < corpus.size() &&
083         (!corpus.getDocumentName(i).equals(documentName));
084         i++);
085     if(corpus.getDocumentName(i).equals(documentName)){
086       //trigger document loading if needed
087       Document doc = (Document)corpus.get(i);
088       //try to select the document
089       Component root = SwingUtilities.getRoot(this);
090       if(root instanceof MainFrame){
091         MainFrame mainFrame = (MainFrame)root;
092         mainFrame.select(doc);
093       }
094     }
095   }
096 
097   /**
098    * Called by the GUI when this viewer/editor has to initialise itself for a
099    * specific object.
100    @param target the object (be it a {@link gate.Resource},
101    {@link gate.DataStore} or whatever) this viewer has to display
102    */
103   public void setTarget(Object target){
104     if(!(target instanceof SearchPR)){
105       throw new IllegalArgumentException(
106         "The GATE IR results viewer can only be used with a GATE search PR!\n" +
107         target.getClass().toString() " is not a GATE search PR!");
108     }
109     this.target = (SearchPR)target;
110     this.target.addProgressListener(this);
111   }
112 
113   /**
114    * Does nothing.
115    @param i
116    */
117   public void progressChanged(int i){}
118 
119   /**
120    * Called when the process is finished, fires a refresh for this VR.
121    */
122   public void processFinished(){
123     updateDisplay();
124   }
125 
126   protected void updateDisplay(){
127     results.clear();
128     if(target != null){
129       QueryResultList resultsList = target.getResult();
130       Iterator resIter = resultsList.getQueryResults();
131       while(resIter.hasNext()){
132         results.add(resIter.next());
133       }
134       SwingUtilities.invokeLater(new Runnable(){
135         public void run(){
136           resultsTableModel.fireTableDataChanged();
137         }
138       });
139     }
140   }
141 
142   protected class ResultsTableModel extends AbstractTableModel{
143     public int getRowCount(){
144       return results.size();
145     }
146 
147     public int getColumnCount(){
148       return 2;
149     }
150 
151     public String getColumnName(int columnIndex){
152       switch(columnIndex){
153         case DOC_NAME_COLUMN: return "Document";
154         case DOC_SCORE_COLUMN: return "Score";
155         defaultreturn "?";
156       }
157     }
158 
159     public Class getColumnClass(int columnIndex){
160       switch(columnIndex){
161         case DOC_NAME_COLUMN: return String.class;
162         case DOC_SCORE_COLUMN: return Float.class;
163         defaultreturn Object.class;
164       }
165     }
166 
167     public boolean isCellEditable(int rowIndex, int columnIndex){
168       return false;
169     }
170 
171     public Object getValueAt(int rowIndex, int columnIndex){
172       QueryResult aResult = (QueryResult)results.get(rowIndex);
173       switch(columnIndex){
174         case DOC_NAME_COLUMN: return guessDocName(aResult.getDocumentID()).
175                                      toString();
176         case DOC_SCORE_COLUMN: return new Float(aResult.getScore());
177         defaultreturn null;
178       }
179     }
180 
181     /**
182      * Attempts to guess the document name from the ID returned by the searcher
183      */
184     protected Object guessDocName(Object docID){
185       //the doc ID is most likely the persistence ID for the doc
186       Corpus corpus = target.getCorpus();
187       DataStore ds = corpus.getDataStore();
188       if(ds != null){
189         try{
190           return ds.getLrName(docID);
191         }catch(Exception e){}
192       }
193       //we couldn't guess anything
194       return docID;
195     }
196 
197     static private final int DOC_NAME_COLUMN = 0;
198     static private final int DOC_SCORE_COLUMN = 1;
199   }
200 
201   protected class FloatRenderer extends JProgressBar
202                                 implements TableCellRenderer{
203     public FloatRenderer(){
204       setStringPainted(true);
205       setForeground(new Color(15075150));
206       setBorder(BorderFactory.createEmptyBorder(2222));
207       setMinimum(0);
208       //we'll use 3 decimal digits
209       setMaximum(1000);
210       numberFormat = NumberFormat.getInstance(Locale.getDefault());
211       numberFormat.setMaximumFractionDigits(3);
212     }
213 
214 
215     public Component getTableCellRendererComponent(JTable table,
216                                                    Object value,
217                                                    boolean isSelected,
218                                                    boolean hasFocus,
219                                                    int row,
220                                                    int column){
221 
222       float fValue = ((Float)value).floatValue();
223       setValue((int)(fValue * 1000));
224       setBackground(table.getBackground());
225 
226       setString(numberFormat.format(value));
227       return this;
228     }
229 
230     /*
231      * The following methods are overridden as a performance measure to
232      * to prune code-paths are often called in the case of renders
233      * but which we know are unnecessary.
234      */
235 
236     /**
237      * Overridden for performance reasons.
238      */
239     public boolean isOpaque() {
240       Color back = getBackground();
241       Component p = getParent();
242       if (p != null) {
243         p = p.getParent();
244       }
245       // p should now be the JTable.
246       boolean colorMatch = (back != null&& (p != null&&
247       back.equals(p.getBackground()) &&
248       p.isOpaque();
249       return !colorMatch && super.isOpaque();
250     }
251 
252     /**
253      * Overridden for performance reasons.
254      */
255     public void validate() {}
256 
257     /**
258      * Overridden for performance reasons.
259      */
260     public void revalidate() {}
261 
262     /**
263      * Overridden for performance reasons.
264      */
265     public void repaint(long tm, int x, int y, int width, int height) {}
266 
267     /**
268      * Overridden for performance reasons.
269      */
270     public void repaint(Rectangle r) { }
271 
272     /**
273      * Overridden for performance reasons.
274      */
275     protected void firePropertyChange(String propertyName, Object oldValue,
276                                       Object newValue) {
277       // Strings get interned...
278       if (propertyName=="text") {
279         super.firePropertyChange(propertyName, oldValue, newValue);
280       }
281     }
282 
283     /**
284      * Overridden for performance reasons.
285      */
286     public void firePropertyChange(String propertyName, boolean oldValue,
287                                    boolean newValue) { }
288 
289     NumberFormat numberFormat;
290   }
291 
292   /**
293    * The search PR this VR is associated to.
294    */
295   SearchPR target;
296 
297   /**
298    * The table displaying the results
299    */
300   XJTable resultsTable;
301 
302   /**
303    * The model for the results table.
304    */
305   ResultsTableModel resultsTableModel;
306 
307   /**
308    * Contains the {@link gate.creole.ir.QueryResult} objects returned by the
309    * search.
310    */
311   List results;
312 
313 }