|
DatabaseCorpusImpl |
|
1 /* 2 * DatabaseCorpusImpl.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 * Marin Dimitrov, 05/Nov/2001 12 * 13 * $Id: DatabaseCorpusImpl.java,v 1.10 2002/03/07 19:36:46 marin Exp $ 14 */ 15 16 package gate.corpora; 17 18 import java.util.*; 19 20 import junit.framework.*; 21 22 import gate.*; 23 import gate.persist.*; 24 import gate.annotation.*; 25 import gate.creole.*; 26 import gate.event.*; 27 import gate.util.*; 28 29 30 public class DatabaseCorpusImpl extends CorpusImpl 31 implements DatastoreListener, 32 EventAwareCorpus { 33 34 /** Debug flag */ 35 private static final boolean DEBUG = false; 36 37 private boolean featuresChanged; 38 private boolean nameChanged; 39 /** 40 * The listener for the events coming from the features. 41 */ 42 protected EventsHandler eventHandler; 43 protected List documentData; 44 protected List removedDocuments; 45 46 public DatabaseCorpusImpl() { 47 super(); 48 } 49 50 51 public DatabaseCorpusImpl(String _name, 52 DatabaseDataStore _ds, 53 Long _persistenceID, 54 FeatureMap _features, 55 Vector _dbDocs) { 56 57 super(); 58 59 this.name = _name; 60 this.dataStore = _ds; 61 this.lrPersistentId = _persistenceID; 62 this.features = _features; 63 // this.supportList = _dbDocs; 64 this.documentData = _dbDocs; 65 this.supportList = new ArrayList(this.documentData.size()); 66 this.removedDocuments = new ArrayList(); 67 68 //init the document list 69 for (int i=0; i< this.documentData.size(); i++) { 70 this.supportList.add(null); 71 } 72 73 this.featuresChanged = false; 74 this.nameChanged = false; 75 76 //3. add the listeners for the features 77 if (eventHandler == null) 78 eventHandler = new EventsHandler(); 79 this.features.addFeatureMapListener(eventHandler); 80 81 82 //4. add self as listener for the data store, so that we'll know when the DS is 83 //synced and we'll clear the isXXXChanged flags 84 this.dataStore.addDatastoreListener(this); 85 } 86 87 88 public boolean add(Object o){ 89 90 Assert.assertNotNull(o); 91 boolean result = false; 92 93 //accept only documents 94 if (false == o instanceof Document) { 95 throw new IllegalArgumentException(); 96 } 97 98 Document doc = (Document)o; 99 100 //assert docs are either transient or from the same datastore 101 if (isValidForAdoption(doc)) { 102 result = super.add(doc); 103 } 104 105 //add to doc data too 106 DocumentData newDocData = new DocumentData(doc.getName(),null); 107 this.documentData.add(newDocData); 108 109 if (result) { 110 fireDocumentAdded(new CorpusEvent(this, 111 doc, 112 this.supportList.size()-1, 113 CorpusEvent.DOCUMENT_ADDED)); 114 } 115 116 return result; 117 } 118 119 120 public void add(int index, Object element){ 121 122 Assert.assertNotNull(element); 123 Assert.assertTrue(index >= 0); 124 125 long collInitialSize = this.supportList.size(); 126 127 //accept only documents 128 if (false == element instanceof Document) { 129 throw new IllegalArgumentException(); 130 } 131 132 Document doc = (Document)element; 133 134 //assert docs are either transient or from the same datastore 135 if (isValidForAdoption(doc)) { 136 super.add(index,doc); 137 138 //add to doc data too 139 DocumentData newDocData = new DocumentData(doc.getName(),null); 140 this.documentData.add(index,newDocData); 141 142 //if added then fire event 143 if (this.supportList.size() > collInitialSize) { 144 fireDocumentAdded(new CorpusEvent(this, 145 doc, 146 index, 147 CorpusEvent.DOCUMENT_ADDED)); 148 } 149 } 150 } 151 152 153 154 public boolean addAll(Collection c){ 155 156 boolean collectionChanged = false; 157 158 Iterator it = c.iterator(); 159 while (it.hasNext()) { 160 Document doc = (Document)it.next(); 161 if (isValidForAdoption(doc)) { 162 collectionChanged |= add(doc); 163 } 164 } 165 166 return collectionChanged; 167 } 168 169 170 public boolean addAll(int index, Collection c){ 171 172 Assert.assertTrue(index >=0); 173 174 //funny enough add(index,element) returns void and not boolean 175 //so we can't use it 176 boolean collectionChanged = false; 177 int collInitialSize = this.supportList.size(); 178 int currIndex = index; 179 180 Iterator it = c.iterator(); 181 while (it.hasNext()) { 182 Document doc = (Document)it.next(); 183 if (isValidForAdoption(doc)) { 184 add(currIndex++,doc); 185 } 186 } 187 188 return (this.supportList.size() > collInitialSize); 189 } 190 191 192 private boolean isValidForAdoption(LanguageResource lr) { 193 194 Long lrID = (Long)lr.getLRPersistenceId(); 195 196 if (null == lrID || 197 (this.getDataStore() != null && lr.getDataStore().equals(this.getDataStore()))) { 198 return true; 199 } 200 else { 201 return false; 202 } 203 } 204 205 public void resourceAdopted(DatastoreEvent evt){ 206 } 207 208 public void resourceDeleted(DatastoreEvent evt){ 209 210 Assert.assertNotNull(evt); 211 Long deletedID = (Long)evt.getResourceID(); 212 Assert.assertNotNull(deletedID); 213 214 //unregister self as listener from the DataStore 215 if (deletedID.equals(this.getLRPersistenceId())) { 216 //someone deleted this corpus 217 this.supportList.clear(); 218 getDataStore().removeDatastoreListener(this); 219 } 220 221 //check if the ID is of a document the corpus contains 222 Iterator it = this.supportList.iterator(); 223 while (it.hasNext()) { 224 Document doc = (Document)it.next(); 225 if (doc.getLRPersistenceId().equals(deletedID)) { 226 this.supportList.remove(doc); 227 break; 228 } 229 } 230 } 231 232 public void resourceWritten(DatastoreEvent evt){ 233 Assert.assertNotNull(evt); 234 Assert.assertNotNull(evt.getResourceID()); 235 236 //is the event for us? 237 if (evt.getResourceID().equals(this.getLRPersistenceId())) { 238 //wow, the event is for me 239 //clear all flags, the content is synced with the DB 240 this.featuresChanged = 241 this.nameChanged = false; 242 243 this.removedDocuments.clear(); 244 } 245 } 246 247 public boolean isResourceChanged(int changeType) { 248 249 switch(changeType) { 250 251 case EventAwareLanguageResource.RES_FEATURES: 252 return this.featuresChanged; 253 case EventAwareLanguageResource.RES_NAME: 254 return this.nameChanged; 255 default: 256 throw new IllegalArgumentException(); 257 } 258 } 259 260 /** 261 * Returns true of an LR has been modified since the last sync. 262 * Always returns false for transient LRs. 263 */ 264 public boolean isModified() { 265 return this.isResourceChanged(EventAwareLanguageResource.RES_FEATURES) || 266 this.isResourceChanged(EventAwareLanguageResource.RES_NAME); 267 } 268 269 270 271 /** Sets the name of this resource*/ 272 public void setName(String name){ 273 super.setName(name); 274 275 this.nameChanged = true; 276 } 277 278 279 /** Set the feature set */ 280 public void setFeatures(FeatureMap features) { 281 //1. save them first, so we can remove the listener 282 FeatureMap oldFeatures = this.features; 283 284 super.setFeatures(features); 285 286 this.featuresChanged = true; 287 288 //4. sort out the listeners 289 if (eventHandler != null) 290 oldFeatures.removeFeatureMapListener(eventHandler); 291 else 292 eventHandler = new EventsHandler(); 293 this.features.addFeatureMapListener(eventHandler); 294 } 295 296 297 /** 298 * All the events from the features are handled by 299 * this inner class. 300 */ 301 class EventsHandler implements gate.event.FeatureMapListener { 302 public void featureMapUpdated(){ 303 //tell the document that its features have been updated 304 featuresChanged = true; 305 } 306 } 307 308 /** 309 * Overriden to remove the features listener, when the document is closed. 310 */ 311 public void cleanup() { 312 super.cleanup(); 313 if (eventHandler != null) 314 this.features.removeFeatureMapListener(eventHandler); 315 }///inner class EventsHandler 316 317 318 319 public void setInitData__$$__(Object data) { 320 321 HashMap initData = (HashMap)data; 322 323 this.name = (String)initData.get("CORP_NAME"); 324 this.dataStore = (DatabaseDataStore)initData.get("DS"); 325 this.lrPersistentId = (Long)initData.get("LR_ID"); 326 this.features = (FeatureMap)initData.get("CORP_FEATURES"); 327 this.supportList = (List)initData.get("CORP_SUPPORT_LIST"); 328 329 this.documentData = new ArrayList(this.supportList.size()); 330 this.removedDocuments = new ArrayList(); 331 332 //init the documentData list 333 for (int i=0; i< this.supportList.size(); i++) { 334 Document dbDoc = (Document)this.supportList.get(i); 335 DocumentData dd = new DocumentData(dbDoc.getName(),dbDoc.getLRPersistenceId()); 336 this.documentData.add(dd); 337 } 338 339 this.featuresChanged = false; 340 this.nameChanged = false; 341 342 //3. add the listeners for the features 343 if (eventHandler == null) 344 eventHandler = new EventsHandler(); 345 this.features.addFeatureMapListener(eventHandler); 346 347 348 //4. add self as listener for the data store, so that we'll know when the DS is 349 //synced and we'll clear the isXXXChanged flags 350 this.dataStore.addDatastoreListener(this); 351 } 352 353 public Object getInitData__$$__(Object initData) { 354 return null; 355 } 356 357 /** 358 * Gets the names of the documents in this corpus. 359 * @return a {@link List} of Strings representing the names of the documents 360 * in this corpus. 361 */ 362 public List getDocumentNames(){ 363 364 List docsNames = new ArrayList(); 365 366 if(this.documentData == null) 367 return docsNames; 368 369 Iterator iter = this.documentData.iterator(); 370 while (iter.hasNext()) { 371 DocumentData data = (DocumentData)iter.next(); 372 docsNames.add(data.getDocumentName()); 373 } 374 375 return docsNames; 376 } 377 378 379 /** 380 * Gets the name of a document in this corpus. 381 * @param index the index of the document 382 * @return a String value representing the name of the document at 383 * <tt>index</tt> in this corpus.<P> 384 */ 385 public String getDocumentName(int index){ 386 387 if (index >= this.documentData.size()) return "No such document"; 388 389 return ((DocumentData)this.documentData.get(index)).getDocumentName(); 390 } 391 392 /** 393 * returns a document in the coprus by index 394 * @param index the index of the document 395 * @return an Object value representing DatabaseDocumentImpl 396 */ 397 public Object get(int index){ 398 399 //0. preconditions 400 Assert.assertTrue(index >= 0); 401 Assert.assertTrue(index < this.documentData.size()); 402 Assert.assertTrue(index < this.supportList.size()); 403 404 if (index >= this.documentData.size()) 405 return null; 406 407 Object res = this.supportList.get(index); 408 409 //if the document is null, then I must get it from the database 410 if (null == res) { 411 Long currLRID = (Long)((DocumentData)this.documentData.get(index)).getPersistentID(); 412 FeatureMap params = Factory.newFeatureMap(); 413 params.put(DataStore.DATASTORE_FEATURE_NAME, this.getDataStore()); 414 params.put(DataStore.LR_ID_FEATURE_NAME, currLRID); 415 416 try { 417 Document dbDoc = (Document)Factory.createResource(DBHelper.DOCUMENT_CLASS, params); 418 419 if (DEBUG) { 420 Out.prln("Loaded document :" + dbDoc.getName()); 421 } 422 423 //change the result to the newly loaded doc 424 res = dbDoc; 425 426 //finally replace the doc with the instantiated version 427 Assert.assertNull(this.supportList.get(index)); 428 this.supportList.set(index, dbDoc); 429 } 430 catch (ResourceInstantiationException ex) { 431 Err.prln("Error reading document inside a serialised corpus."); 432 throw new GateRuntimeException(ex.getMessage()); 433 } 434 } 435 436 return res; 437 } 438 439 public Object remove(int index){ 440 441 //1. get the persistent id and add it to the removed list 442 DocumentData docData = (DocumentData)this.documentData.get(index); 443 Long removedID = (Long)docData.getPersistentID(); 444 // Assert.assertTrue(null != removedID); 445 //removedID may be NULL if the doc is still transient 446 447 //2. add to the list of removed documents 448 if (null != removedID) { 449 this.removedDocuments.add(removedID); 450 } 451 452 //3. delete 453 this.documentData.remove(index); 454 Document res = (Document)this.supportList.remove(index); 455 456 //4, fire events 457 fireDocumentRemoved(new CorpusEvent(DatabaseCorpusImpl.this, 458 res, 459 index, 460 CorpusEvent.DOCUMENT_REMOVED)); 461 return res; 462 463 } 464 465 466 public boolean remove(Object obj){ 467 468 //0. preconditions 469 Assert.assertNotNull(obj); 470 Assert.assertTrue(obj instanceof DatabaseDocumentImpl); 471 472 if (false == obj instanceof Document) { 473 return false; 474 } 475 476 Document doc = (Document) obj; 477 478 //see if we can find it first. If not, then judt return 479 int index = findDocument(doc); 480 if (index == -1) { 481 return false; 482 } 483 484 if(index < this.documentData.size()) { 485 //we found it, so remove it 486 487 //1. get the persistent id and add it to the removed list 488 DocumentData docData = (DocumentData)this.documentData.get(index); 489 Long removedID = (Long)docData.getPersistentID(); 490 //Assert.assertTrue(null != removedID); 491 //removed ID may be null - doc is still transient 492 493 //2. add to the list of removed documents 494 if (null != removedID) { 495 this.removedDocuments.add(removedID); 496 } 497 498 //3. delete 499 this.documentData.remove(index); 500 Document oldDoc = (Document) this.supportList.remove(index); 501 502 fireDocumentRemoved(new CorpusEvent(DatabaseCorpusImpl.this, 503 oldDoc, 504 index, 505 CorpusEvent.DOCUMENT_REMOVED)); 506 } 507 508 return true; 509 } 510 511 512 public int findDocument(Document doc) { 513 514 boolean found = false; 515 DocumentData docData = null; 516 517 //first try finding the document in memory 518 int index = this.supportList.indexOf(doc); 519 520 if (index > -1 && index < this.documentData.size()) { 521 return index; 522 } 523 524 //else try finding a document with the same name and persistent ID 525 Iterator iter = this.documentData.iterator(); 526 527 for (index = 0; iter.hasNext(); index++) { 528 docData = (DocumentData) iter.next(); 529 if (docData.getDocumentName().equals(doc.getName()) && 530 docData.getPersistentID().equals(doc.getLRPersistenceId())) { 531 found = true; 532 break; 533 } 534 } 535 536 if (found && index < this.documentData.size()) { 537 return index; 538 } 539 else { 540 return -1; 541 } 542 }//findDocument 543 544 545 public boolean contains(Object o){ 546 //return true if: 547 // - the document data list contains a document with such a name 548 // and persistent id 549 550 if(false == o instanceof Document) 551 return false; 552 553 int index = findDocument((Document) o); 554 555 if (index < 0) { 556 return false; 557 } 558 else { 559 return true; 560 } 561 } 562 563 public Iterator iterator(){ 564 return new DatabaseCorpusIterator(this.documentData); 565 } 566 567 public List getLoadedDocuments() { 568 return new ArrayList(this.supportList); 569 } 570 571 public List getRemovedDocuments() { 572 return new ArrayList(this.removedDocuments); 573 } 574 575 private class DatabaseCorpusIterator implements Iterator { 576 577 private Iterator docDataIter; 578 private List docDataList; 579 580 public DatabaseCorpusIterator(List docDataList) { 581 this.docDataList = docDataList; 582 this.docDataIter = this.docDataList.iterator(); 583 } 584 585 public boolean hasNext() { 586 return docDataIter.hasNext(); 587 } 588 589 public Object next(){ 590 591 //try finding a document with the same name and persistent ID 592 DocumentData docData = (DocumentData)docDataIter.next(); 593 int index = this.docDataList.indexOf(docData); 594 return DatabaseCorpusImpl.this.get(index); 595 } 596 597 public void remove() { 598 throw new UnsupportedOperationException("DatabaseCorpusImpl does not " + 599 "support remove in the iterators"); 600 } 601 } 602 }
|
DatabaseCorpusImpl |
|