DatabaseAnnotationSetImpl.java
001 /*
002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *
010  *  Kalina Bontcheva 21/10/2001
011  *
012  *  $Id: DatabaseAnnotationSetImpl.java 12006 2009-12-01 17:24:28Z thomas_heitz $
013  */
014 package gate.annotation;
015 
016 import java.util.*;
017 import junit.framework.Assert;
018 import gate.*;
019 import gate.corpora.DatabaseDocumentImpl;
020 import gate.corpora.DocumentImpl;
021 import gate.event.*;
022 
023 // import gate.persist.*;
024 public class DatabaseAnnotationSetImpl extends AnnotationSetImpl
025                                                                 implements
026                                                                 DatastoreListener,
027                                                                 EventAwareAnnotationSet,
028                                                                 AnnotationListener {
029   /**
030    * The listener for the events coming from the document (annotations and
031    * annotation sets added or removed).
032    */
033   // = protected EventsHandler eventHandler;
034   protected HashSet<Annotation> addedAnnotations = new HashSet<Annotation>();
035   protected HashSet<Annotation> removedAnnotations = new HashSet<Annotation>();
036   protected HashSet<Annotation> updatedAnnotations = new HashSet<Annotation>();
037   private boolean validating = false;
038 
039   public void assertValid() {
040     if(validatingreturn;
041     validating = true;
042     // avoid recursion
043     // doc can't be null
044     Assert.assertNotNull(this.doc);
045     // doc.assertValid();
046     validating = false;
047   }
048 
049   /** Construction from Document. */
050   public DatabaseAnnotationSetImpl(Document doc) {
051     super(doc);
052     // preconditions
053     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
054     // = eventHandler = new EventsHandler();
055     // = this.addAnnotationSetListener(eventHandler);
056     // add self as listener for sync events from the document's datastore
057     // 00 doc.getDataStore().removeDatastoreListener(this);
058     doc.getDataStore().addDatastoreListener(this);
059     // ((VerboseHashMap)annotsById).setOwner(this);
060   // construction from document
061 
062   /** Construction from Document and name. */
063   public DatabaseAnnotationSetImpl(Document doc, String name) {
064     super(doc, name);
065     // preconditions
066     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
067     // = eventHandler = new EventsHandler();
068     // = this.addAnnotationSetListener(eventHandler);
069     // add self as listener for sync events from the document's datastore
070     // 00 doc.getDataStore().removeDatastoreListener(this);
071     doc.getDataStore().addDatastoreListener(this);
072     // ((VerboseHashMap)annotsById).setOwner(this);
073   // construction from document and name
074 
075   /** Construction from Document and name. */
076   public DatabaseAnnotationSetImpl(Document doc, AnnotationSet c) {
077     this(c);
078     this.doc = (DocumentImpl)doc;
079     // add self as listener for sync events from the document's datastore
080     // 00 doc.getDataStore().removeDatastoreListener(this);
081     // 00 doc.getDataStore().addDatastoreListener(this);
082   // construction from document and name
083 
084   /** Construction from Document and name. */
085   public DatabaseAnnotationSetImpl(Document doc, String name, AnnotationSet c) {
086     this(doc, c);
087     this.name = name;
088     // add self as listener for sync events from the document's datastore
089     // 00 doc.getDataStore().removeDatastoreListener(this);
090     doc.getDataStore().addDatastoreListener(this);
091   // construction from document and name
092 
093   /** Construction from Collection (which must be an AnnotationSet) */
094   public DatabaseAnnotationSetImpl(AnnotationSet cthrows ClassCastException {
095     super(c);
096     // also copy the name, because that super one doesn't
097     AnnotationSet as = (AnnotationSet)c;
098     this.name = as.getName();
099     // = eventHandler = new EventsHandler();
100     // = this.addAnnotationSetListener(eventHandler);
101     Iterator<Annotation> iter = this.iterator();
102     while(iter.hasNext())
103       iter.next().addAnnotationListener(this);
104     Document doc = as.getDocument();
105     // add self as listener for sync events from the document's datastore
106     // 00 doc.getDataStore().removeDatastoreListener(this);
107     doc.getDataStore().addDatastoreListener(this);
108     // ((VerboseHashMap)annotsById).setOwner(this);
109   // construction from collection
110 
111   public String toString() {
112     return super.toString() "added annots: " + addedAnnotations
113             "removed annots: " + removedAnnotations + "updated annots: "
114             + updatedAnnotations;
115   }
116 
117   // /** Two AnnotationSet are equal if their name, the documents of which
118   // belong
119   // * to the AnnotationSets and annotations from the sets are the same
120   // */
121   // public boolean equals(Object other) {
122   //
123   // if (false == other instanceof DatabaseAnnotationSetImpl) {
124   // return super.equals(other);
125   // }
126   //
127   // boolean result = true;
128   //
129   // if (!super.equals((AnnotationSet)other)) {
130   // return false;
131   // }
132   //
133   // DatabaseAnnotationSetImpl target = (DatabaseAnnotationSetImpl)other;
134   //
135   // result = result && this.addedAnnotations.equals(target.addedAnnotations)
136   // && this.removedAnnotations.equals(target.removedAnnotations)
137   // && this.updatedAnnotations.equals(target.updatedAnnotations);
138   //
139   // //FINALLY - CHECK THAT THE SET IS FROM THE SAME DOCUMENT *INSTANCE*
140   // //DO *NOT* USE EQUALS()
141   // result = result && ( this.getDocument() == target.getDocument());
142   //
143   // return result;
144   // } // equals
145   /**
146    * All the events from the document or its annotation sets are handled by this
147    * inner class.
148    */
149   /*
150    * class EventsHandler implements AnnotationListener AnnotationSetListener{
151    
152    
153    * public void annotationAdded(gate.event.AnnotationSetEvent e) {
154    * AnnotationSet set = (AnnotationSet)e.getSource(); String setName =
155    * set.getName(); if (setName != DatabaseAnnotationSetImpl.this.name && !
156    * setName.equals(DatabaseAnnotationSetImpl.this.name)) return; Annotation ann =
157    * e.getAnnotation(); ann.addAnnotationListener(this);
158    * DatabaseAnnotationSetImpl.this.addedAnnotations.add(ann); }
159    
160    * public void annotationRemoved(AnnotationSetEvent e){ AnnotationSet set =
161    * (AnnotationSet)e.getSource(); String setName = set.getName(); if (setName !=
162    * DatabaseAnnotationSetImpl.this.name && !
163    * setName.equals(DatabaseAnnotationSetImpl.this.name)) return; Annotation ann =
164    * e.getAnnotation(); ann.removeAnnotationListener(this);
165    
166    * //1. check if this annot is in the newly created annotations set if
167    * (addedAnnotations.contains(ann)) { //a new annotatyion that was deleted
168    * afterwards, remove it from all sets
169    * DatabaseAnnotationSetImpl.this.addedAnnotations.remove(ann); return; } //2.
170    * check if the annotation was updated, if so, remove it from the //update
171    * list if (updatedAnnotations.contains(ann)) {
172    * DatabaseAnnotationSetImpl.this.updatedAnnotations.remove(ann); }
173    
174    * DatabaseAnnotationSetImpl.this.removedAnnotations.add(ann); }
175    
176    
177    * public void annotationUpdated(AnnotationEvent e){ Annotation ann =
178    * (Annotation) e.getSource();
179    
180    * //check if the annotation is newly created //if so, do not add it to the
181    * update list, since it was not stored in the //database yet, so the most
182    * recent value will be inserted into the DB upon //DataStore::sync() if
183    * (addedAnnotations.contains(ann)) { return; }
184    
185    * DatabaseAnnotationSetImpl.this.updatedAnnotations.add(ann); }
186    
187    * }//inner class EventsHandler
188    
189    */
190   /**
191    * Called by a datastore when a new resource has been adopted
192    */
193   public void resourceAdopted(DatastoreEvent evt) {
194     Assert.assertNotNull(evt);
195     Assert.assertNotNull(evt.getResourceID());
196     // check if this is our resource
197     // rememeber - a data store handles many resources
198     if(evt.getResourceID().equals(this.doc.getLRPersistenceId())) {
199       // System.out.println("ASNAME=["+this.getName()+"], resourceAdopted()
200       // called");
201       // we're synced wtith the DB now
202       clearChangeLists();
203     }
204   }
205 
206   /**
207    * Called by a datastore when a resource has been deleted
208    */
209   public void resourceDeleted(DatastoreEvent evt) {
210     Assert.assertNotNull(evt);
211     Assert.assertNotNull(evt.getResourceID());
212     // check if this is our resource
213     // rememeber - a data store handles many resources
214     if(evt.getResourceID().equals(this.doc.getLRPersistenceId())) {
215       // System.out.println("ASNAME=["+this.getName()+"],resourceDeleted()
216       // called");
217       // unregister self
218       // this is not the correct way, since the resource is null in this case
219       // DataStore ds = (DataStore)evt.getResource();
220       DataStore ds = this.doc.getDataStore();
221       if(ds != nullds.removeDatastoreListener(this);
222     }
223   }// resourceDeleted
224 
225   /**
226    * Called by a datastore when a resource has been wrote into the datastore
227    */
228   public void resourceWritten(DatastoreEvent evt) {
229     Assert.assertNotNull(evt);
230     Assert.assertNotNull(evt.getResourceID());
231     // check if this is our resource
232     // rememeber - a data store handles many resources
233     if(evt.getResourceID().equals(this.doc.getLRPersistenceId())) {
234       // System.out.println("ASNAME=["+this.getName()+"],resourceWritten()
235       // called");
236       // clear lists with updates - we're synced with the DB
237       clearChangeLists();
238     }
239   }
240 
241   private void clearChangeLists() {
242     // ok, we're synced now, clear all lists with changed IDs
243     synchronized(this) {
244       // System.out.println("clearing lists...");
245       this.addedAnnotations.clear();
246       this.updatedAnnotations.clear();
247       this.removedAnnotations.clear();
248     }
249   }
250 
251   public Collection<Annotation> getAddedAnnotations() {
252     // System.out.println("getAddedIDs() called");
253     HashSet<Annotation> result = new HashSet<Annotation>();
254     result.addAll(this.addedAnnotations);
255     return result;
256   }
257 
258   public Collection<Annotation> getChangedAnnotations() {
259     // System.out.println("getChangedIDs() called");
260     HashSet<Annotation> result = new HashSet<Annotation>();
261     result.addAll(this.updatedAnnotations);
262     return result;
263   }
264 
265   public Collection<Annotation> getRemovedAnnotations() {
266     // System.out.println("getremovedIDs() called...");
267     HashSet<Annotation> result = new HashSet<Annotation>();
268     result.addAll(this.removedAnnotations);
269     return result;
270   }
271 
272   public void annotationUpdated(AnnotationEvent e) {
273     Annotation ann = (Annotation)e.getSource();
274     // check if the annotation is newly created
275     // if so, do not add it to the update list, since it was not stored in the
276     // database yet, so the most recent value will be inserted into the DB upon
277     // DataStore::sync()
278     if(false == this.addedAnnotations.contains(ann)) {
279       this.updatedAnnotations.add(ann);
280     }
281     // sanity check
282     Assert.assertTrue(false == this.removedAnnotations.contains(ann));
283   }
284 
285   /** Add an existing annotation. Returns true when the set is modified. */
286   public boolean add(Annotation othrows ClassCastException {
287     // check if this annotation was removed beforehand
288     // if so then just delete it from the list of annotations waiting for
289     // persistent removal
290     if(this.removedAnnotations.contains(o)) {
291       this.removedAnnotations.remove(o);
292     }
293     boolean result = super.add(o);
294     if(true == result) {
295       // register as listener for update events from this annotation
296       o.addAnnotationListener(this);
297       // add to the newly created annotations set
298       this.addedAnnotations.add(o);
299     }
300     return result;
301   }
302 
303   /**
304    
305    @param e
306    */
307   protected void fireAnnotationRemoved(AnnotationSetEvent e) {
308     if(annotationSetListeners != null) {
309       Vector listeners = annotationSetListeners;
310       int count = listeners.size();
311       for(int i = 0; i < count; i++) {
312         ((AnnotationSetListener)listeners.elementAt(i)).annotationRemoved(e);
313       }
314     }
315   }
316 
317   /** Remove an element from this set. */
318   public boolean remove(Object othrows ClassCastException {
319     boolean result = super.remove(o);
320     if(true == result) {
321       // UNregister as listener for update events from this annotation
322       Annotation ann = (Annotation)o;
323       ann.removeAnnotationListener(this);
324       // 1. check if this annot is in the newly created annotations set
325       if(this.addedAnnotations.contains(ann)) {
326         // a new annotation that was deleted afterwards, remove it from all sets
327         this.addedAnnotations.remove(ann);
328       else {
329         // 2. check if the annotation was updated, if so, remove it from the
330         // update list
331         if(this.updatedAnnotations.contains(ann)) {
332           this.updatedAnnotations.remove(ann);
333         }
334         // 3. add to the list with deleted anns
335         this.removedAnnotations.add(ann);
336       }
337     }
338     return result;
339   }
340 
341   public Iterator<Annotation> iterator() {
342     return new DatabaseAnnotationSetIterator();
343   }
344 
345   class DatabaseAnnotationSetIterator
346                                      extends
347                                        AnnotationSetImpl.AnnotationSetIterator {
348     public void remove() {
349       super.remove();
350       Annotation annRemoved = lastNext;
351       // UNregister as listener for update events from this annotation
352       annRemoved.removeAnnotationListener(DatabaseAnnotationSetImpl.this);
353       // 1. check if this annot is in the newly created annotations set
354       if(DatabaseAnnotationSetImpl.this.addedAnnotations.contains(annRemoved)) {
355         // a new annotation that was deleted afterwards, remove it from all sets
356         DatabaseAnnotationSetImpl.this.addedAnnotations.remove(annRemoved);
357       else {
358         // 2. check if the annotation was updated, if so, remove it from the
359         // update list
360         if(DatabaseAnnotationSetImpl.this.updatedAnnotations
361                 .contains(annRemoved)) {
362           DatabaseAnnotationSetImpl.this.updatedAnnotations.remove(annRemoved);
363         }
364         // 3. add to the list with deleted anns
365         DatabaseAnnotationSetImpl.this.removedAnnotations.add(annRemoved);
366       }
367     }
368   }
369 }