|
DatabaseDocumentImpl |
|
1 /* 2 * DatabaseDocumentImpl.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, 16/Oct/2001 12 * 13 * $Id: DatabaseDocumentImpl.java,v 1.54 2002/03/07 15:19:22 marin Exp $ 14 */ 15 16 package gate.corpora; 17 18 19 import java.sql.*; 20 import java.io.*; 21 import java.util.*; 22 import java.net.*; 23 24 import oracle.jdbc.driver.*; 25 import junit.framework.*; 26 27 import gate.*; 28 import gate.util.*; 29 import gate.persist.*; 30 import gate.annotation.*; 31 import gate.creole.*; 32 import gate.event.*; 33 34 public class DatabaseDocumentImpl extends DocumentImpl 35 implements //DatastoreListener, 36 //Document, 37 EventAwareDocument { 38 39 private static final boolean DEBUG = false; 40 41 private boolean isContentRead; 42 private Object contentLock; 43 private Connection jdbcConn; 44 45 private boolean contentChanged; 46 private boolean featuresChanged; 47 private boolean nameChanged; 48 private boolean documentChanged; 49 50 private Collection removedAnotationSets; 51 private Collection addedAnotationSets; 52 53 private Document parentDocument; 54 55 /** 56 * The listener for the events coming from the features. 57 */ 58 protected EventsHandler eventHandler; 59 60 61 public DatabaseDocumentImpl() { 62 63 //super(); 64 contentLock = new Object(); 65 66 this.namedAnnotSets = new HashMap(); 67 // this.defaultAnnots = new DatabaseAnnotationSetImpl(this); 68 69 this.isContentRead = false; 70 71 this.contentChanged = false; 72 this.featuresChanged = false; 73 this.nameChanged = false; 74 this.documentChanged = false; 75 76 this.removedAnotationSets = new Vector(); 77 this.addedAnotationSets = new Vector(); 78 79 parentDocument = null; 80 } 81 82 public DatabaseDocumentImpl(Connection conn) { 83 84 //super(); 85 contentLock = new Object(); 86 87 this.namedAnnotSets = new HashMap(); 88 // this.defaultAnnots = new DatabaseAnnotationSetImpl(this); 89 90 this.isContentRead = false; 91 this.jdbcConn = conn; 92 93 this.contentChanged = false; 94 this.featuresChanged = false; 95 this.nameChanged = false; 96 this.documentChanged = false; 97 98 this.removedAnotationSets = new Vector(); 99 this.addedAnotationSets = new Vector(); 100 101 parentDocument = null; 102 } 103 104 105 /* public DatabaseDocumentImpl(Connection _conn, 106 String _name, 107 DatabaseDataStore _ds, 108 Long _persistenceID, 109 DocumentContent _content, 110 FeatureMap _features, 111 Boolean _isMarkupAware, 112 URL _sourceURL, 113 Long _urlStartOffset, 114 Long _urlEndOffset, 115 AnnotationSet _default, 116 Map _named) { 117 118 //this.jdbcConn = _conn; 119 this(_conn); 120 121 this.name = _name; 122 this.dataStore = _ds; 123 this.lrPersistentId = _persistenceID; 124 this.content = _content; 125 this.isContentRead = true; 126 this.features = _features; 127 this.markupAware = _isMarkupAware; 128 this.sourceUrl = _sourceURL; 129 this.sourceUrlStartOffset = _urlStartOffset; 130 this.sourceUrlEndOffset = _urlEndOffset; 131 132 //annotations 133 //1. default 134 _setAnnotations(null,_default); 135 136 //2. named (if any) 137 if (null != _named) { 138 Iterator itNamed = _named.values().iterator(); 139 while (itNamed.hasNext()){ 140 AnnotationSet currSet = (AnnotationSet)itNamed.next(); 141 //add them all to the DBAnnotationSet 142 _setAnnotations(currSet.getName(),currSet); 143 } 144 } 145 146 //3. add the listeners for the features 147 if (eventHandler == null) 148 eventHandler = new EventsHandler(); 149 this.features.addFeatureMapListener(eventHandler); 150 151 //4. add self as listener for the data store, so that we'll know when the DS is 152 //synced and we'll clear the isXXXChanged flags 153 this.dataStore.addDatastoreListener(this); 154 } 155 */ 156 157 /** The content of the document: a String for text; MPEG for video; etc. */ 158 public DocumentContent getContent() { 159 160 //1. if this is a child document then return the content of the parent resource 161 if (null != this.parentDocument) { 162 return this.parentDocument.getContent(); 163 } 164 else { 165 //2. assert that no one is reading from DB now 166 synchronized(this.contentLock) { 167 if (false == this.isContentRead) { 168 _readContent(); 169 this.isContentRead = true; 170 } 171 } 172 173 //return content 174 return super.getContent(); 175 } 176 } 177 178 private void _readContent() { 179 180 //preconditions 181 if (null == getLRPersistenceId()) { 182 throw new GateRuntimeException("can't construct a DatabaseDocument - not associated " + 183 " with any data store"); 184 } 185 186 if (false == getLRPersistenceId() instanceof Long) { 187 throw new GateRuntimeException("can't construct a DatabaseDocument - " + 188 " invalid persistence ID"); 189 } 190 191 Long lrID = (Long)getLRPersistenceId(); 192 //0. preconditions 193 Assert.assertNotNull(lrID); 194 Assert.assertTrue(false == this.isContentRead); 195 Assert.assertNotNull(this.content); 196 197 //1. read from DB 198 PreparedStatement pstmt = null; 199 ResultSet rs = null; 200 201 try { 202 String sql = " select v1.enc_name, " + 203 " v1.dc_character_content, " + 204 " v1.dc_binary_content, " + 205 " v1.dc_content_type " + 206 " from "+Gate.DB_OWNER+".v_content v1 " + 207 " where v1.lr_id = ? "; 208 209 pstmt = this.jdbcConn.prepareStatement(sql); 210 pstmt.setLong(1,lrID.longValue()); 211 pstmt.execute(); 212 rs = pstmt.getResultSet(); 213 214 rs.next(); 215 216 String encoding = rs.getString(1); 217 if (encoding.equals(DBHelper.DUMMY_ENCODING)) { 218 //no encoding was specified for this document 219 encoding = ""; 220 } 221 Clob clb = rs.getClob(2); 222 Blob blb = rs.getBlob(3); 223 long contentType = rs.getLong(4); 224 225 Assert.assertTrue(DBHelper.CHARACTER_CONTENT == contentType); 226 227 StringBuffer buff = new StringBuffer(); 228 OracleDataStore.readCLOB(clb,buff); 229 230 //2. set data members that were not previously initialized 231 this.content = new DocumentContentImpl(buff.toString()); 232 this.encoding = encoding; 233 } 234 catch(SQLException sqle) { 235 throw new SynchronisationException("can't read content from DB: ["+ sqle.getMessage()+"]"); 236 } 237 catch(IOException ioe) { 238 throw new SynchronisationException(ioe); 239 } 240 finally { 241 try { 242 DBHelper.cleanup(rs); 243 DBHelper.cleanup(pstmt); 244 } 245 catch(PersistenceException pe) { 246 throw new SynchronisationException("JDBC error: ["+ pe.getMessage()+"]"); 247 } 248 } 249 } 250 251 252 /** Get the encoding of the document content source */ 253 public String getEncoding() { 254 255 //1. assert that no one is reading from DB now 256 synchronized(this.contentLock) { 257 if (false == this.isContentRead) { 258 _readContent(); 259 260 this.isContentRead = true; 261 } 262 } 263 264 return super.getEncoding(); 265 } 266 267 /** Returns a map with the named annotation sets. It returns <code>null</code> 268 * if no named annotaton set exists. */ 269 public Map getNamedAnnotationSets() { 270 271 Vector annNames = new Vector(); 272 273 PreparedStatement pstmt = null; 274 ResultSet rs = null; 275 276 //1. get the names of all sets 277 try { 278 String sql = " select as_name " + 279 " from "+Gate.DB_OWNER+".v_annotation_set " + 280 " where lr_id = ? " + 281 " and as_name is not null"; 282 283 pstmt = this.jdbcConn.prepareStatement(sql); 284 pstmt.setLong(1,((Long)this.lrPersistentId).longValue()); 285 pstmt.execute(); 286 rs = pstmt.getResultSet(); 287 288 while (rs.next()) { 289 annNames.add(rs.getString("as_name")); 290 } 291 } 292 catch(SQLException sqle) { 293 throw new SynchronisationException("can't get named annotatios: ["+ sqle.getMessage()+"]"); 294 } 295 finally { 296 try { 297 DBHelper.cleanup(rs); 298 DBHelper.cleanup(pstmt); 299 } 300 catch(PersistenceException pe) { 301 throw new SynchronisationException("JDBC error: ["+ pe.getMessage()+"]"); 302 } 303 } 304 305 //2. read annotations 306 for (int i=0; i< annNames.size(); i++) { 307 //delegate because of the data is already read getAnnotations() will just return 308 getAnnotations((String)annNames.elementAt(i)); 309 } 310 311 //3. delegate to the parent method 312 return super.getNamedAnnotationSets(); 313 314 } // getNamedAnnotationSets 315 316 317 /** Get the default set of annotations. The set is created if it 318 * doesn't exist yet. 319 */ 320 public AnnotationSet getAnnotations() { 321 322 //1. read from DB 323 _getAnnotations(null); 324 325 //2. is there such set in the DB? 326 if (null == this.defaultAnnots) { 327 //create a DatabaseAnnotationSetImpl 328 //NOTE: we create the set and then delegate to the super mehtod, otherwise 329 //the super mehtod will create AnnotationSetImpl instead of DatabaseAnnotationSetImpl 330 //which will not work with DatabaseDocumentImpl 331 AnnotationSet aset = new DatabaseAnnotationSetImpl(this); 332 333 //set internal member 334 this.defaultAnnots = aset; 335 336 //3. fire events 337 fireAnnotationSetAdded(new DocumentEvent(this, 338 DocumentEvent.ANNOTATION_SET_ADDED, 339 null)); 340 } 341 342 //4. delegate 343 return super.getAnnotations(); 344 } // getAnnotations() 345 346 347 /** Get a named set of annotations. Creates a new set if one with this 348 * name doesn't exist yet. 349 * If the provided name is null then it returns the default annotation set. 350 */ 351 public AnnotationSet getAnnotations(String name) { 352 353 //0. preconditions 354 Assert.assertNotNull(name); 355 356 //1. read from DB if the set is there at all 357 _getAnnotations(name); 358 359 //2. is there such set in the DB? 360 if (false == this.namedAnnotSets.keySet().contains(name)) { 361 //create a DatabaseAnnotationSetImpl 362 //NOTE: we create the set and then delegate to the super mehtod, otherwise 363 //the super mehtod will create AnnotationSetImpl instead of DatabaseAnnotationSetImpl 364 //which will not work with DatabaseDocumentImpl 365 AnnotationSet aset = new DatabaseAnnotationSetImpl(this,name); 366 367 //add to internal collection 368 this.namedAnnotSets.put(name,aset); 369 370 //add the set name to the list with the recently created sets 371 this.addedAnotationSets.add(name); 372 373 //3. fire events 374 DocumentEvent evt = new DocumentEvent(this, DocumentEvent.ANNOTATION_SET_ADDED, name); 375 fireAnnotationSetAdded(evt); 376 } 377 378 //3. delegate 379 return super.getAnnotations(name); 380 } 381 382 383 private void _getAnnotations(String name) { 384 385 AnnotationSet as = null; 386 387 //preconditions 388 if (null == getLRPersistenceId()) { 389 throw new GateRuntimeException("can't construct a DatabaseDocument - not associated " + 390 " with any data store"); 391 } 392 393 if (false == getLRPersistenceId() instanceof Long) { 394 throw new GateRuntimeException("can't construct a DatabaseDocument - " + 395 " invalid persistence ID"); 396 } 397 398 //have we already read this set? 399 400 if (null == name) { 401 //default set 402 if (this.defaultAnnots != null) { 403 //the default set is alredy read - do nothing 404 //super methods will take care 405 return; 406 } 407 } 408 else { 409 //named set 410 if (this.namedAnnotSets.containsKey(name)) { 411 //we've already read it - do nothing 412 //super methods will take care 413 return; 414 } 415 } 416 417 Long lrID = (Long)getLRPersistenceId(); 418 Long asetID = null; 419 //0. preconditions 420 Assert.assertNotNull(lrID); 421 422 //1. read a-set info 423 PreparedStatement pstmt = null; 424 ResultSet rs = null; 425 try { 426 String sql = " select as_id " + 427 " from "+Gate.DB_OWNER+".v_annotation_set " + 428 " where lr_id = ? "; 429 //do we have aset name? 430 String clause = null; 431 if (null != name) { 432 clause = " and as_name = ? "; 433 } 434 else { 435 clause = " and as_name is null "; 436 } 437 sql = sql + clause; 438 439 pstmt = this.jdbcConn.prepareStatement(sql); 440 pstmt.setLong(1,lrID.longValue()); 441 if (null != name) { 442 pstmt.setString(2,name); 443 } 444 pstmt.execute(); 445 rs = pstmt.getResultSet(); 446 447 if (rs.next()) { 448 //ok, there is such aset in the DB 449 asetID = new Long(rs.getLong(1)); 450 } 451 else { 452 //wow, there is no such aset, so create new ... 453 //... by delegating to the super method 454 return; 455 } 456 457 //1.5 cleanup 458 DBHelper.cleanup(rs); 459 DBHelper.cleanup(pstmt); 460 461 //2. read annotation Features 462 HashMap featuresByAnnotationID = _readFeatures(asetID); 463 464 //3. read annotations 465 AnnotationSetImpl transSet = new AnnotationSetImpl(this); 466 String hint = "/*+ use_nl(v.t_annotation v.t_as_annotation) " + 467 " use_nl(v.t_annotation_type v.t_annotation) "+ 468 " */"; 469 470 String sql1 = " select "+hint+ 471 " ann_local_id, " + 472 " at_name, " + 473 " start_offset, " + 474 " end_offset " + 475 " from "+Gate.DB_OWNER+".v_annotation v" + 476 " where asann_as_id = ? "; 477 478 if (DEBUG) Out.println(">>>>> asetID=["+asetID+"]"); 479 480 pstmt = this.jdbcConn.prepareStatement(sql1); 481 pstmt.setLong(1,asetID.longValue()); 482 ((OraclePreparedStatement)pstmt).setRowPrefetch(DBHelper.CHINK_SIZE_LARGE); 483 pstmt.execute(); 484 rs = pstmt.getResultSet(); 485 486 while (rs.next()) { 487 //1. read data memebers 488 Integer annID = new Integer(rs.getInt(1)); 489 String type = rs.getString(2); 490 Long startOffset = new Long(rs.getLong(3)); 491 Long endOffset = new Long(rs.getLong(4)); 492 493 if (DEBUG) Out.println("ann_local_id=["+annID+"]"); 494 if (DEBUG) Out.println("start_off=["+startOffset+"]"); 495 if (DEBUG) Out.println("end_off=["+endOffset+"]"); 496 497 //2. get the features 498 FeatureMap fm = (FeatureMap)featuresByAnnotationID.get(annID); 499 //fm should NOT be null 500 if (null == fm) { 501 fm = new SimpleFeatureMapImpl(); 502 } 503 504 //3. add to annotation set 505 transSet.add(annID,startOffset,endOffset,type,fm); 506 }//while 507 508 //1.5, create a-set 509 if (null == name) { 510 as = new DatabaseAnnotationSetImpl(this, transSet); 511 } 512 else { 513 as = new DatabaseAnnotationSetImpl(this,name, transSet); 514 } 515 } 516 catch(SQLException sqle) { 517 throw new SynchronisationException("can't read annotations from DB: ["+ sqle.getMessage()+"]"); 518 } 519 catch(InvalidOffsetException oe) { 520 throw new SynchronisationException(oe); 521 } 522 catch(PersistenceException pe) { 523 throw new SynchronisationException("JDBC error: ["+ pe.getMessage()+"]"); 524 } 525 finally { 526 try { 527 DBHelper.cleanup(rs); 528 DBHelper.cleanup(pstmt); 529 } 530 catch(PersistenceException pe) { 531 throw new SynchronisationException("JDBC error: ["+ pe.getMessage()+"]"); 532 } 533 } 534 535 536 //4. update internal data members 537 if (name == null) { 538 //default as 539 this.defaultAnnots = as; 540 } 541 else { 542 //named as 543 this.namedAnnotSets.put(name,as); 544 } 545 546 //don't return the new aset, the super method will take care 547 return; 548 } 549 550 551 552 553 private HashMap _readFeatures(Long asetID) { 554 555 PreparedStatement pstmt = null; 556 ResultSet rs = null; 557 558 //1 559 String prevKey = DBHelper.DUMMY_FEATURE_KEY; 560 String currKey = null; 561 562 Integer prevAnnID = null; 563 Integer currAnnID = null; 564 565 Object currFeatureValue = null; 566 Vector currFeatureArray = new Vector(); 567 568 HashMap currFeatures = new HashMap(); 569 FeatureMap annFeatures = null; 570 571 HashMap featuresByAnnotID = new HashMap(); 572 573 //2. read the features from DB 574 try { 575 String sql = " select /*+ use_nl(v.t_annotation v.t_as_annotation) "+ 576 " use_nl(v.t_feature v.t_annotation) "+ 577 " index(v.t_feature xt_feature_01) "+ 578 " use_nl(v.t_feature_key v.t_feature) "+ 579 " full(v.t_feature_key) "+ 580 " */ "+ 581 " " + 582 " ann_local_id, " + 583 " key, " + 584 " ft_value_type, " + 585 " ft_number_value, " + 586 " ft_character_value, " + 587 " ft_long_character_value, " + 588 " ft_binary_value " + 589 " from "+Gate.DB_OWNER+".v_annotation_features " + 590 " where set_id = ? " + 591 " order by ann_local_id,key "; 592 593 pstmt = this.jdbcConn.prepareStatement(sql); 594 pstmt.setLong(1,asetID.longValue()); 595 ((OraclePreparedStatement)pstmt).setRowPrefetch(DBHelper.CHINK_SIZE_LARGE); 596 pstmt.execute(); 597 rs = pstmt.getResultSet(); 598 599 while (rs.next()) { 600 //NOTE: because there are LOBs in the resulset 601 //the columns should be read in the order they appear 602 //in the query 603 604 prevAnnID = currAnnID; 605 currAnnID = new Integer(rs.getInt(1)); 606 607 //2.1 is this a new Annotation? 608 if (!currAnnID.equals(prevAnnID) && prevAnnID != null) { 609 //new one 610 //2.1.1 normalize the hashmap with the features, and add 611 //the elements into a new FeatureMap 612 annFeatures = new SimpleFeatureMapImpl(); 613 Set entries = currFeatures.entrySet(); 614 Iterator itFeatureArrays = entries.iterator(); 615 616 while(itFeatureArrays.hasNext()) { 617 Map.Entry currEntry = (Map.Entry)itFeatureArrays.next(); 618 String key = (String)currEntry.getKey(); 619 Vector val = (Vector)currEntry.getValue(); 620 621 //add to feature map normalized array 622 Assert.assertTrue(val.size() >= 1); 623 624 if (val.size() == 1) { 625 //the single elemnt of the array 626 annFeatures.put(key,val.firstElement()); 627 } 628 else { 629 //the whole array 630 annFeatures.put(key,val); 631 } 632 }//while 633 634 //2.1.2. add the featuremap for this annotation to the hashmap 635 featuresByAnnotID.put(prevAnnID,annFeatures); 636 //2.1.3. clear temp hashtable with feature vectors 637 currFeatures.clear(); 638 /*??*/ prevAnnID = currAnnID; 639 }//if -- is new annotation 640 641 currKey = rs.getString(2); 642 Long valueType = new Long(rs.getLong(3)); 643 644 //we don't quite know what is the type of the NUMBER 645 //stored in DB 646 Object numberValue = null; 647 648 //for all numeric types + boolean -> read from DB as appropriate 649 //Java object 650 switch(valueType.intValue()) { 651 652 case DBHelper.VALUE_TYPE_BOOLEAN: 653 numberValue = new Boolean(rs.getBoolean(4)); 654 break; 655 656 case DBHelper.VALUE_TYPE_FLOAT: 657 numberValue = new Float(rs.getFloat(4)); 658 break; 659 660 case DBHelper.VALUE_TYPE_INTEGER: 661 numberValue = new Integer(rs.getInt(4)); 662 break; 663 664 case DBHelper.VALUE_TYPE_LONG: 665 numberValue = new Long(rs.getLong(4)); 666 break; 667 668 default: 669 //do nothing, will be handled in the next switch statement 670 } 671 672 //don't forget to read the rest of the current row 673 String stringValue = rs.getString(5); 674 Clob clobValue = rs.getClob(6); 675 Blob blobValue = rs.getBlob(7); 676 677 switch(valueType.intValue()) { 678 679 case DBHelper.VALUE_TYPE_NULL: 680 currFeatureValue = null; 681 break; 682 683 case DBHelper.VALUE_TYPE_BINARY: 684 throw new MethodNotImplementedException(); 685 686 case DBHelper.VALUE_TYPE_BOOLEAN: 687 case DBHelper.VALUE_TYPE_FLOAT: 688 case DBHelper.VALUE_TYPE_INTEGER: 689 case DBHelper.VALUE_TYPE_LONG: 690 currFeatureValue = numberValue; 691 break; 692 693 case DBHelper.VALUE_TYPE_STRING: 694 //this one is tricky too 695 //if the string is < 4000 bytes long then it's stored as varchar2 696 //otherwise as CLOB 697 if (null == stringValue) { 698 //oops, we got CLOB 699 StringBuffer temp = new StringBuffer(); 700 OracleDataStore.readCLOB(clobValue,temp); 701 currFeatureValue = temp.toString(); 702 } 703 else { 704 currFeatureValue = stringValue; 705 } 706 break; 707 708 default: 709 throw new SynchronisationException("Invalid feature type found in DB, value is ["+valueType+"]"); 710 }//switch 711 712 //ok, we got the key/value pair now 713 //2.2 is this a new feature key? 714 if (false == currFeatures.containsKey(currKey)) { 715 //new key 716 Vector keyValue = new Vector(); 717 keyValue.add(currFeatureValue); 718 currFeatures.put(currKey,keyValue); 719 } 720 else { 721 //key is present, append to existing vector 722 ((Vector)currFeatures.get(currKey)).add(currFeatureValue); 723 } 724 725 prevKey = currKey; 726 }//while 727 728 729 //2.3 process the last Annotation left 730 annFeatures = new SimpleFeatureMapImpl(); 731 732 Set entries = currFeatures.entrySet(); 733 Iterator itFeatureArrays = entries.iterator(); 734 735 while(itFeatureArrays.hasNext()) { 736 Map.Entry currEntry = (Map.Entry)itFeatureArrays.next(); 737 String key = (String)currEntry.getKey(); 738 Vector val = (Vector)currEntry.getValue(); 739 740 //add to feature map normalized array 741 Assert.assertTrue(val.size() >= 1); 742 743 if (val.size() == 1) { 744 //the single elemnt of the array 745 annFeatures.put(key,val.firstElement()); 746 } 747 else { 748 //the whole array 749 annFeatures.put(key,val); 750 } 751 }//while 752 753 //2.3.1. add the featuremap for this annotation to the hashmap 754 if (null != currAnnID) { 755 // do we have features at all for this annotation? 756 featuresByAnnotID.put(currAnnID,annFeatures); 757 } 758 759 //3. return the hashmap 760 return featuresByAnnotID; 761 } 762 catch(SQLException sqle) { 763 throw new SynchronisationException("can't read content from DB: ["+ sqle.getMessage()+"]"); 764 } 765 catch(IOException sqle) { 766 throw new SynchronisationException("can't read content from DB: ["+ sqle.getMessage()+"]"); 767 } 768 finally { 769 try { 770 DBHelper.cleanup(rs); 771 DBHelper.cleanup(pstmt); 772 } 773 catch(PersistenceException pe) { 774 throw new SynchronisationException("JDBC error: ["+ pe.getMessage()+"]"); 775 } 776 } 777 } 778 779 780 /** Set method for the document content */ 781 public void setContent(DocumentContent content) { 782 783 //if the document is a child document then setContent()is prohibited 784 if (null != this.parentDocument) { 785 Err.prln("content of document ["+this.name+"] cannot be changed!"); 786 return; 787 } 788 else { 789 super.setContent(content); 790 this.contentChanged = true; 791 } 792 } 793 794 /** Set the feature set */ 795 public void setFeatures(FeatureMap features) { 796 //1. save them first, so we can remove the listener 797 FeatureMap oldFeatures = this.features; 798 799 super.setFeatures(features); 800 801 this.featuresChanged = true; 802 803 //4. sort out the listeners 804 if (eventHandler != null) 805 oldFeatures.removeFeatureMapListener(eventHandler); 806 else 807 eventHandler = new EventsHandler(); 808 this.features.addFeatureMapListener(eventHandler); 809 } 810 811 /** Sets the name of this resource*/ 812 public void setName(String name){ 813 super.setName(name); 814 815 this.nameChanged = true; 816 } 817 818 819 private List getAnnotationsForOffset(AnnotationSet aDumpAnnotSet,Long offset){ 820 throw new MethodNotImplementedException(); 821 } 822 823 /** Generate and return the next annotation ID */ 824 /* public Integer getNextAnnotationId() { 825 826 //1.try to get ID fromt he pool 827 if (DEBUG) { 828 Out.println(">>> get annID called..."); 829 } 830 //is there anything left in the pool? 831 if (this.SEQUENCE_POOL_SIZE == this.poolMarker) { 832 //oops, pool is empty 833 fillSequencePool(); 834 this.poolMarker = 0; 835 } 836 837 return this.sequencePool[this.poolMarker++]; 838 839 return super.getNextAnnotationId(); 840 } // getNextAnnotationId 841 842 843 public void setNextAnnotationId(int aNextAnnotationId){ 844 845 //if u get this exception then u definitely don't have an idea what u're doing 846 throw new UnsupportedOperationException("Annotation IDs cannot be changed in " + 847 "database stores"); 848 }// setNextAnnotationId(); 849 850 851 private void fillSequencePool() { 852 853 if(DEBUG) { 854 Out.println("filling ID lot..."); 855 } 856 857 CallableStatement stmt = null; 858 try { 859 stmt = this.jdbcConn.prepareCall( 860 "{ call "+Gate.DB_OWNER+".persist.get_id_lot(?,?,?,?,?,?,?,?,?,?) }"); 861 stmt.registerOutParameter(1,java.sql.Types.BIGINT); 862 stmt.registerOutParameter(2,java.sql.Types.BIGINT); 863 stmt.registerOutParameter(3,java.sql.Types.BIGINT); 864 stmt.registerOutParameter(4,java.sql.Types.BIGINT); 865 stmt.registerOutParameter(5,java.sql.Types.BIGINT); 866 stmt.registerOutParameter(6,java.sql.Types.BIGINT); 867 stmt.registerOutParameter(7,java.sql.Types.BIGINT); 868 stmt.registerOutParameter(8,java.sql.Types.BIGINT); 869 stmt.registerOutParameter(9,java.sql.Types.BIGINT); 870 stmt.registerOutParameter(10,java.sql.Types.BIGINT); 871 stmt.execute(); 872 873 for (int i=0; i < this.SEQUENCE_POOL_SIZE; i++) { 874 //JDBC countsa from 1, not from 0 875 this.sequencePool[0] = new Integer(stmt.getInt(i+1)); 876 } 877 } 878 catch(SQLException sqle) { 879 throw new SynchronisationException("can't get Annotation ID pool: ["+ sqle.getMessage()+"]"); 880 } 881 finally { 882 try { 883 DBHelper.cleanup(stmt); 884 } 885 catch(PersistenceException pe) { 886 throw new SynchronisationException("JDBC error: ["+ pe.getMessage()+"]"); 887 } 888 } 889 } 890 */ 891 892 public void setNextNodeId(int nextID){ 893 Assert.assertTrue(nextID >= 0); 894 this.nextNodeId = nextID; 895 } 896 897 898 public boolean isResourceChanged(int changeType) { 899 900 switch(changeType) { 901 902 case EventAwareLanguageResource.DOC_CONTENT: 903 return this.contentChanged; 904 case EventAwareLanguageResource.RES_FEATURES: 905 return this.featuresChanged; 906 case EventAwareLanguageResource.RES_NAME: 907 return this.nameChanged; 908 case EventAwareLanguageResource.DOC_MAIN: 909 return this.documentChanged; 910 default: 911 throw new IllegalArgumentException(); 912 } 913 914 } 915 916 private void _setAnnotations(String setName,Collection annotations) { 917 918 if (null == setName) { 919 Assert.assertTrue(null == this.defaultAnnots); 920 this.defaultAnnots = new DatabaseAnnotationSetImpl(this,annotations); 921 922 //add to the set of loaded a-sets but do not add its annotations to the 923 //list of new annotations 924 // this.loadedAnnotSets.add(this.defaultAnnots); 925 } 926 else { 927 Assert.assertTrue(false == this.namedAnnotSets.containsKey(setName)); 928 AnnotationSet annSet = new DatabaseAnnotationSetImpl(this,setName,annotations); 929 this.namedAnnotSets.put(setName,annSet); 930 931 //add to the set of loaded a-sets but do not add its annotations to the 932 //list of new annotations 933 // this.loadedAnnotSets.add(annSet); 934 } 935 } 936 937 /** Set method for the document's URL */ 938 public void setSourceUrl(URL sourceUrl) { 939 940 this.documentChanged = true; 941 super.setSourceUrl(sourceUrl); 942 } // setSourceUrl 943 944 945 /** Documents may be packed within files; in this case an optional pair of 946 * offsets refer to the location of the document. This method sets the 947 * end offset. 948 */ 949 public void setSourceUrlEndOffset(Long sourceUrlEndOffset) { 950 951 this.documentChanged = true; 952 super.setSourceUrlEndOffset(sourceUrlEndOffset); 953 } // setSourceUrlStartOffset 954 955 956 /** Documents may be packed within files; in this case an optional pair of 957 * offsets refer to the location of the document. This method sets the 958 * start offset. 959 */ 960 public void setSourceUrlStartOffset(Long sourceUrlStartOffset) { 961 962 this.documentChanged = true; 963 super.setSourceUrlStartOffset(sourceUrlStartOffset); 964 } // setSourceUrlStartOffset 965 966 /** Make the document markup-aware. This will trigger the creation 967 * of a DocumentFormat object at Document initialisation time; the 968 * DocumentFormat object will unpack the markup in the Document and 969 * add it as annotations. Documents are <B>not</B> markup-aware by default. 970 * 971 * @param b markup awareness status. 972 */ 973 public void setMarkupAware(Boolean newMarkupAware) { 974 975 this.documentChanged = true; 976 super.setMarkupAware(newMarkupAware); 977 } 978 979 /** 980 * All the events from the features are handled by 981 * this inner class. 982 */ 983 class EventsHandler implements gate.event.FeatureMapListener { 984 public void featureMapUpdated(){ 985 //tell the document that its features have been updated 986 featuresChanged = true; 987 } 988 } 989 990 /** 991 * Overriden to remove the features listener, when the document is closed. 992 */ 993 public void cleanup() { 994 super.cleanup(); 995 if (eventHandler != null) 996 this.features.removeFeatureMapListener(eventHandler); 997 }///inner class EventsHandler 998 999 1000 /** 1001 * Called by a datastore when a new resource has been adopted 1002 */ 1003 public void resourceAdopted(DatastoreEvent evt){ 1004 } 1005 1006 /** 1007 * Called by a datastore when a resource has been deleted 1008 */ 1009 public void resourceDeleted(DatastoreEvent evt){ 1010 1011 Assert.assertNotNull(evt); 1012 Assert.assertNotNull(evt.getResourceID()); 1013 1014 //unregister self as listener from the DataStore 1015 if (evt.getResourceID().equals(this.getLRPersistenceId())) { 1016 //someone deleted this document 1017 getDataStore().removeDatastoreListener(this); 1018 } 1019 1020 }//resourceDeleted 1021 1022 /** 1023 * Called by a datastore when a resource has been wrote into the datastore 1024 */ 1025 public void resourceWritten(DatastoreEvent evt){ 1026 1027 Assert.assertNotNull(evt); 1028 Assert.assertNotNull(evt.getResourceID()); 1029 1030 //is the event for us? 1031 if (evt.getResourceID().equals(this.getLRPersistenceId())) { 1032 //wow, the event is for me 1033 //clear all flags, the content is synced with the DB 1034 this.contentChanged = 1035 this.documentChanged = 1036 this.featuresChanged = 1037 this.nameChanged = false; 1038 1039 this.removedAnotationSets.clear(); 1040 this.addedAnotationSets.clear(); 1041 } 1042 1043 1044 } 1045 1046 public Collection getLoadedAnnotationSets() { 1047 1048 //never return the data member - return a clone 1049 Assert.assertNotNull(this.namedAnnotSets); 1050 Vector result = new Vector(this.namedAnnotSets.values()); 1051 if (null != this.defaultAnnots) { 1052 result.add(this.defaultAnnots); 1053 } 1054 1055 return result; 1056 } 1057 1058 1059 public Collection getRemovedAnnotationSets() { 1060 1061 //return a clone 1062 return new Vector(this.removedAnotationSets); 1063 } 1064 1065 public Collection getAddedAnnotationSets() { 1066 1067 //return a clone 1068 return new Vector(this.addedAnotationSets); 1069 } 1070 1071 public void removeAnnotationSet(String name) { 1072 1073 //1. add to the list of removed a-sets 1074 this.removedAnotationSets.add(name); 1075 1076 //if the set was read from the DB then it is registered as datastore listener and ... 1077 //there may be chnges in it 1078 //NOTE that default set cannot be reoved, so we just ignore it 1079 1080 if (this.namedAnnotSets.keySet().contains(name)) { 1081 //set was loaded 1082 AnnotationSet aset = (AnnotationSet)this.namedAnnotSets.get(name); 1083 1084 Assert.assertNotNull(aset); 1085 Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl); 1086 1087 //3. unregister it as a DataStoreListener 1088 this.dataStore.removeDatastoreListener((DatastoreListener)aset); 1089 } 1090 1091 //4. delegate 1092 super.removeAnnotationSet(name); 1093 } 1094 1095 /** 1096 * Returns true of an LR has been modified since the last sync. 1097 * Always returns false for transient LRs. 1098 */ 1099 public boolean isModified() { 1100 return this.isResourceChanged(EventAwareLanguageResource.DOC_CONTENT) || 1101 this.isResourceChanged(EventAwareLanguageResource.RES_FEATURES) || 1102 this.isResourceChanged(EventAwareLanguageResource.RES_NAME) || 1103 this.isResourceChanged(EventAwareLanguageResource.DOC_MAIN); 1104 } 1105 1106 1107 /** 1108 * Returns the parent LR of this LR. 1109 * Only relevant for LRs that support shadowing. Most do not by default. 1110 */ 1111 public LanguageResource getParent() 1112 throws PersistenceException,SecurityException { 1113 1114 return this.parentDocument; 1115 }//getParent 1116 1117 /** 1118 * Sets the parent LR of this LR. 1119 * Only relevant for LRs that support shadowing. Most do not by default. 1120 */ 1121 public void setParent(LanguageResource parentLR) 1122 throws PersistenceException,SecurityException { 1123 1124 //0. preconditions 1125 Assert.assertNotNull(parentLR); 1126 1127 if (false == parentLR instanceof DatabaseDocumentImpl) { 1128 throw new IllegalArgumentException("invalid parent resource set"); 1129 } 1130 1131 //1. 1132 this.parentDocument = (Document)parentLR; 1133 1134 }//setParent 1135 1136 public void setInitData__$$__(Object data) { 1137 1138 HashMap initData = (HashMap)data; 1139 1140 this.jdbcConn = (Connection)initData.get("JDBC_CONN"); 1141 this.dataStore = (DatabaseDataStore)initData.get("DS"); 1142 this.lrPersistentId = (Long)initData.get("LR_ID"); 1143 this.name = (String)initData.get("DOC_NAME"); 1144 this.content = (DocumentContent)initData.get("DOC_CONTENT"); 1145 this.isContentRead = true; 1146 this.features = (FeatureMap)initData.get("DOC_FEATURES"); 1147 this.markupAware = (Boolean)initData.get("DOC_MARKUP_AWARE"); 1148 this.sourceUrl = (URL)initData.get("DOC_SOURCE_URL"); 1149 this.sourceUrlStartOffset = (Long)initData.get("DOC_SOURCE_URL_START"); 1150 this.sourceUrlEndOffset = (Long)initData.get("DOC_SOURCE_URL_END"); 1151 1152 Integer nextNodeID = (Integer)initData.get("DOC_NEXT_NODE_ID"); 1153 if (null != nextNodeID) { 1154 this.setNextNodeId(nextNodeID.intValue()); 1155 } 1156 1157 Integer nextAnnID = (Integer)initData.get("DOC_NEXT_ANN_ID"); 1158 if (null != nextAnnID) { 1159 this.setNextAnnotationId(nextAnnID.intValue()); 1160 } 1161 1162 this.parentDocument = (Document)initData.get("PARENT_LR"); 1163 1164 //annotations 1165 //1. default 1166 AnnotationSet _default = (AnnotationSet)initData.get("DOC_DEFAULT_ANNOTATIONS"); 1167 if (null != _default) { 1168 _setAnnotations(null,_default); 1169 } 1170 1171 //2. named (if any) 1172 Map _named = (Map)initData.get("DOC_NAMED_ANNOTATION_SETS"); 1173 if (null != _named) { 1174 Iterator itNamed = _named.values().iterator(); 1175 while (itNamed.hasNext()){ 1176 AnnotationSet currSet = (AnnotationSet)itNamed.next(); 1177 //add them all to the DBAnnotationSet 1178 _setAnnotations(currSet.getName(),currSet); 1179 } 1180 } 1181 1182 //3. add the listeners for the features (if any) 1183 if (null != this.features) { 1184 if (eventHandler == null) 1185 eventHandler = new EventsHandler(); 1186 this.features.addFeatureMapListener(eventHandler); 1187 } 1188 1189 //4. add self as listener for the data store, so that we'll know when the DS is 1190 //synced and we'll clear the isXXXChanged flags 1191 if (null != this.dataStore) { 1192 this.dataStore.addDatastoreListener(this); 1193 } 1194 1195 } 1196 1197 public Object getInitData__$$__(Object initData) { 1198 return null; 1199 } 1200 1201}
|
DatabaseDocumentImpl |
|