| AnnotationImpl |

    1   /*
    2    *  AnnotationImpl.java
    3    *
    4    *  Copyright (c) 1998-2001, 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   *  Valentin Tablan, Jan/00
    12   *
    13   *  $Id: AnnotationImpl.java,v 1.25 2001/11/12 17:14:12 kalina Exp $
    14   */
    15
    16  package gate.annotation;
    17
    18  import java.util.*;
    19
    20  import gate.*;
    21  import gate.util.*;
    22  import gate.corpora.*;
    23  import gate.event.*;
    24
    25  /** Provides an implementation for the interface gate.Annotation
    26   *
    27   */
    28  public class AnnotationImpl extends AbstractFeatureBearer
    29                              implements Annotation, FeatureBearer, Comparable {
    30
    31    /** Debug flag
    32     */
    33    private static final boolean DEBUG = false;
    34    /** Freeze the serialization UID. */
    35    static final long serialVersionUID = -5658993256574857725L;
    36
    37    /** Constructor. Package access - annotations have to be constructed via
    38     * AnnotationSets.
    39     *
    40     * @param id The id of the new annotation;
    41     * @param start The node from where the annotation will depart;
    42     * @param end The node where trhe annotation ends;
    43     * @param type The type of the new annotation;
    44     * @param features The features of the annotation.
    45     */
    46    AnnotationImpl(
    47      Integer id, Node start, Node end, String type, FeatureMap features
    48    ) {
    49      this.id       = id;
    50      this.start    = start;
    51      this.end      = end;
    52      this.type     = type;
    53      this.features = features;
    54
    55    } // AnnotationImpl
    56
    57
    58    /** The ID of the annotation.
    59     */
    60    public Integer getId() {
    61      return id;
    62    } // getId()
    63
    64    /** The type of the annotation (corresponds to TIPSTER "name").
    65     */
    66    public String getType() {
    67      return type;
    68    } // getType()
    69
    70    /** The start node.
    71     */
    72    public Node getStartNode() {
    73      return start;
    74    } // getStartNode()
    75
    76    /** The end node.
    77     */
    78    public Node getEndNode() {
    79      return end;
    80    } // getEndNode()
    81
    82    /** String representation of hte annotation
    83     */
    84    public String toString() {
    85      return "AnnotationImpl: id=" + id + "; type=" + type +
    86             "; features=" + features + "; start=" + start +
    87             "; end=" + end + System.getProperty("line.separator");
    88    } // toString()
    89
    90    /** Ordering
    91     */
    92    public int compareTo(Object o) throws ClassCastException {
    93      Annotation other = (Annotation) o;
    94      return id.compareTo(other.getId());
    95    } // compareTo
    96
    97    /** When equals called on two annotations returns true, is REQUIRED that the
    98      * value hashCode for each annotation to be the same. It is not required
    99      * that when equals return false, the values to be different. For speed, it
    100     * would be beneficial to happen that way.
    101     */
    102
    103   public int hashCode(){
    104     int hashCodeRes = 0;
    105     if (start != null && start.getOffset() != null)
    106        hashCodeRes ^= start.getOffset().hashCode();
    107     if (end != null && end.getOffset() != null)
    108       hashCodeRes ^= end.getOffset().hashCode();
    109     if(features != null)
    110       hashCodeRes ^= features.hashCode();
    111     return  hashCodeRes;
    112   }// hashCode
    113
    114   /** Returns true if two annotation are Equals.
    115    *  Two Annotation are equals if their offsets, types, id and features are the
    116    *  same.
    117    */
    118   public boolean equals(Object obj){
    119     if(obj == null)
    120       return false;
    121     Annotation other;
    122     if(obj instanceof AnnotationImpl){
    123       other = (Annotation) obj;
    124     }else return false;
    125
    126     // If their types are not equals then return false
    127     if((type == null) ^ (other.getType() == null))
    128       return false;
    129     if(type != null && (!type.equals(other.getType())))
    130       return false;
    131
    132     // If their types are not equals then return false
    133     if((id == null) ^ (other.getId() == null))
    134       return false;
    135     if((id != null )&& (!id.equals(other.getId())))
    136       return false;
    137
    138     // If their start offset is not the same then return false
    139     if((start == null) ^ (other.getStartNode() == null))
    140       return false;
    141     if(start != null){
    142       if((start.getOffset() == null) ^
    143          (other.getStartNode().getOffset() == null))
    144         return false;
    145       if(start.getOffset() != null &&
    146         (!start.getOffset().equals(other.getStartNode().getOffset())))
    147         return false;
    148     }
    149
    150     // If their end offset is not the same then return false
    151     if((end == null) ^ (other.getEndNode() == null))
    152       return false;
    153     if(end != null){
    154       if((end.getOffset() == null) ^
    155          (other.getEndNode().getOffset() == null))
    156         return false;
    157       if(end.getOffset() != null &&
    158         (!end.getOffset().equals(other.getEndNode().getOffset())))
    159         return false;
    160     }
    161
    162     // If their featureMaps are not equals then return false
    163     if((features == null) ^ (other.getFeatures() == null))
    164       return false;
    165     if(features != null && (!features.equals(other.getFeatures())))
    166       return false;
    167     return true;
    168   }// equals
    169
    170   /** Set the feature set. Overriden from the implementation in
    171    *  AbstractFeatureBearer because it needs to fire events
    172    */
    173   public void setFeatures(FeatureMap features) {
    174     //I need to remove first the old features listener if any
    175     if (eventHandler != null)
    176       this.features.removeFeatureMapListener(eventHandler);
    177
    178     this.features = features;
    179
    180     //if someone cares about the annotation changes, then we need to
    181     //track the events from the new feature
    182     if (! annotationListeners.isEmpty())
    183       this.features.addFeatureMapListener(eventHandler);
    184
    185     //finally say that the annotation features have been updated
    186     fireAnnotationUpdated(new AnnotationEvent(
    187                             this,
    188                             AnnotationEvent.FEATURES_UPDATED));
    189
    190
    191   }
    192
    193
    194   /** This verifies if <b>this</b> annotation is compatible with another one.
    195     * Compatible means that they hit the same possition and the FeatureMap of
    196     * <b>this</b> is incuded into aAnnot FeatureMap.
    197     * @param anAnnot a gate Annotation. If anAnnotation is null then false is
    198     * returned.
    199     * @return <code>true</code> if aAnnot is compatible with <b>this</> and
    200     * <code>false</code> otherwise.
    201     */
    202   public boolean isCompatible(Annotation anAnnot){
    203     if (anAnnot == null) return false;
    204     if (coextensive(anAnnot)){
    205       if (anAnnot.getFeatures() == null) return true;
    206       if (anAnnot.getFeatures().subsumes(this.getFeatures()))
    207         return true;
    208     }// End if
    209     return false;
    210   }//isCompatible
    211
    212   /** This verifies if <b>this</b> annotation is compatible with another one,
    213     * given a set with certain keys.
    214     * In this case, compatible means that they hit the same possition
    215     * and those keys from <b>this</b>'s FeatureMap intersected with
    216     * aFeatureNamesSet are incuded together with their values into the aAnnot's
    217     * FeatureMap.
    218     * @param anAnnot a gate Annotation. If param is null, it will return false.
    219     * @param aFeatureNamesSet is a set containing certian key that will be
    220     * intersected with <b>this</b>'s FeatureMap's keys.If param is null then
    221     * isCompatible(Annotation) will be called.
    222     * @return <code>true</code> if aAnnot is compatible with <b>this</> and
    223     * <code>false</code> otherwise.
    224     */
    225   public boolean isCompatible(Annotation anAnnot, Set aFeatureNamesSet){
    226     // If the set is null then isCompatible(Annotation) will decide.
    227     if (aFeatureNamesSet == null) return isCompatible(anAnnot);
    228     if (anAnnot == null) return false;
    229     if (coextensive(anAnnot)){
    230       if (anAnnot.getFeatures() == null) return true;
    231       if (anAnnot.getFeatures().subsumes(this.getFeatures(),aFeatureNamesSet))
    232         return true;
    233     }// End if
    234     return false;
    235   }//isCompatible()
    236
    237   /** This method verifies if two annotation and are partially compatible.
    238     * Partially compatible means that they overlap and the FeatureMap of
    239     * <b>this</b> is incuded into FeatureMap of aAnnot.
    240     * @param aAnnot a gate Annotation.
    241     * @return <code>true</code> if <b>this</b> is partially compatible with
    242     * aAnnot and <code>false</code> otherwise.
    243     */
    244   public boolean isPartiallyCompatible(Annotation anAnnot){
    245     if (anAnnot == null) return false;
    246     if (overlaps(anAnnot)){
    247       if (anAnnot.getFeatures() == null) return true;
    248       if (anAnnot.getFeatures().subsumes(this.getFeatures()))
    249         return true;
    250     }// End if
    251     return false;
    252   }//isPartiallyCompatible
    253
    254   /** This method verifies if two annotation and are partially compatible,
    255     * given a set with certain keys.
    256     * In this case, partially compatible means that they overlap
    257     * and those keys from <b>this</b>'s FeatureMap intersected with
    258     * aFeatureNamesSet are incuded together with their values into the aAnnot's
    259     * FeatureMap.
    260     * @param anAnnot a gate Annotation. If param is null, the method will return
    261     * false.
    262     * @param aFeatureNamesSet is a set containing certian key that will be
    263     * intersected with <b>this</b>'s FeatureMap's keys.If param is null then
    264     * isPartiallyCompatible(Annotation) will be called.
    265     * @return <code>true</code> if <b>this</b> is partially compatible with
    266     * aAnnot and <code>false</code> otherwise.
    267     */
    268   public boolean isPartiallyCompatible(Annotation anAnnot,Set aFeatureNamesSet){
    269     if (aFeatureNamesSet == null) return isPartiallyCompatible(anAnnot);
    270     if (anAnnot == null) return false;
    271     if (overlaps(anAnnot)){
    272       if (anAnnot.getFeatures() == null) return true;
    273       if (anAnnot.getFeatures().subsumes(this.getFeatures(),aFeatureNamesSet))
    274         return true;
    275     }// End if
    276     return false;
    277   }//isPartiallyCompatible()
    278
    279   /**
    280     *  Two Annotation are coextensive if their offsets are the
    281     *  same.
    282     *  @param anAnnot A Gate annotation.
    283     *  @return <code>true</code> if two annotation hit the same possition and
    284     *  <code>false</code> otherwise
    285     */
    286   public boolean coextensive(Annotation anAnnot){
    287     // If their start offset is not the same then return false
    288     if((anAnnot.getStartNode() == null) ^ (this.getStartNode() == null))
    289       return false;
    290
    291     if(anAnnot.getStartNode() != null){
    292       if((anAnnot.getStartNode().getOffset() == null) ^
    293          (this.getStartNode().getOffset() == null))
    294         return false;
    295       if(anAnnot.getStartNode().getOffset() != null &&
    296         (!anAnnot.getStartNode().getOffset().equals(
    297                             this.getStartNode().getOffset())))
    298         return false;
    299     }// End if
    300
    301     // If their end offset is not the same then return false
    302     if((anAnnot.getEndNode() == null) ^ (this.getEndNode() == null))
    303       return false;
    304
    305     if(anAnnot.getEndNode() != null){
    306       if((anAnnot.getEndNode().getOffset() == null) ^
    307          (this.getEndNode().getOffset() == null))
    308         return false;
    309       if(anAnnot.getEndNode().getOffset() != null &&
    310         (!anAnnot.getEndNode().getOffset().equals(
    311               this.getEndNode().getOffset())))
    312         return false;
    313     }// End if
    314
    315     // If we are here, then the annotations hit the same position.
    316     return true;
    317   }//coextensive
    318
    319   /** This method tells if <b>this</b> overlaps aAnnot.
    320     * @param aAnnot a gate Annotation.
    321     * @return <code>true</code> if they overlap and <code>false</code> false if
    322     * they don't.
    323     */
    324   public boolean overlaps(Annotation aAnnot){
    325     if (aAnnot == null) return false;
    326     if (aAnnot.getStartNode() == null ||
    327         aAnnot.getEndNode() == null ||
    328         aAnnot.getStartNode().getOffset() == null ||
    329         aAnnot.getEndNode().getOffset() == null) return false;
    330
    331 //    if ( (aAnnot.getEndNode().getOffset().longValue() ==
    332 //          aAnnot.getStartNode().getOffset().longValue()) &&
    333 //          this.getStartNode().getOffset().longValue() <=
    334 //          aAnnot.getStartNode().getOffset().longValue() &&
    335 //          aAnnot.getEndNode().getOffset().longValue() <=
    336 //          this.getEndNode().getOffset().longValue()
    337 //       ) return true;
    338
    339
    340     if ( aAnnot.getEndNode().getOffset().longValue() <=
    341          this.getStartNode().getOffset().longValue())
    342       return false;
    343
    344     if ( aAnnot.getStartNode().getOffset().longValue() >=
    345          this.getEndNode().getOffset().longValue())
    346       return false;
    347
    348     return true;
    349   }//overlaps
    350
    351 //////////////////THE EVENT HANDLING CODE/////////////////////
    352 //Needed so an annotation set can listen to its annotations//
    353 //and update correctly the database/////////////////////////
    354
    355   /**
    356    * The set of listeners of the annotation update events. At present there
    357    * are two event types supported:
    358    * <UL>
    359    *   <LI> ANNOTATION_UPDATED event
    360    *   <LI> FEATURES_UPDATED event
    361    * </UL>
    362    */
    363   private transient Vector annotationListeners;
    364   /**
    365    * The listener for the events coming from the features.
    366    */
    367   protected EventsHandler eventHandler;
    368
    369
    370   /**
    371    *
    372    * Removes an annotation listener
    373    */
    374   public synchronized void removeAnnotationListener(AnnotationListener l) {
    375     if (annotationListeners != null && annotationListeners.contains(l)) {
    376       Vector v = (Vector) annotationListeners.clone();
    377       v.removeElement(l);
    378       annotationListeners = v;
    379     }
    380   }
    381   /**
    382    *
    383    * Adds an annotation listener
    384    */
    385   public synchronized void addAnnotationListener(AnnotationListener l) {
    386     Vector v = annotationListeners == null ? new Vector(2) : (Vector) annotationListeners.clone();
    387
    388     //now check and if this is the first listener added,
    389     //start listening to all features, so their changes can
    390     //also be propagated
    391     if (v.isEmpty()) {
    392       FeatureMap features = getFeatures();
    393       if (eventHandler == null)
    394         eventHandler = new EventsHandler();
    395       features.addFeatureMapListener(eventHandler);
    396     }
    397
    398     if (!v.contains(l)) {
    399       v.addElement(l);
    400       annotationListeners = v;
    401     }
    402   }
    403   /**
    404    *
    405    * @param e
    406    */
    407   protected void fireAnnotationUpdated(AnnotationEvent e) {
    408     if (annotationListeners != null) {
    409       Vector listeners = annotationListeners;
    410       int count = listeners.size();
    411       for (int i = 0; i < count; i++) {
    412         ((AnnotationListener) listeners.elementAt(i)).annotationUpdated(e);
    413       }
    414     }
    415   }//fireAnnotationUpdated
    416
    417
    418   /**
    419    * The id of this annotation (for persitency resons)
    420    *
    421    */
    422   Integer id;
    423   /**
    424    * The type of the annotation
    425    *
    426    */
    427   String type;
    428   /**
    429    * The features of the annotation are inherited from Abstract feature bearer
    430    * so no need to define here
    431    */
    432
    433   /**
    434    * The start node
    435    */
    436   Node start;
    437
    438   /**
    439    *  The end node
    440    */
    441   Node end;
    442
    443   /** @link dependency */
    444   /*#AnnotationImpl lnkAnnotationImpl;*/
    445
    446   /**
    447    * All the events from the features are handled by
    448    * this inner class.
    449    */
    450   class EventsHandler implements gate.event.FeatureMapListener {
    451     public void featureMapUpdated(){
    452       //tell the annotation listeners that my features have been updated
    453       fireAnnotationUpdated(new AnnotationEvent(
    454                                   AnnotationImpl.this,
    455                                   AnnotationEvent.FEATURES_UPDATED));
    456     }
    457   }//inner class EventsHandler
    458
    459
    460 } // class AnnotationImpl
    461

| AnnotationImpl |