/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.client.ui.rcp.handlers.project;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.persistence.EntityManager;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.jubula.client.core.businessprocess.UsedToolkitBP;
import org.eclipse.jubula.client.core.businessprocess.db.TestCaseBP;
import org.eclipse.jubula.client.core.businessprocess.progress.ProgressMonitorTracker;
import org.eclipse.jubula.client.core.events.DataEventDispatcher;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.ProjectState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.TestresultState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.UpdateState;
import org.eclipse.jubula.client.core.model.IProjectPO;
import org.eclipse.jubula.client.core.model.IReusedProjectPO;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.model.IUsedToolkitPO;
import org.eclipse.jubula.client.core.persistence.GeneralStorage;
import org.eclipse.jubula.client.core.persistence.NodePM;
import org.eclipse.jubula.client.core.persistence.PMException;
import org.eclipse.jubula.client.core.persistence.PMReadException;
import org.eclipse.jubula.client.core.persistence.ProjectPM;
import org.eclipse.jubula.client.core.persistence.TestResultPM;
import org.eclipse.jubula.client.core.utils.StringHelper;
import org.eclipse.jubula.client.ui.constants.Constants;
import org.eclipse.jubula.client.ui.constants.ContextHelpIds;
import org.eclipse.jubula.client.ui.constants.IconConstants;
import org.eclipse.jubula.client.ui.handlers.project.AbstractProjectHandler;
import org.eclipse.jubula.client.ui.rcp.Plugin;
import org.eclipse.jubula.client.ui.rcp.businessprocess.ProjectUIBP;
import org.eclipse.jubula.client.ui.rcp.businessprocess.ToolkitBP;
import org.eclipse.jubula.client.ui.rcp.controllers.PMExceptionHandler;
import org.eclipse.jubula.client.ui.rcp.dialogs.NagDialog;
import org.eclipse.jubula.client.ui.rcp.dialogs.ProjectDialog;
import org.eclipse.jubula.client.ui.rcp.i18n.Messages;
import org.eclipse.jubula.client.ui.rcp.utils.Utils;
import org.eclipse.jubula.client.ui.utils.DialogUtils;
import org.eclipse.jubula.client.ui.utils.ErrorHandlingUtil;
import org.eclipse.jubula.toolkit.common.businessprocess.ToolkitSupportBP;
import org.eclipse.jubula.toolkit.common.exception.ToolkitPluginException;
import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder;
import org.eclipse.jubula.tools.internal.constants.ToolkitConstants;
import org.eclipse.jubula.tools.internal.exception.ConfigXmlException;
import org.eclipse.jubula.tools.internal.exception.JBException;
import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException;
import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
* @author BREDEX GmbH
* @created 18.04.2005
*/
public class OpenProjectHandler extends AbstractProjectHandler {
/**
* @author BREDEX GmbH
* @created Jan 2, 2008
*/
public static class OpenProjectOperation implements IRunnableWithProgress {
/**
* <code>TESTRESULT_DETAIL_JOB_CLEANUP_DELAY</code> is 10 minutes
*/
private static final int TESTRESULT_DETAIL_JOB_CLEANUP_DELAY = 600000;
/** The project to open */
private IProjectPO m_selectedProject;
/**
* @param selectedProject
* The project that is being opened.
*/
public OpenProjectOperation(IProjectPO selectedProject) {
m_selectedProject = selectedProject;
}
/**
* {@inheritDoc}
*/
public void run(IProgressMonitor monitor) throws InterruptedException {
Utils.clearClient();
int totalWork = getTotalWork();
ProgressMonitorTracker instance = ProgressMonitorTracker.SINGLETON;
instance.setProgressMonitor(monitor);
monitor.beginTask(NLS.bind(
Messages.OpenProjectOperationOpeningProject,
new Object[] { m_selectedProject.getName(),
m_selectedProject.getProjectVersion() }),
totalWork);
DataEventDispatcher ded = DataEventDispatcher.getInstance();
try {
if (!checkProjectToolkits(m_selectedProject)) {
throw new InterruptedException();
}
checkToolkitAvailable(m_selectedProject.getToolkit());
try {
NodePM.getInstance().setUseCache(true);
load(m_selectedProject, monitor);
if (monitor.isCanceled()) {
throw new InterruptedException();
}
} catch (ConfigXmlException ce) {
handleCapDataNotFound(ce);
}
try {
List<ISpecTestCasePO> tcs =
TestCaseBP.fixSpecsWithProjectAsParent();
if (tcs == null || tcs.size() != 0) {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.I_FOUND_SPECS_NO_PARENT);
}
} catch (PMException e) {
ErrorHandlingUtil.createMessageDialog(e);
}
ded.fireProjectLoadedListener(monitor);
// re-init the string helper in case of a toolkit change during
// load
StringHelper.getInstance();
startCleanTestresultsJob(GeneralStorage.getInstance()
.getProject());
if (monitor.isCanceled()) {
throw new InterruptedException();
}
} catch (final JBException e) {
errorOccured();
Display.getDefault().asyncExec(new Runnable() {
public void run() {
ErrorHandlingUtil.createMessageDialog(e, null, null);
}
});
} finally {
instance.setProgressMonitor(null);
NodePM.getInstance().setUseCache(false);
monitor.done();
}
if (Plugin.getDefault().getPreferenceStore().getBoolean(
Constants.UPDATE_REUSED_PROJECT_KEY)) {
return;
}
final UpdateReusedProjectHandler oldReusedProjectHandler =
new UpdateReusedProjectHandler();
if (oldReusedProjectHandler.isThereOldReusedProject()) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
updateReusedProjects(oldReusedProjectHandler);
}
});
}
refreshTestResultsView(ded);
}
/**
* this method sends a {@link TestresultState#Refresh}
* with {@link DataEventDispatcher#fireTestresultChanged(TestresultState)}
* to refresh some views
* @param ded the {@link DataEventDispatcher} to be used
*/
private void refreshTestResultsView(final DataEventDispatcher ded) {
Plugin.getDisplay().syncExec(new Runnable() {
public void run() {
ded.fireTestresultChanged(TestresultState.Refresh);
}
});
}
/**
* @param handler the UpdateReusedProjectHandler
*/
private void updateReusedProjects(UpdateReusedProjectHandler handler) {
int wouldNeedUpdate = handler.showUpdateReusedProjectDialog();
if (wouldNeedUpdate == Window.OK) {
Shell shell = Plugin.getActiveWorkbenchWindowShell();
ExportDialog.showExportDialog(shell);
String pageId = Constants.REUSED_PROJECT_PROPERTY_ID;
ProjectPropertyDialog.showPropertyDialog(shell, pageId, null);
}
}
/**
* set persisted values for cleaning testresults in database
*
* @param project
* the project to clean the test results for
*/
public static void startCleanTestresultsJob(final IProjectPO project) {
final int cleanupInterval = project.getTestResultCleanupInterval();
final String projGUID = String.valueOf(project.getGuid());
final Integer projMajVer = project.getMajorProjectVersion();
final Integer projMinVer = project.getMinorProjectVersion();
final Integer projMicVer = project.getMicroProjectVersion();
final String projVerQual = project.getProjectVersionQualifier();
if (cleanupInterval > 0) {
Job job = new Job(NLS.bind(
Messages.UIJobCleaningTestResultFromDB,
project.getName())) {
public IStatus run(IProgressMonitor monitor) {
TestResultPM.cleanTestresultDetails(cleanupInterval,
projGUID, projMajVer, projMinVer, projMicVer,
projVerQual);
monitor.done();
return Status.OK_STATUS;
}
};
job.schedule(TESTRESULT_DETAIL_JOB_CLEANUP_DELAY);
}
}
/**
* Checks whether the given toolkit is available. If the given toolkit
* is not available, a warning is displayed.
*
* @param toolkitId
* The id of the toolkit to check.
*/
private void checkToolkitAvailable(String toolkitId) {
try {
if (!ComponentBuilder.getInstance().getLevelToolkitIds()
.contains(toolkitId)
&& ToolkitConstants.LEVEL_TOOLKIT
.equals(ToolkitSupportBP
.getToolkitLevel(toolkitId))) {
ErrorHandlingUtil.createMessageDialog(MessageIDs.
W_PROJECT_TOOLKIT_NOT_AVAILABLE);
}
} catch (ToolkitPluginException e) {
ErrorHandlingUtil.createMessageDialog(MessageIDs.
W_PROJECT_TOOLKIT_NOT_AVAILABLE);
}
}
/**
* @return the amount of work required to complete the operation. This
* value can then be used when creating a progress monitor.
*/
private int getTotalWork() {
int totalWork = 0;
EntityManager masterSession = GeneralStorage.getInstance()
.getMasterSession();
long selectedProjectId = m_selectedProject.getId();
// (node=1)
totalWork += NodePM.getNumNodes(selectedProjectId, masterSession);
// (tdMan=1)
totalWork += NodePM.getNumTestDataManagers(selectedProjectId,
masterSession);
// (execTC=1 [each corresponding specTC needs to be fetched])
totalWork += NodePM.getNumExecTestCases(selectedProjectId,
masterSession);
for (IReusedProjectPO reused : m_selectedProject.
getUsedProjects()) {
try {
IProjectPO reusedProject = ProjectPM
.loadReusedProject(reused);
if (reusedProject != null) {
long reusedId = reusedProject.getId();
// (node=1)
totalWork += NodePM
.getNumNodes(reusedId, masterSession);
// (tdMan=1)
totalWork += NodePM.getNumTestDataManagers(reusedId,
masterSession);
// (execTC=1 [each corresponding specTC needs to be
// fetched])
totalWork += NodePM.getNumExecTestCases(reusedId,
masterSession);
}
} catch (JBException e) {
// Do nothing
}
}
return totalWork;
}
/**
*
* @param proj
* the project to open.
* @param monitor
* The progress monitor for this operation.
* @throws InterruptedException
* if the operation was canceled.
*/
private void load(IProjectPO proj, IProgressMonitor monitor)
throws InterruptedException {
if (proj == null) {
Plugin.stopLongRunning();
showErrorDialog(Messages.OpenProjectActionInternalError);
return;
}
try {
Plugin.getDisplay().syncExec(new Runnable() {
public void run() {
Plugin.closeAllOpenedJubulaEditors(false);
}
});
IProjectPO prevProj = GeneralStorage.getInstance().getProject();
ProjectPM.loadProjectInROSession(proj);
try {
final IProjectPO project = GeneralStorage.getInstance()
.getProject();
try {
UsedToolkitBP.getInstance().refreshToolkitInfo(project);
} catch (PMException e1) {
PMExceptionHandler
.handlePMExceptionForMasterSession(e1);
} catch (ProjectDeletedException e1) {
PMExceptionHandler.handleProjectDeletedException();
}
if (monitor.isCanceled()) {
throw new InterruptedException();
}
} catch (ConfigXmlException ce) {
if (prevProj == null) {
GeneralStorage.getInstance().reset();
} else {
ProjectPM.loadProjectInROSession(prevProj);
}
throw ce;
}
} catch (PMReadException e) {
showErrorDialog(Messages.ErrorMessageCantReadProject);
} catch (OperationCanceledException oce) {
Utils.clearClient();
} catch (Exception e) {
ErrorHandlingUtil.createMessageDialogException(e);
} finally {
Plugin.stopLongRunning();
}
}
/**
* Handles an exception thrown while opening a project.
*/
public void handleOperationException() {
// Clear all current project data
IProjectPO clearedProject = GeneralStorage.getInstance()
.getProject();
if (clearedProject != null) {
Utils.clearClient();
GeneralStorage.getInstance().nullProject();
final DataEventDispatcher ded = DataEventDispatcher
.getInstance();
ded.fireDataChangedListener(clearedProject, DataState.Deleted,
UpdateState.all);
}
}
/**
* Checks that the toolkits and toolkit versions are correct enough to
* be able to load the project.
*
* @param proj
* the project for which to check the toolkits
* @return <code>true</code> if project can be loaded. Otherwise
* <code>false</code>.
*/
private boolean checkProjectToolkits(IProjectPO proj)
throws PMException {
final Set<IUsedToolkitPO> usedToolkits = UsedToolkitBP
.getInstance().readUsedToolkitsFromDB(proj);
return ToolkitBP.getInstance().checkXMLVersion(usedToolkits);
}
/**
* Create an appropriate error dialog.
*
* @param ce
* The exception that prevented the loading of project.
*/
private void handleCapDataNotFound(ConfigXmlException ce) {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.E_LOAD_PROJECT_CONFIG_CONFLICT, null,
new String[] { ce.getMessage() });
}
/**
* Opens an error dialog.
*
* @param message
* the message to show in the dialog.
*/
private void showErrorDialog(String message) {
ErrorHandlingUtil.createMessageDialog(new JBException(message,
MessageIDs.E_UNEXPECTED_EXCEPTION), null,
new String[] { message });
}
/**
* {@inheritDoc}
*/
protected void errorOccured() {
Plugin.stopLongRunning();
}
}
/**
* Checks all available projects
*
* @return list of all projects
*/
private List<IProjectPO> checkAllAvailableProjects() {
List<IProjectPO> projList = null;
try {
projList = ProjectPM.findAllProjects();
if (projList.isEmpty()) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
ErrorHandlingUtil.createMessageDialog(
MessageIDs.I_NO_PROJECT_IN_DB);
}
});
Plugin.stopLongRunning();
} else {
SortedMap<String, List<String>> projNameToVersionMap =
new TreeMap<String, List<String>>();
for (IProjectPO proj : projList) {
String projName = proj.getName();
String projVersion = proj.getVersionString();
if (!StringUtils.isBlank(projName)
&& !projNameToVersionMap.containsKey(projName)) {
projNameToVersionMap.put(projName,
new ArrayList<String>());
}
projNameToVersionMap.get(projName).add(projVersion);
}
}
} catch (final JBException e) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
ErrorHandlingUtil.createMessageDialog(e, null, null);
}
});
}
return projList;
}
/**
* opens the project open dialog
* @param projList the project list
* @return the open project dialog
*/
public ProjectDialog openProjectOpenDialog(List<IProjectPO> projList) {
ProjectDialog dialog = null;
if (GeneralStorage.getInstance().getProject() != null
&& !Plugin.getDefault().showSaveEditorDialog(
getActiveShell())) {
Plugin.stopLongRunning();
} else {
dialog = openProjectSelectionDialog(projList);
if (dialog.getReturnCode() == Window.CANCEL) {
Plugin.stopLongRunning();
}
}
return dialog;
}
/**
* open the selected or the default project
*
* @param projectData the project data
* @param projList list which contains all available projects
*/
public void loadProject(ProjectDialog.ProjectData projectData,
List<IProjectPO> projList) {
IProjectPO projectToOpen = null;
for (IProjectPO project : projList) {
if (project.getGuid().equals(projectData.getGUID())
&& project.getVersionString().equals(
projectData.getVersionString())) {
projectToOpen = project;
break;
}
}
if (projectToOpen == null) {
openProjectOpenDialog(projList);
} else {
OpenProjectOperation openOperation = new OpenProjectOperation(
projectToOpen);
try {
PlatformUI.getWorkbench().getProgressService()
.busyCursorWhile(openOperation);
DataEventDispatcher.getInstance().fireProjectStateChanged(
ProjectState.opened);
checkAndNagForMissingProjects();
} catch (InvocationTargetException ite) {
openOperation.handleOperationException();
} catch (InterruptedException ie) {
openOperation.handleOperationException();
}
}
}
/**
* checks for missing reused projects after project loading
*/
private void checkAndNagForMissingProjects() {
List<String> missingProjects = new LinkedList<String>();
final IProjectPO project = GeneralStorage.getInstance().getProject();
if (project != null) {
final Set<IReusedProjectPO> usedProjects = project
.getUsedProjects();
if (usedProjects != null) {
for (IReusedProjectPO rProjects : usedProjects) {
if (!ProjectPM.doesProjectVersionExist(
rProjects.getProjectGuid(),
rProjects.getMajorNumber(),
rProjects.getMinorNumber(),
rProjects.getMicroNumber(),
rProjects.getVersionQualifier())) {
missingProjects.add(rProjects.getProjectGuid());
}
}
}
}
if (!missingProjects.isEmpty()) {
NagDialog.runNagDialog(getActiveShell(),
"InfoNagger.ImportAllRequiredProjects", //$NON-NLS-1$
ContextHelpIds.IMPORT_ALL_REQUIRED_PROJECTS);
}
}
/**
* @param projList
* the list of projects in the database
* @return the dialog
*/
private ProjectDialog openProjectSelectionDialog(
List<IProjectPO> projList) {
final ProjectDialog dialog = new ProjectDialog(getActiveShell(),
projList, Messages.OpenProjectActionMessage,
Messages.OpenProjectActionTitle,
IconConstants.OPEN_PROJECT_DIALOG_IMAGE,
Messages.OpenProjectActionCaption, false);
// set up help for dialog, with link
dialog.setHelpAvailable(true);
dialog.create();
DialogUtils.setWidgetNameForModalDialog(dialog);
Plugin.getHelpSystem().setHelp(dialog.getShell(),
ContextHelpIds.OPEN_PROJECT);
Display.getDefault().syncExec(new Runnable() {
public void run() {
Plugin.startLongRunning(Messages.
OpenProjectActionLoadProjectWaitMessage);
dialog.open();
}
});
return dialog;
}
/**
* {@inheritDoc}
*/
public Object executeImpl(ExecutionEvent event) {
ProjectDialog.ProjectData project = null;
boolean cancelPressed = false;
ProjectDialog dialog = null;
List<IProjectPO> projList = checkAllAvailableProjects();
if (projList != null && !projList.isEmpty()) {
if (ProjectUIBP.getInstance().shouldPerformAutoProjectLoad()) {
project = ProjectUIBP.getMostRecentProjectData();
} else {
dialog = openProjectOpenDialog(projList);
if (dialog == null || dialog.getReturnCode() == Window.CANCEL) {
cancelPressed = true;
} else {
project = dialog.getSelection();
}
}
if (!cancelPressed) {
loadProject(project, projList);
}
}
return null;
}
}