/*******************************************************************************
* 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.archive.businessprocess;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import org.apache.commons.lang.Validate;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jubula.client.archive.JsonStorage;
import org.eclipse.jubula.client.archive.XmlStorage;
import org.eclipse.jubula.client.archive.errorhandling.IProjectNameConflictResolver;
import org.eclipse.jubula.client.archive.errorhandling.NullProjectNameConflictResolver;
import org.eclipse.jubula.client.archive.i18n.Messages;
import org.eclipse.jubula.client.core.Activator;
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.ProjectCompNameCache;
import org.eclipse.jubula.client.core.businessprocess.ProjectNameBP;
import org.eclipse.jubula.client.core.businessprocess.UsedToolkitBP;
import org.eclipse.jubula.client.core.businessprocess.db.TestSuiteBP;
import org.eclipse.jubula.client.core.businessprocess.progress.ProgressMonitorTracker;
import org.eclipse.jubula.client.core.errorhandling.ErrorMessagePresenter;
import org.eclipse.jubula.client.core.model.IExecTestCasePO;
import org.eclipse.jubula.client.core.model.INodePO;
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.ITestSuitePO;
import org.eclipse.jubula.client.core.model.ProjectVersion;
import org.eclipse.jubula.client.core.persistence.GeneralStorage;
import org.eclipse.jubula.client.core.persistence.ISpecPersistable;
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.PMSaveException;
import org.eclipse.jubula.client.core.persistence.Persistor;
import org.eclipse.jubula.client.core.persistence.ProjectPM;
import org.eclipse.jubula.client.core.progress.IProgressConsole;
import org.eclipse.jubula.toolkit.common.exception.ToolkitPluginException;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.exception.ConfigXmlException;
import org.eclipse.jubula.tools.internal.exception.JBException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author BREDEX GmbH
* @created Jun 4, 2010
*/
public class FileStorageBP {
/** Extension of XML */
public static final String XML = ".xml"; //$NON-NLS-1$
/** Extension of JUB. It is an zip file which contain a project and a test result json file*/
public static final String JUB = ".jub"; //$NON-NLS-1$
/**
* Reads XML files and parses them into related domain objects.
*
* @author BREDEX GmbH
* @created Jan 9, 2008
*/
private static class ReadFilesOperation implements IRunnableWithProgress {
/**
* mapping: projects to import => corresponding param name mapper
*/
private Map<IProjectPO, INameMapper> m_projectToMapperMap;
/**
* mapping: projects to import => corresponding component name mapper
*/
private Map<IProjectPO, IWritableComponentNameCache>
m_projectToCompCacheMap;
/** names of the files to read */
private List<URL> m_fileURLs;
/** the console to use to display progress and error messages */
private IProgressConsole m_console;
/**
* Constructor
*
* @param fileURLs
* URLs of the project files to read.
* @param console
* The console to use to display progress and
* error messages.
*/
public ReadFilesOperation(
List<URL> fileURLs, IProgressConsole console) {
m_fileURLs = fileURLs;
m_projectToMapperMap =
new LinkedHashMap<IProjectPO, INameMapper>();
m_projectToCompCacheMap =
new LinkedHashMap<IProjectPO,
IWritableComponentNameCache>();
m_console = console;
}
/**
* {@inheritDoc}
*/
public void run(IProgressMonitor monitor) throws InterruptedException {
if (m_fileURLs == null) {
// Nothing to import. Just return.
return;
}
SubMonitor subMonitor = SubMonitor.convert(monitor, Messages
.ImportFileBPReading, m_fileURLs.size());
String lastFileName = StringConstants.EMPTY;
try {
showStartingImport(m_console);
showStartingReadingProjects(m_console);
for (URL fileURL : m_fileURLs) {
ParamNameBPDecorator paramNameMapper =
new ParamNameBPDecorator(ParamNameBP.getInstance());
final IWritableComponentNameCache compNameCache =
new ProjectCompNameCache(null);
String fileName = fileURL.getFile();
lastFileName = fileName;
m_console.writeStatus(new Status(IStatus.INFO,
Activator.PLUGIN_ID, NLS.bind(Messages
.ImportFileActionInfoStartingReadingProject,
fileName)));
try {
IProjectPO proj = readProject(subMonitor, fileURL,
paramNameMapper, compNameCache, fileName);
if (proj == null) {
continue;
}
compNameCache.setContext(proj);
m_projectToMapperMap.put(proj, paramNameMapper);
m_projectToCompCacheMap.put(proj, compNameCache);
} catch (JBVersionException e) {
for (Object msg : e.getErrorMsgs()) {
m_console.writeStatus(new Status(IStatus.ERROR,
Activator.PLUGIN_ID, (String)msg));
}
m_console.writeStatus(new Status(IStatus.ERROR,
Activator.PLUGIN_ID, NLS.bind(Messages.
ImportFileActionErrorImportFailed,
fileName)));
} catch (ToolkitPluginException e) {
m_console.writeStatus(new Status(IStatus.ERROR,
Activator.PLUGIN_ID, e.getMessage()));
handleUnsupportedToolkits(e.getMessage());
}
}
showFinishedReadingProjects(m_console);
} catch (final PMReadException e) {
m_console.writeStatus(new Status(IStatus.ERROR,
Activator.PLUGIN_ID, NLS.bind(Messages
.ImportFileActionErrorImportFailedProject,
lastFileName, StringConstants.TAB
+ Messages.InvalidImportFile)));
handlePMReadException(e, m_fileURLs);
} catch (final ConfigXmlException ce) {
handleCapDataNotFound(ce);
} finally {
monitor.done();
}
}
/**
*
* @param subMonitor
* The progress monitor for this potentially long-running
* operation.
* @param fileURL
* URL of the project file to read
* @param paramNameMapper
* mapper to resolve param names
* @param compNameCache
* cache to resolve component names
* @param fileName
* name of the project file
* @return the persisted object
* @throws PMReadException
* in case of error
* @throws JBVersionException
* in case of version conflict between used toolkits of
* imported project and the installed Toolkit Plugins
* @throws InterruptedException
* if the operation was canceled.
* @throws ToolkitPluginException
* in case of the toolkit of the project is not supported
*/
private IProjectPO readProject(SubMonitor subMonitor, URL fileURL,
ParamNameBPDecorator paramNameMapper,
final IWritableComponentNameCache compNameCache,
String fileName) throws PMReadException, JBVersionException,
InterruptedException, ToolkitPluginException {
String fileExt = fileName.substring(
fileName.lastIndexOf(StringConstants.DOT),
fileName.length());
IProjectPO proj = null;
if (fileExt.equals(XML)) {
proj = new XmlStorage().readProject(fileURL, paramNameMapper,
compNameCache, subMonitor.newChild(1), m_console);
} else if (fileExt.equals(JUB)) {
proj = new JsonStorage().readProject(fileURL, paramNameMapper,
compNameCache, false, false, subMonitor.newChild(1),
m_console);
}
return proj;
}
/**
*
* @return the projects to import, as read from the project files.
*/
public Map<IProjectPO, INameMapper> getProjectToMapperMap() {
return m_projectToMapperMap;
}
/**
*
* @return the mapping between projects to import and their
* corresponding component name cache
*/
public Map<IProjectPO, IWritableComponentNameCache>
getProjectToCompCacheMap() {
return m_projectToCompCacheMap;
}
}
/**
* imports an entire project
*
* @author BREDEX GmbH
*
*/
private static class CompleteImportOperation
implements IRunnableWithProgress {
/** mapping: projects to import => corresponding param name mapper */
private Map<IProjectPO, INameMapper> m_projectToMapperMap;
/** mapping: projects to import => corresponding comp name cache */
private Map<IProjectPO, IWritableComponentNameCache>
m_projectToCompCacheMap;
/** whether a refresh is required after import */
private boolean m_isRefreshRequired = false;
/** whether the import succeeded */
private boolean m_wasImportSuccessful = false;
/** the console to use for reporting progress and errors */
private IProgressConsole m_console;
/**
* constructor
*
* @param projectToMapperMap
* Mapping from projects to import to corresponding param
* name mappers.
* @param projectToCompCacheMap
* Mapping from projects to import to corresponding
* component name caches.
* @param console
* The console to use to display progress and
* error messages.
*/
public CompleteImportOperation(
Map<IProjectPO, INameMapper> projectToMapperMap,
Map<IProjectPO, IWritableComponentNameCache>
projectToCompCacheMap, IProgressConsole console) {
m_projectToMapperMap = projectToMapperMap;
m_projectToCompCacheMap = projectToCompCacheMap;
m_console = console;
}
/**
*
* {@inheritDoc}
*/
public void run(IProgressMonitor monitor) throws InterruptedException {
SubMonitor subMonitor = SubMonitor.convert(
monitor, Messages.ImportFileBPImporting,
m_projectToMapperMap.size());
if (checkImportProblems()) {
return;
}
for (final IProjectPO proj : m_projectToMapperMap.keySet()) {
if (subMonitor.isCanceled()) {
throw new InterruptedException();
}
String projectName = proj.getDisplayName();
showStartingImport(m_console, projectName);
try {
m_wasImportSuccessful =
importProject(proj, subMonitor.newChild(1));
showFinishedImport(m_console, projectName);
} catch (PMSaveException e) {
LOG.warn(Messages.ErrorWhileImportingProject, e);
JBException gde = new JBException(
e + StringConstants.SPACE + StringConstants.COLON
+ Messages.SaveOf + proj.getName()
+ StringConstants.SPACE + Messages.Failed,
MessageIDs.E_IMPORT_PROJECT_XML_FAILED);
showErrorDuringImport(m_console, projectName, gde);
ErrorMessagePresenter.getPresenter().showErrorMessage(
gde, new String [] {proj.getName()}, null);
} catch (PMException pme) {
LOG.warn(Messages.ErrorWhileImportingProject, pme);
JBException gde = new JBException(
pme + Messages.ImportOf + proj.getName()
+ StringConstants.SPACE + Messages.Failed,
MessageIDs.E_IMPORT_PROJECT_XML_FAILED);
showErrorDuringImport(m_console, projectName, gde);
ErrorMessagePresenter.getPresenter().showErrorMessage(
gde, new String [] {proj.getName()}, null);
} catch (ProjectDeletedException e) {
JBException gde = new JBException(
e + Messages.ImportOf + proj.getName()
+ StringConstants.SPACE + Messages.Failed,
MessageIDs.E_ALREADY_DELETED_PROJECT);
showErrorDuringImport(m_console, projectName, gde);
ErrorMessagePresenter.getPresenter().showErrorMessage(
gde, new String [] {proj.getName()}, null);
}
}
showFinishedImport(m_console);
}
/**
* Checks the list of projects to import for problems. Handles problems
* by displaying an error message to the user.
*
* @return <code>true</code> if a problem was found, meaning that the
* operation cannot complete successfully. Otherwise,
* <code>false</code>.
*/
private boolean checkImportProblems() {
Map<String, String> guidToNameMap = new HashMap<String, String>();
if (checkImportedProjects(guidToNameMap)) {
return true;
}
EntityManager circularDependencyCheckSess =
Persistor.instance().openSession();
// if a name/guid conflict occurs
// then show error message(s) and cancel
try {
if (checkNameGuidConflict(guidToNameMap)) {
return true;
}
// check for reusable project problems (circular dependencies)
if (checkCircularDependencies(circularDependencyCheckSess)) {
return true;
}
} catch (PMException pme) {
ErrorMessagePresenter.getPresenter().showErrorMessage(
new JBException(
pme + Messages.ImportFailed,
MessageIDs.E_DATABASE_GENERAL),
null, null);
return true;
} finally {
Persistor.instance().dropSessionWithoutLockRelease(
circularDependencyCheckSess);
}
return false;
}
/**
* @param circularDependencyCheckSess The session to use for getting
* projects from the database.
* @return <code>true</code> if any circular dependencies are found.
* Otherwise <code>false</code>.
*/
private boolean checkCircularDependencies(
EntityManager circularDependencyCheckSess) {
for (final IProjectPO proj : m_projectToMapperMap.keySet()) {
Set<IProjectPO> checkedProjects = new HashSet<IProjectPO>();
Set<IProjectPO> illegalProjects = new HashSet<IProjectPO>();
illegalProjects.add(proj);
Set<IProjectPO> projectsToCheck = new HashSet<IProjectPO>();
for (IReusedProjectPO reused : proj.getUsedProjects()) {
IProjectPO reusedProject = null;
for (IProjectPO importedProject
: m_projectToMapperMap.keySet()) {
if (reused.getProjectGuid().equals(
importedProject.getGuid())
&& reused.getProjectVersion().equals(
importedProject.getProjectVersion())) {
reusedProject = importedProject;
break;
}
}
if (reusedProject == null) {
try {
reusedProject = ProjectPM.loadReusedProject(
reused, circularDependencyCheckSess);
} catch (JBException e) {
// We can't detect circular dependencies from a
// project if we can't load it from the db.
// Report to the user that the error will
// cause the import to abort.
handleCircularDependency(m_console, proj.getName());
return true;
}
}
if (reusedProject != null) {
projectsToCheck.add(reusedProject);
}
}
for (IProjectPO projToCheck : projectsToCheck) {
ProjectPM.findIllegalProjects(projToCheck,
checkedProjects, illegalProjects,
m_projectToMapperMap.keySet());
}
illegalProjects.remove(proj);
if (!illegalProjects.isEmpty()) {
handleCircularDependency(m_console, proj.getName());
return true;
}
}
return false;
}
/**
*
* @return <code>true</code> if the import succeeded. Otherwise
* <code>false</code>
*/
public boolean wasImportSuccessful() {
return m_wasImportSuccessful;
}
/**
* @param proj
* The project to import.
* @param monitor
* The progress monitor for this operation.
* @return <code>true</code> if the project was successfully imported.
* Returns <code>false</code> if their were conflicts that
* prevented the project from being successfully imported.
* @throws PMException
* in case of any db error
* @throws ProjectDeletedException
* if project is already deleted
* @throws InterruptedException
* if the operation was canceled
*/
private boolean importProject(IProjectPO proj,
IProgressMonitor monitor)
throws PMException, ProjectDeletedException,
InterruptedException {
// if (import.guid exists and guid->version == import.version)
// then show error message and cancel
if (projectExists(proj.getGuid(), proj.getMajorProjectVersion(),
proj.getMinorProjectVersion(), proj.getMicroProjectVersion(),
proj.getProjectVersionQualifier())) {
String projectNameToImport = proj.getName();
handleProjectExists(
m_console,
ProjectNameBP.getInstance().getName(proj.getGuid(), false),
projectNameToImport,
proj.getProjectVersion());
return false;
}
String selectedProjectName =
checkProjectAndRename(proj.getGuid(), proj.getName());
if (selectedProjectName != null) {
// Import project
proj.setClientMetaDataVersion(
IVersion.JB_CLIENT_METADATA_VERSION);
boolean willRequireRefresh = false;
IProjectPO currentProject =
GeneralStorage.getInstance().getProject();
if (currentProject != null) {
for (IReusedProjectPO reused
: currentProject.getUsedProjects()) {
if (m_isRefreshRequired || willRequireRefresh) {
break;
}
String guid = reused.getProjectGuid();
willRequireRefresh = proj.getGuid().equals(guid)
&& proj.getProjectVersion().equals(
reused.getProjectVersion());
}
m_isRefreshRequired = willRequireRefresh
|| m_isRefreshRequired;
}
monitor.beginTask(StringConstants.EMPTY, getTotalWork(proj));
monitor.subTask(Messages.ImportFileBPSaveToDB);
// Register Persistence (JPA / EclipseLink) progress listeners
ProgressMonitorTracker tracker =
ProgressMonitorTracker.SINGLETON;
tracker.setProgressMonitor(monitor);
IWritableComponentNameCache compNameCache =
m_projectToCompCacheMap.get(proj);
try {
ProjectPM.saveProject(proj, selectedProjectName,
m_projectToMapperMap.get(proj), compNameCache);
} finally {
// Remove JPA progress listeners
tracker.setProgressMonitor(null);
}
UsedToolkitBP.getInstance().refreshToolkitInfo(proj);
return true;
}
return false;
}
/**
*
* @param proj The project for which to find the required work
* amount.
* @return the amount of work required to save the given project to the
* database.
*/
private int getTotalWork(IProjectPO proj) {
// (project_node=1)
int totalWork = 1;
// (INodePO=1)
for (ITestSuitePO testSuite
: TestSuiteBP.getListOfTestSuites(proj)) {
totalWork += getWorkForNode(testSuite);
}
for (ISpecPersistable spec
: proj.getSpecObjCont().getSpecObjList()) {
totalWork += getWorkForNode(spec);
}
// 1 for each event type
totalWork *= NUM_HBM_PROGRESS_EVENT_TYPES;
return totalWork;
}
/**
* Recursively determines the amount of work involved in saving the
* given node to the database.
*
* @param node The node for which to determine the amount of work.
* @return the amount of work required to save the given node to the
* database.
*/
private int getWorkForNode(INodePO node) {
int work = 1;
if (!(node instanceof IExecTestCasePO)) {
Iterator<INodePO> childIter = node.getNodeListIterator();
while (childIter.hasNext()) {
work += getWorkForNode(childIter.next());
}
}
if (node instanceof ISpecTestCasePO) {
work += ((ISpecTestCasePO)node).getAllEventEventExecTC().size();
}
return work;
}
/**
* @param guidToNameMap mapping from project guids to names
* @return <code>true</code> if any name/guid conflicts are found.
* Otherwise <code>false</code>.
*/
private boolean checkImportedProjects(
Map<String, String> guidToNameMap) {
for (IProjectPO proj : m_projectToMapperMap.keySet()) {
final String projectName = proj.getName();
final String guid = proj.getGuid();
Validate.notNull(projectName, Messages.ImportWithoutName);
Validate.notEmpty(projectName, Messages.ImportEmptyName);
// Check for name/guid conflicts among the imported projects as
// we go.
if (isSameGuidOtherName(guidToNameMap, projectName, guid)) {
// Same guid, different name
handleGuidConflict(projectName, guidToNameMap.get(guid));
return true;
} else if (isOtherGuidSameName(guidToNameMap, projectName,
guid)) {
// Different guid, same name
handleNameConflict(projectName);
return true;
} else {
guidToNameMap.put(guid, projectName);
}
}
return false;
}
/**
* Checks if the mapping contains an entry that conflicts with given
* name and guid.
*
* @param guidToNameMap The mapping to check against.
* @param projectName The name to check.
* @param guid The guid to check.
* @return <code>true</code> if there exists an entry in the mapping
* such that name<code>.equals(projectName)</code> and
* not guid<code>.equals(guid)</code>. Otherwise
* <code>false</code>.
*/
private boolean isOtherGuidSameName(Map<String, String> guidToNameMap,
final String projectName, final String guid) {
return guidToNameMap.containsValue(projectName)
&& !projectName.equals(guidToNameMap.get(guid));
}
/**
* Checks if the mapping contains an entry that conflicts with given
* name and guid.
*
* @param guidToNameMap The mapping to check against.
* @param projectName The name to check.
* @param guid The guid to check.
* @return <code>true</code> if there exists an entry in the mapping
* such that not name<code>.equals(projectName)</code> and
* guid<code>.equals(guid)</code>. Otherwise
* <code>false</code>.
*/
private boolean isSameGuidOtherName(
Map<String, String> guidToNameMap, final String projectName,
final String guid) {
return guidToNameMap.containsKey(guid)
&& !projectName.equals(guidToNameMap.get(guid));
}
/**
* Creates an error dialog.
*
* @param name The name that is causing the conflict.
*/
private void handleNameConflict(String name) {
ErrorMessagePresenter.getPresenter().showErrorMessage(
MessageIDs.E_PROJ_NAME_CONFLICT,
new String [] {}, new String [] {name});
}
/**
* Creates an error dialog.
*
* @param console
* The console to use to display progress and
* error messages.
* @param name The name of the project that is causing the problem.
*/
private void handleCircularDependency(
IProgressConsole console, String name) {
console.writeErrorLine(
NLS.bind(Messages.ErrorMessagePROJ_CIRC_DEPEND, name));
ErrorMessagePresenter.getPresenter().showErrorMessage(
MessageIDs.E_PROJ_CIRC_DEPEND, new String [] {name}, null);
}
/**
* Checks that the import will not create any name/GUID conflicts. If
* a conflict would be caused, this method will attempt to rename the
* project.
*
* @param guid The guid of the project to check.
* @param projectName The name of the project to check.
* @return a name if the import will not cause conflicts
* (either because no conflicts existed or because the project
* was successfully renamed such that no more conflicts exist).
* Otherwise <code>null</code>.
*/
private String checkProjectAndRename(
String guid, final String projectName) {
String selectedName = projectName;
// if (import.guid exists and guid->name != import.name)
// then show error message and offer to rename project
// : options are guid->name and import.name
String existingNameForGuid =
ProjectNameBP.getInstance().getName(guid);
if (existingNameForGuid != null
&& !existingNameForGuid.equals(projectName)) {
if (ProjectPM.doesProjectNameExist(projectName)) {
ArrayList<String> possibleNames = new ArrayList<String>(1);
possibleNames.add(existingNameForGuid);
selectedName =
projectNameConflictResolver.resolveNameConflict(
possibleNames);
} else {
String [] possibleNames = new String [] {
existingNameForGuid, projectName};
selectedName =
projectNameConflictResolver.resolveNameConflict(
Arrays.asList(possibleNames));
}
} else if (ProjectPM.doesProjectNameExist(projectName)
&& !projectName.equals(existingNameForGuid)) {
// if (import.name exists and name->guid != import.guid)
// then show error message and offer to rename project
ArrayList<String> possibleNames = new ArrayList<String>(1);
possibleNames.add(existingNameForGuid);
selectedName =
projectNameConflictResolver.resolveNameConflict(
possibleNames);
}
return selectedName;
}
/**
* Checks whether there are any name/guid conflicts between the given
* project information and the projects currently existing in the
* database.
*
* @param guidToNameMap mapping of imported project guids to names
* @return <code>true</code> if the given project information contains
* any name/guid conflicts. Otherwise <code>false</code>.
*/
private boolean checkNameGuidConflict(
Map<String, String> guidToNameMap) throws PMException {
Map<String, String> dbGuidToNameMap =
ProjectNameBP.getInstance().readAllProjectNamesFromDB();
for (String guid : guidToNameMap.keySet()) {
if (isOtherGuidSameName(dbGuidToNameMap,
guidToNameMap.get(guid), guid)) {
handleNameConflict(guidToNameMap.get(guid));
return true;
} else if (isSameGuidOtherName(dbGuidToNameMap, guid,
guidToNameMap.get(guid))) {
handleGuidConflict(guidToNameMap.get(guid),
dbGuidToNameMap.get(guid));
return true;
}
}
return false;
}
/**
* Displays an error dialog.
*
* @param importName name of the imported proejct causing the guid conflict
* @param existingName name of the existing project causing the guid conflict
*/
private void handleGuidConflict(
String importName, String existingName) {
ErrorMessagePresenter.getPresenter().showErrorMessage(
MessageIDs.E_PROJ_GUID_CONFLICT,
new String [0], new String [] {importName, existingName});
}
/**
* Checks whether the currently imported project already exists in the
* database.
*
* @param guid
* GUID to check
* @param majorNumber
* Major version number to check
* @param minorNumber
* Minor version number to check
* @param microNumber The micro version number to check
* @param versionQualifier The version qualifier to check
* @return <code>true</code> if another project with the same GUID and
* version number as the currently imported project already
* exists in the database. Otherwise <code>false</code>.
*/
private boolean projectExists(String guid, Integer majorNumber,
Integer minorNumber, Integer microNumber,
String versionQualifier) {
return ProjectPM.doesProjectVersionExist(guid, majorNumber,
minorNumber, microNumber, versionQualifier);
}
/**
* Writes an error to the console.
*
* @param console
* The console to use to display progress and
* error messages.
* @param existingName
* Name of the project that already exists in the database
* @param importName
* Name of the project that is being imported
* @param version the project version
*/
private void handleProjectExists(IProgressConsole console,
String existingName, String importName,
ProjectVersion version) {
console.writeErrorLine(
NLS.bind(Messages.ErrorMessageIMPORT_PROJECT_FAILED,
importName));
console.writeErrorLine(NLS.bind(
Messages.ErrorMessageIMPORT_PROJECT_FAILED_EXISTING,
new String[] { existingName, version.toString() }));
}
}
/**
* Performs a project import by starting either a complete import operation
* or a partial import operation, as appropriate.
*
* @author BREDEX GmbH
* @created Jan 9, 2008
*/
private static class ImportOperation implements IRunnableWithProgress {
/** mapping: projects to import => corresponding name mappers */
private Map<IProjectPO, INameMapper> m_projectToMapperMap;
/** mapping: projects to import => corresponding comp name cache*/
private Map<IProjectPO, IWritableComponentNameCache>
m_projectToCompCacheMap;
/** the project to open immediately after import */
private IProjectPO m_projectToOpen = null;
/** the console used for reporting progress and errors */
private IProgressConsole m_console;
/** flag for whether to open a project immediately after import */
private boolean m_isOpenProject;
/**
* Constructor
* @param projectToMapperMap
* Mapping from projects to import to corresponding param
* name mappers.
* @param projectToCompCacheMap
* Mapping from projects to import to corresponding
* component name caches.
* @param console
* The console to use to display progress and
* error messages.
* @param openProject
* Flag indicating whether the imported project should be
* immediately opened after import.
*/
public ImportOperation(Map<IProjectPO,
INameMapper> projectToMapperMap,
Map<IProjectPO, IWritableComponentNameCache>
projectToCompCacheMap,
IProgressConsole console, boolean openProject) {
m_projectToMapperMap = projectToMapperMap;
m_projectToCompCacheMap = projectToCompCacheMap;
m_console = console;
m_isOpenProject = openProject;
}
/**
*
* @return the imported project to open, or <code>null</code> if no project should be opened.
*/
public IProjectPO getProjectToOpen() {
return m_projectToOpen;
}
/**
* {@inheritDoc}
*/
public void run(IProgressMonitor monitor) throws InterruptedException {
try {
// run() is used directly here rather than
// starting a new monitor. We want the operation to run
// within this monitor.
NodePM.getInstance().setUseCache(true);
CompleteImportOperation op = new CompleteImportOperation(
m_projectToMapperMap, m_projectToCompCacheMap,
m_console);
op.run(monitor);
if (op.wasImportSuccessful() && m_isOpenProject) {
for (IProjectPO project
: m_projectToMapperMap.keySet()) {
m_projectToOpen = project;
break;
}
}
} catch (final ConfigXmlException ce) {
handleCapDataNotFound(ce);
} finally {
NodePM.getInstance().setUseCache(false);
monitor.done();
}
}
}
/** the logger */
public static final Logger LOG =
LoggerFactory.getLogger(FileStorageBP.class);
/** the total amount of work for an import operation */
private static final int TOTAL_IMPORT_WORK = 100;
/** number of Persistence (JPA / EclipseLink) event types with progress listeners */
// Event types:
// save, recreateCollection, postInsert, postUpdate
private static final int NUM_HBM_PROGRESS_EVENT_TYPES = 4;
/** the amount of work required to read and parse xml files into related domain objects */
private static final int PARSE_FILES_WORK = 95;
/** the amount of work required to save and commit domain objects to the db */
private static final int SAVE_TO_DB_WORK =
TOTAL_IMPORT_WORK - PARSE_FILES_WORK;
/**
* responsible for resolving project name conflicts that occur
* during import
*/
private static IProjectNameConflictResolver projectNameConflictResolver =
new NullProjectNameConflictResolver();
/**
* Private constructor for utility class.
*/
private FileStorageBP() {
// Nothing to initialize
}
/**
* @param projectList The list of projects to export
* @param exportDirName The export directory of the projects
* @param exportSession The session to be used for Persistence (JPA / EclipseLink)
* @param monitor The progress monitor
* @param writeToSystemTempDir Indicates whether the projects have to be
* written to the system temp directory
* @param listOfProjectFiles The written project files are added to this
* list, if the temp dir was used and the list
* is not null.
* @param console
* The console to use to display progress and
* error messages.
*/
public static void exportProjectList(List<IProjectPO> projectList,
String exportDirName, EntityManager exportSession,
IProgressMonitor monitor, boolean writeToSystemTempDir,
List<File> listOfProjectFiles, IProgressConsole console)
throws JBException, InterruptedException {
SubMonitor subMonitor = SubMonitor.convert(monitor,
Messages.ExportAllBPExporting,
XmlStorage.getWorkToSave(projectList));
for (IProjectPO proj : projectList) {
if (subMonitor.isCanceled()) {
throw new InterruptedException();
}
IProjectPO projectToExport =
ProjectPM.loadProjectByIdAndPreLoad(
proj.getId(), exportSession);
String projectFileName = projectToExport.getDisplayName() + JUB;
final String exportFileName;
if (writeToSystemTempDir) {
exportFileName = projectFileName;
} else {
if (projectToExport.equals(
GeneralStorage.getInstance().getProject())) {
// project is current project
projectToExport =
GeneralStorage.getInstance().getProject();
}
exportFileName = exportDirName + projectFileName;
}
if (subMonitor.isCanceled()) {
throw new InterruptedException();
}
console.writeStatus(new Status(IStatus.INFO, Activator.PLUGIN_ID,
NLS.bind(Messages.ExportAllBPInfoStartingExportProject,
projectFileName)));
try {
if (subMonitor.isCanceled()) {
throw new InterruptedException();
}
JsonStorage.save(projectToExport, exportFileName,
true, subMonitor.newChild(1), console);
if (subMonitor.isCanceled()) {
throw new InterruptedException();
}
console.writeStatus(new Status(IStatus.INFO,
Activator.PLUGIN_ID,
NLS.bind(Messages.ExportAllBPInfoFinishedExportProject,
projectFileName)));
} catch (final PMSaveException e) {
LOG.error(Messages.CouldNotExportProject, e);
console.writeStatus(new Status(IStatus.INFO,
Activator.PLUGIN_ID,
NLS.bind(Messages.ExportAllBPErrorExportFailedProject,
new Object [] {projectFileName,
e.getMessage()})));
}
exportSession.detach(projectToExport);
}
}
/** allow importing some files
*
* @param importProjectURLs list of file URLs. Each URL must be valid.
* @param monitor The progress monitor for the operation.
* @param console
* The console to use to display progress and
* error messages.
* @param openProject
* Flag indicating whether the imported project should be
* immediately opened after import.
*/
public static void importFiles(List<URL> importProjectURLs,
IProgressMonitor monitor, IProgressConsole console,
boolean openProject) {
// import all data from projects
try {
doImport(importProjectURLs,
SubMonitor.convert(monitor), console, openProject);
} catch (InterruptedException e) {
// Operation was canceled. Do nothing.
}
}
/**
* Imports a chosen project from a file.
* @param fileURLs
* The URLs of the files to import.
* @param monitor
* The progress monitor for the operation.
* @param console
* The console to use to display progress and
* error messages.
* @param openProject
* Flag indicating whether the imported project should be
* immediately opened after import.
*
* @return the project to open immediately after import, or
* <code>null</code> if no project should be opened.
* @throws InterruptedException if the operation was canceled or the thread
* was interrupted.
*/
public static IProjectPO importProject(final List<URL> fileURLs,
IProgressMonitor monitor, IProgressConsole console,
boolean openProject)
throws InterruptedException {
SubMonitor subMonitor = SubMonitor.convert(monitor,
Messages.ImportFileBPImporting, TOTAL_IMPORT_WORK);
return doImport(fileURLs, subMonitor, console, openProject);
}
/**
* actually do the import work. Separated to only batch calls
* @param fileURLs
* The URLs of the files to import.
* @param subMonitor @see #importProject(int)
* @param console
* The console to use to display progress and
* error messages.
* @param openProject
* Flag indicating whether the imported project should be
* immediately opened after import.
* @return the project to open immediately after import, or
* <code>null</code> if no project should be opened.
* @throws InterruptedException @see #importProject(int)
*/
private static IProjectPO doImport(List<URL> fileURLs,
SubMonitor subMonitor, IProgressConsole console,
boolean openProject)
throws InterruptedException {
// Read project files
ReadFilesOperation readFilesOp =
new ReadFilesOperation(fileURLs, console);
readFilesOp.run(subMonitor.newChild(PARSE_FILES_WORK));
// Import projects
ImportOperation importOp = new ImportOperation(
readFilesOp.getProjectToMapperMap(),
readFilesOp.getProjectToCompCacheMap(),
console, openProject);
importOp.run(subMonitor.newChild(SAVE_TO_DB_WORK));
return importOp.getProjectToOpen();
}
/**
* Report to the user that an error occurred while importing the project.
*
* @param console
* The console to use to display progress and
* error messages.
* @param projectFileName The filename of the project that was being
* imported.
* @param e The error that occurred.
*/
private static void showErrorDuringImport(IProgressConsole console,
String projectFileName, Exception e) {
console.writeErrorLine(
NLS.bind(Messages.ImportFileActionErrorImportFailedProject,
new Object [] {projectFileName, e.getMessage()}));
}
/**
* Report to the user that all projects have been imported.
*
* @param console
* The console to use to display progress and
* error messages.
*/
private static void showFinishedImport(IProgressConsole console) {
console.writeLine(
Messages.ImportFileActionInfoFinishedImport);
}
/**
* Report to the user that the project has been imported.
*
* @param console
* The console to use to display progress and
* error messages.
* @param projectFileName The filename of the imported project.
*/
private static void showFinishedImport(IProgressConsole console,
String projectFileName) {
console.writeLine(
NLS.bind(Messages.ImportFileActionInfoFinishedImportProject,
projectFileName));
}
/**
* Report to the user that all projects to import have been analyzed.
*
* @param console
* The console to use to display progress and
* error messages.
*/
private static void showFinishedReadingProjects(IProgressConsole console) {
console.writeLine(
Messages.ImportFileActionInfoFinishedReadingProjects);
}
/**
* Report to the user that the import process is beginning.
*
* @param console
* The console to use to display progress and
* error messages.
*/
private static void showStartingImport(IProgressConsole console) {
console.writeLine(
Messages.ImportFileActionInfoStartingImport);
}
/**
* Report to the user that the project is being imported.
*
* @param console
* The console to use to display progress and
* error messages.
* @param projectFileName The filename of the imported project.
*/
private static void showStartingImport(IProgressConsole console,
String projectFileName) {
console.writeLine(
NLS.bind(Messages.ImportFileActionInfoStartingImportProject,
projectFileName));
}
/**
* Report to the user that all projects to import will be analyzed.
*
* @param console
* The console to use to display progress and
* error messages.
*/
private static void showStartingReadingProjects(IProgressConsole console) {
console.writeLine(
Messages.ImportFileActionInfoStartingReadingProjects);
}
/**
*
* @param resolver The new conflict resolver.
*/
public static void setProjectNameConflictResolver(
IProjectNameConflictResolver resolver) {
Validate.notNull(resolver);
projectNameConflictResolver = resolver;
}
/**
* @param e PMReadException
* @param fileURLs The URLs of the files that were being imported.
*/
private static void handlePMReadException(final PMReadException e,
final List<URL> fileURLs) {
ErrorMessagePresenter.getPresenter().showErrorMessage(
new JBException(e + Messages.Reading + fileURLs.toArray()
+ Messages.Failed,
MessageIDs.E_IMPORT_XML_FAILED), null,
MessageIDs.getMessageObject(e.getErrorId()).getDetails());
}
/**
* Create an appropriate error dialog.
*
* @param ce The exception that prevented the import of the
* project.
*/
private static void handleCapDataNotFound(final ConfigXmlException ce) {
ErrorMessagePresenter.getPresenter().showErrorMessage(
MessageIDs.E_IMPORT_PROJECT_CONFIG_CONFLICT,
null, new String[] {ce.getMessage()});
}
/**
* Create an error dialog
* @param errorMessage msg to show
*/
private static void handleUnsupportedToolkits(String errorMessage) {
ErrorMessagePresenter.getPresenter().showErrorMessage(
MessageIDs.E_UNSUPPORTED_TOOLKIT,
null, new String[] {errorMessage});
}
}