/******************************************************************************* * 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.List; import org.apache.commons.lang.StringUtils; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.window.Window; import org.eclipse.jubula.client.archive.JsonStorage; import org.eclipse.jubula.client.archive.dto.ProjectDTO; import org.eclipse.jubula.client.core.businessprocess.ProjectCompNameCache; import org.eclipse.jubula.client.core.businessprocess.INameMapper; import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameCache; import org.eclipse.jubula.client.core.businessprocess.ParamNameBP; import org.eclipse.jubula.client.core.businessprocess.ParamNameBPDecorator; import org.eclipse.jubula.client.core.businessprocess.ProjectNameBP; 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.UpdateState; import org.eclipse.jubula.client.core.model.IProjectPO; import org.eclipse.jubula.client.core.model.ProjectVersion; 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.PMSaveException; import org.eclipse.jubula.client.core.persistence.ProjectPM; 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.controllers.PMExceptionHandler; import org.eclipse.jubula.client.ui.rcp.dialogs.VersionDialog; 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.exception.ToolkitPluginException; import org.eclipse.jubula.tools.internal.exception.JBVersionException; import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.jubula.tools.internal.version.IVersion; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.PlatformUI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author BREDEX GmbH * @created 11.05.2005 */ @SuppressWarnings("synthetic-access") public class SaveProjectAsHandler extends AbstractProjectHandler { /** standard logging */ private static Logger log = LoggerFactory.getLogger(SaveProjectAsHandler.class); /** * Worker operation for SaveProjectAs action. * * @author BREDEX GmbH * @created Dec 4, 2007 */ private class SaveAsOperation implements IRunnableWithProgress { /** the total work for the operation */ private static final int TOTAL_WORK = 100; /** the work for gathering project data from the database*/ private static final int WORK_GET_PROJECT_FROM_DB = 5; /** the work for creating the domain objects for the project */ private static final int WORK_PROJECT_CREATION = 10; /** the work for saving the project to the database */ private static final int WORK_PROJECT_SAVE = TOTAL_WORK - WORK_PROJECT_CREATION - WORK_GET_PROJECT_FROM_DB; /** The name for the new project */ private String m_newProjectName; /** The version for the new project */ private ProjectVersion m_newProjectVersion; /** * Constructor * * @param newProjectName name of new project * @param version version of the new project */ public SaveAsOperation(String newProjectName, ProjectVersion version) { m_newProjectName = newProjectName; m_newProjectVersion = version; } /** * {@inheritDoc} */ public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException { String cProjectName = GeneralStorage.getInstance() .getProject().getName(); SubMonitor subMonitor = SubMonitor.convert(monitor, NLS.bind(Messages.SaveProjectAsOperationSavingProject, new Object[] {cProjectName, m_newProjectName}), TOTAL_WORK); final ParamNameBPDecorator paramNameMapper = new ParamNameBPDecorator(ParamNameBP.getInstance()); try { NodePM.getInstance().setUseCache(true); ProjectDTO dto = JsonStorage.save(GeneralStorage.getInstance() .getProject(), null, false, subMonitor.newChild( WORK_GET_PROJECT_FROM_DB), Plugin.getDefault()); changeProjectVersion(dto); if (monitor.isCanceled()) { throw new InterruptedException(); } if (dto != null) { IProjectPO duplicatedProject = null; IWritableComponentNameCache cache = new ProjectCompNameCache(null); try { duplicatedProject = JsonStorage.load(dto, subMonitor.newChild(WORK_PROJECT_CREATION), Plugin.getDefault(), true, false, paramNameMapper, cache, true, null); } catch (ToolkitPluginException e1) { log.error(e1.getMessage()); // This should not be occur } try { cache.setContext(duplicatedProject); duplicatedProject.setClientMetaDataVersion(IVersion .JB_CLIENT_METADATA_VERSION); attachProjectWithProgress(subMonitor.newChild( WORK_PROJECT_SAVE), paramNameMapper, cache, duplicatedProject); } catch (PMSaveException e) { Plugin.stopLongRunning(); PMExceptionHandler.handlePMExceptionForMasterSession( new PMSaveException(e.getMessage(), MessageIDs .E_SAVE_AS_PROJECT_FAILED)); throw new InvocationTargetException(e); } catch (PMException e) { Plugin.stopLongRunning(); PMExceptionHandler.handlePMExceptionForMasterSession(e); throw new InvocationTargetException(e); } catch (ProjectDeletedException e) { Plugin.stopLongRunning(); PMExceptionHandler.handleProjectDeletedException(); throw new InvocationTargetException(e); } } } catch (PMSaveException e) { Plugin.stopLongRunning(); PMExceptionHandler.handlePMExceptionForMasterSession( new PMSaveException(e.getMessage(), MessageIDs.E_SAVE_AS_PROJECT_FAILED)); } catch (PMException e) { Plugin.stopLongRunning(); PMExceptionHandler.handlePMExceptionForMasterSession(e); } catch (ProjectDeletedException e) { Plugin.stopLongRunning(); PMExceptionHandler.handleProjectDeletedException(); } catch (JBVersionException e) { // should not be occur, that a used toolkit of current project // has a version conflict with installed Toolkit Plugin. log.error(Messages. ToolkitVersionConflictWhileSaveProjectAsAction); } finally { NodePM.getInstance().setUseCache(false); Plugin.stopLongRunning(); monitor.done(); } } /** * @param dto the original project dto */ private void changeProjectVersion(ProjectDTO dto) { dto.setMajorProjectVersion(m_newProjectVersion.getMajorNumber()); dto.setMinorProjectVersion(m_newProjectVersion.getMinorNumber()); dto.setMicroProjectVersion(m_newProjectVersion.getMicroNumber()); dto.setProjectVersionQualifier(m_newProjectVersion .getVersionQualifier()); } /** * Attaches the given project to the Master Session and database using * the given parameter name mapper. Reports progress during the * operation. * * @param monitor * The progress monitor for the operation. * @param paramNameMapper * The parameter name mapper to use when adding the project * to the database. * @param compNameCache * The component name cache to use when adding the project * to the database. * @param project * The project to add to the database * @throws PMException * in case of any database error * @throws ProjectDeletedException * if project is already deleted * @throws InterruptedException * if the operation was canceled. */ private void attachProjectWithProgress(IProgressMonitor monitor, final ParamNameBPDecorator paramNameMapper, IWritableComponentNameCache compNameCache, final IProjectPO project) throws PMException, ProjectDeletedException, InterruptedException { // We need to clear the current project data so // we are in a known state if the operation is // canceled. 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); } List<INameMapper> mapperList = new ArrayList<INameMapper>(); List<IWritableComponentNameCache> compNameCacheList = new ArrayList<IWritableComponentNameCache>(); mapperList.add(paramNameMapper); compNameCacheList.add(compNameCache); ProjectPM.attachProjectToROSession(project, m_newProjectName, mapperList, compNameCacheList, monitor); Plugin.stopLongRunning(); } } /** * @param newProjectName name of new project * @param version version of the new projct * @return a new operation for project import */ private IRunnableWithProgress createOperation(final String newProjectName, ProjectVersion version) { return new SaveAsOperation(newProjectName, version); } /** * Opens the dialog to change the project name * * @return the dialog */ private VersionDialog openInputDialog() { VersionDialog dialog = new VersionDialog(getActiveShell(), Messages.SaveProjectAsActionTitle, Messages.SaveProjectAsActionMessage, IconConstants.BIG_PROJECT_STRING, Messages.SaveProjectAsActionShellTitle, true) { /** * {@inheritDoc} */ protected boolean isInputAllowed() { final String newProjectName = getProjectNameFieldValue(); boolean isInputAllowed = true; if (StringUtils.isBlank(newProjectName) || !ProjectNameBP .isValidProjectName(newProjectName, true)) { setErrorMessage(Messages.SaveProjectAsActionInvalidName); isInputAllowed = false; } if (ProjectPM.doesProjectNameExist(newProjectName)) { setErrorMessage( Messages.SaveProjectAsActionDoubleOrInvalidName); isInputAllowed = false; } return isInputAllowed; } /** * {@inheritDoc} */ protected void okPressed() { String newProjectName = getProjectNameFieldValue(); if (ProjectPM.doesProjectNameExist(newProjectName) || StringUtils.isBlank(newProjectName)) { ErrorHandlingUtil.createMessageDialog( MessageIDs.E_PROJECTNAME_ALREADY_EXISTS, new Object[] { getProjectNameFieldValue() }, null); return; } super.okPressed(); } }; dialog.setHelpAvailable(true); dialog.create(); DialogUtils.setWidgetNameForModalDialog(dialog); Plugin.getHelpSystem().setHelp(dialog.getShell(), ContextHelpIds.DIALOG_PROJECT_SAVEAS); dialog.open(); return dialog; } /** * call this if the "save as" has ended to update the GUI. */ private void fireReady() { DataEventDispatcher ded = DataEventDispatcher.getInstance(); ded.fireProjectLoadedListener(new NullProgressMonitor()); ded.fireProjectStateChanged(ProjectState.opened); } /** * {@inheritDoc} */ public Object executeImpl(ExecutionEvent event) { VersionDialog dialog = openInputDialog(); if (dialog.getReturnCode() == Window.OK) { final String newProjectName = dialog.getProjectName(); IRunnableWithProgress op = createOperation(newProjectName, dialog.getProjectVersion()); try { PlatformUI.getWorkbench().getProgressService() .busyCursorWhile(op); fireReady(); } catch (InvocationTargetException ite) { // Exception occurred during operation log.error(ite.getLocalizedMessage(), ite.getCause()); } catch (InterruptedException e) { // Operation was canceled. // We have to clear the GUI because all of // the save work was done in the Master Session, which has been // rolled back. Utils.clearClient(); } } return null; } }