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

| DatabaseDocumentImpl |