/**
* Copyright Copyright 2010-15 Simon Andrews
*
* This file is part of BamQC.
*
* BamQC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* BamQC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with BamQC; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Changelog:
* - Piero Dalle Pezze: Imported from SeqMonk and adjusted for BamQC
* - Simon Andrews: Class creation.
*/
package uk.ac.babraham.BamQC.Preferences;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.commons.lang3.SystemUtils;
import org.apache.log4j.Logger;
/**
* A set of preferences, both temporary and permanent which are used
* throughout BamQC. Permanent preferences can be loaded from and
* saved to a preferences file allowing persistence between sessions.
* @author Simon Andrews
* @author Piero Dalle Pezze
*/
public class BamQCPreferences {
private static Logger log = Logger.getLogger(BamQCPreferences.class);
/** The single instantiated instance of preferences */
private static BamQCPreferences p = new BamQCPreferences();
/** A list of annotation types not to load */
private HashSet<String> ignoredAnnotations = new HashSet<String>();
/** The directory under which to look for genome files */
private File genomeBase = null;
/** The default save location. */
private File saveLocation = null;
/** The last used save location. */
private File lastUsedSaveLocation = null;
/** The preferences file. */
private File preferencesFile = null;
/** The network address from where we can download new genomes */
private String genomeDownloadLocation = "http://www.bioinformatics.babraham.ac.uk/seqmonk/genomes/";
/** Whether we're using a network proxy */
private boolean useProxy = false;
/** The proxy host. */
private String proxyHost = "";
/** The proxy port. */
private int proxyPort = 0;
/** The recently opened files list */
private LinkedList<String> recentlyOpenedFiles = new LinkedList<String>();
/**
* Instantiates a preferences object. Only ever called once from inside this
* class. External access is via the getInstnace() method.
*/
private BamQCPreferences () {
// // This code uses apache commons.lang3. If you don't want to use it, you can edit this code and use the commented code just below.
// if(SystemUtils.IS_OS_MAC_OSX) {
// // let's store these files inside a folder BamQC
//// preferencesFile= new File(System.getProperty("user.home") + File.separator + "Library" + File.separator + "BamQC" + File.separator + "bamqc_prefs.txt");
// saveLocation = new File(System.getProperty("user.home") + File.separator + "Library" + File.separator + "BamQC" + File.separator + "BamQC_files");
// genomeBase = new File(saveLocation.getAbsolutePath() + File.separator + "genomes");
// } else if (SystemUtils.IS_OS_WINDOWS) {
// // let's store these files inside a folder BamQC
//// preferencesFile= new File(System.getProperty("user.home") + File.separator + "bamqc" + File.separator + "bamqc_prefs.txt");
// saveLocation = new File(System.getProperty("user.home") + File.separator + "bamqc" + File.separator + "BamQC_files");
// genomeBase = new File(saveLocation.getAbsolutePath() + File.separator + "genomes");
// } else if(SystemUtils.IS_OS_UNIX) {
// // let's store these files as hidden files inside a folder .bamqc
//// preferencesFile= new File(System.getProperty("user.home") + File.separator + ".bamqc" + File.separator + "bamqc_prefs.txt");
// saveLocation = new File(System.getProperty("user.home") + File.separator + ".bamqc" + File.separator + "BamQC_files");
// genomeBase = new File(saveLocation.getAbsolutePath() + File.separator + "genomes");
// } else {
// // let's store these files explicitly inside a folder bamqc
//// preferencesFile= new File(System.getProperty("user.home") + File.separator + "bamqc" + File.separator + "bamqc_prefs.txt");
// saveLocation = new File(System.getProperty("user.home") + File.separator + "bamqc" + File.separator + "BamQC_files");
// genomeBase = new File(saveLocation.getAbsolutePath() + File.separator + "genomes");
// }
// Code independent of apache commons.lang3
saveLocation = new File(System.getProperty("user.home") + File.separator + File.separator + "BamQC_files");
genomeBase = new File(saveLocation.getAbsolutePath() + File.separator + "genomes");
// This is left outside for now.
preferencesFile= new File(System.getProperty("user.home") + File.separator + "bamqc_prefs.txt");
new File(genomeBase.getAbsolutePath()).mkdirs();
new File(saveLocation.getAbsolutePath()).mkdirs();
if (preferencesFile!=null && preferencesFile.exists()) {
// System.out.println("Loading preferences from file...");
loadPreferences();
}
else {
ignoredAnnotations.add("source");
ignoredAnnotations.add("exon");
ignoredAnnotations.add("sts");
ignoredAnnotations.add("misc_feature");
try {
savePreferences();
}
catch (IOException e) {
log.error(e, e);
}
}
updateProxyInfo();
}
/**
* Load preferences from a saved file
*/
private void loadPreferences () {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(preferencesFile));
String line;
String [] sections;
while ((line = br.readLine())!= null) {
if (line.startsWith("#")) continue; // It's a comment
sections = line.split("\\t",-1);
if (sections[0].equals("GenomeBase")) {
/*
* We used to save the genome base even if it was the same
* as the default genome directory in the install. To
* make life easier if the install location moves we now
* only store this if the location is something other than
* the default.
*/
File defaultGenomesLocation = getGenomeBase();
File customBase = new File(sections[1]);
if (! customBase.equals(defaultGenomesLocation)) {
genomeBase = customBase;
}
}
else if (sections[0].equals("SaveLocation")) {
saveLocation = new File(sections[1]);
}
else if (sections[0].equals("GenomeDownloadLocation")) {
if (sections[1].equals("http://www.bioinformatics.bbsrc.ac.uk/chipmonk/genomes/")) {
genomeDownloadLocation = "http://www.bioinformatics.babraham.ac.uk/seqmonk/genomes/";
}
else {
genomeDownloadLocation = sections[1];
}
}
else if (sections[0].equals("Proxy")) {
proxyHost = sections[1];
if (sections.length>2 && sections[2].length()>0) {
proxyPort = Integer.parseInt(sections[2]);
}
if (proxyHost.length()>0) {
useProxy = true;
}
}
else if (sections[0].equals("RecentFile")) {
File f = new File(sections[1]);
if (f.exists()) {
recentlyOpenedFiles.add(sections[1]);
}
}
else if (sections[0].equals("LoadedFeatures")) {
// This was present in old versions of the preferences
// file and is no longer used. We'll substitute in
// the default set of ignored values
ignoredAnnotations.add("source");
ignoredAnnotations.add("exon");
ignoredAnnotations.add("sts");
ignoredAnnotations.add("misc_feature");
}
else if (sections[0].equals("IgnoredFeatures")) {
for (int i=1;i<sections.length;i++) {
ignoredAnnotations.add(sections[i]);
}
}
else {
log.error("Unknown preference '"+sections[0]+"'");
}
}
br.close();
} catch (FileNotFoundException e) {
log.error(e, e);
}
catch (IOException e) {
log.error(e, e);
}
}
/**
* Save preferences.
*
* @throws IOException
*/
public void savePreferences() throws IOException {
PrintWriter p = new PrintWriter(new FileWriter(preferencesFile));
p.println("# BamQC Preferences file. Do not edit by hand.");
// First write out the GenomeBase if they've specified a custom location
if (genomeBase != null) {
p.println("GenomeBase\t"+genomeBase.getAbsolutePath());
}
// Then the saveLocation
p.println("SaveLocation\t"+saveLocation.getAbsolutePath());
// Then the proxy information
p.println("Proxy\t"+proxyHost+"\t"+proxyPort);
// The genome download URL
p.println("GenomeDownloadLocation\t"+genomeDownloadLocation);
// Save the recently opened file list
Iterator<String> rof = recentlyOpenedFiles.iterator();
while (rof.hasNext()) {
p.println("RecentFile\t"+rof.next());
}
// Finally the list of features to load
StringBuilder b = new StringBuilder("IgnoredFeatures");
Iterator<String> i = ignoredAnnotations.iterator();
while (i.hasNext()) {
b.append("\t");
b.append(i.next().toLowerCase());
}
p.println(b);
p.close();
}
/**
* Gets the single instance of BamQCPreferences.
*
* @return single instance of BamQCPreferences
*/
public static BamQCPreferences getInstance () {
return p;
}
/**
* Gets the list of recently opened files.
*
* @return the recently opened files
*/
public String [] getRecentlyOpenedFiles () {
return recentlyOpenedFiles.toArray(new String[0]);
}
/**
* Adds a path to the recently opened files list. We store
* up to 5 recently used files on a rotating basis. Adding
* a new one pushes out the oldest one.
*
* @param filePath The new file location to add
*/
public void addRecentlyOpenedFile (String filePath) {
// I know this is inefficient in a linked list but
// it's only going to contain 5 elements so who cares
if (recentlyOpenedFiles.contains(filePath)) {
recentlyOpenedFiles.remove(filePath);
}
recentlyOpenedFiles.add(0, filePath);
// Only keep 9 items
while (recentlyOpenedFiles.size() > 9) {
recentlyOpenedFiles.remove(9);
}
try {
savePreferences();
}
catch (IOException e) {
// In this case we don't report this error since
// the user isn't explicitly asking us to save.
}
}
/**
* Asks whether a particular type of annotation is on the list
* of types to exclude from loading.
*
* @param type The annotation type to check
* @return true, if this type should be loaded
*/
public boolean loadAnnotation (String type) {
return ! ignoredAnnotations.contains(type.toLowerCase());
}
/**
* Gets a list of feature types which will not be loaded
*
* @return a list of ignored feature types
*/
public String [] getIgnoredFeatures () {
return ignoredAnnotations.toArray(new String [0]);
}
/**
* Flag to say if network access should go through a proxy
*
* @return true, if a proxy should be used
*/
public boolean useProxy () {
return useProxy;
}
/**
* Proxy host.
*
* @return The name of the proxy to use. Only use this if the
* useProxy flag is set.
*/
public String proxyHost () {
return proxyHost;
}
/**
* Proxy port.
*
* @return The port to access the proxy on. Only use this if the
* useProxy flag is set
*/
public int proxyPort () {
return proxyPort;
}
/**
* Sets proxy information
*
* @param host The name of the proxy
* @param port The port to access the proxy on
*/
public void setProxy (String host, int port) {
proxyHost = host;
proxyPort = port;
updateProxyInfo();
}
/**
* Sets the genome download location.
*
* @param url The URL under which new genomes can be downloaded
*/
public void setGenomeDownloadLocation (String url) {
genomeDownloadLocation = url;
}
/**
* Custom genome base used.
*
* @return true, a non-default genome folder has been selected
*/
public boolean customGenomeBaseUsed () {
/**
* Says whether the user has specified a genome base location
* of if we're using the default one.
*/
// genomeBase will be null if we're using the default
return genomeBase != null;
}
/**
* Gets the genome base.
*
* @return The folder under which genomes are stored
* @throws FileNotFoundException
*/
public File getGenomeBase () throws FileNotFoundException {
/*
* This method returns a file which represents the directory
* under which the genomes are stored. If a custom location
* has not been specified then the default Genomes folder in
* the install dir is returned. If that can't be found then
* a FileNotFound exception is thrown
*
* If a custom location has been set then this is returned
* regardless of it it exists or can be used.
*/
File f;
if (genomeBase == null) {
// Check for the default genomes folder. This should always be present, but
// you can't be too careful!
try {
f = new File(ClassLoader.getSystemResource("Genomes").getFile().replaceAll("%20"," "));
}
catch (NullPointerException npe) {
throw new FileNotFoundException("Couldn't find default Genomes folder");
}
}
else {
f = genomeBase;
}
return f;
}
/**
* Gets the genome download location.
*
* @return The URL under which new genomes can be downloaded
*/
public String getGenomeDownloadLocation () {
return genomeDownloadLocation;
}
/**
* Sets the genome base location
*
* @param f The folder under which new genomes should be stored
*/
public void setGenomeBase (File f) {
// If this file is the same as the default then
// we leave the default in place
try {
if (!f.equals(getGenomeBase())) {
genomeBase = f;
}
}
catch (FileNotFoundException e) {
genomeBase = f;
}
}
/**
* Gets the default save location for projects / images / reports etc.
* This will initially be the location in the preferences file but will
* be updated during use to reflect the last actually used location.
*
* @return The default save location.
*/
public File getSaveLocation () {
if (lastUsedSaveLocation != null) return lastUsedSaveLocation;
return saveLocation;
}
/**
* Gets the default save location from the preferences file. This value
* will always match the preferences file and will not update to reflect
* actual usage within the current session.
*
* @return The default save location
*/
public File getSaveLocationPreference() {
/**
* Always returns the save location saved in the preferences file. Used by
* the preferences editing dialog. Everywhere else should use getSaveLocation()
*/
return saveLocation;
}
/**
* Sets the save location to record in the preferences file
*
* @param f The new save location
*/
public void setSaveLocation (File f) {
saveLocation = f;
}
/**
* Sets the last used save location. This is a temporary setting and will
* not be recorded in the preferences file.
*
* @param f The new last used save location
*/
public void setLastUsedSaveLocation (File f) {
if (f.isDirectory()) {
lastUsedSaveLocation = f;
}
else {
lastUsedSaveLocation = f.getParentFile();
}
}
/**
* Sets the list of ignored feature types
*
* @param s The new list of ignored feature types
*/
public void setIgnoredFeatures (String [] s) {
ignoredAnnotations = new HashSet<String>(s.length);
for (int i=0;i<s.length;i++) {
ignoredAnnotations.add(s[i]);
}
}
/**
* Applies the stored proxy information to the environment of the
* current session so it is picked up automatically by any network
* calls made within the program. No further configuration is
* required within classes requiring network access.
*/
private void updateProxyInfo () {
if (useProxy) {
System.getProperties().put("proxySet","true");
System.getProperties().put("proxyHost",proxyHost);
System.getProperties().put("proxyPort",""+proxyPort);
}
else {
System.getProperties().put("proxySet","false");
}
}
}