/*******************************************************************************
* 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.gui.action.project;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.InputStream;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.JOptionPane;
import edu.yu.einstein.genplay.core.manager.ProjectFiles;
import edu.yu.einstein.genplay.core.manager.project.ProjectManager;
import edu.yu.einstein.genplay.core.manager.recording.ProjectInformation;
import edu.yu.einstein.genplay.core.manager.recording.ProjectRecording;
import edu.yu.einstein.genplay.core.manager.recording.RecordingManager;
import edu.yu.einstein.genplay.exception.ExceptionManager;
import edu.yu.einstein.genplay.gui.dialog.invalidFileDialog.InvalidFileDialog;
import edu.yu.einstein.genplay.util.Utils;
/**
* This action initializes a project just before loading it.
* The loading action {@link PALoadProject} gets the track list.
* A project files contains two elements before the track list, they are:
* - the project information {@link ProjectInformation}
* - the managers {@link ProjectManager}
*
* This action first reads the project information in order to validate the files related to the project whether the project is file dependent.
* Then, this action reads the managers.
*
* This action is set either with an input stream or with a file.
* The method hasBeenInitialized() must be used in order to validate the initialization.
* If any error happened, the method getErrorMessage() gives the error message.
*
* @author Nicolas Fourel
* @author Julien Lajugie
*/
public final class PAInitManagers extends AbstractAction {
private static final long serialVersionUID = 2102571378866219218L; // generated ID
private static final String DESCRIPTION = "Show About GenPlay"; // tooltip
private static final int MNEMONIC = KeyEvent.VK_A; // mnemonic key
private static final String ACTION_NAME = "Initializes Managers"; // action name
private File file; // the file of the project to load
private InputStream inputStream; // the input stream of the project to load
private boolean loadingFromWelcomeScreen;
private String[] formerPaths; // array of the former paths
private String[] invalidPaths; // array of the invalid paths
private String[] newPaths; // array of the new paths
private String error; // the error message
/**
* key of the action in the {@link ActionMap}
*/
public static final String ACTION_KEY = PAInitManagers.class.getName();
/**
* Creates an instance of {@link PAInitManagers}
*/
public PAInitManagers() {
super();
putValue(NAME, ACTION_NAME);
putValue(ACTION_COMMAND_KEY, ACTION_KEY);
putValue(SHORT_DESCRIPTION, DESCRIPTION);
putValue(MNEMONIC_KEY, MNEMONIC);
file = null;
inputStream = null;
loadingFromWelcomeScreen = false;
formerPaths = null;
invalidPaths = null;
newPaths = null;
error = null;
}
/**
* Shows the about dialog window
*/
@Override
public void actionPerformed(ActionEvent evt) {
if ((file != null) || (inputStream != null)) {
ProjectRecording projectRecording = RecordingManager.getInstance().getProjectRecording();
// Initializes the object input stream
try {
if (file != null) {
projectRecording.initObjectInputStream(file); // according to the given file
} else if (inputStream != null) { // or
projectRecording.initObjectInputStream(inputStream); // according to the given input stream
}
} catch (Exception e) {
error = "Could not open the project file.";
ExceptionManager.getInstance().caughtException(e);
}
if (error == null) {
// Reads the project information object
try {
projectRecording.initProjectInformation();
} catch (Exception e) {
error = "Could not read the project information.";
ExceptionManager.getInstance().caughtException(e);
}
if (!isValidProjectType()) {
String message = "";
if (loadIncorrectMultiGenomeProject()) {
message += "You are trying to load a Multi Genome Project from a Single Genome Project.\n";
} else if (loadIncorrectSingleProject()) {
message += "You are trying to load a Single Genome Project from a Multi Genome Project.\n";
}
message += "GenPlay does not allow this operation yet.\n";
message += "Please restart GenPlay and load your project from the welcome screen.";
JOptionPane.showMessageDialog(null, message, "Invalid project type", JOptionPane.INFORMATION_MESSAGE);
error = message;
} else {
if (error == null) {
// Manages the missing files
try {
// Gets the project information object
ProjectInformation projectInformation = projectRecording.getProjectInformation();
// Gets the files dependant to the project
formerPaths = projectInformation.getProjectFiles();
if (formerPaths != null) { // if the project is file dependent
invalidPaths = getInvalidPath(formerPaths); // we get the invalid files
if (hasInvalidFiles()) { // if some invalid files exist,
newPaths = getPathInProjectDirectory(invalidPaths); // try to see if the files are not in the same directory as the project
if (getNumberOfInvalidFiles(newPaths) == 0) {
ProjectFiles.getInstance().setCurrentFiles(formerPaths);
ProjectFiles.getInstance().setNewFiles(newPaths);
} else {
// Warn the user about the .gz and .gz.tbi files
if (!projectInformation.isSingleProject()) {
JOptionPane.showMessageDialog(null, "You are about to load a Multi Genome Project but some files have been moved.\n" +
"The next window will allow you to define their new location.\n" +
"Please keep in mind that .gz and .gz.tbi files must have the same name and location.");
}
InvalidFileDialog invalidFileDialog = new InvalidFileDialog(invalidPaths);
if (invalidFileDialog.showDialog(null) == InvalidFileDialog.APPROVE_OPTION) {
newPaths = invalidFileDialog.getCorrectedPaths();
if (getNumberOfInvalidFiles(newPaths) == 0) {
ProjectFiles.getInstance().setCurrentFiles(formerPaths);
ProjectFiles.getInstance().setNewFiles(newPaths);
} else { // the user can valid the dialog using invalid files
throw new Exception();
}
} else { // the user canceled the dialog
throw new Exception();
}
}
}
}
} catch (Exception e) {
error = "Invalid files path not corrected.";
}
if (error == null) {
// Reads the project manager
try {
projectRecording.initProjectManager();
} catch (Exception e) {
error = "Could not read the managers.";
ExceptionManager.getInstance().caughtException(e);
}
}
}
}
}
}
}
/**
* @return the error message
*/
public String getErrorMessage () {
return error;
}
/**
* @return the inputStream
*/
public InputStream getInputStream() {
return inputStream;
}
/**
* Create an array of invalid paths.
* @param paths the file paths
* @return the array of invalid paths
*/
private String[] getInvalidPath (String[] paths) {
String[] invalidPaths = new String[paths.length];
for (int i = 0; i < invalidPaths.length; i++) {
if (!isValidFile(paths[i], false)) {
invalidPaths[i] = paths[i];
} else {
invalidPaths[i] = null;
}
}
return invalidPaths;
}
/**
* @param paths the array of paths
* @return the number of valid paths
*/
private int getNumberOfInvalidFiles (String[] paths) {
int cpt = 0;
for (int i = 0; i < paths.length; i++) {
if (!isValidFile(paths[i], true)) {
cpt++;
}
}
return cpt;
}
/**
* @param invalidPaths
* @return an array containing the specified files having the project directory as parent directory
*/
private String[] getPathInProjectDirectory(String[] invalidPaths) {
if (file != null) {
File projectDirect = file.getParentFile();
String[] correctedPaths = new String[invalidPaths.length];
for (int i = 0; i < invalidPaths.length; i++) {
String newPath = new File(projectDirect, Utils.getFileName(invalidPaths[i])).getAbsolutePath();
correctedPaths[i] = newPath;
}
return correctedPaths;
} else {
return invalidPaths;
}
}
/**
* @return true if the managers have been initialized, false otherwise
*/
public boolean hasBeenInitialized () {
if (error == null) {
return true;
}
return false;
}
private boolean hasInvalidFiles () {
for (int i = 0; i < invalidPaths.length; i++) {
if (invalidPaths[i] != null) {
return true;
}
}
return false;
}
/**
* @param path file path
* @param skipNull allow to skip path that are null.
* @return true if the file is valid
*/
private boolean isValidFile (String path, boolean skipNull) {
if ((path == null) && skipNull) {
return true;
}
if (path != null) {
File file = new File(path);
return file.exists();
}
return false;
}
/**
* Checks if the current project and the new project are the same type (single/multigenome)
* @return
*/
private boolean isValidProjectType () {
if (loadIncorrectMultiGenomeProject() || loadIncorrectSingleProject()) {
return false;
}
return true;
}
/**
* @return true if the user is trying to load a multi genome project from a single genome project, false otherwise
*/
private boolean loadIncorrectMultiGenomeProject () {
if (!loadingFromWelcomeScreen) {
boolean newSingleProject = RecordingManager.getInstance().getProjectRecording().getProjectInformation().isSingleProject();
boolean currentMultiGenomeProject = ProjectManager.getInstance().isMultiGenomeProject();
if (!currentMultiGenomeProject && !newSingleProject) {
return true;
}
}
return false;
}
/**
* @return true if the user is trying to load a single genome project from a multi genome project, false otherwise
*/
private boolean loadIncorrectSingleProject () {
if (!loadingFromWelcomeScreen) {
boolean newSingleProject = RecordingManager.getInstance().getProjectRecording().getProjectInformation().isSingleProject();
boolean currentMultiGenomeProject = ProjectManager.getInstance().isMultiGenomeProject();
if (currentMultiGenomeProject && newSingleProject) {
return true;
}
}
return false;
}
/**
* @param file the file to set
*/
public void setFile(File file) {
this.file = file;
ProjectManager.getInstance().setProjectDirectory(file.getParentFile());
}
/**
* @param inputStream the inputStream to set
*/
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
/**
* Use this method when a project is loaded from the welcome screen.
* Do not need to use when the action is started from the main frame.
* False by default.
* @param loadingFromWelcomeScreen the loadingFromWelcomeScreen to set
*/
public void setLoadingFromWelcomeScreen(boolean loadingFromWelcomeScreen) {
this.loadingFromWelcomeScreen = loadingFromWelcomeScreen;
}
}