| DocumentEditor |

    1   /*
    2    *  Copyright (c) 1998-2001, The University of Sheffield.
    3    *
    4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
    5    *  software, licenced under the GNU Library General Public License,
    6    *  Version 2, June 1991 (in the distribution as file licence.html,
    7    *  and also available at http://gate.ac.uk/gate/licence.html).
    8    *
    9    *  Valentin Tablan 13/11/2000
    10   *
    11   *  $Id: DocumentEditor.java,v 1.49 2001/12/03 14:50:58 valyt Exp $
    12   *
    13   */
    14  package gate.gui;
    15
    16  import gate.*;
    17  import gate.util.*;
    18  import gate.corpora.TestDocument;
    19  import gate.creole.tokeniser.DefaultTokeniser;
    20  import gate.creole.*;
    21  import gate.event.*;
    22  import gate.swing.*;
    23
    24  import javax.swing.*;
    25  import javax.swing.event.*;
    26  import javax.swing.table.*;
    27  import javax.swing.text.*;
    28  import javax.swing.tree.*;
    29  import javax.swing.border.*;
    30
    31  import java.awt.*;
    32  import java.awt.font.*;
    33  import java.awt.event.*;
    34  import java.awt.image.BufferedImage;
    35  import java.awt.print.*;
    36
    37  import java.beans.*;
    38  import java.util.*;
    39  import java.net.*;
    40  import java.io.*;
    41
    42  /**
    43   * This class implements a viewer/editor for the annotations on a document.
    44   * As a viewer, this visual resource will display all the annotations found on
    45   * the document. The editor needs to have some data about annotation types in
    46   * order to allow the editing of annotations. This data comes from the
    47   * {@link gate.creole.AnnotationSchema} objects that are loaded in the Gate
    48   * system at a given moment. If there are no such objects the editing of
    49   * annotations will be restricted to a very crude method allowing the user to
    50   * add any type of annotations having any features with any String values.
    51   */
    52  public class DocumentEditor extends AbstractVisualResource
    53                              implements ANNIEConstants{
    54    //properties
    55    private transient PropertyChangeSupport propertyChangeListeners =
    56                                            new PropertyChangeSupport(this);
    57    /**
    58     * The {@link gate.Document} currently displayed.
    59     */
    60    private gate.Document document;
    61
    62    /**
    63     * A random colour generator used to generate initial default colours for
    64     * highlighting various types of annotations.
    65     */
    66    protected ColorGenerator colGenerator = new ColorGenerator();
    67
    68    //GUI components
    69    /** The text display.*/
    70    protected JTextPane textPane;
    71
    72    /** Scroller used for the text diaplay*/
    73    protected JScrollPane textScroll;
    74
    75    /** The table placed below the text display used for showing annotations*/
    76    protected XJTable annotationsTable;
    77
    78    /**Model for the annotations table*/
    79    protected AnnotationsTableModel annotationsTableModel;
    80
    81    /** Scroller for the annotations table*/
    82    protected JScrollPane tableScroll;
    83
    84    /*The split that contains the text(top) and the annotations table(bottom)*/
    85    protected JSplitPane leftSplit;
    86
    87    /**
    88     * The split that contains the styles tree and the coreference viewer.
    89     */
    90    protected JSplitPane rightSplit;
    91
    92    /**
    93     * The main horizontal split that contains all the contents of this viewer
    94     */
    95    protected JSplitPane mainSplit;
    96
    97    /**
    98     * The right hand side tree with all  the annotation sets and types of
    99     * annotations
    100    */
    101   protected JTree stylesTree;
    102
    103   /**
    104    * The toolbar displayed on the top part of the component
    105    */
    106   protected JToolBar toolbar;
    107
    108   /**Scroller for the styles tree*/
    109   protected JScrollPane stylesTreeScroll;
    110
    111   /**The root for the styles tree*/
    112   protected DefaultMutableTreeNode stylesTreeRoot;
    113
    114   /**The model for the styles tree*/
    115   protected DefaultTreeModel stylesTreeModel;
    116
    117   /**The dialog used for editing the styles used to highlight annotations*/
    118   protected TextAttributesChooser styleChooser;
    119
    120
    121   /**
    122    * The Jtree that displays the coreference data
    123    */
    124   protected JTree corefTree;
    125   /**
    126    * The root for the coref tree
    127    */
    128   protected DefaultMutableTreeNode corefTreeRoot;
    129
    130   /**
    131    * The model for the coref tree
    132    */
    133   protected DefaultTreeModel corefTreeModel;
    134
    135   /** The scroller for the coref list*/
    136   protected JScrollPane corefScroll;
    137
    138   /**
    139    * A box containing a {@link javax.swing.JProgressBar} used to keep the user
    140    * entertained while the text display is being updated
    141    */
    142   protected Box progressBox;
    143
    144   /**The progress bar used during updating the text*/
    145   protected JProgressBar progressBar;
    146
    147   /**
    148    * The highlighter used to help the user select annotations that overlap
    149    * and for highligting in the text the annotations selected in the lower
    150    * table.
    151    */
    152   protected Highlighter highlighter;
    153
    154   /**
    155    * This highlighter is actually used as a data structure. It is used to keep
    156    * the data for the selected annotations; the actual highlighting will be
    157    * done by the {@link AnnotationEditor#highlighter} as using two different
    158    * highlighters on the same text component is looking for trouble.
    159    */
    160   protected Highlighter selectionHighlighter;
    161
    162   /**
    163    * The object responsible with blinking the selected annotations.
    164    */
    165   protected SelectionBlinker selectionBlinker;
    166
    167
    168   protected Handle myHandle;
    169
    170   /**
    171    * holds the data for the  annotations table: a list of Annotation objects
    172    */
    173   protected java.util.List data;
    174
    175   /**
    176    * a list containing {@link AnnotationEditor.Range} objects. These are the
    177    * ranges in the {@link AnnotationEditor#data} structure. A range is a bunch
    178    * of annotations belonging to the same annotation set that are contiguous
    179    * in the {@link AnnotationEditor#data} structure.
    180    */
    181   protected java.util.List ranges;
    182
    183   /**
    184    * A composed map used to get the metadata for an annotation type starting
    185    * from the annotation set name and the type name.
    186    * Annotation set name -> Annotation type -> {@link AnnotationEditor.TypeData}
    187    * Maps from String to Map to {@link AnnotationEditor.TypeData}.
    188    */
    189   protected Map typeDataMap;
    190
    191   /**
    192    * The listener for the events coming from the document (annotations and
    193    * annotation sets added or removed).
    194    */
    195   protected EventsHandler eventHandler;
    196
    197
    198   /**
    199    * Object used to sychronise all the various threads involved in GUI
    200    * updating;
    201    */
    202   protected Object lock;
    203
    204   /**Should the table be visible*/
    205
    206   /**Should the text be visible*/
    207
    208   /**
    209    * Should the right hand side tree be visible. That tree is used to select
    210    * what types of annotations are visible in the text display, hence the name
    211    * filters.
    212    */
    213
    214   /**Should this component bahave as an editor as well as an viewer*/
    215   private boolean editable = true;
    216
    217
    218
    219   private JToggleButton textVisibleBtn;
    220   private JToggleButton typesTreeVisibleBtn;
    221   private JToggleButton annotationsTableVisibleBtn;
    222   private JToggleButton coreferenceVisibleBtn;
    223   private boolean annotationsTableVisible = false;
    224   private boolean coreferenceVisible = false;
    225   private boolean textVisible = true;
    226   private boolean typesTreeVisible = false;
    227   private boolean corefOptionAvailable = false;
    228
    229   /**
    230    * Default constructor. Creats all the components and initialises all the
    231    * internal data to default values where possible.
    232    */
    233   public DocumentEditor() {
    234   }
    235
    236   public Resource init(){
    237     initLocalData();
    238     initGuiComponents();
    239     initListeners();
    240     return this;
    241   }
    242
    243   /** Test code*/
    244   public static void main(String[] args) {
    245     try{
    246       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    247       Gate.setLocalWebServer(false);
    248       Gate.setNetConnected(false);
    249       Gate.init();
    250       JFrame frame = new JFrame("Gate Document Editor Test");
    251       frame.addWindowListener(new WindowAdapter(){
    252         public void windowClosing(WindowEvent e){
    253           System.exit(0);
    254         }
    255       });
    256
    257       //get a document
    258       FeatureMap params = Factory.newFeatureMap();
    259       params.put("markupAware", new Boolean(true));
    260
    261       params.put("sourceUrl",
    262                  "file:///d:/tmp/help-doc.html");
    263                  //"file:///d:/tmp/F7V.xml");
    264                  //"http://redmires.dcs.shef.ac.uk/admin/index.html");
    265                  //"http://redmires.dcs.shef.ac.uk/java1.3docs/api/javax/
    266                  //                                       swing/Action.html");
    267                  //"http://redmires.dcs.shef.ac.uk/java1.3docs/api/java/awt
    268                  ///AWTEventMulticaster.html");
    269       gate.Document doc = (gate.Document)Factory.createResource(
    270                                           "gate.corpora.DocumentImpl", params);
    271       //create a default tokeniser
    272      params.clear();
    273      params.put("rulesResourceName", "creole/tokeniser/DefaultTokeniser.rules");
    274      DefaultTokeniser tokeniser = (DefaultTokeniser) Factory.createResource(
    275                             "gate.creole.tokeniser.DefaultTokeniser", params);
    276
    277       tokeniser.setDocument(doc);
    278       tokeniser.setAnnotationSetName("TokeniserAS");
    279       tokeniser.execute();
    280
    281       DocumentEditor editor = (DocumentEditor)Factory.createResource(
    282                             "gate.gui.DocumentEditor", Factory.newFeatureMap());
    283       frame.getContentPane().add(editor);
    284       frame.pack();
    285       frame.setVisible(true);
    286       editor.setTarget(doc);
    287
    288       //get the annotation schemas
    289       params =  Factory.newFeatureMap();
    290       params.put("xmlFileUrl", DocumentEditor.class.getResource(
    291                               "/gate/resources/creole/schema/PosSchema.xml"));
    292
    293       AnnotationSchema annotSchema = (AnnotationSchema)
    294          Factory.createResource("gate.creole.AnnotationSchema", params);
    295       Set annotationSchemas = new HashSet();
    296       annotationSchemas.add(annotSchema);
    297
    298     }catch(Exception e){
    299       e.printStackTrace(Err.getPrintWriter());
    300     }
    301   }//public static void main(String[] args)
    302
    303
    304   /**
    305    * Initialises all the listeners that this component has to register with
    306    * other classes.
    307    */
    308   protected void initListeners() {
    309     //listen for our own properties change events
    310     this.addPropertyChangeListener(new PropertyChangeListener() {
    311       public void propertyChange(PropertyChangeEvent e) {
    312         if(e.getPropertyName().equals("annotationsTableVisible") ||
    313            e.getPropertyName().equals("coreferenceVisible") ||
    314            e.getPropertyName().equals("textVisible") ||
    315            e.getPropertyName().equals("typesTreeVisible")){
    316           layoutComponents();
    317         }else if(e.getPropertyName().equals("corefOptionAvailable")){
    318           if(((Boolean)e.getNewValue()).booleanValue()){
    319             if(toolbar.getComponentIndex(coreferenceVisibleBtn) == -1)
    320               toolbar.add(coreferenceVisibleBtn,
    321                           toolbar.getComponentCount() - 1);
    322           }else{
    323             toolbar.remove(coreferenceVisibleBtn);
    324           }
    325           layoutComponents();
    326         }
    327       }
    328     });
    329
    330     textVisibleBtn.addActionListener(new ActionListener() {
    331       public void actionPerformed(ActionEvent e) {
    332         setTextVisible(textVisibleBtn.isSelected());
    333       }
    334     });
    335
    336     annotationsTableVisibleBtn.addActionListener(new ActionListener() {
    337       public void actionPerformed(ActionEvent e) {
    338         setAnnotationsTableVisible(annotationsTableVisibleBtn.isSelected());
    339       }
    340     });
    341
    342
    343     typesTreeVisibleBtn.addActionListener(new ActionListener() {
    344       public void actionPerformed(ActionEvent e) {
    345         setTypesTreeVisible(typesTreeVisibleBtn.isSelected());
    346       }
    347     });
    348
    349
    350     coreferenceVisibleBtn.addActionListener(new ActionListener() {
    351       public void actionPerformed(ActionEvent e) {
    352         setCoreferenceVisible(coreferenceVisibleBtn.isSelected());
    353       }
    354     });
    355
    356     stylesTree.addMouseListener(new MouseAdapter() {
    357       public void mouseClicked(MouseEvent e) {
    358         if(SwingUtilities.isLeftMouseButton(e)){
    359           //where inside the tree?
    360           int x = e.getX();
    361           int y = e.getY();
    362           TreePath path = stylesTree.getPathForLocation(x, y);
    363           if(path != null){
    364             DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.
    365                                          getLastPathComponent();
    366             TypeData nData = (TypeData)node.getUserObject();
    367             //where inside the cell?
    368             Rectangle cellRect = stylesTree.getPathBounds(path);
    369             x -= cellRect.x;
    370             y -= cellRect.y;
    371             Component cellComp = stylesTree.getCellRenderer().
    372                                  getTreeCellRendererComponent(stylesTree,
    373                                                               node, true,
    374                                                               false, false,
    375                                                               0, true);
    376 //            cellComp.setSize(cellRect.width, cellRect.height);
    377             cellComp.setBounds(cellRect);
    378             Component clickedComp = cellComp.getComponentAt(x, y);
    379
    380             if(clickedComp instanceof JCheckBox){
    381               nData.setVisible(! nData.getVisible());
    382 //              stylesTree.repaint(cellRect);
    383               stylesTreeModel.nodeChanged(node);
    384             // Check if the click indicates a shortcut to create an annotation
    385             }else if( e.getClickCount() == 1 &&
    386                       clickedComp instanceof JLabel &&
    387                       isTextSelected()){
    388               // Here creates an annotation with the selected text into the
    389               // target annotation set
    390
    391               if(!editable) return;
    392               Long startOffset = new Long(textPane.getSelectionStart());
    393               Long endOffset = new Long(textPane.getSelectionEnd());
    394               TreePath treePath = stylesTree.getSelectionPath();
    395               TypeData typeData = (TypeData)((DefaultMutableTreeNode)
    396                               treePath.getLastPathComponent()).getUserObject();
    397               String setName = typeData.getSet();
    398               if(typeData.getType() == null){
    399                 // The set is empty. It will not create an annotation.
    400                 // Loose the selection and return
    401                 textPane.setSelectionStart(startOffset.intValue());
    402                 textPane.setSelectionEnd(startOffset.intValue());
    403                 return;
    404               }// End if
    405               try{
    406                 if ("Default".equals(setName)){
    407                   document.getAnnotations().add(startOffset,
    408                                                 endOffset,
    409                                                 typeData.getType(),
    410                                                 Factory.newFeatureMap());
    411                 }else{
    412                   document.getAnnotations(setName).add( startOffset,
    413                                                         endOffset,
    414                                                         typeData.getType(),
    415                                                        Factory.newFeatureMap());
    416                 }// End if
    417               } catch(gate.util.InvalidOffsetException ioe){
    418                 throw new GateRuntimeException(ioe.getMessage());
    419               }// End try
    420               // Loose the selection
    421               textPane.setSelectionStart(startOffset.intValue());
    422               textPane.setSelectionEnd(startOffset.intValue());
    423             }else if(clickedComp instanceof JLabel &&
    424                      e.getClickCount() == 2){
    425               if(styleChooser == null){
    426                 Window parent = SwingUtilities.getWindowAncestor(
    427                                   DocumentEditor.this);
    428                 styleChooser = parent instanceof Frame ?
    429                                new TextAttributesChooser((Frame)parent,
    430                                                          "Please select your options",
    431                                                          true) :
    432                                new TextAttributesChooser((Dialog)parent,
    433                                                          "Please select your options",
    434                                                          true);
    435
    436               }
    437
    438               styleChooser.setLocationRelativeTo(stylesTree);
    439               nData.setAttributes(
    440                     styleChooser.show(nData.getAttributes().copyAttributes()));
    441               stylesTreeModel.nodeChanged(node);
    442 //              stylesTree.repaint(cellRect);
    443             }
    444           }
    445         }
    446       }
    447     });
    448
    449     stylesTree.addComponentListener(new ComponentAdapter() {
    450       public void componentHidden(ComponentEvent e) {
    451
    452       }
    453
    454       public void componentMoved(ComponentEvent e) {
    455       }
    456
    457       public void componentResized(ComponentEvent e) {
    458         SwingUtilities.invokeLater(new Runnable(){
    459           public void run(){
    460             Enumeration nodes = stylesTreeRoot.depthFirstEnumeration();
    461             while(nodes.hasMoreElements()){
    462               stylesTreeModel.nodeChanged((TreeNode)nodes.nextElement());
    463             }
    464           }
    465         });
    466       }
    467
    468       public void componentShown(ComponentEvent e) {
    469       }
    470     });
    471
    472     //clear selection in table on outside clicks
    473     tableScroll.addMouseListener(new MouseAdapter() {
    474       public void mouseClicked(MouseEvent e) {
    475         Point location = e.getPoint();
    476         if(!tableScroll.getViewport().getView().getBounds().contains(location)){
    477           //deselect everything in the table
    478           annotationsTable.clearSelection();
    479         }
    480       }
    481     });
    482
    483     annotationsTable.addMouseListener(new MouseAdapter() {
    484       public void mouseClicked(MouseEvent e) {
    485         int row = annotationsTable.rowAtPoint(e.getPoint());
    486         Annotation ann = (Annotation)annotationsTable.getModel().
    487                                                       getValueAt(row, -1);
    488         //find the annotation set
    489         String setName = (String)annotationsTable.getModel().
    490                                                     getValueAt(row, 1);
    491         AnnotationSet set = setName.equals("Default")?
    492                             document.getAnnotations() :
    493                             document.getAnnotations(setName);
    494
    495         EditAnnotationAction editAnnAct = new EditAnnotationAction(set, ann);
    496         if(SwingUtilities.isLeftMouseButton(e)){
    497           if(e.getClickCount() == 1){
    498           }else if(e.getClickCount() == 2){
    499             //double left click -> edit the annotation
    500             if(editable) editAnnAct.actionPerformed(null);
    501           }
    502         } else if(SwingUtilities.isRightMouseButton(e)) {
    503           //right click
    504           //add select all option
    505           JPopupMenu popup = new JPopupMenu();
    506           popup.add(new AbstractAction(){
    507             {
    508               putValue(NAME, "Select all");
    509             }
    510             public void actionPerformed(ActionEvent evt){
    511               annotationsTable.selectAll();
    512             }
    513           });
    514
    515           popup.addSeparator();
    516           //add save as XML and preserve format
    517           popup.add(new DumpAsXmlAction());
    518           if(editable){
    519             //add delete option
    520             popup.addSeparator();
    521             popup.add(new DeleteSelectedAnnotationsAction(annotationsTable));
    522             popup.addSeparator();
    523             popup.add(new XJMenuItem(editAnnAct, myHandle));
    524           }
    525           popup.show(annotationsTable, e.getX(), e.getY());
    526         }
    527       }
    528     });//annotationsTable.addMouseListener(new MouseAdapter()
    529
    530
    531
    532     annotationsTable.getInputMap().put(
    533                   KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
    534                   "Delete");
    535     annotationsTable.getActionMap().put(
    536                         "Delete",
    537                         new DeleteSelectedAnnotationsAction(annotationsTable));
    538
    539     stylesTree.getInputMap().put(
    540                   KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
    541                   "Delete");
    542     stylesTree.getActionMap().put(
    543                         "Delete",
    544                         new DeleteSelectedAnnotationsAction(stylesTree));
    545
    546     //takes care of highliting the selected annotations
    547     annotationsTable.getSelectionModel().addListSelectionListener(
    548       new ListSelectionListener(){
    549         public void valueChanged(ListSelectionEvent e){
    550           int[] rows = annotationsTable.getSelectedRows();
    551           synchronized(selectionHighlighter){
    552             selectionHighlighter.removeAllHighlights();
    553           }
    554           for(int i = 0; i < rows.length; i++){
    555             int start = ((Long)annotationsTable.getModel().
    556                          getValueAt(rows[i], 2)
    557                         ).intValue();
    558             int end = ((Long)annotationsTable.getModel().
    559                        getValueAt(rows[i], 3)
    560                       ).intValue();
    561             //bring the annotation in view
    562             try{
    563               Rectangle startRect = textPane.modelToView(start);
    564               Rectangle endRect = textPane.modelToView(end);
    565               SwingUtilities.computeUnion(endRect.x, endRect.y,
    566                                           endRect.width, endRect.height,
    567                                           startRect);
    568               textPane.scrollRectToVisible(startRect);
    569               annotationsTable.requestFocus();
    570             }catch(BadLocationException ble){
    571               throw new GateRuntimeException(ble.toString());
    572             }
    573             //start blinking the annotation
    574             try{
    575               synchronized (selectionHighlighter){
    576                 selectionHighlighter.addHighlight(start, end,
    577                             DefaultHighlighter.DefaultPainter);
    578               }
    579             }catch(BadLocationException ble){
    580               throw new GateRuntimeException(ble.toString());
    581             }
    582           }//for(int i = 0; i < rows.length; i++)
    583           //start the blinker
    584           selectionBlinker.testAndStart();
    585         }
    586       });
    587
    588
    589     textPane.addMouseListener(new MouseAdapter() {
    590       public void mouseClicked(MouseEvent e) {
    591         if(SwingUtilities.isRightMouseButton(e)){
    592           int position = textPane.viewToModel(e.getPoint());
    593           if(textPane.getSelectionStart() ==  textPane.getSelectionEnd()){
    594             //no selection -> select an annotation
    595             JPopupMenu popup = new JPopupMenu("Select:");
    596             //find annotations at this position
    597             Iterator annIter = document.getAnnotations().
    598                                         get(new Long(position),
    599                                             new Long(position)
    600                                         ).iterator();
    601             if(annIter.hasNext()){
    602               JMenu menu = new JMenu("Default");
    603               popup.add(menu);
    604               while(annIter.hasNext()){
    605                 Annotation ann = (Annotation)annIter.next();
    606                 JMenuItem item = new SelectAnnotationPopupItem(ann,
    607                                                                   "Default");
    608                 menu.add(item);
    609               }
    610             }
    611             Map namedASs = document.getNamedAnnotationSets();
    612             if(namedASs != null){
    613               Iterator namedASiter = namedASs.values().iterator();
    614               while(namedASiter.hasNext()){
    615                 //find annotations at this position
    616                 AnnotationSet set = (AnnotationSet)namedASiter.next();
    617                 annIter = set.get(new Long(position), new Long(position)).
    618                               iterator();
    619                 if(annIter.hasNext()){
    620                   JMenu menu = new JMenu(set.getName());
    621                   popup.add(menu);
    622                   while(annIter.hasNext()){
    623                     Annotation ann = (Annotation)annIter.next();
    624                     JMenuItem item = new SelectAnnotationPopupItem(ann,
    625                                                                 set.getName());
    626                     menu.add(item);
    627                   }
    628                 }
    629               }
    630             }
    631             popup.show(textPane, e.getPoint().x, e.getPoint().y);
    632           } else {
    633             //there is selected text -> create a new annotation
    634             if(!editable) return;
    635             Long startOffset = new Long(textPane.getSelectionStart());
    636             Long endOffset = new Long(textPane.getSelectionEnd());
    637             JPopupMenu popup = new JPopupMenu();
    638             //add new annotation in the Default AS
    639             JMenu menu = new JMenu("Add annotation to \"Default\"");
    640             menu.add(new XJMenuItem(
    641                          new NewAnnotationAction(document.getAnnotations(),
    642                                                  startOffset, endOffset),
    643                          myHandle));
    644             java.util.List customisedAnnTypes = Gate.getCreoleRegister().
    645                                                 getVREnabledAnnotationTypes();
    646             if(!customisedAnnTypes.isEmpty()){
    647               menu.addSeparator();
    648               Iterator typesIter = customisedAnnTypes.iterator();
    649               while(typesIter.hasNext()){
    650                 menu.add(new XJMenuItem(
    651                              new NewAnnotationAction(document.getAnnotations(),
    652                                                      (String)typesIter.next(),
    653                                                      startOffset, endOffset),
    654                              myHandle));
    655               }
    656             }//if(!customisedAnnTypes.isEmpty())
    657             popup.add(menu);
    658
    659             //add a new annotation to a named AnnotationSet
    660             if(document.getNamedAnnotationSets() != null){
    661               Iterator annSetsIter = document.getNamedAnnotationSets().
    662                                               keySet().iterator();
    663               if(annSetsIter.hasNext()) popup.addSeparator();
    664               while(annSetsIter.hasNext()){
    665                 AnnotationSet set = document.getAnnotations(
    666                                              (String)annSetsIter.next());
    667
    668
    669                 menu = new JMenu("Add annotation to \"" + set.getName() + "\"");
    670                 menu.add(new XJMenuItem(
    671                              new NewAnnotationAction(set, startOffset, endOffset),
    672                              myHandle));
    673                 if(!customisedAnnTypes.isEmpty()){
    674                   menu.addSeparator();
    675                   Iterator typesIter = customisedAnnTypes.iterator();
    676                   while(typesIter.hasNext()){
    677                     menu.add(new XJMenuItem(
    678                                  new NewAnnotationAction(set,
    679                                                          (String)typesIter.next(),
    680                                                          startOffset, endOffset),
    681                                  myHandle));
    682                   }
    683                 }//if(!customisedAnnTypes.isEmpty())
    684                 popup.add(menu);
    685               }//while(annSetsIter.hasNext())
    686             }
    687
    688             //add to a new annotation set
    689             menu = new JMenu("Add annotation to a new set");
    690             menu.add(new XJMenuItem(
    691                          new NewAnnotationAction(null, startOffset, endOffset),
    692                          myHandle));
    693             if(!customisedAnnTypes.isEmpty()){
    694               menu.addSeparator();
    695               Iterator typesIter = customisedAnnTypes.iterator();
    696               while(typesIter.hasNext()){
    697                 menu.add(new XJMenuItem(
    698                              new NewAnnotationAction(null,
    699                                                      (String)typesIter.next(),
    700                                                      startOffset, endOffset),
    701                              myHandle));
    702               }
    703             }//if(!customisedAnnTypes.isEmpty())
    704             popup.add(menu);
    705             //show the popup
    706             popup.show(textPane, e.getPoint().x, e.getPoint().y);
    707           }//there is selected text
    708         }//if(SwingUtilities.isRightMouseButton(e))
    709       }//mouse clicked
    710
    711       public void mousePressed(MouseEvent e) {
    712       }
    713
    714       public void mouseReleased(MouseEvent e) {
    715       }
    716
    717       public void mouseEntered(MouseEvent e) {
    718       }
    719
    720       public void mouseExited(MouseEvent e) {
    721       }
    722     });
    723
    724     //when the highlighter changes we need to get a hold of the new one
    725     textPane.addPropertyChangeListener(new PropertyChangeListener() {
    726       public void propertyChange(PropertyChangeEvent e) {
    727         if(e.getPropertyName().equals("highlighter")){
    728           highlighter = textPane.getHighlighter();
    729           selectionHighlighter.install(textPane);
    730         }
    731       }
    732     });
    733
    734     corefTree.addMouseListener(new MouseAdapter() {
    735       public void mouseClicked(MouseEvent e) {
    736         if(SwingUtilities.isLeftMouseButton(e)){
    737           //where inside the tree?
    738           int x = e.getX();
    739           int y = e.getY();
    740           TreePath path = corefTree.getPathForLocation(x, y);
    741           if(path != null){
    742             DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.
    743                                          getLastPathComponent();
    744             if(node.getUserObject() instanceof CorefData){
    745               CorefData cData = (CorefData)node.getUserObject();
    746               cData.setVisible(!cData.getVisible());
    747               corefTreeModel.nodeChanged(node);
    748             }
    749           }
    750         }
    751       }
    752
    753       public void mousePressed(MouseEvent e) {
    754       }
    755
    756       public void mouseReleased(MouseEvent e) {
    757       }
    758
    759       public void mouseEntered(MouseEvent e) {
    760       }
    761
    762       public void mouseExited(MouseEvent e) {
    763       }
    764     });
    765
    766
    767
    768     corefTree.addComponentListener(new ComponentAdapter() {
    769       public void componentHidden(ComponentEvent e) {
    770
    771       }
    772
    773       public void componentMoved(ComponentEvent e) {
    774       }
    775
    776       public void componentResized(ComponentEvent e) {
    777         SwingUtilities.invokeLater(new Runnable(){
    778           public void run(){
    779             Enumeration nodes = corefTreeRoot.depthFirstEnumeration();
    780             while(nodes.hasMoreElements()){
    781               corefTreeModel.nodeChanged((TreeNode)nodes.nextElement());
    782             }
    783           }
    784         });
    785       }
    786
    787       public void componentShown(ComponentEvent e) {
    788       }
    789     });
    790   }//protected void initListeners()
    791
    792   /**
    793    * Initialises the local variables to their default values
    794    */
    795   protected void initLocalData(){
    796     //init local vars
    797     lock = new Object();
    798
    799     data = Collections.synchronizedList(new ArrayList());
    800     //dataAsAS = new gate.annotation.AnnotationSetImpl(document);
    801     ranges = new ArrayList();
    802
    803     typeDataMap = new HashMap();
    804
    805     eventHandler = new EventsHandler();
    806
    807   }//protected void initLocalData()
    808
    809   /**Builds all the graphical components*/
    810   protected void initGuiComponents(){
    811     //initialise GUI components
    812     this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    813
    814     //the toolbar
    815     toolbar = new JToolBar(JToolBar.HORIZONTAL);
    816     toolbar.setAlignmentX(Component.LEFT_ALIGNMENT);
    817     toolbar.setAlignmentY(Component.TOP_ALIGNMENT);
    818     toolbar.setFloatable(false);
    819     this.add(toolbar);
    820
    821     textVisibleBtn = new JToggleButton("Text", textVisible);
    822     toolbar.add(textVisibleBtn);
    823
    824     annotationsTableVisibleBtn = new JToggleButton("Annotations",
    825                                                    annotationsTableVisible);
    826     toolbar.add(annotationsTableVisibleBtn);
    827
    828     typesTreeVisibleBtn = new JToggleButton("Annotation Sets", typesTreeVisible);
    829     toolbar.add(typesTreeVisibleBtn);
    830
    831
    832     coreferenceVisibleBtn = new JToggleButton("Coreference", coreferenceVisible);
    833     if(isCorefOptionAvailable()) toolbar.add(coreferenceVisibleBtn);
    834
    835
    836     //printing
    837 //    toolbar.add(Box.createHorizontalStrut(20));
    838 //    toolbar.add(new PrintAction());
    839
    840
    841
    842     toolbar.add(Box.createHorizontalGlue());
    843
    844
    845     //The text
    846     textPane = new XJTextPane();
    847     textPane.setEditable(false);
    848     textPane.setEnabled(true);
    849     textPane.setEditorKit(new CustomStyledEditorKit());
    850     Style defaultStyle = textPane.getStyle("default");
    851     StyleConstants.setBackground(defaultStyle, Color.white);
    852     StyleConstants.setFontFamily(defaultStyle, "Arial Unicode MS");
    853     textScroll = new JScrollPane(textPane);
    854     textScroll.setAlignmentY(Component.TOP_ALIGNMENT);
    855     textScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
    856
    857
    858     //The table
    859     annotationsTableModel = new AnnotationsTableModel();
    860     annotationsTable = new XJTable(annotationsTableModel);
    861     annotationsTable.setIntercellSpacing(new Dimension(10, 5));
    862
    863     tableScroll = new JScrollPane(annotationsTable);
    864     tableScroll.setOpaque(true);
    865     tableScroll.setAlignmentY(Component.TOP_ALIGNMENT);
    866     tableScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
    867
    868
    869     //RIGHT SIDE - the big tree
    870     stylesTreeRoot = new DefaultMutableTreeNode(null, true);
    871     stylesTreeModel = new DefaultTreeModel(stylesTreeRoot, true);
    872     stylesTree = new JTree(stylesTreeModel){
    873       public void updateUI(){
    874         super.updateUI();
    875         setRowHeight(0);
    876       }
    877     };
    878
    879     stylesTree.setRootVisible(false);
    880     stylesTree.setCellRenderer(new NodeRenderer());
    881     //TIP: setting rowHeight to 0 tells the tree to query its renderer for each
    882     //row's size
    883     stylesTree.setRowHeight(0);
    884     stylesTree.setShowsRootHandles(true);
    885     stylesTree.setToggleClickCount(0);
    886     stylesTreeScroll = new JScrollPane(stylesTree);
    887     stylesTreeScroll.setAlignmentY(Component.TOP_ALIGNMENT);
    888     stylesTreeScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
    889
    890
    891     //coreference
    892     corefTreeRoot = new DefaultMutableTreeNode("Coreference data", true);
    893     corefTree = new JTree(corefTreeModel = new DefaultTreeModel(corefTreeRoot,
    894                                                                 true));
    895     corefTree.setCellRenderer(new CorefNodeRenderer());
    896     corefTree.setRowHeight(0);
    897     corefTree.setRootVisible(true);
    898     corefTree.setShowsRootHandles(false);
    899     corefScroll = new JScrollPane(corefTree);
    900     corefScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
    901     corefScroll.setAlignmentY(Component.TOP_ALIGNMENT);
    902     updateCorefTree();
    903
    904     //various containers
    905     leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false);
    906     leftSplit.setOneTouchExpandable(true);
    907     leftSplit.setOpaque(true);
    908     leftSplit.setAlignmentY(Component.TOP_ALIGNMENT);
    909     leftSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
    910     leftSplit.setResizeWeight((double)0.75);
    911
    912     rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false);
    913     rightSplit.setOneTouchExpandable(true);
    914     rightSplit.setOpaque(true);
    915     rightSplit.setAlignmentY(Component.TOP_ALIGNMENT);
    916     rightSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
    917     rightSplit.setResizeWeight((double)0.75);
    918
    919
    920     mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false);
    921     mainSplit.setOneTouchExpandable(true);
    922     mainSplit.setOpaque(true);
    923     mainSplit.setAlignmentY(Component.TOP_ALIGNMENT);
    924     mainSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
    925     mainSplit.setResizeWeight((double)0.75);
    926
    927     //put everything together
    928     layoutComponents();
    929
    930     //Extra Stuff
    931
    932     progressBox = new Box(BoxLayout.X_AXIS);
    933     progressBox.add(Box.createHorizontalStrut(5));
    934     progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
    935     progressBox.add(progressBar);
    936     progressBox.add(Box.createHorizontalStrut(5));
    937
    938     highlighter = textPane.getHighlighter();
    939     if(highlighter instanceof javax.swing.text.DefaultHighlighter){
    940       ((javax.swing.text.DefaultHighlighter)highlighter).
    941       setDrawsLayeredHighlights(true);
    942     }
    943
    944     selectionHighlighter = new DefaultHighlighter();
    945     selectionHighlighter.install(textPane);
    946     selectionBlinker = new SelectionBlinker();
    947
    948   }//protected void initGuiComponents()
    949
    950
    951   /** This method returns true if a text is selected in the textPane*/
    952   private boolean isTextSelected(){
    953     return !(textPane.getSelectionStart()==textPane.getSelectionEnd());
    954   }// isTextSelected()
    955   /**
    956    * Gets all the {@link gate.creole.AnnotationSchema} objects currently
    957    * loaded in the system.
    958    */
    959   protected Set getAnnotationSchemas(){
    960     Set result = new HashSet();
    961     ResourceData rData = (ResourceData)Gate.getCreoleRegister().
    962                                             get("gate.creole.AnnotationSchema");
    963     if(rData != null){
    964       result.addAll(rData.getInstantiations());
    965     }
    966     return result;
    967   }//protected Set getAnnotationSchemas()
    968
    969   public synchronized void removePropertyChangeListener(
    970                                                     PropertyChangeListener l) {
    971     super.removePropertyChangeListener(l);
    972     propertyChangeListeners.removePropertyChangeListener(l);
    973   }
    974
    975   public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
    976     super.addPropertyChangeListener(l);
    977     propertyChangeListeners.addPropertyChangeListener(l);
    978   }
    979
    980   public synchronized void addPropertyChangeListener(String propertyName,
    981                                                      PropertyChangeListener l) {
    982     super.addPropertyChangeListener(propertyName, l);
    983     propertyChangeListeners.addPropertyChangeListener(propertyName, l);
    984   }
    985
    986   /**
    987    * Sets the document to be displayed
    988    */
    989   public void setTarget(Object target){
    990     if(!(target instanceof gate.Document)){
    991       throw new IllegalArgumentException(
    992         "The document editor can only display Gate documents!\n" +
    993         "The provided resource is not a document but a: " +
    994         target.getClass().toString() + "!");
    995     }
    996     gate.Document  oldDocument = document;
    997     document = (gate.Document)target;
    998     //this needs to be executed even if the new document equals(oldDocument)
    999     //in order to update the pointers
    1000    if(oldDocument != document) this_documentChanged();
    1001
    1002    propertyChangeListeners.firePropertyChange("document", oldDocument,
    1003                                               target);
    1004  }//public void setTarget(Object target)
    1005
    1006  public void setHandle(Handle handle){
    1007    myHandle = handle;
    1008  }
    1009
    1010  public void cleanup(){
    1011    document = null;
    1012    stylesTreeRoot.removeAllChildren();
    1013    data.clear();
    1014    ranges.clear();
    1015  }
    1016
    1017
    1018  /**
    1019   * Updates this component when the underlying document is changed. This method
    1020   * is only triggered when the document is changed to a new one and not when
    1021   * the internal data from the document changes. For the document internal
    1022   * events {@see #DelayedListener}.
    1023   */
    1024  protected void this_documentChanged(){
    1025    initLocalData();
    1026    annotationsTableModel.fireTableDataChanged();
    1027    document.getFeatures().addFeatureMapListener(new FeatureMapListener(){
    1028      public void featureMapUpdated(){
    1029          updateCorefTree();
    1030      }
    1031    });
    1032    updateCorefTree();
    1033
    1034    Enumeration enum = stylesTreeRoot.children();
    1035    while(enum.hasMoreElements()){
    1036      stylesTreeModel.removeNodeFromParent((DefaultMutableTreeNode)
    1037                                           enum.nextElement());
    1038    }
    1039    if(document == null) return;
    1040    textPane.setText(document.getContent().toString());
    1041
    1042    //add the default annotation set
    1043    eventHandler.annotationSetAdded(new gate.event.DocumentEvent(
    1044                  document,
    1045                  gate.event.DocumentEvent.ANNOTATION_SET_ADDED, null));
    1046
    1047    //register the for this new document's events
    1048    document.addDocumentListener(eventHandler);
    1049
    1050    //add all the other annotation sets
    1051    Map namedASs = document.getNamedAnnotationSets();
    1052    if(namedASs != null){
    1053      Iterator setsIter = namedASs.values().iterator();
    1054      while(setsIter.hasNext()){
    1055        AnnotationSet currentAS = (AnnotationSet)setsIter.next();
    1056        if(currentAS != null){
    1057          eventHandler.annotationSetAdded(new gate.event.DocumentEvent(
    1058                        document,
    1059                        gate.event.DocumentEvent.ANNOTATION_SET_ADDED,
    1060                        currentAS.getName()));
    1061        }
    1062      }
    1063    }
    1064  }//protected void this_documentChanged()
    1065
    1066  /**
    1067   * Gets the data related to a given annotation type.
    1068   * An annotation type is uniquely identified by the name of its AnnotationSet
    1069   * and the name of the type.
    1070   * For the default annotation set of a document (which has no name) the
    1071   * &quot;&lt;Default&gt;&quot; value is used.
    1072   *
    1073   * Once a {@link AnnotationEditor.TypeData} value has been obtained it can be used to change
    1074   * the way the respective type of annotations are displayed.
    1075   * @param setName a {@link java.lang.String}, the name of the annotation set
    1076   * @param type a {@link java.lang.String}, the name of the type.
    1077   * @return a {@link AnnotationEditor.TypeData} value
    1078   */
    1079  protected TypeData getTypeData(String setName, String type){
    1080    Map setMap = (Map)typeDataMap.get(setName);
    1081    if(setMap != null) return (TypeData)setMap.get(type);
    1082    else return null;
    1083  }// protected TypeData getTypeData(String setName, String type)
    1084
    1085
    1086  /**
    1087   * Repaints the highlighting for annotation types in the text display.
    1088   */
    1089  protected void showHighlights(Set annotations, AttributeSet style) {
    1090    //store the state of the text display
    1091    int selStart = textPane.getSelectionStart();
    1092    int selEnd = textPane.getSelectionEnd();
    1093    final int position = textPane.viewToModel(
    1094                            textScroll.getViewport().getViewPosition());
    1095    //hide the text
    1096    SwingUtilities.invokeLater(new Runnable() {
    1097      public void run() {
    1098        progressBar.setValue(0);
    1099        //progressBar.setMaximumSize(new Dimension(textScroll.getWidth(),20));
    1100        textScroll.getViewport().setView(progressBox);
    1101        textScroll.paintImmediately(textScroll.getBounds());
    1102      }
    1103    });
    1104
    1105    //highlight the annotations
    1106    int size = annotations.size();
    1107    int i = 0;
    1108    int lastValue = 0;
    1109    int value;
    1110    Iterator annIter = annotations.iterator();
    1111    while(annIter.hasNext()){
    1112      Annotation ann = (Annotation)annIter.next();
    1113      textPane.select(ann.getStartNode().getOffset().intValue(),
    1114                      ann.getEndNode().getOffset().intValue());
    1115      textPane.setCharacterAttributes(style, true);
    1116      value = i * 100 / size;
    1117      if(value - lastValue >= 5){
    1118        progressBar.setValue(value);
    1119        progressBar.paintImmediately(progressBar.getBounds());
    1120        lastValue = value;
    1121      }
    1122      i++;
    1123    }
    1124    //restore the state
    1125    textPane.select(selStart, selEnd);
    1126    SwingUtilities.invokeLater(new Runnable(){
    1127      public void run(){
    1128        //show the text
    1129        textScroll.getViewport().setView(textPane);
    1130        try{
    1131          textScroll.getViewport().setViewPosition(
    1132                                  textPane.modelToView(position).getLocation());
    1133          textScroll.paintImmediately(textScroll.getBounds());
    1134        }catch(BadLocationException ble){
    1135        }
    1136      }
    1137    });
    1138  }//protected void showHighlights()
    1139
    1140  /**
    1141   * Updates the GUI when the user has selected an annotation e.g. by using the
    1142   * right click popup. That basically means make the appropiate type of
    1143   * annotations visible in case it isn't already.
    1144   */
    1145  protected void selectAnnotation(String set, Annotation ann) {
    1146    TypeData tData = getTypeData(set, ann.getType());
    1147    if(!tData.getVisible()){
    1148      tData.setVisible(true);
    1149      //sleep a while so the gui updater thread has time to start
    1150      try{
    1151        Thread.sleep(100);
    1152      }catch(InterruptedException ie){}
    1153      //refresh the display for the type
    1154      //(the checkbox has to be shown selected)
    1155      DefaultMutableTreeNode node = (DefaultMutableTreeNode)
    1156                                    ((DefaultMutableTreeNode)stylesTreeRoot).
    1157                                    getFirstChild();
    1158      while(node != null &&
    1159            !((TypeData)node.getUserObject()).getSet().equals(set))
    1160        node = node.getNextSibling();
    1161      if(node != null){
    1162        node = (DefaultMutableTreeNode)node.getFirstChild();
    1163        String type = ann.getType();
    1164        while(node != null &&
    1165              !((TypeData)node.getUserObject()).getType().equals(type))
    1166          node = node.getNextSibling();
    1167        if(node != null) stylesTreeModel.nodeChanged(node);
    1168      }
    1169    }
    1170    int position = -1;
    1171    position = data.indexOf(ann);
    1172    if(position != -1){
    1173      position = annotationsTable.getTableRow(position);
    1174      if(position != -1){
    1175        annotationsTable.clearSelection();
    1176        annotationsTable.addRowSelectionInterval(position, position);
    1177        annotationsTable.scrollRectToVisible(
    1178              annotationsTable.getCellRect(position, 0, true));
    1179      }
    1180    }
    1181  }//protected void selectAnnotation(String set, Annotation ann)
    1182
    1183
    1184  /**
    1185   * Creates the layout of this component acording to the set of subcomponents
    1186   * (text display, annotations table, etc.) that need to be visible.
    1187   */
    1188  protected void layoutComponents(){
    1189    SwingUtilities.invokeLater(new Runnable(){
    1190      public void run(){
    1191        Component leftComp = null, rightComp = null;
    1192        if(isTextVisible() && isAnnotationsTableVisible()){
    1193          leftSplit.setTopComponent(textScroll);
    1194          leftSplit.setBottomComponent(tableScroll);
    1195          leftComp = leftSplit;
    1196        }else{
    1197          if(isTextVisible()) leftComp = textScroll;
    1198          else if(isAnnotationsTableVisible()) leftComp = tableScroll;
    1199        }
    1200
    1201        boolean corefDisplayed = isCoreferenceVisible() &&
    1202                                 isCorefOptionAvailable();
    1203        if(isTypesTreeVisible() && corefDisplayed){
    1204          rightSplit.setTopComponent(stylesTreeScroll);
    1205          rightSplit.setBottomComponent(corefScroll);
    1206          rightComp = rightSplit;
    1207        }else{
    1208          if(isTypesTreeVisible()) rightComp = stylesTreeScroll;
    1209          else if(corefDisplayed) rightComp = corefScroll;
    1210        }
    1211
    1212        if(DocumentEditor.this.getComponentCount() > 1)
    1213          DocumentEditor.this.remove(1);
    1214        if(leftComp != null && rightComp != null){
    1215          //we need the main split
    1216          mainSplit.setLeftComponent(leftComp);
    1217          mainSplit.setRightComponent(rightComp);
    1218          DocumentEditor.this.add(mainSplit);
    1219        }else{
    1220          if(leftComp != null) DocumentEditor.this.add(leftComp);
    1221          else if(rightComp != null)DocumentEditor.this.add(rightComp);
    1222        }
    1223
    1224        DocumentEditor.this.validate();
    1225        DocumentEditor.this.repaint();
    1226      }
    1227    });
    1228  }
    1229
    1230
    1231  /**
    1232   * Updates the coref tree from the coref data on the document's features
    1233   */
    1234  protected void updateCorefTree(){
    1235    if(document == null || document.getFeatures() == null){
    1236      //no coref data; clear the tree
    1237      corefTreeRoot.removeAllChildren();
    1238      corefTreeModel.nodeStructureChanged(corefTreeRoot);
    1239      setCorefOptionAvailable(false);
    1240      return;
    1241    }
    1242
    1243    Map matchesMap = null;
    1244    try{
    1245      matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
    1246    }catch(Exception e){
    1247    }
    1248    if(matchesMap == null){
    1249      //no coref data; clear the tree
    1250      Enumeration nodes = corefTreeRoot.breadthFirstEnumeration();
    1251      while(nodes.hasMoreElements()){
    1252        DefaultMutableTreeNode node = (DefaultMutableTreeNode)
    1253                                      nodes.nextElement();
    1254        if(node.getUserObject() instanceof CorefData){
    1255          ((CorefData)node.getUserObject()).setVisible(false);
    1256        }
    1257      }
    1258      corefTreeRoot.removeAllChildren();
    1259      corefTreeModel.nodeStructureChanged(corefTreeRoot);
    1260      setCorefOptionAvailable(false);
    1261      return;
    1262    }
    1263
    1264    //matches map is not null; check whether it's valid
    1265    Iterator setsIter = matchesMap.keySet().iterator();
    1266    setsLoop: while(setsIter.hasNext()){
    1267      String setName = (String)setsIter.next();
    1268      AnnotationSet annSet = setName == null ? document.getAnnotations() :
    1269                                               document.getAnnotations(setName);
    1270      Iterator entitiesIter = ((java.util.List)matchesMap.get(setName)).
    1271                              iterator();
    1272      //each entity is a list of annotation IDs
    1273      while(entitiesIter.hasNext()){
    1274        Iterator idsIter = ((java.util.List)entitiesIter.next()).iterator();
    1275        while(idsIter.hasNext()){
    1276          if(annSet.get((Integer)idsIter.next()) == null){
    1277            //remove the data for this set
    1278            setsIter.remove();
    1279            Err.prln("Coreference data for the \"" +
    1280                     (setName == null ? "Default" : setName) +
    1281                      "\" annotation set of document \"" + document.getName() +
    1282                     "\" was invalid and has been removed");
    1283            continue setsLoop;
    1284          }
    1285        }
    1286      }
    1287    }
    1288
    1289    if(matchesMap.isEmpty()){
    1290      //no more coref data
    1291      corefTreeRoot.removeAllChildren();
    1292      corefTreeModel.nodeStructureChanged(corefTreeRoot);
    1293      setCorefOptionAvailable(false);
    1294      return;
    1295    }
    1296
    1297    String[] newSetNames = (String[])
    1298                           matchesMap.keySet().toArray(new String[]{});
    1299    Arrays.sort(newSetNames);
    1300
    1301    ArrayList oldSetNames = new ArrayList(corefTreeRoot.getChildCount());
    1302    Enumeration setNodes = corefTreeRoot.children();
    1303    while(setNodes.hasMoreElements()){
    1304      String oldSetName = (String)
    1305                           ((DefaultMutableTreeNode)setNodes.nextElement()).
    1306                           getUserObject();
    1307      oldSetNames.add(oldSetName.equals("Default") ? null : oldSetName);
    1308    }
    1309
    1310
    1311    // stores the new set nodes; they will be added to root after the
    1312    // processing is done
    1313    ArrayList newSetNodes = new ArrayList();
    1314    //for each new set update the children
    1315    for(int i =0; i < newSetNames.length; i++){
    1316      String setName = newSetNames[i];
    1317      int oldNodeIndex = oldSetNames.indexOf(setName);
    1318      DefaultMutableTreeNode setNode =
    1319          (oldNodeIndex != -1) ?
    1320          (DefaultMutableTreeNode)
    1321          corefTreeRoot.getChildAt(oldNodeIndex) :
    1322          new DefaultMutableTreeNode((setName == null ? "Default" : setName),
    1323                                     true);
    1324      //if found it will be reused so delete it from the list
    1325      if(oldNodeIndex != -1) oldSetNames.remove(oldNodeIndex);
    1326
    1327      // temporarily stores the new nodes
    1328      ArrayList newEntityNodes = new ArrayList();
    1329      //for each set the coref data is a list of lists
    1330      Iterator corefDataIter = ((java.util.List)matchesMap.get(setName)).
    1331                               iterator();
    1332      while(corefDataIter.hasNext()){
    1333        java.util.List newAnnotIDs = (java.util.List)corefDataIter.next();
    1334        CorefData cData = null;
    1335        DefaultMutableTreeNode entityNode = null;
    1336        //try to find the old coref data
    1337        Enumeration entityNodes = setNode.children();
    1338        while(cData == null && entityNodes.hasMoreElements()){
    1339          entityNode = (DefaultMutableTreeNode)entityNodes.nextElement();
    1340          java.util.List oldAnnotIDs = ((CorefData)entityNode.getUserObject()).
    1341                                     getAnnoationIDs();
    1342          java.util.List intersection = new ArrayList(oldAnnotIDs);
    1343          intersection.retainAll(newAnnotIDs);
    1344          if(!intersection.isEmpty()){
    1345            //we have some common values; assume we found it
    1346            cData = (CorefData)entityNode.getUserObject();
    1347            if(intersection.size() == newAnnotIDs.size()){
    1348              //identical values, we just got lucky: noting to do
    1349            }else{
    1350              cData.setAnnotationIDs(newAnnotIDs);
    1351            }
    1352          }
    1353        }
    1354        if(cData == null){
    1355          //we couldn't find a suitable node, create a new one
    1356          cData = new CorefData(newAnnotIDs, false, setName == null ?
    1357                                                    "Default" : setName);
    1358          entityNode = new DefaultMutableTreeNode(cData, false);
    1359        }
    1360        newEntityNodes.add(entityNode);
    1361      }//while(corefDataIter.hasNext())
    1362      //we're done with this set: add all the nodes to the set node
    1363      //set visible to false for all nodes that will not be kept
    1364      for(Enumeration entityNodes = setNode.children();
    1365          entityNodes.hasMoreElements();){
    1366        Object anOldNode = entityNodes.nextElement();
    1367        if(!newEntityNodes.contains(anOldNode)){
    1368          ((CorefData)((DefaultMutableTreeNode)anOldNode).
    1369          getUserObject()).setVisible(false);
    1370        }
    1371      }
    1372
    1373      setNode.removeAllChildren();
    1374      for(Iterator nodesIter = newEntityNodes.iterator();
    1375          nodesIter.hasNext();
    1376          setNode.add((DefaultMutableTreeNode)nodesIter.next())){
    1377      }
    1378      newSetNodes.add(setNode);
    1379    }//for(int i =0; i < newSetNames.length; i++)
    1380    //we're done with all the sets: add the nodes to the tree root
    1381    corefTreeRoot.removeAllChildren();
    1382    for(Iterator nodesIter = newSetNodes.iterator();
    1383        nodesIter.hasNext();){
    1384      DefaultMutableTreeNode setNode = (DefaultMutableTreeNode)nodesIter.next();
    1385      corefTreeRoot.add(setNode);
    1386    }
    1387    corefTreeModel.nodeStructureChanged(corefTreeRoot);
    1388    //expand the root
    1389    corefTree.expandPath(new TreePath(new Object[]{corefTreeRoot}));
    1390    //expand all of root's children
    1391    Enumeration children = corefTreeRoot.children();
    1392    while(children.hasMoreElements()){
    1393      corefTree.expandPath(new TreePath(corefTreeModel.getPathToRoot(
    1394                           (DefaultMutableTreeNode)children.nextElement())));
    1395    }
    1396    setCorefOptionAvailable(true);
    1397  }//protected void updateCorefTree()
    1398
    1399
    1400  /**Should the editor functionality of this component be enabled*/
    1401  public void setEditable(boolean newEditable) {
    1402    editable = newEditable;
    1403  }
    1404
    1405  /**Is the editor functionality enabled*/
    1406  public boolean isEditable() {
    1407    return editable;
    1408  }
    1409  public void setAnnotationsTableVisible(boolean annotationsTableVisible) {
    1410    boolean  oldAnnotationsTableVisible = this.annotationsTableVisible;
    1411    this.annotationsTableVisible = annotationsTableVisible;
    1412    propertyChangeListeners.firePropertyChange(
    1413        "annotationsTableVisible",
    1414        new Boolean(oldAnnotationsTableVisible),
    1415        new Boolean(annotationsTableVisible));
    1416  }
    1417  public boolean isAnnotationsTableVisible() {
    1418    return annotationsTableVisible;
    1419  }
    1420  public void setCoreferenceVisible(boolean coreferenceVisible) {
    1421    boolean  oldCoreferenceVisible = this.coreferenceVisible;
    1422    this.coreferenceVisible = coreferenceVisible;
    1423    propertyChangeListeners.firePropertyChange(
    1424      "coreferenceVisible",
    1425      new Boolean(oldCoreferenceVisible),
    1426      new Boolean(coreferenceVisible));
    1427  }
    1428
    1429  public boolean isCoreferenceVisible() {
    1430    return coreferenceVisible;
    1431  }
    1432  public void setTextVisible(boolean textVisible) {
    1433    boolean  oldTextVisible = this.textVisible;
    1434    this.textVisible = textVisible;
    1435    propertyChangeListeners.firePropertyChange("textVisible",
    1436                                               new Boolean(oldTextVisible),
    1437                                               new Boolean(textVisible));
    1438  }
    1439  public boolean isTextVisible() {
    1440    return textVisible;
    1441  }
    1442  public void setTypesTreeVisible(boolean typesTreeVisible) {
    1443    boolean  oldTypesTreeVisible = this.typesTreeVisible;
    1444    this.typesTreeVisible = typesTreeVisible;
    1445    propertyChangeListeners.firePropertyChange("typesTreeVisible",
    1446                                               new Boolean(oldTypesTreeVisible),
    1447                                               new Boolean(typesTreeVisible));
    1448  }
    1449  public boolean isTypesTreeVisible() {
    1450    return typesTreeVisible;
    1451  }
    1452  public void setCorefOptionAvailable(boolean corefOptionAvailable) {
    1453    boolean  oldCorefOptionAvailable = this.corefOptionAvailable;
    1454    this.corefOptionAvailable = corefOptionAvailable;
    1455    propertyChangeListeners.firePropertyChange(
    1456      "corefOptionAvailable", new Boolean(oldCorefOptionAvailable),
    1457      new Boolean(corefOptionAvailable));
    1458  }
    1459
    1460  public boolean isCorefOptionAvailable() {
    1461    return corefOptionAvailable;
    1462  }
    1463
    1464  //inner classes
    1465  /**
    1466   * A custom table model used to render a table containing the annotations
    1467   * from a set of annotation sets.
    1468   * The columns will be: Type, Set, Start, End, Features
    1469   */
    1470  protected class AnnotationsTableModel extends AbstractTableModel{
    1471    public AnnotationsTableModel(){
    1472    }
    1473
    1474    public int getRowCount(){
    1475      return data.size();
    1476    }
    1477
    1478    public int getColumnCount(){
    1479      return 5;
    1480    }
    1481
    1482    public String getColumnName(int column){
    1483      switch(column){
    1484        case 0: return "Type";
    1485        case 1: return "Set";
    1486        case 2: return "Start";
    1487        case 3: return "End";
    1488        case 4: return "Features";
    1489        default:return "?";
    1490      }
    1491    }
    1492
    1493    public Class getColumnClass(int column){
    1494      switch(column){
    1495        case 0: return String.class;
    1496        case 1: return String.class;
    1497        case 2: return Long.class;
    1498        case 3: return Long.class;
    1499        case 4: return String.class;
    1500        default:return Object.class;
    1501      }
    1502    }
    1503
    1504    public Object getValueAt(int row, int column){
    1505      Annotation ann;
    1506      ann = (Annotation)data.get(row);
    1507      switch(column){
    1508        case -1:{//The actual annotation
    1509          return ann;
    1510        }
    1511        case 0:{//Type
    1512          return ann.getType();
    1513        }
    1514        case 1:{//Set
    1515          Iterator rangesIter = ranges.iterator();
    1516          while(rangesIter.hasNext()){
    1517            Range range = (Range)rangesIter.next();
    1518            if(range.start <= row && row < range.end) return range.setName;
    1519          }
    1520          return "?";
    1521        }
    1522        case 2:{//Start
    1523          return ann.getStartNode().getOffset();
    1524        }
    1525        case 3:{//End
    1526          return ann.getEndNode().getOffset();
    1527        }
    1528        case 4:{//Features
    1529          if(ann.getFeatures() == null) return null;
    1530          else return ann.getFeatures().toString();
    1531        }
    1532        default:{
    1533        }
    1534      }
    1535      return null;
    1536    }
    1537  }//class AnnotationsTableModel extends AbstractTableModel
    1538
    1539/*
    1540  protected class CorefListModel extends AbstractListModel{
    1541    CorefListModel(){
    1542      corefComboModel.addListDataListener(new ListDataListener() {
    1543        public void intervalAdded(ListDataEvent e) {
    1544          fireDataChanged();
    1545        }
    1546
    1547        public void intervalRemoved(ListDataEvent e) {
    1548          fireDataChanged();
    1549        }
    1550
    1551        public void contentsChanged(ListDataEvent e) {
    1552          fireDataChanged();
    1553        }
    1554      });
    1555      coloursList = new ArrayList();
    1556      visibleList = new ArrayList();
    1557      highlights = new ArrayList();
    1558      lastReturnedSize = 0;
    1559    }
    1560
    1561    public int getSize(){
    1562      if(document == null || document.getFeatures() == null) return 0;
    1563      Map matchesMap = null;
    1564      try{
    1565        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
    1566      }catch(Exception e){
    1567        e.printStackTrace();
    1568      }
    1569      if(matchesMap == null) return 0;
    1570      java.util.List matchesList = (java.util.List)
    1571                                   matchesMap.get(corefCombo.getSelectedItem());
    1572      int size = (matchesList == null) ? 0 : matchesList.size();
    1573      if(lastReturnedSize != size){
    1574        lastReturnedSize = size;
    1575        fireDataChanged();
    1576      }
    1577      return lastReturnedSize;
    1578    }
    1579
    1580    public Object getElementAt(int index){
    1581      if(document == null || document.getFeatures() == null) return null;
    1582      Map matchesMap = null;
    1583      try{
    1584        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
    1585      }catch(Exception e){
    1586        e.printStackTrace();
    1587      }
    1588      if(matchesMap == null) return null;
    1589      java.util.List matchesList = (java.util.List)
    1590                                   matchesMap.get(corefCombo.getSelectedItem());
    1591      if(matchesList == null || matchesList.size() <= index) return null;
    1592      java.util.List oneMatch = (java.util.List)matchesList.get(index);
    1593      return oneMatch;
    1594    }
    1595
    1596    void fireDataChanged(){
    1597      fireContentsChanged(this, 0, getSize());
    1598    }
    1599
    1600    Color getColour(int row){
    1601      if(row >= coloursList.size()){
    1602        for(int i = coloursList.size(); i <= row; i++){
    1603          coloursList.add(i, colGenerator.getNextColor());
    1604        }
    1605      }
    1606      return (Color)coloursList.get(row);
    1607    }
    1608
    1609    void setColour(int row, Color color){
    1610      if(row >= coloursList.size()){
    1611        for(int i = coloursList.size(); i <= row; i++){
    1612          coloursList.add(i, colGenerator.getNextColor());
    1613        }
    1614      }
    1615      coloursList.set(row, color);
    1616    }
    1617
    1618    boolean getVisible(int row){
    1619      if(row >= visibleList.size()){
    1620        for(int i = visibleList.size(); i <= row; i++){
    1621          visibleList.add(i, new Boolean(false));
    1622        }
    1623      }
    1624      return ((Boolean)visibleList.get(row)).booleanValue();
    1625    }
    1626
    1627    void setVisible(int row, boolean visible){
    1628      if(row >= visibleList.size()){
    1629        for(int i = visibleList.size(); i <= row; i++){
    1630          visibleList.add(i, new Boolean(false));
    1631        }
    1632      }
    1633      visibleList.set(row, new Boolean(visible));
    1634      java.util.List highlightsForRow = getHighlights(row);
    1635      if(visible){
    1636        //add new highlights and store them
    1637        java.util.List ids = (java.util.List)getElementAt(row);
    1638        String setName = (String)corefCombo.getSelectedItem();
    1639        AnnotationSet set = setName.equals("Default") ?
    1640                            document.getAnnotations() :
    1641                            document.getAnnotations(setName);
    1642        Iterator idIter = ids.iterator();
    1643        while(idIter.hasNext()){
    1644          Integer id = (Integer)idIter.next();
    1645          Annotation ann = set.get(id);
    1646          try{
    1647            highlightsForRow.add(highlighter.addHighlight(
    1648              ann.getStartNode().getOffset().intValue(),
    1649              ann.getEndNode().getOffset().intValue(),
    1650              new DefaultHighlighter.DefaultHighlightPainter(getColour(row))));
    1651          }catch(BadLocationException ble){
    1652            ble.printStackTrace();
    1653          }
    1654        }
    1655      }else{
    1656        //remove the highlights
    1657        if(!highlightsForRow.isEmpty()){
    1658          Iterator hlIter = highlightsForRow.iterator();
    1659          while(hlIter.hasNext()){
    1660            Object tag = hlIter.next();
    1661            highlighter.removeHighlight(tag);
    1662            hlIter.remove();
    1663          }
    1664        }
    1665      }
    1666    }//void setVisible(int row, boolean visible){
    1667
    1668    java.util.List getHighlights(int row){
    1669      if(row >= highlights.size()){
    1670        for(int i = highlights.size(); i <= row; i++){
    1671          highlights.add(i, new ArrayList());
    1672        }
    1673      }
    1674      return ((java.util.List)highlights.get(row));
    1675    }
    1676
    1677    void setHighlights(int row, java.util.List highlightsForRow ){
    1678      if(row >= highlights.size()){
    1679        for(int i = highlights.size(); i <= row; i++){
    1680          highlights.add(i, new ArrayList());
    1681        }
    1682      }
    1683      highlights.set(row, highlightsForRow);
    1684    }
    1685
    1686//    /**
    1687//     * Holds the <b>visible</b> attribute for each row in the list
    1688//     */
    1689//    ArrayList visibleList;
    1690//
    1691//    /**
    1692//     * Holds the <b>colour</b> attribute for each row in the list
    1693//     */
    1694//    ArrayList coloursList;
    1695//
    1696//    /**
    1697//     * A list of lists; holds the currently showing highlights for each row
    1698//     */
    1699//    ArrayList highlights;
    1700//
    1701//    int lastReturnedSize;
    1702//  }
    1703/*
    1704  class CorefListRenderer extends JCheckBox implements ListCellRenderer{
    1705    public CorefListRenderer(){
    1706      setOpaque(true);
    1707    }
    1708
    1709    public Component getListCellRendererComponent(JList list,
    1710                                                  Object value,
    1711                                                  int index,
    1712                                                  boolean isSelected,
    1713                                                  boolean cellHasFocus){
    1714//      if (isSelected) {
    1715//        setForeground(list.getSelectionForeground());
    1716//        setBackground(list.getSelectionBackground());
    1717//      }else{
    1718        setForeground(list.getForeground());
    1719        setBackground(list.getBackground());
    1720//      }
    1721      setBackground(((CorefListModel)list.getModel()).getColour(index));
    1722      setFont(list.getFont());
    1723      if (cellHasFocus) {
    1724        setBorder( UIManager.getBorder("Table.focusCellHighlightBorder") );
    1725      }else{
    1726        setBorder(noFocusBorder);
    1727      }
    1728      setText(getNameForCorefList((java.util.List) value));
    1729      setSelected(((CorefListModel)list.getModel()).getVisible(index));
    1730      return this;
    1731    }
    1732
    1733    String getNameForCorefList(java.util.List list){
    1734      if(list == null || list.isEmpty()) return null;
    1735      Integer id = (Integer)list.get(0);
    1736      String setName = (String)corefCombo.getSelectedItem();
    1737      AnnotationSet set = setName.equals("Default") ?
    1738                          document.getAnnotations() :
    1739                          document.getAnnotations(setName);
    1740      Annotation ann = set.get(id);
    1741
    1742      String name = null;
    1743      try{
    1744        name = document.getContent().
    1745                        getContent(ann.getStartNode().getOffset(),
    1746                                   ann.getEndNode().getOffset()).toString();
    1747      }catch(InvalidOffsetException ioe){
    1748      }
    1749      return name;
    1750    }
    1751    Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
    1752  }
    1753
    1754*/
    1755  protected class CorefData{
    1756    CorefData(java.util.List annotationIDs, boolean visible, String setName){
    1757      this.visible = visible;
    1758      this.setName = setName;
    1759      this.colour = colGenerator.getNextColor();
    1760      highlights = new ArrayList();
    1761      this.annotationIDs = annotationIDs;
    1762      this.title = getNameForCorefList(annotationIDs);
    1763    }
    1764
    1765    /**
    1766     * Finds the name for a set of co refering entities (uses the string of the
    1767     * first one).
    1768     * @param list a list of annotation IDs
    1769     */
    1770    String getNameForCorefList(java.util.List list){
    1771      if(list == null || list.isEmpty()) return null;
    1772      Integer id = (Integer)list.get(0);
    1773      AnnotationSet set = setName.equals("Default") ?
    1774                          document.getAnnotations() :
    1775                          document.getAnnotations(setName);
    1776      Annotation ann = set.get(id);
    1777
    1778      String name = null;
    1779      try{
    1780        name = document.getContent().
    1781                        getContent(ann.getStartNode().getOffset(),
    1782                                   ann.getEndNode().getOffset()).toString();
    1783      }catch(InvalidOffsetException ioe){
    1784      }
    1785      return name;
    1786    }
    1787
    1788    public boolean getVisible(){
    1789      return visible;
    1790    }
    1791
    1792    public void setVisible(boolean isVisible){
    1793      if(this.visible == isVisible) return;
    1794      this.visible = isVisible;
    1795      if(visible){
    1796if(!highlights.isEmpty()){
    1797  Out.prln("Redundant highlights detected!");
    1798}
    1799        //add new highlights and store them
    1800        AnnotationSet set = setName.equals("Default") ?
    1801                            document.getAnnotations() :
    1802                            document.getAnnotations(setName);
    1803        Iterator idIter = annotationIDs.iterator();
    1804        while(idIter.hasNext()){
    1805          Integer id = (Integer)idIter.next();
    1806          Annotation ann = set.get(id);
    1807          try{
    1808            highlights.add(highlighter.addHighlight(
    1809              ann.getStartNode().getOffset().intValue(),
    1810              ann.getEndNode().getOffset().intValue(),
    1811              new DefaultHighlighter.DefaultHighlightPainter(colour)));
    1812          }catch(BadLocationException ble){
    1813            ble.printStackTrace();
    1814          }
    1815        }
    1816      }else{
    1817        //remove the highlights
    1818        if(!highlights.isEmpty()){
    1819          Iterator hlIter = highlights.iterator();
    1820          while(hlIter.hasNext()){
    1821            Object tag = hlIter.next();
    1822            highlighter.removeHighlight(tag);
    1823            hlIter.remove();
    1824          }
    1825        }
    1826      }
    1827    }
    1828
    1829    public String getTitle(){
    1830      return title;
    1831    }
    1832
    1833    public Color getColour(){
    1834      return colour;
    1835    }
    1836
    1837    public void setColour(Color newColour){
    1838      this.colour = newColour;
    1839      if(visible){
    1840        //update the highlights
    1841        setVisible(false);
    1842        setVisible(true);
    1843      }
    1844    }
    1845
    1846    public java.util.List getAnnoationIDs(){
    1847      return annotationIDs;
    1848    }
    1849
    1850    public String toString(){
    1851      return title;
    1852    }
    1853
    1854    public void setAnnotationIDs(java.util.List newAnnIDs){
    1855      this.annotationIDs =newAnnIDs;
    1856      this.title = getNameForCorefList(annotationIDs);
    1857      if(visible){
    1858        //restore the highlights
    1859        setVisible(false);
    1860        setVisible(true);
    1861      }
    1862    }
    1863
    1864    private boolean visible;
    1865    private String title;
    1866    private String setName;
    1867    private Color colour;
    1868    private java.util.List highlights;
    1869    private java.util.List annotationIDs;
    1870  }
    1871
    1872/*
    1873  protected class CorefComboModel extends AbstractListModel
    1874                                  implements ComboBoxModel{
    1875
    1876    CorefComboModel(){
    1877      lastReturnedSize = 0;
    1878    }
    1879
    1880    public int getSize(){
    1881      if(document == null || document.getFeatures() == null) return 0;
    1882      Map matchesMap = null;
    1883      try{
    1884        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
    1885      }catch(Exception e){
    1886        e.printStackTrace();
    1887      }
    1888      int size = (matchesMap == null) ? 0 : matchesMap.size();
    1889      if(lastReturnedSize != size){
    1890        lastReturnedSize = size;
    1891        fireDataChanged();
    1892      }
    1893      return lastReturnedSize;
    1894    }
    1895
    1896
    1897    public Object getElementAt(int index){
    1898      if(document == null || document.getFeatures() == null) return null;
    1899      Map matchesMap = null;
    1900      try{
    1901        matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
    1902      }catch(Exception e){
    1903        e.printStackTrace();
    1904      }
    1905      if(matchesMap == null) return null;
    1906      java.util.List setsList = new ArrayList(matchesMap.keySet());
    1907      boolean nullPresent = setsList.remove(null);
    1908      Collections.sort(setsList);
    1909      if(nullPresent) setsList.add(0, null);
    1910      String res = (String)setsList.get(index);
    1911      return (res == null) ? "Default" : res;
    1912    }
    1913
    1914    public void setSelectedItem(Object anItem){
    1915      if(anItem == null) selectedItem = null;
    1916      else selectedItem = ((String)anItem).equals("Default") ? null : anItem;
    1917    }
    1918
    1919    public Object getSelectedItem(){
    1920      return selectedItem == null ? "Default" : selectedItem;
    1921    }
    1922
    1923    void fireDataChanged(){
    1924      fireContentsChanged(this, 0, getSize());
    1925    }
    1926
    1927    Object selectedItem = null;
    1928    int lastReturnedSize;
    1929  }
    1930*/
    1931
    1932  /**
    1933   * Panels used in cell/node renderers
    1934   */
    1935  class LazyJPanel extends JPanel{
    1936    /**
    1937     * Overridden for performance reasons.
    1938     */
    1939    public void revalidate() {}
    1940
    1941    /**
    1942     * Overridden for performance reasons.
    1943     */
    1944    public void repaint(long tm, int x, int y, int width, int height) {}
    1945
    1946    /**
    1947     * Overridden for performance reasons.
    1948     */
    1949    public void repaint(Rectangle r) {}
    1950
    1951    /**
    1952     * Overridden for performance reasons.
    1953     */
    1954    protected void firePropertyChange(String propertyName, Object oldValue,
    1955                                                            Object newValue) {}
    1956
    1957    /**
    1958     * Overridden for performance reasons.
    1959     */
    1960    public void firePropertyChange(String propertyName, byte oldValue,
    1961                                                              byte newValue) {}
    1962
    1963    /**
    1964     * Overridden for performance reasons.
    1965     */
    1966    public void firePropertyChange(String propertyName, char oldValue,
    1967                                                              char newValue) {}
    1968
    1969    /**
    1970     * Overridden for performance reasons.
    1971     */
    1972    public void firePropertyChange(String propertyName, short oldValue,
    1973                                                            short newValue) {}
    1974
    1975    /**
    1976     * Overridden for performance reasons.
    1977     */
    1978    public void firePropertyChange(String propertyName, int oldValue,
    1979                                                              int newValue) {}
    1980
    1981    /**
    1982     * Overridden for performance reasons.
    1983     */
    1984    public void firePropertyChange(String propertyName, long oldValue,
    1985                                                              long newValue) {}
    1986
    1987    /**
    1988     * Overridden for performance reasons.
    1989     */
    1990    public void firePropertyChange(String propertyName, float oldValue,
    1991                                                              float newValue) {}
    1992
    1993    /**
    1994     * Overridden for performance reasons.
    1995     */
    1996    public void firePropertyChange(String propertyName, double oldValue,
    1997                                                            double newValue) {}
    1998
    1999    /**
    2000     * Overridden for performance reasons.
    2001     */
    2002    public void firePropertyChange(String propertyName, boolean oldValue,
    2003                                                            boolean newValue) {}
    2004  }
    2005
    2006  /**
    2007   * A tree node renderer used by the coref tree
    2008   */
    2009  class CorefNodeRenderer implements TreeCellRenderer{
    2010
    2011    CorefNodeRenderer(){
    2012      label = new JLabel();
    2013      label.setOpaque(true);
    2014
    2015      checkBox = new JCheckBox();
    2016      checkBox.setBorderPaintedFlat(true);
    2017
    2018      panel = new LazyJPanel();
    2019      panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    2020      panel.setOpaque(false);
    2021
    2022      hBox = new LazyJPanel();
    2023      hBox.setLayout(new BoxLayout(hBox, BoxLayout.X_AXIS));
    2024      hBox.setOpaque(false);
    2025
    2026      panel.add(Box.createVerticalStrut(2));
    2027      panel.add(hBox);
    2028      panel.add(Box.createVerticalStrut(2));
    2029
    2030      leftSpacer = Box.createHorizontalStrut(3);
    2031      rightSpacer = Box.createHorizontalStrut(3);
    2032
    2033      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
    2034      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
    2035    }
    2036
    2037    public Component getTreeCellRendererComponent(JTree tree,
    2038                                              Object value,
    2039                                              boolean selected,
    2040                                              boolean expanded,
    2041                                              boolean leaf,
    2042                                              int row,
    2043                                              boolean hasFocus){
    2044
    2045      hBox.removeAll();
    2046      hBox.add(leftSpacer);
    2047
    2048      if(value instanceof DefaultMutableTreeNode){
    2049        value = ((DefaultMutableTreeNode)value).getUserObject();
    2050      }
    2051      if(value instanceof CorefData){
    2052        CorefData cData = (CorefData)value;
    2053        checkBox.setSelected(cData.getVisible());
    2054        checkBox.setBackground(tree.getBackground());
    2055
    2056        label.setBackground(cData.getColour());
    2057        label.setForeground(tree.getForeground());
    2058        label.setText(cData.getTitle());
    2059        label.setFont(tree.getFont());
    2060        hBox.add(checkBox);
    2061        hBox.add(label);
    2062        hBox.add(rightSpacer);
    2063      }else{
    2064        label.setText(value == null ? "" : value.toString());
    2065        label.setForeground(tree.getForeground());
    2066        label.setBackground(tree.getBackground());
    2067        label.setFont(tree.getFont());
    2068        hBox.add(label);
    2069      }
    2070      if(selected) panel.setBorder(selectedBorder);
    2071      else panel.setBorder(normalBorder);
    2072      return panel;
    2073    }
    2074
    2075    JLabel label;
    2076    JCheckBox checkBox;
    2077    JPanel panel;
    2078    JPanel hBox;
    2079    Border selectedBorder;
    2080    Border normalBorder;
    2081    Component leftSpacer, rightSpacer;
    2082  }
    2083
    2084  /**
    2085   * A tree node renderer used byt the coref tree
    2086   */
    2087  class CorefNodeRenderer1 implements TreeCellRenderer{
    2088
    2089    CorefNodeRenderer1(){
    2090      label = new JLabel();
    2091      label.setOpaque(true);
    2092
    2093      toggleButton = new JToggleButton();
    2094      toggleButton.setMargin(new Insets(0,3,0,3));
    2095
    2096      panel = new LazyJPanel();
    2097      panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    2098      panel.setOpaque(false);
    2099      topSpacer = Box.createVerticalStrut(2);
    2100      bottomSpacer = Box.createVerticalStrut(2);
    2101
    2102      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
    2103      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
    2104
    2105    }
    2106
    2107    public Component getTreeCellRendererComponent(JTree tree,
    2108                                              Object value,
    2109                                              boolean selected,
    2110                                              boolean expanded,
    2111                                              boolean leaf,
    2112                                              int row,
    2113                                              boolean hasFocus){
    2114
    2115      panel.removeAll();
    2116      panel.add(topSpacer);
    2117
    2118      if(value instanceof DefaultMutableTreeNode){
    2119        value = ((DefaultMutableTreeNode)value).getUserObject();
    2120      }
    2121      if(value instanceof CorefData){
    2122        CorefData cData = (CorefData)value;
    2123        toggleButton.setSelected(cData.getVisible());
    2124        toggleButton.setBackground(cData.getColour());
    2125        toggleButton.setForeground(tree.getForeground());
    2126        toggleButton.setText(cData.getTitle());
    2127        toggleButton.setFont(tree.getFont());
    2128        panel.add(toggleButton);
    2129      }else{
    2130        label.setText(value.toString());
    2131        label.setForeground(tree.getForeground());
    2132        label.setBackground(tree.getBackground());
    2133        label.setFont(tree.getFont());
    2134        panel.add(label);
    2135      }
    2136      panel.add(bottomSpacer);
    2137      if(selected) panel.setBorder(selectedBorder);
    2138      else panel.setBorder(normalBorder);
    2139      return panel;
    2140    }
    2141
    2142    JLabel label;
    2143    JToggleButton toggleButton;
    2144    JPanel panel;
    2145    Border selectedBorder;
    2146    Border normalBorder;
    2147    Component topSpacer, bottomSpacer;
    2148  }
    2149
    2150
    2151  /**
    2152   * Displays an entry in the right hand side tree.
    2153   * <strong>Implementation Note:</strong>
    2154   * This class overrides
    2155   * <code>revalidate</code>,
    2156   * <code>repaint</code>,
    2157   * and
    2158   * <code>firePropertyChange</code>
    2159   * solely to improve performance.
    2160   * If not overridden, these frequently called methods would execute code paths
    2161   * that are unnecessary for a tree cell renderer.
    2162   */
    2163  class NodeRenderer extends LazyJPanel implements TreeCellRenderer{
    2164
    2165    public NodeRenderer(){
    2166      visibleChk = new JCheckBox("",false);
    2167      visibleChk.setOpaque(false);
    2168      visibleChk.setBorderPaintedFlat(true);
    2169
    2170      label = new JLabel();
    2171      label.setOpaque(true);
    2172      fontAttrs = new HashMap();
    2173      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
    2174      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
    2175      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    2176      setOpaque(false);
    2177      spacer = Box.createHorizontalStrut(3);
    2178    }
    2179
    2180    public Component getTreeCellRendererComponent(JTree tree,
    2181                                              Object value,
    2182                                              boolean selected,
    2183                                              boolean expanded,
    2184                                              boolean leaf,
    2185                                              int row,
    2186                                              boolean hasFocus){
    2187      removeAll();
    2188      add(spacer);
    2189
    2190      int width = spacer.getWidth();
    2191
    2192
    2193      TypeData nData = (TypeData)
    2194                            ((DefaultMutableTreeNode)value).getUserObject();
    2195
    2196      if(nData != null){
    2197        label.setText(nData.getTitle());
    2198        setLabelAttributes(nData.getAttributes());
    2199
    2200        if(nData.getType() != null) {
    2201          visibleChk.setSelected(nData.getVisible());
    2202          add(visibleChk);
    2203          width += visibleChk.getMinimumSize().width;
    2204        }
    2205      }else{
    2206        label.setText(((value == null || value.toString() == null) ?
    2207                              "" : value.toString()));
    2208      }
    2209      add(label);
    2210
    2211      if(selected) setBorder(selectedBorder);
    2212      else setBorder(normalBorder);
    2213      return this;
    2214    }//public Component getTreeCellRendererComponent
    2215
    2216    protected void setLabelAttributes(AttributeSet style){
    2217      label.setForeground(StyleConstants.getForeground(style));
    2218      label.setBackground(StyleConstants.getBackground(style));
    2219      fontAttrs.clear();
    2220      fontAttrs.put(TextAttribute.FAMILY, StyleConstants.getFontFamily(style));
    2221      fontAttrs.put(TextAttribute.SIZE, new Float(StyleConstants.getFontSize(style)));
    2222      if(StyleConstants.isBold(style))
    2223        fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
    2224      else fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
    2225      if(StyleConstants.isItalic(style))
    2226        fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
    2227      else fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR);
    2228      if(StyleConstants.isUnderline(style))
    2229        fontAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
    2230      else fontAttrs.remove(TextAttribute.UNDERLINE);
    2231      if(StyleConstants.isStrikeThrough(style))
    2232        fontAttrs.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
    2233      else fontAttrs.remove(TextAttribute.STRIKETHROUGH);
    2234      if(StyleConstants.isSuperscript(style))
    2235        fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
    2236      else if(StyleConstants.isSubscript(style))
    2237        fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
    2238      else fontAttrs.remove(TextAttribute.SUPERSCRIPT);
    2239
    2240      label.setFont(new Font(fontAttrs));
    2241    }
    2242
    2243    Border selectedBorder;
    2244    Border normalBorder;
    2245    JCheckBox visibleChk;
    2246    JLabel label;
    2247    Map fontAttrs;
    2248    Component spacer;
    2249  }//class NodeRenderer extends JPanel implements TreeCellRenderer
    2250  /**
    2251   * Displays an entry in the right hand side tree.
    2252   * <strong>Implementation Note:</strong>
    2253   * This class overrides
    2254   * <code>revalidate</code>,
    2255   * <code>repaint</code>,
    2256   * and
    2257   * <code>firePropertyChange</code>
    2258   * solely to improve performance.
    2259   * If not overridden, these frequently called methods would execute code paths
    2260   * that are unnecessary for a tree cell renderer.
    2261   */
    2262  class NodeRenderer1 extends LazyJPanel implements TreeCellRenderer{
    2263
    2264    public NodeRenderer1(){
    2265      visibleChk = new JCheckBox("",false);
    2266      visibleChk.setOpaque(false);
    2267      visibleChk.setBorderPaintedFlat(true);
    2268      textComponent = new JTextPane();
    2269      selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
    2270      normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
    2271      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    2272      setOpaque(false);
    2273      spacer = Box.createHorizontalStrut(3);
    2274    }
    2275
    2276    public Component getTreeCellRendererComponent(JTree tree,
    2277                                              Object value,
    2278                                              boolean selected,
    2279                                              boolean expanded,
    2280                                              boolean leaf,
    2281                                              int row,
    2282                                              boolean hasFocus){
    2283      removeAll();
    2284      add(spacer);
    2285
    2286      int width = spacer.getWidth();
    2287
    2288      //the text pane needs to be sized for modelToView() to work
    2289      textComponent.setSize(1000, 1000);
    2290
    2291      TypeData nData = (TypeData)
    2292                            ((DefaultMutableTreeNode)value).getUserObject();
    2293//      javax.swing.text.Document doc = textComponent.getDocument();
    2294
    2295      if(nData != null){
    2296        textComponent.setText(nData.getTitle());
    2297        textComponent.selectAll();
    2298        textComponent.setCharacterAttributes(nData.getAttributes(), false);
    2299        textComponent.select(0, 0);
    2300//        try{
    2301//          doc.remove(0, doc.getLength());
    2302//          doc.insertString(0, nData.getTitle(),
    2303//                           nData.getAttributes());
    2304//        }catch(BadLocationException ble){
    2305//          ble.printStackTrace();
    2306//        }
    2307
    2308        if(nData.getType() != null) {
    2309          visibleChk.setSelected(nData.getVisible());
    2310          add(visibleChk);
    2311          width += visibleChk.getMinimumSize().width;
    2312        }
    2313      }else{
    2314        textComponent.setText(((value == null || value.toString() == null) ?
    2315                              "" : value.toString()));
    2316//        try{
    2317//          doc.remove(0, doc.getLength());
    2318//          doc.insertString(0, value.toString(),
    2319//                           textComponent.getStyle("default"));
    2320//        }catch(BadLocationException ble){
    2321//          ble.printStackTrace();
    2322//        }
    2323      }
    2324      setTextComponentSize(textComponent);
    2325      add(textComponent);
    2326      width += textComponent.getPreferredSize().width;
    2327      if(selected) setBorder(selectedBorder);
    2328      else setBorder(normalBorder);
    2329      width += getInsets().left + getInsets().right;
    2330      setPreferredSize(null);
    2331      setPreferredSize(new Dimension(width, super.getPreferredSize().height));
    2332      return this;
    2333    }//public Component getTreeCellRendererComponent
    2334
    2335   protected void setTextComponentSize(JTextComponent comp){
    2336      try{
    2337        if(comp.getDocument() == null || comp.getDocument().getLength() <= 0){
    2338          return;
    2339        }
    2340        int width = 0;
    2341        Rectangle rect = comp.modelToView(0);
    2342        int height = rect.height;
    2343        int length = comp.getDocument().getLength();
    2344        if(length > 0){
    2345          Rectangle rect2 = comp.modelToView(length );
    2346          if(rect2 != null){
    2347            if(rect.x < rect2.x){
    2348              //left to right
    2349              width = rect2.x + rect2.width - rect.x;
    2350            }else{
    2351              //RtL
    2352              width = rect.x +rect.width - rect2.x;
    2353            }
    2354            height = Math.max(height, rect2.height);
    2355          }
    2356        }
    2357        Insets insets = comp.getInsets();
    2358        Dimension dim = new Dimension(width + insets.left + insets.right + 5,
    2359                                      height + insets.top + insets.bottom);
    2360        comp.setPreferredSize(dim);
    2361      }catch(BadLocationException ble){
    2362        //this will work the next time around so it's safe to ignore it now
    2363      }
    2364    }
    2365    Border selectedBorder;
    2366    Border normalBorder;
    2367    JCheckBox visibleChk;
    2368    JTextPane textComponent;
    2369    Component spacer;
    2370  }//class NodeRenderer extends JPanel implements TreeCellRenderer
    2371
    2372  /**
    2373   * Displays an entry in the right hand side tree.
    2374   * <strong><a name="override">Implementation Note:</a></strong>
    2375   * This class overrides
    2376   * <code>revalidate</code>,
    2377   * <code>repaint</code>,
    2378   * and
    2379   * <code>firePropertyChange</code>
    2380   * solely to improve performance.
    2381   * If not overridden, these frequently called methods would execute code paths
    2382   * that are unnecessary for a tree cell renderer.
    2383   */
    2384/*
    2385  class NodeRenderer1 extends JPanel implements TreeCellRenderer{
    2386
    2387    public NodeRenderer1(){
    2388      visibleChk = new JCheckBox("",false);
    2389      visibleChk.setOpaque(false);
    2390      typeComponent = new JTextPane();
    2391      setComponent = new JTextPane();
    2392      selectedBorder = BorderFactory.createLineBorder(Color.blue);
    2393      normalBorder = BorderFactory.createEmptyBorder(1,1,1,1);
    2394
    2395      setPanel = new LazyJPanel();
    2396      setPanel.setOpaque(false);
    2397      setPanel.setLayout(new BoxLayout(setPanel, BoxLayout.X_AXIS));
    2398      setPanel.add(setComponent);
    2399      typePanel = new LazyJPanel();
    2400      typePanel.setOpaque(false);
    2401      typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.X_AXIS));
    2402      typePanel.add(visibleChk);
    2403      typePanel.add(typeComponent);
    2404    }
    2405
    2406    public Component getTreeCellRendererComponent(JTree tree,
    2407                                              Object value,
    2408                                              boolean selected,
    2409                                              boolean expanded,
    2410                                              boolean leaf,
    2411                                              int row,
    2412                                              boolean hasFocus){
    2413      JComponent renderer = null;
    2414      TypeData nData = (TypeData)
    2415                            ((DefaultMutableTreeNode)value).getUserObject();
    2416      if(nData != null){
    2417        if(nData.getType() != null) {
    2418          visibleChk.setSelected(nData.getVisible());
    2419          typeComponent.setSize(1000, 1000);
    2420          javax.swing.text.Document doc = typeComponent.getDocument();
    2421          try{
    2422            doc.remove(0, doc.getLength());
    2423            doc.insertString(0, nData.getTitle(), nData.getAttributes());
    2424          }catch(BadLocationException ble){
    2425            ble.printStackTrace();
    2426          }
    2427          setTextComponentSize(typeComponent);
    2428//          typePanel.removeAll();
    2429//          typePanel.add(visibleChk);
    2430//          typePanel.add(typeComponent);
    2431          renderer = typePanel;
    2432        }else{
    2433          setComponent.setSize(1000, 1000);
    2434          javax.swing.text.Document doc = setComponent.getDocument();
    2435          try{
    2436            doc.remove(0, doc.getLength());
    2437            doc.insertString(0, nData.getTitle(), nData.getAttributes());
    2438          }catch(BadLocationException ble){
    2439            ble.printStackTrace();
    2440          }
    2441          setTextComponentSize(setComponent);
    2442//          setPanel.removeAll();
    2443//          setPanel.add(setComponent);
    2444          renderer = setPanel;
    2445        }
    2446      }else{
    2447        setComponent.setSize(1000, 1000);
    2448        javax.swing.text.Document doc = setComponent.getDocument();
    2449        try{
    2450          doc.remove(0, doc.getLength());
    2451          doc.insertString(0, value.toString(), setComponent.getStyle("default"));
    2452        }catch(BadLocationException ble){
    2453          ble.printStackTrace();
    2454        }
    2455        setTextComponentSize(setComponent);
    2456//        setPanel.removeAll();
    2457//        setPanel.add(setComponent);
    2458        renderer = setPanel;
    2459      }
    2460      if(selected) renderer.setBorder(selectedBorder);
    2461      else renderer.setBorder(normalBorder);
    2462      return renderer;
    2463    }//public Component getTreeCellRendererComponent
    2464
    2465    protected void setTextComponentSize(JTextComponent comp){
    2466      try{
    2467        Rectangle rect = comp.modelToView(0);
    2468        int length = comp.getDocument().getLength();
    2469        if(length > 0){
    2470          Rectangle rect2 = comp.modelToView(length - 1);
    2471          if(rect2 != null){
    2472Out.pr("Rect2.x " + rect2.x);
    2473            //this mutates rect
    2474            rect = SwingUtilities.computeUnion(rect2.x, rect2.y, rect2.width,
    2475                                        rect2.height, rect);
    2476Out.prln("Rect.width " + rect.width);
    2477          }else{
    2478Out.prln("NULL size");
    2479          }
    2480        }
    2481        Insets insets = comp.getInsets();
    2482        Dimension dim = new Dimension(rect.width + insets.left + insets.right,
    2483                                      rect.height + insets.top + insets.bottom);
    2484        comp.setPreferredSize(dim);
    2485      }catch(BadLocationException ble){
    2486        ble.printStackTrace();
    2487      }
    2488    }
    2489
    2490    Border selectedBorder;
    2491    Border normalBorder;
    2492    JCheckBox visibleChk;
    2493    JTextPane setComponent;
    2494    JTextPane typeComponent;
    2495    JPanel setPanel;
    2496    JPanel typePanel;
    2497  }//class NodeRenderer extends JPanel implements TreeCellRenderer
    2498*/
    2499  /**
    2500   * Holds the GUI metadata for a given annotation type. An annotation type is
    2501   * uniquely identified by the name of its AnnotationSet and the name of the
    2502   * type.
    2503   * For the default annotation set of a document (which has no name) the
    2504   * &quot;&lt;Default&gt;&quot; value is used.
    2505   * The GUI metadata contains, amongst other things, the style used for
    2506   * highlighting the annotations of this type.
    2507   * These styles are cascading styles (there is a relation of inheritance
    2508   * between them) so the annotation type style inherits the characteristics
    2509   * from the style associated with the annotation set it belongs to.
    2510   *
    2511   * For eficiency reasons there are some intermediary styles between a parent
    2512   * and a child style that used for changing the display in one operation.
    2513   */
    2514  public class TypeData {
    2515
    2516    public TypeData(String set, String type, boolean visible){
    2517      this.set = set;
    2518      this.type = type;
    2519      this.visible = visible;
    2520      Map setMap = (Map)typeDataMap.get(set);
    2521      if(setMap == null){
    2522        setMap = new HashMap();
    2523        typeDataMap.put(set, setMap);
    2524      }
    2525      if(type == null) {
    2526        //this node represents a Set
    2527        style = textPane.addStyle(set, textPane.getStyle("default"));
    2528      } else {
    2529        style = textPane.addStyle(set + "." + type, textPane.getStyle(set));
    2530        StyleConstants.setBackground(style,
    2531                                        colGenerator.getNextColor().brighter());
    2532        //add an intermediary style that will be used for the actual display
    2533        textPane.addStyle("_" + set + "." + type, style);
    2534        //add the style that will be used for the actual display
    2535        textPane.addStyle("_" + set + "." + type + "_",
    2536                          textPane.getStyle("_" + set + "." + type));
    2537        setMap.put(type, this);
    2538      }
    2539    }
    2540
    2541    public String getSet() { return set;}
    2542
    2543    public void setSet(String set) {this.set = set;}
    2544
    2545    public String getType() {return type;}
    2546
    2547    public String getTitle() {return (type == null) ? set + " annotations" :
    2548                                                      type;}
    2549    public boolean getVisible() {return visible;}
    2550
    2551    public void setVisible(boolean isVisible) {
    2552      if(this.visible == isVisible) return;
    2553      this.visible = isVisible;
    2554      //this is most likely called from the SWING thread so we want to get
    2555      //out of here as quickly as possible. We'll start a new thread that will
    2556      //do all that needs doing
    2557      Runnable runnable = new Runnable() {
    2558        public void run() {
    2559          if(visible) {
    2560            //make the corresponding range visible
    2561            //update the annotations table
    2562            synchronized(data) {
    2563              range = new Range(set, type, data.size(),
    2564                                data.size() + annotations.size());
    2565              ranges.add(range);
    2566              data.addAll(annotations);
    2567              SwingUtilities.invokeLater(new Runnable() {
    2568                public void run() {
    2569                  annotationsTableModel.fireTableDataChanged();
    2570                }
    2571              });
    2572            }
    2573
    2574            //update the text display
    2575            Style actualStyle = textPane.getStyle("_" + set + "." + type);
    2576            actualStyle.setResolveParent(style);
    2577            showHighlights(annotations, textPane.getStyle("_" + set + "."
    2578                                                          + type + "_"));
    2579          } else {
    2580            //hide the corresponding range
    2581            //update the annotations table
    2582            Collections.sort(ranges);
    2583            Iterator rangesIter = ranges.iterator();
    2584            while(rangesIter.hasNext()) {
    2585              //find my range
    2586              Range aRange = (Range)rangesIter.next();
    2587              if(aRange == range){
    2588                rangesIter.remove();
    2589                int size = range.end - range.start;
    2590                //remove the elements from Data
    2591                data.subList(range.start, range.end).clear();
    2592                //shift back all the remaining ranges
    2593                while(rangesIter.hasNext()) {
    2594                  aRange = (Range)rangesIter.next();
    2595                  aRange.start -= size;
    2596                  aRange.end -= size;
    2597                }
    2598              }
    2599            }
    2600            range = null;
    2601            SwingUtilities.invokeLater(new Runnable() {
    2602              public void run() {
    2603                annotationsTableModel.fireTableDataChanged();
    2604              }
    2605            });
    2606            //update the text display
    2607            Style actualStyle = textPane.getStyle("_" + set + "." + type);
    2608            actualStyle.setResolveParent(textPane.getStyle("default"));
    2609          }//if(visible)
    2610        }//public void run()
    2611      };//Runnable runnable = new Runnable()
    2612      Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
    2613                                   runnable,
    2614                                   "AnnotationEditor4");
    2615      thread.setPriority(Thread.MIN_PRIORITY);
    2616      thread.start();
    2617    }//public void setVisible(boolean isVisible)
    2618
    2619    public AttributeSet getAttributes() { return style;}
    2620
    2621    public void setAttributes(AttributeSet newAttributes) {
    2622      style.removeAttributes(style.copyAttributes());
    2623      style.addAttributes(newAttributes);
    2624    }
    2625
    2626
    2627    public void setAnnotations(Set as) {
    2628      this.annotations = as;
    2629    }
    2630
    2631    public Set getAnnotations() {
    2632      return annotations;
    2633    }
    2634
    2635    public String toString() {return getTitle();}
    2636
    2637    private String set;
    2638    private String type;
    2639    private boolean visible;
    2640    private Style style;
    2641    private Set annotations = null;
    2642    private Range range = null;
    2643  }//class TypeData
    2644
    2645
    2646  /**
    2647   * Describes a range in the {@link #data} structure. A range is a bunch of
    2648   * annotations of the same type belonging to the same annotation set that
    2649   * are contiguous in the {@link #data} structure.
    2650   */
    2651  class Range implements Comparable {
    2652    public Range(String setName, String type, int start, int end) {
    2653      this.setName = setName;
    2654      this.type = type;
    2655      this.start = start;
    2656      this.end = end;
    2657    }
    2658
    2659    public String toString() {
    2660      return setName +  ", " + type + " (" + start + ", " + end + ")";
    2661    }
    2662
    2663    public int compareTo(Object other) {
    2664      if(other instanceof Range) return start - ((Range)other).start;
    2665      else throw new ClassCastException("Can't compare a " +
    2666                                         other.getClass() + " to a " +
    2667                                         getClass() + "!");
    2668    }
    2669
    2670    String setName;
    2671    String type;
    2672    int start;
    2673    int end;
    2674  }//class Range
    2675
    2676
    2677  /**
    2678   * All the events from the document or its annotation sets are handled by
    2679   * this inner class.
    2680   */
    2681  class EventsHandler implements gate.event.DocumentListener,
    2682                                 AnnotationSetListener{
    2683
    2684    public void annotationSetAdded(gate.event.DocumentEvent e) {
    2685      String setName = e.getAnnotationSetName();
    2686      AnnotationSet as = (setName == null ? document.getAnnotations() :
    2687                             document.getAnnotations(setName));
    2688
    2689      as.addAnnotationSetListener(this);
    2690      if(setName == null) setName = "Default";
    2691      TypeData setData = new TypeData(setName, null, false);
    2692      setData.setAnnotations(as);
    2693
    2694      SwingUtilities.invokeLater(new NodeAdder(setData));
    2695
    2696      ArrayList typesLst = new ArrayList(as.getAllTypes());
    2697      Collections.sort(typesLst);
    2698
    2699      Iterator typesIter = typesLst.iterator();
    2700      while(typesIter.hasNext()){
    2701        String type = (String)typesIter.next();
    2702        TypeData typeData = new TypeData(setName, type, false);
    2703        AnnotationSet sameType = as.get(type);
    2704        typeData.setAnnotations(sameType);
    2705
    2706        SwingUtilities.invokeLater(new NodeAdder(typeData));
    2707      }
    2708    }
    2709
    2710    public void annotationSetRemoved(gate.event.DocumentEvent e) {
    2711      //we access the GUI a lot here so we'll do everything from the
    2712      //Swing thread
    2713      SwingUtilities.invokeLater(
    2714                     new SetRemovedOperation(e.getAnnotationSetName()));
    2715    }//public void annotationSetRemoved(gate.event.DocumentEvent e)
    2716
    2717    public void annotationAdded(AnnotationSetEvent e) {
    2718      AnnotationSet set = (AnnotationSet)e.getSource();
    2719      String setName = set.getName();
    2720      if(setName == null) setName = "Default";
    2721      Annotation ann = e.getAnnotation();
    2722      String type = ann.getType();
    2723      TypeData tData = getTypeData(setName, type);
    2724
    2725      boolean tableChanged = false;
    2726      if(tData != null){
    2727//                tData.annotations.add(ann);
    2728        if(tData.getVisible()){
    2729          //1) update the table
    2730          data.add(tData.range.end, ann);
    2731          tData.range.end++;
    2732          Iterator rangesIter = ranges.
    2733                                subList(
    2734                                    ranges.indexOf(tData.range) + 1,
    2735                                        ranges.size()).
    2736                                iterator();
    2737          while(rangesIter.hasNext()){
    2738            Range aRange = (Range) rangesIter.next();
    2739            aRange.start++;
    2740            aRange.end++;
    2741          }//while(rangesIter.hasNext())
    2742          tableChanged = true;
    2743
    2744          //2) update the text
    2745          SwingUtilities.invokeLater(
    2746                         new HihglightsShower(ann,
    2747                                              textPane.getStyle(
    2748                                                "_" + setName + "." +
    2749                                                type + "_")));
    2750        }//if(tData.getVisible())
    2751      } else {
    2752        //new type
    2753        Map setMap = (Map)typeDataMap.get(setName);
    2754        if(setMap == null){
    2755          setMap = new HashMap();
    2756          typeDataMap.put(setName, setMap);
    2757        }
    2758        tData = new TypeData(setName, type, false);
    2759        tData.setAnnotations(set.get(type));
    2760        setMap.put(type, tData);
    2761
    2762        SwingUtilities.invokeLater(new NodeAdder(tData));
    2763
    2764
    2765      }//new type
    2766
    2767      if(tableChanged){
    2768        SwingUtilities.invokeLater(new Runnable() {
    2769          public void run(){
    2770            if(annotationsTableModel != null){
    2771              annotationsTableModel.fireTableDataChanged();
    2772            }
    2773          }
    2774        });
    2775      }//if(tableChanged)
    2776    }//public void annotationAdded(AnnotationSetEvent e)
    2777
    2778    public void annotationRemoved(AnnotationSetEvent e){
    2779      AnnotationSet set = (AnnotationSet)e.getSource();
    2780      String setName = set.getName();
    2781      if(setName == null) setName = "Default";
    2782      Annotation ann = e.getAnnotation();
    2783      String type = ann.getType();
    2784      TypeData tData = getTypeData(setName, type);
    2785      boolean tableChanged = false;
    2786
    2787      if(tData != null){
    2788//                tData.annotations.remove(ann);
    2789        if(tData.getVisible()){
    2790          //1) update the annotations table
    2791          data.remove(ann);
    2792          //shorten the range conatining the annotation
    2793          tData.range.end--;
    2794          //shift all the remaining ranges
    2795          Iterator rangesIter = ranges.
    2796                              subList(ranges.indexOf(tData.range) + 1,
    2797                              ranges.size()).
    2798                                iterator();
    2799          while(rangesIter.hasNext()){
    2800            Range aRange = (Range) rangesIter.next();
    2801            aRange.start--;
    2802            aRange.end--;
    2803          }//while(rangesIter.hasNext())
    2804          tableChanged = true;
    2805
    2806          //update the text -> hide the highlight
    2807          SwingUtilities.invokeLater(new HighlightsRemover(ann));
    2808        }//if(tData.getVisible())
    2809        //if this was the last annotation of this type remove the type node
    2810        if((tData.annotations.size() == 1 &&
    2811           tData.annotations.iterator().next() == ann) ||
    2812           tData.annotations.size() == 0){
    2813          //no more annotations of this type -> delete the node
    2814          //first find the set
    2815          DefaultMutableTreeNode node = (DefaultMutableTreeNode)
    2816            ((DefaultMutableTreeNode)stylesTreeRoot).getFirstChild();
    2817          while(node != null &&
    2818            !((TypeData)node.getUserObject()).getSet().equals(setName))
    2819            node = node.getNextSibling();
    2820          if(node != null && node.getChildCount() > 0){
    2821            node = (DefaultMutableTreeNode)node.getFirstChild();
    2822            while(node != null &&
    2823              !((TypeData)node.getUserObject()).getType().equals(type))
    2824              node = node.getNextSibling();
    2825            if(node != null){
    2826              SwingUtilities.invokeLater(new NodeRemover(node));
    2827            }
    2828          }
    2829          //remove the data for this type
    2830          Map setMap = (Map)typeDataMap.get(setName);
    2831          setMap.remove(tData.getType());
    2832        }//if(tData.getAnnotations().isEmpty())
    2833      }//if(tData != null)
    2834
    2835      if(tableChanged){
    2836        SwingUtilities.invokeLater(new Runnable() {
    2837          public void run(){
    2838            if(annotationsTableModel != null){
    2839              annotationsTableModel.fireTableDataChanged();
    2840            }
    2841          }
    2842        });
    2843      }//if(tableChanged)
    2844    }//public void annotationRemoved(AnnotationSetEvent e)
    2845
    2846    /**
    2847     * Helper class that removes one highlight corresponding to an annotation.
    2848     */
    2849    class HighlightsRemover implements Runnable{
    2850      HighlightsRemover(Annotation ann){
    2851        this.ann = ann;
    2852      }
    2853      public void run(){
    2854        int selStart = textPane.getSelectionStart();
    2855        int selEnd = textPane.getSelectionEnd();
    2856        textPane.select(ann.getStartNode().getOffset().intValue(),
    2857                        ann.getEndNode().getOffset().intValue());
    2858        textPane.setCharacterAttributes(
    2859                  textPane.getStyle("default"), true);
    2860        textPane.select(selStart, selEnd);
    2861      }
    2862      Annotation ann;
    2863    }//class HihglightsRemover implements Runnable
    2864
    2865    /**
    2866     * Helper class that highlights a given annotation with the specified style.
    2867     */
    2868    class HihglightsShower implements Runnable{
    2869      HihglightsShower(Annotation ann, Style style){
    2870        this.ann = ann;
    2871        this.style = style;
    2872      }
    2873      public void run(){
    2874        textPane.select(ann.getStartNode().getOffset().intValue(),
    2875                        ann.getEndNode().getOffset().intValue());
    2876        textPane.setCharacterAttributes(style, true);
    2877      }
    2878      Annotation ann;
    2879      Style style;
    2880    }//class HihglightsRemover implements Runnable
    2881
    2882    /**
    2883     * Helper class that removes one node from the types tree.
    2884     */
    2885    class NodeRemover implements Runnable{
    2886      NodeRemover(DefaultMutableTreeNode node){
    2887        this.node = node;
    2888      }
    2889      public void run(){
    2890        stylesTreeModel.removeNodeFromParent(node);
    2891      }
    2892      DefaultMutableTreeNode node;
    2893    }//class NodeRemover implements Runnable
    2894
    2895    /**
    2896     * Helper class that adds a specified tree node
    2897     */
    2898    class NodeAdder implements Runnable{
    2899      NodeAdder(TypeData tData){
    2900        this.tData = tData;
    2901      }
    2902      public void run(){
    2903        //create the new node
    2904        DefaultMutableTreeNode newNode =
    2905                  new DefaultMutableTreeNode(tData, tData.getType() == null);
    2906
    2907        //find its parent
    2908        DefaultMutableTreeNode node = null;
    2909        if(tData.getType() == null){
    2910          //set node
    2911          node = (DefaultMutableTreeNode)stylesTreeRoot;
    2912//System.out.println("Set node " + tData.getSet());
    2913        }else{
    2914//System.out.println("Type node " + tData.getSet() + ":" + tData.getType());
    2915          node = (DefaultMutableTreeNode)
    2916            ((DefaultMutableTreeNode)stylesTreeRoot).getFirstChild();
    2917          while(node != null &&
    2918            !((TypeData)node.getUserObject()).getSet().equals(tData.getSet()))
    2919            node = node.getNextSibling();
    2920        }
    2921
    2922        //we have to add typeNode to node
    2923        //find the right place
    2924        int i = 0;
    2925        if(tData.getType() == null){
    2926          while (i < node.getChildCount() &&
    2927                ((TypeData)
    2928                  ((DefaultMutableTreeNode)node.getChildAt(i)).
    2929                  getUserObject()
    2930                ).getSet().compareTo(tData.getSet())<0) i++;
    2931        }else{
    2932          while (i < node.getChildCount() &&
    2933                ((TypeData)
    2934                  ((DefaultMutableTreeNode)node.getChildAt(i)).
    2935                  getUserObject()
    2936                ).getType().compareTo(tData.getType())<0) i++;
    2937        }
    2938
    2939        //insert it!
    2940        stylesTreeModel.insertNodeInto(newNode, node, i);
    2941
    2942        if(tData.getType() == null){
    2943          //set node, expand it!
    2944          stylesTree.expandPath(new TreePath(new Object[]{stylesTreeRoot,
    2945                                                          newNode}));
    2946        }
    2947      }
    2948
    2949      TypeData tData;
    2950    }//class NodeAdder implements Runnable
    2951
    2952    /**
    2953     * Helper class that handles the removal of a named annotation set.
    2954     * This runnable should only be called from the Swing thread
    2955     */
    2956    class SetRemovedOperation implements Runnable{
    2957      SetRemovedOperation(String setName){
    2958        this.setName = setName;
    2959      }
    2960
    2961      public void run(){
    2962        //find the set node
    2963        Enumeration setNodesEnum = stylesTreeRoot.children();
    2964        DefaultMutableTreeNode setNode = null;
    2965        boolean done = false;
    2966        while(!done && setNodesEnum.hasMoreElements()){
    2967          setNode = (DefaultMutableTreeNode)setNodesEnum.nextElement();
    2968          done = ((TypeData)setNode.getUserObject()).getSet().equals(setName);
    2969        }
    2970
    2971        if(!((TypeData)setNode.getUserObject()).getSet().equals(setName)){
    2972          throw new GateRuntimeException(
    2973                "Could not find the tree node for the " + setName +
    2974                " annotation set!");
    2975        }
    2976
    2977        boolean tableChanged = false;
    2978        Enumeration typeNodesEnum = setNode.children();
    2979        while(typeNodesEnum.hasMoreElements()){
    2980          DefaultMutableTreeNode typeNode =
    2981            (DefaultMutableTreeNode)typeNodesEnum.nextElement();
    2982          TypeData tData = (TypeData)typeNode.getUserObject();
    2983          if(tData.getVisible()){
    2984            //1) update the annotations table
    2985            data.subList(tData.range.start, tData.range.end).clear();
    2986            //remove the range
    2987            int delta = tData.range.end - tData.range.start;
    2988            //1a)first shift all following ranges
    2989            Iterator rangesIter = ranges.
    2990                                subList(ranges.indexOf(tData.range) + 1,
    2991                                ranges.size()).
    2992                                  iterator();
    2993            while(rangesIter.hasNext()){
    2994              Range aRange = (Range) rangesIter.next();
    2995              aRange.start -= delta;
    2996              aRange.end -= delta;
    2997            }//while(rangesIter.hasNext())
    2998            //1b)now remove the range
    2999            ranges.remove(tData.range);
    3000            tableChanged = true;
    3001
    3002            //2)update the text
    3003            //hide the highlights
    3004
    3005            Iterator annIter = tData.getAnnotations().iterator();
    3006            while(annIter.hasNext()){
    3007              Annotation ann = (Annotation)annIter.next();
    3008              new HighlightsRemover(ann).run();
    3009            }//while(annIter.hasNext())
    3010          }//if(tData.getVisible())
    3011        }//while(typeNodesEnum.hasMoreElements())
    3012
    3013        if(tableChanged){
    3014          if(annotationsTableModel != null){
    3015            annotationsTableModel.fireTableDataChanged();
    3016          }
    3017        }//if(tableChanged)
    3018
    3019        //remove the node for the set
    3020        typeDataMap.remove(setName);
    3021        new NodeRemover(setNode).run();
    3022      }//public void run()
    3023
    3024      String setName;
    3025    }
    3026
    3027  }//class EventsHandler
    3028
    3029  /**
    3030   * This class handles the blinking for the selected annotations in the
    3031   * text display.
    3032   */
    3033  class SelectionBlinker implements Runnable{
    3034    public void run(){
    3035      synchronized(selectionHighlighter){
    3036        highlights = selectionHighlighter.getHighlights();
    3037      }
    3038
    3039
    3040      while(highlights != null && highlights.length > 0){
    3041        SwingUtilities.invokeLater(new Runnable(){
    3042          public void run(){
    3043            showHighlights();
    3044          }
    3045        });
    3046        try{
    3047          Thread.sleep(400);
    3048        }catch(InterruptedException ie){
    3049          ie.printStackTrace(Err.getPrintWriter());
    3050        }
    3051        SwingUtilities.invokeLater(new Runnable(){
    3052          public void run(){
    3053            hideHighlights();
    3054          }
    3055        });
    3056
    3057        try{
    3058          Thread.sleep(600);
    3059        }catch(InterruptedException ie){
    3060          ie.printStackTrace(Err.getPrintWriter());
    3061        }
    3062        synchronized(selectionHighlighter){
    3063          highlights = selectionHighlighter.getHighlights();
    3064        }
    3065      }//while we have highlights
    3066      //no more highlights; stop the thread by exiting run();
    3067      synchronized(selectionHighlighter){
    3068        thread = null;
    3069      }
    3070    }//run()
    3071
    3072    /**
    3073     * If there is no running thread then starts one and stores it in
    3074     * the <tt>thread</tt> member.
    3075     */
    3076    public synchronized void testAndStart(){
    3077      synchronized(selectionHighlighter){
    3078        if(thread == null){
    3079          thread  = new Thread(Thread.currentThread().getThreadGroup(),
    3080                               this, "AnnotationEditor2");
    3081          thread.setPriority(Thread.MIN_PRIORITY);
    3082          thread.start();
    3083        }
    3084      }
    3085    }
    3086
    3087    protected void showHighlights(){
    3088      actualHighlights.clear();
    3089      try{
    3090        for(int i = 0; i < highlights.length; i++){
    3091          actualHighlights.add(highlighter.addHighlight(
    3092                                   highlights[i].getStartOffset(),
    3093                                   highlights[i].getEndOffset(),
    3094                                   highlights[i].getPainter()));
    3095        }
    3096      }catch(BadLocationException ble){
    3097        ble.printStackTrace(Err.getPrintWriter());
    3098      }
    3099    }
    3100
    3101    protected void hideHighlights(){
    3102      Iterator hIter = actualHighlights.iterator();
    3103      while(hIter.hasNext()) highlighter.removeHighlight(hIter.next());
    3104    }
    3105
    3106    ArrayList actualHighlights = new ArrayList();
    3107    Thread thread;
    3108    Highlighter.Highlight[] highlights;
    3109  }//class SelectionBlinker implements Runnable
    3110
    3111  /**
    3112   * Fixes the <a
    3113   * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
    3114   * 4406598 bug</a> in swing text components.
    3115   * The bug consists in the fact that the Background attribute is ignored by
    3116   * the text component whent it is defined in a style from which the current
    3117   * style inherits.
    3118   */
    3119  public class CustomLabelView extends javax.swing.text.LabelView {
    3120    public CustomLabelView(Element elem) {
    3121      super(elem);
    3122    }
    3123
    3124    public Color getBackground() {
    3125      AttributeSet attr = getAttributes();
    3126      if (attr != null) {
    3127        javax.swing.text.Document d = super.getDocument();
    3128        if (d instanceof StyledDocument){
    3129          StyledDocument doc = (StyledDocument) d;
    3130          return doc.getBackground(attr);
    3131        }else{
    3132          return null;
    3133        }
    3134      }
    3135      return null;
    3136    }
    3137  }
    3138
    3139  /**
    3140   * The popup menu items used to select annotations at right click.
    3141   * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu
    3142   * item also highlits the annotation which it would select if pressed.
    3143   */
    3144  protected class SelectAnnotationPopupItem extends JMenuItem {
    3145    public SelectAnnotationPopupItem(Annotation ann, String setName) {
    3146      super(ann.getType());
    3147      setToolTipText("<html><b>Features:</b><br>" +
    3148                     (ann.getFeatures() == null ? "" :
    3149                     ann.getFeatures().toString()) + "</html>");
    3150      annotation = ann;
    3151      start = ann.getStartNode().getOffset().intValue();
    3152      end = ann.getEndNode().getOffset().intValue();
    3153      set = setName;
    3154      this.addMouseListener(new MouseAdapter() {
    3155        public void mouseEntered(MouseEvent e) {
    3156          try {
    3157            highlight = highlighter.addHighlight(start, end,
    3158                                            DefaultHighlighter.DefaultPainter);
    3159          }catch(BadLocationException ble){
    3160            throw new GateRuntimeException(ble.toString());
    3161          }
    3162        }
    3163
    3164        public void mouseExited(MouseEvent e) {
    3165          if(highlight != null){
    3166            highlighter.removeHighlight(highlight);
    3167            highlight = null;
    3168          }
    3169        }
    3170      });
    3171
    3172      this.addActionListener(new ActionListener() {
    3173        public void actionPerformed(ActionEvent e) {
    3174          Runnable runnable = new Runnable(){
    3175            public void run(){
    3176              if(highlight != null){
    3177                highlighter.removeHighlight(highlight);
    3178                highlight = null;
    3179              }
    3180             selectAnnotation(set, annotation);
    3181            }
    3182          };
    3183          Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
    3184                                     runnable,
    3185                                     "AnnotationEditor5");
    3186          thread.start();
    3187        }
    3188      });
    3189    }
    3190
    3191    int start;
    3192    int end;
    3193    String set;
    3194    Annotation annotation;
    3195    Object highlight;
    3196  }
    3197
    3198  protected class DeleteSelectedAnnotationsAction extends AbstractAction {
    3199    public DeleteSelectedAnnotationsAction(JComponent source){
    3200      super("Delete selected annotations");
    3201      this.source = source;
    3202    }
    3203
    3204    public void actionPerformed(ActionEvent evt){
    3205      if(source == annotationsTable){
    3206        //collect the list of annotations to be removed
    3207        //maps from set name to list of annotations to be removed
    3208        Map annotationsBySet = new HashMap();
    3209        int[] rows = annotationsTable.getSelectedRows();
    3210        String setName;
    3211        for(int i = 0; i < rows.length; i++){
    3212          int row = rows[i];
    3213          //find the annotation
    3214          Annotation ann = (Annotation)annotationsTable.
    3215                              getModel().getValueAt(row, -1);
    3216          //find the annotation set
    3217          setName = (String)annotationsTable.getModel().
    3218                                                      getValueAt(row, 1);
    3219          java.util.List existingList = (java.util.List)
    3220                                        annotationsBySet.get(setName);
    3221          if(existingList == null){
    3222            existingList = new ArrayList();
    3223            annotationsBySet.put(setName, existingList);
    3224          }
    3225          existingList.add(ann);
    3226        }//for(int i = 0; i < rows.length; i++)
    3227        //remove the collected annotations
    3228        Iterator setsIter = annotationsBySet.keySet().iterator();
    3229        while(setsIter.hasNext()){
    3230          setName = (String)setsIter.next();
    3231          AnnotationSet set = setName.equals("Default")?
    3232                              document.getAnnotations() :
    3233                              document.getAnnotations(setName);
    3234          set.removeAll((java.util.List)annotationsBySet.get(setName));
    3235        }//while(setsIter.hasNext())
    3236      }else if(source == stylesTree){
    3237        TreePath[] paths = stylesTree.getSelectionPaths();
    3238        for(int i = 0; i < paths.length; i++){
    3239          TypeData tData = (TypeData)((DefaultMutableTreeNode)
    3240                            paths[i].getLastPathComponent()).getUserObject();
    3241          String setName = tData.getSet();
    3242          if(tData.getType() == null){
    3243            //set node
    3244            if(setName.equals("Default")){
    3245              JOptionPane.showMessageDialog(
    3246                DocumentEditor.this,
    3247                "The default annotation set cannot be deleted!\n" +
    3248                "It will only be cleared...",
    3249                "Gate", JOptionPane.ERROR_MESSAGE);
    3250              document.getAnnotations().clear();
    3251            }else{
    3252              document.removeAnnotationSet(setName);
    3253            }
    3254          }else{
    3255            //type node
    3256            if(!setName.equals("Default") &&
    3257               !document.getNamedAnnotationSets().containsKey(setName)){
    3258              //the set for this type has already been removed completely
    3259              //nothing more do (that's nice :) )
    3260              return;
    3261            }
    3262            AnnotationSet set = setName.equals("Default") ?
    3263                                document.getAnnotations() :
    3264                                document.getAnnotations(setName);
    3265            if(set != null){
    3266              AnnotationSet subset = set.get(tData.getType());
    3267              if(subset != null) set.removeAll(new ArrayList(subset));
    3268            }//if(set != null)
    3269          }//type node
    3270        }//for(int i = 0; i < paths.length; i++)
    3271      }//else if(source == stylesTree)
    3272    }//public void actionPerformed(ActionEvent evt)
    3273    JComponent source;
    3274  }//protected class DeleteSelectedAnnotationsAction
    3275
    3276  /**
    3277   * The action that is fired when the user wants to edit an annotation.
    3278   * This will show a {@link gate.gui.AnnotationEditDialog} to allow the user
    3279   * to do the editing.
    3280   */
    3281  protected class DumpAsXmlAction extends AbstractAction{
    3282    private Set annotationsToDump = null;
    3283
    3284    /** Constructs an DumpAsXmlAction from an annotation and a set*/
    3285    public DumpAsXmlAction(){
    3286      super("Dump as XML & preserve format");
    3287    }// EditAnnotationAction()
    3288
    3289    /** This method takes care of how the dumping is done*/
    3290    public void actionPerformed(ActionEvent e){
    3291      Runnable runableAction = new Runnable(){
    3292        public void run(){
    3293          JFileChooser fileChooser = MainFrame.getFileChooser();
    3294          File selectedFile = null;
    3295
    3296          fileChooser.setMultiSelectionEnabled(false);
    3297          fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
    3298          fileChooser.setDialogTitle("Select document to save ...");
    3299          fileChooser.setSelectedFiles(null);
    3300
    3301          int res = fileChooser.showDialog(DocumentEditor.this, "Save");
    3302          if(res == JFileChooser.APPROVE_OPTION){
    3303            selectedFile = fileChooser.getSelectedFile();
    3304            fileChooser.setCurrentDirectory(fileChooser.getCurrentDirectory());
    3305            if(selectedFile == null) return;
    3306            if (myHandle!= null)
    3307              myHandle.statusChanged("Please wait while dumping as XML and"+
    3308              " preserving the format to " + selectedFile.toString() + " ...");
    3309            // This method construct a set with all annotations that need to be
    3310            // dupmped as Xml. If the set is null then only the original markups
    3311            // are dumped.
    3312            constructAnnotationsToDump();
    3313            try{
    3314              // Prepare to write into the xmlFile using UTF-8 encoding
    3315              OutputStreamWriter writer = new OutputStreamWriter(
    3316                                    new FileOutputStream(selectedFile),"UTF-8");
    3317
    3318              // Write (test the toXml() method)
    3319              // This Action is added only when a gate.Document is created.
    3320              // So, is for sure that the resource is a gate.Document
    3321              writer.write(document.toXml(annotationsToDump));
    3322              writer.flush();
    3323              writer.close();
    3324            } catch (Exception ex){
    3325              ex.printStackTrace(Out.getPrintWriter());
    3326            }// End try
    3327            if (myHandle!= null)
    3328              myHandle.statusChanged("Finished dumping into the "+
    3329              "file : " + selectedFile.toString());
    3330          }// End if
    3331        }// End run()
    3332      };// End Runnable
    3333      Thread thread = new Thread(runableAction, "");
    3334      thread.setPriority(Thread.MIN_PRIORITY);
    3335      thread.start();
    3336    }//public void actionPerformed(ActionEvent e)
    3337
    3338    /** This method constructs a set containing all annotation that user wants
    3339      *  to dump as XML
    3340      */
    3341    private void constructAnnotationsToDump(){
    3342      // Read the selected annotations and insert them into a set
    3343      int[] rows = annotationsTable.getSelectedRows();
    3344      if (rows.length > 0)
    3345        annotationsToDump = new HashSet();
    3346      for(int i = 0; i < rows.length; i++){
    3347        int row = rows[i];
    3348        //Find an annotation and add it to the annotationsToDump set.
    3349        Annotation ann = (Annotation)annotationsTable.
    3350                            getModel().getValueAt(row, -1);
    3351        annotationsToDump.add(ann);
    3352      }// End for
    3353    }// constructAnnotationsToDump()
    3354  }//class DumpAsXmlAction
    3355
    3356  protected class PrintAction extends AbstractAction{
    3357    public PrintAction(){
    3358      super("Print");
    3359    }// EditAnnotationAction()
    3360
    3361    /** This method takes care of how the dumping is done*/
    3362    public void actionPerformed(ActionEvent e){
    3363      PrinterJob printerJob = PrinterJob.getPrinterJob();
    3364
    3365      if (printerJob.printDialog()) {
    3366        try{
    3367          PageFormat pageFormat = printerJob.pageDialog(printerJob.defaultPage());
    3368          Printable printable = new Printable(){
    3369            public int print(Graphics graphics, PageFormat pageFormat,
    3370                             int pageIndex)throws PrinterException{
    3371              if(pageIndex == 0){
    3372                textScroll.getViewport().printAll(graphics);
    3373                return Printable.PAGE_EXISTS;
    3374              }else return Printable.NO_SUCH_PAGE;
    3375            }
    3376          };
    3377          printerJob.setPrintable(printable , pageFormat);
    3378          printerJob.print();
    3379        }catch(Exception ex) {
    3380          ex.printStackTrace();
    3381        }
    3382      }
    3383    }
    3384  }
    3385
    3386
    3387  /**
    3388   * The action that is fired when the user wants to edit an annotation.
    3389   * It will build a dialog containing all the valid annotation editors.
    3390   */
    3391  protected class EditAnnotationAction extends AbstractAction {
    3392    public EditAnnotationAction(AnnotationSet set, Annotation annotation){
    3393      super("Edit");
    3394      this.set = set;
    3395      this.annotation = annotation;
    3396      putValue(SHORT_DESCRIPTION, "Edits the annotation");
    3397    }
    3398
    3399    public void actionPerformed(ActionEvent e){
    3400      //get the list of editors
    3401      java.util.List specificEditors = Gate.getCreoleRegister().
    3402                                       getAnnotationVRs(annotation.getType());
    3403      java.util.List genericEditors = Gate.getCreoleRegister().
    3404                                      getAnnotationVRs();
    3405      //create the GUI
    3406      JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
    3407      //add all the specific editors
    3408      Iterator editorIter = specificEditors.iterator();
    3409      while(editorIter.hasNext()){
    3410        String editorType = (String)editorIter.next();
    3411        //create the editor
    3412        AnnotationVisualResource editor;
    3413        try{
    3414          editor = (AnnotationVisualResource)
    3415                                          Factory.createResource(editorType);
    3416          JScrollPane scroller = new JScrollPane((Component)editor);
    3417          scroller.setPreferredSize(((Component) editor).getPreferredSize());
    3418          tabbedPane.add(scroller,
    3419                      ((ResourceData)Gate.getCreoleRegister().get(editorType)).
    3420                                                                getName()
    3421
    3422                      );
    3423          editor.setTarget(set);
    3424          editor.setAnnotation(annotation);
    3425        }catch(ResourceInstantiationException rie){
    3426          rie.printStackTrace(Err.getPrintWriter());
    3427        }
    3428      }
    3429
    3430      //add all the generic editors
    3431      editorIter = genericEditors.iterator();
    3432      while(editorIter.hasNext()){
    3433        String editorType = (String)editorIter.next();
    3434        //create the editor
    3435        AnnotationVisualResource editor;
    3436        try{
    3437          editor  = (AnnotationVisualResource)
    3438                                          Factory.createResource(editorType);
    3439          if(editor.canDisplayAnnotationType(annotation.getType())){
    3440            editor.setTarget(set);
    3441            editor.setAnnotation(annotation);
    3442            tabbedPane.add(new JScrollPane((Component)editor),
    3443                           ((ResourceData)Gate.getCreoleRegister().
    3444                                              get(editorType)).getName());
    3445          }
    3446        }catch(ResourceInstantiationException rie){
    3447          rie.printStackTrace(Err.getPrintWriter());
    3448        }
    3449
    3450      }
    3451
    3452      //show the modal dialog until the data is OK or the user cancels
    3453      boolean allOK = false;
    3454      while(!allOK){
    3455        if(OkCancelDialog.showDialog(DocumentEditor.this,
    3456                                     tabbedPane,
    3457                                     "Edit Annotation")){
    3458          try{
    3459            ((AnnotationVisualResource)((JScrollPane)tabbedPane.
    3460                                        getSelectedComponent()).getViewport().
    3461                                                                getView()
    3462             ).okAction();
    3463             allOK = true;
    3464          }catch(GateException ge){
    3465            JOptionPane.showMessageDialog(
    3466              DocumentEditor.this,
    3467              "There was an error:\n" +
    3468              ge.toString(),
    3469              "Gate", JOptionPane.ERROR_MESSAGE);
    3470//            ge.printStackTrace(Err.getPrintWriter());
    3471            allOK = false;
    3472          }
    3473        }else{
    3474          if (OkCancelDialog.userHasPressedCancel)
    3475            try{
    3476              ((AnnotationVisualResource)((JScrollPane)tabbedPane.
    3477                                        getSelectedComponent()).getViewport().
    3478                                                                getView()
    3479              ).cancelAction();
    3480               allOK = true;
    3481            } catch(GateException ge){
    3482              JOptionPane.showMessageDialog(
    3483                DocumentEditor.this,
    3484                "There was an error:\n" +
    3485                ge.toString(),
    3486                "Gate", JOptionPane.ERROR_MESSAGE);
    3487              allOK = false;
    3488            }
    3489          allOK = true;
    3490        }
    3491      }//while(!allOK)
    3492    }//public void actionPerformed(ActionEvent e)
    3493
    3494    protected AnnotationSet set;
    3495    protected Annotation annotation;
    3496  }//class EditAnnotationAction
    3497
    3498  /**
    3499   * The action that is fired when the user wants to create a new annotation.
    3500   * It will build a dialog containing all the valid annotation editors.
    3501   */
    3502  class NewAnnotationAction extends AbstractAction{
    3503    public NewAnnotationAction(AnnotationSet set,
    3504                               Long startOffset,
    3505                               Long endOffset){
    3506      super("New annotation");
    3507      putValue(SHORT_DESCRIPTION, "Creates a new annotation");
    3508      this.set = set;
    3509      this.startOffset = startOffset;
    3510      this.endOffset = endOffset;
    3511      this.type = null;
    3512    }
    3513
    3514    public NewAnnotationAction(AnnotationSet set, String type,
    3515                               Long startOffset, Long endOffset){
    3516      super("New \"" + type + "\" annotation");
    3517      putValue(SHORT_DESCRIPTION, "Creates a new annotation of type \"" +
    3518                                  type + "\"");
    3519      this.set = set;
    3520      this.startOffset = startOffset;
    3521      this.endOffset = endOffset;
    3522      this.type = type;
    3523    }
    3524
    3525    public void actionPerformed(ActionEvent e){
    3526      if(set == null){
    3527        //get the name from the user
    3528        String setName = JOptionPane.showInputDialog(
    3529              DocumentEditor.this,
    3530              "Please provide a name for the new annotation set",
    3531              "Gate", JOptionPane.QUESTION_MESSAGE);
    3532        if(setName == null) return;
    3533        this.set = document.getAnnotations(setName);
    3534      }
    3535      //get the lists of editors
    3536      java.util.List specificEditors;
    3537      if(type != null) specificEditors = Gate.getCreoleRegister().
    3538                                         getAnnotationVRs(type);
    3539      else specificEditors = new ArrayList();
    3540
    3541      java.util.List genericEditors = Gate.getCreoleRegister().
    3542                                      getAnnotationVRs();
    3543      //create the GUI
    3544      JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
    3545      //add all the specific editors
    3546      Iterator editorIter = specificEditors.iterator();
    3547      while(editorIter.hasNext()){
    3548        String editorType = (String)editorIter.next();
    3549        //create the editor
    3550        AnnotationVisualResource editor;
    3551        try{
    3552          editor = (AnnotationVisualResource)
    3553                                          Factory.createResource(editorType);
    3554          tabbedPane.add(new JScrollPane((Component)editor),
    3555                        ((ResourceData)Gate.getCreoleRegister().get(editorType)).
    3556                                                                getName());
    3557          editor.setTarget(set);
    3558          editor.setSpan(startOffset, endOffset, type);
    3559
    3560        }catch(ResourceInstantiationException rie){
    3561          rie.printStackTrace(Err.getPrintWriter());
    3562        }
    3563      }
    3564
    3565      //add all the generic editors
    3566      editorIter = genericEditors.iterator();
    3567      while(editorIter.hasNext()){
    3568        String editorType = (String)editorIter.next();
    3569        //create the editor
    3570        AnnotationVisualResource editor;
    3571        try{
    3572          editor  = (AnnotationVisualResource)
    3573                                          Factory.createResource(editorType);
    3574
    3575          if(type == null ||
    3576             (type != null && editor.canDisplayAnnotationType(type))){
    3577            editor.setTarget(set);
    3578            editor.setSpan(startOffset, endOffset, type);
    3579            tabbedPane.add(new JScrollPane((Component)editor),
    3580                           ((ResourceData)Gate.getCreoleRegister().
    3581                                              get(editorType)).getName());
    3582          }
    3583        }catch(ResourceInstantiationException rie){
    3584          rie.printStackTrace(Err.getPrintWriter());
    3585        }
    3586
    3587      }
    3588
    3589      //show the modal dialog until the data is OK or the user cancels
    3590      boolean allOK = false;
    3591      while(!allOK){
    3592        if(OkCancelDialog.showDialog(DocumentEditor.this,
    3593                                     tabbedPane, "Edit Annotation")){
    3594          try{
    3595            ((AnnotationVisualResource)((JScrollPane)tabbedPane.
    3596                                        getSelectedComponent()).getViewport().
    3597                                                                getView()
    3598             ).okAction();
    3599             allOK = true;
    3600          }catch(GateException ge){
    3601            JOptionPane.showMessageDialog(
    3602              DocumentEditor.this,
    3603              "There was an error:\n" +
    3604              ge.toString(),
    3605              "Gate", JOptionPane.ERROR_MESSAGE);
    3606//            ge.printStackTrace(Err.getPrintWriter());
    3607            allOK = false;
    3608          }
    3609        }else{
    3610          allOK = true;
    3611        }
    3612      }//while(!allOK)
    3613
    3614
    3615    }//public void actionPerformed(ActionEvent e)
    3616
    3617    AnnotationSet set;
    3618    Long startOffset;
    3619    Long endOffset;
    3620    String type;
    3621  }//class NewAnnotationAction extends AbstractAction
    3622
    3623  /**
    3624   * Fixes the <a
    3625   * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
    3626   * 4406598 bug</a> in swing text components.
    3627   * The bug consists in the fact that the Background attribute is ignored by
    3628   * the text component whent it is defined in a style from which the current
    3629   * style inherits.
    3630   */
    3631  public class CustomStyledEditorKit extends StyledEditorKit{
    3632    private final ViewFactory defaultFactory = new CustomStyledViewFactory();
    3633    public ViewFactory getViewFactory() {
    3634      return defaultFactory;
    3635    }
    3636
    3637    /**
    3638      * Inserts content from the given stream, which will be
    3639      * treated as plain text.
    3640      * This insertion is done without checking \r or \r \n sequence.
    3641      * It takes the text from the Reader and place it into Document at position
    3642      * pos
    3643      */
    3644    public void read(Reader in, javax.swing.text.Document doc, int pos)
    3645                throws IOException, BadLocationException {
    3646
    3647      char[] buff = new char[65536];
    3648      int charsRead = 0;
    3649      while ((charsRead = in.read(buff, 0, buff.length)) != -1) {
    3650            doc.insertString(pos, new String(buff, 0, charsRead), null);
    3651            pos += charsRead;
    3652      }// while
    3653    }// read
    3654  }
    3655
    3656  /**
    3657   * Fixes the <a
    3658   * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
    3659   * 4406598 bug</a> in swing text components.
    3660   * The bug consists in the fact that the Background attribute is ignored by
    3661   * the text component whent it is defined in a style from which the current
    3662   * style inherits.
    3663   */
    3664  public class CustomStyledViewFactory implements ViewFactory{
    3665    public View create(Element elem) {
    3666      String kind = elem.getName();
    3667      if (kind != null) {
    3668        if (kind.equals(AbstractDocument.ContentElementName)) {
    3669          return new CustomLabelView(elem);
    3670        }else if (kind.equals(AbstractDocument.ParagraphElementName)) {
    3671          return new ParagraphView(elem);
    3672        }else if (kind.equals(AbstractDocument.SectionElementName)) {
    3673          return new BoxView(elem, View.Y_AXIS);
    3674        }else if (kind.equals(StyleConstants.ComponentElementName)) {
    3675          return new ComponentView(elem);
    3676        }else if (kind.equals(StyleConstants.IconElementName)) {
    3677          return new IconView(elem);
    3678        }
    3679      }
    3680      // default to text display
    3681      return new CustomLabelView(elem);
    3682    }
    3683  }
    3684  }//class AnnotationEditor

| DocumentEditor |