1   /*
2    *  Copyright (c) 1998-2005, 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    *  Kalina Bontcheva 21/10/2001
10   *
11   *  $Id: DatabaseAnnotationSetImpl.java,v 1.21 2005/01/11 13:51:30 ian Exp $
12   */
13  
14  package gate.annotation;
15  
16  import java.util.*;
17  
18  import junit.framework.Assert;
19  
20  import gate.*;
21  import gate.corpora.DatabaseDocumentImpl;
22  import gate.corpora.DocumentImpl;
23  import gate.event.*;
24  //import gate.persist.*;
25  
26  
27  public class DatabaseAnnotationSetImpl extends AnnotationSetImpl
28                                         implements DatastoreListener,
29                                                    EventAwareAnnotationSet,
30                                                    AnnotationListener {
31  
32    /**
33     * The listener for the events coming from the document (annotations and
34     * annotation sets added or removed).
35     */
36  //=  protected EventsHandler eventHandler;
37  
38    protected HashSet addedAnnotations = new HashSet();
39    protected HashSet removedAnnotations = new HashSet();
40    protected HashSet updatedAnnotations = new HashSet();
41  
42    private boolean validating = false;
43  
44    public void assertValid() {
45  
46      if (validating)
47        return;
48  
49      validating = true;
50      //avoid recursion
51  
52      //doc can't be null
53      Assert.assertNotNull(this.doc);
54      //doc.assertValid();
55  
56      validating = false;
57    }
58  
59    /** Construction from Document. */
60    public DatabaseAnnotationSetImpl(Document doc) {
61  
62      super(doc);
63  
64      //preconditions
65      Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
66  
67  //=    eventHandler = new EventsHandler();
68  //=    this.addAnnotationSetListener(eventHandler);
69  
70      //add self as listener for sync events from the document's datastore
71  //00    doc.getDataStore().removeDatastoreListener(this);
72      doc.getDataStore().addDatastoreListener(this);
73  //    ((VerboseHashMap)annotsById).setOwner(this);
74    } // construction from document
75  
76    /** Construction from Document and name. */
77    public DatabaseAnnotationSetImpl(Document doc, String name) {
78      super(doc, name);
79  
80      //preconditions
81      Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
82  
83  //=    eventHandler = new EventsHandler();
84  //=    this.addAnnotationSetListener(eventHandler);
85  
86      //add self as listener for sync events from the document's datastore
87  //00    doc.getDataStore().removeDatastoreListener(this);
88      doc.getDataStore().addDatastoreListener(this);
89  //    ((VerboseHashMap)annotsById).setOwner(this);
90    } // construction from document and name
91  
92  
93    /** Construction from Document and name. */
94    public DatabaseAnnotationSetImpl(Document doc, Collection c) {
95      this(c);
96      this.doc = (DocumentImpl) doc;
97      //add self as listener for sync events from the document's datastore
98  //00    doc.getDataStore().removeDatastoreListener(this);
99  //00    doc.getDataStore().addDatastoreListener(this);
100   } // construction from document and name
101 
102   /** Construction from Document and name. */
103   public DatabaseAnnotationSetImpl(Document doc, String name, Collection c) {
104     this(doc,c);
105     this.name = name;
106     //add self as listener for sync events from the document's datastore
107 //00    doc.getDataStore().removeDatastoreListener(this);
108     doc.getDataStore().addDatastoreListener(this);
109   } // construction from document and name
110 
111 
112   /** Construction from Collection (which must be an AnnotationSet) */
113   public DatabaseAnnotationSetImpl(Collection c) throws ClassCastException {
114 
115     super(c);
116 
117     //also copy the name, because that super one doesn't
118     AnnotationSet as = (AnnotationSet) c;
119     this.name = as.getName();
120 
121 //=    eventHandler = new EventsHandler();
122 //=    this.addAnnotationSetListener(eventHandler);
123 
124     Iterator iter = this.iterator();
125     while(iter.hasNext())
126       ((Annotation) iter.next()).addAnnotationListener(this);
127 
128     Document doc = as.getDocument();
129     //add self as listener for sync events from the document's datastore
130 //00    doc.getDataStore().removeDatastoreListener(this);
131     doc.getDataStore().addDatastoreListener(this);
132 
133 //    ((VerboseHashMap)annotsById).setOwner(this);
134   } // construction from collection
135 
136 
137   public String toString() {
138     return super.toString()
139               + "added annots: " + addedAnnotations
140               + "removed annots: " + removedAnnotations
141               + "updated annots: " + updatedAnnotations;
142   }
143 
144 
145 //  /** Two AnnotationSet are equal if their name, the documents of which belong
146 //    *  to the AnnotationSets and annotations from the sets are the same
147 //    */
148 //  public boolean equals(Object other) {
149 //
150 //    if (false == other instanceof DatabaseAnnotationSetImpl) {
151 //      return super.equals(other);
152 //    }
153 //
154 //    boolean result = true;
155 //
156 //    if (!super.equals((AnnotationSet)other)) {
157 //      return false;
158 //    }
159 //
160 //    DatabaseAnnotationSetImpl target = (DatabaseAnnotationSetImpl)other;
161 //
162 //    result = result && this.addedAnnotations.equals(target.addedAnnotations)
163 //                    && this.removedAnnotations.equals(target.removedAnnotations)
164 //                    && this.updatedAnnotations.equals(target.updatedAnnotations);
165 //
166 //    //FINALLY - CHECK THAT THE SET IS FROM THE SAME DOCUMENT *INSTANCE*
167 //    //DO *NOT* USE EQUALS()
168 //    result = result && ( this.getDocument() == target.getDocument());
169 //
170 //    return result;
171 //  } // equals
172 
173   /**
174    * All the events from the document or its annotation sets are handled by
175    * this inner class.
176    */
177 /*  class EventsHandler implements AnnotationListener
178                                  AnnotationSetListener{
179 
180 
181     public void annotationAdded(gate.event.AnnotationSetEvent e) {
182       AnnotationSet set = (AnnotationSet)e.getSource();
183       String setName = set.getName();
184       if (setName != DatabaseAnnotationSetImpl.this.name &&
185           ! setName.equals(DatabaseAnnotationSetImpl.this.name))
186         return;
187       Annotation ann = e.getAnnotation();
188       ann.addAnnotationListener(this);
189       DatabaseAnnotationSetImpl.this.addedAnnotations.add(ann);
190     }
191 
192     public void annotationRemoved(AnnotationSetEvent e){
193       AnnotationSet set = (AnnotationSet)e.getSource();
194       String setName = set.getName();
195       if (setName != DatabaseAnnotationSetImpl.this.name &&
196           ! setName.equals(DatabaseAnnotationSetImpl.this.name))
197         return;
198       Annotation ann = e.getAnnotation();
199       ann.removeAnnotationListener(this);
200 
201       //1. check if this annot is in the newly created annotations set
202       if (addedAnnotations.contains(ann)) {
203         //a new annotatyion that was deleted afterwards, remove it from all sets
204         DatabaseAnnotationSetImpl.this.addedAnnotations.remove(ann);
205         return;
206       }
207       //2. check if the annotation was updated, if so, remove it from the
208       //update list
209       if (updatedAnnotations.contains(ann)) {
210         DatabaseAnnotationSetImpl.this.updatedAnnotations.remove(ann);
211       }
212 
213       DatabaseAnnotationSetImpl.this.removedAnnotations.add(ann);
214     }
215 
216 
217     public void annotationUpdated(AnnotationEvent e){
218       Annotation ann = (Annotation) e.getSource();
219 
220       //check if the annotation is newly created
221       //if so, do not add it to the update list, since it was not stored in the
222       //database yet, so the most recent value will be inserted into the DB upon
223       //DataStore::sync()
224       if (addedAnnotations.contains(ann)) {
225         return;
226       }
227 
228       DatabaseAnnotationSetImpl.this.updatedAnnotations.add(ann);
229     }
230 
231   }//inner class EventsHandler
232 
233 */
234 
235   /**
236    * Called by a datastore when a new resource has been adopted
237    */
238   public void resourceAdopted(DatastoreEvent evt){
239     Assert.assertNotNull(evt);
240     Assert.assertNotNull(evt.getResourceID());
241 
242     //check if this is our resource
243     //rememeber -  a data store handles many resources
244     if (evt.getResourceID().equals(this.doc.getLRPersistenceId()))  {
245 //System.out.println("ASNAME=["+this.getName()+"], resourceAdopted() called");
246       //we're synced wtith the DB now
247       clearChangeLists();
248     }
249   }
250 
251   /**
252    * Called by a datastore when a resource has been deleted
253    */
254   public void resourceDeleted(DatastoreEvent evt){
255 
256     Assert.assertNotNull(evt);
257     Assert.assertNotNull(evt.getResourceID());
258 
259     //check if this is our resource
260     //rememeber -  a data store handles many resources
261     if (evt.getResourceID().equals(this.doc.getLRPersistenceId()))  {
262 //System.out.println("ASNAME=["+this.getName()+"],resourceDeleted() called");
263 
264       //unregister self
265 //this is not the correct way, since the resource is null in this case
266 //      DataStore ds = (DataStore)evt.getResource();
267       DataStore ds = this.doc.getDataStore();
268       if (ds != null)
269         ds.removeDatastoreListener(this);
270     }
271 
272   }//resourceDeleted
273 
274   /**
275    * Called by a datastore when a resource has been wrote into the datastore
276    */
277   public void resourceWritten(DatastoreEvent evt){
278     Assert.assertNotNull(evt);
279     Assert.assertNotNull(evt.getResourceID());
280 
281     //check if this is our resource
282     //rememeber -  a data store handles many resources
283     if (evt.getResourceID().equals(this.doc.getLRPersistenceId()))  {
284 //System.out.println("ASNAME=["+this.getName()+"],resourceWritten() called");
285 
286       //clear lists with updates - we're synced with the DB
287       clearChangeLists();
288     }
289   }
290 
291 
292   private void clearChangeLists() {
293 
294     //ok, we're synced now, clear all lists with changed IDs
295     synchronized(this) {
296 //System.out.println("clearing lists...");
297       this.addedAnnotations.clear();
298       this.updatedAnnotations.clear();
299       this.removedAnnotations.clear();
300     }
301   }
302 
303   public Collection getAddedAnnotations() {
304 //System.out.println("getAddedIDs() called");
305     HashSet result = new HashSet();
306     result.addAll(this.addedAnnotations);
307 
308     return result;
309   }
310 
311 
312   public Collection getChangedAnnotations() {
313 //System.out.println("getChangedIDs() called");
314     HashSet result = new HashSet();
315     result.addAll(this.updatedAnnotations);
316 
317     return result;
318   }
319 
320 
321   public Collection getRemovedAnnotations() {
322 //System.out.println("getremovedIDs() called...");
323     HashSet result = new HashSet();
324     result.addAll(this.removedAnnotations);
325 
326     return result;
327   }
328 
329   public void annotationUpdated(AnnotationEvent e){
330     Annotation ann = (Annotation) e.getSource();
331 
332     //check if the annotation is newly created
333     //if so, do not add it to the update list, since it was not stored in the
334     //database yet, so the most recent value will be inserted into the DB upon
335     //DataStore::sync()
336     if (false == this.addedAnnotations.contains(ann)) {
337       this.updatedAnnotations.add(ann);
338     }
339 
340     //sanity check
341     Assert.assertTrue(false == this.removedAnnotations.contains(ann));
342   }
343 
344 
345   /** Add an existing annotation. Returns true when the set is modified. */
346   public boolean add(Object o) throws ClassCastException {
347 
348     //check if this annotation was removed beforehand
349     //if so then just delete it from the list of annotations waiting for persistent removal
350     if (this.removedAnnotations.contains(o)) {
351       this.removedAnnotations.remove(o);
352     }
353 
354     boolean result = super.add(o);
355 
356     if (true == result) {
357       //register as listener for update events from this annotation
358       Annotation ann = (Annotation)o;
359       ann.addAnnotationListener(this);
360 
361       //add to the newly created annotations set
362       this.addedAnnotations.add(ann);
363     }
364 
365     return result;
366   }
367 
368 
369   /**
370    *
371    * @param e
372    */
373   protected void fireAnnotationRemoved(AnnotationSetEvent e) {
374     if (annotationSetListeners != null) {
375       Vector listeners = annotationSetListeners;
376       int count = listeners.size();
377       for (int i = 0; i < count; i++) {
378         ((AnnotationSetListener) listeners.elementAt(i)).annotationRemoved(e);
379       }
380     }
381   }
382 
383   /** Remove an element from this set. */
384   public boolean remove(Object o) throws ClassCastException {
385 
386     boolean result = super.remove(o);
387 
388     if (true == result) {
389       //UNregister as listener for update events from this annotation
390       Annotation ann = (Annotation)o;
391       ann.removeAnnotationListener(this);
392 
393       //1. check if this annot is in the newly created annotations set
394       if (this.addedAnnotations.contains(ann)) {
395         //a new annotation that was deleted afterwards, remove it from all sets
396         this.addedAnnotations.remove(ann);
397       }
398       else {
399 
400         //2. check if the annotation was updated, if so, remove it from the
401         //update list
402         if (this.updatedAnnotations.contains(ann)) {
403           this.updatedAnnotations.remove(ann);
404         }
405 
406         //3. add to the list with deleted anns
407         this.removedAnnotations.add(ann);
408       }
409     }
410 
411     return result;
412   }
413 
414   public Iterator iterator() { return new DatabaseAnnotationSetIterator(); }
415 
416 
417     class DatabaseAnnotationSetIterator extends AnnotationSetImpl.AnnotationSetIterator {
418 
419       public void remove() {
420 
421         super.remove();
422 
423         Annotation annRemoved = (Annotation)lastNext;
424 
425         //UNregister as listener for update events from this annotation
426           annRemoved.removeAnnotationListener(DatabaseAnnotationSetImpl.this);
427 
428         //1. check if this annot is in the newly created annotations set
429         if (DatabaseAnnotationSetImpl.this.addedAnnotations.contains(annRemoved)) {
430           //a new annotation that was deleted afterwards, remove it from all sets
431             DatabaseAnnotationSetImpl.this.addedAnnotations.remove(annRemoved);
432           }
433           else {
434 
435             //2. check if the annotation was updated, if so, remove it from the
436             //update list
437             if (DatabaseAnnotationSetImpl.this.updatedAnnotations.contains(annRemoved)) {
438               DatabaseAnnotationSetImpl.this.updatedAnnotations.remove(annRemoved);
439             }
440 
441             //3. add to the list with deleted anns
442             DatabaseAnnotationSetImpl.this.removedAnnotations.add(annRemoved);
443           }
444         }
445 
446     }
447 
448 
449 }