org.eclipse.jubula.client.archive.businessprocess.FileStorageBP.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jubula.client.archive.businessprocess.FileStorageBP.java

Source

/*******************************************************************************
 * 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.lang.reflect.InvocationTargetException;
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 javax.persistence.EntityTransaction;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
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.businessprocess.ComponentNamesBP;
import org.eclipse.jubula.client.core.businessprocess.ComponentNamesDecorator;
import org.eclipse.jubula.client.core.businessprocess.IComponentNameMapper;
import org.eclipse.jubula.client.core.businessprocess.INameMapper;
import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameMapper;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBP;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBPDecorator;
import org.eclipse.jubula.client.core.businessprocess.ProjectComponentNameMapper;
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.events.DataEventDispatcher;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.UpdateState;
import org.eclipse.jubula.client.core.model.ICategoryPO;
import org.eclipse.jubula.client.core.model.IComponentNamePO;
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.NodeMaker;
import org.eclipse.jubula.client.core.persistence.CompNamePM;
import org.eclipse.jubula.client.core.persistence.GeneralStorage;
import org.eclipse.jubula.client.core.persistence.ISpecPersistable;
import org.eclipse.jubula.client.core.persistence.IncompatibleTypeException;
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.businessprocess.ToolkitSupportBP;
import org.eclipse.jubula.toolkit.common.exception.ToolkitPluginException;
import org.eclipse.jubula.toolkit.common.utils.ToolkitUtils;
import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder;
import org.eclipse.jubula.tools.constants.StringConstants;
import org.eclipse.jubula.tools.exception.ConfigXmlException;
import org.eclipse.jubula.tools.exception.JBException;
import org.eclipse.jubula.tools.exception.JBVersionException;
import org.eclipse.jubula.tools.exception.ProjectDeletedException;
import org.eclipse.jubula.tools.jarutils.IVersion;
import org.eclipse.jubula.tools.messagehandling.MessageIDs;
import org.eclipse.jubula.tools.xml.businessmodell.CompSystem;
import org.eclipse.jubula.tools.xml.businessmodell.ToolkitPluginDescriptor;
import org.eclipse.osgi.util.NLS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author BREDEX GmbH
 * @created Jun 4, 2010
 */
public class FileStorageBP {

    /**
     * Reads XML files and parses them into related domain objects.
     * 
     * @author BREDEX GmbH
     * @created Jan 9, 2008
     */
    private static class ReadFilesOperation implements IRunnableWithProgress {

        /** indicates what part(s) of the project(s) will be imported */
        private boolean m_isImportWholeProjects;

        /** 
         * mapping: projects to import => corresponding param name mapper 
         */
        private Map<IProjectPO, List<INameMapper>> m_projectToMapperMap;

        /** 
         * mapping: projects to import => corresponding component name mapper 
         */
        private Map<IProjectPO, List<IWritableComponentNameMapper>> m_projectToCompMapperMap;

        /** 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 isImportWholeProjects 
         *              <code>true</code> if entire projects are being imported.
         *              <code>false</code> if only components (TCs, AUTs, etc.)
         *              are being imported.
         * @param fileURLs
         *              URLs of the project files to read.
         * @param console
         *              The console to use to display pogress and 
         *              error messages.
         */
        public ReadFilesOperation(boolean isImportWholeProjects, List<URL> fileURLs, IProgressConsole console) {
            m_isImportWholeProjects = isImportWholeProjects;
            m_fileURLs = fileURLs;
            m_projectToMapperMap = new LinkedHashMap<IProjectPO, List<INameMapper>>();
            m_projectToCompMapperMap = new LinkedHashMap<IProjectPO, List<IWritableComponentNameMapper>>();
            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 ComponentNamesDecorator(null);
                    String fileName = fileURL.getFile();
                    lastFileName = fileName;
                    m_console.writeLine(NLS.bind(Messages.ImportFileActionInfoStartingReadingProject, fileName));
                    try {
                        IProjectPO proj = new XmlStorage().readProject(fileURL, paramNameMapper, compNameCache,
                                !m_isImportWholeProjects, subMonitor.newChild(1), m_console);
                        List<INameMapper> mapperList = new ArrayList<INameMapper>();
                        List<IWritableComponentNameMapper> compNameMapperList = new ArrayList<IWritableComponentNameMapper>();
                        mapperList.add(paramNameMapper);
                        compNameMapperList.add(new ProjectComponentNameMapper(compNameCache, proj));
                        m_projectToMapperMap.put(proj, mapperList);
                        m_projectToCompMapperMap.put(proj, compNameMapperList);
                    } catch (JBVersionException e) {
                        for (Object msg : e.getErrorMsgs()) {
                            m_console.writeErrorLine((String) msg);
                        }
                        m_console.writeErrorLine(NLS.bind(Messages.ImportFileActionErrorImportFailed, fileName));
                    }
                }
                showFinishedReadingProjects(m_console);
            } catch (final PMReadException e) {
                m_console.writeErrorLine(NLS.bind(Messages.ImportFileActionErrorImportFailedProject, lastFileName,
                        StringConstants.TAB + Messages.InvalidImportFile));
                handlePMReadException(e, m_fileURLs);
            } catch (final ConfigXmlException ce) {
                handleCapDataNotFound(ce);
            } finally {
                monitor.done();
            }
        }

        /**
         * 
         * @return the projects to import, as read from the project files.
         */
        public Map<IProjectPO, List<INameMapper>> getProjectToMapperMap() {
            return m_projectToMapperMap;
        }

        /**
         * 
         * @return the mapping between projects to import and their 
         *         corresponding component name mapper
         */
        public Map<IProjectPO, List<IWritableComponentNameMapper>> getProjectToCompCacheMap() {

            return m_projectToCompMapperMap;
        }
    }

    /**
     * imports an entire project
     * 
     * @author BREDEX GmbH
     * 
     */
    private static class CompleteImportOperation implements IRunnableWithProgress {

        /** mapping: projects to import => corresponding param name mapper */
        private Map<IProjectPO, List<INameMapper>> m_projectToMapperMap;

        /** mapping: projects to import => corresponding comp name mapper */
        private Map<IProjectPO, List<IWritableComponentNameMapper>> 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 mappers.
         * @param console
         *              The console to use to display pogress and 
         *              error messages.
         */
        public CompleteImportOperation(Map<IProjectPO, List<INameMapper>> projectToMapperMap,
                Map<IProjectPO, List<IWritableComponentNameMapper>> 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.getMajorNumber().equals(importedProject.getMajorProjectVersion())
                                && reused.getMinorNumber().equals(importedProject.getMinorProjectVersion())) {

                            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())) {
                String projectNameToImport = proj.getName();
                handleProjectExists(m_console, ProjectNameBP.getInstance().getName(proj.getGuid(), false),
                        projectNameToImport, proj.getMajorProjectVersion(), proj.getMinorProjectVersion());
                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();
                        Integer majorVersion = reused.getMajorNumber();
                        Integer minorVersion = reused.getMinorNumber();
                        willRequireRefresh = proj.getGuid().equals(guid)
                                && proj.getMajorProjectVersion().equals(majorVersion)
                                && proj.getMinorProjectVersion().equals(minorVersion);

                    }
                    m_isRefreshRequired = willRequireRefresh || m_isRefreshRequired;
                }

                // Register Persistence (JPA / EclipseLink) progress listeners
                ProgressMonitorTracker.getInstance().setProgressMonitor(monitor);
                monitor.beginTask(StringConstants.EMPTY, getTotalWork(proj));
                List<INameMapper> mapperList = m_projectToMapperMap.get(proj);
                List<IWritableComponentNameMapper> compNameBindingList = m_projectToCompCacheMap.get(proj);
                try {
                    ProjectPM.saveProject(proj, selectedProjectName, mapperList, compNameBindingList);
                } finally {
                    // Remove JPA progress listeners
                    ProgressMonitorTracker.getInstance().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 pogress 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
         * @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) {

            return ProjectPM.doesProjectVersionExist(guid, majorNumber, minorNumber);
        }

        /**
         * Writes an error to the console.
         * 
         * @param console
         *              The console to use to display pogress 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 majNum Project major number
         * @param minNum Project minor number
         */
        private void handleProjectExists(IProgressConsole console, String existingName, String importName,
                Integer majNum, Integer minNum) {

            console.writeErrorLine(
                    NLS.bind(Messages.ErrorMessageIMPORT_PROJECT_XML_FAILED, new String[] { importName }));
            console.writeErrorLine(NLS.bind(Messages.ErrorMessageIMPORT_PROJECT_XML_FAILED_EXISTING,
                    new String[] { existingName, String.valueOf(majNum), String.valueOf(minNum) }));
        }

    }

    /**
     * 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 {

        /** indicates what part(s) of the project(s) will be imported */
        private int m_elements;

        /** mapping: projects to import => corresponding name mapper List */
        private Map<IProjectPO, List<INameMapper>> m_projectToMapperMap;

        /** mapping: projects to import => corresponding comp name cache List */
        private Map<IProjectPO, List<IWritableComponentNameMapper>> 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 elements
         *            What to import ? 0 = all >0 = elements
         * @param projectToMapperMap
         *            Mapping from projects to import to corresponding param
         *            name mappers.
         * @param projectToCompCacheMap
         *            Mapping from projects to import to corresponding 
         *            component name mappers.
         * @param console
         *              The console to use to display pogress and 
         *              error messages.
         * @param openProject
         *            Flag indicating whether the imported project should be 
         *            immediately opened after import.
         */
        public ImportOperation(int elements, Map<IProjectPO, List<INameMapper>> projectToMapperMap,
                Map<IProjectPO, List<IWritableComponentNameMapper>> projectToCompCacheMap, IProgressConsole console,
                boolean openProject) {

            m_elements = elements;
            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, InvocationTargetException {
            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);

                if (m_elements == FileStorageBP.IMPORT_ALL) {
                    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;
                        }
                    }
                } else {
                    new PartsImportOperation(m_projectToMapperMap, m_projectToCompCacheMap, m_elements, m_console)
                            .run(monitor);
                }
            } catch (final ConfigXmlException ce) {
                handleCapDataNotFound(ce);
            } finally {
                NodePM.getInstance().setUseCache(false);
                monitor.done();
            }
        }
    }

    /**
     * merge parts of loaded project into active project
     * @author BREDEX GmbH
     */
    private static class PartsImportOperation implements IRunnableWithProgress {

        /** elements to import */
        private int m_elements;

        /** mapping: projects to import => corresponding param name mapper */
        private Map<IProjectPO, List<INameMapper>> m_projectToMapperMap;

        /** mapping: projects to import => corresponding comp name cache List */
        private Map<IProjectPO, List<IWritableComponentNameMapper>> m_projectToCompCacheMap;

        /** 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 projectToCompMapperMap
         *            Mapping from projects to import to corresponding 
         *            Component Name mappers.
         * @param elements elements
         * @param console
         *              The console to use to display pogress and 
         *              error messages.
         */
        public PartsImportOperation(Map<IProjectPO, List<INameMapper>> projectToMapperMap,
                Map<IProjectPO, List<IWritableComponentNameMapper>> projectToCompMapperMap, int elements,
                IProgressConsole console) {

            m_projectToMapperMap = projectToMapperMap;
            m_projectToCompCacheMap = projectToCompMapperMap;
            m_elements = elements;
            m_console = console;
        }

        /**
         * 
         * {@inheritDoc}
         */
        public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException {
            SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.ImportFileBPImporting,
                    m_projectToMapperMap.size());
            try {
                for (IProjectPO proj : m_projectToMapperMap.keySet()) {
                    if (subMonitor.isCanceled()) {
                        throw new InterruptedException();
                    }
                    if ((m_elements & FileStorageBP.IMPORT_TESTCASES) == FileStorageBP.IMPORT_TESTCASES) {

                        List<INameMapper> mapperList = m_projectToMapperMap.get(proj);
                        List<IWritableComponentNameMapper> compMapperList = m_projectToCompCacheMap.get(proj);
                        String projectName = proj.getDisplayName();
                        showStartingImport(m_console, projectName);
                        // removes reuse in TestSuites
                        List<ISpecPersistable> specObjList = proj.getSpecObjCont().getSpecObjList();
                        IProjectPO project = GeneralStorage.getInstance().getProject();
                        String importedToolkit = proj.getToolkit();
                        try {
                            String importedLevel = ToolkitSupportBP.getToolkitLevel(importedToolkit);
                            String currentToolkit = project.getToolkit();
                            String currentLevel = project.getToolkitLevel();

                            tryImport(subMonitor, mapperList, compMapperList, projectName, specObjList, project,
                                    importedToolkit, importedLevel, currentToolkit, currentLevel);
                        } catch (ToolkitPluginException e) {
                            showErrorDuringImport(m_console, proj.getDisplayName(), e);
                        } catch (IncompatibleTypeException ite) {
                            ErrorMessagePresenter.getPresenter().showErrorMessage(ite, ite.getErrorMessageParams(),
                                    null);
                        }
                    }
                }
            } catch (PMException e) {
                showAbortImport(m_console, e);
                throw new InvocationTargetException(e);
            } catch (ProjectDeletedException e) {
                showAbortImport(m_console, e);
                throw new InvocationTargetException(e);
            } finally {
                // drop cache generated by importer
                ProjectNameBP.getInstance().clearCache();
            }
            showFinishedImport(m_console);
        }

        /**
         * Tries to perform the import, and displays an error message if not
         * successful. This method was created because of checkstyle's method
         * length restrictions. As such, there are a lot of arguments, and none
         * of them are well documented.
         * 
         * @param subMonitor
         *            The progress monitor.
         * @param mapperList
         *            This is responsible for mapping Parameter names from the
         *            imported test cases into the given project.
         * @param compMapperList
         *            Responsible for mapping Component Names from the imported
         *            test cases into the given project.
         * @param projectName
         *            The project name.
         * @param specObjList
         *            The specObjList.
         * @param project
         *            The project.
         * @param importedToolkit
         *            The imported toolkit.
         * @param importedLevel
         *            The imported level.
         * @param currentToolkit
         *            The current toolkit.
         * @param currentLevel
         *            The current level.
         * @throws PMException
         *             if a Persistence (JPA / EclipseLink) exception occurs.
         * @throws ProjectDeletedException
         *             if the the project was already deleted.
         * @throws InterruptedException
         *             if the operation is canceled by the user.
         * @throws IncompatibleTypeException
         * @throws ToolkitPluginException
         *             If a toolkit error occurs.
         */
        private void tryImport(SubMonitor subMonitor, List<INameMapper> mapperList,
                List<IWritableComponentNameMapper> compMapperList, String projectName,
                List<ISpecPersistable> specObjList, IProjectPO project, String importedToolkit,
                String importedLevel, String currentToolkit, String currentLevel) throws PMException,
                ProjectDeletedException, InterruptedException, IncompatibleTypeException, ToolkitPluginException {

            // Perform the import if the project toolkits are 
            // compatible...
            if (isToolkitCompatible(importedToolkit, importedLevel, currentToolkit, currentLevel)) {

                for (IWritableComponentNameMapper compMapper : compMapperList) {
                    for (IComponentNamePO added : compMapper.getCompNameCache().getNewNames()) {
                        // NOTE: there is no need to check for Component Names 
                        //       with the same GUID because we always generate 
                        //       new GUIDs when importing parts of a project.

                        // Check whether a Component Name with the same name 
                        // exists in current project. If so, append a number to
                        // make the name unique within the Project.
                        boolean nameExists = ComponentNamesBP.getInstance().getGuidForName(added.getName(),
                                project.getId()) != null;
                        int counter = 0;
                        while (nameExists) {
                            counter++;
                            nameExists = ComponentNamesBP.getInstance().getGuidForName(added.getName() + counter,
                                    project.getId()) != null;
                        }

                        if (counter != 0) {
                            added.setName(added.getName() + counter);
                        }
                    }
                }
                importTestCases(mapperList, compMapperList, specObjList, project, subMonitor.newChild(1));
                showFinishedImport(m_console, projectName);
            } else {
                // Otherwise, show the user what went wrong and
                // continue with the next project
                String currentToolkitName = getToolkitName(currentToolkit);
                String importedToolkitName = getToolkitName(importedToolkit);
                showIncompatibleToolkit(m_console, projectName, currentToolkitName, importedToolkitName);
            }
        }

        /**
         * 
         * @param toolkitId A toolkit plugin ID.
         * @return The name associated with the given ID.
         */
        private String getToolkitName(String toolkitId) throws ToolkitPluginException {

            CompSystem compSys = ComponentBuilder.getInstance().getCompSystem();
            ToolkitPluginDescriptor desc = compSys.getToolkitPluginDescriptor(toolkitId);
            if (desc == null) {
                throw new ToolkitPluginException(
                        NLS.bind(Messages.ToolkitSupportToolkitNotFound, new String[] { toolkitId }));
            }
            return desc.getName();
        }

        /**
         * Imports the given test cases into the given project.
         * 
         * @param mapperList
         *            This is responsible for mapping Parameter names from the
         *            imported test cases into the given project.
         * @param compMapperList
         *            Responsible for mapping Component Names from the imported
         *            test cases into the given project.
         * @param specObjList
         *            List of test cases to import.
         * @param project
         *            The project into which the test cases will be imported.
         * @param monitor
         *            The progress monitor for this potentially long-running
         *            operation.
         * @return <code>true</code> if the test cases were imported
         *         successfully. Otherwise, <code>false</code>.
         * @throws PMException
         *             if a database error occurs.
         * @throws ProjectDeletedException
         *             if the current project has been deleted.
         */
        private boolean importTestCases(List<INameMapper> mapperList,
                List<IWritableComponentNameMapper> compMapperList, List<ISpecPersistable> specObjList,
                IProjectPO project, IProgressMonitor monitor)
                throws PMException, ProjectDeletedException, InterruptedException, IncompatibleTypeException {

            String newName = createCategoryName(project.getSpecObjCont().getSpecObjList());
            final ICategoryPO category = NodeMaker.createCategoryPO(newName);
            if (monitor.isCanceled()) {
                throw new InterruptedException();
            }

            // Register JPA progress listeners
            ProgressMonitorTracker.getInstance().setProgressMonitor(monitor);

            monitor.beginTask(StringConstants.EMPTY, getTotalWork(specObjList));
            try {
                NodePM.addImportedTestCases(category, specObjList);
            } finally {
                // Remove JPA progress listeners
                ProgressMonitorTracker.getInstance().setProgressMonitor(null);
            }
            EntityManager compNameSession = Persistor.instance().openSession();
            EntityTransaction tx = Persistor.instance().getTransaction(compNameSession);
            for (INameMapper mapper : mapperList) {
                mapper.persist(compNameSession, project.getId());
            }
            for (IWritableComponentNameMapper compMapper : compMapperList) {
                CompNamePM.flushCompNames(compNameSession, project.getId(), compMapper);
            }

            Persistor.instance().commitTransaction(compNameSession, tx);

            for (IComponentNameMapper compMapper : compMapperList) {
                compMapper.getCompNameCache().updateStandardMapperAndCleanup(project.getId());
            }

            UsedToolkitBP.getInstance().refreshToolkitInfo(project);

            DataEventDispatcher.getInstance().fireDataChangedListener(category, DataState.Added, UpdateState.all);
            DataEventDispatcher.getInstance().fireDataChangedListener(category, DataState.StructureModified,
                    UpdateState.all);

            UsedToolkitBP.getInstance().refreshToolkitInfo(GeneralStorage.getInstance().getProject());

            return true;
        }

        /**
         * @param specObjList The list for which to find the total required
         *                    work.
         * @return the amount of work required to save the given list to the
         *         database.
         */
        private int getTotalWork(List<ISpecPersistable> specObjList) {
            int totalWork = 0;

            for (ISpecPersistable spec : specObjList) {

                totalWork += getWorkForNode(spec);
            }

            // 1 for each progress event type except postUpdate
            totalWork *= NUM_HBM_PROGRESS_EVENT_TYPES - 1;

            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 importedToolkit The toolkit ID for the imported project.
         * @param importedLevel The toolkit level for the imported project.
         * @param currentToolkit The toolkit ID for the current project.
         * @param currentLevel The toolkit level for the current project.
         * @return <code>true</code> if the imported toolkit/level is compatible
         *         with the current toolkit/level. Otherwise <code>false</code>.
         */
        private boolean isToolkitCompatible(String importedToolkit, String importedLevel, String currentToolkit,
                String currentLevel) {

            return importedToolkit.equals(currentToolkit)
                    || ToolkitUtils.doesToolkitInclude(currentToolkit, importedToolkit)
                    || ToolkitUtils.isToolkitMoreConcrete(currentLevel, importedLevel);
        }

        /**
         * @param specObjList The list into which the new category will be 
         *                    added.
         * @return a suitable name for a new category in the given list.
         */
        private String createCategoryName(List<ISpecPersistable> specObjList) {
            String standardName = Messages.TreeOpsBPImportedCat;
            int postfix = 1;
            String newName = standardName + postfix;
            final Set<String> usedNames = new HashSet<String>();
            for (ISpecPersistable node : specObjList) {
                if (node instanceof ICategoryPO && (node).getName().startsWith(standardName)) {

                    usedNames.add(((INodePO) node).getName());
                }
            }
            while (usedNames.contains(newName)) {
                postfix++;
                newName = standardName + postfix;
            }
            return newName;
        }

    }

    /** the logger */
    public static final Logger LOG = LoggerFactory.getLogger(FileStorageBP.class);

    /** Bit set for importing all */
    public static final int IMPORT_ALL = 0;
    /** Bit set for importing testcases */
    public static final int IMPORT_TESTCASES = 1;

    /** 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 = 15;

    /** 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 pogress 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.loadProjectById(proj.getId(), exportSession);
            String projectFileName = projectToExport.getDisplayName() + ".xml"; //$NON-NLS-1$
            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.writeLine(
                    NLS.bind(Messages.ExportAllBPInfoStartingExportProject, new Object[] { projectFileName }));
            try {
                if (subMonitor.isCanceled()) {
                    throw new InterruptedException();
                }
                XmlStorage.save(projectToExport, exportFileName, true,
                        subMonitor.newChild(XmlStorage.getWorkToSave(projectToExport)), writeToSystemTempDir,
                        listOfProjectFiles);

                if (subMonitor.isCanceled()) {
                    throw new InterruptedException();
                }

                console.writeLine(
                        NLS.bind(Messages.ExportAllBPInfoFinishedExportProject, new Object[] { projectFileName }));

            } catch (final PMSaveException e) {
                LOG.error(Messages.CouldNotExportProject, e);
                console.writeErrorLine(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 pogress 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) throws PMException, ProjectDeletedException {
        // import all data from projects
        try {
            doImport(IMPORT_ALL, importProjectURLs, SubMonitor.convert(monitor), console, openProject);
        } catch (InterruptedException e) {
            // Operation was canceled. Do nothing.
        }
    }

    /**
     * Imports a choosed project from a file.
     * 
     * @param elements
     *            What to import ? 0 = all >0 = elements
     * @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 pogress 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 int elements, final List<URL> fileURLs, IProgressMonitor monitor,
            IProgressConsole console, boolean openProject)
            throws InterruptedException, PMException, ProjectDeletedException {

        SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.ImportFileBPImporting, TOTAL_IMPORT_WORK);
        return doImport(elements, fileURLs, subMonitor, console, openProject);
    }

    /**
     * actually do the import work. Separated to only batch calls
     * @param elements @see #importProject(int)
     * @param fileURLs
     *            The URLs of the files to import.
     * @param subMonitor @see #importProject(int)
     * @param console
     *              The console to use to display pogress 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(final int elements, List<URL> fileURLs, SubMonitor subMonitor,
            IProgressConsole console, boolean openProject)
            throws InterruptedException, PMException, ProjectDeletedException {

        // Read project files
        ReadFilesOperation readFilesOp = new ReadFilesOperation(elements == 0, fileURLs, console);
        readFilesOp.run(subMonitor.newChild(PARSE_FILES_WORK));

        // Import projects
        ImportOperation importOp = new ImportOperation(elements, readFilesOp.getProjectToMapperMap(),
                readFilesOp.getProjectToCompCacheMap(), console, openProject);

        try {
            importOp.run(subMonitor.newChild(SAVE_TO_DB_WORK));
        } catch (InvocationTargetException ite) {
            Throwable cause = ExceptionUtils.getRootCause(ite);
            if (cause instanceof PMException) {
                throw (PMException) cause;
            } else if (cause instanceof ProjectDeletedException) {
                throw (ProjectDeletedException) cause;
            } else {
                throw new RuntimeException(ite);
            }
        }
        return importOp.getProjectToOpen();
    }

    /**
     * Report to the user that the import operation was aborted due to an
     * error.
     * 
     * @param console
     *              The console to use to display pogress and 
     *              error messages.
     * @param e The error that caused the import operation to abort.
     */
    private static void showAbortImport(IProgressConsole console, Exception e) {
        console.writeErrorLine(
                NLS.bind(Messages.ImportFileActionErrorImportFailed, new Object[] { e.getMessage() }));
    }

    /**
     * Report to the user that an error occurred while importing the project.
     * 
     * @param console
     *              The console to use to display pogress 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 pogress 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 pogress 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, new Object[] { projectFileName }));
    }

    /**
     * Report to the user that all projects to import have been analyzed.
     * 
     * @param console
     *              The console to use to display pogress and 
     *              error messages.
     */
    private static void showFinishedReadingProjects(IProgressConsole console) {
        console.writeLine(Messages.ImportFileActionInfoFinishedReadingProjects);
    }

    /**
     * Report to the user that the import of a project failed because the
     * toolkit for the imported project was not compatible with the toolkit
     * ofthe current project.
     * 
     * @param console
     *              The console to use to display pogress and 
     *              error messages.
     * @param projectName Name of the project to import.
     * @param currentToolkit Toolkit of the current project.
     * @param importedToolkit Toolkit of the imported project.
     */
    private static void showIncompatibleToolkit(IProgressConsole console, String projectName, String currentToolkit,
            String importedToolkit) {

        console.writeErrorLine(NLS.bind(Messages.ImportFileActionErrorIncompatibleToolkits,
                new Object[] { projectName, importedToolkit, currentToolkit }));
    }

    /**
     * Report to the user that the import process is beginning.
     * 
     * @param console
     *              The console to use to display pogress 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 pogress 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, new Object[] { projectFileName }));
    }

    /**
     * Report to the user that all projects to import will be analyzed.
     * 
     * @param console
     *              The console to use to display pogress 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() });
    }

}