|
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.26 2002/03/07 12:32:51 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 != null && ! 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 |
|