View Javadoc

1   package uk.ac.ebi.intenz.mapper;
2   
3   import java.sql.Connection;
4   import java.sql.PreparedStatement;
5   import java.sql.ResultSet;
6   import java.sql.SQLException;
7   import java.sql.Statement;
8   import java.util.ArrayList;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  import java.util.TreeMap;
13  import java.util.TreeSet;
14  
15  import org.apache.log4j.Logger;
16  
17  import uk.ac.ebi.intenz.domain.constants.EnzymeNameTypeConstant;
18  import uk.ac.ebi.intenz.domain.constants.EnzymeSourceConstant;
19  import uk.ac.ebi.intenz.domain.constants.Status;
20  import uk.ac.ebi.intenz.domain.enzyme.EnzymaticReactions;
21  import uk.ac.ebi.intenz.domain.enzyme.EnzymeComment;
22  import uk.ac.ebi.intenz.domain.enzyme.EnzymeCommissionNumber;
23  import uk.ac.ebi.intenz.domain.enzyme.EnzymeEntry;
24  import uk.ac.ebi.intenz.domain.enzyme.EnzymeLink;
25  import uk.ac.ebi.intenz.domain.enzyme.EnzymeName;
26  import uk.ac.ebi.intenz.domain.exceptions.DomainException;
27  import uk.ac.ebi.intenz.domain.exceptions.EcException;
28  import uk.ac.ebi.intenz.domain.exceptions.EnzymeReactionException;
29  import uk.ac.ebi.intenz.domain.exceptions.EnzymeReferenceException;
30  import uk.ac.ebi.intenz.domain.history.HistoryGraph;
31  import uk.ac.ebi.intenz.domain.reference.Reference;
32  import uk.ac.ebi.intenz.domain.stats.EnzymeStatistics;
33  import uk.ac.ebi.rhea.mapper.MapperException;
34  
35  /**
36   * Maps enzyme entry information to the corresponding database tables.
37   * <br>
38   * <b>IMPORTANT</b>: after using instances of this class, call the
39   * {@link #close()} method, otherwise the underlying Rhea mapper objects will
40   * keep their statements open.
41   * @author Michael Darsow
42   * @version $Revision: 1.5 $ $Date: 2009/05/26 14:59:09 $
43   */
44  public class EnzymeEntryMapper {
45  	
46    private static final Logger LOGGER = Logger.getLogger(EnzymeEntryMapper.class);
47  
48    /**
49     * The names are used for the heading of each enzyme, that is to which class,
50     * sub- and sub-subclass it belongs to.
51     */
52    private static final String COLUMNS =
53  	  "e.enzyme_id, e.ec1, e.ec2, e.ec3, e.ec4, e.history, e.note, e.status, " +
54        "e.source, e.active, s1.name, s2.name, s3.name";
55  
56    private static final String LIST_COLUMNS =
57  	  "enzyme_id, ec1, ec2, ec3, ec4, history, note, status, source, active";
58  
59    private EnzymeReactionMapper enzymeReactionMapper;
60  
61    public EnzymeEntryMapper() {
62  	  enzymeReactionMapper = new EnzymeReactionMapper();
63    }
64  
65    public EnzymeEntryMapper(EnzymeReactionMapper enzymeReactionMapper){
66  	  this.enzymeReactionMapper = enzymeReactionMapper;	
67    }
68    
69    @Override
70  	protected void finalize() throws Throwable {
71  		close();
72  	}
73  
74  	public void close() {
75  		if (enzymeReactionMapper != null){
76  			try {
77  				enzymeReactionMapper.close();
78  			} catch (MapperException e) {
79  				LOGGER.error("Closing enzymeReactionMapper", e);
80  			}
81  		}
82  	}
83  
84    /**
85     * Returns the SQL statement used for loading an enzyme by the given EC.
86     *
87     * @return the SQL statement.
88     */
89    private String findByEcStatement() {
90      return new StringBuilder("SELECT ").append(COLUMNS)
91         .append(" FROM enzymes e, classes s1, subclasses s2, subsubclasses s3")
92  	   .append(" WHERE e.ec1 = ? AND e.ec2 = ? AND e.ec3 = ? AND e.ec4 = ?")
93  	   .append(" AND s1.ec1 = ? AND s2.ec1 = ? AND s2.ec2 = ?")
94  	   .append(" AND s3.ec1 = ? AND s3.ec2 = ? AND s3.ec3 = ?")
95  	   .append(" AND e.status = ? AND enzyme_id NOT IN")
96         .append(" (SELECT before_id FROM history_events WHERE event_class = 'MOD')")
97  	   .toString();
98    }
99  
100   /**
101    * Returns the SQL statement used for loading all enzymes by the given EC.
102    * @return the SQL statement.
103    */
104   private String findAllSubSubclassEntriesByEcStatement() {
105     return new StringBuilder("SELECT ").append(COLUMNS)
106        .append(" FROM enzymes e, classes s1, subclasses s2, subsubclasses s3")
107        .append(" WHERE e.ec1 = ? AND e.ec2 = ? AND e.ec3 = ?")
108        .append(" AND s1.ec1 = ? AND s2.ec1 = ? AND s2.ec2 = ?")
109        .append(" AND s3.ec1 = ? AND s3.ec2 = ? AND s3.ec3 = ? AND e.source = 'INTENZ'")
110        .append(" ORDER BY e.ec1, e.ec2, e.ec3, e.ec4").toString();
111   }
112   
113   /**
114    * @param preliminary Search only for preliminary (<code>true</code>)
115    * 		or only non-preliminary (<code>false</code>)? If <code>null</code>,
116    * 		both are included in the search.
117    * @return
118    */
119   private String findAllByEcStatement(Boolean preliminary){
120 	    StringBuilder sb = new StringBuilder("SELECT ").append(COLUMNS)
121 	       .append(" FROM enzymes e, classes s1, subclasses s2, subsubclasses s3")
122 	       .append(" WHERE e.ec1 = ? AND e.ec2 = ? AND e.ec3 = ? AND e.ec4 = ?")
123 	       .append(" AND s1.ec1 = ? AND s2.ec1 = ? AND s2.ec2 = ?")
124 	       .append(" AND s3.ec1 = ? AND s3.ec2 = ? AND s3.ec3 = ?");
125 	    if (preliminary != null){
126 	    	sb.append(" AND e.status ").append(preliminary? "=" : "!=").append(" 'PM'");
127 	    }
128 	    return sb.toString();
129   }
130 
131   /**
132    * Returns the SQL statement used for loading an enzyme by the given ID.
133    *
134    * @return the SQL statement.
135    */
136   private String findByIdStatement() {
137     return new StringBuilder("SELECT ").append(COLUMNS)
138        .append(" FROM enzymes e, classes s1, subclasses s2, subsubclasses s3")
139        .append(" WHERE e.enzyme_id = ? AND s1.ec1 = ? AND s2.ec1 = ? AND s2.ec2 = ?")
140        .append(" AND s3.ec1 = ? AND s3.ec2 = ? AND s3.ec3 = ?").toString();
141   }
142 
143   /**
144    * Returns the SQL statement used for loading a list of public enzymes.
145    * <p/>
146    * Because of modified enzymes which still have the same EC number
147    * (and are both approved) only the most up-to-date
148    * version should be loaded. This is done by the substatement.
149    * @return the SQL statement.
150    */
151   private String findListStatement() {
152     return new StringBuilder("SELECT ").append(LIST_COLUMNS)
153        .append(" FROM enzymes WHERE ec1 = ? AND ec2 = ? AND ec3 = ?")
154        .append(" AND status IN ('OK','PM') AND enzyme_id NOT IN")
155        .append(" (SELECT before_id FROM history_events WHERE event_class = 'MOD')")
156        .append(" ORDER BY status, ec1, ec2, ec3, ec4").toString();
157   }
158 
159   /**
160    * Returns the SQL statement used for loading the list of all public enzymes,
161    * be it accepted by NC-IUBMB or preliminary EC numbers used in UniProt.
162    * <p/>
163    * Because of modified enzymes which still have the same EC number (and are both approved) only the most up-to-date
164    * version should be loaded. This is done by the substatement.
165    *
166    * @return the SQL statement.
167    */
168   private String findAllStatement() {
169     return new StringBuilder("SELECT ").append(LIST_COLUMNS)
170        .append(" FROM enzymes WHERE status IN ('OK','PM') AND enzyme_id NOT IN")
171 	   .append(" (SELECT before_id FROM history_events WHERE event_class = 'MOD')")
172 	   .append(" ORDER BY ec1, ec2, ec3, status, ec4").toString();
173   }
174 
175   /**
176    * Returns the SQL statement used for loading the list of enzymes with a
177    * given status.
178    * @return the SQL statement.
179    */
180   private String findCoreListStatement() {
181     return new StringBuilder("SELECT ").append(LIST_COLUMNS)
182        .append(" FROM enzymes WHERE status = ?")
183        .append(" ORDER BY ec1, ec2, ec3, ec4").toString();
184   }
185 
186   /**
187    * Returns the SQL statement used for loading an EC number by a given enzyme ID.
188    * @return the SQL statement.
189    */
190   private String findEcStatement() {
191     return "SELECT ec1, ec2, ec3, ec4, status FROM enzymes WHERE enzyme_id = ?";
192   }
193 
194   private String findIDInMappingTable() {
195     return "SELECT enzyme_id FROM id2ec WHERE ec = ? and status = ?";
196   }
197 
198   /**
199    * Returns the SQL statement used for loading the history line by a given enzyme ID.
200    *
201    * @return the SQL statement.
202    */
203   private String findHistoryLineStatement() {
204     return "SELECT history FROM enzymes WHERE enzyme_id = ?";
205   }
206 
207   /**
208    * Returns the SQL statement used for loading the note by a given enzyme ID.
209    *
210    * @return the SQL statement.
211    */
212   private String findNoteStatement() {
213     return "SELECT note FROM enzymes WHERE enzyme_id = ?";
214   }
215 
216   /**
217    * Returns the SQL statement to fetch the next available enzyme ID.
218    *
219    * @return the SQL statement.
220    */
221   private String findNextEnzymeIdStatement() {
222     return "SELECT s_enzyme_id.nextval from DUAL";
223   }
224 
225   private String countIUBMBEnzymesStatement() {
226 	  return new StringBuilder("SELECT status, active FROM enzymes")
227        .append(" WHERE source = 'IUBMB' AND enzyme_id NOT IN")
228        .append(" (SELECT before_id FROM history_events WHERE event_class = 'MOD')")
229        .append(" ORDER BY ec1, ec2, ec3, ec4").toString();
230   }
231 
232   private String countClassesStatement() {
233     return "SELECT count(*) FROM classes";
234   }
235 
236   private String countSubclassesStatement() {
237     return "SELECT count(*) FROM subclasses";
238   }
239 
240   private String countSubSubclassesStatement() {
241     return "SELECT count(*) FROM subsubclasses";
242   }
243 
244   /**
245    * Returns the INSERT statement for inserting an enzyme.
246    *
247    * @return the SQL statement.
248    */
249   private String insertStatement() {
250     return "INSERT INTO enzymes (enzyme_id, ec1, ec2, ec3, ec4, history, note, status, source, active)" +
251     		" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
252   }
253 
254   /**
255    * Creates the SQL statement to be used to update the core data of an enzyme.
256    *
257    * @return the SQL statement.
258    */
259   private String updateStatement() {
260     return "UPDATE enzymes SET ec1 = ?, ec2 = ?, ec3 = ?, ec4 = ?, status = ?," +
261     		" source = ?, note = ?, history = ?, active = ? WHERE enzyme_id = ?";
262   }
263 
264   /**
265    * Updates the EC.
266    *
267    * @return the SQL statement.
268    */
269   private String updateEcStatement() {
270     return "UPDATE enzymes SET ec1 = ?, ec2 = ?, ec3 = ?, ec4 = ? WHERE enzyme_id = ?";
271   }
272 
273   /**
274    * Updates the history line.
275    *
276    * @return the SQL statement.
277    */
278   private String updateHistoryLineStatement() {
279     return "UPDATE enzymes SET history = ? WHERE enzyme_id = ?";
280   }
281 
282   /**
283    * Updates the note.
284    *
285    * @return the SQL statement.
286    */
287   private String updateNoteStatement() {
288     return "UPDATE enzymes SET note = ? WHERE enzyme_id = ?";
289   }
290 
291   /**
292    * Updates the status.
293    *
294    * @return the SQL statement.
295    */
296   private String updateStatusStatement() {
297     return "UPDATE enzymes SET status = ? WHERE enzyme_id = ?";
298   }
299 
300   /**
301    * Tries to find entry information about an enzyme.
302    * @param ec1 Number of class to search for.
303    * @param ec2 Number of subclass to search for.
304    * @param ec3 Number of sub-subclass to search for.
305    * @param ec4 Number of entry to search for.
306    * @param status the status of the searched enzyme.
307    * 	If <code>null</code>, it defaults to {@link Status#APPROVED}.
308    * @param con The logical connection.
309    * @return an <code>EnzymeEntry</code> instance or <code>null</code> if
310    * nothing has been found.
311    * @throws java.sql.SQLException 
312    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException
313  * @throws MapperException in case of problem retrieving reaction/cofactor info.
314    */
315   public EnzymeEntry findByEc(int ec1, int ec2, int ec3, int ec4, Status status,
316 		  Connection con)
317   throws SQLException, DomainException, MapperException {
318     PreparedStatement findStatement = null;
319     ResultSet rs = null;
320     EnzymeEntry result = null;
321     try {
322       // Core information.
323       findStatement = con.prepareStatement(findByEcStatement());
324       findStatement.setInt(1, ec1);
325       findStatement.setInt(2, ec2);
326       findStatement.setInt(3, ec3);
327       findStatement.setInt(4, ec4);
328       findStatement.setInt(5, ec1);
329       findStatement.setInt(6, ec1);
330       findStatement.setInt(7, ec2);
331       findStatement.setInt(8, ec1);
332       findStatement.setInt(9, ec2);
333       findStatement.setInt(10, ec3);
334       findStatement.setString(11, status == null? "OK" : status.getCode());
335       rs = findStatement.executeQuery();
336       if (rs.next()) {
337         result = doLoad(rs);
338         findEnzymeData(result, con);
339       }
340     } finally {
341     	if (rs != null) rs.close();
342       if (findStatement != null) findStatement.close();
343     }
344 
345     return result;
346   }
347   
348   /**
349    * Finds an enzyme by EC number (as String) and status.
350    * @param ecString the EC number.
351    * @param status the enzyme status.
352    * @param con a database connection.
353    * @return an enzyme entry, or <code>null</code> if not found.
354    * @throws NumberFormatException
355    * @throws SQLException
356    * @throws DomainException
357    * @throws MapperException
358    */
359   public EnzymeEntry findByEc(String ecString, Status status, Connection con)
360   throws NumberFormatException, SQLException, DomainException, MapperException{
361 	  EnzymeCommissionNumber ec = EnzymeCommissionNumber.valueOf(ecString);
362 	  if (EnzymeCommissionNumber.isPreliminary(ecString)){
363 		  status = Status.PRELIMINARY;
364 	  }
365 	  return findByEc(ec.getEc1(), ec.getEc2(), ec.getEc3(), ec.getEc4(),
366 			  status, con);
367   }
368   
369   public List<EnzymeEntry> findAllByEc(int ec1, int ec2, int ec3, int ec4,
370 		  Boolean preliminary, Connection con)
371   throws SQLException, DomainException {
372 	    if (con == null) throw new NullPointerException("Parameter 'con' must not be null.");
373 	    PreparedStatement findAllByEcStatement = null;
374 	    ResultSet rs = null;
375 	    List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
376 
377 	    try {
378 	      // Core information.
379 	      findAllByEcStatement = con.prepareStatement(findAllByEcStatement(preliminary));
380 	      findAllByEcStatement.setInt(1, ec1);
381 	      findAllByEcStatement.setInt(2, ec2);
382 	      findAllByEcStatement.setInt(3, ec3);
383 	      findAllByEcStatement.setInt(4, ec4);
384 	      findAllByEcStatement.setInt(5, ec1);
385 	      findAllByEcStatement.setInt(6, ec1);
386 	      findAllByEcStatement.setInt(7, ec2);
387 	      findAllByEcStatement.setInt(8, ec1);
388 	      findAllByEcStatement.setInt(9, ec2);
389 	      findAllByEcStatement.setInt(10, ec3);
390 	      rs = findAllByEcStatement.executeQuery();
391 	      while (rs.next()) {
392 	        EnzymeEntry enzymeEntry = doLoadGhost(rs);
393 
394 	        // History information
395 	        loadHistoryGraph(enzymeEntry, con);
396 
397 	        // Common name
398 	        EnzymeNameMapper nameMapper = new EnzymeNameMapper();
399 	        List<EnzymeName> commonNames = nameMapper.findCommonNames(enzymeEntry.getId(), con);
400 	        if (commonNames != null) enzymeEntry.setCommonNames(commonNames);
401 
402 	        result.add(enzymeEntry);
403 	      }
404 	    } finally {
405 	    	if (rs != null) rs.close();
406 	      if (findAllByEcStatement != null) findAllByEcStatement.close();
407 	    }
408 
409 	    if (result.size() == 0) return null;
410 	    return result;
411 	  
412 	  
413   }
414 
415   /**
416    * Tries to find entry information about all enzymes specified by the given EC.
417    *
418    * @param ec1 Number of class to search for.
419    * @param ec2 Number of subclass to search for.
420    * @param ec3 Number of sub-subclass to search for.
421    * @param ec4 Number of entry to search for.
422    * @param con The logical connection.
423    * @return an <code>EnzymeEntry</code> instance or <code>null</code>
424    * 		if nothing has been found.
425    * @throws NullPointerException if parameter <code>con</code> is <code>null</code>.
426    * @throws SQLException if a database error occurs.
427    * @throws DomainException if any error related to domain information occurs.
428    */
429   public List<EnzymeEntry> findAllByEc(int ec1, int ec2, int ec3, int ec4, Connection con)
430   throws SQLException, DomainException {
431 	  return findAllByEc(ec1, ec2, ec3, ec4, null, con);
432   }
433 
434 	private HistoryGraph getHistoryGraph(Connection con, EnzymeEntry enzymeEntry)
435 			throws SQLException, DomainException {
436 		HistoryGraph historyGraph;
437 		switch (enzymeEntry.getStatus()){
438 		case PRELIMINARY:
439 			if (enzymeEntry.isActive()){
440 				// look into the future in case it is being transferred:
441 			    EnzymeFutureMapper futureEventsMapper = new EnzymeFutureMapper();
442 			    historyGraph = futureEventsMapper.find(enzymeEntry, con);
443 				break;
444 			}
445 			// else it has been transferred, look past like for APPROVED:
446 		case APPROVED:
447 		    EnzymeHistoryMapper historyEventsMapper = new EnzymeHistoryMapper();
448 		    historyGraph = historyEventsMapper.find(enzymeEntry, con);
449 			break;
450 		default:
451 		    EnzymeFutureMapper futureEventsMapper = new EnzymeFutureMapper();
452 		    historyGraph = futureEventsMapper.find(enzymeEntry, con);
453 		}
454 		return historyGraph;
455 	}
456 
457   public List<EnzymeEntry> findAllSubSubclassEntriesByEc(int ec1, int ec2, int ec3, Connection con)
458   throws SQLException, DomainException {
459     if (con == null) throw new NullPointerException("Parameter 'con' must not be null.");
460     PreparedStatement findAllByEcStatement = null;
461     ResultSet rs = null;
462     Map<EnzymeCommissionNumber, EnzymeEntry> map =
463     	new TreeMap<EnzymeCommissionNumber, EnzymeEntry>();
464 
465     try {
466       // Core information.
467       findAllByEcStatement = con.prepareStatement(findAllSubSubclassEntriesByEcStatement());
468       findAllByEcStatement.setInt(1, ec1);
469       findAllByEcStatement.setInt(2, ec2);
470       findAllByEcStatement.setInt(3, ec3);
471       findAllByEcStatement.setInt(4, ec1);
472       findAllByEcStatement.setInt(5, ec1);
473       findAllByEcStatement.setInt(6, ec2);
474       findAllByEcStatement.setInt(7, ec1);
475       findAllByEcStatement.setInt(8, ec2);
476       findAllByEcStatement.setInt(9, ec3);
477       rs = findAllByEcStatement.executeQuery();
478       while (rs.next()) {
479         EnzymeEntry enzymeEntry = doLoadGhost(rs);
480         if (map.containsKey(enzymeEntry.getEc())){
481         	EnzymeEntry existing = (EnzymeEntry) map.get(enzymeEntry.getEc());
482         	// From several entries with the same EC number,
483         	// we choose the most recent one (higher id):
484         	if (existing.getId().longValue() > enzymeEntry.getId().longValue())
485         		continue;
486         }
487         // History information
488         loadHistoryGraph(enzymeEntry, con);
489 
490         // Common name
491         EnzymeNameMapper nameMapper = new EnzymeNameMapper();
492         List<EnzymeName> commonNames = nameMapper.findCommonNames(enzymeEntry.getId(), con);
493         if (commonNames != null) enzymeEntry.setCommonNames(commonNames);
494 
495         map.put(enzymeEntry.getEc(), enzymeEntry);
496       }
497     } finally {
498     	if (rs != null) rs.close();
499       if (findAllByEcStatement != null) findAllByEcStatement.close();
500     }
501 
502     return map.isEmpty()? null : new ArrayList<EnzymeEntry>(map.values());
503   }
504 
505   /**
506    * Tries to find entry information about an enzyme.
507    * @param id  The enzyme ID.
508    * @param con The logical connection.
509    * @param ghost return a ghost (minimal info)?
510    * @return an <code>EnzymeEntry</code> instance
511    * 		or <code>null</code> if nothing has been found.
512    * @throws java.sql.SQLException
513    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
514    * @throws MapperException in case of problem retrieving reaction/cofactor
515    * 		info for non-ghosts.
516    */
517   public EnzymeEntry findById(Long id, Connection con, boolean ghost)
518   throws SQLException, DomainException, MapperException {
519     PreparedStatement findStatement = null;
520     EnzymeEntry result = null;
521     ResultSet rsEc = null, rs = null;
522     int ec1, ec2, ec3;
523 
524     try {
525       // EC number (necessary for the heading)
526       findStatement = con.prepareStatement(findEcStatement());
527       findStatement.setLong(1, id.longValue());
528       rsEc = findStatement.executeQuery();
529       if (rsEc.next()) {
530         if (rsEc.getInt("ec1") != 0)
531           ec1 = rsEc.getInt("ec1");
532         else
533           return result;
534         if (rsEc.getInt("ec2") != 0)
535           ec2 = rsEc.getInt("ec2");
536         else
537           return result;
538         if (rsEc.getInt("ec3") != 0)
539           ec3 = rsEc.getInt("ec3");
540         else
541           return result;
542       } else
543         return result;
544 
545       if (findStatement != null) findStatement.close();
546 
547       // Core information.
548       findStatement = con.prepareStatement(findByIdStatement());
549       findStatement.setLong(1, id.longValue());
550       findStatement.setInt(2, ec1);
551       findStatement.setInt(3, ec1);
552       findStatement.setInt(4, ec2);
553       findStatement.setInt(5, ec1);
554       findStatement.setInt(6, ec2);
555       findStatement.setInt(7, ec3);
556       rs = findStatement.executeQuery();
557       if (rs.next()) {
558     	  if (ghost){
559     		  result = doLoadGhost(rs);
560     	  } else {
561     	      result = doLoad(rs);
562     	      findEnzymeData(result, con);
563     	  }
564       }
565     } finally {
566     	if (rsEc != null) rsEc.close();
567     	if (rs != null) rs.close();
568     	if (findStatement != null) findStatement.close();
569     }
570 
571     return result;
572   }
573   
574   /**
575    * Loads a complete version of the enzyme with the given ID.
576    * @param id
577    * @param con
578    * @return an <code>EnzymeEntry</code> instance
579    * 	or <code>null</code> if nothing has been found.
580    * @throws SQLException
581    * @throws DomainException
582  * @throws MapperException in case of problem retrieving reaction/cofactor info.
583    */
584   public EnzymeEntry findById(long id, Connection con)
585   throws SQLException, DomainException, MapperException{
586 	  return findById(id, con, false);
587   }
588 
589   /**
590    * Loads a ghost version of the enzyme with the given ID.
591    *
592    * @param id  The enzyme ID.
593    * @param con The logical connection.
594    * @return an <code>EnzymeEntry</code> instance or <code>null</code> if nothing has been found.
595    * @throws SQLException
596    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException
597    */
598   public EnzymeEntry findGhostById(int id, Connection con)
599   throws SQLException, DomainException {
600 	  EnzymeEntry entry = null;
601 	  try {
602 		entry = findById((long) id, con, true);
603 	} catch (MapperException e) {
604 		// Never thrown for ghosts
605 	}
606 	return entry;
607   }
608 
609   public Long findIDInMappingTable(String ec, Status status, Connection con)
610   throws SQLException {
611     if (con == null || ec == null) throw new NullPointerException();
612     PreparedStatement findStatement = null;
613     ResultSet rs = null;
614     try {
615       // Get id
616       findStatement = con.prepareStatement(findIDInMappingTable());
617       findStatement.setString(1, ec);
618       findStatement.setString(2, status.getCode());
619       rs = findStatement.executeQuery();
620       if (rs.next()) {
621         return new Long(rs.getLong(1));
622       }
623       return new Long(-1);
624     } finally {
625     	if (rs != null) rs.close();
626       if (findStatement != null) findStatement.close();
627     }
628   }
629 
630   /**
631    * Tries to find all public entries but loads the entries only with minimum
632    * information.
633    * @param con The logical connection.
634    * @return a <code>List</code> of <code>EnzymeEntry</code> instances
635    * 		or <code>null</code> if nothing has been found.
636    * @throws SQLException
637    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
638    */
639   public List<EnzymeEntry> findAll(Connection con) throws SQLException, DomainException {
640     Statement findListStatement = null;
641     ResultSet rs = null;
642     List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
643     boolean noResult = true;
644 
645     try {
646       findListStatement = con.createStatement();
647       rs = findListStatement.executeQuery(findAllStatement());
648 
649       while (rs.next()) {
650         noResult = false;
651         EnzymeEntry enzymeEntry = doLoadGhost(rs);
652 
653         // Common name
654         EnzymeNameMapper nameMapper = new EnzymeNameMapper();
655         List<EnzymeName> commonNames = nameMapper.findCommonNames(enzymeEntry.getId(), con);
656         if (commonNames != null) enzymeEntry.setCommonNames(commonNames);
657         result.add(enzymeEntry);
658       }
659     } finally {
660     	if (rs != null) rs.close();
661       if (findListStatement != null) findListStatement.close();
662     }
663 
664     if (noResult) return null;
665     return result;
666   }
667 
668   /**
669    * Tries to find all entries requested but loads the entries only with
670    * minimum information.
671    * @param ec1 Number of class to search for.
672    * @param ec2 Number of subclass to search for.
673    * @param ec3 Number of sub-subclass to search for.
674    * @param con The logical connection.
675    * @return a <code>List</code> of <code>EnzymeEntry</code> instances or <code>null</code>
676    * if nothing has been found.
677    * @throws java.sql.SQLException
678    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
679    */
680   public List<EnzymeEntry> findList(String ec1, String ec2, String ec3,
681 		  Connection con)
682   throws SQLException, DomainException {
683     PreparedStatement findListStatement = null;
684     ResultSet rs = null;
685     List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
686     boolean noResult = true;
687 
688     try {
689       findListStatement = con.prepareStatement(findListStatement());
690       findListStatement.setString(1, ec1);
691       findListStatement.setString(2, ec2);
692       findListStatement.setString(3, ec3);
693       rs = findListStatement.executeQuery();
694 
695       while (rs.next()) {
696         noResult = false;
697         EnzymeEntry enzymeEntry = doLoadGhost(rs);
698 
699         // History information
700         loadHistoryGraph(enzymeEntry, con);
701 
702         // Common name
703         EnzymeNameMapper nameMapper = new EnzymeNameMapper();
704         List<EnzymeName> commonNames = nameMapper.findCommonNames(enzymeEntry.getId(), con);
705         if (commonNames != null) enzymeEntry.setCommonNames(commonNames);
706         result.add(enzymeEntry);
707       }
708     } finally {
709     	if (rs != null) rs.close();
710       if (findListStatement != null) findListStatement.close();
711     }
712 
713     if (noResult) return null;
714     return result;
715   }
716 
717   /**
718    * Retrieves the list of enzymes with a given status.
719    * @param con
720    * @param status
721    * @return the list of enzymes with the given status.
722    * @throws SQLException
723    * @throws DomainException
724    */
725   public List<EnzymeEntry> findByStatus(Connection con, Status status)
726   throws SQLException, DomainException{
727 	    PreparedStatement findListStatement = null;
728 	    ResultSet rs = null;
729 	    List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
730 	    try {
731 	      findListStatement = con.prepareStatement(findCoreListStatement());
732 	      findListStatement.setString(1, status.getCode());
733 	      rs = findListStatement.executeQuery();
734 
735 	      while (rs.next()) {
736 	        EnzymeEntry enzymeEntry = doLoadGhost(rs);
737 	        // History information
738 	        loadHistoryGraph(enzymeEntry, con);
739 	        // Common name
740 	        EnzymeNameMapper nameMapper = new EnzymeNameMapper();
741 	        List<EnzymeName> commonNames = nameMapper.findCommonNames(enzymeEntry.getId(), con);
742 	        if (commonNames != null) enzymeEntry.setCommonNames(commonNames);
743 	        result.add(enzymeEntry);
744 	      }
745 	    } finally {
746 	    	if (rs != null) rs.close();
747 	        if (findListStatement != null) findListStatement.close();
748 	    }
749 	    return result;
750   }
751   
752   /**
753    * Tries to find all proposed entries but loads the entries only with
754    * minimum information.
755    * @param con The logical connection.
756    * @return a {@link java.util.List} of
757    * 	{@link uk.ac.ebi.intenz.domain.enzyme.EnzymeEntry} instances.
758    * @throws SQLException    if a database error occurs.
759    * @throws DomainException if a domain related error occurs.
760    */
761   public List<EnzymeEntry> findProposedList(Connection con)
762   throws SQLException, DomainException {
763     return findByStatus(con, Status.PROPOSED);
764   }
765 
766   /**
767    * Tries to find all suggested entries but loads the entries only with
768    * minimum information.
769    * @param con The logical connection.
770    * @return a <code>Vector</code> of <code>EnzymeEntry</code> instances
771    * 	or <code>null</code> if nothing has been found.
772    * @throws SQLException
773    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
774    */
775   public List<EnzymeEntry> findSuggestedList(Connection con)
776   throws SQLException, DomainException {
777     return findByStatus(con, Status.SUGGESTED);
778   }
779 
780   /**
781    * Gets the list of preliminary EC numbers issued by UniProt.
782    * @param con
783    * @return A list of preliminary EC numbers issued by UniProt.
784    * @throws SQLException
785    * @throws DomainException
786    */
787   public List<EnzymeEntry> findPreliminaryEcsList(Connection con)
788   throws SQLException, DomainException{
789       return findByStatus(con, Status.PRELIMINARY);
790   }
791   
792   /**
793    * Exports every piece of data for publicly available entries.
794    * @param con
795    * @return A <code>List</code> of <code>EnzymeEntry</code>s
796    * @throws SQLException 
797    * @throws DomainException 
798  * @throws MapperException in case of problem retrieving reaction/cofactor info.
799    */
800   public List<EnzymeEntry> exportAllEntries(Connection con)
801   throws SQLException, DomainException, MapperException{
802 	  List<EnzymeEntry> entries = new ArrayList<EnzymeEntry>();
803 	  Statement findListStatement = null;
804 	    ResultSet rs = null;
805 	  try {
806 		  findListStatement = con.createStatement();
807 		  rs = findListStatement.executeQuery(findAllStatement());
808 		  while (rs.next()){
809 			  EnzymeEntry entry = doLoadCore(rs);
810 			  findEnzymeData(entry, con);
811 			  entries.add(entry);
812 		  }
813 	  } finally {
814 	    	if (rs != null) rs.close();
815 	        if (findListStatement != null) findListStatement.close();
816 	  }
817 	  return entries.isEmpty()? null : entries;
818   }
819 
820   /**
821    * Tries to export all approved entries containing only ENZYME relevant information.
822    * <p/>
823    * Affected table rows will be locked.
824    *
825    * @param con The logical connection.
826    * @return an <code>ArrayList</code> of <code>EnzymeEntry</code> instances or <code>null</code>
827    * 	if nothing has been found.
828    * @throws SQLException
829    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
830  * @throws MapperException in case of problem retrieving reaction/cofactor info.
831    */
832   public List<EnzymeEntry> exportApprovedSibEntries(Connection con) 
833   throws SQLException, DomainException, MapperException {
834     Statement findListStatement = null;
835     ResultSet rs = null;
836     List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
837     try {
838       findListStatement = con.createStatement();
839       rs = findListStatement.executeQuery(findAllStatement());
840       while (rs.next()) {
841         EnzymeEntry enzymeEntry = doLoadCore(rs);
842         exportSIBEnzymeData(enzymeEntry, con);
843         result.add(enzymeEntry);
844       }
845     } finally {
846     	if (rs != null) rs.close();
847         if (findListStatement != null) findListStatement.close();
848     }
849     return result.isEmpty()? null: result;
850   }
851 
852   /**
853    * Tries to find all proposed entries with all information.
854    * <p/>
855    * This feature is just needed to export a list of all proposed entries.
856    *
857    * @param con The logical connection.
858    * @return a <code>Vector</code> of <code>EnzymeEntry</code> instances or <code>null</code>
859    * 	if nothing has been found.
860    * @throws SQLException
861    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
862  * @throws MapperException in case of problem retrieving reaction/cofactor info.
863    */
864   public List<EnzymeEntry> findFullProposedList(Connection con)
865   throws SQLException, DomainException, MapperException {
866     PreparedStatement findListStatement = null;
867     ResultSet rs = null;
868     List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
869     boolean noResult = true;
870 
871     try {
872       findListStatement = con.prepareStatement(findCoreListStatement());
873       findListStatement.setString(1, "PR");
874       rs = findListStatement.executeQuery();
875 
876       while (rs.next()) {
877         noResult = false;
878         EnzymeEntry enzymeEntry = doLoadCore(rs);
879         findEnzymeData(enzymeEntry, con);
880         result.add(enzymeEntry);
881       }
882     } finally {
883     	if (rs != null) rs.close();
884         if (findListStatement != null) findListStatement.close();
885     }
886 
887     if (noResult) return null;
888     return result;
889   }
890 
891   /**
892    * Tries to find all suggested entries with all information.
893    * <p/>
894    * This feature is just needed to export a list of all suggested entries.
895    *
896    * @param con The logical connection.
897    * @return a <code>Vector</code> of <code>EnzymeEntry</code> instances or <code>null</code>
898    * 	if nothing has been found.
899    * @throws SQLException
900    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
901  * @throws MapperException in case of problem retrieving reaction/cofactor info.
902    */
903   public List<EnzymeEntry> findFullSuggestedList(Connection con)
904   throws SQLException, DomainException, MapperException {
905     PreparedStatement findListStatement = null;
906     ResultSet rs = null;
907     List<EnzymeEntry> result = new ArrayList<EnzymeEntry>();
908     boolean noResult = true;
909 
910     try {
911       findListStatement = con.prepareStatement(findCoreListStatement());
912       findListStatement.setString(1, "SU");
913       rs = findListStatement.executeQuery();
914 
915       while (rs.next()) {
916         noResult = false;
917         EnzymeEntry enzymeEntry = doLoadCore(rs);
918         findEnzymeData(enzymeEntry, con);
919         result.add(enzymeEntry);
920       }
921     } finally {
922     	if (rs != null) rs.close();
923         if (findListStatement != null) findListStatement.close();
924     }
925 
926     if (noResult) return null;
927     return result;
928   }
929 
930   /**
931    * Returns the EC number of the given enzyme ID.
932    *
933    * @param id  The enzyme ID.
934    * @param con The logical connection.
935    * @return The EC, if found, otherwise <code>null</code>.
936    * @throws SQLException
937    * @throws uk.ac.ebi.intenz.domain.exceptions.DomainException 
938    */
939   public EnzymeCommissionNumber findEC(String id, Connection con)
940   throws SQLException, DomainException {
941     int ec1 = 0, ec2 = 0, ec3 = 0, ec4 = 0;
942     Status status = null;
943     PreparedStatement findEcStatement = null;
944     ResultSet rs = null;
945     try {
946       findEcStatement = con.prepareStatement(findEcStatement());
947       findEcStatement.setString(1, id);
948       rs = findEcStatement.executeQuery();
949 
950       while (rs.next()) {
951         ec1 = rs.getInt("ec1");
952         ec2 = rs.getInt("ec2");
953         ec3 = rs.getInt("ec3");
954         ec4 = rs.getInt("ec4");
955         status = Status.fromCode(rs.getString("status"));
956       }
957     } finally {
958     	if (rs != null) rs.close();
959       if (findEcStatement != null) findEcStatement.close();
960     }
961 
962     return EnzymeCommissionNumber.valueOf(
963     		ec1, ec2, ec3, ec4, status.equals(Status.PRELIMINARY));
964   }
965 
966   /**
967    * Checks whether a given EC number already exists in the database.
968    *
969    * @param ec  The EC number to be checked.
970    * @param con The database connection.
971    * @return <code>true</code> if the given EC number exists in the database.
972    * @throws SQLException         if a database error occurs.
973    * @throws NullPointerException if any of the parameters is <code>null</code>
974    */
975   public static boolean ecExists(EnzymeCommissionNumber ec, Connection con)
976   throws SQLException {
977     if (ec == null) throw new NullPointerException("Parameter 'ec' must not be null.");
978     if (con == null) throw new NullPointerException("Parameter 'con' must not be null.");
979 
980     PreparedStatement findStatement = null;
981     ResultSet rs = null;
982     try {
983       switch (ec.getType()) {
984         case CLASS:
985           findStatement = con.prepareStatement("SELECT ec1 FROM classes WHERE ec1 = ?");
986           findStatement.setInt(1, ec.getEc1());
987           break;
988         case SUBCLASS:
989           findStatement = con.prepareStatement("SELECT ec1 FROM subclasses" +
990           		" WHERE ec1 = ? AND ec2 = ?");
991           findStatement.setInt(1, ec.getEc1());
992           findStatement.setInt(2, ec.getEc2());
993           break;
994         case SUBSUBCLASS:
995           findStatement = con.prepareStatement("SELECT ec1 FROM subsubclasses" +
996           		" WHERE ec1 = ? AND ec2 = ? AND ec3 = ?");
997           findStatement.setInt(1, ec.getEc1());
998           findStatement.setInt(2, ec.getEc2());
999           findStatement.setInt(3, ec.getEc3());
1000           break;
1001         case ENZYME:
1002           findStatement = con.prepareStatement("SELECT ec1 FROM enzymes " +
1003           		"WHERE ec1 = ? AND ec2 = ? AND ec3 = ? AND ec4 = ? AND status != 'PM'");
1004           findStatement.setInt(1, ec.getEc1());
1005           findStatement.setInt(2, ec.getEc2());
1006           findStatement.setInt(3, ec.getEc3());
1007           findStatement.setInt(4, ec.getEc4());
1008           break;
1009         case PRELIMINARY:
1010             findStatement = con.prepareStatement("SELECT ec1 FROM enzymes " +
1011       			"WHERE ec1 = ? AND ec2 = ? AND ec3 = ? AND ec4 = ? AND status = 'PM'");
1012 		      findStatement.setInt(1, ec.getEc1());
1013 		      findStatement.setInt(2, ec.getEc2());
1014 		      findStatement.setInt(3, ec.getEc3());
1015 		      findStatement.setInt(4, ec.getEc4());
1016           break;
1017       }
1018       rs = findStatement.executeQuery();
1019       return rs.next();
1020     } finally {
1021     	if (rs != null) rs.close();
1022       if (findStatement != null) findStatement.close();
1023     }
1024   }
1025 
1026   /**
1027    * Returns the history line of the enzyme with the given ID.
1028    *
1029    * @param id  The enzyme ID.
1030    * @param con The logical connection.
1031    * @return The history line, if found, otherwise <code>null</code>.
1032    * @throws SQLException
1033    */
1034   public String findHistoryLine(Long id, Connection con) throws SQLException {
1035     String historyLine = "";
1036     PreparedStatement findHistoryLineStatement = null;
1037     ResultSet rs = null;
1038     try {
1039       findHistoryLineStatement = con.prepareStatement(findHistoryLineStatement());
1040       findHistoryLineStatement.setLong(1, id.longValue());
1041       rs = findHistoryLineStatement.executeQuery();
1042 
1043       while (rs.next()) {
1044         if (rs.getString("history") != null) historyLine = rs.getString("history");
1045       }
1046     } finally {
1047     	if (rs != null) rs.close();
1048       if (findHistoryLineStatement != null) findHistoryLineStatement.close();
1049     }
1050 
1051     return historyLine;
1052   }
1053 
1054   /**
1055    * Returns the note of the enzyme with the given ID.
1056    *
1057    * @param id  The enzyme ID.
1058    * @param con The logical connection.
1059    * @return The note, if found, otherwise <code>null</code>.
1060    * @throws SQLException
1061    */
1062   public String findNote(Long id, Connection con) throws SQLException {
1063     String note = "";
1064     PreparedStatement findNoteStatement = null;
1065     ResultSet rs = null;
1066 
1067     try {
1068       findNoteStatement = con.prepareStatement(findNoteStatement());
1069       findNoteStatement.setLong(1, id.longValue());
1070       rs = findNoteStatement.executeQuery();
1071 
1072       while (rs.next()) {
1073         if (rs.getString("note") != null) note = rs.getString("note");
1074       }
1075     } finally {
1076     	if (rs != null) rs.close();
1077         if (findNoteStatement != null) findNoteStatement.close();
1078     }
1079 
1080     return note;
1081   }
1082 
1083   /**
1084    * @param con The logical connection.
1085    * @return
1086    * @throws SQLException
1087    */
1088   public EnzymeStatistics findStats(Connection con) throws SQLException {
1089     PreparedStatement countIUBMBEnzymes = null, countClasses = null,
1090     	countSubclasses = null, countSubSubclasses = null;
1091     EnzymeStatistics result = new EnzymeStatistics();
1092     ResultSet rs = null;
1093 
1094     int allEnzymes = 0;
1095     int approved = 0;
1096     int proposed = 0;
1097     int suggested = 0;
1098     int approvedDeleted = 0;
1099     int proposedDeleted = 0;
1100     int suggestedDeleted = 0;
1101 
1102     try {
1103       countIUBMBEnzymes = con.prepareStatement(countIUBMBEnzymesStatement());
1104       rs = countIUBMBEnzymes.executeQuery();
1105       while (rs.next()) {
1106         allEnzymes++;
1107         if (rs.getString(1).equals("OK")) {
1108           approved++;
1109           if (rs.getString(2).equals("N")) approvedDeleted++;
1110         }
1111         if (rs.getString(1).equals("PR")) {
1112           proposed++;
1113           if (rs.getString(2).equals("N")) proposedDeleted++;
1114         }
1115         if (rs.getString(1).equals("SU")) {
1116           suggested++;
1117           if (rs.getString(2).equals("N")) suggestedDeleted++;
1118         }
1119       }
1120 
1121       result.setAllEnzymes(allEnzymes);
1122       result.setApprovedEnzymes(approved);
1123       result.setApprovedDeletedEnzymes(approvedDeleted);
1124       result.setProposedEnzymes(proposed);
1125       result.setProposedDeletedEnzymes(proposedDeleted);
1126       result.setSuggestedEnzymes(suggested);
1127       result.setSuggestedDeletedEnzymes(suggestedDeleted);
1128 
1129       countClasses = con.prepareStatement(countClassesStatement());
1130       rs = countClasses.executeQuery();
1131       if (rs.next()) {
1132         result.setClasses(rs.getInt(1));
1133       }
1134 
1135       countSubclasses = con.prepareStatement(countSubclassesStatement());
1136       rs = countSubclasses.executeQuery();
1137       if (rs.next()) {
1138         result.setSubclasses(rs.getInt(1));
1139       }
1140 
1141       countSubSubclasses = con.prepareStatement(countSubSubclassesStatement());
1142       rs = countSubSubclasses.executeQuery();
1143       if (rs.next()) {
1144         result.setSubsubclasses(rs.getInt(1));
1145       }
1146 
1147     } finally {
1148     	if (rs != null) rs.close();
1149       countIUBMBEnzymes.close();
1150       countClasses.close();
1151       countSubclasses.close();
1152       countSubSubclasses.close();
1153     }
1154 
1155     return result;
1156   }
1157 
1158 
1159   /**
1160    * Inserts the core data of a new enzyme entry.
1161    * <p/>
1162    * A new enzyme ID must already be available. It can be obtained via
1163    * {@link EnzymeEntryMapper#findNextEnzymeId(java.sql.Connection)}
1164    *
1165    * @param enzymeId 
1166    * @param ec 
1167    * @param status 
1168    * @param source
1169    * @param isActive
1170    * @param note 
1171    * @param historyLine 
1172    * @param con
1173    * @throws java.sql.SQLException 
1174    */
1175   public void insert(Long enzymeId, EnzymeCommissionNumber ec, Status status,
1176                      EnzymeSourceConstant source, String note, String historyLine,
1177                      boolean isActive, Connection con)
1178           throws SQLException {
1179     PreparedStatement insertStatement = null;
1180     try {
1181       insertStatement = con.prepareStatement(insertStatement());
1182       insertStatement.setLong(1, enzymeId.longValue());
1183       insertStatement.setInt(2, ec.getEc1());
1184       insertStatement.setInt(3, ec.getEc2());
1185       insertStatement.setInt(4, ec.getEc3());
1186       insertStatement.setInt(5, ec.getEc4());
1187       insertStatement.setString(6, historyLine);
1188       insertStatement.setString(7, note);
1189       insertStatement.setString(8, status.getCode());
1190       insertStatement.setString(9, source.toString());
1191       insertStatement.setString(10, isActive ? "Y" : "N");
1192       insertStatement.execute();
1193     } finally {
1194       insertStatement.close();
1195     }
1196   }
1197 
1198 
1199   // ----------------- UPDATE PROCEDURES ----------------------
1200 
1201   /**
1202    * Updates the core data of an enzyme stored in the table <b><code>ENZYMES</code></b>.
1203    * <p/>
1204    * <b>NOTE:</b> The <b><code>ACTIVE</code></b> column of this table is cannot be updated
1205    * using this method since this column
1206    * is managed by the event package.
1207    *
1208    * @param enzymeId    The enzyme's ID.
1209    * @param ec          The EC number.
1210    * @param status      The current status of the enzyme.
1211    * @param source      The source of this enzyme.
1212    * @param note        A note the curator might have added.
1213    * @param historyLine The enzyme's history line.
1214    * @param active The enzyme's active status.
1215    * @param con         A database connection.
1216    * @throws java.sql.SQLException 
1217    */
1218   public void update(Long enzymeId, EnzymeCommissionNumber ec, Status status,
1219 		  EnzymeSourceConstant source, String note, String historyLine,
1220 		  boolean active, Connection con)
1221   throws SQLException {
1222     if (enzymeId == null) throw new NullPointerException("Parameter 'enzymeId' must not be null.");
1223     if (ec == null) throw new NullPointerException("Parameter 'ec' must not be null.");
1224     if (status == null) throw new NullPointerException("Parameter 'status' must not be null.");
1225     if (source == null) throw new NullPointerException("Parameter 'source' must not be null.");
1226     if (note == null) throw new NullPointerException("Parameter 'note' must not be null.");
1227     if (historyLine == null) throw new NullPointerException("Parameter 'historyLine' must not be null.");
1228     if (con == null) throw new NullPointerException("Parameter 'con' must not be null.");
1229 
1230     PreparedStatement updateStatement = null;
1231     try {
1232       updateStatement = con.prepareStatement(updateStatement());
1233       updateStatement.setInt(1, ec.getEc1());
1234       updateStatement.setInt(2, ec.getEc2());
1235       updateStatement.setInt(3, ec.getEc3());
1236       updateStatement.setInt(4, ec.getEc4());
1237       updateStatement.setString(5, status.getCode());
1238       updateStatement.setString(6, source.toString());
1239       updateStatement.setString(7, note);
1240       updateStatement.setString(8, historyLine);
1241       updateStatement.setString(9, active? "Y" : "N");
1242       updateStatement.setLong(10, enzymeId.longValue());
1243       updateStatement.execute();
1244     } finally {
1245       updateStatement.close();
1246     }
1247   }
1248 
1249   /**
1250    * Updates the EC of the given enzyme.
1251    *
1252    * @param enzymeId The enzyme's ID.
1253    * @param newEc    The new EC.
1254    * @param con      The logical connection.
1255    * @throws SQLException
1256    */
1257   public void updateEc(Long enzymeId, EnzymeCommissionNumber newEc, Connection con) throws SQLException {
1258     PreparedStatement updateEcStatement = null;
1259 
1260     try {
1261       updateEcStatement = con.prepareStatement(updateEcStatement());
1262       updateEcStatement.setInt(1, newEc.getEc1());
1263       updateEcStatement.setInt(2, newEc.getEc2());
1264       updateEcStatement.setInt(3, newEc.getEc3());
1265       updateEcStatement.setInt(4, newEc.getEc4());
1266       updateEcStatement.setLong(5, enzymeId.longValue());
1267       updateEcStatement.execute();
1268     } finally {
1269       updateEcStatement.close();
1270     }
1271   }
1272 
1273   /**
1274    * Updates the notes of the given enzyme.
1275    *
1276    * @param enzymeId The enzyme's ID.
1277    * @param note     The note.
1278    * @param con      The logical connection.
1279    * @throws SQLException
1280    */
1281   public void updateNote(Long enzymeId, String note, Connection con) throws SQLException {
1282     PreparedStatement updateNoteStatement = null;
1283 
1284     try {
1285       updateNoteStatement = con.prepareStatement(updateNoteStatement());
1286       updateNoteStatement.setString(1, note);
1287       updateNoteStatement.setLong(2, enzymeId.longValue());
1288       updateNoteStatement.execute();
1289     } finally {
1290       updateNoteStatement.close();
1291     }
1292   }
1293 
1294   /**
1295    * Updates the status of the given enzyme.
1296    *
1297    * @param enzymeId The enzyme's ID.
1298    * @param status   The status.
1299    * @param con      The logical connection.
1300    * @throws SQLException
1301    */
1302   public void updateStatus(Long enzymeId, Status status, Connection con) throws SQLException {
1303     PreparedStatement updateStatusStatement = null;
1304 
1305     try {
1306       updateStatusStatement = con.prepareStatement(updateStatusStatement());
1307       updateStatusStatement.setString(1, status.getCode());
1308       updateStatusStatement.setLong(2, enzymeId.longValue());
1309       updateStatusStatement.execute();
1310     } finally {
1311       updateStatusStatement.close();
1312     }
1313   }
1314 
1315   /**
1316    * Updates the history line of the given enzyme.
1317    *
1318    * @param enzymeId    The enzyme's ID.
1319    * @param historyLine The new history line.
1320    * @param con         The logical connection.
1321    * @throws SQLException
1322    */
1323   public void updateHistoryLine(Long enzymeId, String historyLine, Connection con) throws SQLException {
1324     PreparedStatement updateHistoryLineStatement = null;
1325 
1326     try {
1327       updateHistoryLineStatement = con.prepareStatement(updateHistoryLineStatement());
1328       updateHistoryLineStatement.setString(1, historyLine);
1329       updateHistoryLineStatement.setLong(2, enzymeId.longValue());
1330       updateHistoryLineStatement.execute();
1331     } finally {
1332       updateHistoryLineStatement.close();
1333     }
1334   }
1335 
1336   /**
1337    * Checks if a clone of an enzyme already exists.
1338    *
1339    * @param id  The enzyme ID.
1340    * @param con The logical connection.
1341    * @return <code>true</code>, if a clone already exists.
1342    * @throws SQLException
1343    */
1344   public boolean cloneExists(Long id, Connection con) throws SQLException {
1345     EnzymeFutureMapper futureEventsMapper = new EnzymeFutureMapper();
1346     return futureEventsMapper.futureEventExists(id, con);
1347   }
1348 
1349   // ------------------- PRIVATE METHODS ------------------------
1350 
1351   /**
1352    * Creates the <code>EnzymeEntry</code> object from the given result set.
1353    * <p/>
1354    * This object includes information about its corresponding class, subclass and sub-subclass.
1355    *
1356    * @param rs The result set object.
1357    * @return a <code>EnzymeEntry</code> instance containing the core data of an enzyme.
1358    * @throws java.sql.SQLException if a database error occurs.
1359    */
1360   private EnzymeEntry doLoad(ResultSet rs) throws SQLException, EcException {
1361     Long enzymeId = null;
1362     int ec1 = 0;
1363     int ec2 = 0;
1364     int ec3 = 0;
1365     int ec4 = 0;
1366     String history = "";
1367     String note = "";
1368     String status = "";
1369     String source = "";
1370     String active = "";
1371     String className = "";
1372     String subclassName = "";
1373     String subSubclassName = "";
1374 
1375     if (rs.getInt(1) > 0) enzymeId = new Long(rs.getLong(1));
1376     if (rs.getInt(2) > 0) ec1 = rs.getInt(2);
1377     if (rs.getInt(3) > 0) ec2 = rs.getInt(3);
1378     if (rs.getInt(4) > 0) ec3 = rs.getInt(4);
1379     if (rs.getInt(5) > 0) ec4 = rs.getInt(5);
1380     if (rs.getString(6) != null) history = rs.getString(6);
1381     if (rs.getString(7) != null) note = rs.getString(7);
1382     if (rs.getString(8) != null) status = rs.getString(8);
1383     if (rs.getString(9) != null) source = rs.getString(9);
1384     if (rs.getString(10) != null) active = rs.getString(10);
1385     if (rs.getString(11) != null) className = rs.getString(11);
1386     if (rs.getString(12) != null) subclassName = rs.getString(12);
1387     if (rs.getString(13) != null) subSubclassName = rs.getString(13);
1388 
1389     EnzymeEntry enzymeEntry = new EnzymeEntry();
1390     Status st = Status.fromCode(status);
1391     enzymeEntry.setId(enzymeId);
1392     EnzymeCommissionNumber ec = EnzymeCommissionNumber.valueOf(
1393     		ec1, ec2, ec3, ec4, st.equals(Status.PRELIMINARY));
1394     enzymeEntry.setEc(ec);
1395     enzymeEntry.getHistory().getRootNode().setHistoryLine(history);
1396     enzymeEntry.setNote(note);
1397 	enzymeEntry.setStatus(st);
1398     enzymeEntry.setSource(EnzymeSourceConstant.valueOf(source));
1399     enzymeEntry.setActive(active.charAt(0) == 'Y');
1400     enzymeEntry.setSubSubclassName(subSubclassName);
1401     enzymeEntry.setSubclassName(subclassName);
1402     enzymeEntry.setClassName(className);
1403 
1404     return enzymeEntry;
1405   }
1406 
1407 
1408   /**
1409    * Creates the <code>EnzymeEntry</code> object from the given result set.
1410    *
1411    * @param rs The result set object.
1412    * @return an <code>EnzymeEntry</code> instance.
1413    * @throws java.sql.SQLException
1414    */
1415   private EnzymeEntry doLoadCore(ResultSet rs) throws SQLException, DomainException {
1416     Long enzymeId = null;
1417     int ec1 = 0;
1418     int ec2 = 0;
1419     int ec3 = 0;
1420     int ec4 = 0;
1421     String history = "";
1422     String note = "";
1423     String status = "";
1424     String source = "";
1425     String active = "";
1426 
1427     if (rs.getInt(1) > 0) enzymeId = new Long(rs.getLong(1));
1428     if (rs.getInt(2) > 0) ec1 = rs.getInt(2);
1429     if (rs.getInt(3) > 0) ec2 = rs.getInt(3);
1430     if (rs.getInt(4) > 0) ec3 = rs.getInt(4);
1431     if (rs.getInt(5) > 0) ec4 = rs.getInt(5);
1432     if (rs.getString(6) != null) history = rs.getString(6);
1433     if (rs.getString(7) != null) note = rs.getString(7);
1434     if (rs.getString(8) != null) status = rs.getString(8);
1435     if (rs.getString(9) != null) source = rs.getString(9);
1436     if (rs.getString(10) != null) active = rs.getString(10);
1437 
1438     EnzymeEntry enzymeEntry = new EnzymeEntry();
1439     enzymeEntry.setId(enzymeId);
1440     Status st = Status.fromCode(status);
1441     EnzymeCommissionNumber ec = EnzymeCommissionNumber.valueOf(
1442     		ec1, ec2, ec3, ec4, st.equals(Status.PRELIMINARY));
1443     enzymeEntry.setEc(ec);
1444     enzymeEntry.getHistory().getRootNode().setHistoryLine(history);
1445     enzymeEntry.setNote(note);
1446 	enzymeEntry.setStatus(st);
1447     enzymeEntry.setSource(EnzymeSourceConstant.valueOf(source));
1448     enzymeEntry.setActive(active.charAt(0) == 'Y');
1449 
1450     return enzymeEntry;
1451   }
1452 
1453   /**
1454    * Checks the list of loaded names and stores them in an entry object according to its type
1455    * (common, systematic or other name).
1456    *
1457    * @param entry The entry where the names will be stored.
1458    * @param names The list of names.
1459    */
1460   private void doLoadNames(EnzymeEntry entry, List<EnzymeName> names) throws DomainException {
1461     for (int iii = 0; iii < names.size(); iii++) {
1462       EnzymeName enzymeName = names.get(iii);
1463       if (enzymeName.getType().equals(EnzymeNameTypeConstant.COMMON_NAME)) {
1464         entry.addCommonName(enzymeName);
1465       }
1466       if (enzymeName.getType().equals(EnzymeNameTypeConstant.SYSTEMATIC_NAME)) {
1467         entry.setSystematicName(enzymeName);
1468       }
1469       if (enzymeName.getType().equals(EnzymeNameTypeConstant.OTHER_NAME)) {
1470         entry.addSynonym(enzymeName);
1471       }
1472     }
1473   }
1474 
1475   /**
1476    * Creates the <code>EnzymeEntry</code> ghost (light-weight) object from the given result set.
1477    *
1478    * @param rs The result set object.
1479    * @return an <code>EnzymeEntry</code> instance.
1480    * @throws java.sql.SQLException
1481    */
1482   private EnzymeEntry doLoadGhost(ResultSet rs) throws SQLException, DomainException {
1483     Long enzymeId = null;
1484     int ec1 = 0;
1485     int ec2 = 0;
1486     int ec3 = 0;
1487     int ec4 = 0;
1488     String status = "";
1489     String source = "";
1490     String active = "";
1491     String history = "";
1492 
1493     if (rs.getInt("enzyme_id") > 0) enzymeId = new Long(rs.getLong("enzyme_id"));
1494     if (rs.getInt("ec1") > 0) ec1 = rs.getInt("ec1");
1495     if (rs.getInt("ec2") > 0) ec2 = rs.getInt("ec2");
1496     if (rs.getInt("ec3") > 0) ec3 = rs.getInt("ec3");
1497     if (rs.getInt("ec4") > 0) ec4 = rs.getInt("ec4");
1498     if (rs.getString("history") != null) history = rs.getString("history");
1499     if (rs.getString("status") != null) status = rs.getString("status");
1500     if (rs.getString("source") != null) source = rs.getString("source");
1501     if (rs.getString("active") != null) active = rs.getString("active");
1502 
1503     EnzymeEntry enzymeEntry = new EnzymeEntry();
1504 	Status st = Status.fromCode(status);
1505     enzymeEntry.setId(enzymeId);
1506     EnzymeCommissionNumber ec = EnzymeCommissionNumber.valueOf(
1507     		ec1, ec2, ec3, ec4, st.equals(Status.PRELIMINARY));
1508     enzymeEntry.setEc(ec);
1509     enzymeEntry.getHistory().getRootNode().setHistoryLine(history);
1510 	enzymeEntry.setStatus(st);
1511     if (!source.equals("")) enzymeEntry.setSource(EnzymeSourceConstant.valueOf(source));
1512     if (!active.equals("")) enzymeEntry.setActive(active.charAt(0) == 'Y');
1513     enzymeEntry.setGhost(true);
1514     return enzymeEntry;
1515   }
1516 
1517   /**
1518    * Loads the enzyme data except for the core information (see <code>findBy</code> methods).
1519    *
1520    * @param enzymeEntry The <code>EnzymeEntry</code> instance to be populated.
1521    * @param con         The logical connection.
1522    * @throws SQLException if a generic database error occurs.
1523    * @throws MapperException in case of problem retrieving reaction/cofactor data.
1524    */
1525   private void findEnzymeData(EnzymeEntry enzymeEntry, Connection con)
1526   throws SQLException, DomainException, EnzymeReactionException,
1527   		EnzymeReferenceException, MapperException {
1528     loadHistoryGraph(enzymeEntry, con);
1529 
1530     // Names
1531     EnzymeNameMapper nameMapper = new EnzymeNameMapper();
1532     List<EnzymeName> names = nameMapper.find(enzymeEntry.getId(), con);
1533     if (names != null) doLoadNames(enzymeEntry, names);
1534 
1535     // Reaction
1536     EnzymaticReactions reactions =
1537     		enzymeReactionMapper.find(enzymeEntry.getId(), con);
1538     if (reactions != null) enzymeEntry.setEnzymaticReactions(reactions);
1539 
1540     // Cofactors
1541     EnzymeCofactorMapper cofactorMapper = new EnzymeCofactorMapper();
1542     Set<Object> cofactors = cofactorMapper.find(enzymeEntry.getId(), con);
1543     cofactorMapper.close();
1544     if (cofactors != null) enzymeEntry.setCofactors(cofactors);
1545 
1546     // Links
1547     EnzymeLinkMapper linkMapper = new EnzymeLinkMapper();
1548     List<EnzymeLink> links = linkMapper.find(enzymeEntry.getId(), con);
1549     if (links != null) {
1550 		enzymeEntry.setLinks(new TreeSet<EnzymeLink>(links));
1551 	}
1552 
1553     // Comments
1554     EnzymeCommentMapper commentMapper = new EnzymeCommentMapper();
1555     List<EnzymeComment> comments = commentMapper.find(enzymeEntry.getId(), con);
1556     if (comments != null) enzymeEntry.setComments(comments);
1557 
1558     // References
1559     EnzymeReferenceMapper referenceMapper = new EnzymeReferenceMapper();
1560     List<Reference> references = referenceMapper.find(enzymeEntry.getId(), con);
1561     if (references != null) enzymeEntry.setReferences(references);
1562   }
1563 
1564   /**
1565    * Exports all data available for this ENZYME entry.
1566    * <p/>
1567    * Affected table rows will be locked.
1568    *
1569    * @param enzymeEntry The instance storing the data to be gathered.
1570    * @param con         The logical connection.
1571    * @throws SQLException            if a database error occurs.
1572    * @throws DomainException         if a domain related error occurs.
1573    * @throws EnzymeReactionException if no reaction has been found; one reaction is mandatory.
1574  * @throws MapperException in case of a problem getting reaction/cofactor info.
1575    */
1576   private void exportSIBEnzymeData(EnzymeEntry enzymeEntry, Connection con)
1577   throws SQLException, DomainException,
1578           EnzymeReactionException, MapperException {
1579     assert enzymeEntry != null : "Parameter 'enzymeEntry' must not be null.";
1580     assert con != null : "Parameter 'con' must not be null.";
1581 
1582     loadHistoryGraph(enzymeEntry, con);
1583     
1584     // Names
1585     EnzymeNameMapper nameMapper = new EnzymeNameMapper();
1586     List<EnzymeName> names = nameMapper.exportSibNames(enzymeEntry.getId(), con);
1587     if (names != null) doLoadNames(enzymeEntry, names);
1588 
1589     // Reaction
1590     EnzymaticReactions reactions =
1591     		enzymeReactionMapper.exportSibReactions(enzymeEntry.getId(), con);
1592     if (reactions != null) enzymeEntry.setEnzymaticReactions(reactions);
1593 
1594     // Cofactors
1595     EnzymeCofactorMapper cofactorMapper = new EnzymeCofactorMapper();
1596     Set<Object> cofactors = cofactorMapper.exportSibCofactors(enzymeEntry.getId(), con);
1597     cofactorMapper.close();
1598     if (cofactors != null) enzymeEntry.setCofactors(cofactors);
1599 
1600     // Links
1601     EnzymeLinkMapper linkMapper = new EnzymeLinkMapper();
1602     List<EnzymeLink> links = linkMapper.exportSibLinks(enzymeEntry.getId(), con);
1603     if (links != null)
1604       enzymeEntry.setLinks(new TreeSet<EnzymeLink>(links));
1605 
1606     // Comments
1607     EnzymeCommentMapper commentMapper = new EnzymeCommentMapper();
1608     List<EnzymeComment> comments = commentMapper.exportSibComments(enzymeEntry.getId(), con);
1609     if (comments != null) enzymeEntry.setComments(comments);
1610   }
1611 
1612   /**
1613    * Returns the next available enzyme ID.
1614    *
1615    * @param con The connection.
1616    * @return The next enzyme ID.
1617    * @throws SQLException
1618    */
1619   public Long findNextEnzymeId(Connection con) throws SQLException {
1620     if (con == null) throw new NullPointerException("Parameter 'con' must not be null.");
1621     long enzymeId = 0;
1622     PreparedStatement findNextEnzymeId = null;
1623 
1624     try {
1625       findNextEnzymeId = con.prepareStatement(findNextEnzymeIdStatement());
1626       ResultSet rs = findNextEnzymeId.executeQuery();
1627       if (rs.next()) {
1628         enzymeId = rs.getLong(1);
1629       }
1630     } finally {
1631       findNextEnzymeId.close();
1632     }
1633 
1634     return new Long(enzymeId);
1635   }
1636 
1637 	private void loadHistoryGraph(EnzymeEntry enzymeEntry, Connection con)
1638     throws SQLException, DomainException {
1639 		HistoryGraph historyGraph = getHistoryGraph(con, enzymeEntry);
1640 	    if (historyGraph != null) enzymeEntry.setHistory(historyGraph);
1641 	}
1642 
1643 }