package com.compomics.util.experiment.identification.protein_inference.proteintree;
import com.compomics.util.Util;
import com.compomics.util.db.DerbyUtil;
import com.compomics.util.db.ObjectsCache;
import com.compomics.util.db.ObjectsDB;
import com.compomics.util.experiment.identification.protein_sequences.SequenceFactory;
import com.compomics.util.preferences.UtilitiesUserPreferences;
import com.compomics.util.waiting.WaitingHandler;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/**
* This factory stores and returns protein trees components from databases.
*
* @author Marc Vaudel
*/
public class ProteinTreeComponentsFactory {
/**
* Instance of the sequence factory.
*/
private SequenceFactory sequenceFactory = SequenceFactory.getInstance();
/**
* Instance of the factory.
*/
private static ProteinTreeComponentsFactory instance = null;
/**
* The folder containing the databases.
*/
private static String defaultDbFolderPath = System.getProperty("user.home") + "/.compomics/proteins/indexes/";
/**
* The objects db used to retrieve saved nodes.
*/
private ObjectsDB objectsDB;
/**
* The cache of the objectsDB.
*/
private ObjectsCache objectsCache = new ObjectsCache();
/**
* The splitter in the key between database name and database version.
*/
public static final String folderSeparator = "_cus_";
/**
* The name of the db to use.
*/
public static final String dbName = "proteinTree";
/**
* The name of the node table.
*/
private static final String nodeTable = "nodes";
/**
* The name of the protein length table.
*/
private static final String parametersTable = "parameters";
/**
* List of all tags in tree.
*/
private HashSet<String> tagsInTree = null;
/**
* Constructor.
*/
private ProteinTreeComponentsFactory() throws IOException {
objectsCache.setAutomatedMemoryManagement(false); // Change this to true if large objects are stored
objectsCache.setCacheSize(1000);
objectsCache.setBatchSize(100); // @TODO: why 100 and not higher?
}
/**
* Static method returning the instance of the factory. Note: the
* serialization folder should have been already set.
*
* @return the instance of the factory
* @throws IOException if an IOException occurs
*/
public static ProteinTreeComponentsFactory getInstance() throws IOException {
if (instance == null) {
instance = new ProteinTreeComponentsFactory();
}
return instance;
}
/**
* Initiates the connection to the database and indicates whether the
* corresponding folder is already created.
*
* @return a boolean indicating whether the database folder is already
* created
*
* @throws SQLException exception thrown whenever an error occurs while
* interacting with the database
* @throws IOException exception thrown whenever an error occurs while
* reading or writing a file
* @throws ClassNotFoundException exception thrown whenever an error
* occurred while deserializing a file from the database
* @throws InterruptedException exception thrown if a threading error occurs
* while interacting with the database
*/
public boolean initiate() throws SQLException, IOException, ClassNotFoundException, InterruptedException {
File dbFolder = getDbFolder();
boolean exists = true;
if (!dbFolder.exists()) {
exists = false;
if (!dbFolder.mkdir()) {
throw new IOException("Impossible to create database folder " + dbFolder.getAbsolutePath() + ".");
}
}
objectsDB = new ObjectsDB(dbFolder.getAbsolutePath(), dbName, false, objectsCache);
if (!exists) {
objectsDB.addTable(nodeTable);
objectsDB.addTable(parametersTable);
}
return exists;
}
/**
* Sets the currently loaded database as corrupted and tries to delete it.
*
* @return true if deletion was successful
*
* @throws java.io.IOException exception thrown whenever an error occurs
* while interacting with a file
* @throws java.sql.SQLException exception thrown whenever an error occurs
* while interacting with the database
* @throws java.lang.InterruptedException exception thrown whenever a
* threading error occurs while attempting to delete the database file
*/
public boolean delete() throws IOException, SQLException, InterruptedException {
boolean success;
try {
if (!isCorrupted()) {
setCorrupted(true);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
close();
} catch (Exception e) {
e.printStackTrace();
}
try {
DerbyUtil.closeConnection();
} catch (Exception e) {
e.printStackTrace();
}
File dbFolder = getDbFolder();
success = Util.deleteDir(dbFolder);
return success;
}
/**
* Closes the factory, closes all connection and deletes the file.
*
* @throws IOException if an IOException occurs
* @throws SQLException exception thrown if closing the db failed
*/
public void close() throws IOException, SQLException {
if (objectsDB != null) {
objectsDB.close();
objectsCache = new ObjectsCache();
}
}
/**
* Returns the folder name where to store information about the protein
* sequence database loaded in the sequence factory.
*
* @return the folder name where to store information about the protein
* sequence database loaded in the sequence factory
*/
public String getDbFolderName() {
return sequenceFactory.getFileName() + folderSeparator + sequenceFactory.getCurrentFastaIndex().getLastModified();
}
/**
* Returns the folder where the db in the sequence factory is stored.
*
* @return the folder where the db in the sequence factory is stored
* @throws IOException if an IOException occurs
*/
public File getDbFolder() throws IOException {
UtilitiesUserPreferences utilitiesUserPreferences = UtilitiesUserPreferences.loadUserPreferences();
File folder = utilitiesUserPreferences.getProteinTreeFolder();
if (!folder.exists()) {
boolean success = folder.mkdirs();
if (!success) {
throw new IOException("Unable to create database folder " + folder + ".");
}
}
return new File(folder, getDbFolderName());
}
/**
* Adds a node to the database.
*
* @param tag the tag referring to the node of interest
* @param node the node
* @throws SQLException exception thrown whenever an error occurred while
* loading data in the database
* @throws IOException exception thrown whenever an error occurred while
* loading data in the database
* @throws InterruptedException if an InterruptedException occurs
*/
public void saveNode(String tag, Node node) throws SQLException, IOException, InterruptedException {
objectsDB.insertObject(nodeTable, tag, node, false);
}
/**
* Adds nodes to the database.
*
* @param nodes map of the nodes
* @param waitingHandler the waiting handler
*
* @throws SQLException exception thrown whenever an error occurred while
* loading data in the database
* @throws IOException exception thrown whenever an error occurred while
* loading data in the database
*/
public void saveNodes(HashMap<String, Object> nodes, WaitingHandler waitingHandler) throws SQLException, IOException {
objectsDB.insertObjects(nodeTable, nodes, waitingHandler, true);
}
/**
* Retrieves the node of the given tag.
*
* @param tag the tag of interest
* @return the node at this tag
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public Node getNode(String tag) throws SQLException, ClassNotFoundException, IOException, InterruptedException {
if (tagsInTree != null && !tagsInTree.contains(tag)) {
return null;
}
Node result = (Node) objectsDB.retrieveObject(nodeTable, tag, true, false);
if (tagsInTree != null && result == null) {
throw new IllegalArgumentException(tag + " not found in database.");
}
return result;
}
/**
* Loads nodes in the cache.
*
* @param tags list of tags corresponding to the nodes to load
* @param waitingHandler the waiting handler allowing displaying progress and cancelling the process
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void loadNodes(ArrayList<String> tags, WaitingHandler waitingHandler) throws SQLException, IOException, ClassNotFoundException, InterruptedException {
objectsDB.loadObjects(nodeTable, tags, waitingHandler, false);
}
/**
* Saves the initial tag size in the parameters table of the DB.
*
* @param size the initial tag size
*
* @throws IOException if an IOException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void saveInitialSize(int size) throws SQLException, IOException, InterruptedException {
objectsDB.insertObject(parametersTable, "initialSize", size, false);
}
/**
* Retrieves the initial tag size from the db.
*
* @return the initial tag size
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public Integer getInitialSize() throws SQLException, IOException, ClassNotFoundException, InterruptedException {
return (Integer) objectsDB.retrieveObject(parametersTable, "initialSize", true);
}
/**
* Loads all tree parameters.
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void loadParameters() throws SQLException, ClassNotFoundException, IOException, InterruptedException {
objectsDB.loadObjects(parametersTable, null, false);
}
/**
* Sets whether the import was completed.
*
* @param completed whether the import was completed
* @throws IOException if an IOException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void setImportComplete(boolean completed) throws SQLException, IOException, InterruptedException {
objectsDB.insertObject(parametersTable, "importComplete", completed, false);
}
/**
* Returns a boolean indicating whether the import was complete. False if
* not set.
*
* @return true if the import was complete
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public boolean importComplete() throws SQLException, IOException, ClassNotFoundException, InterruptedException {
Boolean result = (Boolean) objectsDB.retrieveObject(parametersTable, "importComplete", true);
if (result == null) {
return false;
} else {
return result;
}
}
/**
* Sets whether the database is corrupted.
*
* @param corrupted whether the database is corrupted
* @throws IOException if an IOException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void setCorrupted(boolean corrupted) throws SQLException, IOException, InterruptedException {
objectsDB.insertObject(parametersTable, "corrupted", corrupted, false);
}
/**
* Returns a boolean indicating whether the database is corrupted. False if
* not set.
*
* @return true if the database is corrupted
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public boolean isCorrupted() throws SQLException, IOException, ClassNotFoundException, InterruptedException {
Boolean result = (Boolean) objectsDB.retrieveObject(parametersTable, "corrupted", true);
if (result == null) {
return false;
} else {
return result;
}
}
/**
* Sets the version.
*
* @param version the version
* @throws IOException if an IOException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void setVersion(String version) throws SQLException, IOException, InterruptedException {
objectsDB.insertObject(parametersTable, "version", version, false);
}
/**
* Returns the version. Null if not set.
*
* @param objectsDB the objects db to look into
* @return the version
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public static String getVersion(ObjectsDB objectsDB) throws SQLException, IOException, ClassNotFoundException, InterruptedException {
return (String) objectsDB.retrieveObject(parametersTable, "version", true);
}
/**
* Returns the version. Null if not set.
*
* @return the version
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public String getVersion() throws SQLException, IOException, ClassNotFoundException, InterruptedException {
return getVersion(objectsDB);
}
/**
* Sets the FASTA file path.
*
* @param fastaFilePath the FASTA file path
*
* @throws IOException if an IOException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public void setFastaFilePath(String fastaFilePath) throws SQLException, IOException, InterruptedException {
objectsDB.insertObject(parametersTable, "fastaFile", fastaFilePath, false);
}
/**
* Returns the FASTA file path.
*
* @return the FASTA file path
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public String getFastaFilePath() throws SQLException, IOException, ClassNotFoundException, InterruptedException {
return getFastaFilePath(objectsDB);
}
/**
* Returns the FASTA file path.
*
* @param objectsDB the objects DB to look into
*
* @return the FASTA file path
*
* @throws IOException if an IOException occurs
* @throws ClassNotFoundException if a ClassNotFoundException occurs
* @throws InterruptedException if an InterruptedException occurs
* @throws SQLException if an SQLException occurs
*/
public static String getFastaFilePath(ObjectsDB objectsDB) throws SQLException, IOException, ClassNotFoundException, InterruptedException {
return (String) objectsDB.retrieveObject(parametersTable, "fastaFile", true);
}
/**
* Loads the tags implemented in the database.
*
* @throws SQLException if an SQLException occurs
*/
public void loadTags() throws SQLException {
tagsInTree = objectsDB.tableContentAsSet(nodeTable);
}
/**
* Returns the default folder to use when storing the trees.
*
* @return the default folder to use when storing the trees
*/
public static String getDefaultDbFolderPath() {
return defaultDbFolderPath;
}
/**
* Sets the default folder to use when storing the trees.
*
* @param defaultDbFolderPath the default folder to use when storing the
* trees
*/
public static void setDefaultDbFolderPath(String defaultDbFolderPath) {
ProteinTreeComponentsFactory.defaultDbFolderPath = defaultDbFolderPath;
}
/**
* Returns the cache used to store the nodes.
*
* @return the cache used to store the nodes
*/
public ObjectsCache getCache() {
return objectsCache;
}
/**
* Deletes the outdated trees.
*
* @throws IOException if an IOException occurs
*/
public static void deletOutdatedTrees() throws IOException {
UtilitiesUserPreferences utilitiesUserPreferences = UtilitiesUserPreferences.loadUserPreferences();
File folder = utilitiesUserPreferences.getProteinTreeFolder();
if (folder.exists()) {
for (File dbFolder : folder.listFiles()) {
if (dbFolder.isDirectory() && dbFolder.getName().contains(folderSeparator)) {
try {
ObjectsCache tempCache = new ObjectsCache();
ObjectsDB objectsDB = new ObjectsDB(dbFolder.getAbsolutePath(), dbName, false, tempCache);
boolean upToDate = true;
try {
String version = getVersion(objectsDB);
if (version != null && version.equals(ProteinTree.version)) {
String fastaFilePath = getFastaFilePath(objectsDB);
if (fastaFilePath != null) {
File fastaFile = new File(fastaFilePath);
if (!fastaFile.exists()) { //@TODO: check if the drive is available
upToDate = false;
}
} else {
upToDate = false;
}
} else {
upToDate = false;
}
} catch (Exception e) {
upToDate = false;
}
objectsDB.close();
//@TODO: is it sufficient to unlock it?
if (!upToDate) {
DerbyUtil.closeConnection(); //TODO: Restore connections?
Util.deleteDir(folder);
}
} catch (Exception e) {
// Possibly not a tree, skip
}
}
}
}
}
}