1   /*
2    *  AnnotationDiff.java
3    *
4    *  Copyright (c) 1998-2004, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Cristian URSU, 27/Oct/2000
12   *
13   *  $Id: AnnotationDiff.java,v 1.49 2004/07/21 17:10:02 akshay Exp $
14   */
15  
16  package gate.annotation;
17  
18  import java.awt.*;
19  import java.text.NumberFormat;
20  import java.util.*;
21  
22  import javax.swing.*;
23  import javax.swing.table.*;
24  
25  import gate.*;
26  import gate.creole.*;
27  import gate.swing.XJTable;
28  import gate.util.*;
29  
30  /**
31    * This class compare two annotation sets on annotation type given by the
32    * AnnotationSchema object. It also deals with graphic representation of the
33    * result.
34    */
35  public class AnnotationDiff extends AbstractVisualResource
36    implements  Scrollable{
37  
38    // number of pixels to be used as increment by scroller
39    protected int maxUnitIncrement = 10;
40  
41    /** Debug flag */
42    private static final boolean DEBUG = false;
43  
44    /** This document contains the key annotation set which is taken as reference
45     *  in comparison*/
46    private Document keyDocument = null;
47  
48    /** The name of the annotation set. If is null then the default annotation set
49      * will be considered.
50      */
51    private String keyAnnotationSetName = null;
52  
53    /** This document contains the response annotation set which is being
54      * compared against the key annotation set.
55      */
56    private Document responseDocument = null;
57  
58    /** The name of the annotation set. If is null then the default annotation set
59      * will be considered.
60      */
61    private String responseAnnotationSetName = null;
62  
63    /** The name of the annotation set considered in calculating FalsePozitive.
64      * If is null then the default annotation set will be considered.
65      */
66    private String responseAnnotationSetNameFalsePoz = null;
67  
68    /** The annotation schema object used to get the annotation name*/
69    private AnnotationSchema annotationSchema = null;
70  
71    /** A set of feature names bellonging to annotations from keyAnnotList
72      * used in isCompatible() and isPartiallyCompatible() methods
73      */
74    private Set keyFeatureNamesSet = null;
75  
76    /** The precision strict value (see NLP Information Extraction)*/
77    private double precisionStrict = 0.0;
78    /** The precision lenient value (see NLP Information Extraction)*/
79    private double precisionLenient = 0.0;
80    /** The precision average value (see NLP Information Extraction)*/
81    private double precisionAverage = 0.0;
82  
83    /** The Recall strict value (see NLP Information Extraction)*/
84    private double recallStrict = 0.0;
85    /** The Recall lenient value (see NLP Information Extraction)*/
86    private double recallLenient = 0.0;
87    /** The Recall average value (see NLP Information Extraction)*/
88    private double recallAverage = 0.0;
89  
90    /** The False positive strict (see NLP Information Extraction)*/
91    private double falsePositiveStrict = 0.0;
92    /** The False positive lenient (see NLP Information Extraction)*/
93    private double falsePositiveLenient = 0.0;
94    /** The False positive average (see NLP Information Extraction)*/
95    private double falsePositiveAverage = 0.0;
96  
97    /** The F-measure strict (see NLP Information Extraction)*/
98    private double fMeasureStrict = 0.0;
99    /** The F-measure lenient (see NLP Information Extraction)*/
100   private double fMeasureLenient = 0.0;
101   /** The F-measure average (see NLP Information Extraction)*/
102   private double fMeasureAverage = 0.0;
103   /** The weight used in F-measure (see NLP Information Extraction)*/
104   public  static double weight = 0.5;
105 
106   /**  This string represents the type of annotations used to play the roll of
107     *  total number of words needed to calculate the False Positive.
108     */
109   private String annotationTypeForFalsePositive = null;
110 
111   /** A number formater for displaying precision and recall*/
112   protected static NumberFormat formatter = NumberFormat.getInstance();
113 
114   /** The components that will stay into diffPanel*/
115   private XJTable diffTable = null;
116 
117   /** Used to represent the result of diff. See DiffSetElement class.*/
118   private Set diffSet = null;
119 
120   /** This field is used in doDiff() and detectKeyType() methods and holds all
121    *  partially correct keys */
122   private Set keyPartiallySet = null;
123   /** This field is used in doDiff() and detectResponseType() methods*/
124   private Set responsePartiallySet = null;
125 
126   /** This list is created from keyAnnotationSet at init() time*/
127   private java.util.List keyAnnotList = null;
128   /** This list is created from responseAnnotationSet at init() time*/
129   private java.util.List responseAnnotList = null;
130 
131   /** This field indicates wheter or not the annot diff should run int the text
132    *  mode*/
133   private boolean textMode = false;
134 
135   /**  Field designated to represent the max nr of annot types and coolors for
136     *  each type
137     **/
138   public static final int MAX_TYPES = 5;
139   /** A default type when all annotation are the same represented by White color*/
140   public static final int DEFAULT_TYPE = 0;
141   /** A correct type when all annotation are corect represented by Green color*/
142   public static final int CORRECT_TYPE = 1;
143   /** A partially correct type when all annotation are corect represented
144    *  by Blue color*/
145   public static final int PARTIALLY_CORRECT_TYPE = 2;
146   /** A spurious type when annotations in response were not present in key.
147    *  Represented by Red color*/
148   public static final int SPURIOUS_TYPE = 3;
149   /** A missing type when annotations in key were not present in response
150    *  Represented by Yellow color*/
151   public static final int MISSING_TYPE = 4;
152 
153   /** Red used for SPURIOUS_TYPE*/
154   private  final Color RED = new Color(255,173,181);
155   /** Green used for CORRECT_TYPE*/
156   private  final Color GREEN = new Color(173,255,214);
157   /** White used for DEFAULT_TYPE*/
158   private  final Color WHITE = new Color(255,255,255);
159   /** Blue used for PARTIALLY_CORRECT_TYPE*/
160   private  final Color BLUE = new Color(173,215,255);
161   /** Yellow used for MISSING_TYPE*/
162   private  final Color YELLOW = new Color(255,231,173);
163 
164   /** Used in DiffSetElement to represent an empty raw in the table*/
165   private final int NULL_TYPE = -1;
166   /** Used in some setForeground() methods*/
167   private  final Color BLACK = new Color(0,0,0);
168   /** The array holding the colours according to the annotation types*/
169   private Color colors[] = new Color[MAX_TYPES];
170 
171   /** A scroll for the AnnotDiff's table*/
172   private JScrollPane scrollPane = null;
173 
174   /** Used to store the no. of annotations from response,identified as belonging
175     * to one of the previous types.
176     */
177   private long typeCounter[] = new long[MAX_TYPES];
178 
179 
180   private gate.util.AnnotationDiffer annotDiffer;
181 
182   /** Constructs a AnnotationDif*/
183   public AnnotationDiff(){
184     annotDiffer = new AnnotationDiffer();
185   } //AnnotationDiff
186 
187   /** Sets the annotation type needed to calculate the falsePossitive measure
188     * @param anAnnotType is the annotation type needed to calculate a special
189     *  mesure called falsePossitive. Usualy the value is "token", but it can be
190     *  any other string with the same semantic.
191     */
192   public void setAnnotationTypeForFalsePositive(String anAnnotType){
193     annotationTypeForFalsePositive = anAnnotType;
194   } // setAnnotationTypeForFalsePositive
195 
196   /** Gets the annotation type needed to calculate the falsePossitive measure
197     * @return annotation type needed to calculate a special
198     * mesure called falsePossitive.
199     */
200   public String getAnnotationTypeForFalsePositive(){
201     return annotationTypeForFalsePositive;
202   } // getAnnotationTypeForFalsePositive
203 
204   /** Sets the keyDocument in AnnotDiff
205     * @param aKeyDocument The GATE document used as a key in annotation diff.
206     */
207   public void setKeyDocument(Document aKeyDocument) {
208     keyDocument = aKeyDocument;
209   } // setKeyDocument
210 
211   /** @return the keyDocument used in AnnotDiff process */
212   public Document getKeyDocument(){
213     return keyDocument;
214   } // getKeyDocument
215 
216   /** Sets the keyAnnotationSetName in AnnotDiff
217     * @param aKeyAnnotationSetName The name of the annotation set from the
218     * keyDocument.If aKeyAnnotationSetName is null then the default annotation
219     * set will be used.
220     */
221   public void setKeyAnnotationSetName(String aKeyAnnotationSetName){
222     keyAnnotationSetName = aKeyAnnotationSetName;
223   } // setKeyAnnotationSetName();
224 
225   /** Gets the keyAnnotationSetName.
226     * @return The name of the keyAnnotationSet used in AnnotationDiff. If
227     * returns null then the the default annotation set will be used.
228     */
229   public String getKeyAnnotationSetName(){
230     return keyAnnotationSetName;
231   } // getKeyAnnotationSetName()
232 
233   /** Sets the keyFeatureNamesSet in AnnotDiff.
234     * @param aKeyFeatureNamesSet a set containing the feature names from key
235     * that will be used in isPartiallyCompatible()
236     */
237   public void setKeyFeatureNamesSet(Set aKeyFeatureNamesSet){
238     keyFeatureNamesSet = aKeyFeatureNamesSet;
239   }//setKeyFeatureNamesSet();
240 
241   /** Gets the keyFeatureNamesSet in AnnotDiff.
242     * @return A set containing the feature names from key
243     * that will be used in isPartiallyCompatible()
244     */
245   public Set getKeyFeatureNamesSet(){
246     return keyFeatureNamesSet;
247   }//getKeyFeatureNamesSet();
248 
249   /** Sets the responseAnnotationSetName in AnnotDiff
250     * @param aResponseAnnotationSetName The name of the annotation set from the
251     * responseDocument.If aResponseAnnotationSetName is null then the default
252     * annotation set will be used.
253     */
254   public void setResponseAnnotationSetName(String aResponseAnnotationSetName){
255     responseAnnotationSetName = aResponseAnnotationSetName;
256   } // setResponseAnnotationSetName();
257 
258   /** gets the responseAnnotationSetName.
259     * @return The name of the responseAnnotationSet used in AnnotationDiff. If
260     * returns null then the the default annotation set will be used.
261     */
262   public String getResponseAnnotationSetName(){
263     return responseAnnotationSetName;
264   } // getResponseAnnotationSetName()
265 
266   /** Sets the responseAnnotationSetNameFalsePoz in AnnotDiff
267     * @param aResponseAnnotationSetNameFalsePoz The name of the annotation set
268     * from the responseDocument.If aResponseAnnotationSetName is null
269     * then the default annotation set will be used.
270     */
271   public void setResponseAnnotationSetNameFalsePoz(
272                                     String aResponseAnnotationSetNameFalsePoz){
273     responseAnnotationSetNameFalsePoz = aResponseAnnotationSetNameFalsePoz;
274   } // setResponseAnnotationSetNameFalsePoz();
275 
276   /** gets the responseAnnotationSetNameFalsePoz.
277     * @return The name of the responseAnnotationSetFalsePoz used in
278     * AnnotationDiff. If returns null then the the default annotation
279     * set will be used.
280     */
281   public String getResponseAnnotationSetNameFalsePoz(){
282     return responseAnnotationSetNameFalsePoz;
283   } // getResponseAnnotationSetNamefalsePoz()
284 
285   /**  Sets the annot diff to work in the text mode.This would not initiate the
286     *  GUI part of annot diff but it would calculate precision etc
287     */
288   public void setTextMode(Boolean aTextMode){
289     //it needs to be a Boolean and not boolean, because you cannot put
290     //in the parameters hashmap a boolean, it needs an object
291     textMode = aTextMode.booleanValue();
292   }// End setTextMode();
293 
294   /** Gets the annot diff textmode.True means that the text mode is activated.*/
295   public boolean isTextMode(){
296     return textMode;
297   }// End setTextMode();
298 
299   /** Returns a set with all annotations of a specific type*/
300   public Set getAnnotationsOfType(int annotType){
301     return annotDiffer.getAnnotationsOfType(annotType);
302   }//getAnnotationsOfType
303 
304 
305   ///////////////////////////////////////////////////
306   // PRECISION methods
307   ///////////////////////////////////////////////////
308 
309   /** @return the precisionStrict field*/
310   public double getPrecisionStrict(){
311     return annotDiffer.getPrecisionStrict();
312   } // getPrecisionStrict
313 
314   /** @return the precisionLenient field*/
315   public double getPrecisionLenient(){
316     return annotDiffer.getPrecisionLenient();
317   } // getPrecisionLenient
318 
319   /** @return the precisionAverage field*/
320   public double getPrecisionAverage(){
321     return annotDiffer.getPrecisionAverage();
322   } // getPrecisionAverage
323 
324   /** @return the fMeasureStrict field*/
325   public double getFMeasureStrict(){
326     return annotDiffer.getFMeasureStrict(1);
327   } // getFMeasureStrict
328 
329   /** @return the fMeasureLenient field*/
330   public double getFMeasureLenient(){
331     return annotDiffer.getFMeasureLenient(1);
332   } // getFMeasureLenient
333 
334   /** @return the fMeasureAverage field*/
335   public double getFMeasureAverage(){
336     return annotDiffer.getFMeasureAverage(1);
337   } // getFMeasureAverage
338 
339   ///////////////////////////////////////////////////
340   // RECALL methods
341   ///////////////////////////////////////////////////
342 
343   /** @return the recallStrict field*/
344   public double getRecallStrict(){
345     return annotDiffer.getRecallStrict();
346   } // getRecallStrict
347 
348   /** @return the recallLenient field*/
349   public double getRecallLenient(){
350     return annotDiffer.getRecallLenient();
351   } // getRecallLenient
352 
353   /** @return the recallAverage field*/
354   public double getRecallAverage(){
355     return annotDiffer.getRecallAverage();
356   } // getRecallAverage
357 
358   ///////////////////////////////////////////////////
359   // Missing, spurious, etc methods
360   ///////////////////////////////////////////////////
361 
362   public long getCorrectCount() {
363     return annotDiffer.getCorrectMatches();
364   }
365 
366   public long getPartiallyCorrectCount() {
367     return annotDiffer.getPartiallyCorrectMatches();
368   }
369 
370   public long getSpuriousCount() {
371     return annotDiffer.getSpurious();
372   }
373 
374   public long getMissingCount() {
375     return annotDiffer.getMissing();
376   }
377 
378   ///////////////////////////////////////////////////
379   // FALSE POSITIVE methods
380   ///////////////////////////////////////////////////
381 
382   /** @return the falsePositiveStrict field*/
383   public double getFalsePositiveStrict(){
384     return annotDiffer.getFalsePositivesStrict();
385   } // getFalsePositiveStrict
386 
387   /** @return the falsePositiveLenient field*/
388   public double getFalsePositiveLenient(){
389     return annotDiffer.getFalsePositivesLenient();
390   } // getFalsePositiveLenient
391 
392   /** @return the falsePositiveAverage field*/
393   public double getFalsePositiveAverage(){
394     return (double)(((double)getFalsePositiveLenient() + getFalsePositiveStrict()) / (double)(2.0));
395   } // getFalsePositive
396 
397   /**
398     * @param aResponseDocument the GATE response Document
399     * containing the annotation Set being compared against the annotation from
400     * the keyDocument.
401     */
402   public void setResponseDocument(Document aResponseDocument) {
403     responseDocument = aResponseDocument;
404   } //setResponseDocument
405 
406   /**
407     * @param anAnnotationSchema the annotation type being compared.
408     * This type is found in annotationSchema object as field
409     * {@link gate.creole.AnnotationSchema#getAnnotationName()}. If is <b>null<b>
410     * then AnnotDiff will throw an exception when it comes to do the diff.
411     */
412   public void setAnnotationSchema(AnnotationSchema anAnnotationSchema) {
413     annotationSchema = anAnnotationSchema;
414   } // setAnnotationType
415 
416   /** @return the annotation schema object used in annotation diff process */
417   public AnnotationSchema getAnnotationSchema(){
418     return annotationSchema;
419   } // AnnotationSchema
420 
421   public Dimension getPreferredScrollableViewportSize() {
422         return getPreferredSize();
423   }// public Dimension getPreferredScrollableViewportSize()
424 
425   public int getScrollableUnitIncrement(Rectangle visibleRect,
426                                               int orientation, int direction) {
427     return maxUnitIncrement;
428   }// public int getScrollableUnitIncrement
429 
430   public int getScrollableBlockIncrement(Rectangle visibleRect,
431                                               int orientation, int direction) {
432     if (orientation == SwingConstants.HORIZONTAL)
433         return visibleRect.width - maxUnitIncrement;
434     else
435         return visibleRect.height - maxUnitIncrement;
436   }// public int getScrollableBlockIncrement
437 
438   public boolean getScrollableTracksViewportWidth() {
439     return false;
440   }// public boolean getScrollableTracksViewportWidth()
441 
442   public boolean getScrollableTracksViewportHeight() {
443     return false;
444   }
445 
446   /**
447     * This method does the diff, Precision,Recall,FalsePositive
448     * calculation and so on.
449     */
450   public Resource init() throws ResourceInstantiationException {
451     colors[DEFAULT_TYPE] = WHITE;
452     colors[CORRECT_TYPE] = GREEN;
453     colors[SPURIOUS_TYPE] = RED;
454     colors[PARTIALLY_CORRECT_TYPE] = BLUE;
455     colors[MISSING_TYPE] = YELLOW;
456 
457     // Initialize the partially sets...
458     keyPartiallySet = new HashSet();
459     responsePartiallySet = new HashSet();
460 
461     // Do the diff, P&R calculation and so on
462     AnnotationSet keyAnnotSet = null;
463     AnnotationSet responseAnnotSet = null;
464 
465     if(annotationSchema == null)
466      throw new ResourceInstantiationException("No annotation schema defined !");
467 
468     if(keyDocument == null)
469       throw new ResourceInstantiationException("No key document defined !");
470 
471     if(responseDocument == null)
472       throw new ResourceInstantiationException("No response document defined !");
473 
474     if (keyAnnotationSetName == null)
475       // Get the default key AnnotationSet from the keyDocument
476       keyAnnotSet = keyDocument.getAnnotations().get(
477                               annotationSchema.getAnnotationName());
478     else
479       keyAnnotSet = keyDocument.getAnnotations(keyAnnotationSetName).
480                                     get(annotationSchema.getAnnotationName());
481 
482 
483     if (responseAnnotationSetName == null)
484       // Get the response AnnotationSet from the default set
485       responseAnnotSet = responseDocument.getAnnotations().get(
486                                           annotationSchema.getAnnotationName());
487     else
488       responseAnnotSet = responseDocument.getAnnotations(responseAnnotationSetName).get(annotationSchema.getAnnotationName());
489 
490 
491 
492     // Calculate the diff Set. This set will be used later with graphic
493     // visualisation.
494     annotDiffer.setSignificantFeaturesSet(getKeyFeatureNamesSet());
495     ArrayList choices = (ArrayList) annotDiffer.calculateDiff(keyAnnotSet, responseAnnotSet);
496     diffSet = new HashSet();
497     for(int i=0;i<choices.size();i++) {
498       AnnotationDiffer.PairingImpl choice = (AnnotationDiffer.PairingImpl) choices.get(i);
499       int type = choice.getType();
500       int leftType = 0;
501       int rightType = 0;
502       if(type == AnnotationDiffer.CORRECT) {
503         leftType = CORRECT_TYPE;
504         rightType = CORRECT_TYPE;
505       } else if(type == AnnotationDiffer.PARTIALLY_CORRECT) {
506         leftType = PARTIALLY_CORRECT_TYPE;
507         rightType = PARTIALLY_CORRECT_TYPE;
508       } else {
509         if(choice.getKey() == null) { leftType = SPURIOUS_TYPE; rightType = SPURIOUS_TYPE; }
510         else { leftType = MISSING_TYPE; rightType = MISSING_TYPE; }
511       }
512       diffSet.add(new DiffSetElement(choice.getKey(),choice.getResponse(), leftType, rightType));
513     }
514 
515     // If it runs under text mode just stop here.
516     if (textMode) return this;
517 
518     //Show it
519     // Configuring the formatter object. It will be used later to format
520     // precision and recall
521     formatter.setMaximumIntegerDigits(1);
522     formatter.setMinimumFractionDigits(4);
523     formatter.setMinimumFractionDigits(4);
524 
525     // Create an Annotation diff table model
526     AnnotationDiffTableModel diffModel = new AnnotationDiffTableModel(diffSet);
527     // Create a XJTable based on this model
528     diffTable = new XJTable(diffModel);
529     diffTable.setAlignmentX(Component.LEFT_ALIGNMENT);
530     // Set the cell renderer for this table.
531     AnnotationDiffCellRenderer cellRenderer = new AnnotationDiffCellRenderer();
532     diffTable.setDefaultRenderer(java.lang.String.class,cellRenderer);
533     diffTable.setDefaultRenderer(java.lang.Long.class,cellRenderer);
534     // Put the table into a JScroll
535 
536     // Arange all components on a this JPanel
537     SwingUtilities.invokeLater(new Runnable(){
538       public void run(){
539         arangeAllComponents();
540       }
541     });
542 
543     if (DEBUG)
544       printStructure(diffSet);
545 
546     return this;
547   } //init()
548 
549   /** This method creates the graphic components and aranges them on
550     * <b>this</b> JPanel
551     */
552   protected void arangeAllComponents(){
553     this.removeAll();
554     // Setting the box layout for diffpanel
555     BoxLayout boxLayout = new BoxLayout(this,BoxLayout.Y_AXIS);
556     this.setLayout(boxLayout);
557 
558     JTableHeader tableHeader = diffTable.getTableHeader();
559     tableHeader.setAlignmentX(Component.LEFT_ALIGNMENT);
560     this.add(tableHeader);
561     diffTable.setAlignmentX(Component.LEFT_ALIGNMENT);
562     // Add the tableScroll to the diffPanel
563     this.add(diffTable);
564 
565 
566     // ADD the LEGEND
567     //Lay out the JLabels from left to right.
568     //Box infoBox = new Box(BoxLayout.X_AXIS);
569     JPanel infoBox = new  JPanel();
570     infoBox.setLayout(new BoxLayout(infoBox,BoxLayout.X_AXIS));
571     infoBox.setAlignmentX(Component.LEFT_ALIGNMENT);
572     // Keep the components together
573     //box.add(Box.createHorizontalGlue());
574 
575     Box box = new Box(BoxLayout.Y_AXIS);
576     JLabel jLabel = new JLabel("LEGEND");
577     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
578     jLabel.setOpaque(true);
579     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
580     box.add(jLabel);
581 
582     jLabel = new JLabel("Missing (present in Key but not in Response):  " +
583                                                 annotDiffer.getMissing());
584     jLabel.setForeground(BLACK);
585     jLabel.setBackground(colors[MISSING_TYPE]);
586     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
587     jLabel.setOpaque(true);
588     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
589     box.add(jLabel);
590 
591     // Add a space
592     box.add(Box.createRigidArea(new Dimension(0,5)));
593 
594     jLabel = new JLabel("Correct (total match):  " + annotDiffer.getCorrectMatches());
595     jLabel.setForeground(BLACK);
596     jLabel.setBackground(colors[CORRECT_TYPE]);
597     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
598     jLabel.setOpaque(true);
599     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
600     box.add(jLabel);
601 
602     // Add a space
603     box.add(Box.createRigidArea(new Dimension(0,5)));
604 
605     jLabel =new JLabel("Partially correct (overlap in Key and Response):  "+
606                                         annotDiffer.getPartiallyCorrectMatches());
607     jLabel.setForeground(BLACK);
608     jLabel.setBackground(colors[PARTIALLY_CORRECT_TYPE]);
609     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
610     jLabel.setOpaque(true);
611     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
612     box.add(jLabel);
613 
614     // Add a space
615     box.add(Box.createRigidArea(new Dimension(0,5)));
616 
617     jLabel = new JLabel("Spurious (present in Response but not in Key):  " +
618                                         annotDiffer.getSpurious());
619     jLabel.setForeground(BLACK);
620     jLabel.setBackground(colors[SPURIOUS_TYPE]);
621     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
622     jLabel.setOpaque(true);
623     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
624     box.add(jLabel);
625 
626     infoBox.add(box);
627     // Add a space
628     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
629 
630     // Precision measure
631     //Lay out the JLabels from left to right.
632     box = new Box(BoxLayout.Y_AXIS);
633 
634     jLabel = new JLabel("Precision strict: " +
635                                     formatter.format(getPrecisionStrict()));
636     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
637     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
638     box.add(jLabel);
639 
640     jLabel = new JLabel("Precision average: " +
641                                     formatter.format(getPrecisionAverage()));
642     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
643     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
644     box.add(jLabel);
645 
646     jLabel = new JLabel("Precision lenient: " +
647                                     formatter.format(getPrecisionLenient()));
648     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
649     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
650     box.add(jLabel);
651 
652     infoBox.add(box);
653     // Add a space
654     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
655 
656     // RECALL measure
657     //Lay out the JLabels from left to right.
658     box = new Box(BoxLayout.Y_AXIS);
659 
660     jLabel = new JLabel("Recall strict: " + formatter.format(getRecallStrict()));
661     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
662     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
663     box.add(jLabel);
664 
665     jLabel = new JLabel("Recall average: " + formatter.format(getRecallAverage()));
666     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
667     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
668     box.add(jLabel);
669 
670     jLabel = new JLabel("Recall lenient: " + formatter.format(getRecallLenient()));
671     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
672     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
673     box.add(jLabel);
674 
675     infoBox.add(box);
676     // Add a space
677     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
678 
679     // F-Measure
680     //Lay out the JLabels from left to right.
681     box = new Box(BoxLayout.Y_AXIS);
682 
683     jLabel = new JLabel("F-Measure strict: " +
684                                         formatter.format(getFMeasureStrict()));
685     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
686     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
687     box.add(jLabel);
688 
689     jLabel = new JLabel("F-Measure average: " +
690                                         formatter.format(getFMeasureAverage()));
691     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
692     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
693     box.add(jLabel);
694 
695     jLabel = new JLabel("F-Measure lenient: " +
696                                         formatter.format(getFMeasureLenient()));
697     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
698     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
699     box.add(jLabel);
700     infoBox.add(box);
701 
702     // Add a space
703     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
704 
705     // FALSE POZITIVE measure
706     //Lay out the JLabels from left to right.
707     box = new Box(BoxLayout.Y_AXIS);
708 
709     jLabel = new JLabel("False positive strict: " +
710                                         formatter.format(getFalsePositiveStrict()));
711     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
712     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
713     box.add(jLabel);
714 
715     jLabel = new JLabel("False positive average: " +
716                                         formatter.format(getFalsePositiveAverage()));
717     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
718     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
719     box.add(jLabel);
720 
721     jLabel = new JLabel("False positive lenient: " +
722                                         formatter.format(getFalsePositiveLenient()));
723     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
724     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
725     box.add(jLabel);
726     infoBox.add(box);
727 
728     // Add a space
729     infoBox.add(Box.createRigidArea(new Dimension(10,0)));
730 
731     this.add(infoBox);
732   } //arangeAllComponents
733 
734   /** Used internally for debugging */
735   protected void printStructure(Set aDiffSet){
736     Iterator iterator = aDiffSet.iterator();
737     String leftAnnot = null;
738     String rightAnnot = null;
739     while(iterator.hasNext()){
740       DiffSetElement diffElem = (DiffSetElement) iterator.next();
741       if (diffElem.getLeftAnnotation() == null)
742         leftAnnot = "NULL ";
743       else
744         leftAnnot = diffElem.getLeftAnnotation().toString();
745       if (diffElem.getRightAnnotation() == null)
746         rightAnnot = " NULL";
747       else
748         rightAnnot = diffElem.getRightAnnotation().toString();
749       Out.prln( leftAnnot + "|" + rightAnnot);
750     } // end while
751   } // printStructure
752 
753 
754   /* ********************************************************************
755    * INNER CLASS
756    * ********************************************************************/
757 
758   /**
759     * A custom table model used to render a table containing the two annotation
760     * sets. The columns will be:
761     * (KEY) Type, Start, End, Features, empty column,(Response) Type,Start, End, Features
762     */
763   protected class AnnotationDiffTableModel extends AbstractTableModel{
764 
765     /** Constructs an AnnotationDiffTableModel given a data Collection */
766     public AnnotationDiffTableModel(Collection data){
767       modelData = new ArrayList();
768       modelData.addAll(data);
769     } // AnnotationDiffTableModel
770 
771     /** Constructs an AnnotationDiffTableModel */
772     public AnnotationDiffTableModel(){
773       modelData = new ArrayList();
774     } // AnnotationDiffTableModel
775 
776     /** Return the size of data.*/
777     public int getRowCount(){
778       return modelData.size();
779     } //getRowCount
780 
781     /** Return the number of columns.*/
782     public int getColumnCount(){
783       return 9;
784     } //getColumnCount
785 
786     /** Returns the name of each column in the model*/
787     public String getColumnName(int column){
788       switch(column){
789         case 0: return "String - Key";
790         case 1: return "Start - Key";
791         case 2: return "End - Key";
792         case 3: return "Features - Key";
793         case 4: return "   ";
794         case 5: return "String - Response";
795         case 6: return "Start - Response";
796         case 7: return "End -Response";
797         case 8: return "Features - Response";
798         default:return "?";
799       }
800     } //getColumnName
801 
802     /** Return the class type for each column. */
803     public Class getColumnClass(int column){
804       switch(column){
805         case 0: return String.class;
806         case 1: return Long.class;
807         case 2: return Long.class;
808         case 3: return String.class;
809         case 4: return String.class;
810         case 5: return String.class;
811         case 6: return Long.class;
812         case 7: return Long.class;
813         case 8: return String.class;
814         default:return Object.class;
815       }
816     } //getColumnClass
817 
818     /**Returns a value from the table model */
819     public Object getValueAt(int row, int column){
820       DiffSetElement diffSetElement = (DiffSetElement) modelData.get(row);
821       if (diffSetElement == null) return null;
822       switch(column){
823         // Left Side (Key)
824         //Type - Key
825         case 0:{
826            if (diffSetElement.getLeftAnnotation() == null) return null;
827            Annotation annot = diffSetElement.getLeftAnnotation();
828            String theString = "";
829            try {
830              theString = keyDocument.getContent().getContent(
831                     annot.getStartNode().getOffset(),
832                     annot.getEndNode().getOffset()).toString();
833            } catch (gate.util.InvalidOffsetException ex) {
834              Err.prln(ex.getMessage());
835            }
836            return theString;
837         }
838         // Start - Key
839         case 1:{
840            if (diffSetElement.getLeftAnnotation() == null) return null;
841            return diffSetElement.getLeftAnnotation().getStartNode().getOffset();
842         }
843         // End - Key
844         case 2:{
845            if (diffSetElement.getLeftAnnotation() == null) return null;
846            return diffSetElement.getLeftAnnotation().getEndNode().getOffset();
847         }
848         // Features - Key
849         case 3:{
850            if (diffSetElement.getLeftAnnotation() == null) return null;
851            if (diffSetElement.getLeftAnnotation().getFeatures() == null)
852              return null;
853            return diffSetElement.getLeftAnnotation().getFeatures().toString();
854         }
855         // Empty column
856         case 4:{
857           return "   ";
858         }
859         // Right Side (Response)
860         //Type - Response
861         case 5:{
862            if (diffSetElement.getRightAnnotation() == null) return null;
863            Annotation annot = diffSetElement.getRightAnnotation();
864            String theString = "";
865            try {
866              theString = responseDocument.getContent().getContent(
867                     annot.getStartNode().getOffset(),
868                     annot.getEndNode().getOffset()).toString();
869            } catch (gate.util.InvalidOffsetException ex) {
870              Err.prln(ex.getMessage());
871            }
872            return theString;
873         }
874         // Start - Response
875         case 6:{
876            if (diffSetElement.getRightAnnotation() == null) return null;
877           return diffSetElement.getRightAnnotation().getStartNode().getOffset();
878         }
879         // End - Response
880         case 7:{
881            if (diffSetElement.getRightAnnotation() == null) return null;
882            return diffSetElement.getRightAnnotation().getEndNode().getOffset();
883         }
884         // Features - resonse
885         case 8:{
886            if (diffSetElement.getRightAnnotation() == null) return null;
887            return diffSetElement.getRightAnnotation().getFeatures().toString();
888         }
889         // The hidden column
890         case 9:{
891           return diffSetElement;
892         }
893         default:{return null;}
894       } // End switch
895     } //getValueAt
896 
897     public Object getRawObject(int row){
898       return modelData.get(row);
899     } //getRawObject
900 
901     /** Holds the data for TableDiff*/
902     private java.util.List modelData = null;
903 
904   } //Inner class AnnotationDiffTableModel
905 
906 
907   /* ********************************************************************
908    * INNER CLASS
909    * ********************************************************************/
910   /**
911     * This class defines a Cell renderer for the AnnotationDiff table
912     */
913   public class AnnotationDiffCellRenderer extends DefaultTableCellRenderer{
914 
915     /** Constructs a randerer with a table model*/
916     public AnnotationDiffCellRenderer() { }  //AnnotationDiffCellRenderer
917 
918     private Color background = WHITE;
919 
920     private Color foreground = BLACK;
921 
922     /** This method is called by JTable*/
923 
924     public Component getTableCellRendererComponent(
925       JTable table, Object value, boolean isSelected, boolean hasFocus,
926       int row, int column
927     ) {
928       JComponent defaultComp = null;
929       defaultComp = (JComponent) super.getTableCellRendererComponent(
930   table, value, isSelected, hasFocus, row, column
931       );
932 
933       // The column number four will be randered using a blank component
934       if (column == 4 || value == null)
935         return new JPanel();
936 
937       if (!(table.getModel().getValueAt(row,9) instanceof DiffSetElement))
938         return defaultComp;
939 
940       DiffSetElement diffSetElement =
941                         (DiffSetElement) table.getModel().getValueAt(row,9);
942 
943       if (diffSetElement == null)
944         return defaultComp;
945 
946       if (column < 4){
947         if (diffSetElement.getLeftAnnotation() != null) {
948           background = colors[diffSetElement.getLeftType()];
949         }
950         else return new JPanel();
951       }else{
952         if (diffSetElement.getRightAnnotation() != null)
953           background = colors[diffSetElement.getRightType()];
954         else return new JPanel();
955       }
956 
957       defaultComp.setBackground(background);
958       defaultComp.setForeground(BLACK);
959       defaultComp.setOpaque(true);
960       return defaultComp;
961     } //getTableCellRendererComponent
962 
963   } // class AnnotationDiffCellRenderer
964 
965 
966   /* ********************************************************************
967    * INNER CLASS
968    * ********************************************************************/
969 
970   /**
971     * This class is used for internal purposes. It represents a row from the
972     * table.
973     */
974   protected class DiffSetElement {
975     /** This field represent a key annotation*/
976     private Annotation leftAnnotation = null;
977     /** This field represent a response annotation*/
978     private Annotation rightAnnotation = null;
979     /** Default initialization of the key type*/
980     private int leftType = DEFAULT_TYPE;
981     /** Default initialization of the response type*/
982     private int rightType = DEFAULT_TYPE;
983 
984     /** Constructor for DiffSetlement*/
985     public DiffSetElement() {}
986 
987     /** Constructor for DiffSetlement*/
988     public DiffSetElement( Annotation aLeftAnnotation,
989                            Annotation aRightAnnotation,
990                            int aLeftType,
991                            int aRightType){
992       leftAnnotation = aLeftAnnotation;
993       rightAnnotation = aRightAnnotation;
994       leftType = aLeftType;
995       rightType = aRightType;
996     } // DiffSetElement
997 
998     /** Sets the left annotation*/
999     public void setLeftAnnotation(Annotation aLeftAnnotation){
1000      leftAnnotation = aLeftAnnotation;
1001    } // setLeftAnnot
1002
1003    /** Gets the left annotation*/
1004    public Annotation getLeftAnnotation(){
1005      return leftAnnotation;
1006    } // getLeftAnnotation
1007
1008    /** Sets the right annotation*/
1009    public void setRightAnnotation(Annotation aRightAnnotation){
1010      rightAnnotation = aRightAnnotation;
1011    } // setRightAnnot
1012
1013    /** Gets the right annotation*/
1014    public Annotation getRightAnnotation(){
1015      return rightAnnotation;
1016    } // getRightAnnotation
1017
1018    /** Sets the left type*/
1019    public void setLeftType(int aLeftType){
1020      leftType = aLeftType;
1021    } // setLeftType
1022
1023    /** Get the left type */
1024    public int getLeftType() {
1025      return leftType;
1026    } // getLeftType
1027
1028    /** Sets the right type*/
1029    public void setRightType(int aRightType) {
1030      rightType = aRightType;
1031    } // setRightType
1032
1033    /** Get the right type*/
1034    public int getRightType() {
1035      return rightType;
1036    } // getRightType
1037  } // classs DiffSetElement
1038} // class AnnotationDiff
1039