001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojavax.bio.db.biosql; 023import java.io.Serializable; 024import java.lang.reflect.InvocationTargetException; 025import java.lang.reflect.Method; 026import java.util.HashSet; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031 032import org.biojava.bio.BioError; 033import org.biojava.bio.BioException; 034import org.biojava.bio.seq.Feature; 035import org.biojava.bio.seq.FeatureFilter; 036import org.biojava.bio.seq.FeatureHolder; 037import org.biojava.bio.seq.SimpleFeatureHolder; 038import org.biojava.bio.seq.db.IllegalIDException; 039import org.biojava.utils.ChangeEvent; 040import org.biojava.utils.ChangeSupport; 041import org.biojava.utils.ChangeVetoException; 042import org.biojavax.bio.db.AbstractRichSequenceDB; 043import org.biojavax.bio.db.HashRichSequenceDB; 044import org.biojavax.bio.db.RichSequenceDB; 045import org.biojavax.bio.seq.RichFeature; 046import org.biojavax.bio.seq.RichSequence; 047import org.biojavax.bio.seq.SimpleRichSequence; 048 049 050/** 051 * 052 * @author Richard Holland 053 * @author David Scott 054 * @since 1.5 055 */ 056public class BioSQLRichSequenceDB extends AbstractRichSequenceDB { 057 058 private Object session; 059 private String name; 060 061 private Method createCriteria; 062 private Method addCriteria; 063 private Method listCriteria; 064 private Method createAlias; 065 private Method createQuery; 066 private Method setParameter; 067 private Method list; 068 private Method delete; 069 private Method saveOrUpdate; 070 private Method getIdentifier; 071 private Method load; 072 private Method evict; 073 074 /** Creates a new instance of BioSQLRichSequenceDB */ 075 public BioSQLRichSequenceDB(Object session) { 076 this(null,session); 077 } 078 079 /** Creates a new instance of BioSQLRichSequenceDB */ 080 public BioSQLRichSequenceDB(String name, Object session) { 081 this.name = name; 082 this.session = session; 083 try { 084 // Lazy load the Session class from Hibernate. 085 Class hibernateSession = session.getClass(); 086 Class realHibernateSession = Class.forName("org.hibernate.Session"); 087 // Test to see if our parameter is really a Session 088 if (!realHibernateSession.isAssignableFrom(hibernateSession)) 089 throw new IllegalArgumentException("Parameter must be a org.hibernate.Session object"); 090 this.session = session; 091 // Lookup the createQuery method 092 this.createQuery = hibernateSession.getMethod("createQuery", new Class[]{String.class}); 093 this.delete = hibernateSession.getMethod("delete", new Class[]{String.class,Object.class}); 094 this.saveOrUpdate = hibernateSession.getMethod("saveOrUpdate", new Class[]{String.class,Object.class}); 095 this.getIdentifier = hibernateSession.getMethod("getIdentifier", new Class[]{Object.class}); 096 this.evict = hibernateSession.getMethod("evict", new Class[]{Object.class}); 097 this.load = hibernateSession.getMethod("load", new Class[]{String.class,Serializable.class}); 098 // Lazy load the Query class from Hibernate. 099 Class hibernateQuery = Class.forName("org.hibernate.Query"); 100 // Lookup the setParameter and uniqueQuery methods 101 this.setParameter = hibernateQuery.getMethod("setParameter", new Class[]{int.class,Object.class}); 102 this.list = hibernateQuery.getMethod("list", new Class[]{}); 103 // Lazy load the Criteria class. 104 Class criteria = Class.forName("org.hibernate.Criteria"); 105 // Lookup the critera methods 106 this.createCriteria = hibernateSession.getMethod("createCriteria", new Class[]{Class.class}); 107 this.addCriteria = criteria.getMethod("add", new Class[]{Class.forName("org.hibernate.criterion.Criterion")}); 108 this.listCriteria = criteria.getMethod("list", new Class[]{}); 109 this.createAlias = criteria.getMethod("createAlias", new Class[]{String.class,String.class}); 110 } catch (ClassNotFoundException e) { 111 throw new RuntimeException(e); 112 } catch (NoSuchMethodException e) { 113 throw new RuntimeException(e); 114 } 115 } 116 117 public String getName() { 118 return this.name; 119 } 120 121 public Object getHibernateSession() { 122 return this.session; 123 } 124 125 public FeatureHolder processFeatureFilter(FeatureFilter ff) { 126 BioSQLFeatureFilter bff = BioSQLFeatureFilter.Tools.convert(ff); 127 SimpleFeatureHolder results = new SimpleFeatureHolder(); 128 // Apply the filter to the db. 129 try { 130 Object criteria = this.createCriteria.invoke(this.session, new Object[]{RichFeature.class}); 131 this.addCriteria.invoke(criteria, new Object[]{bff.asCriterion()}); 132 Map aliases = bff.criterionAliasMap(); 133 for (Iterator i = aliases.keySet().iterator(); i.hasNext(); ) { 134 String property = (String)i.next(); 135 String alias = (String)aliases.get(property); 136 this.createAlias.invoke(criteria,new Object[]{property,alias}); 137 } 138 List cats = (List)this.listCriteria.invoke(criteria, (Object[])null); 139 for (Iterator i = cats.iterator(); i.hasNext(); ) results.addFeature((Feature)i.next()); 140 } catch (IllegalAccessException e) { 141 throw new RuntimeException(e); 142 } catch (InvocationTargetException e) { 143 throw new RuntimeException(e); 144 } catch (ChangeVetoException cve) { 145 throw new BioError("Assertion failed: couldn't modify newly created SimpleFeatureHolder",cve); 146 } 147 return results; 148 } 149 150 public FeatureHolder filter(FeatureFilter ff) { 151 FeatureHolder fh = this.processFeatureFilter(ff); 152 // Post-process only if original filter was not a BioSQLFeatureFilter. 153 if (!(ff instanceof BioSQLFeatureFilter)) { 154 // Iterate through returned features and remove any that are not accepted. 155 SimpleFeatureHolder sfh = new SimpleFeatureHolder(); 156 for (Iterator i = fh.features(); i.hasNext(); ) { 157 Feature f = (Feature)i.next(); 158 try { 159 if (ff.accept(f)) sfh.addFeature(f); 160 } catch (ChangeVetoException cve) { 161 throw new BioError("Assertion failed: couldn't modify newly created SimpleFeatureHolder",cve); 162 } 163 } 164 fh = sfh; 165 } 166 return fh; 167 } 168 169 public Set ids() { 170 try { 171 // Build the query object 172 String queryText = "select distinct name from Sequence"; 173 Object query = this.createQuery.invoke(this.session, new Object[]{queryText}); 174 // Get the results 175 List result = (List)this.list.invoke(query, (Object[])null); 176 // Return the found object, if found - null if not. 177 return new HashSet(result); 178 } catch (Exception e) { 179 // Throw the exception with our nice message 180 throw new RuntimeException("Error while trying to load all names",e); 181 } 182 } 183 184 public RichSequence fullyLoadRichSequence(RichSequence id) throws IllegalIDException, BioException { 185 if (id instanceof SimpleRichSequence) return id; 186 try { 187 Serializable identifier = (Serializable)this.getIdentifier.invoke(this.session, new Object[]{id}); 188 this.evict.invoke(this.session, new Object[]{id}); 189 return (RichSequence)this.load.invoke(this.session, new Object[]{"Sequence",identifier}); 190 } catch (Exception e) { 191 // Throw the exception with our nice message 192 throw new RuntimeException("Error while trying to load by id: "+id,e); 193 } 194 } 195 196 public RichSequence getRichSequence(String id) throws IllegalIDException, BioException { 197 try { 198 // Build the query object 199 String queryText = "from Sequence where name = ?"; 200 Object query = this.createQuery.invoke(this.session, new Object[]{queryText}); 201 // Set the parameters 202 query = this.setParameter.invoke(query, new Object[]{new Integer(0), id}); 203 // Get the results 204 List result = (List)this.list.invoke(query,(Object[]) null); 205 // If the result doesn't just have a single entry, throw an exception 206 if (result.size()==0) throw new IllegalIDException("Id not found: "+id); 207 else if (result.size()>1) throw new IllegalIDException("Multiple records found with that id - use getRichSequences: "+id); 208 // Return the found object, if found - null if not. 209 return (RichSequence)result.get(0); 210 } catch (Exception e) { 211 // Throw the exception with our nice message 212 throw new RuntimeException("Error while trying to load by id: "+id,e); 213 } 214 } 215 216 public RichSequenceDB getRichSequences(Set ids) throws BioException, IllegalIDException { 217 return this.getRichSequences(ids,null); 218 } 219 220 public RichSequenceDB getRichSequences(Set ids, RichSequenceDB db) throws BioException, IllegalIDException { 221 if (db==null) db = new HashRichSequenceDB(); 222 try { 223 for (Iterator i = ids.iterator(); i.hasNext(); ) { 224 String id = (String)i.next(); 225 // Build the query object 226 String queryText = "from Sequence where name = ?"; 227 Object query = this.createQuery.invoke(this.session, new Object[]{queryText}); 228 // Set the parameters 229 query = this.setParameter.invoke(query, new Object[]{new Integer(0), id}); 230 // Get the results 231 List result = (List)this.list.invoke(query,(Object[]) null); 232 // If the result doesn't just have a single entry, throw an exception 233 if (result.size()==0) throw new IllegalIDException("Id not found: "+id); 234 // Add the results to the results db. 235 for (Iterator j = result.iterator(); j.hasNext(); ) db.addRichSequence((RichSequence)j.next()); 236 } 237 } catch (Exception e) { 238 // Throw the exception with our nice message 239 throw new RuntimeException("Error while trying to load by ids: "+ids,e); 240 } 241 return db; 242 } 243 244 public void removeRichSequence(String id) throws IllegalIDException, BioException, ChangeVetoException { 245 if(!hasListeners(RichSequenceDB.SEQUENCES)) { 246 this._removeRichSequence(id); 247 } else { 248 ChangeSupport changeSupport = getChangeSupport(RichSequenceDB.SEQUENCES); 249 synchronized(changeSupport) { 250 ChangeEvent ce = new ChangeEvent( 251 this, 252 RichSequenceDB.SEQUENCES, 253 null, 254 id 255 ); 256 changeSupport.firePreChangeEvent(ce); 257 this._removeRichSequence(id); 258 changeSupport.firePostChangeEvent(ce); 259 } 260 } 261 } 262 263 private void _removeRichSequence(String id) throws IllegalIDException, BioException, ChangeVetoException { 264 try { 265 // Find the object 266 RichSequence be = this.getRichSequence(id); 267 // Get the results 268 this.delete.invoke(this.session, new Object[]{"Sequence",be}); 269 } catch (Exception e) { 270 // Throw the exception with our nice message 271 throw new RuntimeException("Error while trying to delete by id: "+id,e); 272 } 273 } 274 275 public void addRichSequence(RichSequence seq) throws IllegalIDException, BioException, ChangeVetoException { 276 if(!hasListeners(RichSequenceDB.SEQUENCES)) { 277 this._addRichSequence(seq); 278 } else { 279 ChangeSupport changeSupport = getChangeSupport(RichSequenceDB.SEQUENCES); 280 synchronized(changeSupport) { 281 ChangeEvent ce = new ChangeEvent( 282 this, 283 RichSequenceDB.SEQUENCES, 284 null, 285 seq 286 ); 287 changeSupport.firePreChangeEvent(ce); 288 this._addRichSequence(seq); 289 changeSupport.firePostChangeEvent(ce); 290 } 291 } 292 } 293 294 private void _addRichSequence(RichSequence seq) throws IllegalIDException, BioException, ChangeVetoException { 295 try { 296 // Get the results 297 this.saveOrUpdate.invoke(this.session, new Object[]{"Sequence",seq}); 298 } catch (Exception e) { 299 // Throw the exception with our nice message 300 throw new RuntimeException("Error while trying to save RichSequence with id: "+seq.getName(),e); 301 } 302 } 303 304}