/*******************************************************************************
* GenPlay, Einstein Genome Analyzer
* Copyright (C) 2009, 2014 Albert Einstein College of Medicine
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
* Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu>
* Nicolas Fourel <nicolas.fourel@einstein.yu.edu>
* Eric Bouhassira <eric.bouhassira@einstein.yu.edu>
*
* Website: <http://genplay.einstein.yu.edu>
******************************************************************************/
package edu.yu.einstein.genplay.util;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.filechooser.FileFilter;
import edu.yu.einstein.genplay.core.manager.project.ProjectChromosomes;
import edu.yu.einstein.genplay.core.manager.project.ProjectManager;
import edu.yu.einstein.genplay.dataStructure.chromosome.Chromosome;
import edu.yu.einstein.genplay.dataStructure.enums.GeneScoreType;
import edu.yu.einstein.genplay.dataStructure.enums.LogBase;
import edu.yu.einstein.genplay.dataStructure.enums.ScoreOperation;
import edu.yu.einstein.genplay.gui.clipboard.LocalClipboard;
import edu.yu.einstein.genplay.gui.dialog.chromosomeChooser.ChromosomeChooserDialog;
import edu.yu.einstein.genplay.gui.fileFilter.AllTrackFileFilter;
import edu.yu.einstein.genplay.gui.fileFilter.BAMFilter;
import edu.yu.einstein.genplay.gui.fileFilter.BedFilter;
import edu.yu.einstein.genplay.gui.fileFilter.BedGraphFilter;
import edu.yu.einstein.genplay.gui.fileFilter.BedGraphWith0Filter;
import edu.yu.einstein.genplay.gui.fileFilter.ElandExtendedFilter;
import edu.yu.einstein.genplay.gui.fileFilter.ExtendedFileFilter;
import edu.yu.einstein.genplay.gui.fileFilter.GFFFilter;
import edu.yu.einstein.genplay.gui.fileFilter.GTFFilter;
import edu.yu.einstein.genplay.gui.fileFilter.GenPlayTrackFilter;
import edu.yu.einstein.genplay.gui.fileFilter.PSLFilter;
import edu.yu.einstein.genplay.gui.fileFilter.PairFilter;
import edu.yu.einstein.genplay.gui.fileFilter.SAMFilter;
import edu.yu.einstein.genplay.gui.fileFilter.TwoBitFilter;
import edu.yu.einstein.genplay.gui.fileFilter.WiggleFilter;
import edu.yu.einstein.genplay.gui.track.Track;
import edu.yu.einstein.genplay.gui.track.layer.Layer;
import edu.yu.einstein.genplay.gui.track.layer.LayerType;
/**
* Collection of static methods used in this project
* @author Julien Lajugie
* @author Nicolas Fourel
*/
public final class Utils {
/**
* The increment unit for all {@link JScrollPane}
*/
public final static int SCROLL_INCREMENT_UNIT = 20;
/**
* Checks if the specified {@link File} name ends with one of the specified extensions.
* If not adds the first specified extension to the file name.
* @param file a file
* @param extensions file extensions
* @return a File with the specified extension
*/
public final static File addExtension(File file, String... extensions) {
String currentExtension = getExtension(file);
boolean specifedExtensionsFound = false;
// if there is no extension specified we return the input file
if (extensions == null) {
return file;
}
// if the current file has an extension we check if it's one of the specified extension
if (currentExtension != null) {
int i = 0;
while ((i < extensions.length) && !specifedExtensionsFound) {
if (currentExtension.equalsIgnoreCase(extensions[i])) {
specifedExtensionsFound = true;
}
i++;
}
}
// if we didn't find one of the specified extensions we return
// a new file having the name of the input file concatenated
// with the first specified extension
if (!specifedExtensionsFound) {
String newFile = file.getPath() + "." + extensions[0];
return new File(newFile);
} else {
return file;
}
}
/**
* Asks if the user wants to replace a file if this file already exists.
* @param parentComponent determines the Frame in which the dialog is displayed; if null, or if the parentComponent has no Frame, a default Frame is used
* @param f A file.
* @return True if the user wants to cancel. False otherwise.
*/
public final static boolean cancelBecauseFileExist(Component parentComponent, File f) {
if (f.exists()) {
int res = JOptionPane.showConfirmDialog(parentComponent, "The file " + f.getName() + " already exists. Do you want to replace the existing file?", "File already exists", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null);
if (res == JOptionPane.NO_OPTION) {
return true;
}
}
f.delete();
return false;
}
/**
* Opens a dialog box asking the user to choose some chromosomes among all the chromosomes available for the project
* @param parentComponent determines the Frame in which the dialog is displayed; if null, or if the parentComponent has no Frame, a default Frame is used
* @return an array of boolean with a length equal to the number of chromosome in the project.
* The elements of the array are set to true if selected or false otherwise.
*/
public final static boolean[] chooseChromosomes(Component parentComponent) {
ProjectChromosomes projectChromosomes = ProjectManager.getInstance().getProjectChromosomes();
List<Chromosome> chromosomeList = projectChromosomes.getChromosomeList();
Collections.sort(chromosomeList);
ChromosomeChooserDialog chromoChooser = new ChromosomeChooserDialog();
chromoChooser.setFullChromosomeList(chromosomeList);
chromoChooser.setSelectedChromosomeList(chromosomeList);
chromoChooser.setOrdering(false);
if (chromoChooser.showDialog(parentComponent) == ChromosomeChooserDialog.APPROVE_OPTION) {
if (chromoChooser.getSelectedChromosomeList() != null) {
boolean[] selectedChromo = new boolean[chromosomeList.size()];
for (int i = 0; i < chromosomeList.size(); i++) {
selectedChromo[i] = chromoChooser.getSelectedChromosomeList().contains(chromosomeList.get(i));
}
return selectedChromo;
}
}
return null;
}
/**
* A dialog box used to choose a {@link GeneScoreType}
* @param parentComponent the parent Component for the dialog
* @return a {@link GeneScoreType}
*/
public final static GeneScoreType chooseGeneScoreCalculation(Component parentComponent) {
return (GeneScoreType) JOptionPane.showInputDialog(
parentComponent,
"Choose a method for the calculation of the score of the genes",
"Score Calculation",
JOptionPane.QUESTION_MESSAGE,
null,
GeneScoreType.values(),
GeneScoreType.RPKM);
}
/**
* A dialog box used to choose a {@link LogBase}
* @param parentComponent the parent Component for the dialog
* @return a {@link LogBase} value
*/
public final static LogBase chooseLogBase(Component parentComponent) {
return (LogBase) JOptionPane.showInputDialog(
parentComponent,
"Choose a base for the logarithm",
"Logarithm Base",
JOptionPane.QUESTION_MESSAGE,
null,
LogBase.values(),
LogBase.BASE_2);
}
/**
* A dialog box used to choose a {@link ScoreOperation}
* @param parentComponent the parent Component for the dialog
* @return a {@link ScoreOperation}
*/
public final static ScoreOperation chooseScoreCalculation(Component parentComponent) {
return (ScoreOperation)JOptionPane.showInputDialog(
parentComponent,
"Choose a method for the calculation of the score",
"Score Calculation",
JOptionPane.QUESTION_MESSAGE,
null,
ScoreOperation.values(),
ScoreOperation.AVERAGE);
}
/**
* Tries to force the garbage collector to run
*/
public final static void garbageCollect() {
System.gc();
}
/**
* @return the system clipboard if the application has the permission to access it.
* A local clipboard otherwise.
*/
public static Clipboard getClipboard() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkSystemClipboardAccess();
} catch(SecurityException e) {
return LocalClipboard.getInstance();
}
}
return Toolkit.getDefaultToolkit().getSystemClipboard();
}
/**
* If the user is using Windows, the configuration directory is the temporary one (eg: C:\Users\USER\AppData\Local\Temp)
* If the user is using POSIX like platform, the configuration directory is $HOME/.genplay
* @return the configuration directory
*/
public static String getConfigurationDirectoryPath() {
if (isWindowsOS()) {
// windows system
return System.getenv("APPDATA") + "\\GenPlay\\";
} else if (isMacOS()){
// mac
return System.getProperty("user.home") + "/Library/Application Support/GenPlay/";
} else {
// linux / unix
return System.getProperty("user.home") + "/.config/GenPlay/";
}
}
/**
* @param file a {@link File}
* @return the extension of the file (without the dot), null if none.
*/
public final static String getExtension(File file) {
String fileName = file.getName();
if (fileName == null) {
return null;
}
int dotIndex = fileName.lastIndexOf('.');
if ((dotIndex > 0) && (dotIndex < (fileName.length() - 1))) {
return fileName.substring(dotIndex + 1).toLowerCase().trim();
} else {
return null;
}
}
/**
* @param path
* @return the name of file without its path
*/
public static String getFileName(String path) {
int lastBackSlashIndex = path.lastIndexOf('\\');
int latSlashIndex = path.lastIndexOf('/');
String[] splitPath;
if (lastBackSlashIndex > latSlashIndex) {
splitPath = path.split("\\\\");
} else {
splitPath = path.split("/");
}
return splitPath[splitPath.length - 1];
}
/**
* @param file a {@link File}
* @return the name of a file without its extension
*/
public final static String getFileNameWithoutExtension(File file) {
String fileName = file.getName();
int index = fileName.lastIndexOf('.');
if ((index > 0) && (index <= (file.getName().length() - 2))) {
return fileName.substring(0, index);
} else {
return fileName;
}
}
/**
* This method return the index of the first int found in a string, starting from the specified index position
* @param s the string
* @param index the index to start
* @return the index of the first int found in the string after the specified start, -1 if not found
*/
public final static int getFirstIntegerOffset (String s, int index) {
for (int i = 0; i < s.length(); i++) {
int c = s.charAt(i);
if ((c >= 48) && (c <= 57)) {
return i;
}
}
return -1;
}
/**
* This method looks for the full integer part in a string from a start index.
* @param s the string
* @param index index of the first integer
* @return the full integer starting at the index
*/
public final static Integer getFullIntegerPart (String s, int index) {
Integer result = null; // Initialize the result to null
int nextIndex = index + 1; // Next index is initialized with index + 1
while (nextIndex <= s.length()) { // while the next index is shorter or equal to the string length
String text = s.substring(index, nextIndex); // gets the sub string from the string (index to next index)
try {
result = Integer.parseInt(text); // tries to get the integer part
} catch (Exception e) { // if there is no integer part
return result; // we return result (that contains the previous integer part or null)
}
nextIndex++; // if it worked, we keep looking in the string increasing the next index
}
return result; // return the result of the scan
}
/**
* @return the java version number, -1 if not found
*/
public static int getJavaVersion () {
String version = System.getProperty("java.specification.version", "");
int versionNumber = -1;
if (version.length() > 2) {
versionNumber = Integer.parseInt("" + version.charAt(2));
}
return versionNumber;
}
/**
* @param layers a list of {@link Layer}
* @param filter an array of {@link LayerType} as filters
* @return a list of {@link Layer} containing only the {@link LayerType} defined in the filters.
*/
public static List<Layer<?>> getLayers(List<Layer<?>> layers, LayerType[] filter) {
List<Layer<?>> result = new ArrayList<Layer<?>>();
for (Layer<?> layer: layers) {
boolean found = false;
int index = 0;
while (!found & (index < filter.length)) {
if (layer.getType() == filter[index]) {
found = true;
}
index++;
}
if (found) {
result.add(layer);
}
}
return result;
}
/**
* @param tracks a list of {@link Track}
* @param layerTypes a list of {@link LayerType}
* @return all the layer from the list of tracks that are in the specified list of the {@link LayerType}. All layers if the layer type list is null
*/
public final static Layer<?>[] getLayers(Track[] tracks, LayerType[] layerTypes) {
List<Layer<?>> layerList = new ArrayList<Layer<?>>();
for (Track currentTrack: tracks) {
for (Layer<?> currentLayer: currentTrack.getLayers()) {
if ((layerTypes == null) || currentLayer.getType().isContainedIn(layerTypes)) {
layerList.add(currentLayer);
}
}
}
Layer<?>[] returnLayers = new Layer<?>[layerList.size()];
return layerList.toArray(returnLayers);
}
/**
* @return the {@link ExtendedFileFilter} associated to the files that can be loaded as GeneList
*/
public final static FileFilter[] getReadableLayerFileFilters() {
ExtendedFileFilter[] filters = {
new AllTrackFileFilter(),
new GenPlayTrackFilter(),
new BAMFilter(),
new SAMFilter(),
new BedFilter(),
new BedGraphFilter(),
new WiggleFilter(),
new GTFFilter(),
new GFFFilter(),
new PSLFilter(),
new TwoBitFilter(),
new ElandExtendedFilter()
};
return filters;
}
/**
* @return the file filters for the sequence files
*/
public static FileFilter[] getReadableSequenceFileFilters() {
FileFilter[] filefilters = {new TwoBitFilter()};
return filefilters;
}
/**
* @return the {@link ExtendedFileFilter} associated to the files that can be sorted by the application
*/
public final static FileFilter[] getSortableFileFilters() {
ExtendedFileFilter[] filters = {new BedGraphFilter(), new BedFilter(), new SAMFilter(), new GFFFilter(), new PairFilter(), new PSLFilter()};
return filters;
}
/**
* @return the location of the temporary directory
*/
public static String getTmpDirectoryPath() {
return System.getProperty("java.io.tmpdir");
}
/**
* @return the {@link ExtendedFileFilter} associated to the files that can be saved as BinList
*/
public final static FileFilter[] getWritableBinListFileFilters() {
ExtendedFileFilter[] filters = {new BedGraphFilter(), new BedFilter(), new GFFFilter(), new WiggleFilter()};
return filters;
}
/**
* @return the {@link ExtendedFileFilter} associated to the files that can be saved as GeneList
*/
public final static FileFilter[] getWritableGeneFileFilters() {
ExtendedFileFilter[] filters = {new BedFilter()};
return filters;
}
/**
* @return the {@link ExtendedFileFilter} associated to the files that can be saved as SCWList
*/
public final static FileFilter[] getWritableSCWFileFilter() {
ExtendedFileFilter[] filters = {new BedGraphFilter(), new BedGraphWith0Filter(), new BedFilter(), new GFFFilter()};
return filters;
}
/**
* @return true if the software is a mac installation (ie: the plateform is mac and there is an content directory).
*/
public static boolean isMacInstall() {
File genPlayApp = new File("GenPlay.app");
return isMacOS() && genPlayApp.exists();
}
/**
* @return true if the computer running GenPlay is a mac, false otherwise
*/
public static boolean isMacOS() {
return System.getProperty("os.name").toLowerCase().indexOf("mac") >= 0;
}
/**
* @return true if the software is a windows installation (ie: the plateform is windows and there is an exe file).
*/
public static boolean isWindowsInstall() {
File genPlayExe = new File("GenPlay.exe");
return isWindowsOS() && genPlayExe.exists();
}
/**
* @return true if the OS running the program is Windows, false otherwise
*/
public static boolean isWindowsOS() {
return System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
}
/**
* Returns the logarithm of a double value. The logarithm is computed in the specified base
* @param logBase
* @param value value to l
* @return a logarithm value
*/
public final static double log(LogBase logBase, double value) {
if (logBase == LogBase.BASE_E) {
// the Math.log function return the natural log (no needs to change the base)
return Math.log(value);
} else {
// change of base: logb(x) = logk(x) / logk(b)
return Math.log(value) / Math.log(logBase.getValue());
}
}
/**
* This methods reverse an array of int
* @param b the int array
* @return the reversed array
*/
public final static int[] reverse(int[] b) {
int left = 0; // index of leftmost element
int right = b.length-1; // index of rightmost element
while (left < right) {
// exchange the left and right elements
int temp = b[left];
b[left] = b[right];
b[right] = temp;
// move the bounds toward the center
left++;
right--;
}
return b;
}
/**
* Split a string using the char code of a character.
* @param s the string to split
* @param c the integer code of the character
* @return an array containing the split string (or empty if the string is null)
*/
public final static String[] split (String s, int c) {
List<String> list = new ArrayList<String>();
if (s != null) {
int pos = 0, end;
while ((end = s.indexOf(c, pos)) >= 0) {
String sub = s.substring(pos, end);
list.add(sub);
pos = end + 1;
}
list.add(s.substring(pos));
}
if (list.isEmpty()) {
return null;
}
int size = list.size();
if (list.get(size - 1).isEmpty()) {
size--;
}
String[] result = new String[size];
for (int i = 0; i < size; i++) {
result[i] = list.get(i);
}
return result;
}
/**
* This methods will split a String in different lines according to a maximum length.
* It does split in middle of a word but at a whitespace if necessary
* @param s the string
* @param length the length
* @param fm the font metrics
* @return the formatted line
*/
public final static String[] splitStringWithLength (String s, int length, FontMetrics fm) {
List<String> result = new ArrayList<String>();
String[] array = split(s, ' ');
String current = array[0];
int currentLength = fm.stringWidth(current);
for (int i = 1; i < array.length; i++) {
String tmp = array[i];
currentLength += fm.stringWidth(" " + tmp);
if (currentLength < length) {
current += " " + tmp;
} else {
result.add(current);
current = tmp;
currentLength = fm.stringWidth(tmp);
}
}
result.add(current);
String[] arrayResult = new String[result.size()];
for (int i = 0; i < arrayResult.length; i++) {
arrayResult[i] = result.get(i);
}
return arrayResult;
}
/**
* Split a string using the tabulation character.
* @param s the string to split
* @return an array containing the split string
*/
public final static String[] splitWithTab (String s) {
return split(s, ' ');
}
}