0001 /*
0002 * Copyright (c) 1998-2009, The University of Sheffield and Ontotext.
0003 *
0004 * This file is part of GATE (see http://gate.ac.uk/), and is free
0005 * software, licenced under the GNU Library General Public License,
0006 * Version 2, June 1991 (in the distribution as file licence.html,
0007 * and also available at http://gate.ac.uk/gate/licence.html).
0008 *
0009 * Thomas Heitz, Dec 11, 2007
0010 * based on Niraj Aswani GUI
0011 *
0012 * $Id$
0013 */
0014
0015 package gate.gui;
0016
0017 import gate.*;
0018 import gate.corpora.SerialCorpusImpl;
0019 import gate.creole.AbstractVisualResource;
0020 import gate.creole.metadata.CreoleResource;
0021 import gate.creole.metadata.GuiType;
0022 import gate.event.DatastoreEvent;
0023 import gate.event.DatastoreListener;
0024 import gate.gui.docview.*;
0025 import gate.persist.LuceneDataStoreImpl;
0026 import gate.persist.PersistenceException;
0027 import gate.persist.SerialDataStore;
0028 import gate.swing.XJTable;
0029 import gate.swing.BlockingGlassPane;
0030 import gate.swing.XJFileChooser;
0031 import gate.util.*;
0032
0033 import gate.creole.annic.Constants;
0034 import gate.creole.annic.Hit;
0035 import gate.creole.annic.PatternAnnotation;
0036 import gate.creole.annic.Pattern;
0037 import gate.creole.annic.SearchException;
0038 import gate.creole.annic.Searcher;
0039 import gate.creole.annic.lucene.QueryParser;
0040 import gate.gui.docview.AnnotationStack.*;
0041
0042 import java.util.*;
0043 import java.util.List;
0044 import java.util.Timer;
0045 import java.util.regex.*;
0046
0047 import java.awt.*;
0048 import java.awt.event.*;
0049 import java.io.File;
0050 import java.io.BufferedWriter;
0051 import java.io.FileWriter;
0052 import java.io.IOException;
0053
0054 import javax.swing.table.*;
0055 import javax.swing.text.BadLocationException;
0056 import javax.swing.*;
0057 import javax.swing.border.EmptyBorder;
0058 import javax.swing.border.EtchedBorder;
0059 import javax.swing.border.CompoundBorder;
0060 import javax.swing.event.*;
0061
0062
0063 /**
0064 * GUI allowing to write a query with a JAPE derived syntax for querying
0065 * a Lucene Datastore and display the results with a stacked view of the
0066 * annotations and their values.
0067 * <br>
0068 * This VR is associated to {@link gate.creole.ir.SearchPR}.
0069 * You have to set the target with setTarget().
0070 * <br>
0071 * Features: query auto-completion, syntactic error checker,
0072 * display of very big values, export of results in a file,
0073 * 16 types of statistics, store display settings in gate config.
0074 */
0075 @CreoleResource(name = "Lucene Datastore Searcher", guiType = GuiType.LARGE,
0076 resourceDisplayed = "gate.creole.annic.SearchableDataStore",
0077 comment = "GUI allowing to write a query with a JAPE derived syntax for querying\n" +
0078 " a Lucene Datastore and display the results with a stacked view of the\n" +
0079 " annotations and their values.",
0080 helpURL = "http://gate.ac.uk/userguide/chap:annic")
0081 public class LuceneDataStoreSearchGUI extends AbstractVisualResource
0082 implements DatastoreListener {
0083
0084 /** The GUI is associated with the AnnicSearchPR */
0085 private Object target;
0086 /** instances of results associated found in the document */
0087 private List<Hit> results;
0088 /** Annotation types as keys and list of features as values*/
0089 private Map<String, List<String>> allAnnotTypesAndFeaturesFromDatastore;
0090 private Map<String, Set<String>> populatedAnnotationTypesAndFeatures;
0091 /** Lists the results found by the query */
0092 private XJTable resultTable;
0093 private ResultTableModel resultTableModel;
0094 /** Display the stack view configuration window. */
0095 private JButton configureStackViewButton;
0096 /** Contains statistics for the corpus and the annotation set selected. */
0097 private XJTable globalStatisticsTable;
0098 /** Contains statistics of one row each. */
0099 private XJTable oneRowStatisticsTable;
0100 /** Comparator for Integer in statistics tables. */
0101 private Comparator<Integer> integerComparator;
0102 /** Collator for String with insensitive case. */
0103 private java.text.Collator stringCollator;
0104 /** Horizontal split between the results pane and statistics pane. */
0105 private JSplitPane bottomSplitPane;
0106 /** Display statistics on the datastore. */
0107 private JTabbedPane statisticsTabbedPane;
0108 /** Text Area that contains the query */
0109 private QueryTextArea queryTextArea;
0110 private JComboBox corpusToSearchIn;
0111 private JComboBox annotationSetsToSearchIn;
0112 /** list of IDs available in datastore */
0113 private List<Object> corpusIds;
0114 /*** AnnotationSet IDS the structure is: CorpusID;annotationSetName */
0115 private String[] annotationSetIDsFromDataStore;
0116 private JSlider numberOfResultsSlider;
0117 /** Number of tokens to be shown as context in the results */
0118 private JSlider contextSizeSlider;
0119 /** Gives the page number displayed in the results. */
0120 private JLabel titleResults;
0121 /** Show the next page of results. */
0122 private JButton nextResults;
0123 /** Number of the page of results. */
0124 private int pageOfResults;
0125 /** Number of row to show in the results. */
0126 int noOfResults;
0127 /** JPanel that contains the central panel of stack rows. */
0128 private AnnotationStack centerPanel;
0129 private ExecuteQueryAction executeQueryAction;
0130 private NextResultsAction nextResultsAction;
0131 private ExportResultsAction exportResultsAction;
0132 /** Current instance of the stack view frame. */
0133 private ConfigureStackViewFrame configureStackViewFrame;
0134 /** Names of the columns for stackRows data. */
0135 String[] columnNames =
0136 {"Display", "Shortcut", "Annotation type", "Feature", "Crop"};
0137 /** Column (second dimension) of stackRows double array. */
0138 static private final int DISPLAY = 0;
0139 /** Column (second dimension) of stackRows double array. */
0140 static private final int SHORTCUT = 1;
0141 /** Column (second dimension) of stackRows double array. */
0142 static private final int ANNOTATION_TYPE = 2;
0143 /** Column (second dimension) of stackRows double array. */
0144 static private final int FEATURE = 3;
0145 /** Column (second dimension) of stackRows double array. */
0146 static private final int CROP = 4;
0147 /** Maximum number of stackRow */
0148 static private final int maxStackRows = 100;
0149 /** Number of stackRows. */
0150 private int numStackRows = 0;
0151 /** Double array that contains [row, column] of the stackRows data. */
0152 private String[][] stackRows = new String[maxStackRows+1][columnNames.length];
0153 private ConfigureStackViewTableModel configureStackViewTableModel;
0154 private DefaultTableModel oneRowStatisticsTableModel;
0155 private DefaultTableModel globalStatisticsTableModel;
0156 /** Contains the tooltips of the first column. */
0157 private Vector<String> oneRowStatisticsTableToolTips;
0158 /** Searcher object obtained from the datastore */
0159 private Searcher searcher;
0160 /** true if there was an error on the last query. */
0161 private boolean errorOnLastQuery;
0162
0163 /**
0164 * Called when a View is loaded in GATE.
0165 */
0166 public Resource init() {
0167
0168 results = new ArrayList<Hit>();
0169 allAnnotTypesAndFeaturesFromDatastore = new HashMap<String, List<String>>();
0170 corpusIds = new ArrayList<Object>();
0171 populatedAnnotationTypesAndFeatures = new HashMap<String, Set<String>>();
0172 noOfResults = 0;
0173 for (int row = 0; row <= maxStackRows; row++) {
0174 stackRows[row][DISPLAY] = "true";
0175 stackRows[row][SHORTCUT] = "";
0176 stackRows[row][ANNOTATION_TYPE] = "";
0177 stackRows[row][FEATURE] = "";
0178 stackRows[row][CROP] = "Crop end";
0179 }
0180
0181 // read the user config data for annotation stack rows
0182 // saved as a string: "[true, Cat, Token, category, Crop end, ...]"
0183 String prefix = LuceneDataStoreSearchGUI.class.getName() + ".";
0184 if (Gate.getUserConfig().containsKey(prefix + "rows")) {
0185 List<String> list = Gate.getUserConfig().getList(prefix + "rows");
0186 for (int i = 0; i < list.size()
0187 && i < (maxStackRows * columnNames.length); i++) {
0188 stackRows[i / columnNames.length][i % columnNames.length] = list.get(i);
0189 if (i % columnNames.length == 0) { numStackRows++; }
0190 }
0191 }
0192
0193 // initialize GUI
0194 initGui();
0195 updateViews();
0196 validate();
0197 SwingUtilities.invokeLater(new Runnable() {
0198 public void run() {
0199 queryTextArea.requestFocusInWindow();
0200 }});
0201
0202 return this;
0203 }
0204
0205 /**
0206 * Called when the user close the datastore.
0207 */
0208 public void cleanup() {
0209 // no parent so need to be disposed explicitly
0210 configureStackViewFrame.dispose();
0211 }
0212
0213 /**
0214 * Initialize the GUI.
0215 */
0216 protected void initGui() {
0217
0218 // see the global layout schema at the end
0219 setLayout(new BorderLayout());
0220
0221 stringCollator = java.text.Collator.getInstance();
0222 stringCollator.setStrength(java.text.Collator.TERTIARY);
0223
0224 Comparator<String> lastWordComparator = new Comparator<String>() {
0225 public int compare(String o1, String o2) {
0226 if(o1 == null || o2 == null) {
0227 return 0;
0228 }
0229 return stringCollator.compare(
0230 o1.substring(o1.trim().lastIndexOf(' ') + 1),
0231 o2.substring(o2.trim().lastIndexOf(' ') + 1));
0232 }
0233 };
0234
0235 integerComparator = new Comparator<Integer>() {
0236 public int compare(Integer o1, Integer o2) {
0237 if (o1 == null || o2 == null) { return 0; }
0238 return o1.compareTo(o2);
0239 }
0240 };
0241
0242 /**********************************
0243 * Stack view configuration frame *
0244 **********************************/
0245
0246 configureStackViewFrame =
0247 new ConfigureStackViewFrame("Stack view configuration");
0248 configureStackViewFrame.setIconImage(((ImageIcon)MainFrame
0249 .getIcon("crystal-clear-action-window-new")).getImage());
0250 configureStackViewFrame.setLocationRelativeTo(LuceneDataStoreSearchGUI.this);
0251 configureStackViewFrame.getRootPane().getInputMap(
0252 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
0253 .put(KeyStroke.getKeyStroke("ESCAPE"), "close row manager");
0254 configureStackViewFrame.getRootPane().getActionMap()
0255 .put("close row manager", new AbstractAction() {
0256 public void actionPerformed(ActionEvent e) {
0257 configureStackViewFrame.setVisible(false);
0258 }
0259 });
0260 configureStackViewFrame.validate();
0261 configureStackViewFrame.setSize(200, 300);
0262 configureStackViewFrame.pack();
0263
0264 // called when Gate is exited, in case the user doesn't close the datastore
0265 MainFrame.getInstance().addWindowListener(new WindowAdapter() {
0266 public void windowClosed(WindowEvent e) {
0267 // no parent so need to be disposed explicitly
0268 configureStackViewFrame.dispose();
0269 }
0270 });
0271
0272
0273 /*************
0274 * Top panel *
0275 *************/
0276
0277 JPanel topPanel = new JPanel(new GridBagLayout());
0278 topPanel.setOpaque(false);
0279 topPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 0, 3));
0280 GridBagConstraints gbc = new GridBagConstraints();
0281
0282 // first column, three rows span
0283 queryTextArea = new QueryTextArea();
0284 queryTextArea.setToolTipText(
0285 "<html>Enter a query to search the datastore." +
0286 "<br><small>'{' or '.' activate auto-completion." +
0287 "<br>Ctrl+Enter add a new line.</small></html>");
0288 queryTextArea.setLineWrap(true);
0289 gbc.gridheight = 3;
0290 gbc.weightx = 1;
0291 gbc.weighty = 1;
0292 gbc.fill = GridBagConstraints.BOTH;
0293 gbc.insets = new Insets(0, 0, 0, 4);
0294 topPanel.add(new JScrollPane(queryTextArea), gbc);
0295 gbc.gridheight = 1;
0296 gbc.weightx = 0;
0297 gbc.weighty = 0;
0298 gbc.insets = new Insets(0, 0, 0, 0);
0299
0300 // second column, first row
0301 gbc.gridx = GridBagConstraints.RELATIVE;
0302 topPanel.add(new JLabel("Corpus: "), gbc);
0303 corpusToSearchIn = new JComboBox();
0304 corpusToSearchIn.addItem(Constants.ENTIRE_DATASTORE);
0305 corpusToSearchIn.setPrototypeDisplayValue(Constants.ENTIRE_DATASTORE);
0306 corpusToSearchIn.setToolTipText("Select the corpus to search in.");
0307 if(target == null || target instanceof Searcher) {
0308 corpusToSearchIn.setEnabled(false);
0309 }
0310 corpusToSearchIn.addActionListener(new ActionListener() {
0311 public void actionPerformed(ActionEvent ie) {
0312 SwingUtilities.invokeLater(new Runnable() { public void run() {
0313 updateAnnotationSetsList();
0314 }});
0315 }
0316 });
0317 topPanel.add(corpusToSearchIn, gbc);
0318 topPanel.add(Box.createHorizontalStrut(4), gbc);
0319 topPanel.add(new JLabel("Annotation set: "), gbc);
0320 annotationSetsToSearchIn = new JComboBox();
0321 annotationSetsToSearchIn.setPrototypeDisplayValue(Constants.COMBINED_SET);
0322 annotationSetsToSearchIn.setToolTipText("Select the annotation set to search in.");
0323 annotationSetsToSearchIn.addActionListener(new ActionListener() {
0324 public void actionPerformed(ActionEvent ie) {
0325 updateAnnotationTypesList();
0326 }
0327 });
0328 topPanel.add(annotationSetsToSearchIn, gbc);
0329
0330 // second column, second row
0331 gbc.gridy = 1;
0332 JLabel noOfResultsLabel = new JLabel("Results: ");
0333 topPanel.add(noOfResultsLabel, gbc);
0334 numberOfResultsSlider = new JSlider(1, 1100, 50);
0335 numberOfResultsSlider.setToolTipText("50 results per page");
0336 numberOfResultsSlider.addChangeListener(new ChangeListener() {
0337 public void stateChanged(ChangeEvent e) {
0338 if (numberOfResultsSlider.getValue()
0339 > (numberOfResultsSlider.getMaximum() - 100)) {
0340 numberOfResultsSlider.setToolTipText("Retrieve all results.");
0341 nextResults.setText("Retrieve all results.");
0342 nextResultsAction.setEnabled(false);
0343 } else {
0344 numberOfResultsSlider.setToolTipText("Retrieve "
0345 + numberOfResultsSlider.getValue() + " results per page.");
0346 nextResults.setText(
0347 "Next page of " + numberOfResultsSlider.getValue() + " results");
0348 if (searcher.getHits().length == noOfResults) {
0349 nextResultsAction.setEnabled(true);
0350 }
0351 }
0352 // show the tooltip each time the value change
0353 ToolTipManager.sharedInstance().mouseMoved(new MouseEvent(
0354 numberOfResultsSlider, MouseEvent.MOUSE_MOVED, 0, 0, 0, 0, 0, false));
0355 }
0356 });
0357 // always show the tooltip for this component
0358 numberOfResultsSlider.addMouseListener(new MouseAdapter() {
0359 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
0360 int initialDelay, reshowDelay, dismissDelay;
0361 boolean enabled;
0362 public void mouseEntered(MouseEvent e) {
0363 initialDelay = toolTipManager.getInitialDelay();
0364 reshowDelay = toolTipManager.getReshowDelay();
0365 dismissDelay = toolTipManager.getDismissDelay();
0366 enabled = toolTipManager.isEnabled();
0367 toolTipManager.setInitialDelay(0);
0368 toolTipManager.setReshowDelay(0);
0369 toolTipManager.setDismissDelay(Integer.MAX_VALUE);
0370 toolTipManager.setEnabled(true);
0371 }
0372 public void mouseExited(MouseEvent e) {
0373 toolTipManager.setInitialDelay(initialDelay);
0374 toolTipManager.setReshowDelay(reshowDelay);
0375 toolTipManager.setDismissDelay(dismissDelay);
0376 toolTipManager.setEnabled(enabled);
0377 }
0378 });
0379 gbc.insets = new Insets(5, 0, 0, 0);
0380 topPanel.add(numberOfResultsSlider, gbc);
0381 gbc.insets = new Insets(0, 0, 0, 0);
0382 topPanel.add(Box.createHorizontalStrut(4), gbc);
0383 JLabel contextWindowLabel = new JLabel("Context size: ");
0384 topPanel.add(contextWindowLabel, gbc);
0385 contextSizeSlider = new JSlider(1, 50, 5);
0386 contextSizeSlider.setToolTipText(
0387 "Display 5 tokens of context in the results.");
0388 contextSizeSlider.addChangeListener(new ChangeListener() {
0389 public void stateChanged(ChangeEvent e) {
0390 contextSizeSlider.setToolTipText("Display "
0391 + contextSizeSlider.getValue()
0392 + " tokens of context in the results.");
0393 ToolTipManager.sharedInstance().mouseMoved(new MouseEvent(
0394 contextSizeSlider, MouseEvent.MOUSE_MOVED, 0, 0, 0, 0, 0, false));
0395 }
0396 });
0397 // always show the tooltip for this component
0398 contextSizeSlider.addMouseListener(new MouseAdapter() {
0399 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
0400 int initialDelay, reshowDelay, dismissDelay;
0401 boolean enabled;
0402 public void mouseEntered(MouseEvent e) {
0403 initialDelay = toolTipManager.getInitialDelay();
0404 reshowDelay = toolTipManager.getReshowDelay();
0405 dismissDelay = toolTipManager.getDismissDelay();
0406 enabled = toolTipManager.isEnabled();
0407 toolTipManager.setInitialDelay(0);
0408 toolTipManager.setReshowDelay(0);
0409 toolTipManager.setDismissDelay(Integer.MAX_VALUE);
0410 toolTipManager.setEnabled(true);
0411 }
0412 public void mouseExited(MouseEvent e) {
0413 toolTipManager.setInitialDelay(initialDelay);
0414 toolTipManager.setReshowDelay(reshowDelay);
0415 toolTipManager.setDismissDelay(dismissDelay);
0416 toolTipManager.setEnabled(enabled);
0417 }
0418 });
0419 gbc.insets = new Insets(5, 0, 0, 0);
0420 topPanel.add(contextSizeSlider, gbc);
0421 gbc.insets = new Insets(0, 0, 0, 0);
0422
0423 // second column, third row
0424 gbc.gridy = 2;
0425 JPanel panel = new JPanel();
0426 panel.setBorder(new EmptyBorder(new Insets(0, 0, 0, 0)));
0427 executeQueryAction = new ExecuteQueryAction();
0428 JButton executeQuery =
0429 new ButtonBorder(new Color(240, 240, 240), new Insets(0, 2, 0, 3), false);
0430 executeQuery.setAction(executeQueryAction);
0431 panel.add(executeQuery);
0432 ClearQueryAction clearQueryAction = new ClearQueryAction();
0433 JButton clearQueryTF =
0434 new ButtonBorder(new Color(240,240,240), new Insets(4,2,4,3), false);
0435 clearQueryTF.setAction(clearQueryAction);
0436 panel.add(Box.createHorizontalStrut(5));
0437 panel.add(clearQueryTF);
0438 nextResultsAction = new NextResultsAction();
0439 nextResultsAction.setEnabled(false);
0440 nextResults =
0441 new ButtonBorder(new Color(240,240,240), new Insets(0,0,0,3), false);
0442 nextResults.setAction(nextResultsAction);
0443 panel.add(Box.createHorizontalStrut(5));
0444 panel.add(nextResults);
0445 gbc.fill = GridBagConstraints.NONE;
0446 gbc.anchor = GridBagConstraints.WEST;
0447 gbc.gridwidth = GridBagConstraints.REMAINDER;
0448 topPanel.add(panel, gbc);
0449
0450 // will be added to the GUI via a split panel
0451
0452 /****************
0453 * Center panel *
0454 ****************/
0455
0456 // these components will be used in updateStackView()
0457 centerPanel = new AnnotationStack(150, 30);
0458
0459 configureStackViewButton =
0460 new ButtonBorder(new Color(250,250,250), new Insets(0,0,0,3), true);
0461 configureStackViewButton.setHorizontalAlignment(SwingConstants.LEFT);
0462 configureStackViewButton.setAction(new ConfigureStackViewAction());
0463
0464 // will be added to the GUI via a split panel
0465
0466 /*********************
0467 * Bottom left panel *
0468 *********************/
0469
0470 JPanel bottomLeftPanel = new JPanel(new GridBagLayout());
0471 bottomLeftPanel.setOpaque(false);
0472 gbc = new GridBagConstraints();
0473
0474 // title of the table, results options, export and next results button
0475 gbc.gridy = 0;
0476 panel = new JPanel();
0477 panel.setBorder(new EmptyBorder(new Insets(0, 0, 0, 0)));
0478 titleResults = new JLabel("Results");
0479 titleResults.setBorder(new EmptyBorder(new Insets(0, 0, 0, 0)));
0480 panel.add(titleResults);
0481 panel.add(Box.createHorizontalStrut(5), gbc);
0482 exportResultsAction = new ExportResultsAction();
0483 exportResultsAction.setEnabled(false);
0484 JButton exportToHTML = new ButtonBorder(new Color(240, 240, 240), new Insets(0, 0, 0, 3), false);
0485 exportToHTML.setAction(exportResultsAction);
0486 panel.add(exportToHTML, gbc);
0487 bottomLeftPanel.add(panel, gbc);
0488
0489 // table of results
0490 resultTableModel = new ResultTableModel();
0491 resultTable = new XJTable(resultTableModel);
0492 resultTable.setDefaultRenderer(String.class, new ResultTableCellRenderer());
0493 resultTable.setEnableHidingColumns(true);
0494
0495 resultTable.addMouseListener(new MouseAdapter() {
0496 private JPopupMenu mousePopup;
0497 JMenuItem menuItem;
0498
0499 public void mousePressed(MouseEvent e) {
0500 int row = resultTable.rowAtPoint(e.getPoint());
0501 if(e.isPopupTrigger()
0502 && !resultTable.isRowSelected(row)) {
0503 // if right click outside the selection then reset selection
0504 resultTable.getSelectionModel().setSelectionInterval(row, row);
0505 }
0506 if (e.isPopupTrigger()) {
0507 createPopup();
0508 mousePopup.show(e.getComponent(), e.getX(), e.getY());
0509 }
0510 }
0511
0512 public void mouseReleased(MouseEvent e) {
0513 if (e.isPopupTrigger()) {
0514 createPopup();
0515 mousePopup.show(e.getComponent(), e.getX(), e.getY());
0516 }
0517 }
0518
0519 private void createPopup() {
0520 mousePopup = new JPopupMenu();
0521 menuItem = new JMenuItem("Remove the selected result" +
0522 (resultTable.getSelectedRowCount() > 1 ? "s" : ""));
0523 mousePopup.add(menuItem);
0524 menuItem.addActionListener(new ActionListener() {
0525 public void actionPerformed(ActionEvent ae) {
0526 int[] rows = resultTable.getSelectedRows();
0527 for(int i = 0; i < rows.length; i++) {
0528 rows[i] = resultTable.rowViewToModel(rows[i]);
0529 }
0530 Arrays.sort(rows);
0531 for(int i = rows.length - 1; i >= 0; i--) {
0532 results.remove(rows[i]);
0533 }
0534 resultTable.clearSelection();
0535 resultTableModel.fireTableDataChanged();
0536 mousePopup.setVisible(false);
0537 }
0538 });
0539
0540 if(target instanceof LuceneDataStoreImpl
0541 && SwingUtilities.getRoot(LuceneDataStoreSearchGUI.this)
0542 instanceof MainFrame) {
0543 menuItem = new JMenuItem("Open the selected document" +
0544 (resultTable.getSelectedRowCount() > 1 ? "s" : ""));
0545 menuItem.addActionListener(new ActionListener() {
0546 public void actionPerformed(ActionEvent ae) {
0547 Set<Pattern> patterns = new HashSet<Pattern>();
0548 Set<String> documentIds = new HashSet<String>();
0549 for (int rowView : resultTable.getSelectedRows()) {
0550 // create and display the document for this result
0551 int rowModel = resultTable.rowViewToModel(rowView);
0552 Pattern pattern = (Pattern) results.get(rowModel);
0553 if (!documentIds.contains(pattern.getDocumentID())) {
0554 patterns.add(pattern);
0555 documentIds.add(pattern.getDocumentID());
0556 }
0557 }
0558 if (patterns.size() > 10) {
0559 Object[] possibleValues =
0560 { "Open the "+patterns.size()+" documents", "Don't open" };
0561 int selectedValue = JOptionPane.showOptionDialog(
0562 LuceneDataStoreSearchGUI.this,
0563 "Do you want to open " + patterns.size() +
0564 " documents in the central tabbed pane ?",
0565 "Warning", JOptionPane.DEFAULT_OPTION,
0566 JOptionPane.QUESTION_MESSAGE, null,
0567 possibleValues, possibleValues[1]);
0568 if (selectedValue == 1
0569 || selectedValue == JOptionPane.CLOSED_OPTION) {
0570 return;
0571 }
0572 }
0573 for (final Pattern pattern : patterns) {
0574 // create and display the document for this result
0575 FeatureMap features = Factory.newFeatureMap();
0576 features.put(DataStore.DATASTORE_FEATURE_NAME, target);
0577 features.put(DataStore.LR_ID_FEATURE_NAME,
0578 pattern.getDocumentID());
0579 final Document doc;
0580 try {
0581 doc = (Document) Factory.createResource(
0582 "gate.corpora.DocumentImpl", features);
0583 } catch (gate.util.GateException e) {
0584 e.printStackTrace();
0585 return;
0586 }
0587 // show the expression in the document
0588 SwingUtilities.invokeLater(new Runnable() { public void run() {
0589 MainFrame.getInstance().select(doc);
0590 }});
0591 if (patterns.size() == 1) {
0592 // wait some time for the document to be displayed
0593 Date timeToRun = new Date(System.currentTimeMillis() + 2000);
0594 Timer timer = new Timer("Annic show document timer", true);
0595 timer.schedule(new TimerTask() { public void run() {
0596 showResultInDocument(doc, pattern);
0597 }}, timeToRun);
0598 }
0599 }
0600 }
0601 });
0602 mousePopup.add(menuItem);
0603 }
0604 }
0605 }); // resultTable.addMouseListener
0606
0607 // when selection change in the result table
0608 // update the stack view and export button
0609 resultTable.getSelectionModel().addListSelectionListener(
0610 new javax.swing.event.ListSelectionListener() {
0611 public void valueChanged(javax.swing.event.ListSelectionEvent e) {
0612 if (!e.getValueIsAdjusting()) {
0613 updateStackView();
0614 }
0615 }
0616 });
0617 resultTable.setColumnSelectionAllowed(false);
0618 resultTable.setRowSelectionAllowed(true);
0619 resultTable.setSortable(true);
0620 resultTable.setComparator(
0621 ResultTableModel.LEFT_CONTEXT_COLUMN, lastWordComparator);
0622 resultTable.setComparator(
0623 ResultTableModel.RESULT_COLUMN, stringCollator);
0624 resultTable.setComparator(
0625 ResultTableModel.RIGHT_CONTEXT_COLUMN, stringCollator);
0626
0627 JScrollPane tableScrollPane = new JScrollPane(resultTable,
0628 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
0629 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
0630
0631 gbc.fill = GridBagConstraints.BOTH;
0632 gbc.anchor = GridBagConstraints.NORTH;
0633 gbc.gridy = 1;
0634 gbc.gridx= 0;
0635 gbc.insets = new Insets(0, 0, 0, 0);
0636 gbc.gridwidth = GridBagConstraints.REMAINDER;
0637 gbc.gridheight = GridBagConstraints.REMAINDER;
0638 gbc.weightx = 1;
0639 gbc.weighty = 1;
0640 bottomLeftPanel.add(tableScrollPane, gbc);
0641
0642 /**************************
0643 * Statistics tabbed pane *
0644 **************************/
0645
0646 statisticsTabbedPane = new JTabbedPane();
0647
0648 globalStatisticsTable = new XJTable() {
0649 public boolean isCellEditable(int rowIndex, int vColIndex) {
0650 return false;
0651 }
0652 };
0653 globalStatisticsTableModel = new DefaultTableModel(
0654 new Object[]{"Annotation Type", "Count"},0);
0655 globalStatisticsTable.setModel(globalStatisticsTableModel);
0656 globalStatisticsTable.setComparator(0, stringCollator);
0657 globalStatisticsTable.setComparator(1, integerComparator);
0658 globalStatisticsTable.setSortedColumn(1);
0659 globalStatisticsTable.setAscending(false);
0660
0661 statisticsTabbedPane.addTab("Global", null,
0662 new JScrollPane(globalStatisticsTable),
0663 "Global statistics on the Corpus and Annotation Set selected.");
0664
0665 statisticsTabbedPane.addMouseListener(new MouseAdapter() {
0666 private JPopupMenu mousePopup;
0667 JMenuItem menuItem;
0668 public void mousePressed(MouseEvent e) {
0669 int tabIndex = statisticsTabbedPane.indexAtLocation(e.getX(), e.getY());
0670 if (e.isPopupTrigger()
0671 && tabIndex > 0) {
0672 createPopup(tabIndex);
0673 mousePopup.show(e.getComponent(), e.getX(), e.getY());
0674 }
0675 }
0676 public void mouseReleased(MouseEvent e) {
0677 int tabIndex = statisticsTabbedPane.indexAtLocation(e.getX(), e.getY());
0678 if (e.isPopupTrigger()
0679 && tabIndex > 0) {
0680 createPopup(tabIndex);
0681 mousePopup.show(e.getComponent(), e.getX(), e.getY());
0682 }
0683 }
0684 private void createPopup(final int tabIndex) {
0685 mousePopup = new JPopupMenu();
0686 if (tabIndex == 1) {
0687 menuItem = new JMenuItem("Clear table");
0688 menuItem.addActionListener(new ActionListener() {
0689 public void actionPerformed(ActionEvent ie) {
0690 oneRowStatisticsTableModel.setRowCount(0);
0691 }
0692 });
0693 } else {
0694 menuItem = new JMenuItem("Close tab");
0695 menuItem.addActionListener(new ActionListener() {
0696 public void actionPerformed(ActionEvent ie) {
0697 statisticsTabbedPane.remove(tabIndex);
0698 }
0699 });
0700 }
0701 mousePopup.add(menuItem);
0702 }
0703 });
0704
0705 class RemoveCellEditorRenderer extends AbstractCellEditor
0706 implements TableCellRenderer, TableCellEditor, ActionListener {
0707 private JButton button;
0708 public RemoveCellEditorRenderer() {
0709 button = new JButton();
0710 button.setHorizontalAlignment(SwingConstants.CENTER);
0711 button.setIcon(MainFrame.getIcon("crystal-clear-action-button-cancel"));
0712 button.setToolTipText("Remove this row.");
0713 button.setContentAreaFilled(false);
0714 button.setBorderPainted(false);
0715 button.setMargin(new Insets(0, 0, 0, 0));
0716 button.addActionListener(this);
0717 }
0718 public Component getTableCellRendererComponent(
0719 JTable table, Object color, boolean isSelected,
0720 boolean hasFocus, int row, int col) {
0721 button.setSelected(isSelected);
0722 return button;
0723 }
0724 public boolean shouldSelectCell(EventObject anEvent) {
0725 return false;
0726 }
0727 public void actionPerformed(ActionEvent e) {
0728 int editingRow = oneRowStatisticsTable.getEditingRow();
0729 fireEditingStopped();
0730 oneRowStatisticsTableModel.removeRow(
0731 oneRowStatisticsTable.rowViewToModel(editingRow));
0732 }
0733 public Object getCellEditorValue() {
0734 return null;
0735 }
0736 public Component getTableCellEditorComponent(JTable table,
0737 Object value, boolean isSelected, int row, int col) {
0738 button.setSelected(isSelected);
0739 return button;
0740 }
0741 }
0742
0743 oneRowStatisticsTable = new XJTable() {
0744 public boolean isCellEditable(int rowIndex, int vColIndex) {
0745 return vColIndex == 2;
0746 }
0747 public Component prepareRenderer(TableCellRenderer renderer,
0748 int row, int col) {
0749 Component c = super.prepareRenderer(renderer, row, col);
0750 if (c instanceof JComponent && col != 2) {
0751 // display a custom tooltip saved when adding statistics
0752 ((JComponent)c).setToolTipText("<html>"
0753 +oneRowStatisticsTableToolTips.get(rowViewToModel(row))+"</html>");
0754 }
0755 return c;
0756 }
0757 };
0758
0759 oneRowStatisticsTableModel = new DefaultTableModel(
0760 new Object[]{"Annotation Type/Feature","Count",""}, 0);
0761 oneRowStatisticsTable.setModel(oneRowStatisticsTableModel);
0762 oneRowStatisticsTable.getColumnModel().getColumn(2)
0763 .setCellEditor(new RemoveCellEditorRenderer());
0764 oneRowStatisticsTable.getColumnModel().getColumn(2)
0765 .setCellRenderer(new RemoveCellEditorRenderer());
0766 oneRowStatisticsTable.setComparator(0, stringCollator);
0767 oneRowStatisticsTable.setComparator(1, integerComparator);
0768
0769 statisticsTabbedPane.addTab("One item", null,
0770 new JScrollPane(oneRowStatisticsTable),
0771 "<html>One item statistics.<br>"+
0772 "Right-click on an annotation<br>"+
0773 "to add statistics here.");
0774 oneRowStatisticsTableToolTips = new Vector<String>();
0775
0776 // will be added to the GUI via a split panel
0777
0778 /**************************************************************
0779 * Vertical splits between top, center panel and bottom panel *
0780 **************************************************************/
0781
0782 /** ________________________________________
0783 * | topPanel |
0784 * |__________________3_____________________|
0785 * | |
0786 * | centerPanel |
0787 * |________2________ __________2___________|
0788 * | | |
0789 * | bottomLeftPanel 1 statisticsTabbedPane |
0790 * |_________________|______________________|
0791 *
0792 * 1 bottomSplitPane
0793 * 2 centerBottomSplitPane
0794 * 3 topBottomSplitPane
0795 */
0796
0797 bottomSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
0798 Dimension minimumSize = new Dimension(0, 0);
0799 bottomLeftPanel.setMinimumSize(minimumSize);
0800 statisticsTabbedPane.setMinimumSize(minimumSize);
0801 bottomSplitPane.add(bottomLeftPanel);
0802 bottomSplitPane.add(statisticsTabbedPane);
0803 bottomSplitPane.setOneTouchExpandable(true);
0804 bottomSplitPane.setResizeWeight(0.75);
0805 bottomSplitPane.setContinuousLayout(true);
0806
0807 JSplitPane centerBottomSplitPane =
0808 new JSplitPane(JSplitPane.VERTICAL_SPLIT);
0809 centerBottomSplitPane.add(new JScrollPane(centerPanel,
0810 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
0811 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
0812 centerBottomSplitPane.add(bottomSplitPane);
0813 centerBottomSplitPane.setResizeWeight(0.5);
0814 centerBottomSplitPane.setContinuousLayout(true);
0815
0816 JSplitPane topBottomSplitPane =
0817 new JSplitPane(JSplitPane.VERTICAL_SPLIT);
0818 topBottomSplitPane.add(topPanel);
0819 topBottomSplitPane.add(centerBottomSplitPane);
0820 topBottomSplitPane.setContinuousLayout(true);
0821
0822 add(topBottomSplitPane, BorderLayout.CENTER);
0823
0824 }
0825
0826 private void showResultInDocument(Document doc, Pattern result) {
0827 try {
0828 // find the document view associated with the document
0829 TextualDocumentView t = null;
0830 for (Resource r : Gate.getCreoleRegister().getAllInstances(
0831 "gate.gui.docview.TextualDocumentView")) {
0832 if (((TextualDocumentView)r).getDocument().getName()
0833 .equals(doc.getName())) {
0834 t = (TextualDocumentView)r;
0835 break;
0836 }
0837 }
0838
0839 if (t != null && t.getOwner() != null) {
0840 // display the annotation sets view
0841 t.getOwner().setRightView(0);
0842 try {
0843 // scroll to the expression that matches the query result
0844 t.getTextView().scrollRectToVisible(
0845 t.getTextView().modelToView(
0846 result.getRightContextEndOffset()));
0847 } catch (BadLocationException e) {
0848 e.printStackTrace();
0849 return;
0850 }
0851 // select the expression that matches the query result
0852 t.getTextView().select(
0853 result.getLeftContextStartOffset(),
0854 result.getRightContextEndOffset());
0855 t.getTextView().requestFocus();
0856 }
0857
0858 // find the annotation sets view associated with the document
0859 for (Resource r : Gate.getCreoleRegister().getAllInstances(
0860 "gate.gui.docview.AnnotationSetsView")) {
0861 AnnotationSetsView asv = (AnnotationSetsView)r;
0862 if (asv == null) { continue; }
0863 if (asv.isActive()
0864 && asv.getDocument().getName().equals(doc.getName())) {
0865 // display the same annotation types as in Annic
0866 for (int row = 0; row < numStackRows; row++) {
0867 if (stackRows[row][DISPLAY].equals("false")) {
0868 continue;
0869 }
0870 String type = stackRows[row][ANNOTATION_TYPE];
0871 if (type.equals(Constants.ANNIC_TOKEN)) {
0872 // not interesting to display them
0873 continue;
0874 }
0875 // look if there is the type displayed in Annic
0876 String asn = result.getAnnotationSetName();
0877 if (asn.equals(Constants.DEFAULT_ANNOTATION_SET_NAME)
0878 && doc.getAnnotations().getAllTypes().contains(type)) {
0879 asv.setTypeSelected(null, type, true);
0880 } else if (doc.getAnnotationSetNames().contains(asn)
0881 && doc.getAnnotations(asn).getAllTypes().contains(type)) {
0882 asv.setTypeSelected(asn, type, true);
0883 }
0884 }
0885 break;
0886 }
0887 }
0888
0889 } catch (gate.util.GateException e) {
0890 e.printStackTrace();
0891 }
0892 } // private void showExpressionInDocument
0893
0894 /**
0895 * Update the result table and center view according to the result of
0896 * the search contained in <code>searcher</code>.
0897 */
0898 protected void updateViews() {
0899
0900 if (searcher != null) {
0901 Collections.addAll(results, searcher.getHits());
0902 // update the table of results
0903 resultTableModel.fireTableDataChanged();
0904 }
0905
0906 if(results.size() > 0) {
0907 String query = queryTextArea.getText().trim();
0908 if(query.length() > 0 && !results.isEmpty()) {
0909 int row;
0910 do { // delete previous temporary stack rows
0911 row = findStackRow(DISPLAY, "one time");
0912 deleteStackRow(row);
0913 } while (row >= 0);
0914 // from the query display all the existing stackRows
0915 // that are not already displayed
0916 Matcher matcher = java.util.regex.Pattern.compile(
0917 "\\{" // first condition
0918 +"([^\\{\\}=,.]+)" // annotation type or shortcut (1)
0919 +"(?:(?:\\.([^=]+)==\"([^\\}\"]+)\")" // feature (2), value (3)
0920 +"|(?:==([^\\}]+)))?" // value of a shortcut (4)
0921 +"(?:, ?" // second condition
0922 +"([^\\{\\}=,.]+)" // annotation type or shortcut (5)
0923 +"(?:(?:\\.([^=]+)==\"([^\\}\"]+)\")" // feature (6), value (7)
0924 +"|(?:==([^\\}]+)))?)?" // value of a shortcut (8)
0925 +"\\}").matcher(query);
0926 while (matcher.find()) {
0927 for (int i = 0; i <= 4; i += 4) { // first then second condition
0928 String type = null, feature = null, shortcut; row = -1;
0929 if (matcher.group(1+i) != null
0930 && matcher.group(2+i) == null
0931 && matcher.group(3+i) == null
0932 && matcher.group(4+i) == null) {
0933 type = matcher.group(1+i);
0934 feature = "";
0935 row = findStackRow(ANNOTATION_TYPE, type, FEATURE, feature);
0936 } else if (matcher.group(1+i) != null
0937 && matcher.group(2+i) == null
0938 && matcher.group(3+i) == null) {
0939 shortcut = matcher.group(1+i);
0940 row = findStackRow(SHORTCUT, shortcut);
0941 } else if (matcher.group(1+i) != null
0942 && matcher.group(2+i) != null
0943 && matcher.group(4+i) == null) {
0944 type = matcher.group(1+i);
0945 feature = matcher.group(2+i);
0946 row = findStackRow(ANNOTATION_TYPE, type, FEATURE, feature);
0947 }
0948 if (row >= 0) {
0949 stackRows[row][DISPLAY] = "true";
0950 } else if (type != null && feature != null
0951 && numStackRows < maxStackRows) {
0952 stackRows[numStackRows][DISPLAY] = "one time";
0953 stackRows[numStackRows][SHORTCUT] = "";
0954 stackRows[numStackRows][ANNOTATION_TYPE] = type;
0955 stackRows[numStackRows][FEATURE] = feature;
0956 stackRows[numStackRows][CROP] = "Crop end";
0957 numStackRows++;
0958 }
0959 }
0960 }
0961 configureStackViewTableModel.fireTableDataChanged();
0962 }
0963 exportResultsAction.setEnabled(true);
0964 if (numberOfResultsSlider.getValue()
0965 <= (numberOfResultsSlider.getMaximum() - 100)) {
0966 nextResultsAction.setEnabled(true);
0967 }
0968 if (searcher.getHits().length < noOfResults) {
0969 nextResultsAction.setEnabled(false);
0970 }
0971 resultTable.setRowSelectionInterval(0, 0);
0972 resultTable.scrollRectToVisible(resultTable.getCellRect(0, 0, true));
0973
0974 } else if (queryTextArea.getText().trim().length() < 1) {
0975 centerPanel.removeAll();
0976 centerPanel.add(new JTextArea(
0977 "Have a look at the statistics table at the bottom right\n" +
0978 "for the most frequent annotations.\n\n" +
0979 "Enter a query in the text area at the top and press Enter.\n\n" +
0980 "For example: {Person} to retrieve Person annotations."
0981 ),
0982 new GridBagConstraints());
0983 centerPanel.updateUI();
0984 nextResultsAction.setEnabled(false);
0985 exportResultsAction.setEnabled(false);
0986
0987 } else {
0988 GridBagConstraints gbc = new GridBagConstraints();
0989 gbc.gridwidth = GridBagConstraints.REMAINDER;
0990 gbc.gridy = GridBagConstraints.RELATIVE;
0991 if (errorOnLastQuery) {
0992 errorOnLastQuery = false;
0993 } else {
0994 centerPanel.removeAll();
0995 centerPanel.add(new JTextArea("No result found for your query."), gbc);
0996 if (!corpusToSearchIn.getSelectedItem().equals(Constants.ENTIRE_DATASTORE)
0997 || !annotationSetsToSearchIn.getSelectedItem().equals(Constants.ALL_SETS)) {
0998 gbc.insets = new Insets(20, 0, 0, 0);
0999 centerPanel.add(new JTextArea(
1000 "Consider increasing the number of documents to search "
1001 +"in selecting \""+Constants.ENTIRE_DATASTORE+"\" as corpus\n"
1002 +" and \""+Constants.ALL_SETS+"\" as annotation set "
1003 +"in the drop-down lists."), gbc);
1004 }
1005 }
1006 gbc.insets = new Insets(20, 0, 0, 0);
1007 centerPanel.add(new JTextArea(
1008 "Try one of these types of query:\n"+
1009 "- word (each word must match a Token)\n"+
1010 "- {AnnotationType}\n"+
1011 "- {AnnotationType==\"text\"}\n"+
1012 "- {AnnotationType.feature==\"value\"}\n"+
1013 "- {AnnotationType, AnnotationType}\n"+
1014 "- ({A}âà£{B}) (means A or B)\n"+
1015 "- ({A})+n (means one and up to n occurrences)\n"+
1016 "- ({A})*n (means zero or up to n occurrences)\n"),
1017 gbc);
1018 centerPanel.updateUI();
1019 exportResultsAction.setEnabled(false);
1020 nextResultsAction.setEnabled(false);
1021 }
1022 }
1023
1024 /**
1025 * Updates the annotation stack in the central view.
1026 */
1027 protected void updateStackView() {
1028
1029 GridBagConstraints gbc = new GridBagConstraints();
1030 gbc.gridx = 0;
1031 gbc.gridy = 0;
1032 gbc.fill = GridBagConstraints.BOTH;
1033
1034 if (resultTable.getSelectedRow() == -1) {
1035 // no result is selected in the result table
1036 centerPanel.removeAll();
1037 if (resultTable.getRowCount() > 0) {
1038 centerPanel.add(new JLabel(
1039 "Select a row in the results table below."), gbc);
1040 } else {
1041 if (numberOfResultsSlider.getValue()
1042 > (numberOfResultsSlider.getMaximum() - 100)) {
1043 centerPanel.add(new JLabel("Retrieving all results..."), gbc);
1044 } else {
1045 centerPanel.add(new JLabel("Retrieving " +
1046 numberOfResultsSlider.getValue() + " results..."), gbc);
1047 }
1048 }
1049 centerPanel.validate();
1050 centerPanel.repaint();
1051 return;
1052 }
1053
1054 // get information for the selected row in the results table
1055 Pattern result = (Pattern) results.get(resultTable.rowViewToModel(
1056 resultTable.getSelectionModel().getLeadSelectionIndex()));
1057
1058 // initialize the annotation stack
1059 centerPanel.setText(result.getPatternText());
1060 centerPanel.setExpressionStartOffset(result.getStartOffset());
1061 centerPanel.setExpressionEndOffset(result.getEndOffset());
1062 centerPanel.setContextBeforeSize(
1063 result.getStartOffset()-result.getLeftContextStartOffset());
1064 centerPanel.setContextAfterSize(
1065 result.getRightContextEndOffset()-result.getEndOffset());
1066 centerPanel.setLastRowButton(configureStackViewButton);
1067 centerPanel.setTextMouseListener(new TextMouseListener());
1068 centerPanel.setHeaderMouseListener(new HeaderMouseListener());
1069 centerPanel.setAnnotationMouseListener(new AnnotationMouseListener());
1070 centerPanel.clearAllRows();
1071
1072 // add each row to the annotation stack
1073 for (int row = 0; row < numStackRows; row++) {
1074 if (stackRows[row][DISPLAY].equals("false")) { continue; }
1075
1076 String type = stackRows[row][ANNOTATION_TYPE];
1077 String feature = stackRows[row][FEATURE];
1078 String shortcut = stackRows[row][SHORTCUT];
1079
1080 // remove button displayed at the end of each row
1081 JButton removeRowButton = new ButtonBorder(
1082 new Color(250,250,250), new Insets(0,3,0,3), true);
1083 removeRowButton.setIcon(
1084 MainFrame.getIcon("crystal-clear-action-edit-remove"));
1085 removeRowButton.setToolTipText("Hide this row.");
1086 final String typeFinal = type;
1087 final String featureFinal = feature;
1088 removeRowButton.addActionListener(new ActionListener() {
1089 public void actionPerformed(ActionEvent ie) {
1090 int row = findStackRow(ANNOTATION_TYPE, typeFinal,
1091 FEATURE, featureFinal);
1092 if (row >= 0) {
1093 stackRows[row][DISPLAY] = "false";
1094 saveStackViewConfiguration();
1095 }
1096 updateStackView();
1097 }
1098 });
1099
1100 int crop;
1101 if(stackRows[row][CROP].equals("Crop start")) {
1102 crop = AnnotationStack.CROP_START;
1103 } else if(stackRows[row][CROP].equals("Crop end")) {
1104 crop = AnnotationStack.CROP_END;
1105 } else {
1106 crop = AnnotationStack.CROP_MIDDLE;
1107 }
1108
1109 centerPanel.addRow(null, type, feature, removeRowButton, shortcut, crop);
1110
1111 // annotations for this row
1112 PatternAnnotation[] annotations = feature.equals("") ?
1113 result.getPatternAnnotations(type)
1114 : result.getPatternAnnotations(type, feature);
1115 if (annotations != null && annotations.length > 0) {
1116 for (PatternAnnotation annotation : annotations) {
1117 FeatureMap features = Factory.newFeatureMap();
1118 features.putAll(annotation.getFeatures());
1119 centerPanel.addAnnotation(annotation.getStartOffset(),
1120 annotation.getEndOffset(), annotation.getType(), features);
1121 }
1122 }
1123 }
1124
1125 // draw the annotation stack
1126 centerPanel.drawStack();
1127 }
1128
1129 protected void updateAnnotationSetsList() {
1130 String corpusName =
1131 (corpusToSearchIn.getSelectedItem().equals(Constants.ENTIRE_DATASTORE))?
1132 null:(String)corpusIds.get(corpusToSearchIn.getSelectedIndex() - 1);
1133 TreeSet<String> ts = new TreeSet<String>(stringCollator);
1134 ts.addAll(getAnnotationSetNames(corpusName));
1135 DefaultComboBoxModel dcbm = new DefaultComboBoxModel(ts.toArray());
1136 dcbm.insertElementAt(Constants.ALL_SETS, 0);
1137 annotationSetsToSearchIn.setModel(dcbm);
1138 annotationSetsToSearchIn.setSelectedItem(Constants.ALL_SETS);
1139
1140 // used in the ConfigureStackViewFrame as Annotation type column cell editor
1141 TreeSet<String> types = new TreeSet<String>(stringCollator);
1142 types.addAll(getTypesAndFeatures(null, null).keySet());
1143 // put all annotation types from the datastore
1144 // combobox used as cell editor
1145 JComboBox annotTypesBox = new JComboBox();
1146 annotTypesBox.setMaximumRowCount(10);
1147 annotTypesBox.setModel(new DefaultComboBoxModel(types.toArray()));
1148 DefaultCellEditor cellEditor = new DefaultCellEditor(annotTypesBox);
1149 cellEditor.setClickCountToStart(0);
1150 configureStackViewFrame.getTable().getColumnModel()
1151 .getColumn(ANNOTATION_TYPE)
1152 .setCellEditor(cellEditor);
1153 }
1154
1155 protected void updateAnnotationTypesList() {
1156 String corpusName =
1157 (corpusToSearchIn.getSelectedItem().equals(Constants.ENTIRE_DATASTORE))?
1158 null:(String)corpusIds.get(corpusToSearchIn.getSelectedIndex() - 1);
1159 String annotationSetName =
1160 (annotationSetsToSearchIn.getSelectedItem().equals(Constants.ALL_SETS))?
1161 null:(String)annotationSetsToSearchIn.getSelectedItem();
1162 populatedAnnotationTypesAndFeatures =
1163 getTypesAndFeatures(corpusName, annotationSetName);
1164
1165 int countTotal = 0;
1166 try {
1167 int count;
1168 TreeSet<String> ts = new TreeSet<String>(stringCollator);
1169 ts.addAll(populatedAnnotationTypesAndFeatures.keySet());
1170 globalStatisticsTableModel.setRowCount(0);
1171 for (String annotationType : ts) {
1172 // retrieves the number of occurrences for each Annotation Type
1173 // of the choosen Annotation Set
1174 count = searcher.freq(corpusName, annotationSetName, annotationType);
1175 globalStatisticsTableModel.addRow(new Object[]{annotationType, count});
1176 countTotal += count;
1177 }
1178 } catch(SearchException se) {
1179 se.printStackTrace();
1180 return;
1181 }
1182 if (countTotal == 0) {
1183 centerPanel.removeAll();
1184 centerPanel.add(new JLabel("<html>There is no annotation for the moment "
1185 +"for the selected corpus and annotation set.<br><br>"
1186 +"Select another corpus or annotation set or wait for the "
1187 +"end of the automatic indexation."),
1188 new GridBagConstraints());
1189 }
1190 }
1191
1192 protected Set<String> getAnnotationSetNames(String corpusName) {
1193 Set<String> toReturn = new HashSet<String>();
1194 if(corpusName == null) {
1195 for(String aSet : annotationSetIDsFromDataStore) {
1196 aSet = aSet.substring(aSet.indexOf(';') + 1);
1197 toReturn.add(aSet);
1198 }
1199 }
1200 else {
1201 for(String aSet : annotationSetIDsFromDataStore) {
1202 if(aSet.startsWith(corpusName + ";")) {
1203 aSet = aSet.substring(aSet.indexOf(';') + 1);
1204 toReturn.add(aSet);
1205 }
1206 }
1207 }
1208 return toReturn;
1209 }
1210
1211 protected Map<String, Set<String>> getTypesAndFeatures(String corpusName,
1212 String annotationSetName) {
1213 HashMap<String, Set<String>> toReturn = new HashMap<String, Set<String>>();
1214 if(corpusName == null && annotationSetName == null) {
1215 // we simply go through all the annotTyes
1216 // remove corpusID;annotationSetID; from it
1217 for(String type : allAnnotTypesAndFeaturesFromDatastore.keySet()) {
1218 String annotation = type.substring(type.lastIndexOf(';') + 1);
1219 Set<String> features = toReturn.get(annotation);
1220 if(features == null) {
1221 features = new HashSet<String>();
1222 toReturn.put(annotation, features);
1223 }
1224 features.addAll(allAnnotTypesAndFeaturesFromDatastore.get(type));
1225 }
1226 }
1227 else if(corpusName == null) {
1228 // we simply go through all the annotTyes
1229 // remove corpusID;annotationSetID; from it
1230 for(String type : allAnnotTypesAndFeaturesFromDatastore.keySet()) {
1231 String annotation = type.substring(type.indexOf(';') + 1);
1232 if(annotation.startsWith(annotationSetName + ";")) {
1233 annotation = annotation.substring(annotation.indexOf(';') + 1);
1234 Set<String> features = toReturn.get(annotation);
1235 if(features == null) {
1236 features = new HashSet<String>();
1237 toReturn.put(annotation, features);
1238 }
1239 features.addAll(allAnnotTypesAndFeaturesFromDatastore.get(type));
1240 }
1241 }
1242 }
1243 else if(annotationSetName == null) {
1244 // we simply go through all the annotTyes
1245 // remove corpusID;annotationSetID; from it
1246 for(String type : allAnnotTypesAndFeaturesFromDatastore.keySet()) {
1247 if(type.startsWith(corpusName + ";")) {
1248 String annotation = type.substring(type.lastIndexOf(';') + 1);
1249 Set<String> features = toReturn.get(annotation);
1250 if(features == null) {
1251 features = new HashSet<String>();
1252 toReturn.put(annotation, features);
1253 }
1254 features.addAll(allAnnotTypesAndFeaturesFromDatastore.get(type));
1255 }
1256 }
1257 }
1258 else {
1259 // we simply go through all the annotTyes
1260 // remove corpusID;annotationSetID; from it
1261 for(String type : allAnnotTypesAndFeaturesFromDatastore.keySet()) {
1262 if(type.startsWith(corpusName + ";" + annotationSetName + ";")) {
1263 String annotation = type.substring(type.lastIndexOf(';') + 1);
1264 Set<String> features = toReturn.get(annotation);
1265 if(features == null) {
1266 features = new HashSet<String>();
1267 toReturn.put(annotation, features);
1268 }
1269 features.addAll(allAnnotTypesAndFeaturesFromDatastore.get(type));
1270 }
1271 }
1272 }
1273 return toReturn;
1274 }
1275
1276 /**
1277 * Find the first stack row satisfying all the parameters.
1278 * @param parameters couples of int*String that stands for column*value
1279 * @return -2 if there is an error in parameters, -1 if not found,
1280 * row satisfying the parameters otherwise
1281 * @see #DISPLAY DISPLAY column parameter
1282 * @see #SHORTCUT SHORTCUT column parameter
1283 * @see #ANNOTATION_TYPE ANNOTATION_TYPE column parameter
1284 * @see #FEATURE FEATURE column parameter
1285 * @see #CROP CROP column parameter
1286 */
1287 protected int findStackRow(Object... parameters) {
1288 //test the number of parameters
1289 if ((parameters.length % 2) != 0) { return -2; }
1290 // test the type and value of the parameters
1291 for (int num = 0; num < parameters.length; num += 2) {
1292 if (parameters[num] == null || parameters[num+1] == null) { return -2; }
1293 try {
1294 if (Integer.parseInt(parameters[num].toString()) < 0
1295 || Integer.parseInt(parameters[num].toString()) > (columnNames.length-1)) {
1296 return -2;
1297 }
1298 } catch (NumberFormatException nfe) { return -2; }
1299 if (!(parameters[num+1] instanceof String)) { return -2; }
1300 }
1301
1302 // look for the first row satisfying all the parameters
1303 for (int row = 0; row < numStackRows; row++) {
1304 int numParametersSatisfied = 0;
1305 for(int num = 0; num < parameters.length; num += 2) {
1306 if (stackRows[row][Integer.parseInt(parameters[num].toString())]
1307 .equals(parameters[num+1])) {
1308 numParametersSatisfied++;
1309 }
1310 }
1311 if (numParametersSatisfied == (parameters.length/2)) { return row; }
1312 }
1313 return -1;
1314 }
1315
1316 /**
1317 * Delete a row in the stackRows array by shifting the following
1318 * rows to avoid empty row.
1319 * @param row row to delete in the stackRows array
1320 * @return true if deleted, false otherwise
1321 */
1322 protected boolean deleteStackRow(int row) {
1323 if (row < 0 || row > numStackRows) { return false; }
1324 // shift the rows in the array
1325 for(int row2 = row; row2 < numStackRows; row2++) {
1326 System.arraycopy(stackRows[row2 + 1], 0, stackRows[row2], 0,
1327 columnNames.length);
1328 }
1329 stackRows[numStackRows][DISPLAY] = "true";
1330 stackRows[numStackRows][SHORTCUT] = "";
1331 stackRows[numStackRows][ANNOTATION_TYPE] = "";
1332 stackRows[numStackRows][FEATURE] = "";
1333 stackRows[numStackRows][CROP] = "Crop end";
1334 numStackRows--;
1335 return true;
1336 }
1337
1338 /**
1339 * Save the user config data.
1340 */
1341 protected void saveStackViewConfiguration() {
1342 List<String> list = new ArrayList<String>();
1343 for (int row = 0; row < numStackRows; row++) {
1344 list.addAll(Arrays.asList(stackRows[row]));
1345 }
1346 Gate.getUserConfig().put(LuceneDataStoreSearchGUI.class.getName()+".rows",
1347 Strings.toString(list));
1348 }
1349
1350 /**
1351 * Exports results and statistics to a HTML File.
1352 */
1353 protected class ExportResultsAction extends AbstractAction {
1354
1355 public ExportResultsAction() {
1356 super("Export", MainFrame.getIcon("crystal-clear-app-download-manager"));
1357 super.putValue(SHORT_DESCRIPTION,
1358 "Export results and statistics to a HTML file.");
1359 super.putValue(MNEMONIC_KEY, KeyEvent.VK_E);
1360 }
1361
1362 public void actionPerformed(ActionEvent ae) {
1363 XJFileChooser fileChooser = (MainFrame.getFileChooser() != null) ?
1364 MainFrame.getFileChooser() : new XJFileChooser();
1365 fileChooser.setAcceptAllFileFilterUsed(true);
1366 fileChooser.setDialogTitle("Choose a file to export the results");
1367 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1368 ExtensionFileFilter filter = new ExtensionFileFilter("HTML files","html");
1369 fileChooser.addChoosableFileFilter(filter);
1370 String location = (target instanceof SerialDataStore) ?
1371 '-' + ((SerialDataStore)target).getStorageDir().getName() : "";
1372 String fileName = "Datastore" + location + ".html";
1373 fileChooser.setFileName(fileName);
1374 fileChooser.setResource(LuceneDataStoreSearchGUI.class.getName());
1375 int res = fileChooser.showSaveDialog(LuceneDataStoreSearchGUI.this);
1376 if (res != JFileChooser.APPROVE_OPTION) { return; }
1377
1378 File saveFile = fileChooser.getSelectedFile();
1379 BufferedWriter bw = null;
1380 try {
1381 String nl = Strings.getNl();
1382 bw = new BufferedWriter(new FileWriter(saveFile));
1383 bw.write("<!DOCTYPE html PUBLIC " +
1384 "\"-//W3C//DTD HTML 4.01 Transitional//EN\"" + nl);
1385 bw.write("\"http://www.w3.org/TR/html4/loose.dtd\">" + nl);
1386 bw.write("<HTML><HEAD><TITLE>Annic Results and Statistics</TITLE>" + nl);
1387 bw.write("<meta http-equiv=\"Content-Type\"" +
1388 " content=\"text/html; charset=utf-8\">" + nl);
1389 bw.write("</HEAD><BODY>" + nl + nl);
1390
1391 bw.write("<H1 align=\"center\">Annic Results and Statistics</H1>" + nl);
1392 bw.write("<H2>Parameters</H2>" + nl);
1393 bw.write("<UL><LI>Corpus: <B>"
1394 + corpusToSearchIn.getSelectedItem()+"</B></LI>" + nl);
1395 bw.write("<LI>Annotation set: <B>"
1396 + annotationSetsToSearchIn.getSelectedItem()+"</B></LI>" + nl);
1397 bw.write("<LI>Query Issued: <B>" + searcher.getQuery()+"</B></LI>");
1398 bw.write("<LI>Context Window: <B>" + searcher.getParameters().get(
1399 Constants.CONTEXT_WINDOW) + "</B></LI>" + nl);
1400 bw.write("</UL>" + nl + nl);
1401
1402 bw.write("<H2>Results</H2>" + nl);
1403 bw.write("<TABLE border=\"1\"><TBODY>" + nl);
1404 bw.write("<TR>");
1405 for (int col = 0; col < resultTable.getColumnCount(); col++) {
1406 bw.write("<TH>"+resultTable.getColumnName(col)+"</TH>"+nl);
1407 }
1408 bw.write("</TR>" + nl);
1409 for (int row = 0; row < resultTable.getRowCount(); row++) {
1410 bw.write("<TR>");
1411 for (int col = 0; col < resultTable.getColumnCount(); col++) {
1412 bw.write("<TD>"+((String) resultTable.getValueAt(row, col))
1413 .replaceAll("&", "&").replaceAll("<", "<")
1414 .replaceAll(">", ">").replaceAll("\"", """)+"</TD>"+nl);
1415 }
1416 bw.write("</TR>" + nl);
1417 }
1418 bw.write("</TBODY></TABLE>" + nl + nl);
1419
1420 bw.write("<H2>Global Statistics</H2>");
1421 bw.write("<TABLE border=\"1\"><TBODY>" + nl);
1422 bw.write("<TR>");
1423 for (int col = 0; col < globalStatisticsTable.getColumnCount(); col++) {
1424 bw.write("<TH>"+globalStatisticsTable.getColumnName(col)+"</TH>"+nl);
1425 }
1426 bw.write("</TR>" + nl);
1427 for (int row = 0; row < globalStatisticsTable.getRowCount(); row++) {
1428 bw.write("<TR>");
1429 for (int col = 0; col < globalStatisticsTable.getColumnCount(); col++) {
1430 bw.write("<TD>"+globalStatisticsTable.getValueAt(row, col)+"</TD>"+nl);
1431 }
1432 bw.write("</TR>" + nl);
1433 }
1434 bw.write("</TBODY></TABLE>" + nl + nl);
1435
1436 bw.write("<H2>One item Statistics</H2>" + nl);
1437 bw.write("<TABLE border=\"1\"><TBODY>" + nl);
1438 bw.write("<TR>");
1439 for (int col = 0; col < (oneRowStatisticsTable.getColumnCount()-1); col++) {
1440 bw.write("<TH>"+oneRowStatisticsTable.getColumnName(col)+"</TH>"+nl);
1441 }
1442 bw.write("</TR>" + nl);
1443 for (int row = 0; row < oneRowStatisticsTable.getRowCount(); row++) {
1444 bw.write("<TR>");
1445 for (int col = 0; col < (oneRowStatisticsTable.getColumnCount()-1); col++) {
1446 bw.write("<TD>"+oneRowStatisticsTable.getValueAt(row, col)+"</TD>"+nl);
1447 }
1448 bw.write("</TR>" + nl);
1449 }
1450 bw.write("</TBODY></TABLE>" + nl + nl);
1451
1452 bw.write("<P><BR></P><HR>" + nl);
1453 bw.write("</BODY>" + nl);
1454 bw.write("</HTML>");
1455 bw.flush();
1456
1457 } catch(IOException e) {
1458 e.printStackTrace();
1459
1460 } finally {
1461 try {
1462 if (bw != null) { bw.close(); }
1463 } catch(IOException e) {
1464 e.printStackTrace();
1465 }
1466 }
1467 }
1468 }
1469
1470 /**
1471 * Clear the queryTextArea text box.
1472 */
1473 protected class ClearQueryAction extends AbstractAction {
1474
1475 public ClearQueryAction() {
1476 super("Clear", MainFrame.getIcon("crystal-clear-action-button-cancel"));
1477 super.putValue(SHORT_DESCRIPTION, "Clear the query text box.");
1478 super.putValue(MNEMONIC_KEY, KeyEvent.VK_BACK_SPACE);
1479 }
1480
1481 public void actionPerformed(ActionEvent ae) {
1482 queryTextArea.setText("");
1483 queryTextArea.requestFocusInWindow();
1484 }
1485 }
1486
1487 /**
1488 * Finds out the newly created query and execute it.
1489 */
1490 protected class ExecuteQueryAction extends AbstractAction {
1491
1492 public ExecuteQueryAction() {
1493 super("Search", MainFrame.getIcon("crystal-clear-app-xmag"));
1494 super.putValue(SHORT_DESCRIPTION, "Execute the query.");
1495 super.putValue(MNEMONIC_KEY, KeyEvent.VK_ENTER);
1496 }
1497
1498 public void actionPerformed(ActionEvent ae) {
1499
1500 // disable all mouse and key events and display the wait cursor
1501 final BlockingGlassPane blockingGlassPane = new BlockingGlassPane();
1502 LuceneDataStoreSearchGUI.this.getRootPane()
1503 .setGlassPane(blockingGlassPane);
1504 blockingGlassPane.block(true);
1505
1506 // clear the result table and center view
1507 if (results.size() > 0) {
1508 results.clear();
1509 resultTableModel.fireTableDataChanged();
1510 } else {
1511 updateStackView();
1512 }
1513
1514 // set the search parameters
1515 Map<String, Object> parameters = searcher.getParameters();
1516 if(parameters == null)
1517 parameters = new HashMap<String, Object>();
1518
1519 if(target instanceof LuceneDataStoreImpl) {
1520 String corpus2SearchIn = corpusToSearchIn.getSelectedItem()
1521 .equals(Constants.ENTIRE_DATASTORE) ?
1522 null : (String)corpusIds.get(corpusToSearchIn.getSelectedIndex() - 1);
1523 parameters.put(Constants.CORPUS_ID, corpus2SearchIn);
1524 }
1525
1526 noOfResults = (numberOfResultsSlider.getValue()
1527 > (numberOfResultsSlider.getMaximum() - 100))?
1528 -1:((Number)numberOfResultsSlider.getValue()).intValue();
1529 int contextWindow = ((Number)contextSizeSlider.getValue()).intValue();
1530 String query = queryTextArea.getText().trim();
1531 java.util.regex.Pattern pattern =
1532 java.util.regex.Pattern.compile("[\\{, ]([^\\{=]+)==");
1533 Matcher matcher = pattern.matcher(query);
1534 int start = 0;
1535 while (matcher.find(start)) {
1536 start = matcher.end(1); // avoid infinite loop
1537 int row = findStackRow(SHORTCUT, matcher.group(1));
1538 if (row >= 0) {
1539 // rewrite the query to put the long form of the
1540 // shortcut found
1541 query = query.substring(0, matcher.start(1))
1542 +stackRows[row][ANNOTATION_TYPE]+"."
1543 +stackRows[row][FEATURE]
1544 +query.substring(matcher.end(1));
1545 matcher = pattern.matcher(query);
1546 }
1547 }
1548
1549 parameters.put(Constants.CONTEXT_WINDOW, contextWindow);
1550 if (annotationSetsToSearchIn.getSelectedItem()
1551 .equals(Constants.ALL_SETS)) {
1552 parameters.remove(Constants.ANNOTATION_SET_ID);
1553 } else {
1554 String annotationSet =
1555 (String)annotationSetsToSearchIn.getSelectedItem();
1556 parameters.put(Constants.ANNOTATION_SET_ID, annotationSet);
1557 }
1558
1559 // execute the search
1560 final String queryF = query;
1561 final Map<String, Object> parametersF = parameters;
1562 SwingUtilities.invokeLater(new Runnable() {
1563 public void run() {
1564 try {
1565 if(searcher.search(queryF, parametersF)) {
1566 searcher.next(noOfResults);
1567 }
1568
1569 } catch (SearchException se) {
1570 errorOnLastQuery = true;
1571 GridBagConstraints gbc = new GridBagConstraints();
1572 gbc.gridwidth = GridBagConstraints.REMAINDER;
1573 centerPanel.removeAll();
1574 String[] message = se.getMessage().split("\\n");
1575 if (message.length == 1) {
1576 // some errors to fix into QueryParser
1577 se.printStackTrace();
1578 return;
1579 }
1580 // message[0] contains the Java error
1581 JTextArea jta = new JTextArea(message[1]);
1582 jta.setForeground(Color.RED);
1583 centerPanel.add(jta, gbc);
1584 jta = new JTextArea(message[2]);
1585 if (message.length > 3) {
1586 jta.setText(message[2]+"\n"+message[3]);
1587 }
1588 jta.setFont(new Font("Monospaced", Font.PLAIN, 12));
1589 centerPanel.add(jta, gbc);
1590
1591 } catch(Exception e) {
1592 e.printStackTrace();
1593
1594 } finally {
1595 updateViews();
1596 pageOfResults = 1;
1597 titleResults.setText(
1598 "Page "+pageOfResults+" ("+searcher.getHits().length+" results)");
1599 queryTextArea.requestFocusInWindow();
1600 SwingUtilities.invokeLater(new Runnable() {
1601 public void run() {
1602 blockingGlassPane.block(false);
1603 }});
1604 }
1605 }});
1606 }
1607 }
1608
1609 /**
1610 * Finds out the next few results.
1611 */
1612 protected class NextResultsAction extends AbstractAction {
1613
1614 public NextResultsAction() {
1615 super("Next page of " + numberOfResultsSlider.getValue() + " results",
1616 MainFrame.getIcon("crystal-clear-action-loopnone"));
1617 super.putValue(SHORT_DESCRIPTION, "Show next page of results.");
1618 super.putValue(MNEMONIC_KEY, KeyEvent.VK_RIGHT);
1619 }
1620
1621 public void actionPerformed(ActionEvent ae) {
1622
1623 // disable all mouse and key events and display the wait cursor
1624 final BlockingGlassPane blockingGlassPane = new BlockingGlassPane();
1625 LuceneDataStoreSearchGUI.this.getRootPane()
1626 .setGlassPane(blockingGlassPane);
1627 blockingGlassPane.block(true);
1628
1629 // clear the results table and center view
1630 if (results.size() > 0) {
1631 results.clear();
1632 resultTableModel.fireTableDataChanged();
1633 } else {
1634 updateStackView();
1635 }
1636
1637 SwingUtilities.invokeLater(new Runnable() {
1638 public void run() {
1639 noOfResults = ((Number)numberOfResultsSlider.getValue()).intValue();
1640 try {
1641 searcher.next(noOfResults);
1642
1643 } catch(Exception e) {
1644 e.printStackTrace();
1645
1646 } finally {
1647 updateViews();
1648 pageOfResults++;
1649 titleResults.setText(
1650 "Page "+pageOfResults+" ("+searcher.getHits().length+" results)");
1651 if (searcher.getHits().length < noOfResults) {
1652 nextResultsAction.setEnabled(false);
1653 }
1654 queryTextArea.requestFocusInWindow();
1655 SwingUtilities.invokeLater(new Runnable() {
1656 public void run() {
1657 blockingGlassPane.block(false);
1658 }});
1659 }
1660 }});
1661 }
1662 }
1663
1664 /**
1665 * Show the configuration window for the annotation stack view.
1666 */
1667 protected class ConfigureStackViewAction extends AbstractAction {
1668
1669 public ConfigureStackViewAction() {
1670 super("Configure", MainFrame.getIcon("crystal-clear-action-edit-add"));
1671 super.putValue(SHORT_DESCRIPTION, "Configure the view");
1672 super.putValue(MNEMONIC_KEY, KeyEvent.VK_LEFT);
1673 }
1674
1675 public void actionPerformed(ActionEvent e) {
1676 configureStackViewFrame.setVisible(false);
1677 configureStackViewFrame.setVisible(true);
1678 }
1679 }
1680
1681 /**
1682 * Add at the caret position or replace the selection in the query
1683 * according to the text row value left clicked.
1684 */
1685 public class TextMouseListener extends StackMouseListener {
1686
1687 public TextMouseListener() {
1688 }
1689
1690 public TextMouseListener(String text) {
1691 this.text = text;
1692 }
1693
1694 public MouseInputAdapter createListener(String... parameters) {
1695 return new TextMouseListener(parameters[0]);
1696 }
1697
1698 public void mouseClicked(MouseEvent me) {
1699 if (!me.isPopupTrigger()
1700 && me.getButton() == MouseEvent.BUTTON1
1701 && me.getClickCount() == 2) {
1702 int caretPosition = queryTextArea.getCaretPosition();
1703 String query = queryTextArea.getText();
1704 String queryMiddle = text;
1705 String queryLeft =
1706 (queryTextArea.getSelectionStart() == queryTextArea.getSelectionEnd())?
1707 query.substring(0, caretPosition):
1708 query.substring(0, queryTextArea.getSelectionStart());
1709 String queryRight =
1710 (queryTextArea.getSelectionStart() == queryTextArea.getSelectionEnd())?
1711 query.substring(caretPosition, query.length()):
1712 query.substring(queryTextArea.getSelectionEnd(), query.length());
1713 queryTextArea.setText(queryLeft+queryMiddle+queryRight);
1714 }
1715 }
1716
1717 public void mouseEntered(MouseEvent e) {
1718 Component component = e.getComponent();
1719 if (!isTooltipSet && component instanceof JLabel) {
1720 isTooltipSet = true;
1721 JLabel label = (JLabel) component;
1722 Pattern result = (Pattern) results.get(resultTable.rowViewToModel(
1723 resultTable.getSelectionModel().getLeadSelectionIndex()));
1724 label.setToolTipText("The query that matched this " +
1725 "expression was: " + result.getQueryString() + ".");
1726 }
1727 }
1728
1729 String text;
1730 boolean isTooltipSet = false;
1731 }
1732
1733 /**
1734 * Modifies the query or displays statistics according to the
1735 * annotation rectangle clicked.
1736 */
1737 protected class AnnotationMouseListener extends StackMouseListener {
1738
1739 String type;
1740 String feature;
1741 String text;
1742 String description;
1743 String toolTip;
1744 String descriptionTemplate;
1745 String toolTipTemplate;
1746
1747 JPopupMenu mousePopup;
1748 JMenuItem menuItem;
1749
1750 final String corpusID =
1751 (corpusToSearchIn.getSelectedItem().equals(Constants.ENTIRE_DATASTORE))?
1752 null:(String)corpusIds.get(corpusToSearchIn.getSelectedIndex() - 1);
1753 final String annotationSetID =
1754 (annotationSetsToSearchIn.getSelectedItem().equals(Constants.ALL_SETS))?
1755 null:(String)annotationSetsToSearchIn.getSelectedItem();
1756 final String corpusName = (String)corpusToSearchIn.getSelectedItem();
1757 final String annotationSetName = (String)annotationSetsToSearchIn.getSelectedItem();
1758
1759 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
1760 int dismissDelay, initialDelay, reshowDelay;
1761 boolean enabled;
1762 boolean isTooltipSet = false;
1763
1764 public AnnotationMouseListener() {
1765 }
1766
1767 public AnnotationMouseListener(String type, String feature, String text) {
1768 this.type = type;
1769 this.feature = feature;
1770 this.text = text;
1771 String value;
1772 if (text.replace("\\s", "").length() > 20) {
1773 value = text.replace("\\s", "").substring(0, 20)+("...");
1774 } else {
1775 value = text.replace("\\s", "");
1776 }
1777 this.descriptionTemplate =
1778 type + "." + feature + "==\"" + value + "\" (kind)";
1779 this.toolTipTemplate = "Statistics in kind"
1780 +"<br>on Corpus: "+corpusName
1781 +"<br>and Annotation Set: "+annotationSetName
1782 +"<br>for the query: "+results.get(0).getQueryString();
1783 }
1784
1785 public AnnotationMouseListener(String type) {
1786 this.type = type;
1787 }
1788
1789 public MouseInputAdapter createListener(String... parameters) {
1790 switch(parameters.length) {
1791 case 3:
1792 return new AnnotationMouseListener(parameters[1]);
1793 case 5:
1794 return new AnnotationMouseListener(
1795 parameters[1], parameters[2], parameters[3]);
1796 default:
1797 return null;
1798 }
1799 }
1800
1801 public void mouseEntered(MouseEvent e) {
1802 dismissDelay = toolTipManager.getDismissDelay();
1803 initialDelay = toolTipManager.getInitialDelay();
1804 reshowDelay = toolTipManager.getReshowDelay();
1805 enabled = toolTipManager.isEnabled();
1806 Component component = e.getComponent();
1807 if (feature != null && !isTooltipSet && component instanceof JLabel) {
1808 isTooltipSet = true;
1809 JLabel label = (JLabel) component;
1810 String toolTip = label.getToolTipText();
1811 toolTip = (toolTip == null || toolTip.equals("")) ?
1812 "" : toolTip.replaceAll("</?html>", "") + "<br>";
1813 toolTip = "<html>" + toolTip + "Right click to get statistics.</html>";
1814 label.setToolTipText(toolTip);
1815 }
1816 // make the tooltip indefinitely shown when the mouse is over
1817 toolTipManager.setDismissDelay(Integer.MAX_VALUE);
1818 toolTipManager.setInitialDelay(0);
1819 toolTipManager.setReshowDelay(0);
1820 toolTipManager.setEnabled(true);
1821 }
1822
1823 public void mouseExited(MouseEvent e) {
1824 toolTipManager.setDismissDelay(dismissDelay);
1825 toolTipManager.setInitialDelay(initialDelay);
1826 toolTipManager.setReshowDelay(reshowDelay);
1827 toolTipManager.setEnabled(enabled);
1828 }
1829
1830 public void mousePressed(MouseEvent e) {
1831 if (e.isPopupTrigger() && type != null && feature != null) {
1832 createPopup(e);
1833 mousePopup.show(e.getComponent(), e.getX(), e.getY());
1834 } else if (e.getButton() == MouseEvent.BUTTON1
1835 && e.getClickCount() == 2) {
1836 updateQuery();
1837 }
1838 }
1839
1840 public void mouseReleased(MouseEvent e) {
1841 if (e.isPopupTrigger() && type != null && feature != null) {
1842 createPopup(e);
1843 mousePopup.show(e.getComponent(), e.getX(), e.getY());
1844 }
1845 }
1846
1847 private void updateQuery() {
1848 int caretPosition = queryTextArea.getCaretPosition();
1849 String query = queryTextArea.getText();
1850 String queryMiddle;
1851
1852 if (type != null && feature != null) {
1853 int row = findStackRow(ANNOTATION_TYPE, type, FEATURE, feature);
1854 if (row >= 0 && !stackRows[row][SHORTCUT].equals("")) {
1855 queryMiddle = "{"+stackRows[row][SHORTCUT]+"==\""+text+"\"}";
1856 } else {
1857 queryMiddle = "{"+type+"."+feature+"==\""+text+"\"}";
1858 }
1859 } else if (type != null) {
1860 queryMiddle = "{"+type+"}";
1861
1862 } else {
1863 queryMiddle = text;
1864 }
1865 String queryLeft =
1866 (queryTextArea.getSelectionStart() == queryTextArea.getSelectionEnd())?
1867 query.substring(0, caretPosition):
1868 query.substring(0, queryTextArea.getSelectionStart());
1869 String queryRight =
1870 (queryTextArea.getSelectionStart() == queryTextArea.getSelectionEnd())?
1871 query.substring(caretPosition, query.length()):
1872 query.substring(queryTextArea.getSelectionEnd(), query.length());
1873 queryTextArea.setText(queryLeft+queryMiddle+queryRight);
1874 }
1875
1876 private int checkStatistics() {
1877 boolean found = false;
1878 int numRow = 0;
1879 // check if this statistics doesn't already exist in the table
1880 for (int row = 0; row < oneRowStatisticsTable.getRowCount(); row++) {
1881 String oldDescription = (String)
1882 oneRowStatisticsTable.getValueAt(row, 0);
1883 String oldToolTip = oneRowStatisticsTableToolTips
1884 .get(oneRowStatisticsTable.rowViewToModel(numRow));
1885 if (oldDescription.equals(description)
1886 && oldToolTip.equals(toolTip)) {
1887 found = true;
1888 break;
1889 }
1890 numRow++;
1891 }
1892 return found ? numRow : -1;
1893 }
1894
1895 private void addStatistics(String kind, int count, int numRow,
1896 final MouseEvent e) {
1897 JLabel label = (JLabel) e.getComponent();
1898 if (!label.getToolTipText().contains(kind)) {
1899 // add the statistics to the tooltip
1900 String toolTip = label.getToolTipText();
1901 toolTip = toolTip.replaceAll("</?html>", "");
1902 toolTip = kind + " = " + count + "<br>" + toolTip;
1903 toolTip = "<html>" + toolTip + "</html>";
1904 label.setToolTipText(toolTip);
1905 }
1906 if (bottomSplitPane.getDividerLocation()
1907 / bottomSplitPane.getSize().getWidth() < 0.90) {
1908 // select the row in the statistics table
1909 statisticsTabbedPane.setSelectedIndex(1);
1910 oneRowStatisticsTable.setRowSelectionInterval(numRow, numRow);
1911 oneRowStatisticsTable.scrollRectToVisible(
1912 oneRowStatisticsTable.getCellRect(numRow, 0, true));
1913 } else {
1914 // display a tooltip
1915 JToolTip tip = label.createToolTip();
1916 tip.setTipText(kind + " = " + count);
1917 PopupFactory popupFactory = PopupFactory.getSharedInstance();
1918 final Popup tipWindow = popupFactory.getPopup(label, tip,
1919 e.getX()+e.getComponent().getLocationOnScreen().x,
1920 e.getY()+e.getComponent().getLocationOnScreen().y);
1921 tipWindow.show();
1922 Date timeToRun = new Date(System.currentTimeMillis() + 2000);
1923 Timer timer = new Timer("Annic statistics hide tooltip timer", true);
1924 timer.schedule(new TimerTask() {
1925 public void run() {
1926 // hide the tooltip after 2 seconds
1927 tipWindow.hide();
1928 }
1929 }, timeToRun);
1930 }
1931 }
1932
1933 private void createPopup(final MouseEvent e) {
1934 mousePopup = new JPopupMenu();
1935
1936 menuItem = new JMenuItem("Occurrences in datastore");
1937 menuItem.addActionListener(new ActionListener() {
1938 public void actionPerformed(ActionEvent ie) {
1939 description = descriptionTemplate.replaceFirst("kind", "datastore");
1940 toolTip = toolTipTemplate.replaceFirst("kind", "datastore");
1941 int count;
1942 int numRow = checkStatistics();
1943 if (numRow == -1) {
1944 try { // retrieves the number of occurrences
1945 count = searcher.freq(corpusID, annotationSetID,
1946 type, feature, text);
1947 } catch(SearchException se) {
1948 se.printStackTrace();
1949 return;
1950 }
1951 oneRowStatisticsTableModel.addRow(
1952 new Object[]{description, count, ""});
1953 oneRowStatisticsTableToolTips.add(toolTip);
1954 numRow = oneRowStatisticsTable.rowModelToView(
1955 oneRowStatisticsTable.getRowCount() - 1);
1956 } else {
1957 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
1958 }
1959 addStatistics("datastore", count, numRow, e);
1960 }
1961 });
1962 mousePopup.add(menuItem);
1963
1964 menuItem = new JMenuItem("Occurrences in matches");
1965 menuItem.addActionListener(new ActionListener() {
1966 public void actionPerformed(ActionEvent ie) {
1967 description = descriptionTemplate.replaceFirst("kind", "matches");
1968 toolTip = toolTipTemplate.replaceFirst("kind", "matches");
1969 int count;
1970 int numRow = checkStatistics();
1971 if (numRow == -1) {
1972 try { // retrieves the number of occurrences
1973 count = searcher.freq(results, type,
1974 feature, text, true, false);
1975 } catch(SearchException se) {
1976 se.printStackTrace();
1977 return;
1978 }
1979 oneRowStatisticsTableModel.addRow(
1980 new Object[]{description, count, ""});
1981 oneRowStatisticsTableToolTips.add(toolTip);
1982 numRow = oneRowStatisticsTable.rowModelToView(
1983 oneRowStatisticsTable.getRowCount() - 1);
1984 } else {
1985 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
1986 }
1987 addStatistics("matches", count, numRow, e);
1988 }
1989 });
1990 mousePopup.add(menuItem);
1991
1992 menuItem = new JMenuItem("Occurrences in contexts");
1993 menuItem.addActionListener(new ActionListener() {
1994 public void actionPerformed(ActionEvent ie) {
1995 description = descriptionTemplate.replaceFirst("kind", "contexts");
1996 toolTip = toolTipTemplate.replaceFirst("kind", "contexts");
1997 int count;
1998 int numRow = checkStatistics();
1999 if (numRow == -1) {
2000 try { // retrieves the number of occurrences
2001 count = searcher.freq(results, type,
2002 feature, text, false, true);
2003 } catch(SearchException se) {
2004 se.printStackTrace();
2005 return;
2006 }
2007 oneRowStatisticsTableModel.addRow(
2008 new Object[]{description, count, ""});
2009 oneRowStatisticsTableToolTips.add(toolTip);
2010 numRow = oneRowStatisticsTable.rowModelToView(
2011 oneRowStatisticsTable.getRowCount() - 1);
2012 } else {
2013 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2014 }
2015 addStatistics("contexts", count, numRow, e);
2016 }
2017 });
2018 mousePopup.add(menuItem);
2019
2020 menuItem = new JMenuItem("Occurrences in matches+contexts");
2021 menuItem.addActionListener(new ActionListener() {
2022 public void actionPerformed(ActionEvent ie) {
2023 description = descriptionTemplate.replaceFirst("kind", "mch+ctxt");
2024 toolTip = toolTipTemplate.replaceFirst("kind", "matches+contexts");
2025 int count;
2026 int numRow = checkStatistics();
2027 if (numRow == -1) {
2028 try { // retrieves the number of occurrences
2029 count = searcher.freq(results, type,
2030 feature, text, true, true);
2031 } catch(SearchException se) {
2032 se.printStackTrace();
2033 return;
2034 }
2035 oneRowStatisticsTableModel.addRow(
2036 new Object[]{description, count, ""});
2037 oneRowStatisticsTableToolTips.add(toolTip);
2038 numRow = oneRowStatisticsTable.rowModelToView(
2039 oneRowStatisticsTable.getRowCount() - 1);
2040 } else {
2041 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2042 }
2043 addStatistics("matches+contexts", count, numRow, e);
2044 }
2045 });
2046 mousePopup.add(menuItem);
2047 }
2048 }
2049
2050 /**
2051 * Displays statistics according to the stack row header right-clicked.
2052 */
2053 protected class HeaderMouseListener extends StackMouseListener {
2054
2055 String type;
2056 String feature;
2057 String description;
2058 String toolTip;
2059 String descriptionTemplate;
2060 String toolTipTemplate;
2061
2062 JPopupMenu mousePopup;
2063 JMenuItem menuItem;
2064 XJTable table;
2065 JWindow popupWindow;
2066 int row;
2067
2068 final String corpusID =
2069 (corpusToSearchIn.getSelectedItem().equals(Constants.ENTIRE_DATASTORE))?
2070 null:(String)corpusIds.get(corpusToSearchIn.getSelectedIndex() - 1);
2071 final String annotationSetID =
2072 (annotationSetsToSearchIn.getSelectedItem().equals(Constants.ALL_SETS))?
2073 null:(String)annotationSetsToSearchIn.getSelectedItem();
2074 final String corpusName = (String)corpusToSearchIn.getSelectedItem();
2075 final String annotationSetName = (String)annotationSetsToSearchIn.getSelectedItem();
2076 boolean isTooltipSet = false;
2077
2078 public HeaderMouseListener() {
2079 }
2080
2081 public HeaderMouseListener(String type, String feature) {
2082 this.type = type;
2083 this.feature = feature;
2084 this.descriptionTemplate = type + "." + feature + " (kind)";
2085 this.toolTipTemplate = "Statistics in kind"
2086 +"<br>on Corpus: "+corpusName
2087 +"<br>and Annotation Set: "+annotationSetName
2088 +"<br>for the query: "+results.get(0).getQueryString();
2089 init();
2090 }
2091
2092 public HeaderMouseListener(String type) {
2093 this.type = type;
2094 this.descriptionTemplate = type + " (kind)";
2095 this.toolTipTemplate = "Statistics in kind"
2096 +"<br>on Corpus: "+corpusName
2097 +"<br>and Annotation Set: "+annotationSetName
2098 +"<br>for the query: "+results.get(0).getQueryString();
2099 init();
2100 }
2101
2102 void init() {
2103 addAncestorListener(new AncestorListener() {
2104 public void ancestorMoved(AncestorEvent event) {}
2105 public void ancestorAdded(AncestorEvent event) {}
2106 public void ancestorRemoved(AncestorEvent event) {
2107 // no parent so need to be disposed explicitly
2108 if (popupWindow != null) { popupWindow.dispose(); }
2109 }
2110 });
2111 row = findStackRow(ANNOTATION_TYPE, type,
2112 FEATURE, (feature == null ? "" : feature));
2113 }
2114
2115 public MouseInputAdapter createListener(String... parameters) {
2116 switch(parameters.length) {
2117 case 1:
2118 return new HeaderMouseListener(parameters[0]);
2119 case 2:
2120 return new HeaderMouseListener(parameters[0], parameters[1]);
2121 default:
2122 return null;
2123 }
2124 }
2125
2126 public void mouseEntered(MouseEvent e) {
2127 Component component = e.getComponent();
2128 if (!isTooltipSet && component instanceof JLabel) {
2129 isTooltipSet = true;
2130 JLabel label = (JLabel) component;
2131 String shortcut = "";
2132 if (feature != null) {
2133 int row = resultTable.rowViewToModel(
2134 resultTable.getSelectionModel().getLeadSelectionIndex());
2135 if (!stackRows[row][SHORTCUT].equals("")) {
2136 shortcut = "Shortcut for " + type + "." + feature + ".<br>";
2137 }
2138 }
2139 label.setToolTipText("<html>" + shortcut +
2140 "Double click to choose annotation feature.<br>" +
2141 "Right click to get statistics.</html>");
2142 }
2143 }
2144
2145 // when double clicked shows a list of features for this annotation type
2146 public void mouseClicked(MouseEvent e) {
2147 if (popupWindow != null && popupWindow.isVisible()) {
2148 popupWindow.dispose();
2149 return;
2150 }
2151 if (e.getButton() != MouseEvent.BUTTON1
2152 || e.getClickCount() != 2) { return; }
2153 // get a list of features for the current annotation type
2154 TreeSet<String> features = new TreeSet<String>();
2155 if (populatedAnnotationTypesAndFeatures.containsKey(type)) {
2156 // this annotation type still exists in the datastore
2157 features.addAll(populatedAnnotationTypesAndFeatures.get(type));
2158 }
2159 features.add(" ");
2160 // create the list component
2161 final JList list = new JList(features.toArray());
2162 list.setVisibleRowCount(Math.min(8, features.size()));
2163 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
2164 list.setBackground(Color.WHITE);
2165 list.addMouseListener(new MouseAdapter() {
2166 public void mouseClicked(MouseEvent e) {
2167 if (e.getClickCount() == 1) {
2168 String newFeature = (String) list.getSelectedValue();
2169 if (newFeature.equals(" ")) { newFeature = ""; }
2170 stackRows[row][FEATURE] = newFeature;
2171 saveStackViewConfiguration();
2172 popupWindow.setVisible(false);
2173 popupWindow.dispose();
2174 updateStackView();
2175 }
2176 }
2177 });
2178 // create the window that will contain the list
2179 popupWindow = new JWindow();
2180 popupWindow.addKeyListener(new KeyAdapter() {
2181 public void keyPressed(KeyEvent e) {
2182 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
2183 popupWindow.setVisible(false);
2184 popupWindow.dispose();
2185 }
2186 }
2187 });
2188 popupWindow.add(new JScrollPane(list));
2189 Component component = e.getComponent();
2190 popupWindow.setBounds(
2191 component.getLocationOnScreen().x,
2192 component.getLocationOnScreen().y + component.getHeight(),
2193 component.getWidth(),
2194 Math.min(8*component.getHeight(), features.size()*component.getHeight()));
2195 popupWindow.pack();
2196 popupWindow.setVisible(true);
2197 SwingUtilities.invokeLater(new Runnable() {
2198 public void run() {
2199 String newFeature = stackRows[row][FEATURE];
2200 if (newFeature.equals("")) { newFeature = " "; }
2201 list.setSelectedValue(newFeature, true);
2202 popupWindow.requestFocusInWindow();
2203 }});
2204 }
2205
2206 public void mousePressed(MouseEvent e) {
2207 if (e.isPopupTrigger()) {
2208 createPopup(e);
2209 mousePopup.show(e.getComponent(), e.getX(), e.getY());
2210 }
2211 }
2212
2213 public void mouseReleased(MouseEvent e) {
2214 if (e.isPopupTrigger()) {
2215 createPopup(e);
2216 mousePopup.show(e.getComponent(), e.getX(), e.getY());
2217 }
2218 }
2219
2220 private int checkStatistics() {
2221 boolean found = false;
2222 int numRow = 0;
2223 // check if this statistics doesn't already exist in the table
2224 for (int row = 0; row < oneRowStatisticsTable.getRowCount(); row++) {
2225 String oldDescription = (String)
2226 oneRowStatisticsTable.getValueAt(row, 0);
2227 String oldToolTip = oneRowStatisticsTableToolTips
2228 .get(oneRowStatisticsTable.rowViewToModel(numRow));
2229 if (oldDescription.equals(description)
2230 && oldToolTip.equals(toolTip)) {
2231 found = true;
2232 break;
2233 }
2234 numRow++;
2235 }
2236 return found ? numRow : -1;
2237 }
2238
2239 private void addStatistics(String kind, int count, int numRow,
2240 final MouseEvent e) {
2241 JLabel label = (JLabel) e.getComponent();
2242 if (!label.getToolTipText().contains(kind)) {
2243 // add the statistics to the tooltip
2244 String toolTip = label.getToolTipText();
2245 toolTip = toolTip.replaceAll("</?html>", "");
2246 toolTip = kind + " = " + count + "<br>" + toolTip;
2247 toolTip = "<html>" + toolTip + "</html>";
2248 label.setToolTipText(toolTip);
2249 }
2250 if (bottomSplitPane.getDividerLocation()
2251 / bottomSplitPane.getSize().getWidth() < 0.90) {
2252 // select the row in the statistics table
2253 statisticsTabbedPane.setSelectedIndex(1);
2254 oneRowStatisticsTable.setRowSelectionInterval(numRow, numRow);
2255 oneRowStatisticsTable.scrollRectToVisible(
2256 oneRowStatisticsTable.getCellRect(numRow, 0, true));
2257 } else {
2258 // display a tooltip
2259 JToolTip tip = label.createToolTip();
2260 tip.setTipText(kind + " = " + count);
2261 PopupFactory popupFactory = PopupFactory.getSharedInstance();
2262 final Popup tipWindow = popupFactory.getPopup(label, tip,
2263 e.getX()+e.getComponent().getLocationOnScreen().x,
2264 e.getY()+e.getComponent().getLocationOnScreen().y);
2265 tipWindow.show();
2266 Date timeToRun = new Date(System.currentTimeMillis() + 2000);
2267 Timer timer = new Timer("Annic statistics hide tooltip timer", true);
2268 timer.schedule(new TimerTask() {
2269 public void run() {
2270 // hide the tooltip after 2 seconds
2271 tipWindow.hide();
2272 }
2273 }, timeToRun);
2274 }
2275 }
2276
2277 private void createPopup(final MouseEvent e) {
2278 mousePopup = new JPopupMenu();
2279
2280 if (type != null && feature != null) {
2281
2282 // count values for one Feature of an Annotation type
2283
2284 menuItem = new JMenuItem("Occurrences in datastore");
2285 menuItem.addActionListener(new ActionListener() {
2286 public void actionPerformed(ActionEvent ie) {
2287 description = descriptionTemplate.replaceFirst("kind", "datastore");
2288 toolTip = toolTipTemplate.replaceFirst("kind", "datastore");
2289 int count;
2290 int numRow = checkStatistics();
2291 if (numRow == -1) {
2292 try { // retrieves the number of occurrences
2293 count = searcher.freq(corpusID, annotationSetID, type, feature);
2294 } catch(SearchException se) {
2295 se.printStackTrace();
2296 return;
2297 }
2298 oneRowStatisticsTableModel.addRow(
2299 new Object[]{description, count, ""});
2300 oneRowStatisticsTableToolTips.add(toolTip);
2301 numRow = oneRowStatisticsTable.rowModelToView(
2302 oneRowStatisticsTable.getRowCount() - 1);
2303 } else {
2304 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2305 }
2306 addStatistics("datastore", count, numRow, e);
2307 }
2308 });
2309 mousePopup.add(menuItem);
2310
2311 menuItem = new JMenuItem("Occurrences in matches");
2312 menuItem.addActionListener(new ActionListener() {
2313 public void actionPerformed(ActionEvent ie) {
2314 description = descriptionTemplate.replaceFirst("kind", "matches");
2315 toolTip = toolTipTemplate.replaceFirst("kind", "matches");
2316 int count;
2317 int numRow = checkStatistics();
2318 if (numRow == -1) {
2319 try { // retrieves the number of occurrences
2320 count = searcher.freq(results, type,
2321 feature, null, true, false);
2322 } catch(SearchException se) {
2323 se.printStackTrace();
2324 return;
2325 }
2326 oneRowStatisticsTableModel.addRow(
2327 new Object[]{description, count, ""});
2328 oneRowStatisticsTableToolTips.add(toolTip);
2329 numRow = oneRowStatisticsTable.rowModelToView(
2330 oneRowStatisticsTable.getRowCount() - 1);
2331 } else {
2332 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2333 }
2334 addStatistics("matches", count, numRow, e);
2335 }
2336 });
2337 mousePopup.add(menuItem);
2338
2339 menuItem = new JMenuItem("Occurrences in contexts");
2340 menuItem.addActionListener(new ActionListener() {
2341 public void actionPerformed(ActionEvent ie) {
2342 description = descriptionTemplate.replaceFirst("kind", "contexts");
2343 toolTip = toolTipTemplate.replaceFirst("kind", "contexts");
2344 int count;
2345 int numRow = checkStatistics();
2346 if (numRow == -1) {
2347 try { // retrieves the number of occurrences
2348 count = searcher.freq(results, type,
2349 feature, null, false, true);
2350 } catch(SearchException se) {
2351 se.printStackTrace();
2352 return;
2353 }
2354 oneRowStatisticsTableModel.addRow(
2355 new Object[]{description, count, ""});
2356 oneRowStatisticsTableToolTips.add(toolTip);
2357 numRow = oneRowStatisticsTable.rowModelToView(
2358 oneRowStatisticsTable.getRowCount() - 1);
2359 } else {
2360 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2361 }
2362 addStatistics("contexts", count, numRow, e);
2363 }
2364 });
2365 mousePopup.add(menuItem);
2366
2367 menuItem = new JMenuItem("Occurrences in matches+contexts");
2368 menuItem.addActionListener(new ActionListener() {
2369 public void actionPerformed(ActionEvent ie) {
2370 description = descriptionTemplate.replaceFirst("kind", "mch+ctxt");
2371 toolTip = toolTipTemplate.replaceFirst("kind", "matches+contexts");
2372 int count;
2373 int numRow = checkStatistics();
2374 if (numRow == -1) {
2375 try { // retrieves the number of occurrences
2376 count = searcher.freq(results, type,
2377 feature, null, true, true);
2378 } catch(SearchException se) {
2379 se.printStackTrace();
2380 return;
2381 }
2382 oneRowStatisticsTableModel.addRow(
2383 new Object[]{description, count, ""});
2384 oneRowStatisticsTableToolTips.add(toolTip);
2385 numRow = oneRowStatisticsTable.rowModelToView(
2386 oneRowStatisticsTable.getRowCount() - 1);
2387 } else {
2388 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2389 }
2390 addStatistics("matches+contexts", count, numRow, e);
2391 }
2392 });
2393 mousePopup.add(menuItem);
2394
2395 // count values for all Features of an Annotation Type
2396
2397 mousePopup.addSeparator();
2398
2399 menuItem = new JMenuItem("All values from matches");
2400 menuItem.addActionListener(new ActionListener() {
2401 public void actionPerformed(ActionEvent ie) {
2402 Map<String, Integer> freqs;
2403 try { // retrieves the number of occurrences
2404 freqs = searcher.freqForAllValues(
2405 results, type, feature, true, false);
2406 } catch(SearchException se) {
2407 se.printStackTrace();
2408 return;
2409 }
2410 DefaultTableModel model = new DefaultTableModel();
2411 model.addColumn(type+'.'+feature+" (matches)");
2412 model.addColumn("Count");
2413 for (Map.Entry<String,Integer> map : freqs.entrySet()) {
2414 model.addRow(new Object[]{map.getKey(), map.getValue()});
2415 }
2416 table = new XJTable() {
2417 public boolean isCellEditable(int rowIndex, int vColIndex) {
2418 return false;
2419 }
2420 };
2421 table.setModel(model);
2422 table.setComparator(0, stringCollator);
2423 table.setComparator(1, integerComparator);
2424 statisticsTabbedPane.addTab(
2425 String.valueOf(statisticsTabbedPane.getTabCount()-1),
2426 null, new JScrollPane(table),
2427 "<html>Statistics in matches"
2428 +"<br>on Corpus: "+corpusName
2429 +"<br>and Annotation Set: "+annotationSetName
2430 +"<br>for the query: "+results.get(0).getQueryString()
2431 +"</html>");
2432 if (bottomSplitPane.getDividerLocation()
2433 / bottomSplitPane.getSize().getWidth() > 0.75) {
2434 bottomSplitPane.setDividerLocation(0.66);
2435 }
2436 statisticsTabbedPane.setSelectedIndex(
2437 statisticsTabbedPane.getTabCount()-1);
2438 }
2439 });
2440 mousePopup.add(menuItem);
2441
2442 menuItem = new JMenuItem("All values from contexts");
2443 menuItem.addActionListener(new ActionListener() {
2444 public void actionPerformed(ActionEvent ie) {
2445 Map<String, Integer> freqs;
2446 try { // retrieves the number of occurrences
2447 freqs = searcher.freqForAllValues(
2448 results, type, feature, false, true);
2449 } catch(SearchException se) {
2450 se.printStackTrace();
2451 return;
2452 }
2453 DefaultTableModel model = new DefaultTableModel();
2454 model.addColumn(type+'.'+feature+" (contexts)");
2455 model.addColumn("Count");
2456 for (Map.Entry<String,Integer> map : freqs.entrySet()) {
2457 model.addRow(new Object[]{map.getKey(), map.getValue()});
2458 }
2459 table = new XJTable() {
2460 public boolean isCellEditable(int rowIndex, int vColIndex) {
2461 return false;
2462 }
2463 };
2464 table.setModel(model);
2465 table.setComparator(0, stringCollator);
2466 table.setComparator(1, integerComparator);
2467 statisticsTabbedPane.addTab(
2468 String.valueOf(statisticsTabbedPane.getTabCount()-1),
2469 null, new JScrollPane(table),
2470 "<html>Statistics in contexts"
2471 +"<br>on Corpus: "+corpusName
2472 +"<br>and Annotation Set: "+annotationSetName
2473 +"<br>for the query: "+results.get(0).getQueryString()
2474 +"</html>");
2475 if (bottomSplitPane.getDividerLocation()
2476 / bottomSplitPane.getSize().getWidth() > 0.75) {
2477 bottomSplitPane.setDividerLocation(0.66);
2478 }
2479 statisticsTabbedPane.setSelectedIndex(
2480 statisticsTabbedPane.getTabCount()-1);
2481 }
2482 });
2483 mousePopup.add(menuItem);
2484
2485 menuItem = new JMenuItem("All values from matches+contexts");
2486 menuItem.addActionListener(new ActionListener() {
2487 public void actionPerformed(ActionEvent ie) {
2488 Map<String, Integer> freqs;
2489 try { // retrieves the number of occurrences
2490 freqs = searcher.freqForAllValues(
2491 results, type, feature, true, true);
2492 } catch(SearchException se) {
2493 se.printStackTrace();
2494 return;
2495 }
2496 DefaultTableModel model = new DefaultTableModel();
2497 model.addColumn(type+'.'+feature+" (mch+ctxt)");
2498 model.addColumn("Count");
2499 for (Map.Entry<String,Integer> map : freqs.entrySet()) {
2500 model.addRow(new Object[]{map.getKey(), map.getValue()});
2501 }
2502 table = new XJTable() {
2503 public boolean isCellEditable(int rowIndex, int vColIndex) {
2504 return false;
2505 }
2506 };
2507 table.setModel(model);
2508 table.setComparator(0, stringCollator);
2509 table.setComparator(1, integerComparator);
2510 statisticsTabbedPane.addTab(
2511 String.valueOf(statisticsTabbedPane.getTabCount()-1),
2512 null, new JScrollPane(table),
2513 "<html>Statistics in matches+contexts"
2514 +"<br>on Corpus: "+corpusName
2515 +"<br>and Annotation Set: "+annotationSetName
2516 +"<br>for the query: "+results.get(0).getQueryString()
2517 +"</html>");
2518 if (bottomSplitPane.getDividerLocation()
2519 / bottomSplitPane.getSize().getWidth() > 0.75) {
2520 bottomSplitPane.setDividerLocation(0.66);
2521 }
2522 statisticsTabbedPane.setSelectedIndex(
2523 statisticsTabbedPane.getTabCount()-1);
2524 }
2525 });
2526 mousePopup.add(menuItem);
2527
2528 } else {
2529 // count values of one Annotation type
2530
2531 menuItem = new JMenuItem("Occurrences in datastore");
2532 menuItem.addActionListener(new ActionListener() {
2533 public void actionPerformed(ActionEvent ie) {
2534 description = descriptionTemplate.replaceFirst("kind", "datastore");
2535 toolTip = toolTipTemplate.replaceFirst("kind", "datastore");
2536 int count;
2537 int numRow = checkStatistics();
2538 if (numRow == -1) {
2539 try { // retrieves the number of occurrences
2540 count = searcher.freq(corpusID, annotationSetID, type);
2541 } catch(SearchException se) {
2542 se.printStackTrace();
2543 return;
2544 }
2545 oneRowStatisticsTableModel.addRow(
2546 new Object[]{description, count, ""});
2547 oneRowStatisticsTableToolTips.add(toolTip);
2548 numRow = oneRowStatisticsTable.rowModelToView(
2549 oneRowStatisticsTable.getRowCount() - 1);
2550 } else {
2551 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2552 }
2553 addStatistics("datastore", count, numRow, e);
2554 }
2555 });
2556 mousePopup.add(menuItem);
2557
2558 menuItem = new JMenuItem("Occurrences in matches");
2559 menuItem.addActionListener(new ActionListener() {
2560 public void actionPerformed(ActionEvent ie) {
2561 description = descriptionTemplate.replaceFirst("kind", "matches");
2562 toolTip = toolTipTemplate.replaceFirst("kind", "matches");
2563 int count;
2564 int numRow = checkStatistics();
2565 if (numRow == -1) {
2566 try { // retrieves the number of occurrences
2567 count = searcher.freq(results, type, true, false);
2568 } catch(SearchException se) {
2569 se.printStackTrace();
2570 return;
2571 }
2572 oneRowStatisticsTableModel.addRow(
2573 new Object[]{description, count, ""});
2574 oneRowStatisticsTableToolTips.add(toolTip);
2575 numRow = oneRowStatisticsTable.rowModelToView(
2576 oneRowStatisticsTable.getRowCount() - 1);
2577 } else {
2578 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2579 }
2580 addStatistics("matches", count, numRow, e);
2581 }
2582 });
2583 mousePopup.add(menuItem);
2584
2585 menuItem = new JMenuItem("Occurrences in contexts");
2586 menuItem.addActionListener(new ActionListener() {
2587 public void actionPerformed(ActionEvent ie) {
2588 description = descriptionTemplate.replaceFirst("kind", "contexts");
2589 toolTip = toolTipTemplate.replaceFirst("kind", "contexts");
2590 int count;
2591 int numRow = checkStatistics();
2592 if (numRow == -1) {
2593 try { // retrieves the number of occurrences
2594 count = searcher.freq(results, type, false, true);
2595 } catch(SearchException se) {
2596 se.printStackTrace();
2597 return;
2598 }
2599 oneRowStatisticsTableModel.addRow(
2600 new Object[]{description, count, ""});
2601 oneRowStatisticsTableToolTips.add(toolTip);
2602 numRow = oneRowStatisticsTable.rowModelToView(
2603 oneRowStatisticsTable.getRowCount() - 1);
2604 } else {
2605 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2606 }
2607 addStatistics("contexts", count, numRow, e);
2608 }
2609 });
2610 mousePopup.add(menuItem);
2611
2612 menuItem = new JMenuItem("Occurrences in matches+contexts");
2613 menuItem.addActionListener(new ActionListener() {
2614 public void actionPerformed(ActionEvent ie) {
2615 description = descriptionTemplate.replaceFirst("kind", "mch+ctxt");
2616 toolTip = toolTipTemplate.replaceFirst("kind", "matches+contexts");
2617 int count;
2618 int numRow = checkStatistics();
2619 if (numRow == -1) {
2620 try { // retrieves the number of occurrences
2621 count = searcher.freq(results, type, true, true);
2622 } catch(SearchException se) {
2623 se.printStackTrace();
2624 return;
2625 }
2626 oneRowStatisticsTableModel.addRow(
2627 new Object[]{description, count, ""});
2628 oneRowStatisticsTableToolTips.add(toolTip);
2629 numRow = oneRowStatisticsTable.rowModelToView(
2630 oneRowStatisticsTable.getRowCount() - 1);
2631 } else {
2632 count = (Integer) oneRowStatisticsTable.getValueAt(numRow, 1);
2633 }
2634 addStatistics("matches+contexts", count, numRow, e);
2635 }
2636 });
2637 mousePopup.add(menuItem);
2638 }
2639 }
2640 }
2641
2642 protected class ResultTableCellRenderer extends DefaultTableCellRenderer{
2643 public Component getTableCellRendererComponent(JTable table, Object value,
2644 boolean isSelected, boolean hasFocus, int row, int column){
2645 String text = (String) value;
2646 int colModel = resultTable.convertColumnIndexToModel(column);
2647 // cut text in the middle if too long
2648 switch(colModel) {
2649 case ResultTableModel.RESULT_COLUMN:
2650 case ResultTableModel.FEATURES_COLUMN:
2651 if (text.length() > ResultTableModel.MAX_COL_WIDTH) {
2652 text = text.substring(0, ResultTableModel.MAX_COL_WIDTH/2) + "..."
2653 + text.substring(text.length()-(ResultTableModel.MAX_COL_WIDTH/2));
2654 }
2655 text = text.replaceAll("(?:\r?\n)|\r", " ");
2656 text = text.replaceAll("\t", " ");
2657 break;
2658 default:
2659 // do nothing
2660 break;
2661 }
2662 Component component = super.getTableCellRendererComponent(
2663 table, text, isSelected, hasFocus, row, column);
2664 if (!(component instanceof JLabel)) { return component; }
2665 JLabel label = (JLabel) component;
2666 label.setHorizontalAlignment(SwingConstants.LEFT);
2667 String tip = null;
2668 // add tooltips
2669 switch(colModel) {
2670 case ResultTableModel.LEFT_CONTEXT_COLUMN:
2671 label.setHorizontalAlignment(SwingConstants.RIGHT);
2672 break;
2673 case ResultTableModel.RESULT_COLUMN:
2674 case ResultTableModel.FEATURES_COLUMN:
2675 if (((String)value).length() > ResultTableModel.MAX_COL_WIDTH) {
2676 tip = (String) value;
2677 if (tip.length() > 1000) {
2678 tip = tip.substring(0, 1000 / 2) + "<br>...<br>"
2679 + tip.substring(tip.length() - (1000 / 2));
2680 }
2681 tip = tip.replaceAll("\\s*\n\\s*", "<br>");
2682 tip = tip.replaceAll("\\s+", " ");
2683 tip = "<html><table width=\"" + (tip.length() > 150? "500": "100%")
2684 + "\" border=\"0\" cellspacing=\"0\">"
2685 + "<tr><td>" + tip + "</td></tr>"
2686 + "</table></html>";
2687 }
2688 if (colModel == ResultTableModel.RESULT_COLUMN) {
2689 label.setHorizontalAlignment(SwingConstants.CENTER);
2690 }
2691 break;
2692 default:
2693 // do nothing
2694 break;
2695 }
2696 label.setToolTipText(tip);
2697 return label;
2698 }
2699 }
2700
2701 /**
2702 * Table model for the Result Tables.
2703 */
2704 protected class ResultTableModel extends AbstractTableModel {
2705
2706 public ResultTableModel() {
2707 featureByTypeMap = new HashMap<String, String>();
2708 }
2709
2710 public int getRowCount() {
2711 return results.size();
2712 }
2713
2714 public int getColumnCount() {
2715 return COLUMN_COUNT;
2716 }
2717
2718 public String getColumnName(int columnIndex) {
2719 switch(columnIndex) {
2720 case LEFT_CONTEXT_COLUMN:
2721 return "Left context";
2722 case RESULT_COLUMN:
2723 return "Match";
2724 case RIGHT_CONTEXT_COLUMN:
2725 return "Right context";
2726 case FEATURES_COLUMN:
2727 return "Features";
2728 case QUERY_COLUMN:
2729 return "Query";
2730 case DOCUMENT_COLUMN:
2731 return "Document";
2732 case SET_COLUMN:
2733 return "Annotation set";
2734 default:
2735 return "?";
2736 }
2737 }
2738
2739 public Class<?> getColumnClass(int columnIndex) {
2740 return String.class;
2741 }
2742
2743 public boolean isCellEditable(int rowIndex, int columnIndex) {
2744 return false;
2745 }
2746
2747 public Object getValueAt(int rowIndex, int columnIndex) {
2748 Pattern result = (Pattern) results.get(rowIndex);
2749 switch(columnIndex) {
2750 case LEFT_CONTEXT_COLUMN:
2751 return result.getPatternText(result.getLeftContextStartOffset(),
2752 result.getStartOffset()).replaceAll("[\n ]+", " ");
2753 case RESULT_COLUMN:
2754 return result.getPatternText(
2755 result.getStartOffset(), result.getEndOffset());
2756 case RIGHT_CONTEXT_COLUMN:
2757 return result.getPatternText(result.getEndOffset(), result
2758 .getRightContextEndOffset()).replaceAll("[\n ]+", " ");
2759 case FEATURES_COLUMN:
2760 StringBuffer buffer = new StringBuffer();
2761 for (Map.Entry<String, String> featureType :
2762 featureByTypeMap.entrySet()) {
2763 String type = featureType.getKey();
2764 String feature = featureType.getValue();
2765 List<PatternAnnotation> annotations = result.getPatternAnnotations(
2766 result.getStartOffset(), result.getEndOffset());
2767 buffer.append(type).append('.').append(feature).append('=');
2768 for (PatternAnnotation annotation : annotations) {
2769 if (annotation.getType().equals(type)
2770 && annotation.getFeature(feature) != null) {
2771 buffer.append(annotation.getFeatures().get(feature))
2772 .append(", ");
2773 }
2774 }
2775 if (buffer.length() > 2) {
2776 if (buffer.codePointAt(buffer.length()-2) == ',') {
2777 // delete the last ", "
2778 buffer.delete(buffer.length()-2, buffer.length());
2779 // and replace it with a "; "
2780 buffer.append("; ");
2781 } else if (buffer.codePointAt(buffer.length()-1) == '=') {
2782 // delete the last "Type.Feature="
2783 buffer.delete(buffer.length()-type.length()-feature.length()-2,
2784 buffer.length());
2785 }
2786 }
2787 }
2788 if (buffer.length() > 2) {
2789 // delete the last "; "
2790 buffer.delete(buffer.length()-2, buffer.length());
2791 }
2792 return buffer.toString();
2793 case QUERY_COLUMN:
2794 return result.getQueryString();
2795 case DOCUMENT_COLUMN:
2796 return result.getDocumentID();
2797 case SET_COLUMN:
2798 return result.getAnnotationSetName();
2799 default:
2800 return Object.class;
2801 }
2802 }
2803
2804 public void fireTableDataChanged() {
2805 // reinitialise types and features to display in the "Features" column
2806 featureByTypeMap.clear();
2807 for (int row = 0; row < numStackRows; row++) {
2808 if (!stackRows[row][DISPLAY].equals("false")
2809 && !stackRows[row][FEATURE].equals("")) {
2810 String feature = stackRows[row][FEATURE];
2811 String type = stackRows[row][ANNOTATION_TYPE];
2812 featureByTypeMap.put(type, feature);
2813 }
2814 }
2815 super.fireTableDataChanged();
2816 }
2817
2818 /** Maximum number of characters for the result column. */
2819 static public final int MAX_COL_WIDTH = 40;
2820 static public final int LEFT_CONTEXT_COLUMN = 0;
2821 static public final int RESULT_COLUMN = 1;
2822 static public final int RIGHT_CONTEXT_COLUMN = 2;
2823 static public final int FEATURES_COLUMN = 3;
2824 static public final int QUERY_COLUMN = 4;
2825 static public final int DOCUMENT_COLUMN = 5;
2826 static public final int SET_COLUMN = 6;
2827 static public final int COLUMN_COUNT = 7;
2828 protected Map<String, String> featureByTypeMap;
2829 }
2830
2831 /**
2832 * Panel that shows a table of shortcut, annotation type and feature
2833 * to display in the central view of the GUI.
2834 */
2835 protected class ConfigureStackViewFrame extends JFrame {
2836
2837 private final int REMOVE = columnNames.length;
2838 private JTable stackRowsJTable;
2839
2840 public ConfigureStackViewFrame(String title) {
2841 super(title);
2842
2843 setLayout(new BorderLayout());
2844
2845 JScrollPane scrollPane = new JScrollPane(
2846 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
2847 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
2848 scrollPane.getViewport().setOpaque(true);
2849
2850 configureStackViewTableModel = new ConfigureStackViewTableModel();
2851 stackRowsJTable = new XJTable(configureStackViewTableModel);
2852 ((XJTable)stackRowsJTable).setSortable(false);
2853
2854 // combobox used as cell editor
2855
2856 String[] s = {"Crop middle", "Crop start", "Crop end"};
2857 JComboBox cropBox = new JComboBox(s);
2858
2859 // set the cell renderer and/or editor for each column
2860
2861 stackRowsJTable.getColumnModel().getColumn(DISPLAY)
2862 .setCellRenderer(new DefaultTableCellRenderer() {
2863 public Component getTableCellRendererComponent(
2864 JTable table, Object color, boolean isSelected,
2865 boolean hasFocus, int row, int col) {
2866 JCheckBox checkBox = new JCheckBox();
2867 checkBox.setHorizontalAlignment(SwingConstants.CENTER);
2868 checkBox.setToolTipText(
2869 "Tick to display this row in central section.");
2870 checkBox.setSelected((!table.getValueAt(row, col).equals("false")));
2871 return checkBox;
2872 }});
2873
2874 final class DisplayCellEditor extends AbstractCellEditor
2875 implements TableCellEditor, ActionListener {
2876 JCheckBox checkBox;
2877 public DisplayCellEditor() {
2878 checkBox = new JCheckBox();
2879 checkBox.setHorizontalAlignment(SwingConstants.CENTER);
2880 checkBox.addActionListener(this);
2881 }
2882 public boolean shouldSelectCell(EventObject anEvent) {
2883 return false;
2884 }
2885 public void actionPerformed(ActionEvent e) {
2886 fireEditingStopped();
2887 }
2888 public Object getCellEditorValue() {
2889 return (checkBox.isSelected())?"true":"false";
2890 }
2891 public Component getTableCellEditorComponent(JTable table,
2892 Object value, boolean isSelected, int row, int col) {
2893 checkBox.setSelected((!table.getValueAt(row, col).equals("false")));
2894 return checkBox;
2895 }
2896 }
2897 stackRowsJTable.getColumnModel().getColumn(DISPLAY)
2898 .setCellEditor(new DisplayCellEditor());
2899
2900 stackRowsJTable.getColumnModel().getColumn(SHORTCUT)
2901 .setCellRenderer(new DefaultTableCellRenderer() {
2902 public Component getTableCellRendererComponent(
2903 JTable table, Object color, boolean isSelected,
2904 boolean hasFocus, int row, int col) {
2905 Component c = super.getTableCellRendererComponent(
2906 table, color, isSelected, hasFocus, row, col);
2907 if (c instanceof JComponent) {
2908 ((JComponent)c).setToolTipText("Shortcut can be used in queries "
2909 +"instead of \"AnnotationType.Feature\".");
2910 }
2911 c.setBackground(UIManager.getColor("CheckBox.background"));
2912 return c;
2913 }});
2914
2915 DefaultCellEditor cellEditor = new DefaultCellEditor(new JTextField());
2916 cellEditor.setClickCountToStart(0);
2917 stackRowsJTable.getColumnModel()
2918 .getColumn(SHORTCUT)
2919 .setCellEditor(cellEditor);
2920
2921 stackRowsJTable.getColumnModel()
2922 .getColumn(ANNOTATION_TYPE)
2923 .setCellRenderer(new DefaultTableCellRenderer() {
2924 public Component getTableCellRendererComponent(
2925 JTable table, Object color, boolean isSelected,
2926 boolean hasFocus, int row, int col) {
2927 String[] s = {stackRows[row][ANNOTATION_TYPE]};
2928 return new JComboBox(s);
2929 }
2930 });
2931
2932 final class FeatureCellEditor extends AbstractCellEditor
2933 implements TableCellEditor, ActionListener {
2934 private JComboBox featuresBox;
2935 public FeatureCellEditor() {
2936 featuresBox = new JComboBox();
2937 featuresBox.setMaximumRowCount(10);
2938 featuresBox.addActionListener(this);
2939 }
2940 public void actionPerformed(ActionEvent e) {
2941 fireEditingStopped();
2942 }
2943 public Object getCellEditorValue() {
2944 return (featuresBox.getSelectedItem() == null)?
2945 "":featuresBox.getSelectedItem();
2946 }
2947 public Component getTableCellEditorComponent(JTable table,
2948 Object value, boolean isSelected, int row, int col) {
2949 TreeSet<String> ts = new TreeSet<String>(stringCollator);
2950 if (populatedAnnotationTypesAndFeatures.containsKey((String)
2951 stackRowsJTable.getValueAt(row, ANNOTATION_TYPE))) {
2952 // this annotation type still exists in the datastore
2953 ts.addAll(populatedAnnotationTypesAndFeatures.get((String)
2954 stackRowsJTable.getValueAt(row, ANNOTATION_TYPE)));
2955 }
2956 DefaultComboBoxModel dcbm = new DefaultComboBoxModel(ts.toArray());
2957 dcbm.insertElementAt("", 0);
2958 featuresBox.setModel(dcbm);
2959 featuresBox.setSelectedItem(
2960 ts.contains((String) stackRowsJTable.getValueAt(row, col)) ?
2961 stackRowsJTable.getValueAt(row, col) : "");
2962 return featuresBox;
2963 }
2964 }
2965 stackRowsJTable.getColumnModel().getColumn(FEATURE)
2966 .setCellEditor(new FeatureCellEditor());
2967
2968 stackRowsJTable.getColumnModel()
2969 .getColumn(FEATURE)
2970 .setCellRenderer(new DefaultTableCellRenderer() {
2971 public Component getTableCellRendererComponent(
2972 JTable table, Object color, boolean isSelected,
2973 boolean hasFocus, int row, int col) {
2974 String[] s = {stackRows[row][FEATURE]};
2975 return new JComboBox(s);
2976 }
2977 });
2978
2979 cellEditor = new DefaultCellEditor(cropBox);
2980 cellEditor.setClickCountToStart(0);
2981 stackRowsJTable.getColumnModel()
2982 .getColumn(CROP)
2983 .setCellEditor(cellEditor);
2984 stackRowsJTable.getColumnModel()
2985 .getColumn(CROP)
2986 .setCellRenderer(new DefaultTableCellRenderer() {
2987 public Component getTableCellRendererComponent(
2988 JTable table, Object color, boolean isSelected,
2989 boolean hasFocus, int row, int col) {
2990 String[] s = {stackRows[row][CROP]};
2991 return new JComboBox(s);
2992 }
2993 });
2994
2995 final class AddRemoveCellEditorRenderer extends AbstractCellEditor
2996 implements TableCellRenderer, TableCellEditor, ActionListener {
2997 private JButton button;
2998 public AddRemoveCellEditorRenderer() {
2999 button = new JButton();
3000 button.setHorizontalAlignment(SwingConstants.CENTER);
3001 button.addActionListener(this);
3002 }
3003 public Component getTableCellRendererComponent(
3004 JTable table, Object color, boolean isSelected,
3005 boolean hasFocus, int row, int col) {
3006 if (row == numStackRows) {
3007 // add button if it's the last row of the table
3008 button.setIcon(MainFrame.getIcon("crystal-clear-action-edit-add"));
3009 button.setToolTipText("Click to add this line.");
3010 } else {
3011 // remove button otherwise
3012 button.setIcon(
3013 MainFrame.getIcon("crystal-clear-action-button-cancel"));
3014 button.setToolTipText("Click to remove this line.");
3015 }
3016 button.setSelected(isSelected);
3017 return button;
3018 }
3019 public boolean shouldSelectCell(EventObject anEvent) {
3020 return false;
3021 }
3022 public void actionPerformed(ActionEvent e) {
3023 int row = stackRowsJTable.getEditingRow();
3024 fireEditingStopped();
3025 if (row == numStackRows) {
3026 if (stackRows[row][ANNOTATION_TYPE] != null
3027 && !stackRows[row][ANNOTATION_TYPE].equals("")) {
3028 if (numStackRows == maxStackRows) {
3029 JOptionPane.showMessageDialog(configureStackViewFrame,
3030 "The number of rows is limited to "+maxStackRows+".",
3031 "Alert", JOptionPane.ERROR_MESSAGE);
3032 } else {
3033 // add a new row
3034 numStackRows++;
3035 configureStackViewTableModel.fireTableRowsInserted(row, row+1);
3036 updateStackView();
3037 saveStackViewConfiguration();
3038 }
3039 } else {
3040 JOptionPane.showMessageDialog(configureStackViewFrame,
3041 "Fill at least the Annotation type column.",
3042 "Alert", JOptionPane.ERROR_MESSAGE);
3043 }
3044 } else {
3045 // delete a row
3046 deleteStackRow(row);
3047 configureStackViewTableModel.fireTableDataChanged();
3048 updateStackView();
3049 saveStackViewConfiguration();
3050 }
3051 }
3052 public Object getCellEditorValue() {
3053 return null;
3054 }
3055 public Component getTableCellEditorComponent(JTable table,
3056 Object value, boolean isSelected, int row, int col) {
3057 button.setIcon(MainFrame.getIcon(row == numStackRows ?
3058 "crystal-clear-action-edit-add"
3059 : "crystal-clear-action-button-cancel"));
3060 return button;
3061 }
3062 }
3063 stackRowsJTable.getColumnModel().getColumn(REMOVE)
3064 .setCellEditor(new AddRemoveCellEditorRenderer());
3065 stackRowsJTable.getColumnModel().getColumn(REMOVE)
3066 .setCellRenderer(new AddRemoveCellEditorRenderer());
3067
3068 scrollPane.setViewportView(stackRowsJTable);
3069
3070 add(scrollPane, BorderLayout.CENTER);
3071 }
3072
3073 public JTable getTable() {
3074 return stackRowsJTable;
3075 }
3076 }
3077
3078 /**
3079 * Table model for the stack view configuration.
3080 */
3081 protected class ConfigureStackViewTableModel extends AbstractTableModel {
3082
3083 private final int REMOVE = columnNames.length;
3084
3085 // plus one to let the user adding a new row
3086 public int getRowCount() {
3087 return Math.min(numStackRows+1, maxStackRows+1);
3088 }
3089
3090 // plus one for the add/remove column
3091 public int getColumnCount() {
3092 return columnNames.length+1;
3093 }
3094
3095 public String getColumnName(int col) {
3096 return (col == REMOVE)?"Add/Remove":columnNames[col];
3097 }
3098
3099 public boolean isCellEditable(int row, int col) {
3100 return true;
3101 }
3102
3103 public Class<?> getColumnClass(int c) {
3104 return String.class;
3105 }
3106
3107 public Object getValueAt(int row, int col) {
3108 if (col == REMOVE) { return null; }
3109 return stackRows[row][col];
3110 }
3111
3112 public void setValueAt(Object value, int row, int col) {
3113 if (col == REMOVE) { return; }
3114
3115 String valueString;
3116 if (value instanceof String) {
3117 valueString = (String) value;
3118 } else {
3119 valueString = "value should be a String";
3120 }
3121
3122 if (col == SHORTCUT && !valueString.equals("")) {
3123 if (getTypesAndFeatures(null, null).keySet().contains(valueString)) {
3124 JOptionPane.showMessageDialog(configureStackViewFrame,
3125 "A Shortcut cannot have the same name as an Annotation type.",
3126 "Alert", JOptionPane.ERROR_MESSAGE);
3127 return;
3128 } else {
3129 int row2 = findStackRow(SHORTCUT, valueString);
3130 if (row2 >= 0 && row2 != row) {
3131 JOptionPane.showMessageDialog(configureStackViewFrame,
3132 "A Shortcut with the same name already exists.",
3133 "Alert", JOptionPane.ERROR_MESSAGE);
3134 return;
3135 }
3136 }
3137 }
3138
3139 String previousValue = valueString;
3140 stackRows[row][col] = valueString;
3141
3142 if (!stackRows[row][SHORTCUT].equals("")) {
3143 if (stackRows[row][ANNOTATION_TYPE].equals("")
3144 || stackRows[row][FEATURE].equals("")) {
3145 // TODO table should be updated
3146 configureStackViewFrame.getTable().getColumnModel().getColumn(col)
3147 .getCellEditor().cancelCellEditing();
3148 fireTableCellUpdated(row, col);
3149 stackRows[row][col] = previousValue;
3150 JOptionPane.showMessageDialog(configureStackViewFrame,
3151 "A Shortcut need to have a Feature.\n"
3152 +"Choose a Feature or delete the Shortcut value.",
3153 "Alert", JOptionPane.ERROR_MESSAGE);
3154 return;
3155 } else {
3156 int row2 = findStackRow(
3157 ANNOTATION_TYPE, stackRows[row][ANNOTATION_TYPE],
3158 FEATURE, stackRows[row][FEATURE]);
3159 if (row2 >= 0 && row2 != row
3160 && !stackRows[row2][SHORTCUT].equals("")) {
3161 configureStackViewFrame.getTable().getColumnModel().getColumn(col)
3162 .getCellEditor().cancelCellEditing();
3163 stackRows[row][col] = previousValue;
3164 fireTableCellUpdated(row, col);
3165 JOptionPane.showMessageDialog(configureStackViewFrame,
3166 "You can only have one Shortcut for a couple (Annotation "
3167 +"type, Feature).", "Alert", JOptionPane.ERROR_MESSAGE);
3168 return;
3169 }
3170 }
3171 }
3172
3173 if (stackRows[row][DISPLAY].equals("one time")) {
3174 // make a temporary row permanent if the user changes it
3175 stackRows[row][DISPLAY] = "true";
3176 }
3177
3178 stackRows[row][col] = valueString;
3179 fireTableRowsUpdated(row, row);
3180 updateStackView();
3181 saveStackViewConfiguration();
3182 }
3183 }
3184
3185 /**
3186 * JtextArea with autocompletion for the annotation types and
3187 * features, context menu and undo/redo.
3188 */
3189 protected class QueryTextArea extends JTextArea
3190 implements DocumentListener, MouseListener {
3191
3192 private static final String ENTER_ACTION = "enter";
3193 private static final String NEW_LINE = "new line";
3194 private static final String CANCEL_ACTION = "cancel";
3195 private static final String DOWN_ACTION = "down";
3196 private static final String UP_ACTION = "up";
3197 private static final String UNDO_ACTION = "undo";
3198 private static final String REDO_ACTION = "redo";
3199 private static final String NEXT_RESULT = "next result";
3200 private static final String PREVIOUS_RESULT = "previous result";
3201 protected DefaultListModel queryListModel;
3202 protected JList queryList;
3203 protected JWindow queryPopupWindow;
3204 protected JPopupMenu mousePopup;
3205 protected javax.swing.undo.UndoManager undo;
3206 protected UndoAction undoAction;
3207 protected RedoAction redoAction;
3208 /** offset of the first completion character */
3209 protected int start;
3210 /** offset of the last completion character */
3211 protected int end;
3212 protected int mode;
3213 protected static final int INSERT = 0;
3214 protected static final int POPUP_TYPES = 1;
3215 protected static final int POPUP_FEATURES = 2;
3216 protected static final int PROGRAMMATIC = 3;
3217
3218 public QueryTextArea() {
3219 super();
3220
3221 getDocument().addDocumentListener(this);
3222 addMouseListener(this);
3223 addAncestorListener(new AncestorListener() {
3224 public void ancestorMoved(AncestorEvent event) {}
3225 public void ancestorAdded(AncestorEvent event) {}
3226 public void ancestorRemoved(AncestorEvent event) {
3227 // no parent so need to be disposed explicitly
3228 queryPopupWindow.dispose();
3229 }
3230 });
3231
3232 InputMap im = getInputMap(JComponent.WHEN_FOCUSED);
3233 InputMap imw = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
3234 ActionMap am = getActionMap();
3235 // bind keys to actions
3236 im.put(KeyStroke.getKeyStroke("ENTER"), ENTER_ACTION);
3237 am.put(ENTER_ACTION, new EnterAction());
3238 im.put(KeyStroke.getKeyStroke("control ENTER"), NEW_LINE);
3239 am.put(NEW_LINE, new NewLineAction());
3240 imw.put(KeyStroke.getKeyStroke("ESCAPE"), CANCEL_ACTION);
3241 am.put(CANCEL_ACTION, new CancelAction());
3242 im.put(KeyStroke.getKeyStroke("DOWN"), DOWN_ACTION);
3243 am.put(DOWN_ACTION, new DownAction());
3244 im.put(KeyStroke.getKeyStroke("UP"), UP_ACTION);
3245 am.put(UP_ACTION, new UpAction());
3246 undoAction = new UndoAction();
3247 im.put(KeyStroke.getKeyStroke("control Z"), UNDO_ACTION);
3248 am.put(UNDO_ACTION, undoAction);
3249 redoAction = new RedoAction();
3250 im.put(KeyStroke.getKeyStroke("control Y"), REDO_ACTION);
3251 am.put(REDO_ACTION, redoAction);
3252 im.put(KeyStroke.getKeyStroke("alt DOWN"), NEXT_RESULT);
3253 am.put(NEXT_RESULT, new NextResultAction());
3254 im.put(KeyStroke.getKeyStroke("alt UP"), PREVIOUS_RESULT);
3255 am.put(PREVIOUS_RESULT, new PreviousResultAction());
3256
3257 // list for autocompletion
3258 queryListModel = new DefaultListModel();
3259 queryList = new JList(queryListModel);
3260 queryList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
3261 queryList.setBackground(Color.WHITE);
3262 queryList.addMouseListener(new MouseAdapter() {
3263 public void mouseClicked(MouseEvent e) {
3264 if (e.getClickCount() == 2) {
3265 new EnterAction().actionPerformed(null);
3266 }
3267 }
3268 });
3269
3270 queryPopupWindow = new JWindow();
3271 queryPopupWindow.add(new JScrollPane(queryList));
3272
3273 mousePopup = new JPopupMenu();
3274 mousePopup.add(new JMenuItem(new MultiplierAction(0, 1)));
3275 mousePopup.add(new JMenuItem(new MultiplierAction(0, 2)));
3276 mousePopup.add(new JMenuItem(new MultiplierAction(0, 3)));
3277 mousePopup.add(new JMenuItem(new MultiplierAction(0, 4)));
3278 mousePopup.add(new JMenuItem(new MultiplierAction(0, 5)));
3279 mousePopup.add(new JMenuItem(new MultiplierAction(1, 2)));
3280 mousePopup.add(new JMenuItem(new MultiplierAction(1, 3)));
3281 mousePopup.add(new JMenuItem(new MultiplierAction(1, 4)));
3282 mousePopup.add(new JMenuItem(new MultiplierAction(1, 5)));
3283
3284 undo = new javax.swing.undo.UndoManager();
3285 getDocument().addUndoableEditListener(
3286 new javax.swing.event.UndoableEditListener() {
3287 public void undoableEditHappened(
3288 javax.swing.event.UndoableEditEvent e) {
3289 //Remember the edit and update the menus
3290 undo.addEdit(e.getEdit());
3291 undoAction.updateUndoState();
3292 redoAction.updateRedoState();
3293 }
3294 });
3295
3296 start = 0;
3297 end = 0;
3298 mode = INSERT;
3299
3300 addCaretListener(new CaretListener() {
3301 public void caretUpdate(CaretEvent e) {
3302 if ( (mode == POPUP_TYPES || mode == POPUP_FEATURES)
3303 && (getCaretPosition() < start || getCaretPosition() > (end+1)) ) {
3304 // cancel any autocompletion if the user put the caret
3305 // outside brackets when in POPUP mode
3306 cleanup();
3307 }
3308 }
3309 });
3310 }
3311
3312 /** Add multiplier around the selected expression. */
3313 private class MultiplierAction extends AbstractAction {
3314 int from, upto;
3315 public MultiplierAction(int from, int upto) {
3316 super(from + " to " + upto + " time" + (upto == 1? "" : "s"));
3317 this.from = from;
3318 this.upto = upto;
3319 }
3320 public void actionPerformed(ActionEvent ie) {
3321 try {
3322 getDocument().insertString(getSelectionStart(), "(", null);
3323 getDocument().insertString(getSelectionEnd(),
3324 ")" + (from == 0? "*" : "+") + upto, null);
3325 } catch (javax.swing.text.BadLocationException e) {
3326 e.printStackTrace();
3327 }
3328 }
3329 }
3330
3331 public void changedUpdate(DocumentEvent ev) {
3332 }
3333
3334 public void removeUpdate(DocumentEvent ev) {
3335 if (mode == PROGRAMMATIC || mode == INSERT) { return; }
3336
3337 int pos = ev.getOffset();
3338
3339 if (ev.getLength() != 1
3340 || (pos < start || pos > end)) {
3341 // cancel any autocompletion if the user cut some text
3342 // or delete outside brackets when in POPUP mode
3343 cleanup();
3344 return;
3345 }
3346 if (mode == POPUP_TYPES) {
3347 end = pos;
3348 String type = getText().substring(start, end);
3349 if (!type.matches("[a-zA-Z0-9]+")) { return; }
3350 for (int i = 0; i < queryList.getModel().getSize(); i++) {
3351 if (startsWithIgnoreCase(((String)queryList.getModel()
3352 .getElementAt(i)), type)) {
3353 queryPopupWindow.setVisible(true);
3354 queryList.setSelectedIndex((i));
3355 queryList.ensureIndexIsVisible(i);
3356 break;
3357 }
3358 }
3359 } else if (mode == POPUP_FEATURES) {
3360 end = pos;
3361 String feature = getText().substring(start, end);
3362 if (!feature.matches("[a-zA-Z0-9]+")) { return; }
3363 for (int i = 0; i < queryList.getModel().getSize(); i++) {
3364 if (startsWithIgnoreCase(((String)queryList.getModel()
3365 .getElementAt(i)), feature)) {
3366 queryPopupWindow.setVisible(true);
3367 queryList.setSelectedIndex((i));
3368 queryList.ensureIndexIsVisible(i);
3369 break;
3370 }
3371 }
3372 }
3373 }
3374
3375 public void insertUpdate(DocumentEvent ev) {
3376 if (mode == PROGRAMMATIC) { return; }
3377
3378 int pos = ev.getOffset();
3379
3380 if (ev.getLength() != 1) {
3381 // cancel any autocompletion if the user paste some text
3382 cleanup();
3383 return;
3384 }
3385
3386 String typedChar = Character.toString(getText().charAt(pos));
3387 String previousChar = (pos > 0)?
3388 Character.toString(getText().charAt(pos-1)):"";
3389 String nextChar = ((pos+1) < getText().length())?
3390 Character.toString(getText().charAt(pos+1)):"";
3391
3392 // switch accordingly to the key pressed and the context
3393 if (((typedChar.equals("{") && !previousChar.equals("\\"))
3394 || (typedChar.equals(",") && nextChar.equals("}")))
3395 && mode == INSERT) {
3396 mode = POPUP_TYPES;
3397 start = pos+1;
3398 end = pos+1;
3399 SwingUtilities.invokeLater(new PopupTypeTask());
3400
3401 } else if (typedChar.equals(".")
3402 && mode == INSERT) {
3403 mode = POPUP_FEATURES;
3404 start = pos+1;
3405 end = pos+1;
3406 SwingUtilities.invokeLater(new PopupFeatureTask());
3407
3408 } else if (typedChar.matches("[a-zA-Z0-9]")
3409 && mode == POPUP_TYPES) {
3410 end = pos;
3411 String type = getText().substring(start, end+1);
3412 boolean found = false;
3413 if (type.matches("[a-zA-Z0-9]+")) {
3414 for (int i = 0; i < queryList.getModel().getSize(); i++) {
3415 if (startsWithIgnoreCase(((String)queryList.getModel()
3416 .getElementAt(i)), type)) {
3417 queryPopupWindow.setVisible(true);
3418 queryList.setSelectedIndex(i);
3419 queryList.ensureIndexIsVisible(i);
3420 found = true;
3421 break;
3422 }
3423 }
3424 }
3425 if (!found) { queryPopupWindow.setVisible(false); }
3426
3427 } else if (typedChar.matches("[a-zA-Z0-9]")
3428 && mode == POPUP_FEATURES) {
3429 end = pos;
3430 String feature = getText().substring(start, end+1);
3431 boolean found = false;
3432 if (feature.matches("[a-zA-Z0-9]+")) {
3433 for (int i = 0; i < queryList.getModel().getSize(); i++) {
3434 if (startsWithIgnoreCase(((String)queryList.getModel()
3435 .getElementAt(i)), feature)) {
3436 queryPopupWindow.setVisible(true);
3437 queryList.setSelectedIndex(i);
3438 queryList.ensureIndexIsVisible(i);
3439 found = true;
3440 break;
3441 }
3442 }
3443 }
3444 if (!found) { queryPopupWindow.setVisible(false); }
3445 }
3446 }
3447
3448 private boolean startsWithIgnoreCase(String str1, String str2) {
3449 return str1.toUpperCase().startsWith(str2.toUpperCase());
3450 }
3451
3452 private void cleanup() {
3453 mode = INSERT;
3454 queryPopupWindow.setVisible(false);
3455 }
3456
3457 private class PopupTypeTask implements Runnable {
3458 public void run() {
3459 try {
3460 TreeSet<String> types = new TreeSet<String>(stringCollator);
3461 types.addAll(populatedAnnotationTypesAndFeatures.keySet());
3462 if (types.isEmpty()) {
3463 types.add("No annotation type found !");
3464 }
3465 queryListModel.clear();
3466 for (String type : types) {
3467 queryListModel.addElement(type);
3468 }
3469 queryList.setVisibleRowCount(Math.min(12, types.size()));
3470 Rectangle dotRect = modelToView(getCaret().getDot());
3471 queryPopupWindow.setLocation(
3472 getLocationOnScreen().x // x location of top-left text field
3473 + (int) dotRect.getMaxX(), // caret X relative position
3474 getLocationOnScreen().y // y location of top-left text field
3475 + (int) dotRect.getMaxY()); // caret Y relative position
3476 queryPopupWindow.pack();
3477 queryPopupWindow.setVisible(true);
3478 if (queryListModel.getSize() == 1) {
3479 // preselect if only one list item
3480 queryList.setSelectedIndex(0);
3481 }
3482
3483 } catch (javax.swing.text.BadLocationException e) {
3484 e.printStackTrace();
3485 }
3486 }
3487 }
3488
3489 private class PopupFeatureTask implements Runnable {
3490 public void run() {
3491 // search the annotation type before the dot just typed
3492 int index = Math.max(getText().substring(0, end-1).lastIndexOf("{"),
3493 Math.max(getText().substring(0, end-1).lastIndexOf(","),
3494 getText().substring(0, end-1).lastIndexOf(", ")+1));
3495 String type = getText().substring(index+1, end-1);
3496 if (!populatedAnnotationTypesAndFeatures.containsKey(type)) {
3497 // annotation type not found, do nothing
3498 cleanup();
3499 return;
3500 }
3501 try {
3502 TreeSet<String> features = new TreeSet<String>(stringCollator);
3503 features.addAll(populatedAnnotationTypesAndFeatures.get(type));
3504 queryListModel.clear();
3505 for (String feature : features) {
3506 queryListModel.addElement(feature);
3507 }
3508 queryList.setVisibleRowCount(Math.min(12, features.size()));
3509 Rectangle dotRect = modelToView(getCaret().getDot());
3510 queryPopupWindow.setLocation(
3511 getLocationOnScreen().x // x location of top-left text field
3512 + (int) dotRect.getMaxX(), // caret relative position
3513 getLocationOnScreen().y // y location of top-left text field
3514 + (int) dotRect.getMaxY()); // caret Y relative position
3515 queryPopupWindow.pack();
3516 queryPopupWindow.setVisible(true);
3517 if (queryListModel.getSize() == 1) {
3518 // preselect if only one list item
3519 queryList.setSelectedIndex(0);
3520 }
3521
3522 } catch (javax.swing.text.BadLocationException e) {
3523 e.printStackTrace();
3524 }
3525 }
3526 }
3527
3528 private class EnterAction extends AbstractAction {
3529 public void actionPerformed(ActionEvent ev) {
3530 String selection = (String) queryList.getSelectedValue();
3531 if (mode == POPUP_TYPES) {
3532 if (selection == null) { return; }
3533 mode = PROGRAMMATIC;
3534 try {
3535 if (end < getDocument().getLength()) {
3536 // delete already typed partial string
3537 getDocument().remove(start, end-start+1);
3538 }
3539 // insert selected string from list
3540 getDocument().insertString(start, selection, null);
3541 if (getText().charAt(start-1) != ',') {
3542 getDocument().insertString(start+selection.length(), "}", null);
3543 setCaretPosition(getCaretPosition()-1);
3544 }
3545 } catch (javax.swing.text.BadLocationException e) {
3546 e.printStackTrace();
3547 }
3548
3549 } else if (mode == POPUP_FEATURES) {
3550 if (selection == null) { return; }
3551 mode = PROGRAMMATIC;
3552 try {
3553 if (end < getDocument().getLength()
3554 && getText().charAt(end) != '}') {
3555 getDocument().remove(start, end-start+1);
3556 }
3557 getDocument().insertString(start, selection, null);
3558 getDocument().insertString(start+selection.length(),"==\"\"", null);
3559 setCaretPosition(getCaretPosition()-1);
3560 } catch (javax.swing.text.BadLocationException e) {
3561 e.printStackTrace();
3562 }
3563
3564 } else {
3565 executeQueryAction.actionPerformed(null);
3566 }
3567 cleanup();
3568 }
3569 }
3570
3571 private class CancelAction extends AbstractAction {
3572 public void actionPerformed(ActionEvent ev) {
3573 cleanup();
3574 }
3575 }
3576
3577 private class DownAction extends AbstractAction {
3578 public void actionPerformed(ActionEvent ev) {
3579 if (mode == POPUP_TYPES) {
3580 int index = queryList.getSelectedIndex();
3581 if ((index+1) < queryList.getModel().getSize()) {
3582 queryList.setSelectedIndex(index+1);
3583 queryList.ensureIndexIsVisible(index+1);
3584 }
3585 } else if (mode == POPUP_FEATURES) {
3586 int index = queryList.getSelectedIndex();
3587 if ((index+1) < queryList.getModel().getSize()) {
3588 queryList.setSelectedIndex(index+1);
3589 queryList.ensureIndexIsVisible(index+1);
3590 }
3591 }
3592 }
3593 }
3594
3595 private class UpAction extends AbstractAction {
3596 public void actionPerformed(ActionEvent ev) {
3597 if (mode == POPUP_TYPES) {
3598 int index = queryList.getSelectedIndex();
3599 if (index > 0) {
3600 queryList.setSelectedIndex(index-1);
3601 queryList.ensureIndexIsVisible(index-1);
3602 }
3603 } else if (mode == POPUP_FEATURES) {
3604 int index = queryList.getSelectedIndex();
3605 if (index > 0) {
3606 queryList.setSelectedIndex(index-1);
3607 queryList.ensureIndexIsVisible(index-1);
3608 }
3609 }
3610 }
3611 }
3612
3613 private class PreviousResultAction extends AbstractAction {
3614 public void actionPerformed(ActionEvent ev) {
3615 if (resultTable.getSelectedRow() > 0) {
3616 resultTable.setRowSelectionInterval(resultTable.getSelectedRow()-1,
3617 resultTable.getSelectedRow()-1);
3618 resultTable.scrollRectToVisible(resultTable.getCellRect(
3619 resultTable.getSelectedRow()-1, 0, true));
3620 }
3621 }
3622 }
3623
3624 private class NextResultAction extends AbstractAction {
3625 public void actionPerformed(ActionEvent ev) {
3626 if (resultTable.getSelectedRow()+1 < resultTable.getRowCount()) {
3627 resultTable.setRowSelectionInterval(resultTable.getSelectedRow()+1,
3628 resultTable.getSelectedRow()+1);
3629 resultTable.scrollRectToVisible(resultTable.getCellRect(
3630 resultTable.getSelectedRow()+1, 0, true));
3631 }
3632 }
3633 }
3634
3635 private class NewLineAction extends AbstractAction {
3636 public void actionPerformed(ActionEvent ev) {
3637 try {
3638 getDocument().insertString(getCaretPosition(), "\n", null);
3639 } catch (javax.swing.text.BadLocationException e) {
3640 e.printStackTrace();
3641 }
3642 }
3643 }
3644
3645 private class UndoAction extends AbstractAction {
3646 public UndoAction() {
3647 super("Undo");
3648 setEnabled(false);
3649 }
3650
3651 public void actionPerformed(ActionEvent e) {
3652 try {
3653 undo.undo();
3654 } catch (javax.swing.undo.CannotUndoException ex) {
3655 System.out.println("Unable to undo: " + ex);
3656 ex.printStackTrace();
3657 }
3658 updateUndoState();
3659 redoAction.updateRedoState();
3660 }
3661
3662 protected void updateUndoState() {
3663 if (undo.canUndo()) {
3664 setEnabled(true);
3665 putValue(Action.NAME, undo.getUndoPresentationName());
3666 } else {
3667 setEnabled(false);
3668 putValue(Action.NAME, "Undo");
3669 }
3670 }
3671 }
3672
3673 private class RedoAction extends AbstractAction {
3674 public RedoAction() {
3675 super("Redo");
3676 setEnabled(false);
3677 }
3678
3679 public void actionPerformed(ActionEvent e) {
3680 try {
3681 undo.redo();
3682 } catch (javax.swing.undo.CannotRedoException ex) {
3683 System.out.println("Unable to redo: " + ex);
3684 ex.printStackTrace();
3685 }
3686 updateRedoState();
3687 undoAction.updateUndoState();
3688 }
3689
3690 protected void updateRedoState() {
3691 if (undo.canRedo()) {
3692 setEnabled(true);
3693 putValue(Action.NAME, undo.getRedoPresentationName());
3694 } else {
3695 setEnabled(false);
3696 putValue(Action.NAME, "Redo");
3697 }
3698 }
3699 }
3700
3701 public void mouseEntered(MouseEvent e) {
3702 }
3703
3704 public void mouseClicked(MouseEvent e) {
3705 }
3706
3707 public void mouseExited(MouseEvent e) {
3708 }
3709
3710 public void mousePressed(MouseEvent e) {
3711 if (e.isPopupTrigger()) {
3712 createPopup(e);
3713 mousePopup.show(e.getComponent(), e.getX(), e.getY());
3714 }
3715 }
3716
3717 public void mouseReleased(MouseEvent e) {
3718 if (e.isPopupTrigger()) {
3719 createPopup(e);
3720 mousePopup.show(e.getComponent(), e.getX(), e.getY());
3721 }
3722 }
3723
3724 private void createPopup(MouseEvent e) {
3725 if (getSelectedText() != null
3726 && QueryParser.isValidQuery(getSelectedText())) {
3727 // if the selected text is a valid expression then shows a popup menu
3728
3729 } else if (getDocument().getLength() > 3) {
3730 int positionclicked = viewToModel(e.getPoint());
3731 if (positionclicked >= getDocument().getLength()) {
3732 positionclicked = getDocument().getLength()-1;
3733 }
3734 int start = getText()
3735 .substring(0, positionclicked+1).lastIndexOf("{");
3736 int end = getText().substring(positionclicked, getDocument()
3737 .getLength()).indexOf("}") + positionclicked;
3738 if (start != -1 && end != -1
3739 && QueryParser.isValidQuery(getText().substring(start, end+1))) {
3740 // select the shortest valid enclosing braced expression
3741 // and shows a popup menu
3742 setSelectionStart(start);
3743 setSelectionEnd(end+1);
3744 }
3745 }
3746 }
3747
3748 }
3749
3750 /**
3751 * Called by the GUI when this viewer/editor has to initialise itself
3752 * for a specific object.
3753 *
3754 * @param target the object (be it a {@link gate.Resource},
3755 * {@link gate.DataStore}or whatever) this viewer has to
3756 * display
3757 */
3758 public void setTarget(Object target) {
3759
3760 if(!(target instanceof LuceneDataStoreImpl)
3761 && !(target instanceof Searcher)) {
3762 throw new IllegalArgumentException(
3763 "The GATE LuceneDataStoreSearchGUI can only be used with a GATE LuceneDataStores!\n"
3764 + target.getClass().toString()
3765 + " is not a GATE LuceneDataStore or an object of Searcher!");
3766 }
3767
3768 this.target = target;
3769
3770 // standalone Java application
3771 if(target instanceof LuceneDataStoreImpl) {
3772
3773 ((LuceneDataStoreImpl)target).addDatastoreListener(this);
3774 corpusToSearchIn.setEnabled(true);
3775 searcher = ((LuceneDataStoreImpl)target).getSearcher();
3776
3777 updateSetsTypesAndFeatures();
3778
3779 try {
3780 // get the corpus names from the datastore
3781 java.util.List corpusPIds = ((LuceneDataStoreImpl)target)
3782 .getLrIds(SerialCorpusImpl.class.getName());
3783 if(corpusIds != null) {
3784 for(Object corpusPId : corpusPIds) {
3785 String name = ((LuceneDataStoreImpl)target).getLrName(corpusPId);
3786 this.corpusIds.add(corpusPId);
3787 // add the corpus name to combobox
3788 ((DefaultComboBoxModel)corpusToSearchIn.getModel())
3789 .addElement(name);
3790 }
3791 }
3792 SwingUtilities.invokeLater(new Runnable() {
3793 public void run() {
3794 corpusToSearchIn.updateUI();
3795 corpusToSearchIn.setSelectedItem(Constants.ENTIRE_DATASTORE);
3796 }
3797 });
3798 }
3799 catch(PersistenceException e) {
3800 System.out.println("Couldn't find any available corpusIds.");
3801 throw new GateRuntimeException(e);
3802 }
3803 }
3804 // Java Web Start application
3805 else {
3806 searcher = (Searcher)target;
3807 corpusToSearchIn.setEnabled(false);
3808
3809 // find out all annotation sets that are indexed
3810 try {
3811 annotationSetIDsFromDataStore = searcher
3812 .getIndexedAnnotationSetNames();
3813 allAnnotTypesAndFeaturesFromDatastore = searcher
3814 .getAnnotationTypesMap();
3815
3816 SwingUtilities.invokeLater(new Runnable() { public void run() {
3817 updateAnnotationSetsList();
3818 }});
3819 }
3820 catch(SearchException e) {
3821 throw new GateRuntimeException(e);
3822 }
3823 }
3824
3825 }
3826
3827 /**
3828 * This method is called by datastore when a new resource is adopted
3829 */
3830 public void resourceAdopted(DatastoreEvent de) {
3831 // don't want to do anything here
3832 }
3833
3834 /**
3835 * This method is called by datastore when an existing resource is
3836 * deleted
3837 */
3838 public void resourceDeleted(DatastoreEvent de) {
3839 Resource resource = de.getResource();
3840 if(resource instanceof Corpus) {
3841 // lets check if it is already available in our list
3842 Object id = de.getResourceID();
3843 int index = corpusIds.indexOf(id);
3844 if(index < 0) {
3845 return;
3846 }
3847
3848 // skip the first element in combo box that is "EntireDataStore"
3849 index++;
3850
3851 // now lets remove it from the comboBox as well
3852 ((DefaultComboBoxModel)corpusToSearchIn.getModel())
3853 .removeElementAt(index);
3854 }
3855 updateSetsTypesAndFeatures();
3856 }
3857
3858 /**
3859 * This method is called when a resource is written into the datastore
3860 */
3861 public void resourceWritten(DatastoreEvent de) {
3862 Resource resource = de.getResource();
3863 if(resource instanceof Corpus) {
3864 // lets check if it is already available in our list
3865 Object id = de.getResourceID();
3866 if(!corpusIds.contains(id)) {
3867 // we need to add its name to the combobox
3868 corpusIds.add(id);
3869 ((DefaultComboBoxModel)corpusToSearchIn.getModel())
3870 .addElement(resource.getName());
3871 }
3872 }
3873 updateSetsTypesAndFeatures();
3874 }
3875
3876 protected void updateSetsTypesAndFeatures() {
3877
3878 try {
3879 annotationSetIDsFromDataStore = searcher.getIndexedAnnotationSetNames();
3880 allAnnotTypesAndFeaturesFromDatastore = searcher.getAnnotationTypesMap();
3881 SwingUtilities.invokeLater(new Runnable() { public void run() {
3882 updateAnnotationSetsList();
3883 }});
3884
3885 } catch(SearchException se) {
3886 throw new GateRuntimeException(se);
3887 }
3888 }
3889
3890 /**
3891 * A button with a nice etched border that changes when mouse over,
3892 * select or press it.
3893 */
3894 protected class ButtonBorder extends JButton {
3895 /**
3896 * Create a button.
3897 * @param highlight color of the hightlight
3898 * @param insets margin between content and border
3899 * @param showBorderWhenInactive true if there should always be a border
3900 */
3901 public ButtonBorder(final Color highlight,
3902 final Insets insets,
3903 final boolean showBorderWhenInactive) {
3904 final CompoundBorder borderDarker = new CompoundBorder(
3905 new EtchedBorder(EtchedBorder.LOWERED,
3906 highlight, highlight.darker()),
3907 new EmptyBorder(insets));
3908 final CompoundBorder borderDarkerDarker = new CompoundBorder(
3909 new EtchedBorder(EtchedBorder.LOWERED,
3910 highlight, highlight.darker().darker()),
3911 new EmptyBorder(insets));
3912 setBorder(borderDarker);
3913 setBorderPainted(showBorderWhenInactive);
3914 setContentAreaFilled(false);
3915 setFocusPainted(false);
3916 addMouseListener(new MouseAdapter(){
3917 public void mouseEntered(MouseEvent e) {
3918 JButton button = ((JButton)e.getComponent());
3919 button.setBorder(borderDarkerDarker);
3920 button.setBorderPainted(true);
3921 }
3922 public void mouseExited(MouseEvent e) {
3923 JButton button = ((JButton)e.getComponent());
3924 button.setBorder(borderDarker);
3925 button.setBorderPainted(showBorderWhenInactive);
3926 }
3927 public void mousePressed(MouseEvent e) {
3928 JButton button = ((JButton)e.getComponent());
3929 button.setContentAreaFilled(true);
3930 }
3931 public void mouseReleased(MouseEvent e) {
3932 JButton button = ((JButton)e.getComponent());
3933 button.setContentAreaFilled(false);
3934 }
3935 });
3936 addFocusListener(new FocusAdapter() {
3937 public void focusGained(FocusEvent e) {
3938 JButton button = ((JButton)e.getComponent());
3939 button.setBorder(borderDarkerDarker);
3940 button.setBorderPainted(true);
3941 }
3942 public void focusLost(FocusEvent e) {
3943 JButton button = ((JButton)e.getComponent());
3944 button.setBorder(borderDarker);
3945 button.setBorderPainted(showBorderWhenInactive);
3946 }
3947 });
3948 }
3949 }
3950
3951 }
|