OracleDataStore.java
0001 /*
0002  *  OracleDataStore.java
0003  *
0004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Marin Dimitrov, 18/Sep/2001
0013  *
0014  *  $Id: OracleDataStore.java 12006 2009-12-01 17:24:28Z thomas_heitz $
0015  */
0016 
0017 package gate.persist;
0018 
0019 import java.io.*;
0020 import java.net.URL;
0021 import java.sql.*;
0022 import java.util.*;
0023 
0024 import junit.framework.Assert;
0025 import oracle.jdbc.driver.OracleCallableStatement;
0026 import oracle.sql.*;
0027 
0028 import gate.*;
0029 import gate.corpora.DatabaseCorpusImpl;
0030 import gate.corpora.DatabaseDocumentImpl;
0031 import gate.security.SecurityException;
0032 import gate.security.SecurityInfo;
0033 import gate.util.*;
0034 
0035 public class OracleDataStore extends JDBCDataStore {
0036 
0037   /** Name of this resource */
0038   private static final String DS_COMMENT = "GATE Oracle datastore";
0039 
0040   /** the icon for this resource */
0041   private static final String DS_ICON_NAME = "ora_ds";
0042 
0043   /** Debug flag */
0044   private static final boolean DEBUG = false;
0045 
0046   /** "true" value for Oracle (supports no boolean type) */
0047   private static final int ORACLE_TRUE = 1;
0048   /** "false" value for Oracle (supports no boolean type) */
0049   private static final int ORACLE_FALSE = 0;
0050 
0051   /** size of the Oracle varrays used for bulc inserts */
0052   private static final int VARRAY_SIZE = 10;
0053 
0054   /** the size in bytes if varchar2 column in Oracle
0055    *  when a String is stored in Oracle it may be too long
0056    *  for a varchar2 value, and then CLOB will be used
0057    *  Note that the limit is in bytes, not in characters, so
0058    *  in the worst case this will limit the string to 4000/3 characters
0059    *  */
0060   private static final int ORACLE_VARCHAR_LIMIT_BYTES = 4000;
0061 
0062   /** maximum number of bytes that represent a char in UTF8 database */
0063   private static final int UTF_BYTES_PER_CHAR_MAX = 3;
0064 
0065   /** maximum number of characters per string stored as varchar2
0066    *  if longer then stored as CLOB
0067    *   */
0068   private static final int ORACLE_VARCHAR_MAX_SYMBOLS =
0069                                   ORACLE_VARCHAR_LIMIT_BYTES/UTF_BYTES_PER_CHAR_MAX;
0070 
0071   /** read buffer size (for reading CLOBs) */
0072   private static final int INTERNAL_BUFFER_SIZE = 16*1024;
0073 
0074   /** default constructor - just call the super constructor
0075    *  (may change in the future)
0076    *  */
0077   public OracleDataStore() {
0078 
0079     super();
0080 
0081     this.datastoreComment = DS_COMMENT;
0082     this.iconName = DS_ICON_NAME;
0083   }
0084 
0085 
0086 
0087   /** Set the URL for the underlying storage mechanism. */
0088   public void setStorageUrl(String storageUrlthrows PersistenceException {
0089 
0090     super.setStorageUrl(storageUrl);
0091 
0092   }
0093 
0094 
0095 
0096   /** Get the URL for the underlying storage mechanism. */
0097   public String getStorageUrl() {
0098 
0099     return super.getStorageUrl();
0100   }
0101 
0102 
0103 
0104   /**
0105    * Create a new data store. <B>NOTE:</B> for some data stores
0106    * creation is an system administrator task; in such cases this
0107    * method will throw an UnsupportedOperationException.
0108    */
0109   public void create()
0110   throws PersistenceException, UnsupportedOperationException {
0111 
0112     super.create();
0113   }
0114 
0115 
0116 
0117   /** Open a connection to the data store. */
0118   public void open() throws PersistenceException {
0119 
0120     super.open();
0121 
0122     /*try {
0123     //set statement caching for Oracle
0124       ((OracleConnection)this.jdbcConn).setStmtCacheSize(50);
0125     }
0126     catch(SQLException sqle) {
0127       throw new PersistenceException(sqle);
0128     }*/
0129   }
0130 
0131 
0132 
0133   /** Close the data store. */
0134   public void close() throws PersistenceException {
0135 
0136     super.close();
0137   }
0138 
0139 
0140 
0141   /**
0142    * Delete a resource from the data store.
0143    @param lrId a data-store specific unique identifier for the resource
0144    @param lrClassName class name of the type of resource
0145    */
0146 /*
0147   public void delete(String lrClassName, Object lrId)
0148   throws PersistenceException,SecurityException {
0149     //0. preconditions
0150     if (false == lrId instanceof Long) {
0151       throw new IllegalArgumentException();
0152     }
0153 
0154     if (!lrClassName.equals(DBHelper.DOCUMENT_CLASS) &&
0155         !lrClassName.equals(DBHelper.CORPUS_CLASS)) {
0156       throw new IllegalArgumentException("Only Corpus and Document classes are supported" +
0157                                           " by Database data store");
0158     }
0159 
0160     //1. check session
0161     if (null == this.session) {
0162       throw new SecurityException("session not set");
0163     }
0164 
0165     if (false == this.ac.isValidSession(this.session)) {
0166       throw new SecurityException("invalid session supplied");
0167     }
0168 
0169     //2. check permissions
0170     if (false == canWriteLR(lrId)) {
0171       throw new SecurityException("insufficient privileges");
0172     }
0173 
0174     //3. try to lock document, so that we'll be sure no one is editing it
0175     //NOTE: use the private method
0176     User lockingUser = this.getLockingUser((Long)lrId);
0177     User currUser = this.session.getUser();
0178 
0179     if (null != lockingUser && false == lockingUser.equals(currUser)) {
0180       //oops, someone is editing now
0181       throw new PersistenceException("LR locked by another user");
0182     }
0183 
0184     boolean transFailed = false;
0185     try {
0186       //4. autocommit should be FALSE because of LOBs
0187       beginTrans();
0188 
0189       //5. perform changes, if anything goes wrong, rollback
0190       if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
0191         deleteDocument((Long)lrId);
0192       }
0193       else {
0194         deleteCorpus((Long)lrId);
0195       }
0196 
0197       //6. done, commit
0198       commitTrans();
0199     }
0200     catch(PersistenceException pe) {
0201       transFailed = true;
0202       throw(pe);
0203     }
0204     finally {
0205       //problems?
0206       if (transFailed) {
0207         rollbackTrans();
0208       }
0209     }
0210 
0211     //7, unlock
0212     //do nothing - the resource does not exist anymore
0213 
0214     //8. delete from the list of dependent resources
0215     boolean resourceFound = false;
0216     Iterator it = this.dependentResources.iterator();
0217     while (it.hasNext()) {
0218       LanguageResource lr = (LanguageResource)it.next();
0219       if (lr.getLRPersistenceId().equals(lrId)) {
0220         resourceFound = true;
0221         it.remove();
0222         break;
0223       }
0224     }
0225 
0226     //Assert.assertTrue(resourceFound);
0227 
0228     //9. let the world know about it
0229     fireResourceDeleted(
0230       new DatastoreEvent(this, DatastoreEvent.RESOURCE_DELETED, null, lrId));
0231 
0232     //10. unload the resource form the GUI
0233     try {
0234       unloadLR((Long)lrId);
0235     }
0236     catch(GateException ge) {
0237       Err.prln("can't unload resource from GUI...");
0238     }
0239   }
0240 */
0241 
0242 
0243   /**
0244    *  helper method for delete()
0245    *  never call it directly beause proper events will not be fired
0246    */
0247   protected void deleteDocument(Long lrId)
0248   throws PersistenceException {
0249 
0250     //0. preconditions
0251     Assert.assertNotNull(lrId);
0252 
0253     CallableStatement stmt = null;
0254 
0255     //1. delete from DB
0256     try {
0257       stmt = this.jdbcConn.prepareCall(
0258                       "{ call "+Gate.DB_OWNER+".persist.delete_document(?) }");
0259       stmt.setLong(1,lrId.longValue());
0260       stmt.execute();
0261     }
0262     catch(SQLException sqle) {
0263       throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]");
0264     }
0265     finally {
0266       DBHelper.cleanup(stmt);
0267     }
0268   }
0269 
0270 
0271 
0272   /**
0273    *  helper method for delete()
0274    *  never call it directly beause proper events will not be fired
0275    */
0276   protected void deleteCorpus(Long lrId)
0277   throws PersistenceException {
0278 
0279     Long ID = (Long)lrId;
0280 
0281     CallableStatement stmt = null;
0282 
0283     try {
0284       stmt = this.jdbcConn.prepareCall(
0285                       "{ call "+Gate.DB_OWNER+".persist.delete_corpus(?) }");
0286       stmt.setLong(1,ID.longValue());
0287       stmt.execute();
0288     }
0289     catch(SQLException sqle) {
0290       throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]");
0291     }
0292     finally {
0293       DBHelper.cleanup(stmt);
0294     }
0295   }
0296 
0297 
0298 
0299 
0300 
0301   /**
0302    * Set method for the autosaving behaviour of the data store.
0303    <B>NOTE:</B> many types of datastore have no auto-save function,
0304    * in which case this will throw an UnsupportedOperationException.
0305    */
0306   public void setAutoSaving(boolean autoSaving)
0307   throws UnsupportedOperationException,PersistenceException {
0308 
0309     super.setAutoSaving(autoSaving);
0310   }
0311 
0312 
0313 
0314   /** Get the autosaving behaviour of the LR. */
0315   public boolean isAutoSaving() {
0316     throw new MethodNotImplementedException();
0317   }
0318 
0319 
0320   /**
0321    *  helper for adopt()
0322    *  never call directly
0323    */
0324   protected Long createLR(String lrType,
0325                           String lrName,
0326                           SecurityInfo si,
0327                           Long lrParentID)
0328     throws PersistenceException,SecurityException {
0329 
0330     //0. preconditions
0331     Assert.assertNotNull(lrName);
0332 
0333     //1. check the session
0334 //    if (this.ac.isValidSession(s) == false) {
0335 //      throw new SecurityException("invalid session provided");
0336 //    }
0337 
0338     //2. create a record in DB
0339     CallableStatement stmt = null;
0340 
0341     try {
0342       stmt = this.jdbcConn.prepareCall(
0343                     "{ call "+Gate.DB_OWNER+".persist.create_lr(?,?,?,?,?,?,?) }");
0344       stmt.setLong(1,si.getUser().getID().longValue());
0345       stmt.setLong(2,si.getGroup().getID().longValue());
0346       stmt.setString(3,lrType);
0347       stmt.setString(4,lrName);
0348       stmt.setInt(5,si.getAccessMode());
0349       if (null == lrParentID) {
0350         stmt.setNull(6,java.sql.Types.BIGINT);
0351       }
0352       else {
0353         stmt.setLong(6,lrParentID.longValue());
0354       }
0355       //Oracle numbers are BIGNINT
0356       stmt.registerOutParameter(7,java.sql.Types.BIGINT);
0357       stmt.execute();
0358 
0359       Long result =  new Long(stmt.getLong(7));
0360       return result;
0361     }
0362     catch(SQLException sqle) {
0363 
0364       switch(sqle.getErrorCode()) {
0365         case DBHelper.X_ORACLE_INVALID_LR_TYPE:
0366           throw new PersistenceException("can't create LR [step 3] in DB, invalid LR Type");
0367         default:
0368           throw new PersistenceException(
0369                 "can't create LR [step 3] in DB : ["+ sqle.getMessage()+"]");
0370       }
0371     }
0372     finally {
0373       DBHelper.cleanup(stmt);
0374     }
0375   }
0376 
0377 
0378 
0379   /**
0380    *  updates the content of the document if it is binary or a long string
0381    *  (that does not fit into VARCHAR2)
0382    */
0383 //  private void updateDocumentContent(Long docContentID,DocumentContent content)
0384   protected void updateDocumentContent(Long docID,DocumentContent content)
0385   throws PersistenceException {
0386 
0387     //1. get LOB locators from DB
0388     PreparedStatement pstmt = null;
0389     ResultSet rs = null;
0390     CallableStatement cstmt = null;
0391     try {
0392       String sql =  "select dc.dc_id, "+
0393                     "       dc.dc_content_type, " +
0394                     "       dc.dc_character_content, " +
0395                     "       dc.dc_binary_content " +
0396                     "from "+gate.Gate.DB_OWNER+".t_doc_content dc , " +
0397                             gate.Gate.DB_OWNER+".t_document doc " +
0398                     "where  dc.dc_id = doc.doc_content_id " +
0399 //--was                    "       and doc.doc_content_id = ? " +
0400                     "       and doc.doc_id = ? " +
0401                     "for update ";
0402       pstmt = this.jdbcConn.prepareStatement(sql);
0403       pstmt.setLong(1,docID.longValue());
0404       rs = pstmt.executeQuery();
0405 
0406       //rs = pstmt.getResultSet();
0407 
0408       rs.next();
0409       //important: read the objects in the order they appear in
0410       //the ResultSet, otherwise data may be lost
0411       Long contentID = new Long(rs.getLong("dc_id"));
0412       long contentType = rs.getLong("DC_CONTENT_TYPE");
0413       Clob clob = (Clob)rs.getClob("dc_character_content");
0414       Blob blob = (Blob)rs.getBlob("dc_binary_content");
0415 
0416       Assert.assertTrue(contentType == DBHelper.CHARACTER_CONTENT ||
0417                     contentType == DBHelper.BINARY_CONTENT ||
0418                     contentType == DBHelper.EMPTY_CONTENT);
0419 
0420 
0421       //2. write data using the LOB locators
0422       //NOTE: so far only character content is supported
0423       writeCLOB(content.toString(),clob);
0424       long newContentType = DBHelper.CHARACTER_CONTENT;
0425 
0426       //3. update content type
0427       cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.change_content_type(?,?) }");
0428       cstmt.setLong(1,contentID.longValue());
0429       cstmt.setLong(2,newContentType);
0430       cstmt.execute();
0431     }
0432     catch(IOException ioe) {
0433       throw new PersistenceException("can't update document content in DB : ["+
0434                                       ioe.getMessage()+"]");
0435     }
0436     catch(SQLException sqle) {
0437       throw new PersistenceException("can't update document content in DB : ["+
0438                                       sqle.getMessage()+"]");
0439     }
0440     finally {
0441       DBHelper.cleanup(rs);
0442       DBHelper.cleanup(pstmt);
0443       DBHelper.cleanup(cstmt);
0444     }
0445 
0446   }
0447 
0448 
0449 
0450   /**
0451    * helper for adopt
0452    * creates a LR of type Document
0453    */
0454 /*  protected Document createDocument(Document doc,SecurityInfo secInfo)
0455   throws PersistenceException,SecurityException {
0456 
0457     //delegate, set to Null
0458     return createDocument(doc,null,secInfo);
0459   }
0460 */
0461 
0462   /**
0463    * helper for adopt
0464    * never call directly
0465    */
0466   protected Long createDoc(Long _lrID,
0467                           URL _docURL,
0468                           String _docEncoding,
0469                           Long _docStartOffset,
0470                           Long _docEndOffset,
0471                           Boolean _docIsMarkupAware,
0472                           Long _corpusID)
0473     throws PersistenceException {
0474 
0475     CallableStatement cstmt = null;
0476     Long docID = null;
0477 
0478     try {
0479       cstmt = this.jdbcConn.prepareCall(
0480                 "{ call "+Gate.DB_OWNER+".persist.create_document(?,?,?,?,?,?,?,?) }");
0481       cstmt.setLong(1,_lrID.longValue());
0482       if (_docURL == null) {
0483         cstmt.setNull(2,java.sql.Types.VARCHAR);
0484       }else{
0485         cstmt.setString(2,_docURL.toString());
0486       }
0487       //do we have doc encoding?
0488       if (null == _docEncoding) {
0489         cstmt.setNull(3,java.sql.Types.VARCHAR);
0490      }
0491       else {
0492         cstmt.setString(3,_docEncoding);
0493       }
0494       //do we have start offset?
0495       if (null==_docStartOffset) {
0496         cstmt.setNull(4,java.sql.Types.NUMERIC);
0497       }
0498       else {
0499         cstmt.setLong(4,_docStartOffset.longValue());
0500       }
0501       //do we have end offset?
0502       if (null==_docEndOffset) {
0503         cstmt.setNull(5,java.sql.Types.NUMERIC);
0504       }
0505       else {
0506         cstmt.setLong(5,_docEndOffset.longValue());
0507       }
0508 
0509       cstmt.setBoolean(6,_docIsMarkupAware.booleanValue());
0510 
0511       //is the document part of a corpus?
0512       if (null == _corpusID) {
0513         cstmt.setNull(7,java.sql.Types.BIGINT);
0514       }
0515       else {
0516         cstmt.setLong(7,_corpusID.longValue());
0517       }
0518 
0519       //results
0520       cstmt.registerOutParameter(8,java.sql.Types.BIGINT);
0521 
0522       cstmt.execute();
0523       docID = new Long(cstmt.getLong(8));
0524       return docID;
0525 
0526     }
0527     catch(SQLException sqle) {
0528       throw new PersistenceException("can't create document [step 4] in DB: ["+ sqle.getMessage()+"]");
0529     }
0530     finally {
0531       DBHelper.cleanup(cstmt);
0532     }
0533 
0534   }
0535 
0536 
0537 
0538   /** creates an entry for annotation set in the database */
0539   protected void createAnnotationSet(Long lrID, AnnotationSet aset)
0540     throws PersistenceException {
0541 
0542     //1. create a-set
0543     String asetName = aset.getName();
0544     Long asetID = null;
0545 
0546     //DB stuff
0547     CallableStatement stmt = null;
0548     try {
0549       stmt = this.jdbcConn.prepareCall(
0550                     "{ call "+Gate.DB_OWNER+".persist.create_annotation_set(?,?,?) }");
0551       stmt.setLong(1,lrID.longValue());
0552 
0553       if (null == asetName) {
0554         stmt.setNull(2,java.sql.Types.VARCHAR);
0555       }
0556       else {
0557         stmt.setString(2,asetName);
0558       }
0559       stmt.registerOutParameter(3,java.sql.Types.BIGINT);
0560       stmt.execute();
0561 
0562       asetID = new Long(stmt.getLong(3));
0563     }
0564     catch(SQLException sqle) {
0565       throw new PersistenceException("can't create a-set [step 1] in DB: ["+ sqle.getMessage()+"]");
0566     }
0567     finally {
0568       DBHelper.cleanup(stmt);
0569     }
0570 
0571 
0572     //2. insert annotations/nodes for DEFAULT a-set
0573     //for now use a stupid cycle
0574     //TODO: pass all the data with one DB call (?)
0575 
0576     try {
0577       stmt = this.jdbcConn.prepareCall(
0578                 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }");
0579 
0580       Iterator itAnnotations = aset.iterator();
0581 
0582       while (itAnnotations.hasNext()) {
0583         Annotation ann = (Annotation)itAnnotations.next();
0584         Node start = (Node)ann.getStartNode();
0585         Node end = (Node)ann.getEndNode();
0586         String type = ann.getType();
0587 
0588         //DB stuff
0589         Long annGlobalID = null;
0590         stmt.setLong(1,lrID.longValue());
0591         stmt.setLong(2,ann.getId().longValue());
0592         stmt.setLong(3,asetID.longValue());
0593         stmt.setLong(4,start.getId().longValue());
0594         stmt.setLong(5,start.getOffset().longValue());
0595         stmt.setLong(6,end.getId().longValue());
0596         stmt.setLong(7,end.getOffset().longValue());
0597         stmt.setString(8,type);
0598         stmt.registerOutParameter(9,java.sql.Types.BIGINT);
0599 
0600         stmt.execute();
0601 
0602         annGlobalID = new Long(stmt.getLong(9));
0603 
0604         //2.1. set annotation features
0605         FeatureMap features = ann.getFeatures();
0606         Assert.assertNotNull(features);
0607 //        createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
0608         createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
0609       //while
0610     }//try
0611     catch(SQLException sqle) {
0612 
0613       switch(sqle.getErrorCode()) {
0614 
0615         case DBHelper.X_ORACLE_INVALID_ANNOTATION_TYPE:
0616           throw new PersistenceException(
0617                               "can't create annotation in DB, [invalid annotation type]");
0618         default:
0619           throw new PersistenceException(
0620                 "can't create annotation in DB: ["+ sqle.getMessage()+"]");
0621       }//switch
0622     }//catch
0623     finally {
0624       DBHelper.cleanup(stmt);
0625     }
0626   }//func
0627 
0628 
0629 
0630   /** creates a LR of type Corpus  */
0631 /*  protected Corpus createCorpus(Corpus corp,SecurityInfo secInfo, boolean newTransPerDocument)
0632     throws PersistenceException,SecurityException {
0633 
0634     //1. create an LR entry for the corpus (T_LANG_RESOURCE table)
0635     Long lrID = createLR(DBHelper.CORPUS_CLASS,corp.getName(),secInfo,null);
0636 
0637     //2.create am entry in the T_COPRUS table
0638     Long corpusID = null;
0639     //DB stuff
0640     CallableStatement stmt = null;
0641     try {
0642       stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.create_corpus(?,?) }");
0643       stmt.setLong(1,lrID.longValue());
0644       stmt.registerOutParameter(2,java.sql.Types.BIGINT);
0645       stmt.execute();
0646       corpusID = new Long(stmt.getLong(2));
0647     }
0648     catch(SQLException sqle) {
0649       throw new PersistenceException("can't create corpus [step 2] in DB: ["+ sqle.getMessage()+"]");
0650     }
0651     finally {
0652       DBHelper.cleanup(stmt);
0653     }
0654 
0655     //3. for each document in the corpus call createDocument()
0656     Iterator itDocuments = corp.iterator();
0657     Vector dbDocs = new Vector();
0658     while (itDocuments.hasNext()) {
0659       Document doc = (Document)itDocuments.next();
0660 
0661       //3.1. ensure that the document is either transient or is from the ...
0662       // same DataStore
0663       if (doc.getLRPersistenceId() == null) {
0664         //transient document
0665 
0666         //now this is a bit ugly patch, the transaction related functionality
0667         //should not be in this method
0668         if (newTransPerDocument) {
0669           beginTrans();
0670         }
0671 
0672         Document dbDoc = createDocument(doc,corpusID,secInfo);
0673 
0674         if (newTransPerDocument) {
0675           commitTrans();
0676         }
0677 
0678         dbDocs.add(dbDoc);
0679         //8. let the world know
0680         fireResourceAdopted(new DatastoreEvent(this,
0681                                                 DatastoreEvent.RESOURCE_ADOPTED,
0682                                                 dbDoc,
0683                                                 dbDoc.getLRPersistenceId()
0684                                               )
0685                             );
0686 
0687         //9. fire also resource written event because it's now saved
0688         fireResourceWritten(new DatastoreEvent(this,
0689                                                 DatastoreEvent.RESOURCE_WRITTEN,
0690                                                 dbDoc,
0691                                                 dbDoc.getLRPersistenceId()
0692                                               )
0693                            );
0694 
0695       }
0696       else if (doc.getDataStore().equals(this)) {
0697         //persistent doc from the same DataStore
0698         fireResourceAdopted(
0699             new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED,
0700                                doc,
0701                                doc.getLRPersistenceId()));
0702 
0703         //6. fire also resource written event because it's now saved
0704         fireResourceWritten(
0705           new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN,
0706                               doc,
0707                               doc.getLRPersistenceId()));
0708       }
0709       else {
0710         //persistent doc from other datastore
0711         //skip
0712         gate.util.Err.prln("document ["+doc.getLRPersistenceId()+"] is adopted from another "+
0713                             " datastore. Skipped.");
0714       }
0715     }
0716 
0717     //4. create features
0718 //    createFeatures(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures());
0719     createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures());
0720 
0721     //5. create a DatabaseCorpusImpl and return it
0722 ///    Corpus dbCorpus = new DatabaseCorpusImpl(corp.getName(),
0723 ///                                             this,
0724 ///                                              lrID,
0725 ///                                              corp.getFeatures(),
0726 ///                                              dbDocs);
0727 ///
0728 
0729     Corpus dbCorpus = null;
0730     FeatureMap params = Factory.newFeatureMap();
0731     HashMap initData = new HashMap();
0732 
0733     initData.put("DS",this);
0734     initData.put("LR_ID",lrID);
0735     initData.put("CORP_NAME",corp.getName());
0736     initData.put("CORP_FEATURES",corp.getFeatures());
0737     initData.put("CORP_SUPPORT_LIST",dbDocs);
0738 
0739     params.put("initData__$$__", initData);
0740 
0741     try {
0742       //here we create the persistent LR via Factory, so it's registered
0743       //in GATE
0744       dbCorpus = (Corpus)Factory.createResource("gate.corpora.DatabaseCorpusImpl", params);
0745     }
0746     catch (gate.creole.ResourceInstantiationException ex) {
0747       throw new GateRuntimeException(ex.getMessage());
0748     }
0749 
0750     //6. done
0751     return dbCorpus;
0752   }
0753 
0754 */
0755 
0756   /**
0757    * Get a resource from the persistent store.
0758    <B>Don't use this method - use Factory.createResource with
0759    * DataStore and DataStoreInstanceId parameters set instead.</B>
0760    */
0761 /*  public LanguageResource getLr(String lrClassName, Object lrPersistenceId)
0762   throws PersistenceException,SecurityException {
0763 
0764     LanguageResource result = null;
0765 
0766     //0. preconditions
0767     Assert.assertNotNull(lrPersistenceId);
0768 
0769     //1. check session
0770     if (null == this.session) {
0771       throw new SecurityException("session not set");
0772     }
0773 
0774     if (false == this.ac.isValidSession(this.session)) {
0775       throw new SecurityException("invalid session supplied");
0776     }
0777 
0778     //2. check permissions
0779     if (false == canReadLR(lrPersistenceId)) {
0780       throw new SecurityException("insufficient privileges");
0781     }
0782 
0783     //3. get resource from DB
0784     if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
0785       result = readDocument(lrPersistenceId);
0786       Assert.assertTrue(result instanceof DatabaseDocumentImpl);
0787     }
0788     else if (lrClassName.equals(DBHelper.CORPUS_CLASS)) {
0789       result = readCorpus(lrPersistenceId);
0790       Assert.assertTrue(result instanceof DatabaseCorpusImpl);
0791     }
0792     else {
0793       throw new IllegalArgumentException("resource class should be either Document or Corpus");
0794     }
0795 
0796     //4. postconditions
0797     Assert.assertNotNull(result.getDataStore());
0798     Assert.assertTrue(result.getDataStore() instanceof DatabaseDataStore);
0799     Assert.assertNotNull(result.getLRPersistenceId());
0800 
0801     //5. register the read doc as listener for sync events
0802     addDatastoreListener((DatastoreListener)result);
0803 
0804     //6. add the resource to the list of dependent resources - i.e. the ones that the
0805     //data store should take care upon closing [and call sync()]
0806     this.dependentResources.add(result);
0807 
0808     //7. done
0809     return result;
0810   }
0811 */
0812 
0813   /** Gets a timestamp marker that will be used for all changes made in
0814    *  the database so that subsequent calls to deleteSince() could restore (partly)
0815    *  the database state as it was before the update. <B>NOTE:</B> Restoring the previous
0816    *  state may not be possible at all (i.e. if DELETE is performed)
0817    *   */
0818   public Long timestamp()
0819     throws PersistenceException{
0820 
0821     CallableStatement stmt = null;
0822 
0823     try {
0824       stmt = this.jdbcConn.prepareCall(
0825                 "{ call "+Gate.DB_OWNER+".persist.get_timestamp(?)} ");
0826       //numbers generated from Oracle sequences are BIGINT
0827       stmt.registerOutParameter(1,java.sql.Types.BIGINT);
0828       stmt.execute();
0829       long result = stmt.getLong(1);
0830 
0831       return new Long(result);
0832     }
0833     catch(SQLException sqle) {
0834       throw new PersistenceException("can't get a timestamp from DB: ["+ sqle.getMessage()+"]");
0835     }
0836     finally {
0837       DBHelper.cleanup(stmt);
0838     }
0839   }
0840 
0841 
0842   /**
0843    * Checks if the user (identified by the sessionID)
0844    * has some access (read/write) to the LR
0845    */
0846   protected boolean canAccessLR(Long lrID,int mode)
0847     throws PersistenceException, SecurityException{
0848 
0849     //0. preconditions
0850     Assert.assertTrue(DBHelper.READ_ACCESS == mode || DBHelper.WRITE_ACCESS == mode);
0851 
0852     //1. is session initialised?
0853     if (null == this.session) {
0854       throw new SecurityException("user session not set");
0855     }
0856 
0857     //2.first check the session and then check whether the user is member of the group
0858     if (this.ac.isValidSession(this.session== false) {
0859       throw new SecurityException("invalid session supplied");
0860     }
0861 
0862     CallableStatement stmt = null;
0863 
0864     try {
0865       stmt = this.jdbcConn.prepareCall(
0866                 "{ call "+Gate.DB_OWNER+".security.has_access_to_lr(?,?,?,?,?)} ");
0867       stmt.setLong(1,lrID.longValue());
0868       stmt.setLong(2,this.session.getUser().getID().longValue());
0869       stmt.setLong(3,this.session.getGroup().getID().longValue());
0870       stmt.setLong(4,mode);
0871 
0872       stmt.registerOutParameter(5,java.sql.Types.NUMERIC);
0873       stmt.execute();
0874       int result = stmt.getInt(5);
0875 
0876       return (ORACLE_TRUE == result);
0877     }
0878     catch(SQLException sqle) {
0879       throw new PersistenceException("can't check permissions in DB: ["+ sqle.getMessage()+"]");
0880     }
0881     finally {
0882       DBHelper.cleanup(stmt);
0883     }
0884   }
0885 
0886 
0887 
0888   /** reads the content of a CLOB into the specified StringBuffer */
0889   public static void readCLOB(java.sql.Clob src, StringBuffer dest)
0890     throws SQLException, IOException {
0891 
0892     int readLength = 0;
0893 
0894     //1. empty the dest buffer
0895     dest.delete(0,dest.length());
0896 
0897     //2. get Oracle CLOB
0898     CLOB clo = (CLOB)src;
0899 
0900     //3. create temp buffer
0901     int buffSize = Math.max(INTERNAL_BUFFER_SIZE,clo.getBufferSize());
0902     char[] readBuffer = new char[buffSize];
0903 
0904     //3. get Unicode stream
0905     Reader input = clo.getCharacterStream();
0906 
0907     //4. read
0908     BufferedReader buffInput = new BufferedReader(input,INTERNAL_BUFFER_SIZE);
0909 
0910     while ((readLength = buffInput.read(readBuffer, 0, INTERNAL_BUFFER_SIZE)) != -1) {
0911       dest.append(readBuffer, 0, readLength);
0912     }
0913 
0914     //5.close streams
0915     buffInput.close();
0916     input.close();
0917 
0918   }
0919 
0920 
0921 
0922   /** writes the content of a String into the specified CLOB object */
0923   public static void writeCLOB(String src,java.sql.Clob dest)
0924     throws SQLException, IOException {
0925 
0926     //preconditions
0927     Assert.assertNotNull(src);
0928 
0929     //1. get Oracle CLOB
0930     CLOB clo = (CLOB)dest;
0931 
0932     //2. get Unicode stream
0933     Writer output = clo.getCharacterOutputStream();
0934 
0935     //3. write
0936     BufferedWriter buffOutput = new BufferedWriter(output,INTERNAL_BUFFER_SIZE);
0937     buffOutput.write(src.toString());
0938 
0939     //4. flushing is a good idea [although BufferedWriter::close() calls it this is
0940     //implementation specific]
0941     buffOutput.flush();
0942     output.flush();
0943 
0944     //5.close streams
0945     buffOutput.close();
0946     output.close();
0947   }
0948 
0949 
0950 
0951   /** writes the content of a StringBuffer into the specified CLOB object */
0952   public static void writeCLOB(StringBuffer src,java.sql.Clob dest)
0953     throws SQLException, IOException {
0954 
0955     //delegate
0956     writeCLOB(src.toString(),dest);
0957   }
0958 
0959 
0960 
0961   /**
0962    *  reads the content of the specified BLOB object and returns the object
0963    *  contained.
0964    *  NOTE: the BLOB is expected to contain serializable objects, not just any
0965    *  binary stream
0966    */
0967   public static Object readBLOB(java.sql.Blob src)
0968     throws SQLException, IOException,ClassNotFoundException {
0969 
0970     int readLength = 0;
0971     Object result = null;
0972 
0973     //0. preconditions
0974     Assert.assertNotNull(src);
0975 
0976     //2. get Oracle BLOB
0977     BLOB blo = (BLOB)src;
0978 
0979     //3. get binary stream
0980     InputStream input = blo.getBinaryStream();
0981     Assert.assertNotNull(input);
0982 
0983     //4. read
0984     ObjectInputStream ois = new ObjectInputStream(input);
0985     result = ois.readObject();
0986 
0987     //5.close streams
0988     ois.close();
0989     input.close();
0990 
0991     return result;
0992   }
0993 
0994 
0995 
0996   /**
0997    *  writes the specified object into the BLOB
0998    *  NOTE: the object should be serializable
0999    */
1000   public static void writeBLOB(Object src,java.sql.Blob dest)
1001     throws SQLException, IOException {
1002 
1003     //preconditions
1004     Assert.assertNotNull(src);
1005 
1006     //1. get Oracle CLOB
1007     BLOB blo = (BLOB)dest;
1008 
1009     //2. get Unicode stream
1010     OutputStream output = blo.getBinaryOutputStream();
1011 
1012     //3. write
1013     ObjectOutputStream oos = new ObjectOutputStream(output);
1014     oos.writeObject(src);
1015 
1016     //4. flushing is a good idea
1017     //[although ::close() calls it this is implementation specific]
1018     oos.flush();
1019     output.flush();
1020 
1021     //5.close streams
1022     oos.close();
1023     output.close();
1024   }
1025 
1026 
1027 
1028   /**
1029    *  creates a feature of the specified type/value/valueType/key for the specified entity
1030    *  Entity is one of: LR, Annotation
1031    *  Value types are: boolean, int, long, string, float, Object
1032    */
1033   private Long _createFeature(Long entityID,
1034                               int entityType,
1035                               String key,
1036                               Object value,
1037                               int valueType,
1038                               CallableStatement stmt)
1039     throws PersistenceException {
1040 
1041     //1. store in DB
1042     Long featID = null;
1043 //    CallableStatement stmt = null;
1044 
1045     try {
1046 //      stmt = this.jdbcConn.prepareCall(
1047 //                "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} ");
1048 
1049       //1.1 set known values + NULLs
1050       stmt.setLong(1,entityID.longValue());
1051       stmt.setLong(2,entityType);
1052       stmt.setString(3,key);
1053       stmt.setNull(4,java.sql.Types.NUMERIC);
1054       stmt.setNull(5,java.sql.Types.VARCHAR);
1055       stmt.setLong(6,valueType);
1056       stmt.registerOutParameter(7,java.sql.Types.BIGINT);
1057 
1058       //1.2 set proper data
1059       switch(valueType) {
1060 
1061         case DBHelper.VALUE_TYPE_NULL:
1062           break;
1063 
1064         case DBHelper.VALUE_TYPE_BOOLEAN:
1065 
1066           boolean b = ((Boolean)value).booleanValue();
1067           stmt.setLong(4, b ? OracleDataStore.ORACLE_TRUE : OracleDataStore.ORACLE_FALSE);
1068           break;
1069 
1070         case DBHelper.VALUE_TYPE_INTEGER:
1071 
1072           stmt.setLong(4,((Integer)value).intValue());
1073           break;
1074 
1075         case DBHelper.VALUE_TYPE_LONG:
1076 
1077           stmt.setLong(4,((Long)value).longValue());
1078           break;
1079 
1080         case DBHelper.VALUE_TYPE_FLOAT:
1081 
1082           Double d = (Double)value;
1083           stmt.setDouble(4,d.doubleValue());
1084           break;
1085 
1086         case DBHelper.VALUE_TYPE_BINARY:
1087           //ignore
1088           //will be handled later in processing
1089           break;
1090 
1091         case DBHelper.VALUE_TYPE_STRING:
1092 
1093           String s = (String)value;
1094           //does it fin into a varchar2?
1095           if (fitsInVarchar2(s)) {
1096             stmt.setString(5,s);
1097           }
1098           break;
1099 
1100         default:
1101           throw new IllegalArgumentException("unsuppoeted feature type");
1102       }
1103 
1104       stmt.execute();
1105       featID = new Long(stmt.getLong(7));
1106     }
1107     catch(SQLException sqle) {
1108 
1109       switch(sqle.getErrorCode()) {
1110         case DBHelper.X_ORACLE_INVALID_FEATURE_TYPE:
1111           throw new PersistenceException("can't create feature [step 1],"+
1112                       "[invalid feature type] in DB: ["+ sqle.getMessage()+"]");
1113         default:
1114           throw new PersistenceException("can't create feature [step 1] in DB: ["+
1115                                                       sqle.getMessage()+"]");
1116       }
1117     }
1118     finally {
1119 //      DBHelper.cleanup(stmt);
1120     }
1121 
1122     return featID;
1123   }
1124 
1125 
1126   /**
1127    *  creates a feature of the specified type/value/valueType/key for the specified entity
1128    *  Entity is one of: LR, Annotation
1129    *  Value types are: boolean, int, long, string, float, Object
1130    */
1131   private void _createFeatureBulk(Vector features,
1132                                   CallableStatement stmt,
1133                                   ArrayDescriptor adNumber,
1134                                   ArrayDescriptor adString)
1135     throws PersistenceException {
1136 
1137     String[] stringValues = new String[VARRAY_SIZE];
1138     long[] numberValues = new long[VARRAY_SIZE];
1139     double[] floatValues = new double[VARRAY_SIZE];
1140     long[] entityIDs = new long[VARRAY_SIZE];
1141     long[] entityTypes = new long[VARRAY_SIZE];
1142     String[] keys = new String[VARRAY_SIZE];
1143     long[] valueTypes = new long[VARRAY_SIZE];
1144 
1145 //System.out.println("num features=["+features.size()+"]");
1146     //1. store in DB
1147     try {
1148 
1149       int ftInd = 0;
1150       int arrInd = 0;
1151       Iterator it = features.iterator();
1152 
1153       while (it.hasNext()) {
1154 
1155         Feature currFeature = (Feature)it.next();
1156         entityIDs[arrInd= currFeature.entityID.longValue();
1157         entityTypes[arrInd= currFeature.entityType;
1158         keys[arrInd= currFeature.key;
1159         valueTypes[arrInd= currFeature.valueType;
1160 //System.out.println("ftype=["+currFeature.valueType+"]");
1161         //preconditions
1162         Assert.assertTrue(currFeature.valueType == DBHelper.VALUE_TYPE_BOOLEAN ||
1163                           currFeature.valueType == DBHelper.VALUE_TYPE_FLOAT ||
1164                           currFeature.valueType == DBHelper.VALUE_TYPE_INTEGER ||
1165                           currFeature.valueType == DBHelper.VALUE_TYPE_LONG ||
1166                           currFeature.valueType == DBHelper.VALUE_TYPE_NULL ||
1167                           currFeature.valueType == DBHelper.VALUE_TYPE_STRING
1168                           );
1169 
1170 
1171         Object value = currFeature.value;
1172 
1173         switch(currFeature.valueType) {
1174 
1175           case DBHelper.VALUE_TYPE_NULL:
1176             numberValues[arrInd0;
1177             floatValues[arrInd0;
1178             stringValues[arrInd"";
1179             break;
1180 
1181           case DBHelper.VALUE_TYPE_BOOLEAN:
1182             boolean b = ((Boolean)value).booleanValue();
1183             numberValues[arrInd= b ? OracleDataStore.ORACLE_TRUE : OracleDataStore.ORACLE_FALSE;
1184             floatValues[arrInd0;
1185             stringValues[arrInd"";
1186             break;
1187 
1188           case DBHelper.VALUE_TYPE_INTEGER:
1189             numberValues[arrInd((Integer)value).intValue();
1190             floatValues[arrInd0;
1191             stringValues[arrInd"";
1192             break;
1193 
1194           case DBHelper.VALUE_TYPE_LONG:
1195             numberValues[arrInd((Long)value).longValue();
1196             floatValues[arrInd0;
1197             stringValues[arrInd"";
1198             break;
1199 
1200           case DBHelper.VALUE_TYPE_FLOAT:
1201             floatValues[arrInd((Double)value).doubleValue();
1202             numberValues[arrInd0;
1203             stringValues[arrInd"";
1204             break;
1205 
1206           case DBHelper.VALUE_TYPE_BINARY:
1207             Assert.fail();
1208             break;
1209 
1210           case DBHelper.VALUE_TYPE_STRING:
1211             String s = (String)value;
1212             //does it fin into a varchar2?
1213 
1214             if (fitsInVarchar2(s)) {
1215               stringValues[arrInd= s;
1216               floatValues[arrInd0;
1217               numberValues[arrInd0;
1218             }
1219             else {
1220               Assert.fail();
1221             }
1222             break;
1223 
1224           default:
1225             throw new IllegalArgumentException("unsuppoeted feature type");
1226         }
1227 
1228         //save the features?
1229         ftInd++;
1230         arrInd++;
1231 
1232         if (ftInd == features.size() || arrInd == VARRAY_SIZE) {
1233 
1234           if (arrInd == VARRAY_SIZE) {
1235             arrInd = 0;
1236           }
1237 //System.out.println("1");
1238           ARRAY arrEntityIDs = new ARRAY(adNumber, this.jdbcConn,entityIDs);
1239           ARRAY arrEntityTypes = new ARRAY(adNumber, this.jdbcConn,entityTypes);
1240           ARRAY arrKeys = new ARRAY(adString, this.jdbcConn,keys);
1241           ARRAY arrValueTypes = new ARRAY(adNumber, this.jdbcConn,valueTypes);
1242           ARRAY arrNumberValues = new ARRAY(adNumber, this.jdbcConn,numberValues);
1243           ARRAY arrFloatValues = new ARRAY(adNumber, this.jdbcConn,floatValues);
1244           ARRAY arrStringValues = new ARRAY(adString, this.jdbcConn,stringValues);
1245 
1246           OracleCallableStatement ostmt = (OracleCallableStatement)stmt;
1247           ostmt.setARRAY(1,arrEntityIDs);
1248           ostmt.setARRAY(2,arrEntityTypes);
1249           ostmt.setARRAY(3,arrKeys);
1250           ostmt.setARRAY(4,arrNumberValues);
1251           ostmt.setARRAY(5,arrFloatValues);
1252           ostmt.setARRAY(6,arrStringValues);
1253           ostmt.setARRAY(7,arrValueTypes);
1254           ostmt.setInt(8, arrInd == ? VARRAY_SIZE : arrInd);
1255 
1256           ostmt.execute();
1257         }
1258       }
1259     }
1260     catch(SQLException sqle) {
1261 
1262       switch(sqle.getErrorCode()) {
1263 
1264         case DBHelper.X_ORACLE_INVALID_FEATURE_TYPE:
1265           throw new PersistenceException("can't create feature [step 1],"+
1266                       "[invalid feature type] in DB: ["+ sqle.getMessage()+"]");
1267         default:
1268           throw new PersistenceException("can't create feature [step 1] in DB: ["+
1269                                                       sqle.getMessage()+"]");
1270       }
1271     }
1272   }
1273 
1274   /**
1275    *  updates the value of a feature where the value is string (>4000 bytes, stored as CLOB)
1276    *  or Object (stored as BLOB)
1277    */
1278   private void _updateFeatureLOB(Long featID,Object value, int valueType)
1279     throws PersistenceException {
1280 
1281     //NOTE: at this point value is never an array,
1282     // although the type may claim so
1283 
1284     //0. preconditions
1285     Assert.assertTrue(valueType == DBHelper.VALUE_TYPE_BINARY ||
1286                   valueType == DBHelper.VALUE_TYPE_STRING);
1287 
1288 
1289     //1. get the row to be updated
1290     PreparedStatement stmtA = null;
1291     ResultSet rsA = null;
1292     Clob clobValue = null;
1293     Blob blobValue = null;
1294 
1295     try {
1296       String sql = " select ft_long_character_value, " +
1297                    "        ft_binary_value " +
1298                    " from  "+Gate.DB_OWNER+".t_feature " +
1299                    " where  ft_id = ? ";
1300 
1301       stmtA = this.jdbcConn.prepareStatement(sql);
1302       stmtA.setLong(1,featID.longValue());
1303       stmtA.execute();
1304       rsA = stmtA.getResultSet();
1305 
1306       if (false == rsA.next()) {
1307         throw new PersistenceException("Incorrect feature ID supplied ["+featID+"]");
1308       }
1309 
1310       //NOTE1: if the result set contains LOBs always read them
1311       // in the order they appear in the SQL query
1312       // otherwise data will be lost
1313       //NOTE2: access by index rather than name is usually faster
1314       clobValue = rsA.getClob(1);
1315       blobValue = rsA.getBlob(2);
1316 
1317       //blob or clob?
1318       if (valueType == DBHelper.VALUE_TYPE_BINARY) {
1319         //blob
1320         writeBLOB(value,blobValue);
1321       }
1322       else if (valueType == DBHelper.VALUE_TYPE_STRING) {
1323         //clob
1324         String s = (String)value;
1325         writeCLOB(s,clobValue);
1326       }
1327       else {
1328         Assert.fail();
1329       }
1330     }
1331     catch(SQLException sqle) {
1332       throw new PersistenceException("can't create feature [step 2] in DB: ["+ sqle.getMessage()+"]");
1333     }
1334     catch(IOException ioe) {
1335       throw new PersistenceException("can't create feature [step 2] in DB: ["+ ioe.getMessage()+"]");
1336     }
1337     finally {
1338       DBHelper.cleanup(rsA);
1339       DBHelper.cleanup(stmtA);
1340     }
1341 
1342   }
1343 
1344 
1345 
1346   /**
1347    *  creates a feature with the specified type/key/value for the specified entity
1348    *  entitties are either LRs ot Annotations
1349    *  valid values are: boolean,
1350    *                    int,
1351    *                    long,
1352    *                    string,
1353    *                    float,
1354    *                    Object,
1355    *                    boolean List,
1356    *                    int List,
1357    *                    long List,
1358    *                    string List,
1359    *                    float List,
1360    *                    Object List
1361    *
1362    */
1363   private void createFeature(Long entityID, int entityType,String key, Object value, CallableStatement stmt)
1364     throws PersistenceException {
1365 
1366     //1. what kind of feature value is this?
1367 //System.out.println("key=["+key+"], val=["+value+"]");
1368     int valueType = findFeatureType(value);
1369 
1370     //2. how many elements do we store?
1371     Vector elementsToStore = new Vector();
1372 
1373     switch(valueType) {
1374       case DBHelper.VALUE_TYPE_NULL:
1375       case DBHelper.VALUE_TYPE_BINARY:
1376       case DBHelper.VALUE_TYPE_BOOLEAN:
1377       case DBHelper.VALUE_TYPE_FLOAT:
1378       case DBHelper.VALUE_TYPE_INTEGER:
1379       case DBHelper.VALUE_TYPE_LONG:
1380       case DBHelper.VALUE_TYPE_STRING:
1381         elementsToStore.add(value);
1382         break;
1383 
1384       default:
1385         //arrays
1386         List arr = (List)value;
1387         Iterator itValues = arr.iterator();
1388 
1389         while (itValues.hasNext()) {
1390           elementsToStore.add(itValues.next());
1391         }
1392 
1393         //normalize , i.e. ignore arrays
1394         if (valueType == DBHelper.VALUE_TYPE_BINARY_ARR)
1395           valueType = DBHelper.VALUE_TYPE_BINARY;
1396         else if (valueType == DBHelper.VALUE_TYPE_BOOLEAN_ARR)
1397           valueType = DBHelper.VALUE_TYPE_BOOLEAN;
1398         else if (valueType == DBHelper.VALUE_TYPE_FLOAT_ARR)
1399           valueType = DBHelper.VALUE_TYPE_FLOAT;
1400         else if (valueType == DBHelper.VALUE_TYPE_INTEGER_ARR)
1401           valueType = DBHelper.VALUE_TYPE_INTEGER;
1402         else if (valueType == DBHelper.VALUE_TYPE_LONG_ARR)
1403           valueType = DBHelper.VALUE_TYPE_LONG;
1404         else if (valueType == DBHelper.VALUE_TYPE_STRING_ARR)
1405           valueType = DBHelper.VALUE_TYPE_STRING;
1406     }
1407 
1408     //3. for all elements:
1409     for (int i=0; i< elementsToStore.size(); i++) {
1410 
1411         Object currValue = elementsToStore.elementAt(i);
1412 
1413         //3.1. create a dummy feature [LOB hack]
1414         Long featID = _createFeature(entityID,entityType,key,currValue,valueType,stmt);
1415 
1416         //3.2. update CLOBs if needed
1417         if (valueType == DBHelper.VALUE_TYPE_STRING) {
1418           //does this string fit into a varchar2 or into clob?
1419           String s = (String)currValue;
1420           if (false == this.fitsInVarchar2(s)) {
1421             // Houston, we have a problem
1422             // put the string into a clob
1423             _updateFeatureLOB(featID,value,valueType);
1424           }
1425         }
1426         else if (valueType == DBHelper.VALUE_TYPE_BINARY) {
1427           //3.3. BLOBs
1428             _updateFeatureLOB(featID,value,valueType);
1429         }
1430     }
1431 
1432 
1433   }
1434 
1435 
1436   /**
1437    *  splits complex features (Lists) into a vector of Feature entries
1438    *  each entry contains the entity id,
1439    *                          entity type,
1440    *                          feature key
1441    *                          feature value
1442    *                          value type
1443    *
1444    */
1445   private Vector normalizeFeature(Long entityID, int entityType,String key, Object value)
1446     throws PersistenceException {
1447 
1448     //1. what kind of feature value is this?
1449     int valueType = findFeatureType(value);
1450 
1451     //2. how many elements do we store?
1452     Vector elementsToStore = new Vector();
1453     Vector features = new Vector();
1454 
1455     switch(valueType) {
1456       case DBHelper.VALUE_TYPE_NULL:
1457       case DBHelper.VALUE_TYPE_BINARY:
1458       case DBHelper.VALUE_TYPE_BOOLEAN:
1459       case DBHelper.VALUE_TYPE_FLOAT:
1460       case DBHelper.VALUE_TYPE_INTEGER:
1461       case DBHelper.VALUE_TYPE_LONG:
1462       case DBHelper.VALUE_TYPE_STRING:
1463         elementsToStore.add(value);
1464         break;
1465 
1466       default:
1467         //arrays
1468         List arr = (List)value;
1469         Iterator itValues = arr.iterator();
1470 
1471         while (itValues.hasNext()) {
1472           elementsToStore.add(itValues.next());
1473         }
1474 
1475         //normalize , i.e. ignore arrays
1476         if (valueType == DBHelper.VALUE_TYPE_BINARY_ARR)
1477           valueType = DBHelper.VALUE_TYPE_BINARY;
1478         else if (valueType == DBHelper.VALUE_TYPE_BOOLEAN_ARR)
1479           valueType = DBHelper.VALUE_TYPE_BOOLEAN;
1480         else if (valueType == DBHelper.VALUE_TYPE_FLOAT_ARR)
1481           valueType = DBHelper.VALUE_TYPE_FLOAT;
1482         else if (valueType == DBHelper.VALUE_TYPE_INTEGER_ARR)
1483           valueType = DBHelper.VALUE_TYPE_INTEGER;
1484         else if (valueType == DBHelper.VALUE_TYPE_LONG_ARR)
1485           valueType = DBHelper.VALUE_TYPE_LONG;
1486         else if (valueType == DBHelper.VALUE_TYPE_STRING_ARR)
1487           valueType = DBHelper.VALUE_TYPE_STRING;
1488     }
1489 
1490     for (int i=0; i< elementsToStore.size(); i++) {
1491 
1492       Object currValue = elementsToStore.elementAt(i);
1493       Feature currFeature = new Feature(entityID,entityType,key,currValue,valueType);
1494       features.add(currFeature);
1495     }
1496 
1497     return features;
1498   }
1499 
1500 
1501   /**
1502    *  checks if a String should be stores as VARCHAR2 or CLOB
1503    *  because the VARCHAR2 in Oracle is limited to 4000 <b>bytes</b>, not all
1504    *  the strings fit there. If a String is too long then it is store in the
1505    *  database as CLOB.
1506    *  Note that in the worst case 3 bytes are needed to represent a single character
1507    *  in a database with UTF8 encoding, which limits the string length to 4000/3
1508    *  (ORACLE_VARCHAR_LIMIT_BYTES)
1509    *  @see #ORACLE_VARCHAR_LIMIT_BYTES
1510    */
1511   private boolean fitsInVarchar2(String s) {
1512 
1513     return s.getBytes().length < OracleDataStore.ORACLE_VARCHAR_LIMIT_BYTES;
1514   }
1515 
1516 
1517 
1518   /**
1519    *  helper metod
1520    *  iterates a FeatureMap and creates all its features in the database
1521    */
1522   protected void createFeatures(Long entityID, int entityType, FeatureMap features)
1523     throws PersistenceException {
1524 
1525     //0. prepare statement ad use it for all features
1526     CallableStatement stmt = null;
1527     CallableStatement stmtBulk = null;
1528     ArrayDescriptor adNumber = null;
1529     ArrayDescriptor adString = null;
1530 
1531     try {
1532       stmt = this.jdbcConn.prepareCall(
1533                     "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} ");
1534 
1535       stmtBulk = this.jdbcConn.prepareCall(
1536                     "{ call "+Gate.DB_OWNER+".persist.create_feature_bulk(?,?,?,?,?,?,?,?)} ");
1537 
1538       adNumber = ArrayDescriptor.createDescriptor("GATEADMIN.PERSIST.INTARRAY"this.jdbcConn);
1539       adString = ArrayDescriptor.createDescriptor("GATEADMIN.PERSIST.CHARARRAY"this.jdbcConn);
1540     }
1541     catch (SQLException sqle) {
1542       throw new PersistenceException(sqle);
1543     }
1544 
1545     /* when some day Java has macros, this will be a macro */
1546     Set entries = features.entrySet();
1547     Iterator itFeatures = entries.iterator();
1548     while (itFeatures.hasNext()) {
1549       Map.Entry entry = (Map.Entry)itFeatures.next();
1550       String key = (String)entry.getKey();
1551       Object value = entry.getValue();
1552       createFeature(entityID,entityType,key,value,stmt);
1553     }
1554 
1555     //3. cleanup
1556     DBHelper.cleanup(stmt);
1557   }
1558 
1559 
1560   /**
1561    *  helper metod
1562    *  iterates a FeatureMap and creates all its features in the database
1563    *
1564    *  since it uses Oracle VARRAYs the roundtrips between the client and the server
1565    *  are minimized
1566    *
1567    *  make sure the two types STRING_ARRAY and INT_ARRAY have the same name in the
1568    *  PL/SQL files
1569    *
1570    *  also when referencing the types always use the schema owner in upper case
1571    *  because the jdbc driver is buggy (see MetaLink note if u care)
1572    */
1573   protected void createFeaturesBulk(Long entityID, int entityType, FeatureMap features)
1574     throws PersistenceException {
1575 
1576     //0. prepare statement ad use it for all features
1577     CallableStatement stmt = null;
1578     CallableStatement stmtBulk = null;
1579     ArrayDescriptor adNumber = null;
1580     ArrayDescriptor adString = null;
1581 
1582     try {
1583       stmt = this.jdbcConn.prepareCall(
1584                     "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} ");
1585 
1586       stmtBulk = this.jdbcConn.prepareCall(
1587                     "{ call "+Gate.DB_OWNER+".persist.create_feature_bulk(?,?,?,?,?,?,?,?)} ");
1588 
1589       //ACHTUNG!!!
1590       //using toUpper for schema owner is necessary because of the dull JDBC driver
1591       //otherwise u'll end up with "invalid name pattern" Oracle error
1592       adString = ArrayDescriptor.createDescriptor(Gate.DB_OWNER.toUpperCase()+".STRING_ARRAY"this.jdbcConn);
1593       adNumber = ArrayDescriptor.createDescriptor(Gate.DB_OWNER.toUpperCase()+".INT_ARRAY"this.jdbcConn);
1594     }
1595     catch (SQLException sqle) {
1596       throw new PersistenceException(sqle);
1597     }
1598 
1599     /* when some day Java has macros, this will be a macro */
1600     Vector entityFeatures = new Vector();
1601 
1602     Set entries = features.entrySet();
1603     Iterator itFeatures = entries.iterator();
1604     while (itFeatures.hasNext()) {
1605       Map.Entry entry = (Map.Entry)itFeatures.next();
1606       String key = (String)entry.getKey();
1607       Object value = entry.getValue();
1608       Vector normalizedFeatures = normalizeFeature(entityID,entityType,key,value);
1609       entityFeatures.addAll(normalizedFeatures);
1610     }
1611 
1612     //iterate all features, store LOBs directly and other features with bulk store
1613     Iterator itEntityFeatures = entityFeatures.iterator();
1614 
1615     while (itEntityFeatures.hasNext()) {
1616 
1617       Feature currFeature = (Feature)itEntityFeatures.next();
1618 
1619       if (currFeature.valueType == DBHelper.VALUE_TYPE_STRING) {
1620           //does this string fit into a varchar2 or into clob?
1621           String s = (String)currFeature.value;
1622           if (false == this.fitsInVarchar2(s)) {
1623             // Houston, we have a problem
1624             // put the string into a clob
1625             Long featID = _createFeature(currFeature.entityID,
1626                                          currFeature.entityType,
1627                                          currFeature.key,
1628                                          currFeature.value,
1629                                          currFeature.valueType,
1630                                          stmt);
1631             _updateFeatureLOB(featID,currFeature.value,currFeature.valueType);
1632             itEntityFeatures.remove();
1633           }
1634       }
1635       else if (currFeature.valueType == DBHelper.VALUE_TYPE_BINARY) {
1636         //3.3. BLOBs
1637         Long featID = _createFeature(currFeature.entityID,
1638                                      currFeature.entityType,
1639                                      currFeature.key,
1640                                      currFeature.value,
1641                                      currFeature.valueType,
1642                                      stmt);
1643         _updateFeatureLOB(featID,currFeature.value,currFeature.valueType);
1644         itEntityFeatures.remove();
1645       }
1646     }
1647 
1648     //now we have the data for the bulk store
1649     _createFeatureBulk(entityFeatures, stmtBulk, adNumber, adString);
1650 
1651     //3. cleanup
1652     DBHelper.cleanup(stmt);
1653     DBHelper.cleanup(stmtBulk);
1654   }
1655 
1656 
1657 
1658   /** set security information for LR . */
1659   public void setSecurityInfo(LanguageResource lr,SecurityInfo si)
1660     throws PersistenceException, SecurityException {
1661     throw new MethodNotImplementedException();
1662   }
1663 
1664 
1665 
1666   /**
1667    *  helper method for getLR - reads LR of type Corpus
1668    */
1669 /*
1670   private DatabaseCorpusImpl readCorpus(Object lrPersistenceId)
1671     throws PersistenceException {
1672 
1673     //0. preconditions
1674     Assert.assertNotNull(lrPersistenceId);
1675 
1676     if (false == lrPersistenceId instanceof Long) {
1677       throw new IllegalArgumentException();
1678     }
1679 
1680     //3. read from DB
1681     PreparedStatement pstmt = null;
1682     ResultSet rs = null;
1683     DatabaseCorpusImpl result = null;
1684 
1685     try {
1686       String sql = " select lr_name " +
1687                    " from  "+Gate.DB_OWNER+".t_lang_resource " +
1688                    " where  lr_id = ? ";
1689       pstmt = this.jdbcConn.prepareStatement(sql);
1690       pstmt.setLong(1,((Long)lrPersistenceId).longValue());
1691       pstmt.execute();
1692       rs = pstmt.getResultSet();
1693 
1694       if (false == rs.next()) {
1695         //ooops mo data found
1696         throw new PersistenceException("Invalid LR ID supplied - no data found");
1697       }
1698 
1699       //4. fill data
1700 
1701       //4.1 name
1702       String lrName = rs.getString("lr_name");
1703       Assert.assertNotNull(lrName);
1704 
1705       //4.8 features
1706       FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_CORPUS);
1707 
1708       //4.9 cleanup
1709       DBHelper.cleanup(rs);
1710       DBHelper.cleanup(pstmt);
1711 
1712       sql = " select lr_id ," +
1713             "         lr_name " +
1714             " from "+Gate.DB_OWNER+".t_document        doc, " +
1715             "      "+Gate.DB_OWNER+".t_lang_resource   lr, " +
1716             "      "+Gate.DB_OWNER+".t_corpus_document corpdoc, " +
1717             "      "+Gate.DB_OWNER+".t_corpus          corp " +
1718             " where lr.lr_id = doc.doc_lr_id " +
1719             "       and doc.doc_id = corpdoc.cd_doc_id " +
1720             "       and corpdoc.cd_corp_id = corp.corp_id " +
1721             "       and corp_lr_id = ? ";
1722       pstmt = this.jdbcConn.prepareStatement(sql);
1723       pstmt.setLong(1,((Long)lrPersistenceId).longValue());
1724       pstmt.execute();
1725       rs = pstmt.getResultSet();
1726 
1727       //--Vector docLRIDs = new Vector();
1728       Vector documentData = new Vector();
1729       while (rs.next()) {
1730         Long docLRID = new Long(rs.getLong("lr_id"));
1731         String docName = rs.getString("lr_name");
1732         //--docLRIDs.add(docLRID);
1733         documentData.add(new DocumentData(docName, docLRID));
1734       }
1735       DBHelper.cleanup(rs);
1736       DBHelper.cleanup(pstmt);
1737 
1738 
1739 //      Vector dbDocs = new Vector();
1740 //      for (int i=0; i< docLRIDs.size(); i++) {
1741 //        Long currLRID = (Long)docLRIDs.elementAt(i);
1742         //kalina: replaced by a Factory call, so the doc gets registered
1743         //properly in GATE. Otherwise strange behaviour results in the GUI
1744         //and no events come about it
1745 ////        Document dbDoc = (Document)getLr(DBHelper.DOCUMENT_CLASS,currLRID);
1746 //        FeatureMap params = Factory.newFeatureMap();
1747 //        params.put(DataStore.DATASTORE_FEATURE_NAME, this);
1748 //        params.put(DataStore.LR_ID_FEATURE_NAME, currLRID);
1749 //        Document dbDoc = (Document)Factory.createResource(DBHelper.DOCUMENT_CLASS, params);
1750 
1751 
1752 //        dbDocs.add(dbDoc);
1753 //      }
1754 
1755       result = new DatabaseCorpusImpl(lrName,
1756                                       this,
1757                                       (Long)lrPersistenceId,
1758                                       features,
1759   //                                    dbDocs);
1760                                       documentData);
1761     }
1762     catch(SQLException sqle) {
1763       throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]");
1764     }
1765     catch(Exception e) {
1766       throw new PersistenceException(e);
1767     }
1768     finally {
1769       DBHelper.cleanup(rs);
1770       DBHelper.cleanup(pstmt);
1771     }
1772 
1773     return result;
1774   }
1775 */
1776 
1777   /** helper method for getLR - reads LR of type Document */
1778 /*
1779   private DatabaseDocumentImpl readDocument(Object lrPersistenceId)
1780     throws PersistenceException {
1781 
1782     //0. preconditions
1783     Assert.assertNotNull(lrPersistenceId);
1784 
1785     if (false == lrPersistenceId instanceof Long) {
1786       throw new IllegalArgumentException();
1787     }
1788 
1789     // 1. dummy document to be initialized
1790     DatabaseDocumentImpl result = new DatabaseDocumentImpl(this.jdbcConn);
1791 
1792     PreparedStatement pstmt = null;
1793     ResultSet rs = null;
1794 
1795     //3. read from DB
1796     try {
1797       String sql = " select lr_name, " +
1798                    "        lrtp_type, " +
1799                    "        lr_id, " +
1800                    "        lr_parent_id, " +
1801                    "        doc_id, " +
1802                    "        doc_url, " +
1803                    "        doc_start, " +
1804                    "        doc_end, " +
1805                    "        doc_is_markup_aware " +
1806                    " from  "+Gate.DB_OWNER+".v_document " +
1807                    " where  lr_id = ? ";
1808 
1809       pstmt = this.jdbcConn.prepareStatement(sql);
1810       pstmt.setLong(1,((Long)lrPersistenceId).longValue());
1811       pstmt.execute();
1812       rs = pstmt.getResultSet();
1813 
1814       if (false == rs.next()) {
1815         //ooops mo data found
1816         throw new PersistenceException("Invalid LR ID supplied - no data found");
1817       }
1818 
1819       //4. fill data
1820 
1821       //4.0 name
1822       String lrName = rs.getString("lr_name");
1823       Assert.assertNotNull(lrName);
1824       result.setName(lrName);
1825 
1826       //4.1 parent
1827       Long parentID = null;
1828       long parent_id = rs.getLong("lr_parent_id");
1829       if (false == rs.wasNull()) {
1830         parentID = new Long(parent_id);
1831 
1832         //read parent resource
1833         LanguageResource parentLR = this.getLr(DBHelper.DOCUMENT_CLASS,parentID);
1834         Assert.assertNotNull(parentLR);
1835         Assert.assertTrue(parentLR instanceof DatabaseDocumentImpl);
1836 
1837         result.setParent(parentLR);
1838       }
1839 
1840 
1841       //4.2. markup aware
1842       long markup = rs.getLong("doc_is_markup_aware");
1843       Assert.assertTrue(markup == this.ORACLE_FALSE || markup == this.ORACLE_TRUE);
1844       if (markup == this.ORACLE_FALSE) {
1845         result.setMarkupAware(Boolean.FALSE);
1846       }
1847       else {
1848         result.setMarkupAware(Boolean.TRUE);
1849 
1850       }
1851 
1852       //4.3 datastore
1853       result.setDataStore(this);
1854 
1855       //4.4. persist ID
1856       Long persistID = new Long(rs.getLong("lr_id"));
1857       result.setLRPersistenceId(persistID);
1858 
1859       //4.5  source url
1860       String url = rs.getString("doc_url");
1861       result.setSourceUrl(new URL(url));
1862 
1863       //4.6. start offset
1864       Long start = null;
1865       long longVal = rs.getLong("doc_start");
1866       //null?
1867       //if NULL is stored in the DB, Oracle returns 0 which is not what we want
1868       if (false == rs.wasNull()) {
1869         start = new Long(longVal);
1870       }
1871       result.setSourceUrlStartOffset(start);
1872 //      initData.put("DOC_SOURCE_URL_START",start);
1873 
1874       //4.7. end offset
1875       Long end = null;
1876       longVal = rs.getLong("doc_end");
1877       //null?
1878       //if NULL is stored in the DB, Oracle returns 0 which is not what we want
1879       if (false == rs.wasNull()) {
1880         end = new Long(longVal);
1881       }
1882       result.setSourceUrlEndOffset(end);
1883 //      initData.put("DOC_SOURCE_URL_END",end);
1884 
1885       //4.8 features
1886       FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_DOCUMENT);
1887       result.setFeatures(features);
1888       //initData.put("DOC_FEATURES",features);
1889 
1890       //4.9 set the nextAnnotationID correctly
1891       long doc_id = rs.getLong("doc_id");
1892 
1893       DBHelper.cleanup(rs);
1894       DBHelper.cleanup(pstmt);
1895       sql = " select  max(ann_local_id),'ann_id'" +
1896             " from "+Gate.DB_OWNER+".t_annotation " +
1897             " where ann_doc_id = ?" +
1898             " union " +
1899             " select max(node_local_id),'node_id' " +
1900             " from "+Gate.DB_OWNER+".t_node " +
1901             " where node_doc_id = ?";
1902 
1903       pstmt = this.jdbcConn.prepareStatement(sql);
1904       pstmt.setLong(1,doc_id);
1905       pstmt.setLong(2,doc_id);
1906       pstmt.execute();
1907       rs = pstmt.getResultSet();
1908 
1909       int maxAnnID = 0 , maxNodeID = 0;
1910       //ann id
1911       if (false == rs.next()) {
1912         //ooops no data found
1913         throw new PersistenceException("Invalid LR ID supplied - no data found");
1914       }
1915       if (rs.getString(2).equals("ann_id"))
1916         maxAnnID = rs.getInt(1);
1917       else
1918         maxNodeID = rs.getInt(1);
1919 
1920       if (false == rs.next()) {
1921         //ooops no data found
1922         throw new PersistenceException("Invalid LR ID supplied - no data found");
1923       }
1924       if (rs.getString(2).equals("node_id"))
1925         maxNodeID = rs.getInt(1);
1926       else
1927         maxAnnID = rs.getInt(1);
1928 
1929       result.setNextNodeId(maxNodeID+1);
1930 //      initData.put("DOC_NEXT_NODE_ID",new Integer(maxNodeID+1));
1931       result.setNextAnnotationId(maxAnnID+1);
1932 //      initData.put("DOC_NEXT_ANN_ID",new Integer(maxAnnID+1));
1933 
1934 
1935 //      params.put("initData__$$__", initData);
1936 //      try {
1937         //here we create the persistent LR via Factory, so it's registered
1938         //in GATE
1939 //        result = (DatabaseDocumentImpl)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params);
1940 //      }
1941 //      catch (gate.creole.ResourceInstantiationException ex) {
1942 //        throw new GateRuntimeException(ex.getMessage());
1943 //      }
1944     }
1945     catch(SQLException sqle) {
1946       throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]");
1947     }
1948     catch(Exception e) {
1949       throw new PersistenceException(e);
1950     }
1951     finally {
1952       DBHelper.cleanup(rs);
1953       DBHelper.cleanup(pstmt);
1954     }
1955 
1956     return result;
1957   }
1958 */
1959 
1960 
1961   /**
1962    *  reads the features of an entity
1963    *  entities are of type LR or Annotation
1964    */
1965   protected FeatureMap readFeatures(Long entityID, int entityType)
1966     throws PersistenceException {
1967 
1968     //0. preconditions
1969     Assert.assertNotNull(entityID);
1970     Assert.assertTrue(entityType == DBHelper.FEATURE_OWNER_ANNOTATION ||
1971                   entityType == DBHelper.FEATURE_OWNER_CORPUS ||
1972                   entityType == DBHelper.FEATURE_OWNER_DOCUMENT);
1973 
1974 
1975     PreparedStatement pstmt = null;
1976     ResultSet rs = null;
1977     FeatureMap fm = new SimpleFeatureMapImpl();
1978 
1979     //1. read from DB
1980     try {
1981       String sql = " select v2.fk_string, " +
1982                    "        v1.ft_value_type, " +
1983                    "        v1.ft_number_value, " +
1984                    "        v1.ft_binary_value, " +
1985                    "        v1.ft_character_value, " +
1986                    "        v1.ft_long_character_value " +
1987                    " from  "+Gate.DB_OWNER+".t_feature v1, " +
1988                    "       "+Gate.DB_OWNER+".t_feature_key v2 " +
1989                    " where  v1.ft_entity_id = ? " +
1990                    "        and v1.ft_entity_type = ? " +
1991                    "        and v1.ft_key_id = v2.fk_id " +
1992                    " order by v2.fk_string,v1.ft_id";
1993 
1994       pstmt = this.jdbcConn.prepareStatement(sql);
1995       pstmt.setLong(1,entityID.longValue());
1996       pstmt.setLong(2,entityType);
1997       pstmt.execute();
1998       rs = pstmt.getResultSet();
1999 
2000       //3. fill feature map
2001       Vector arrFeatures = new Vector();
2002       String prevKey = null;
2003       String currKey = null;
2004       Object currFeature = null;
2005 
2006 
2007       while (rs.next()) {
2008         //NOTE: because there are LOBs in the resulset
2009         //the columns should be read in the order they appear
2010         //in the query
2011         currKey = rs.getString(1);
2012 
2013         Long valueType = new Long(rs.getLong(2));
2014 
2015         //we don't quite know what is the type of the NUMBER
2016         //stored in DB
2017         Object numberValue = null;
2018 
2019         //for all numeric types + boolean -> read from DB as appropriate
2020         //Java object
2021         switch(valueType.intValue()) {
2022 
2023           case DBHelper.VALUE_TYPE_BOOLEAN:
2024             numberValue = new Boolean(rs.getBoolean(3));
2025             break;
2026 
2027           case DBHelper.VALUE_TYPE_FLOAT:
2028             numberValue = new Double(rs.getDouble(3));
2029             break;
2030 
2031           case DBHelper.VALUE_TYPE_INTEGER:
2032             numberValue = new Integer(rs.getInt(3));
2033             break;
2034 
2035           case DBHelper.VALUE_TYPE_LONG:
2036             numberValue = new Long(rs.getLong(3));
2037             break;
2038         }
2039 
2040         //don't forget to read the rest of the current row
2041         Blob blobValue = rs.getBlob(4);
2042         String stringValue = rs.getString(5);
2043         Clob clobValue = rs.getClob(6);
2044 
2045         switch(valueType.intValue()) {
2046 
2047           case DBHelper.VALUE_TYPE_NULL:
2048             currFeature = null;
2049             break;
2050 
2051           case DBHelper.VALUE_TYPE_BOOLEAN:
2052           case DBHelper.VALUE_TYPE_FLOAT:
2053           case DBHelper.VALUE_TYPE_INTEGER:
2054           case DBHelper.VALUE_TYPE_LONG:
2055             currFeature = numberValue;
2056             break;
2057 
2058           case DBHelper.VALUE_TYPE_BINARY:
2059             currFeature = readBLOB(blobValue);
2060             break;
2061 
2062           case DBHelper.VALUE_TYPE_STRING:
2063             //this one is tricky too
2064             //if the string is < 4000 bytes long then it's stored as varchar2
2065             //otherwise as CLOB
2066             if (null == stringValue) {
2067               //oops, we got CLOB
2068               StringBuffer temp = new StringBuffer();
2069               readCLOB(clobValue,temp);
2070               currFeature = temp.toString();
2071             }
2072             else {
2073               currFeature = stringValue;
2074             }
2075             break;
2076 
2077           default:
2078             throw new PersistenceException("Invalid feature type found in DB, type is ["+valueType.intValue()+"]");
2079         }//switch
2080 
2081         //new feature or part of an array?
2082         if (currKey.equals(prevKey&& prevKey != null) {
2083           //part of array
2084           arrFeatures.add(currFeature);
2085         }
2086         else {
2087           //add prev feature to feature map
2088 
2089           //is the prev feature an array or a single object?
2090           if (arrFeatures.size() 1) {
2091             //put a clone, because this is a temp array that will
2092             //be cleared in few lines
2093             fm.put(prevKey, new Vector(arrFeatures));
2094           }
2095           else if (arrFeatures.size() == 1) {
2096             fm.put(prevKey,arrFeatures.elementAt(0));
2097           }
2098           else {
2099             //do nothing, this is the dummy feature
2100             ;
2101           }//if
2102 
2103           //now clear the array from previous fesature(s) and put the new
2104           //one there
2105           arrFeatures.clear();
2106 
2107           prevKey = currKey;
2108           arrFeatures.add(currFeature);
2109         }//if
2110       }//while
2111 
2112       //add the last feature
2113       if (arrFeatures.size() 1) {
2114         fm.put(currKey,arrFeatures);
2115       }
2116       else if (arrFeatures.size() == 1) {
2117         fm.put(currKey,arrFeatures.elementAt(0));
2118       }
2119     }//try
2120     catch(SQLException sqle) {
2121       throw new PersistenceException("can't read features from DB: ["+ sqle.getMessage()+"]");
2122     }
2123     catch(IOException ioe) {
2124       throw new PersistenceException("can't read features from DB: ["+ ioe.getMessage()+"]");
2125     }
2126     catch(ClassNotFoundException cnfe) {
2127       throw new PersistenceException("can't read features from DB: ["+ cnfe.getMessage()+"]");
2128     }
2129     finally {
2130       DBHelper.cleanup(rs);
2131       DBHelper.cleanup(pstmt);
2132     }
2133 
2134     return fm;
2135   }
2136 
2137 
2138 
2139   /**
2140    *   checks if two databases are identical
2141    *   @see #readDatabaseID()
2142    *   NOTE: the same database may be represented by different OracleDataStore instances
2143    *   but the IDs will be the same
2144    */
2145   public boolean equals(Object obj) {
2146 
2147     if (false == obj instanceof OracleDataStore) {
2148       return false;
2149     }
2150 
2151     OracleDataStore db2 = (OracleDataStore)obj;
2152 
2153     if (false == this.getDatabaseID().equals(db2.getDatabaseID())) {
2154       return false;
2155     }
2156 
2157     return true;
2158   }
2159 
2160 
2161 
2162 
2163   /**
2164    *  helper for sync()
2165    *  NEVER call directly
2166    */
2167   protected void _syncLR(LanguageResource lr)
2168     throws PersistenceException,SecurityException {
2169 
2170     //0.preconditions
2171     Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
2172                       lr instanceof DatabaseCorpusImpl);;
2173     Assert.assertNotNull(lr.getLRPersistenceId());
2174 
2175     CallableStatement stmt = null;
2176 
2177     try {
2178       stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.update_lr(?,?,?) }");
2179       stmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
2180       stmt.setString(2,lr.getName());
2181       //do we have a parent resource?
2182       if (lr instanceof Document &&
2183           null != lr.getParent()) {
2184         stmt.setLong(3,((Long)lr.getParent().getLRPersistenceId()).longValue());
2185       }
2186       else {
2187         stmt.setNull(3,java.sql.Types.BIGINT);
2188       }
2189 
2190       stmt.execute();
2191     }
2192     catch(SQLException sqle) {
2193 
2194       switch(sqle.getErrorCode()) {
2195         case DBHelper.X_ORACLE_INVALID_LR:
2196           throw new PersistenceException("can't set LR name in DB: [invalid LR ID]");
2197         default:
2198           throw new PersistenceException(
2199                 "can't set LR name in DB: ["+ sqle.getMessage()+"]");
2200       }
2201 
2202     }
2203     finally {
2204       DBHelper.cleanup(stmt);
2205     }
2206   }
2207 
2208 
2209 
2210   /** helper for sync() - never call directly */
2211   protected void _syncDocumentHeader(Document doc)
2212     throws PersistenceException {
2213 
2214     Long lrID = (Long)doc.getLRPersistenceId();
2215 
2216     CallableStatement stmt = null;
2217 
2218     try {
2219       stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2220                                                     ".persist.update_document(?,?,?,?,?) }");
2221       stmt.setLong(1,lrID.longValue());
2222       //do we have URL or create from string
2223       if (null==doc.getSourceUrl()) {
2224         stmt.setNull(2,java.sql.Types.VARCHAR);
2225       }
2226       else {
2227       stmt.setString(2,doc.getSourceUrl().toString());
2228       }
2229       //do we have start offset?
2230       if (null==doc.getSourceUrlStartOffset()) {
2231         stmt.setNull(3,java.sql.Types.NUMERIC);
2232       }
2233       else {
2234         stmt.setLong(3,doc.getSourceUrlStartOffset().longValue());
2235       }
2236       //do we have end offset?
2237       if (null==doc.getSourceUrlEndOffset()) {
2238         stmt.setNull(4,java.sql.Types.NUMERIC);
2239       }
2240       else {
2241         stmt.setLong(4,doc.getSourceUrlEndOffset().longValue());
2242       }
2243 
2244       stmt.setLong(5,true == doc.getMarkupAware().booleanValue() ? OracleDataStore.ORACLE_TRUE
2245                                                                   : OracleDataStore.ORACLE_FALSE);
2246 
2247       stmt.execute();
2248     }
2249     catch(SQLException sqle) {
2250 
2251       switch(sqle.getErrorCode()) {
2252         case DBHelper.X_ORACLE_INVALID_LR :
2253           throw new PersistenceException("invalid LR supplied: no such document: ["+
2254                                                             sqle.getMessage()+"]");
2255         default:
2256           throw new PersistenceException("can't change document data: ["+
2257                                                             sqle.getMessage()+"]");
2258       }
2259     }
2260     finally {
2261       DBHelper.cleanup(stmt);
2262     }
2263 
2264   }
2265 
2266 
2267 
2268   /** helper for sync() - never call directly */
2269   protected void _syncDocumentContent(Document doc)
2270     throws PersistenceException {
2271 /*
2272     PreparedStatement pstmt = null;
2273     ResultSet rs = null;
2274     Long docContID = null;
2275 
2276     //1. read from DB
2277     try {
2278 
2279       String sql = " select dc_id " +
2280                    " from  "+Gate.DB_OWNER+".v_content " +
2281                    " where  lr_id = ? ";
2282 
2283       pstmt = this.jdbcConn.prepareStatement(sql);
2284       pstmt.setLong(1,((Long)doc.getLRPersistenceId()).longValue());
2285       pstmt.execute();
2286       rs = pstmt.getResultSet();
2287 
2288       if (false == rs.next()) {
2289         throw new PersistenceException("invalid LR ID supplied");
2290       }
2291 
2292       //1, get DC_ID
2293       docContID = new Long(rs.getLong(1));
2294 */
2295       //2, update LOBs
2296       //was: updateDocumentContent(docContID,doc.getContent());
2297       Long docID = (Long)doc.getLRPersistenceId();
2298       updateDocumentContent(docID,doc.getContent());
2299 
2300 /*
2301     }
2302     catch(SQLException sqle) {
2303       throw new PersistenceException("Cannot update document content ["+
2304                                       sqle.getMessage()+"]");
2305     }
2306     finally {
2307       DBHelper.cleanup(rs);
2308       DBHelper.cleanup(pstmt);
2309     }
2310  */
2311 
2312   }
2313 
2314 
2315 
2316   /** helper for sync() - never call directly */
2317 /*  protected void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes)
2318     throws PersistenceException {
2319 
2320     //0.preconditions
2321     Assert.assertNotNull(doc);
2322     Assert.assertNotNull(as);
2323     Assert.assertNotNull(changes);
2324     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2325     Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
2326     Assert.assertTrue(changes.size() > 0);
2327 
2328 
2329     PreparedStatement pstmt = null;
2330     ResultSet rs = null;
2331     CallableStatement cstmt = null;
2332     Long lrID = (Long)doc.getLRPersistenceId();
2333 //    Long docID = null;
2334     Long asetID = null;
2335 
2336     try {
2337       //1. get the a-set ID in the database
2338       String sql = " select as_id  " +
2339 //                   "        as_doc_id " +
2340                    " from  "+Gate.DB_OWNER+".v_annotation_set " +
2341                    " where  lr_id = ? ";
2342       //do we have aset name?
2343       String clause = null;
2344       String name = as.getName();
2345       if (null != name) {
2346         clause =   "        and as_name = ? ";
2347       }
2348       else {
2349         clause =   "        and as_name is null ";
2350       }
2351       sql = sql + clause;
2352 
2353       pstmt = this.jdbcConn.prepareStatement(sql);
2354       pstmt.setLong(1,lrID.longValue());
2355       if (null != name) {
2356         pstmt.setString(2,name);
2357       }
2358       pstmt.execute();
2359       rs = pstmt.getResultSet();
2360 
2361       if (rs.next()) {
2362         asetID = new Long(rs.getLong("as_id"));
2363 //        docID = new Long(rs.getLong("as_doc_id"));
2364 //System.out.println("syncing annots, lr_id=["+lrID+"],doc_id=["+docID+"], set_id=["+asetID+"]");
2365       }
2366       else {
2367         throw new PersistenceException("cannot find annotation set with" +
2368                                       " name=["+name+"] , LRID=["+lrID+"] in database");
2369       }
2370 
2371       //3. insert the new annotations from this set
2372 
2373       //3.1. prepare call
2374       cstmt = this.jdbcConn.prepareCall(
2375               "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }");
2376 
2377       Long annGlobalID = null;
2378       Iterator it = changes.iterator();
2379 
2380       while (it.hasNext()) {
2381 
2382         //3.2. insert annotation
2383         Annotation ann = (Annotation)it.next();
2384 
2385         Node start = (Node)ann.getStartNode();
2386         Node end = (Node)ann.getEndNode();
2387         String type = ann.getType();
2388 
2389         cstmt.setLong(1,lrID.longValue());
2390         cstmt.setLong(2,ann.getId().longValue());
2391         cstmt.setLong(3,asetID.longValue());
2392         cstmt.setLong(4,start.getId().longValue());
2393         cstmt.setLong(5,start.getOffset().longValue());
2394         cstmt.setLong(6,end.getId().longValue());
2395         cstmt.setLong(7,end.getOffset().longValue());
2396         cstmt.setString(8,type);
2397         cstmt.registerOutParameter(9,java.sql.Types.BIGINT);
2398 
2399         cstmt.execute();
2400         annGlobalID = new Long(cstmt.getLong(9));
2401 
2402         //3.3. set annotation features
2403         FeatureMap features = ann.getFeatures();
2404         Assert.assertNotNull(features);
2405 //        createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
2406         createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
2407       }
2408     }
2409     catch(SQLException sqle) {
2410       throw new PersistenceException("can't add annotations in DB : ["+
2411                                       sqle.getMessage()+"]");
2412     }
2413     finally {
2414       DBHelper.cleanup(rs);
2415       DBHelper.cleanup(pstmt);
2416       DBHelper.cleanup(cstmt);
2417     }
2418   }
2419 */
2420 
2421 
2422   /** helper for sync() - never call directly */
2423 /*  protected void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes)
2424     throws PersistenceException {
2425 
2426     //technically this approach sux
2427     //at least it works
2428 
2429     //1. delete
2430     _syncRemovedAnnotations(doc,as,changes);
2431     //2. recreate
2432     _syncAddedAnnotations(doc,as,changes);
2433   }
2434 */
2435 
2436   /** helper for sync() - never call directly */
2437   protected void _syncRemovedDocumentsFromCorpus(List docLRIDs, Long corpLRID)
2438     throws PersistenceException {
2439 
2440     //0.preconditions
2441     Assert.assertNotNull(docLRIDs);
2442     Assert.assertNotNull(corpLRID);
2443     Assert.assertTrue(docLRIDs.size() 0);
2444 
2445     CallableStatement cstmt = null;
2446 
2447     try {
2448       cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2449                                                 ".persist.remove_document_from_corpus(?,?) }");
2450 
2451       Iterator it = docLRIDs.iterator();
2452       while (it.hasNext()) {
2453         Long currLRID = (Long)it.next();
2454         cstmt.setLong(1,currLRID.longValue());
2455         cstmt.setLong(2,corpLRID.longValue());
2456         cstmt.execute();
2457       }
2458     }
2459     catch(SQLException sqle) {
2460 
2461       switch(sqle.getErrorCode()) {
2462         case DBHelper.X_ORACLE_INVALID_LR :
2463           throw new PersistenceException("invalid LR supplied: no such document: ["+
2464                                                             sqle.getMessage()+"]");
2465         default:
2466           throw new PersistenceException("can't change document data: ["+
2467                                                             sqle.getMessage()+"]");
2468       }
2469     }
2470     finally {
2471       DBHelper.cleanup(cstmt);
2472     }
2473 
2474   }
2475 
2476 
2477   /** helper for sync() - never call directly */
2478 /*  protected void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes)
2479     throws PersistenceException {
2480     //0.preconditions
2481     Assert.assertNotNull(doc);
2482     Assert.assertNotNull(as);
2483     Assert.assertNotNull(changes);
2484     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2485     Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
2486     Assert.assertTrue(changes.size() > 0);
2487 
2488 
2489     PreparedStatement pstmt = null;
2490     ResultSet rs = null;
2491     CallableStatement cstmt = null;
2492     Long lrID = (Long)doc.getLRPersistenceId();
2493     Long docID = null;
2494     Long asetID = null;
2495 
2496     try {
2497       //1. get the a-set ID in the database
2498       String sql = " select as_id,  " +
2499                    "        as_doc_id " +
2500                    " from  "+Gate.DB_OWNER+".v_annotation_set " +
2501                    " where  lr_id = ? ";
2502       //do we have aset name?
2503       String clause = null;
2504       String name = as.getName();
2505       if (null != name) {
2506         clause =   "        and as_name = ? ";
2507       }
2508       else {
2509         clause =   "        and as_name is null ";
2510       }
2511       sql = sql + clause;
2512 
2513       pstmt = this.jdbcConn.prepareStatement(sql);
2514       pstmt.setLong(1,lrID.longValue());
2515       if (null != name) {
2516         pstmt.setString(2,name);
2517       }
2518       pstmt.execute();
2519       rs = pstmt.getResultSet();
2520 
2521       if (rs.next()) {
2522         asetID = new Long(rs.getLong("as_id"));
2523         docID = new Long(rs.getLong("as_doc_id"));
2524       }
2525       else {
2526         throw new PersistenceException("cannot find annotation set with" +
2527                                       " name=["+name+"] , LRID=["+lrID+"] in database");
2528       }
2529 
2530       //3. delete the removed annotations from this set
2531 
2532       //3.1. prepare call
2533       cstmt = this.jdbcConn.prepareCall(
2534               "{ call "+Gate.DB_OWNER+".persist.delete_annotation(?,?) }");
2535 
2536 
2537       Iterator it = changes.iterator();
2538 
2539       while (it.hasNext()) {
2540 
2541         //3.2. insert annotation
2542         Annotation ann = (Annotation)it.next();
2543 
2544         cstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs!
2545         cstmt.setLong(2,ann.getId().longValue());
2546         cstmt.execute();
2547       }
2548     }
2549     catch(SQLException sqle) {
2550       throw new PersistenceException("can't delete annotations in DB : ["+
2551                                       sqle.getMessage()+"]");
2552     }
2553     finally {
2554       DBHelper.cleanup(rs);
2555       DBHelper.cleanup(pstmt);
2556       DBHelper.cleanup(cstmt);
2557     }
2558   }
2559 */
2560 
2561 
2562   /** helper for sync() - never call directly */
2563 /*  protected void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets)
2564     throws PersistenceException {
2565 
2566     //0. preconditions
2567     Assert.assertNotNull(doc);
2568     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2569     Assert.assertNotNull(doc.getLRPersistenceId());
2570     Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
2571                       this.getDatabaseID());
2572     Assert.assertNotNull(removedSets);
2573     Assert.assertNotNull(addedSets);
2574 
2575     Long lrID = (Long)doc.getLRPersistenceId();
2576 
2577     //1. delete from DB removed a-sets
2578     CallableStatement cstmt = null;
2579 
2580     try {
2581       cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2582                                                 ".persist.delete_annotation_set(?,?) }");
2583 
2584       Iterator it = removedSets.iterator();
2585       while (it.hasNext()) {
2586         String setName = (String)it.next();
2587         cstmt.setLong(1,lrID.longValue());
2588         cstmt.setString(2,setName);
2589         cstmt.execute();
2590       }
2591     }
2592     catch(SQLException sqle) {
2593       throw new PersistenceException("can't remove annotation set from DB: ["+ sqle.getMessage()+"]");
2594     }
2595     finally {
2596       DBHelper.cleanup(cstmt);
2597     }
2598 
2599     //2. create in DB new a-sets
2600     Iterator it = addedSets.iterator();
2601     while (it.hasNext()) {
2602       String setName = (String)it.next();
2603       AnnotationSet aset = doc.getAnnotations(setName);
2604 
2605       Assert.assertNotNull(aset);
2606       Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl);
2607 
2608       createAnnotationSet(lrID,aset);
2609     }
2610   }
2611 
2612 */
2613 
2614   /** helper for sync() - never call directly */
2615 /*  protected void _syncAnnotations(Document doc)
2616     throws PersistenceException {
2617 
2618     //0. preconditions
2619     Assert.assertNotNull(doc);
2620     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2621     Assert.assertNotNull(doc.getLRPersistenceId());
2622     Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
2623                       this.getDatabaseID());
2624 
2625 
2626     EventAwareDocument ead = (EventAwareDocument)doc;
2627     //1. get the sets read from the DB for this document
2628     //chnaged annotations can occur only in such sets
2629     Collection loadedSets = ead.getLoadedAnnotationSets();
2630 
2631     Iterator it = loadedSets.iterator();
2632     while (it.hasNext()) {
2633       AnnotationSet as = (AnnotationSet)it.next();
2634       //check that this set is neither NEW nor DELETED
2635       //they should be already synced
2636       if (ead.getAddedAnnotationSets().contains(as.getName()) ||
2637           ead.getRemovedAnnotationSets().contains(as.getName())) {
2638         //oops, ignore it
2639         continue;
2640       }
2641 
2642       EventAwareAnnotationSet eas = (EventAwareAnnotationSet)as;
2643       Assert.assertNotNull(as);
2644 
2645       Collection anns = null;
2646       anns = eas.getAddedAnnotations();
2647       Assert.assertNotNull(anns);
2648       if (anns.size()>0) {
2649         _syncAddedAnnotations(doc,as,anns);
2650       }
2651 
2652       anns = eas.getRemovedAnnotations();
2653       Assert.assertNotNull(anns);
2654       if (anns.size()>0) {
2655         _syncRemovedAnnotations(doc,as,anns);
2656       }
2657 
2658       anns = eas.getChangedAnnotations();
2659       Assert.assertNotNull(anns);
2660       if (anns.size()>0) {
2661         _syncChangedAnnotations(doc,as,anns);
2662       }
2663     }
2664   }
2665 */
2666 
2667 
2668   /** helper for sync() - never call directly */
2669   protected void _syncFeatures(LanguageResource lr)
2670     throws PersistenceException {
2671 
2672     //0. preconditions
2673     Assert.assertNotNull(lr);
2674     Assert.assertNotNull(lr.getLRPersistenceId());
2675     Assert.assertEquals(((DatabaseDataStore)lr.getDataStore()).getDatabaseID(),
2676                       this.getDatabaseID());
2677     Assert.assertTrue(lr instanceof Document || lr instanceof Corpus);
2678     //we have to be in the context of transaction
2679 
2680     //1, get ID  in the DB
2681     Long lrID = (Long)lr.getLRPersistenceId();
2682     int  entityType;
2683 
2684     //2. delete features
2685     CallableStatement stmt = null;
2686     try {
2687       Assert.assertTrue(false == this.jdbcConn.getAutoCommit());
2688       stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2689                                                     ".persist.delete_features(?,?) }");
2690       stmt.setLong(1,lrID.longValue());
2691 
2692       if (lr instanceof Document) {
2693         entityType = DBHelper.FEATURE_OWNER_DOCUMENT;
2694       }
2695       else if (lr instanceof Corpus) {
2696         entityType = DBHelper.FEATURE_OWNER_CORPUS;
2697       }
2698       else {
2699         throw new IllegalArgumentException();
2700       }
2701 
2702       stmt.setInt(2,entityType);
2703       stmt.execute();
2704     }
2705     catch(SQLException sqle) {
2706       throw new PersistenceException("can't delete features in DB: ["+ sqle.getMessage()+"]");
2707     }
2708     finally {
2709       DBHelper.cleanup(stmt);
2710     }
2711 
2712     //3. recreate them
2713     //createFeatures(lrID,entityType, lr.getFeatures());
2714     createFeaturesBulk(lrID,entityType, lr.getFeatures());
2715 
2716   }
2717 
2718 
2719 
2720   /** helper for sync() - saves a Corpus in the database */
2721 /*  protected void syncCorpus(Corpus corp)
2722     throws PersistenceException,SecurityException {
2723 
2724     //0. preconditions
2725     Assert.assertNotNull(corp);
2726     Assert.assertTrue(corp instanceof DatabaseCorpusImpl);
2727     Assert.assertEquals(this,corp.getDataStore());
2728     Assert.assertNotNull(corp.getLRPersistenceId());
2729 
2730     EventAwareCorpus dbCorpus = (EventAwareCorpus)corp;
2731 
2732     //1. sync the corpus name?
2733     if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_NAME)) {
2734       _syncLR(corp);
2735     }
2736 
2737     //2. sync the corpus features?
2738     if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) {
2739       _syncFeatures(corp);
2740     }
2741 
2742     //2.5 get removed documents and detach (not remove) them from the corpus in the
2743     //database
2744     List removedDocLRIDs = dbCorpus.getRemovedDocuments();
2745     if (removedDocLRIDs.size() > 0) {
2746       _syncRemovedDocumentsFromCorpus(removedDocLRIDs,(Long)corp.getLRPersistenceId());
2747     }
2748 
2749     //3. get all documents
2750     //--Iterator it = corp.iterator();
2751     Iterator it = dbCorpus.getLoadedDocuments().iterator();
2752 
2753     while (it.hasNext()) {
2754       Document dbDoc = (Document)it.next();
2755       //note - document may be NULL which means it was not loaded (load on demand)
2756       //just ignore it then
2757       if (null == dbDoc) {
2758         continue;
2759       }
2760 
2761       //adopt/sync?
2762       if (null == dbDoc.getLRPersistenceId()) {
2763         //doc was never adopted, adopt it
2764 
2765         //3.1 remove the transient doc from the corpus
2766         it.remove();
2767 
2768         //3.2 get the security info for the corpus
2769         SecurityInfo si = getSecurityInfo(corp);
2770 
2771 
2772         Document adoptedDoc = null;
2773         try {
2774           //3.3. adopt the doc with the sec info
2775 //System.out.println("adopting ["+dbDoc.getName()+"] ...");
2776           //don't open a new transaction, since sync() already has opended one
2777           adoptedDoc = (Document)_adopt(dbDoc,si,true);
2778 
2779           //3.4. add doc to corpus in DB
2780           addDocumentToCorpus((Long)adoptedDoc.getLRPersistenceId(),
2781                               (Long)corp.getLRPersistenceId());
2782         }
2783         catch(SecurityException se) {
2784           throw new PersistenceException(se);
2785         }
2786 
2787         //3.5 add back to corpus the new DatabaseDocument
2788         corp.add(adoptedDoc);
2789       }
2790       else {
2791         //don't open a new transaction, the sync() called for corpus has already
2792         //opened one
2793         try {
2794           _sync(dbDoc,true);
2795 
2796           // let the world know about it
2797           fireResourceWritten( new DatastoreEvent(this,
2798                                                   DatastoreEvent.RESOURCE_WRITTEN,
2799                                                   dbDoc,
2800                                                   dbDoc.getLRPersistenceId()
2801                                                   )
2802                               );
2803 
2804           //if the document is form the same DS but did not belong to the corpus add it now
2805           //NOTE: if the document already belongs to the corpus then nothing will be changed
2806           //in the DB
2807           addDocumentToCorpus((Long)dbDoc.getLRPersistenceId(),
2808                               (Long)corp.getLRPersistenceId());
2809         }
2810         catch(SecurityException se) {
2811           gate.util.Err.prln("document cannot be synced: ["+se.getMessage()+"]");
2812         }
2813       }
2814     }
2815   }
2816 */
2817 
2818 
2819   /**
2820    * Try to acquire exlusive lock on a resource from the persistent store.
2821    * Always call unlockLR() when the lock is no longer needed
2822    */
2823   public boolean lockLr(LanguageResource lr)
2824   throws PersistenceException,SecurityException {
2825 
2826     //0. preconditions
2827     Assert.assertNotNull(lr);
2828     Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
2829                       lr instanceof DatabaseCorpusImpl);
2830     Assert.assertNotNull(lr.getLRPersistenceId());
2831     Assert.assertEquals(lr.getDataStore(),this);
2832 
2833     //1. delegate
2834     return _lockLr((Long)lr.getLRPersistenceId());
2835   }
2836 
2837 
2838 
2839   /**
2840    *  helper for lockLR()
2841    *  never call directly
2842    */
2843   private boolean _lockLr(Long lrID)
2844   throws PersistenceException,SecurityException {
2845 
2846     //0. preconditions
2847     Assert.assertNotNull(lrID);
2848 
2849     //1. check session
2850     if (null == this.session) {
2851       throw new SecurityException("session not set");
2852     }
2853 
2854     if (false == this.ac.isValidSession(this.session)) {
2855       throw new SecurityException("invalid session supplied");
2856     }
2857 
2858     //2. check permissions
2859     if (false == canWriteLR(lrID)) {
2860       throw new SecurityException("no write access granted to the user");
2861     }
2862 
2863     //3. try to lock
2864     CallableStatement cstmt = null;
2865     boolean lockSucceeded = false;
2866 
2867     try {
2868       cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.lock_lr(?,?,?,?) }");
2869       cstmt.setLong(1,lrID.longValue());
2870       cstmt.setLong(2,this.session.getUser().getID().longValue());
2871       cstmt.setLong(3,this.session.getGroup().getID().longValue());
2872       cstmt.registerOutParameter(4,java.sql.Types.NUMERIC);
2873       cstmt.execute();
2874 
2875       lockSucceeded = cstmt.getLong(4== OracleDataStore.ORACLE_TRUE
2876                                           true
2877                                           false;
2878     }
2879     catch(SQLException sqle) {
2880 
2881       switch(sqle.getErrorCode()) {
2882         case DBHelper.X_ORACLE_INVALID_LR:
2883           throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
2884         default:
2885           throw new PersistenceException(
2886                 "can't lock LR in DB : ["+ sqle.getMessage()+"]");
2887       }
2888     }
2889     finally {
2890       DBHelper.cleanup(cstmt);
2891     }
2892 
2893     return lockSucceeded;
2894   }
2895 
2896 
2897 
2898   /**
2899    * Releases the exlusive lock on a resource from the persistent store.
2900    */
2901   public void unlockLr(LanguageResource lr)
2902   throws PersistenceException,SecurityException {
2903 
2904     //0. preconditions
2905     Assert.assertNotNull(lr);
2906     Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
2907                       lr instanceof DatabaseCorpusImpl);
2908     Assert.assertNotNull(lr.getLRPersistenceId());
2909     Assert.assertEquals(lr.getDataStore(),this);
2910 
2911     //1. check session
2912     if (null == this.session) {
2913       throw new SecurityException("session not set");
2914     }
2915 
2916     if (false == this.ac.isValidSession(this.session)) {
2917       throw new SecurityException("invalid session supplied");
2918     }
2919 
2920     //2. check permissions
2921     if (false == canWriteLR(lr.getLRPersistenceId())) {
2922       throw new SecurityException("no write access granted to the user");
2923     }
2924 
2925     //3. try to unlock
2926     CallableStatement cstmt = null;
2927     boolean lockSucceeded = false;
2928 
2929     try {
2930       cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.unlock_lr(?,?) }");
2931       cstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
2932       cstmt.setLong(2,this.session.getUser().getID().longValue());
2933       cstmt.execute();
2934     }
2935     catch(SQLException sqle) {
2936 
2937       switch(sqle.getErrorCode()) {
2938         case DBHelper.X_ORACLE_INVALID_LR:
2939           throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
2940         default:
2941           throw new PersistenceException(
2942                 "can't unlock LR in DB : ["+ sqle.getMessage()+"]");
2943       }
2944     }
2945     finally {
2946       DBHelper.cleanup(cstmt);
2947     }
2948   }
2949 
2950 
2951 
2952 
2953   /**
2954    *   adds document to corpus in the database
2955    *   if the document is already part of the corpus nothing
2956    *   changes
2957    */
2958   protected void addDocumentToCorpus(Long docID,Long corpID)
2959   throws PersistenceException,SecurityException {
2960 
2961     //0. preconditions
2962     Assert.assertNotNull(docID);
2963     Assert.assertNotNull(corpID);
2964 
2965     //1. check session
2966     if (null == this.session) {
2967       throw new SecurityException("session not set");
2968     }
2969 
2970     if (false == this.ac.isValidSession(this.session)) {
2971       throw new SecurityException("invalid session supplied");
2972     }
2973 
2974     //2. check permissions
2975     if (false == canWriteLR(corpID)) {
2976       throw new SecurityException("no write access granted to the user");
2977     }
2978 
2979     if (false == canWriteLR(docID)) {
2980       throw new SecurityException("no write access granted to the user");
2981     }
2982 
2983     //3. database
2984     CallableStatement cstmt = null;
2985 
2986     try {
2987       cstmt = this.jdbcConn.prepareCall("{ call "+
2988                                   Gate.DB_OWNER+".persist.add_document_to_corpus(?,?) }");
2989       cstmt.setLong(1,docID.longValue());
2990       cstmt.setLong(2,corpID.longValue());
2991       cstmt.execute();
2992     }
2993     catch(SQLException sqle) {
2994 
2995       switch(sqle.getErrorCode()) {
2996         case DBHelper.X_ORACLE_INVALID_LR:
2997           throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
2998         default:
2999           throw new PersistenceException(
3000                 "can't add document to corpus : ["+ sqle.getMessage()+"]");
3001       }
3002     }
3003     finally {
3004       DBHelper.cleanup(cstmt);
3005     }
3006   }
3007 
3008 
3009 
3010   /**
3011    *   unloads a LR from the GUI
3012    */
3013 /*  protected void unloadLR(Long lrID)
3014   throws GateException{
3015 
3016     //0. preconfitions
3017     Assert.assertNotNull(lrID);
3018 
3019     //1. get all LRs in the system
3020     List resources = Gate.getCreoleRegister().getAllInstances("gate.LanguageResource");
3021 
3022     Iterator it = resources.iterator();
3023     while (it.hasNext()) {
3024       LanguageResource lr = (LanguageResource)it.next();
3025       if (lrID.equals(lr.getLRPersistenceId()) &&
3026           this.equals(lr.getDataStore())) {
3027         //found it - unload it
3028         Factory.deleteResource(lr);
3029         break;
3030       }
3031     }
3032   }
3033 */
3034 
3035     /** Get a list of LRs that satisfy some set or restrictions
3036      *
3037      *  @param constraints list of Restriction objects
3038      */
3039   public List findLrIds(List constraintsthrows PersistenceException {
3040     return findLrIds(constraints,null);
3041   }
3042 
3043   /**
3044    *  Get a list of LRs IDs that satisfy some set or restrictions and are
3045    *  of a particular type
3046    *
3047    @param constraints list of Restriction objects
3048    @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS
3049    */
3050   public List findLrIds(List constraints, String lrTypethrows PersistenceException {
3051     return findLrIds(constraints, lrType, null, -1);
3052   }
3053 
3054   /**
3055    *  Get a list of LRs IDs that satisfy some set or restrictions and are
3056    *  of a particular type
3057    *
3058    @param constraints list of Restriction objects
3059    @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS
3060    @param orderByConstraints liat of OrderByRestriction objects
3061    @param limitcount limit returning objects -1 for unlimited
3062    */
3063  public List findLrIds(List constraints, String lrType,
3064                       List orderByConstraints, int limitcountthrows PersistenceException {
3065       Vector lrsIDs = new Vector();
3066       CallableStatement stmt = null;
3067       ResultSet rs = null;
3068       Connection conn = null;
3069 
3070       try {
3071         Vector sqlValues = new Vector();
3072         String sql = getSQLQuery(constraints, lrType, false, orderByConstraints, limitcount, sqlValues);
3073         conn = DBHelper.connect(this.getStorageUrl()true);
3074         stmt = conn.prepareCall(sql);
3075         for (int i = 0; i<sqlValues.size(); i++){
3076           if (sqlValues.elementAt(iinstanceof String){
3077             stmt.setString(i+1,sqlValues.elementAt(i).toString());
3078           }
3079           else if (sqlValues.elementAt(iinstanceof Long){
3080             stmt.setLong(i+1,((LongsqlValues.elementAt(i)).longValue());
3081           }
3082           else if (sqlValues.elementAt(iinstanceof Integer){
3083             stmt.setLong(i+1,((IntegersqlValues.elementAt(i)).intValue());
3084           }
3085         }
3086         stmt.execute();
3087         rs = stmt.getResultSet();
3088 
3089         while (rs.next()) {
3090           long lr_ID = rs.getLong(1);
3091           lrsIDs.addElement(new Long(lr_ID));
3092         }
3093         return lrsIDs;
3094       }
3095       catch(SQLException sqle) {
3096         throw new PersistenceException("can't get LRs from DB: ["+ sqle+"]");
3097       }
3098       catch (ClassNotFoundException cnfe){
3099         throw new PersistenceException("can't not find driver: ["+ cnfe +"]");
3100       }
3101       finally {
3102         DBHelper.cleanup(rs);
3103         DBHelper.cleanup(stmt);
3104         DBHelper.disconnect(conn, true);
3105       }
3106     }
3107   /**
3108    * Return count of LRs which matches the constraints.
3109    *
3110    @param constraints list of Restriction objects
3111    @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS
3112    */
3113   public long getLrsCount(List constraints, String lrTypethrows PersistenceException {
3114       Vector lrs = new Vector();
3115       CallableStatement stmt = null;
3116       ResultSet rs = null;
3117       Connection conn = null;
3118 
3119       try {
3120         Vector sqlValues = new Vector();
3121         String sql = getSQLQuery(constraints,lrType, true, null, -1, sqlValues);
3122         conn = DBHelper.connect(this.getStorageUrl()true);
3123         stmt = conn.prepareCall(sql);
3124         for (int i = 0; i<sqlValues.size(); i++){
3125           if (sqlValues.elementAt(iinstanceof String){
3126             stmt.setString(i+1,sqlValues.elementAt(i).toString());
3127           }
3128           else if (sqlValues.elementAt(iinstanceof Long){
3129             stmt.setLong(i+1,((LongsqlValues.elementAt(i)).longValue());
3130           }
3131           else if (sqlValues.elementAt(iinstanceof Integer){
3132             stmt.setLong(i+1,((IntegersqlValues.elementAt(i)).intValue());
3133           }
3134         }
3135 
3136         stmt.execute();
3137         rs = stmt.getResultSet();
3138         rs.next();
3139         return rs.getLong(1);
3140       }
3141       catch(SQLException sqle) {
3142         throw new PersistenceException("can't get LRs Count from DB: ["+ sqle+"]");
3143       }
3144       catch (ClassNotFoundException cnfe){
3145         throw new PersistenceException("can't not find driver: ["+ cnfe +"]");
3146       }
3147       finally {
3148         DBHelper.cleanup(rs);
3149         DBHelper.cleanup(stmt);
3150         DBHelper.disconnect(conn, true);
3151       }
3152   }
3153 
3154   private String getSQLQuery(List filter, String lrType, boolean count,
3155                               List orderByFilter, int limitcount, Vector sqlValues){
3156     StringBuffer query = new StringBuffer("");
3157     String join = getJoinQuery(filter, orderByFilter, sqlValues);
3158     String select = "lr_id";
3159     if (count){
3160       select = "count(*)";
3161     }
3162 
3163     query = query.append(" SELECT " + select + " " +
3164                           " FROM  "+Gate.DB_OWNER+".t_lang_resource LR " + join);
3165 
3166    if (filter != null && filter.size()>0) {
3167       query = query.append("  ( ");
3168       query = query.append(getIntersectionPart(filter, sqlValues));
3169       query = query.append(" ) intersected_feat_restr ");
3170    }
3171 
3172     String endPartOfJoin = getEndPartOfJoin(filter,orderByFilter, lrType,sqlValues);
3173     query = query.append(endPartOfJoin);
3174 
3175     if (limitcount>0){
3176       query = query.insert(0,"select lr_id from ( ");
3177       query = query.append") where rownum<"+(limitcount+1));
3178     }
3179 
3180     return query.toString();
3181   }
3182 
3183   private String getIntersectionPart(List filter, Vector sqlValues){
3184     StringBuffer query = new StringBuffer(" ");
3185 
3186     Collections.sort(filter, new RestrictionComepator());
3187     Vector list_of_filters = new Vector();
3188     for (int i=0; i<filter.size(); i++){
3189       if (i>0){
3190         Restriction rest = (Restrictionfilter.get(i);
3191         Restriction prev = (Restrictionfilter.get(i-1);
3192         if (rest.getKey().equals(prev.getKey())){
3193           Vector temp = (Vectorlist_of_filters.get(list_of_filters.size()-1);
3194           temp.add(rest);
3195         else {
3196           Vector temp = new Vector();
3197           temp.add(rest);
3198           list_of_filters.add(temp);
3199         }
3200       else {
3201         Vector temp = new Vector();
3202         temp.add(filter.get(0));
3203         list_of_filters.add(temp);
3204       }
3205     }
3206 
3207     if (filter!=null && filter.size()>0){
3208       for (int i=0; i<list_of_filters.size(); i++){
3209           query = query.append(getRestrictionPartOfQuery((Listlist_of_filters.get(i),sqlValues));
3210           if (i<list_of_filters.size()-1) {
3211             query = query.append("  intersect ");
3212           }
3213       }
3214     }
3215     return query.toString();
3216   }
3217 
3218   private String getRestrictionPartOfQuery(List list, Vector sqlValues){
3219     StringBuffer expresion = new StringBuffer(
3220                       " SELECT ft_entity_id "+
3221                        " FROM "+Gate.DB_OWNER+".t_feature FEATURE, " +
3222                        Gate.DB_OWNER + ".t_feature_key FTK" +
3223                        " WHERE FEATURE.ft_entity_type = 2 ");
3224 
3225     Restriction restr = (Restrictionlist.get(0);
3226 
3227     if (restr.getKey() != null){
3228       expresion = expresion.append(" AND FTK.fk_id = FEATURE.ft_key_id ");
3229       expresion = expresion.append(" AND FTK.fk_string = ? ");
3230       sqlValues.addElement(restr.getKey());
3231     }
3232 
3233     for (int i =0; i<list.size(); i++) {
3234         restr = (Restrictionlist.get(i);
3235         if (restr.getValue() != null){
3236           expresion = expresion.append(" AND ");
3237           switch (this.findFeatureType(restr.getValue())){
3238             case DBHelper.VALUE_TYPE_INTEGER:
3239               expresion = expresion.append(getNumberExpresion(restr, sqlValues));
3240               break;
3241             case DBHelper.VALUE_TYPE_LONG:
3242               expresion = expresion.append(getNumberExpresion(restr, sqlValues));
3243               break;
3244             default:
3245               if (restr.getOperator()==Restriction.OPERATOR_EQUATION){
3246                 expresion = expresion.append(" FEATURE.ft_character_value = ? ");
3247                 sqlValues.addElement(restr.getStringValue());
3248               }
3249               if (restr.getOperator()==Restriction.OPERATOR_LIKE){
3250                 expresion = expresion.append(" upper(FEATURE.ft_character_value) like ? ");
3251                 sqlValues.addElement("%"+restr.getStringValue().toUpperCase()+"%");
3252               }
3253               break;
3254           }
3255         }
3256       }
3257 
3258     return expresion.toString();
3259   }
3260 
3261   private String getNumberExpresion(Restriction restr, Vector sqlValues){
3262     StringBuffer expr = new StringBuffer("FEATURE.ft_number_value ");
3263 
3264     switch (restr.getOperator()){
3265       case Restriction.OPERATOR_EQUATION:
3266         expr = expr.append(" = ");
3267         break;
3268       case Restriction.OPERATOR_BIGGER:
3269         expr = expr.append("  > ");
3270         break;
3271       case Restriction.OPERATOR_LESS:
3272         expr = expr.append(" < ");
3273         break;
3274       case Restriction.OPERATOR_EQUATION_OR_BIGGER:
3275         expr = expr.append(" >= ");
3276         break;
3277       case Restriction.OPERATOR_EQUATION_OR_LESS:
3278         expr = expr.append(" <= ");
3279         break;
3280       default:
3281         return " 0 = 0 ";
3282     }
3283 
3284     expr.append(" ? ");
3285     sqlValues.addElement(restr.getValue());
3286 
3287     return expr.toString();
3288   }
3289 
3290   private String getJoinQuery(List filter, List orderByFilter, Vector sqlValues){
3291     StringBuffer join = new StringBuffer("");
3292     if (filter !=null && filter.size()>0) {
3293       join = join.append(" , ");
3294     }
3295     if (orderByFilter!=null){
3296       for (int i = 0; i<orderByFilter.size(); i++){
3297         join = join.append(Gate.DB_OWNER+".t_feature FT"+i);
3298         join = join.append(" , "+Gate.DB_OWNER+".t_feature_key FTK"+i +" , ");
3299       }
3300     }
3301     return join.toString();
3302   }
3303 
3304   private String getEndPartOfJoin(List filter, List orderByFilter, String lrType, Vector sqlValues){
3305     StringBuffer endJoin = new StringBuffer("");
3306     endJoin = endJoin.append(" WHERE ");
3307 
3308     endJoin = endJoin.append(" LR.lr_type_id = ? ");
3309     if (lrType.equals(DBHelper.CORPUS_CLASS)) {
3310       sqlValues.addElement(new Long(2));
3311     }// if DBHelper.CORPUS_CLASS
3312     if (lrType.equals(DBHelper.DOCUMENT_CLASS)) {
3313       sqlValues.addElement(new Long(1));
3314     }// if DBHelper.DOCUMENT_CLASS
3315 
3316     if (filter != null && filter.size()>0){
3317       endJoin = endJoin.append(" and intersected_feat_restr.ft_entity_id = lr.lr_id ");
3318     }
3319 
3320     if (orderByFilter!=null && orderByFilter.size()>0){
3321       for (int i=0; i<orderByFilter.size(); i++){
3322         endJoin = endJoin.append(" and lr_id=FT"+i+".ft_entity_id ");
3323         endJoin = endJoin.append(" and  FT"+i+".ft_key_id = FTK"+i+".fk_id ");
3324         endJoin = endJoin.append(" and  FTK"+i+".fk_string= ? ");
3325         OrderByRestriction restr = (OrderByRestrictionorderByFilter.get(i);
3326         sqlValues.addElement(restr.getKey());
3327       }
3328       endJoin = endJoin.append(" order by ");
3329       for (int i=0; i<orderByFilter.size(); i++){
3330         OrderByRestriction restr = (OrderByRestrictionorderByFilter.get(i);
3331 
3332         endJoin = endJoin.append("  FT"+i+".ft_number_value ");
3333         if (restr.getOperator()==OrderByRestriction.OPERATOR_ASCENDING){
3334           endJoin = endJoin.append(" asc ");
3335         else {
3336           endJoin = endJoin.append(" desc ");
3337         }
3338        /* endJoin = endJoin.append(", FT"+i+".ft_character_value ");
3339         if (restr.getOperator()==OrderByRestriction.OPERATOR_ASCENDING){
3340           endJoin = endJoin.append(" asc ");
3341         } else {
3342           endJoin = endJoin.append(" desc ");
3343         }*/
3344         if (i<orderByFilter.size()-1){
3345           endJoin = endJoin.append(" , ");
3346         }
3347       }
3348     }
3349     return endJoin.toString();
3350   }
3351 
3352   public List findDocIdsByAnn(List constraints, int limitcountthrows PersistenceException {
3353       Vector lrsIDs = new Vector();
3354       CallableStatement stmt = null;
3355       ResultSet rs = null;
3356       Connection conn = null;
3357 
3358       try {
3359         Vector sqlValues = new Vector();
3360         String sql = getSQLQueryAnn(constraints, limitcount, sqlValues);
3361         conn = DBHelper.connect(this.getStorageUrl()true);
3362         stmt = conn.prepareCall(sql);
3363         for (int i = 0; i<sqlValues.size(); i++){
3364           if (sqlValues.elementAt(iinstanceof String){
3365             stmt.setString(i+1,sqlValues.elementAt(i).toString());
3366           }
3367           else if (sqlValues.elementAt(iinstanceof Long){
3368             stmt.setLong(i+1,((LongsqlValues.elementAt(i)).longValue());
3369           }
3370           else if (sqlValues.elementAt(iinstanceof Integer){
3371             stmt.setLong(i+1,((IntegersqlValues.elementAt(i)).intValue());
3372           }
3373         }
3374         stmt.execute();
3375         rs = stmt.getResultSet();
3376 
3377         while (rs.next()) {
3378           long lr_ID = rs.getLong(1);
3379           lrsIDs.addElement(new Long(lr_ID));
3380         }
3381         return lrsIDs;
3382       }
3383       catch(SQLException sqle) {
3384         throw new PersistenceException("can't get LRs from DB: ["+ sqle+"]");
3385       }
3386       catch (ClassNotFoundException cnfe){
3387         throw new PersistenceException("can't not find driver: ["+ cnfe +"]");
3388       }
3389       finally {
3390         DBHelper.cleanup(rs);
3391         DBHelper.cleanup(stmt);
3392         DBHelper.disconnect(conn, true);
3393       }
3394     }
3395 
3396   private String getSQLQueryAnn(List constraints, int limitcount, Vector sqlValues){
3397     StringBuffer sql = new StringBuffer("");
3398     sql.append("SELECT lr_id ");
3399     sql.append(" FROM gateadmin.t_lang_resource LR ");
3400     sql.append(" WHERE LR.lr_type_id = 1 ");
3401 
3402     for (int i = 0; i<constraints.size(); i++){
3403       Restriction rest = (Restrictionconstraints.get(i);
3404       sql.append(" AND EXISTS( ");
3405       sql.append(" SELECT F.ft_id ");
3406       sql.append(" FROM   gateadmin.t_feature F, ");
3407       sql.append(" gateadmin.T_AS_ANNOTATION A, ");
3408       sql.append(" gateadmin.T_ANNOT_SET S, ");
3409       sql.append(" gateadmin.T_DOCUMENT D, ");
3410       sql.append(" gateadmin.t_feature_key FK ");
3411       sql.append(" WHERE  F.ft_entity_id = A.asann_ann_id ");
3412       sql.append(" AND  A.asann_as_id = S.as_id ");
3413       sql.append(" AND  S.as_doc_id = D.doc_id ");
3414       sql.append(" AND  D.doc_lr_id = LR.LR_ID ");
3415       sql.append(" AND  S.AS_NAME = ? ");
3416       sqlValues.add("NewsCollector");
3417       sql.append(" AND  FK.fk_id = F.ft_key_id ");
3418       sql.append(" AND  FK.fk_string= ? ");
3419       sqlValues.add(rest.getKey());
3420       sql.append(" AND  F.FT_CHARACTER_VALUE = ? ");
3421       sqlValues.add(rest.getStringValue());
3422       sql.append(" ) ");
3423     }
3424     sql.append(" group by lr_id ");
3425     if (limitcount>0){
3426       sql = sql.insert(0,"select lr_id from ( ");
3427       sql = sql.append") where rownum<"+(limitcount+1));
3428     }
3429     return sql.toString();
3430   }
3431   private class Feature {
3432 
3433     Long entityID;
3434     int entityType;
3435     String key;
3436     Object value;
3437     int valueType;
3438 
3439     public Feature(Long eid, int eType, String key, Object value, int vType) {
3440 
3441       this.entityID = eid;
3442       this.entityType = eType;
3443       this.key = key;
3444       this.value = value;
3445       this.valueType = vType;
3446     }
3447   }
3448 
3449   private class RestrictionComepator implements Comparator{
3450     public int compare(Object o1, Object o2){
3451       Restriction r1 = (Restrictiono1;
3452       Restriction r2 = (Restrictiono2;
3453       return r1.getKey().compareTo(r2.getKey());
3454     }
3455 
3456     public boolean equals(Object o){
3457       return false;
3458     }
3459   }
3460 
3461 
3462 }