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