LuceneDataStoreSearchGUI.java
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(200300);
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(3303));
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(0004);
0294     topPanel.add(new JScrollPane(queryTextArea), gbc);
0295     gbc.gridheight = 1;
0296     gbc.weightx = 0;
0297     gbc.weighty = 0;
0298     gbc.insets = new Insets(0000);
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(1110050);
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, 00000false));
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(5000);
0380     topPanel.add(numberOfResultsSlider, gbc);
0381     gbc.insets = new Insets(0000);
0382     topPanel.add(Box.createHorizontalStrut(4), gbc);
0383     JLabel contextWindowLabel = new JLabel("Context size: ");
0384     topPanel.add(contextWindowLabel, gbc);
0385     contextSizeSlider = new JSlider(1505);
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, 00000false));
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(5000);
0420     topPanel.add(contextSizeSlider, gbc);
0421     gbc.insets = new Insets(0000);
0422 
0423     // second column, third row
0424     gbc.gridy = 2;
0425     JPanel panel = new JPanel();
0426     panel.setBorder(new EmptyBorder(new Insets(0000)));
0427     executeQueryAction = new ExecuteQueryAction();
0428     JButton executeQuery =
0429       new ButtonBorder(new Color(240240240)new Insets(0203)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(15030);
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(0000)));
0478     titleResults = new JLabel("Results");
0479     titleResults.setBorder(new EmptyBorder(new Insets(0000)));
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(240240240)new Insets(0003)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() "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() "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 = (Patternresults.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 = (DocumentFactory.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(0000);
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(0000));
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 (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(00);
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() && !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(00);
0972       resultTable.scrollRectToVisible(resultTable.getCellRect(00true));
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(20000);
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(20000);
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 = (Patternresults.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+1instanceof 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 < || 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>"+((StringresultTable.getValueAt(row, col))
1413             .replaceAll("&""&amp;").replaceAll("<""&lt;")
1414             .replaceAll(">""&gt;").replaceAll("\"""&quot;")+"</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 = (JLabelcomponent;
1722         Pattern result = (Patternresults.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(020)+("...");
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 = (JLabelcomponent;
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 >= && !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 = (JLabele.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, 0true));
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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (JLabelcomponent;
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 = (Stringlist.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 = (JLabele.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, 0true));
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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (IntegeroneRowStatisticsTable.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 = (Stringvalue;
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 = (JLabelcomponent;
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 = (Stringvalue;
2677             if (tip.length() 1000) {
2678               tip = tip.substring(01000 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 = (Patternresults.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 (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((StringstackRowsJTable.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 = (Stringvalue;
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 >= && 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 >= && 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(01)));
3275       mousePopup.add(new JMenuItem(new MultiplierAction(02)));
3276       mousePopup.add(new JMenuItem(new MultiplierAction(03)));
3277       mousePopup.add(new JMenuItem(new MultiplierAction(04)));
3278       mousePopup.add(new JMenuItem(new MultiplierAction(05)));
3279       mousePopup.add(new JMenuItem(new MultiplierAction(12)));
3280       mousePopup.add(new JMenuItem(new MultiplierAction(13)));
3281       mousePopup.add(new JMenuItem(new MultiplierAction(14)));
3282       mousePopup.add(new JMenuItem(new MultiplierAction(15)));
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         (intdotRect.getMaxX()// caret X relative position
3474           getLocationOnScreen().y // y location of top-left text field
3475         (intdotRect.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           (intdotRect.getMaxX()// caret relative position
3513             getLocationOnScreen().y // y location of top-left text field
3514           (intdotRect.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 = (StringqueryList.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()-10true));
3620         }
3621       }
3622     }
3623 
3624     private class NextResultAction extends AbstractAction {
3625       public void actionPerformed(ActionEvent ev) {
3626         if (resultTable.getSelectedRow()+< resultTable.getRowCount()) {
3627           resultTable.setRowSelectionInterval(resultTable.getSelectedRow()+1,
3628             resultTable.getSelectedRow()+1);
3629           resultTable.scrollRectToVisible(resultTable.getCellRect(
3630             resultTable.getSelectedRow()+10true));
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 != -&& 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 }