001/* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
002/*
003 *                    BioJava development code
004 *
005 * This code may be freely distributed and modified under the
006 * terms of the GNU Lesser General Public Licence.  This should
007 * be distributed with the code.  If you do not have a copy,
008 * see:
009 *
010 *      http://www.gnu.org/copyleft/lesser.html
011 *
012 * Copyright for this code is held jointly by the individual
013 * authors.  These should be listed in @author doc comments.
014 *
015 * For more information on the BioJava project and its aims,
016 * or to join the biojava-l mailing list, visit the home page
017 * at:
018 *
019 *      http://www.biojava.org/
020 *
021 */
022
023package org.biojava.bio.seq.db.biosql;
024
025import java.io.BufferedReader;
026import java.io.File;
027import java.io.FileReader;
028import java.io.IOException;
029import java.sql.Connection;
030import java.sql.PreparedStatement;
031import java.sql.ResultSet;
032import java.sql.SQLException;
033import java.sql.Statement;
034import java.sql.Types;
035import java.util.ArrayList;
036import java.util.Collection;
037import java.util.HashMap;
038import java.util.HashSet;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Map;
042import java.util.NoSuchElementException;
043import java.util.Set;
044import java.util.Stack;
045
046import org.biojava.bio.Annotation;
047import org.biojava.bio.BioException;
048import org.biojava.bio.BioRuntimeException;
049import org.biojava.bio.taxa.CircularReferenceException;
050import org.biojava.bio.taxa.EbiFormat;
051import org.biojava.bio.taxa.Taxon;
052import org.biojava.bio.taxa.TaxonFactory;
053import org.biojava.bio.taxa.WeakTaxonFactory;
054import org.biojava.utils.ChangeVetoException;
055
056/**
057 * Methods for retrieving, storing and manipulate Taxa stored in a BioSQL database.
058 *
059 * @author Len Trigg
060 * @author Andreas Dräger
061 * @deprecated Use hibernate and org.biojavax.bio.db.*
062 */
063public class TaxonSQL {
064
065    /** 
066     * Attempts to get a Taxon object corresponding to the specified
067     * NCBI taxon ID.
068     *
069     * @param conn the connection to the database
070     * @param ncbi_taxon_id the NCBI taxon ID.
071     * @return the corresponding Taxon (which may have already been
072     * present in memory after an earlier retrieval), or null if the
073     * Taxon could not be found in the database.
074     */
075    public static Taxon getTaxon(Connection conn, int ncbi_taxon_id) 
076    {
077      PreparedStatement ps = null;
078      ResultSet rs = null;
079      Taxon t = null;
080      try {
081        ps = conn.prepareStatement(
082          "SELECT name " +
083          "FROM   taxon_name, taxon " +
084          "WHERE  taxon_name.taxon_id = taxon.taxon_id AND" +
085          "       taxon_name.name_class LIKE 'scientific%' AND" +
086          "       ncbi_taxon_id = ?");
087        ps.setInt(1, ncbi_taxon_id);
088        rs = ps.executeQuery();
089        if (rs.next()) t = getTaxon(conn, rs.getString(1));
090      } catch (SQLException exc) {
091        throw new BioRuntimeException(exc);
092      } finally {
093        attemptClose(rs);
094        attemptClose(ps);
095      }
096      return t; 
097    }
098
099
100    /** 
101     * Attempts to get a Taxon object corresponding to the specified
102     * taxon_id (i.e. the database's internal id for the taxon).
103     *
104     * @param conn the connection to the database
105     * @param taxon_id the database-specific id for the Taxon.
106     * @return the corresponding Taxon (which may have already been
107     * present in memory after an earlier retrieval).
108     */
109    public static Taxon getDBTaxon(Connection conn, int taxon_id) throws SQLException, ChangeVetoException 
110    {
111      PreparedStatement statement = null;
112      ResultSet rs = null;
113      try {
114        // Constants for our wee id array
115        final int NCBI_ID = 1;
116        final int TAXON_ID = 0;
117          
118        // First, get the taxon ids up to the root.
119        statement = conn.prepareStatement(
120          "SELECT ncbi_taxon_id, parent_taxon_id "+ 
121          "FROM   taxon "+ 
122          "WHERE  taxon_id = ? ");
123          
124        ArrayList path = new ArrayList();
125        while (taxon_id != 0) {
126          statement.setInt(1, taxon_id);
127          rs = statement.executeQuery();
128          if (rs.next()) {
129            path.add(new int [] {taxon_id, rs.getInt(1)});
130            taxon_id = rs.getInt(2);
131            if (rs.wasNull()) 
132              taxon_id = 0;
133          } else 
134            throw new BioRuntimeException("Error fetching taxonomy structure. No taxon with taxon_id=" + taxon_id);
135          rs.close();
136        }
137        statement.close();
138          
139        // Traverse from the root down as far has has been created previously...
140        TaxonFactory factory = WeakTaxonFactory.GLOBAL;
141        Taxon taxon = factory.getRoot();
142        int pos = path.size() - 1;
143        int []ids = (int[]) path.get(pos--);
144        Map names = getTaxonNames(conn, ids[TAXON_ID]);
145        taxon.getAnnotation().setProperty(EbiFormat.PROPERTY_NCBI_TAXON, "" + ids[NCBI_ID]);
146        taxon.getAnnotation().setProperty(EbiFormat.PROPERTY_TAXON_NAMES, names);
147        for (; pos >= 0; pos--) {
148          // Who's the next id down the path?
149          ids = (int[]) path.get(pos);
150          String nextID = "" + ids[NCBI_ID];
151          // Now look among the children for the next child.
152          Set children = taxon.getChildren();
153          for (Iterator it = children.iterator(); it.hasNext(); ) {
154            Taxon child = (Taxon) it.next();
155            Annotation anno = child.getAnnotation();
156            if (anno.containsProperty(EbiFormat.PROPERTY_NCBI_TAXON)) {
157              String childID = (String) anno.getProperty(EbiFormat.PROPERTY_NCBI_TAXON);
158              if (childID.equals(nextID)) {
159                taxon = child;
160                continue;
161              }
162            } else {
163              throw new BioRuntimeException("Taxon has not been annotated with NCBI taxon ids.");
164            }
165          }
166          // No child with desired ncbi_id has been found.
167          break;
168        }
169          
170        // Now create taxa from here on down.
171        try {
172          for (; pos >= 0; pos--) 
173          {
174            // Now look for the next child.
175            ids = (int[]) path.get(pos);
176            String nextID = "" + ids[NCBI_ID];
177            names = getTaxonNames(conn, ids[TAXON_ID]);
178            String sciName = (String) names.get("scientific name");
179            if (sciName == null) 
180              throw new BioRuntimeException("No scientific name for taxon_id=" + ids[TAXON_ID]);
181            String commonName = (String) names.get("common name");
182            taxon = factory.addChild(taxon, factory.createTaxon(sciName, commonName));
183            taxon.getAnnotation().setProperty(EbiFormat.PROPERTY_NCBI_TAXON, nextID);
184            taxon.getAnnotation().setProperty(EbiFormat.PROPERTY_TAXON_NAMES, names);
185            taxon = getProperties(conn, taxon);
186          }           
187        } catch (CircularReferenceException ex) {
188          throw new BioRuntimeException("Circular references in taxon table. taxon_id=" + ids[TAXON_ID]);
189        }
190      
191        return getProperties(conn, taxon);
192      } finally {
193        attemptClose(rs);
194        attemptClose(statement);
195      }
196    }
197    
198
199    /**
200     * Look up all the names associated with a taxon_id.
201     *
202     * @param conn the current <code>Connection</code>.
203     * @param taxon_id the NCBI taxon id for the taxon of interest.
204     * @return a <code>Map</code> from name_class (e.g.: "scientific
205     * name") to name.
206     */
207    private static Map getTaxonNames(Connection conn, int taxon_id) 
208    {
209      PreparedStatement statement = null;
210      ResultSet rs = null;
211      try {
212        statement = conn.prepareStatement(
213          "SELECT name_class, name "+
214          "FROM   taxon_name "+ 
215          "WHERE  taxon_id = ? ");
216        statement.setInt(1, taxon_id);
217        rs = statement.executeQuery();
218
219        Map names = new HashMap();
220        while (rs.next()) {     
221          String name_class = rs.getString(1);
222          String name = rs.getString(2);
223          if ((name_class.equals("scientific name")) ||
224              (name_class.equals("common name")))
225              names.put(name_class, name);
226          else {
227            Set s = new HashSet();
228            s.add(name);
229            if (names.containsKey(name_class))
230              s.addAll((Collection) names.get(name_class));
231              names.put(name_class, s);
232            }
233        }
234
235        return names;
236      } catch (SQLException ex) {
237        throw new BioRuntimeException("Error fetching taxonomy annotations", ex);
238      } finally {
239        attemptClose(rs);
240        attemptClose(statement);
241      }
242    }
243
244
245    /**
246     * Adds a <code>Taxon</code> (along with its parents) to the
247     * database. If it is already present in the database, no action
248     * is taken.  Returns the id by which the database refers to the
249     * specified <code>Taxon</code> object.
250     *
251     * @param taxon a <code>Taxon</code>. The <code>Taxon</code> must
252     * be annotated with the NCBI taxon id
253     * (<code>key=EbiFormat.PROPERTY_ORGANISM</code>).
254     * @param helper for the certain database system which is in use.
255     * @return an <code>int</code> that corresponds to the
256     * <code>Taxon</code> in the database.
257     */
258    public static int putTaxon(Connection conn, DBHelper helper, Taxon taxon) throws SQLException 
259    {
260      // Find the NCBI taxon id annotation 
261      Annotation anno = taxon.getAnnotation();
262      Object t  = anno.getProperty(EbiFormat.PROPERTY_NCBI_TAXON);
263      if (t instanceof List) {
264        t = (String) ((List) t).get(0);
265      }
266      int ncbi_taxon_id = Integer.parseInt((String) t);
267      PreparedStatement selectTaxon = conn.prepareStatement(
268        "select taxon_id " 
269        + "from taxon " 
270        + "where ncbi_taxon_id = ? "
271      );
272      selectTaxon.setInt(1, ncbi_taxon_id);
273      ResultSet trs = selectTaxon.executeQuery();
274      int taxon_id;
275      if (trs.next()) {
276        // entry exists - link to it
277        taxon_id = trs.getInt(1);
278      } else {
279        // Taxon entry does not exist - create it
280        Taxon parent = taxon.getParent();
281        
282        // Mein modifizierter Code:
283        String rank, gencode, mitocode, left, right;
284        
285        rank = gencode = mitocode = left = right = "NULL";
286        
287        if (taxon.getAnnotation().containsProperty("rank"))
288          rank = taxon.getAnnotation().getProperty("rank").toString();
289        if (taxon.getAnnotation().containsProperty("genetic code id"))
290          gencode = taxon.getAnnotation().getProperty("genetic code id").toString();
291        if (taxon.getAnnotation().containsProperty("mitochondrial genetic code id"))
292          mitocode = taxon.getAnnotation().getProperty("mitochondrial genetic code id").toString();
293        if (taxon.getAnnotation().containsProperty("left value"))
294          left = taxon.getAnnotation().getProperty("left value").toString();
295        if (taxon.getAnnotation().containsProperty("right value"))
296          right = taxon.getAnnotation().getProperty("right value").toString();
297            
298            
299        PreparedStatement createTaxon = null;
300        if (parent != null) {
301          int parent_taxon_id = putTaxon(conn, helper, parent);
302          createTaxon = conn.prepareStatement(
303             "insert into taxon " 
304             + "(ncbi_taxon_id, parent_taxon_id,"
305             + " node_rank, genetic_code,"
306             + " mito_genetic_code, left_value,"
307             + " right_value) " 
308             + "values (?, ?, ?, ?, ?, ?, ?)"
309           );
310          //System.out.println(ncbi_taxon_id+"\t"+rank+"\t"+gencode+"\t"+"\t"+mitocode+"\t"+left+"\t"+right);
311          createTaxon.setInt(1, ncbi_taxon_id);
312          createTaxon.setInt(2, parent_taxon_id);
313          if (rank.equals("NULL")) 
314            createTaxon.setNull(3, Types.VARCHAR);
315          else createTaxon.setString(3, rank);
316          if (gencode.equals("NULL"))
317            createTaxon.setNull(4, Types.TINYINT);
318          else createTaxon.setInt(4, new Integer(gencode).intValue());
319          if (mitocode.equals("NULL"))
320            createTaxon.setNull(5, Types.TINYINT);
321          else createTaxon.setInt(5, new Integer(mitocode).intValue());
322          if (left.equals("NULL"))
323            createTaxon.setNull(6, Types.INTEGER);
324          else createTaxon.setInt(6, new Integer(left).intValue());
325          if (right.equals("NULL"))
326            createTaxon.setNull(7, Types.INTEGER);
327          else createTaxon.setInt(7, new Integer(right).intValue());
328              
329        } else {
330          createTaxon = conn.prepareStatement(
331            "insert into taxon " 
332            + "(ncbi_taxon_id, node_rank,"
333            + " genetic_code, mito_genetic_code,"
334            + " left_value, right_value) " 
335            + "values (?, ?, ?, ?, ?, ?)"
336          );
337          createTaxon.setInt(1, ncbi_taxon_id);
338          if (rank.equals("NULL")) 
339            createTaxon.setNull(2, Types.VARCHAR);
340          else createTaxon.setString(2, rank);
341          if (gencode.equals("NULL"))
342            createTaxon.setNull(3, Types.TINYINT);
343          else createTaxon.setInt(3, new Integer(gencode).intValue());
344          if (mitocode.equals("NULL"))
345            createTaxon.setNull(4, Types.TINYINT);
346          else createTaxon.setInt(4, new Integer(mitocode).intValue());
347          if (left.equals("NULL"))
348            createTaxon.setNull(5, Types.INTEGER);
349          else createTaxon.setInt(5, new Integer(left).intValue());
350          if (right.equals("NULL"))
351            createTaxon.setNull(6, Types.INTEGER);
352          else createTaxon.setInt(6, new Integer(right).intValue());
353        }
354        createTaxon.executeUpdate();
355        createTaxon.close();
356        taxon_id = helper.getInsertID(conn, "taxon", "taxon_id");
357        putTaxonNames(conn, (Map) taxon.getAnnotation().getProperty(EbiFormat.PROPERTY_TAXON_NAMES), taxon_id);
358      }
359      trs.close();
360      selectTaxon.close();
361      return taxon_id;
362    }
363
364
365    /** Attempts to put all the names of a taxon into the database. There's no need
366     * to access this method, it's only for internal use.
367     * @param conn the connection to the database
368     * @param names a map with the <code>name_class</code> as key and a <code>String</code>
369     *   as value or a <code>Set</code> of <code>String</code>s, respectively.
370     * @param taxon_id the internal <code>taxon_id</code> within the database.
371     */
372    private static void putTaxonNames(Connection conn, Map names, int taxon_id) throws SQLException 
373    {
374      if (names != null) {      
375        Iterator it = names.keySet().iterator();
376        while (it.hasNext()) {
377          String nameClass = (String) it.next();
378        
379          // changes:
380          if (names.get(nameClass) instanceof Set) {
381            Set all_names = (Set) names.get(nameClass);
382            Iterator i = all_names.iterator();
383            while(i.hasNext()) {
384              String name = (String) i.next();
385              PreparedStatement createTaxon = conn.prepareStatement(
386                 "insert into taxon_name " 
387                 + "(taxon_id, name, name_class) " 
388                 + "values (?, ?, ?)"
389              );
390              createTaxon.setInt(1, taxon_id);
391              createTaxon.setString(2, name);
392              createTaxon.setString(3, nameClass);
393              createTaxon.executeUpdate();
394              createTaxon.close();
395            }   
396          } else {
397            // normal again
398                        
399            String name = (String) names.get(nameClass);
400            PreparedStatement createTaxon = conn.prepareStatement(
401              "insert into taxon_name " 
402              + "(taxon_id, name, name_class) " 
403              + "values (?, ?, ?)"
404            );
405            createTaxon.setInt(1, taxon_id);
406            createTaxon.setString(2, name);
407            createTaxon.setString(3, nameClass);
408            createTaxon.executeUpdate();
409            createTaxon.close();
410          }
411        }
412      }
413    }
414
415
416    /** Attempt to close the Statement. Continue on if there is a problem during the close. */
417    public static void attemptClose(Statement statement) 
418    {
419      if (statement != null) try {
420        statement.close();
421      } catch (SQLException se) {
422        se.printStackTrace();
423      }
424    }
425
426
427    /** Attempt to close the ResultSet. Continue on if there is a problem during the close. */
428    public static void attemptClose(ResultSet resultset) 
429    {
430      if (resultset != null) try {
431        resultset.close();
432      } catch (SQLException se) {
433        se.printStackTrace();
434      }
435    }
436   
437    
438  /** 
439   * Attempts to get a Taxon object corresponding to the specified
440   * name.
441   *
442   * @param conn the connection to the database
443   * @param name the species scientific name
444   * @return the corresponding Taxon (which may have already been
445   * present in memory after an earlier retrieval), or null if the
446   * Taxon could not be found in the database.
447   * @throws BioRuntimeException
448   */
449  public static Taxon getTaxon(Connection conn, String name) throws BioRuntimeException
450  {
451    PreparedStatement statement = null;
452    ResultSet rs = null;
453    try {
454      int taxon_id = 0;
455      statement = conn.prepareStatement(
456        "SELECT taxon.taxon_id " +
457        "FROM   taxon, taxon_name " +
458        "WHERE  taxon.taxon_id = taxon_name.taxon_id " +
459        "  AND  taxon_name.name LIKE ?");
460      statement.setString(1, name);
461      rs = statement.executeQuery();
462      if (rs.next()) {
463        taxon_id = rs.getInt(1);
464        if (rs.wasNull()) 
465          taxon_id = 0;
466      }
467      rs.close();
468      statement.close();
469
470      return (taxon_id != 0) ? getDBTaxon(conn, taxon_id) : null;
471    } catch (ChangeVetoException ex) {
472      throw new BioRuntimeException("Couldn't manipulate in-memory taxonomy", ex);
473    } catch (SQLException ex) {
474      throw new BioRuntimeException("Error fetching taxonomy annotations", ex);
475    } finally {
476      attemptClose(rs);
477      attemptClose(statement);
478    }
479  }
480
481  
482  /** Returns all the <code>scientific name</code>s, which are currently stored in the
483    * database.
484    * 
485    * @param conn connection to the database
486    * @return array of lexicographically sorted <code>String</code>s
487    * @throws BioRuntimeException
488    */
489  public static String[] getAllScientificNames(Connection conn) throws BioRuntimeException
490  {
491    String scientific_names[] = null;
492    PreparedStatement statement = null;
493    ResultSet rs = null;
494    try {
495      int taxa_counter = 0, i=0;
496      statement = conn.prepareStatement(
497        "SELECT COUNT(distinct name) " +
498        "FROM   taxon_name " +
499        "WHERE  name_class LIKE 'scientific name' ");
500      rs = statement.executeQuery();
501      if (rs.next())
502        taxa_counter = rs.getInt(1);
503      scientific_names = new String[taxa_counter];
504      statement = conn.prepareStatement(
505        "SELECT distinct name " +
506        "FROM   taxon_name " +
507        "WHERE  name_class LIKE 'scientific name' " +
508        "ORDER BY name ASC");
509      rs = statement.executeQuery();
510      while (rs.next()) {
511        scientific_names[i++] = rs.getString(1);
512        if (rs.wasNull()) i--;
513      }
514      rs.close();
515      statement.close();
516
517      return (taxa_counter != 0) ? scientific_names : null;
518    } catch (SQLException exc) {
519      exc.printStackTrace();
520    }
521    return null;
522  }
523
524  
525  /** Returns the annotated properties of a given taxon. Only for internal use,
526    * since this is needed to construct a fully annotated <code>Taxon</code> object,
527    * which will be returned by one of the getTaxon... methods.
528    * @param conn database connection
529    * @param taxon the taxon to be annotated
530    * @return a fully annotated taxon.
531    * @throws BioRuntimeException
532    */
533  private static Taxon getProperties(Connection conn, Taxon taxon) throws BioRuntimeException
534  {
535    PreparedStatement ps = null;
536    ResultSet rs = null;
537    
538    try {
539      String rank = null, gencode = null, mitocode = null, left = null, right = null;
540      ps = conn.prepareStatement(
541        "SELECT node_rank, genetic_code, mito_genetic_code, left_value, right_value " +
542        "FROM   taxon " +
543        "WHERE  taxon_id = ?");
544      ps.setInt(1, getTaxonID(conn, getRealScientificName(taxon)));
545      rs = ps.executeQuery();
546      if (rs.next()) {
547        rank = rs.getString(1);
548        if (!rs.wasNull()) taxon.getAnnotation().setProperty("rank", rank);
549        gencode = rs.getString(2);
550        if (!rs.wasNull()) taxon.getAnnotation().setProperty("genetic code id", gencode);
551        mitocode = rs.getString(3);
552        if (!rs.wasNull()) taxon.getAnnotation().setProperty("mitochondrial genetic code id", mitocode);
553        left = String.valueOf(rs.getInt(4));
554        if (!rs.wasNull()) taxon.getAnnotation().setProperty("left value", left);
555            right = String.valueOf(rs.getInt(5));
556        if (!rs.wasNull()) taxon.getAnnotation().setProperty("right value", right);
557      }  
558    } catch (SQLException exc) {
559      throw new BioRuntimeException(exc);
560    } catch (IllegalArgumentException exc) {
561      throw new BioRuntimeException(exc);
562    } catch (ChangeVetoException exc) {
563      throw new BioRuntimeException(exc);
564    } catch (BioException exc) {
565      exc.printStackTrace();
566    } finally {
567      attemptClose(rs);
568      attemptClose(ps);
569    }
570    
571    return taxon;
572  }
573  
574  
575  /** This returns the true scientific name of a given taxon, if there is one. 
576    * This is necessary because if a taxon does not have a parent node,
577    * a <code>TaxonFactory</code> gives the scientific name 'ROOT' and the
578    * real scientific name (if there is one) is only stored in the taxon's
579    * <code>EbiFormat</code>-annotation. This name 'ROOT' applies only for
580    * in memory taxon objects. In the database the real name is stored.
581    * 
582    * @param t the taxon
583    * @return Name of the taxon.
584    */
585  public static String getRealScientificName(Taxon t)
586  {
587    String tName = "root";
588    if (t.getScientificName() != null) tName = t.getScientificName();
589    if (tName.toLowerCase().equals("root") && (t.getAnnotation() != null)) 
590      if (t.getAnnotation().containsProperty(EbiFormat.PROPERTY_TAXON_NAMES)) 
591        if (((Map) t.getAnnotation().getProperty(EbiFormat.PROPERTY_TAXON_NAMES)).containsKey("scientific name"))
592          tName = ((Map) t.getAnnotation().getProperty(EbiFormat.PROPERTY_TAXON_NAMES)).get("scientific name").toString();
593    return tName;
594  }
595
596  
597  /** Returns a <code>Set</code> containing all internal <code>taxon_id</code>s of the database.
598    * @param conn database connection
599    * @return all <code>taxon_id</code>s or internal use of the database.
600    * @throws SQLException
601    */
602  private static Set ids(Connection conn) throws SQLException
603  {
604    Set ids = new HashSet();
605    Statement statement = conn.createStatement();
606    ResultSet rs = statement.executeQuery("SELECT taxon_id FROM taxon");
607    while (rs.next()) 
608      ids.add(new Integer(rs.getInt(1)));
609    attemptClose(rs);
610    statement.close();
611    
612    return ids;
613  }
614
615  
616  /** Returns a <code>Set</code> of all NCBI-Taxon-IDs which are currently stored in
617    * the database. So it is easy to proove if a taxon is stored in the database or
618    * perform other operations.
619    * @param conn database connection
620    * @return a <code>Set</code> containing all NCBI-IDs.
621    * @throws BioRuntimeException
622    */
623  public static Set NCBIids(Connection conn) throws BioRuntimeException
624  {
625    Set ids = new HashSet();
626    Statement statement = null;
627    ResultSet rs = null;
628    try {
629      statement = conn.createStatement();
630      rs = statement.executeQuery("SELECT ncbi_taxon_id FROM taxon");
631      while (rs.next())
632        ids.add(new Integer(rs.getInt(1)));
633    } catch (SQLException exc) {
634      throw new BioRuntimeException(exc);
635    } finally {
636      try {
637        rs.close();
638        statement.close();
639      } catch (SQLException exc1) {
640        throw new BioRuntimeException(exc1);
641      }
642    }
643    return ids;
644  }
645   
646  
647  /** Deletes a taxon specified by one of it's names with all it's different
648    * names, annotations and sequences from the database. This cannot be undone.
649    * The removed taxon will be returned.
650    * 
651    * @param conn database connection
652    * @param helper the helper for the certain database system to be used.
653    * @param name one of the taxon's names
654    * @return the taxon, which was successfully removed and which is not longer
655    *   stored in the database.
656    * @throws BioException
657    * @throws SQLException
658    */
659  public static Taxon removeTaxon(Connection conn, DBHelper helper, String name) throws SQLException, BioException
660  {    
661    //if (name.contains("'"))  name = name.replace("\'", "\\\'");
662    //if (name.contains("\"")) name = name.replace("\"", "\\\"");
663
664    return removeTaxon(conn, helper, getTaxonID(conn, name));
665  }
666
667  
668  /** This actually performs the delete operations.
669    *  
670    * @param conn
671    * @param helper
672    * @param taxon_id
673    * @return taxon
674    * @throws BioRuntimeException
675    */
676  private static Taxon removeTaxon(Connection conn, DBHelper helper, int taxon_id) throws BioRuntimeException
677  {
678    Taxon taxon = getTaxon(conn, taxon_id);
679    /*
680     * Children which don't have a parent anymore -> they become their own parent.
681     */
682    try {
683      Stack children = getChildrenOf(conn, taxon);
684      while (!children.isEmpty()) try {
685        Taxon child = (Taxon) children.pop();
686        setParent(conn, child, child);
687      } catch (BioRuntimeException exc) {
688        exc.printStackTrace();
689      }
690    } catch (BioException exc) {
691      exc.printStackTrace();
692    }
693    try {
694      Statement delete = conn.createStatement();
695      delete.executeUpdate("DELETE FROM taxon_name WHERE taxon_id = "+taxon_id);
696      delete.close();
697      delete = (Statement) conn.createStatement();
698      delete.executeUpdate("DELETE FROM bioentry WHERE taxon_id = "+taxon_id);
699      delete.close();
700      delete = (Statement) conn.createStatement();        
701      delete.executeUpdate("DELETE FROM taxon WHERE taxon_id = "+taxon_id);
702      delete.close();
703    } catch (SQLException exc) {
704      throw new BioRuntimeException("Could not delete successfully", exc);
705    }
706      
707    return taxon;
708  }
709  
710  
711  /** Deletes the taxon given by it's NCBI-Taxon-ID from the database and returns the
712    * removed taxon.
713    * @param conn database connection
714    * @param helper the helper for the database
715    * @param ncbi_id the ncbi-id
716    * @return the taxon wich is not stored in the database anymore.
717    * @throws BioRuntimeException
718    * @throws BioException
719    * @throws SQLException
720    */
721  public static Taxon removeTaxon(Connection conn, int ncbi_id, DBHelper helper) throws BioRuntimeException, SQLException, BioException
722  {
723    return removeTaxon(conn, helper, getTaxonID(conn, getRealScientificName(getTaxon(conn, ncbi_id))));
724  }
725  
726  
727  /** This changes the scientific name of the given taxon and stores the new name persistent
728    * in the database.
729    * @param conn database connection
730    * @param taxon the taxon to be changed
731    * @param newName the new scientific name
732    * @return the changed taxon with the new scientific name.
733    * @throws SQLException
734    */
735  public static Taxon setScientificName(Connection conn, Taxon taxon, String newName) throws SQLException 
736  {    
737    PreparedStatement updateName = conn.prepareStatement(
738      "UPDATE taxon_name SET name = ? " +
739      "WHERE  name LIKE ? AND " +
740      "       name_class LIKE ?");
741    updateName.setString(1, newName);
742    updateName.setString(2, taxon.getScientificName());
743    updateName.setString(3, "scientific name");
744    updateName.execute();
745    attemptClose(updateName);
746
747    return getTaxon(conn, newName);
748  }
749  
750  
751  /** With this method the common name of the given taxon can be changed or created,
752    * if there was none before. The new common name will be stored persitently.
753    * @param conn database connection
754    * @param taxon the taxon to be updated
755    * @param newName the new common name
756    * @return the updated taxon.
757    * @throws BioException
758    * @throws SQLException
759    */
760  public static Taxon setCommonName(Connection conn, Taxon taxon, String newName) throws BioException, SQLException 
761  {
762    boolean noCommonName = false;
763    String name_class = "common name";
764    
765    /* A taxon does not necessaryly have to have a common name. Let's have a look,
766     * if we need to perform an update or insert operation.
767     */
768    PreparedStatement question = conn.prepareStatement(
769      "SELECT COUNT(name) " +
770      "FROM   taxon_name " +
771      "WHERE  name_class LIKE ? AND " +
772      "       taxon_id = ?");
773    question.setString(1, name_class);
774    question.setInt(2, getTaxonID(conn, taxon.getScientificName()));
775    ResultSet rs = question.executeQuery();
776    
777    if (rs.next()) {
778      if (rs.getInt(1) > 0) {
779        attemptClose(rs);
780        attemptClose(question);
781        PreparedStatement updateName = conn.prepareStatement(
782          "UPDATE taxon_name SET name = ? " +
783          "WHERE  name LIKE ? AND " +
784          "       name_class LIKE ?");
785        updateName.setString(1, newName);
786        updateName.setString(2, taxon.getScientificName());
787        updateName.setString(3, name_class);
788        updateName.execute();
789        attemptClose(updateName);
790      } else noCommonName = true;      
791    } 
792    if (noCommonName) {      
793      attemptClose(rs);
794      attemptClose(question);
795      PreparedStatement insert = conn.prepareStatement(
796        "INSERT INTO taxon_name (taxon_id, name, name_class) VALUES (?, ?, ?)");
797      try {
798        insert.setInt(1, getTaxonID(conn, taxon.getScientificName()));
799        insert.setString(2, newName);
800        insert.setString(3, name_class);
801        insert.execute();
802      } catch (BioException exc) {
803        throw exc;
804      } catch (SQLException exc) {
805        throw exc;
806      } finally {
807        attemptClose(insert); 
808      }
809    }
810        
811    return getTaxon(conn, taxon.getScientificName());
812  }
813
814    
815  /** Returns the internal <code>taxon_id</code> of the taxon specified by the 
816    * scientific name. Only for internal use.
817    * @param conn database connection
818    * @param scientificName the scientific name
819    * @return the internal taxon_id of the taxon.
820    * @throws SQLException
821    * @throws BioException
822    */
823  private static int getTaxonID(Connection conn, String scientificName) throws SQLException, BioException
824  {
825    int taxon_id = -1;   
826    
827    PreparedStatement taxID  = conn.prepareStatement(
828      "SELECT taxon_id "        +
829      "FROM   taxon_name "      +
830      "WHERE  name LIKE ? AND " +
831      "       name_class LIKE ?");
832    taxID.setString(1, scientificName);
833    taxID.setString(2, "scientific name");
834    ResultSet rs = taxID.executeQuery();
835    if (rs.next()) {
836      taxon_id = rs.getInt(1);
837      attemptClose(rs);
838      attemptClose(taxID);
839      return taxon_id;
840    }
841    attemptClose(rs);
842    attemptClose(taxID);
843    throw new BioException("The database does not contain a taxon named "+scientificName+".");
844  }
845
846  
847  /** Adds a new name of the given <code>nameClass</code> to the taxon. However, there must
848    * be exactly one <code>scientific name</code> and maximal one <code>common
849    * name</code>. Otherwise an <code>Exception</code> will be thrown.  
850    * 
851    * @param conn database connection
852    * @param taxon the taxon to be updated
853    * @param nameClass the name_class of the new name.
854    * @param newName the new name.
855    * @return the persistently updated taxon.
856    * @throws BioException
857    * @throws SQLException
858    * @throws BioRuntimeException
859    */
860  public static Taxon addName(Connection conn, Taxon taxon, String nameClass, String newName) throws BioException, SQLException, BioRuntimeException
861  {
862    if (nameClass.equals("scientific name") || nameClass.equals("common name")) 
863      throw new BioException("There can only be one "+nameClass+".");  
864    
865    PreparedStatement insert = conn.prepareStatement(
866      "INSERT INTO taxon_name (taxon_id, name, name_class) VALUES (?, ?, ?)");
867    try {
868      insert.setInt(1, getTaxonID(conn, getRealScientificName(taxon)));
869      insert.setString(2, newName);
870      insert.setString(3, nameClass);
871      insert.executeUpdate();
872    } catch (BioException exc) {
873      throw exc;
874    } catch (SQLException exc) {
875      throw exc;
876    } finally {
877      attemptClose(insert);
878    }
879    
880    return getTaxon(conn, taxon.getScientificName());
881  }
882  
883  
884  /** Deletes the specified name from of the taxon from the database. 
885    * The <code>scientific name</code> has to be uniqe, so this cannot be removed by
886    * this method.
887    * 
888    * @param conn the database connection
889    * @param helper the helper for the used database system
890    * @param taxon the taxon to be updated
891    * @param nameClass the name_class of the name to be removed
892    * @param oldName the old name, which is not needed anymore.
893    * @return the updated taxon.
894    * @throws BioException
895    * @throws SQLException
896    */
897  public static Taxon removeName(Connection conn, DBHelper helper, Taxon taxon, String nameClass, String oldName) throws BioException, SQLException
898  {
899    if (nameClass.equals("scientific name")) 
900      throw new BioException("You can't delete the "+nameClass);
901        
902    PreparedStatement delete = conn.prepareStatement(
903      "DELETE FROM taxon_name " +
904      "WHERE  name_class LIKE ? AND " +
905      "       name LIKE ?"); 
906    delete.setString(1, nameClass);
907    delete.setString(2, oldName);
908    delete.executeUpdate();
909    attemptClose(delete);
910    
911    return getTaxon(conn, taxon.getScientificName());
912  }
913  
914  
915  /** Returns all children of the specified taxon.
916    * @param conn database connection
917    * @param scientificName name of the taxon which children should be searched.
918    * @return a <code>Stac</code>, which contains the children sorted by theire 
919    *   <code>scientific name</code>s from top to the bottom.
920    * @throws BioException
921    * @throws SQLException
922    */
923  public static Stack getChildrenOf(Connection conn, String scientificName) throws BioException
924  {
925    Stack children = new Stack();
926    PreparedStatement ps = null, ps2 = null;
927    ResultSet rs = null, rs2 = null;
928    try {
929      ps = conn.prepareStatement(
930        "SELECT taxon.taxon_id " +
931        "FROM   taxon, taxon_name " +
932        "WHERE  taxon_name.taxon_id = taxon.taxon_id     AND" +
933        "       taxon_name.name_class LIKE 'scientific%' AND" +
934        "       parent_taxon_id = ? " +
935        "ORDER BY taxon_name.name DESC");
936      //if (scientificName.contains("'"))  scientificName = scientificName.replace("\'", "\\\'");
937      //if (scientificName.contains("\"")) scientificName = scientificName.replace("\"", "\\\"");
938      ps.setInt(1, getTaxonID(conn, scientificName));
939      rs = ps.executeQuery();
940      while (rs.next()) {
941        ps2 = conn.prepareStatement(
942          "SELECT name             " +
943          "FROM   taxon_name       " +
944          "WHERE  taxon_id = ? AND " +
945          "       name_class LIKE 'scientific%'");
946        ps2.setInt(1, rs.getInt(1));
947        rs2 = ps2.executeQuery();
948        if (rs2.next()) { 
949          children.push(getTaxon(conn, rs2.getString(1)));
950          if (rs2.wasNull()) children.pop();
951        }
952      }
953    } catch (SQLException exc) {
954      throw new BioException(exc);
955    } finally {
956      attemptClose(ps);
957      attemptClose(rs);
958      attemptClose(ps2);
959      attemptClose(rs2);
960    }
961
962    return children;  
963  }
964
965  
966  /** Returns the children as a <code>Stack</code> of this given taxon.
967    * 
968    * @param conn database connection
969    * @param t the parent taxon
970    * @return a sorted <code>Stack</code> of the children, which might be empty,
971    *   but not <code>null</code>.
972    * @throws BioException
973    */
974  public static Stack getChildrenOf(Connection conn, Taxon t) throws BioException
975  {
976    String tName = t.getScientificName();
977    if (tName.toLowerCase().equals("root") && (t.getAnnotation() != null))
978      if (t.getAnnotation().containsProperty(EbiFormat.PROPERTY_TAXON_NAMES)) 
979        if (((Map) t.getAnnotation().getProperty(EbiFormat.PROPERTY_TAXON_NAMES)).containsKey("scientific name"))
980           tName = ((Map) t.getAnnotation().getProperty(EbiFormat.PROPERTY_TAXON_NAMES)).get("scientific name").toString();
981    
982    return getChildrenOf(conn, tName);
983  }
984
985  
986  /** Updates a taxon and sets it's rank to the specified <code>String</code>.
987    * @param conn database connection.
988    * @param tdb taxon to be updated
989    * @param rank the new rank (like 'kingdom', 'genus' or what ever)
990    * @throws BioRuntimeException
991    */
992  public static void setRank(Connection conn, Taxon tdb, String rank) throws BioRuntimeException
993  {
994    PreparedStatement ps = null;
995    try {
996      ps = conn.prepareStatement(
997        "UPDATE taxon SET node_rank = ? WHERE taxon_id = ?");
998      ps.setString(1, rank);
999      ps.setInt(2, getTaxonID(conn, getRealScientificName(tdb)));
1000      ps.executeUpdate();
1001    } catch (SQLException exc) {
1002      throw new BioRuntimeException(exc);
1003    } catch (BioException exc) {
1004      throw new BioRuntimeException(exc);
1005    } finally {
1006      attemptClose(ps);
1007    }
1008  }
1009
1010  
1011  /** Removes the rank persistently from the taxon in the database.
1012    * @param conn
1013    * @param helper
1014    * @param tdb
1015    * @throws BioRuntimeException
1016    */
1017  public static void removeRank(Connection conn, DBHelper helper, Taxon tdb) throws BioRuntimeException
1018  {
1019    PreparedStatement ps = null;
1020    try {
1021      ps = conn.prepareStatement("UPDATE taxon SET node_rank = NULL WHERE taxon_id = ?");
1022      ps.setInt(1, getTaxonID(conn, getRealScientificName(tdb)));
1023      ps.executeUpdate();
1024    } catch (SQLException exc) {
1025      throw new BioRuntimeException(exc);
1026    } catch (BioException exc) {
1027      throw new BioRuntimeException(exc);
1028    } finally {
1029      attemptClose(ps);
1030    }
1031  }
1032
1033  
1034  /** Updates the taxon in the database and sets its genetic code id to the specified value.
1035    * @param conn
1036    * @param tdb
1037    * @param id
1038    * @throws BioRuntimeException
1039    */
1040  public static void setGeneticCodeID(Connection conn, Taxon tdb, int id) throws BioRuntimeException
1041  {
1042    PreparedStatement ps = null;
1043    try {
1044      ps = conn.prepareStatement(
1045        "UPDATE taxon SET genetic_code = ? WHERE taxon_id = ?");
1046      ps.setInt(1, id);
1047      ps.setInt(2, getTaxonID(conn, getRealScientificName(tdb)));
1048      ps.executeUpdate();
1049    } catch (SQLException exc) {
1050      throw new BioRuntimeException(exc);
1051    } catch (BioException exc) {
1052      throw new BioRuntimeException(exc);
1053    } finally {
1054      attemptClose(ps);
1055    }
1056  }
1057
1058  
1059  /** Deletes the genetic code annotation from the taxon in the database.
1060    * @param conn
1061    * @param helper
1062    * @param tdb
1063    * @throws BioRuntimeException
1064    */
1065  public static void removeGeneticCodeID(Connection conn, DBHelper helper, Taxon tdb) throws BioRuntimeException
1066  {
1067    PreparedStatement ps = null;
1068    try {
1069      ps = conn.prepareStatement("UPDATE taxon SET genetic_code = NULL WHERE taxon_id = ?");
1070      ps.setInt(1, getTaxonID(conn, getRealScientificName(tdb)));
1071      ps.executeUpdate();
1072    } catch (SQLException exc) {
1073      throw new BioRuntimeException(exc);
1074    } catch (BioException exc) {
1075      throw new BioRuntimeException(exc);
1076    } finally {
1077      attemptClose(ps);
1078    }
1079  }
1080
1081  
1082  /** Updates the given taxon and sets it's so called mitochondrial genetic code id to
1083    * the specified value.
1084    * @param conn
1085    * @param tdb
1086    * @param id
1087    * @throws BioRuntimeException
1088    */
1089  public static void setMitochondrialGeneticCodeID(Connection conn, Taxon tdb, int id) throws BioRuntimeException
1090  {
1091    PreparedStatement ps = null;
1092    try {
1093      ps = conn.prepareStatement(
1094        "UPDATE taxon SET mito_genetic_code = ? WHERE taxon_id = ?");
1095      ps.setInt(1, id);
1096      ps.setInt(2, getTaxonID(conn, getRealScientificName(tdb)));
1097      ps.executeUpdate();
1098    } catch (SQLException exc) {
1099      throw new BioRuntimeException(exc);
1100    } catch (BioException exc) {
1101      throw new BioRuntimeException(exc);
1102    } finally {
1103      attemptClose(ps);
1104    }
1105  }
1106
1107  
1108  /** Deletes the so called mitochondrial genetic code annotation from the given taxon.
1109    * @param conn
1110    * @param helper
1111    * @param tdb
1112    * @throws BioRuntimeException
1113    */
1114  public static void removeMitochondrialGeneticCodeID(Connection conn, DBHelper helper, Taxon tdb) throws BioRuntimeException
1115  {
1116    PreparedStatement ps = null;
1117    try {
1118      ps = conn.prepareStatement("UPDATE taxon SET mito_genetic_code = NULL WHERE taxon_id = ?");
1119      ps.setInt(1, getTaxonID(conn, getRealScientificName(tdb)));
1120      ps.executeUpdate();
1121    } catch (SQLException exc) {
1122      throw new BioRuntimeException(exc);
1123    } catch (BioException exc) {
1124      throw new BioRuntimeException(exc);
1125    } finally {
1126      attemptClose(ps);
1127    }
1128  }
1129
1130  
1131  /** Updates the taxon and sets the left value to the specified value.
1132    * @param conn
1133    * @param tdb
1134    * @param left
1135    * @throws BioRuntimeException
1136    */
1137  public static void setLeftValue(Connection conn, Taxon tdb, int left) throws BioRuntimeException
1138  {
1139    PreparedStatement ps = null;
1140    try {
1141      ps = conn.prepareStatement(
1142        "UPDATE taxon SET left_value = ? WHERE taxon_id = ?");
1143      ps.setInt(1, left);
1144      ps.setInt(2, getTaxonID(conn, getRealScientificName(tdb)));
1145      ps.executeUpdate();
1146    } catch (SQLException exc) {
1147      throw new BioRuntimeException(exc);
1148    } catch (BioException exc) {
1149      throw new BioRuntimeException(exc);
1150    } finally {
1151      attemptClose(ps);
1152    }
1153  }
1154
1155  
1156  /** Deletes the left value from the specified taxon in the database.
1157    * @param conn
1158    * @param helper
1159    * @param tdb
1160    * @throws BioRuntimeException
1161    */
1162  public static void removeLeftValue(Connection conn, DBHelper helper, Taxon tdb) throws BioRuntimeException
1163  {
1164    PreparedStatement ps = null;
1165    try {
1166      ps = conn.prepareStatement("UPDATE taxon SEQ left_value = NULL WHERE taxon_id = ?");
1167      ps.setInt(1, getTaxonID(conn, getRealScientificName(tdb)));
1168      ps.executeUpdate();
1169    } catch (SQLException exc) {
1170      throw new BioRuntimeException(exc);
1171    } catch (BioException exc) {
1172      throw new BioRuntimeException(exc);
1173    } finally {
1174      attemptClose(ps);
1175    }
1176  }
1177
1178  
1179  /** Updates the taxon in the database and sets the right value to the specified value.
1180    * @param conn
1181    * @param tdb
1182    * @param right
1183    * @throws BioRuntimeException
1184    */
1185  public static void setRightValue(Connection conn, Taxon tdb, int right) throws BioRuntimeException
1186  {
1187    PreparedStatement ps = null;
1188    try {
1189      ps = conn.prepareStatement(
1190        "UPDATE taxon SET right_value = ? WHERE taxon_id = ?");
1191      ps.setInt(1, right);
1192      ps.setInt(2, getTaxonID(conn, getRealScientificName(tdb)));
1193      ps.executeUpdate();
1194    } catch (SQLException exc) {
1195      throw new BioRuntimeException(exc);
1196    } catch (BioException exc) {
1197      throw new BioRuntimeException(exc);
1198    } finally {
1199      attemptClose(ps);
1200    }
1201  }
1202
1203  
1204  /** Deletes the right value from the specified taxon in the database.
1205    * @param conn
1206    * @param helper
1207    * @param tdb
1208    * @throws BioRuntimeException
1209    */
1210  public static void removeRightValue(Connection conn, DBHelper helper, Taxon tdb) throws BioRuntimeException
1211  {
1212    PreparedStatement ps = null;
1213    try {
1214      ps = conn.prepareStatement("UPDATE taxon SEQ right_value = NULL WHERE taxon_id = ?");
1215      ps.setInt(1, getTaxonID(conn, getRealScientificName(tdb)));
1216      ps.executeUpdate();
1217    } catch (SQLException exc) {
1218      throw new BioRuntimeException(exc);
1219    } catch (BioException exc) {
1220      throw new BioRuntimeException(exc);
1221    } finally {
1222      attemptClose(ps);
1223    }
1224  }
1225
1226  
1227  /** This updates the taxonomic tree in the database and sets the parent of the given 
1228    * child taxon to the parent taxon.
1229    * @param conn
1230    * @param child
1231    * @param parent
1232    * @throws BioRuntimeException
1233    */
1234  public static void setParent(Connection conn, Taxon child, Taxon parent) throws BioRuntimeException
1235  {
1236    PreparedStatement ps = null;
1237    try {
1238      ps = conn.prepareStatement(
1239        "UPDATE taxon SET parent_taxon_id = ? WHERE taxon_id = ?");
1240      ps.setInt(1, getTaxonID(conn, getRealScientificName(parent)));
1241      ps.setInt(2, getTaxonID(conn, getRealScientificName(child)));
1242      ps.executeUpdate();
1243    } catch (SQLException exc) {
1244      throw new BioRuntimeException(exc);
1245    } catch (BioException exc) {
1246      throw new BioRuntimeException(exc);
1247    } finally {
1248      attemptClose(ps);
1249    }
1250  }
1251  
1252  
1253  
1254  /** This method tries to perform a complete update according to the given 
1255    * <code>TaxonFactory</code>, which already contains the newes taxa and the files
1256    * available at the NCBI-FTP-Site.
1257    * @param conn database connection
1258    * @param helper helper for the database system to be used
1259    * @param factory the TaxonFactory containing all the new taxa
1260    * @param delnodes file containing the ncbi taxon ids which don't exist anymore
1261    * @param merged file containing the ncbi taxon ids which were merged.
1262    * @throws IOException
1263    */
1264  public static void automaticUpdate(Connection conn, DBHelper helper, TaxonFactory factory, File delnodes, File merged) throws IOException
1265  {
1266   /*
1267    * Delnodes.
1268    */
1269    BufferedReader br = new BufferedReader(new FileReader(delnodes));
1270    Set ids = NCBIids(conn);
1271    while (br.ready()) {
1272      String line = br.readLine().split("\t")[0];
1273      if (ids.contains(line)) {
1274        try {
1275          removeTaxon(conn, Integer.parseInt(line), helper);
1276          ids.remove(line);
1277        } catch (NumberFormatException exc1) {
1278          exc1.printStackTrace();
1279        } catch (BioRuntimeException exc1) {
1280          exc1.printStackTrace();
1281        } catch (BioException exc1) {
1282          exc1.printStackTrace();
1283        } catch (SQLException exc) {
1284          exc.printStackTrace();
1285        }
1286      } 
1287    }
1288               
1289    
1290    /*
1291     * TaxonFactory.
1292     */
1293    /*
1294     * Mergenodes.
1295     */
1296    br = new BufferedReader(new FileReader(merged));
1297    while (br.ready()) {
1298      String[] line = br.readLine().split("\t");
1299      if (ids.contains(line[0])) {
1300        // line[0] merged with [2].
1301        //System.out.println(line[0]+"\t"+line[2]);
1302        /*
1303         * Change children
1304         */
1305        // Only possible if the new parent exists in the database
1306        if (ids.contains(line[2])) try {
1307          Taxon parent = getTaxon(conn, Integer.parseInt(line[2]));
1308          Stack children = getChildrenOf(conn, getTaxon(conn, Integer.parseInt(line[0])));
1309          while (!children.isEmpty()) {
1310            setParent(conn, (Taxon) children.pop(), parent); 
1311          }
1312         /*
1313          * Sequences to the new node.
1314          */
1315          PreparedStatement ps = conn.prepareStatement(
1316            "UPDATE bioentry SET taxon_id = ? WHERE taxon_id = ?");
1317          ps.setInt(1, getTaxonID(conn, getRealScientificName(parent)));
1318          ps.setInt(2, getTaxonID(conn, getRealScientificName(getTaxon(conn, Integer.parseInt(line[0])))));
1319          ps.executeUpdate();
1320          ps.close();
1321        } catch (NumberFormatException exc1) {
1322          exc1.printStackTrace();
1323        } catch (BioRuntimeException exc1) {
1324          exc1.printStackTrace();
1325        } catch (BioException exc1) {
1326          exc1.printStackTrace();
1327        } catch (SQLException exc) {
1328          exc.printStackTrace();
1329        }
1330        /*
1331         * Delete
1332         */
1333        try {
1334          removeTaxon(conn, Integer.parseInt(line[0]), helper);
1335        } catch (NumberFormatException exc2) {
1336          exc2.printStackTrace();
1337        } catch (BioRuntimeException exc2) {
1338          exc2.printStackTrace();
1339        } catch (BioException exc2) {
1340          exc2.printStackTrace();
1341        } catch (SQLException exc) {
1342          exc.printStackTrace();
1343        }
1344        
1345      }
1346    }
1347    
1348    
1349    try {
1350      /*
1351       * TaxonFactory.
1352       */
1353      for (Iterator i=ids(conn).iterator(); i.hasNext(); ) {
1354        Taxon tDB = getTaxon(conn, ((Integer) i.next()).intValue());
1355        Taxon tFA = factory.search(getRealScientificName(tDB));
1356        boolean change = false;
1357        // This cannot work because the TaxonFactory doesn't provide this information.
1358        if ((tFA == null) && (tDB.getAnnotation().containsProperty(EbiFormat.PROPERTY_NCBI_TAXON))) 
1359          tFA = factory.search(tDB.getAnnotation().getProperty(EbiFormat.PROPERTY_NCBI_TAXON));
1360        if (tFA == null) continue;        
1361        
1362        // scientific name
1363        if (tFA.getScientificName() != null) {
1364          if (!tFA.getScientificName().equals(getRealScientificName(tDB))) try {
1365            setScientificName(conn, tDB, tFA.getScientificName());
1366          } catch (SQLException exc2) {
1367            exc2.printStackTrace();
1368          }
1369        }
1370        
1371        // common name
1372        if (tFA.getCommonName() != null) {
1373          if (tDB.getCommonName() == null) change = true;
1374          else if (!tDB.getCommonName().equals(tFA.getCommonName())) change = true;
1375          if (change) try {
1376            setCommonName(conn, tDB, tFA.getCommonName());
1377          } catch (BioException exc1) {
1378            exc1.printStackTrace();
1379          } catch (SQLException exc) {
1380            exc.printStackTrace();
1381          } finally { change = false; }
1382        } else if (tDB.getCommonName() != null) try {
1383          removeName(conn, helper, tDB, "common name", tDB.getCommonName());
1384        } catch (BioException exc1) {
1385          exc1.printStackTrace();
1386        } catch (SQLException exc) {
1387          exc.printStackTrace();
1388        }
1389        
1390        // annotation
1391        Annotation annoFA = tFA.getAnnotation();
1392        Annotation annoDB = tDB.getAnnotation();
1393        // rank
1394        if (annoFA.containsProperty("rank")) {
1395          if (!annoDB.containsProperty("rank")) change = true;
1396          else if (!annoDB.getProperty("rank").equals(annoFA.getProperty("rank"))) change = true;
1397          if (change) setRank(conn, tDB, annoFA.getProperty("rank").toString());
1398          change = false;
1399        } else if (annoDB.containsProperty("rank")) 
1400          removeRank(conn, helper, tDB);
1401        // genetic code id
1402        if (annoFA.containsProperty("genetic code id")) {
1403          if (!annoDB.containsProperty("genetic code id")) change = true;
1404          else if (!annoDB.getProperty("genetic code id").equals(annoFA)) change = true;
1405          if (change) setGeneticCodeID(conn, tDB, Integer.parseInt(annoFA.getProperty("genetic code id").toString()));
1406          change = false;
1407        } else if (annoDB.containsProperty("genetic code id"))
1408          removeGeneticCodeID(conn, helper, tDB);
1409        // mitochondrial genetic code id
1410        if (annoFA.containsProperty("mitochondrial genetic code id")) {
1411          if (!annoDB.containsProperty("mitochondrial genetic code id")) change = true;
1412          else if (!annoDB.getProperty("mitochondrial genetic code id").equals(annoFA.getProperty("mitochondrial genetic code id"))) change = true;
1413          if (change) setMitochondrialGeneticCodeID(conn, tDB, Integer.parseInt(annoFA.getProperty("mitochondrial genetic code id").toString()));
1414          change = false;
1415        } else if (annoDB.containsProperty("mitochondrial genetic code id"))
1416          removeMitochondrialGeneticCodeID(conn, helper, tDB);
1417        // left value
1418        if (annoFA.containsProperty("left value")) {
1419          if (!annoDB.containsProperty("left value")) change = true;
1420          else if (!annoDB.getProperty("left value").equals(annoFA.getProperty("left value"))) change = true;
1421          if (change) setLeftValue(conn, tDB, Integer.parseInt(annoFA.getProperty("left value").toString()));
1422          change = false;
1423        } else if (annoDB.containsProperty("left value"))
1424          removeLeftValue(conn, helper, tDB);
1425        // right value
1426        if (annoFA.containsProperty("right value")) {
1427          if (!annoDB.containsProperty("right value")) change = true;
1428          else if (!annoDB.getProperty("right value").equals(annoFA.getProperty("right value"))) change = true;
1429          if (change) setRightValue(conn, tDB, Integer.parseInt(annoFA.getProperty("right value").toString()));
1430          change = false;
1431        } else if (annoDB.containsProperty("right value"))
1432          removeRightValue(conn, helper, tDB);
1433        // other names:
1434        if (annoFA.containsProperty(EbiFormat.PROPERTY_TAXON_NAMES)) {
1435          if (annoFA.getProperty(EbiFormat.PROPERTY_TAXON_NAMES) instanceof Map) {
1436            Map names = (Map) annoFA.getProperty(EbiFormat.PROPERTY_TAXON_NAMES);
1437            Map dbNames = (Map) annoDB.getProperty(EbiFormat.PROPERTY_TAXON_NAMES);       
1438          
1439          /*
1440           * delete old names, if not valid anymore.
1441           */
1442          for (Iterator j=dbNames.keySet().iterator(); j.hasNext(); ) {
1443            String key = j.next().toString();
1444            if (dbNames.get(key) instanceof Set) {
1445              if (!names.containsKey(key)) // delete everything 
1446                for (Iterator k=((Set) dbNames.get(key)).iterator(); k.hasNext(); ) try {
1447                  String elem = k.next().toString();
1448                  removeName(conn, helper, tDB, key, elem);
1449                } catch (BioException exc2) {
1450                  exc2.printStackTrace();
1451                } catch (SQLException exc) {
1452                  exc.printStackTrace();
1453                } 
1454              // this key is still in the new taxon.
1455              else for (Iterator k=((Set) dbNames.get(key)).iterator(); k.hasNext(); ) {
1456                String elem = k.next().toString();
1457                // many entries for this key in the new taxon.
1458                if (names.get(key) instanceof Set) {
1459                  if (!((Set) names.get(key)).contains(elem)) try {
1460                    removeName(conn, helper, tDB, key, elem);
1461                  } catch (BioException exc3) {
1462                    exc3.printStackTrace();
1463                  } catch (SQLException exc) {
1464                    exc.printStackTrace();
1465                  }
1466                 
1467                // in the new taxon only one entry for this key
1468                } else if (!names.get(key).equals(elem)) try {
1469                  removeName(conn, helper, tDB, key, elem);
1470                } catch (BioException exc2) {
1471                  exc2.printStackTrace();
1472                } catch (SQLException exc) {
1473                  exc.printStackTrace();
1474                }
1475              }
1476            } else { // only one entry in the new animal
1477              if (!names.containsKey(key)) change = true; 
1478              // many entries in the new animal.
1479              else if (names.get(key) instanceof Set) {
1480                if (!((Set) names.get(key)).contains(dbNames.get(key))) change = true;
1481              // only one entry in the new animal (what ever)
1482              } else if (!names.get(key).equals(dbNames.get(key))) change = true;
1483              if (change) try { 
1484                removeName(conn, helper, tDB, key, dbNames.get(key).toString());
1485              } catch (BioException exc2) {
1486                exc2.printStackTrace();
1487              }
1488              change = false;
1489            }
1490          }
1491          // update:
1492          dbNames = (Map) getTaxon(conn, getRealScientificName(tDB)).getAnnotation().getProperty(EbiFormat.PROPERTY_TAXON_NAMES);
1493          
1494          /*
1495           * New names.
1496           */
1497          for (Iterator j=names.keySet().iterator(); j.hasNext(); ) {
1498            String key = j.next().toString();
1499            // there're multiple names for this key
1500            if (names.get(key) instanceof Set) {
1501              for (Iterator k=((Set) names.get(key)).iterator(); k.hasNext(); ) {
1502                String elem = k.next().toString();
1503                if (!dbNames.containsKey(key)) change = true;
1504                else if (dbNames.get(key) instanceof Set) {
1505                  if (!((Set) dbNames.get(key)).contains(elem)) change = true;
1506                } else if (!dbNames.get(key).equals(elem)) change = true; 
1507                if (change) try {
1508                  addName(conn, tDB, key, elem);
1509                } catch (BioRuntimeException exc3) {
1510                  exc3.printStackTrace();
1511                } catch (BioException exc3) {
1512                  exc3.printStackTrace();
1513                } catch (SQLException exc) {
1514                  exc.printStackTrace();
1515                }
1516                change = false;
1517              }
1518            // there's only one name for this key.
1519            } else if (!dbNames.containsKey(key)) try {
1520                addName(conn, tDB, key, names.get(key).toString());
1521              } catch (BioRuntimeException exc2) {
1522                exc2.printStackTrace();
1523              } catch (BioException exc2) {
1524                exc2.printStackTrace();
1525              } catch (SQLException exc) {
1526                exc.printStackTrace();
1527              }
1528            }
1529          }
1530        }
1531      }
1532    } catch (NumberFormatException exc) {
1533      exc.printStackTrace();
1534    } catch (BioRuntimeException exc) {
1535      exc.printStackTrace();
1536    } catch (NoSuchElementException exc) {
1537      exc.printStackTrace();
1538    } catch (SQLException exc) {
1539      exc.printStackTrace();
1540    }
1541  }
1542
1543  
1544}