io.sarl.tests.api.WorkbenchTestHelper.java Source code

Java tutorial

Introduction

Here is the source code for io.sarl.tests.api.WorkbenchTestHelper.java

Source

/*
 * $Id$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2017 the original authors or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.sarl.tests.api;

import static com.google.common.collect.Sets.newHashSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.jar.Manifest;
import java.util.logging.Logger;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.ErrorEditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.xtend.core.xtend.XtendTypeDeclaration;
import org.eclipse.xtext.resource.FileExtensionProvider;
import org.eclipse.xtext.ui.editor.XtextEditor;
import org.eclipse.xtext.ui.editor.XtextEditorInfo;
import org.eclipse.xtext.ui.editor.utils.EditorUtils;
import org.eclipse.xtext.ui.resource.IResourceSetProvider;
import org.eclipse.xtext.ui.util.PluginProjectFactory;
import org.eclipse.xtext.util.JavaVersion;
import org.eclipse.xtext.util.StringInputStream;
import org.eclipse.xtext.xbase.lib.Functions;
import org.osgi.framework.Bundle;

import io.sarl.lang.sarl.SarlScript;
import io.sarl.lang.ui.preferences.SARLPreferences;

/** Provides tools for setting up and managing the Eclipse workspace.
 * This class was adapted from the Xtend test suite.
 *
 * @author $Author: sgalland$
 * @author $Author: ngaud$
 * @author Jan Koehnlein - Xtend contributor
 * @version $FullVersion$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
@Singleton
@SuppressWarnings("static-method")
public class WorkbenchTestHelper {

    /** Logger.
     */
    public static final Logger log = Logger.getLogger(WorkbenchTestHelper.class.getName());

    /** Name of the project that is generated during unit tests.
     */
    public static final String TESTPROJECT_NAME = "test.project"; //$NON-NLS-1$

    /**
     * ID of this nature.
     */
    public static final String NATURE_ID = "io.sarl.eclipse.SARLProjectNature"; //$NON-NLS-1$

    /** List of the bundles that are required for created a testing project.
     */
    public static final ImmutableList<String> DEFAULT_REQ_BUNDLES = ImmutableList.of("com.google.inject", //$NON-NLS-1$
            "org.eclipse.xtend.lib", //$NON-NLS-1$
            "org.eclipse.xtext.xbase.lib", //$NON-NLS-1$
            "javax.inject", //$NON-NLS-1$
            "io.sarl.lang", //$NON-NLS-1$
            "io.sarl.lang.core", //$NON-NLS-1$
            "io.sarl.lang.ui", //$NON-NLS-1$
            "io.sarl.tests.testdata", //$NON-NLS-1$
            "org.junit"); //$NON-NLS-1$

    private Set<IFile> files = newHashSet();

    @Inject
    private XtextEditorInfo editorInfo;

    @Inject
    private FileExtensionProvider fileExtensionProvider;

    @Inject
    private IWorkbench workbench;

    @Inject
    private IWorkspace workspace;

    @Inject
    private IResourceSetProvider resourceSetProvider;

    @Inject
    private ProjectCreator projectCreator;

    private boolean isLazyCreatedProject = false;

    private String lastProjectName = null;

    /** Init the helper for the unit test.
     * 
     * @throws Exception
     */
    public void setUp() throws Exception {
        //
    }

    /** Dispose the helper for the unit test.
     * 
     * @throws Exception
     */
    public void tearDown() throws Exception {
        this.lastProjectName = null;
        // Close the editors safely.
        if (this.workbench != null) {
            try {
                final IWorkbenchWindow window = this.workbench.getActiveWorkbenchWindow();
                if (window != null) {
                    window.getActivePage().closeAllEditors(false);
                }
            } catch (Throwable e) {
                //
            }
        }
        new WorkspaceModifyOperation() {

            @SuppressWarnings("synthetic-access")
            @Override
            protected void execute(IProgressMonitor monitor)
                    throws CoreException, InvocationTargetException, InterruptedException {
                for (IFile file : getFiles()) {
                    try {
                        file.delete(true, null);
                    } catch (Exception exc) {
                        // Be silent because it is outside the scope of the tests
                    }
                }
                getFiles().clear();
                IFolder binFolder = getProject(false).getFolder("bin"); //$NON-NLS-1$
                if (binFolder.exists()) {
                    for (IResource binMember : binFolder.members()) {
                        try {
                            binMember.delete(true, null);
                        } catch (Exception exc) {
                            // Be silent because it is outside the scope of the tests
                        }
                    }
                }
                if (WorkbenchTestHelper.this.isLazyCreatedProject) {
                    deleteProject(getProject(false));
                    WorkbenchTestHelper.this.isLazyCreatedProject = false;
                }
            }

        }.run(null);
        awaitAutoBuild();
    }

    private static void deleteProjects(IProject[] projects) throws CoreException {
        for (IProject iProject : projects) {
            if (iProject.exists()) {
                iProject.delete(true, true, new NullProgressMonitor());
            }
        }
    }

    /** Clear the workspace.
     */
    public void clearWorkspace() {
        try {
            IProject[] visibleProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
            deleteProjects(visibleProjects);
            IProject[] hiddenProjects = ResourcesPlugin.getWorkspace().getRoot()
                    .getProjects(org.eclipse.core.resources.IContainer.INCLUDE_HIDDEN);
            deleteProjects(hiddenProjects);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /** Replies the ccreated files.
     *
     * @return the created files.
     */
    public Set<IFile> getFiles() {
        return this.files;
    }

    /** Replies the current project.
     * 
     * @return the project.
     */
    public IProject getProject() {
        return getProject(true);
    }

    /** Replies the current Java project.
     * 
     * @return the Java project.
     */
    public IJavaProject getJavaProject() {
        return JavaCore.create(getProject());
    }

    /** Replies the Java project for the given resource.
     *
     * @param resource the resource to search for.
     * @return the Java project.
     */
    public IJavaProject getJavaProject(Resource resource) {
        return JavaCore.create(getProject(resource, true));
    }

    /** Replies the current project.
     * 
     * @param createOnDemand - create the project if it does not exist yet.
     * @return the project.
     */
    public IProject getProject(boolean createOnDemand) {
        return getProject(this.lastProjectName, createOnDemand);
    }

    /** Replies the project with the given name.
     * 
     * @param projectName the name of the project.
     * @param createOnDemand - create the project if it does not exist yet.
     * @return the project.
     */
    public IProject getProject(String projectName, boolean createOnDemand) {
        String prjName = projectName;
        if (Strings.isNullOrEmpty(prjName)) {
            prjName = TESTPROJECT_NAME;
        }
        IProject project = this.workspace.getRoot().getProject(prjName);
        if (createOnDemand && !project.exists()) {
            try {
                this.isLazyCreatedProject = true;
                project = createPluginProject(prjName, this.projectCreator);
            } catch (CoreException e) {
                throw new RuntimeException(e);
            }
        }
        return project;
    }

    /** Replies the project for the given resource.
     * 
     * @param resource the resource to search for.
     * @param createOnDemand - create the project if it does not exist yet.
     * @return the project.
     */
    public IProject getProject(Resource resource, boolean createOnDemand) {
        if (resource != null) {
            final URI uri = resource.getURI();
            final String platformString = uri.toPlatformString(true);
            final IPath resourcePath = new Path(platformString);
            final IFile file = this.workspace.getRoot().getFile(resourcePath);
            if (file != null) {
                final IProject project = file.getProject();
                if (project != null && project.exists()) {
                    return project;
                }
            }
        }
        return getProject(createOnDemand);
    }

    /** Open the Xtext editor.
     * 
     * @param fileName the name of the file to open.
     * @param content the content of the file.
     * @return the editor.
     * @throws Exception
     */
    public XtextEditor openEditor(String fileName, String content) throws Exception {
        int cursor = content.indexOf('|');
        IFile file = createFile(fileName, content.replace("|", "")); //$NON-NLS-1$//$NON-NLS-2$
        XtextEditor editor = openEditor(file);
        editor.getInternalSourceViewer().setSelectedRange(cursor, 0);
        editor.getInternalSourceViewer().getTextWidget().setFocus();
        return editor;
    }

    /** Create a file in the source folder.
     * 
     * @param fileName the name of the file to create.
     * @param content the content of the file.
     * @return the file.
     * @throws Exception
     * see {@link #getFullFileNameInSources(String)}
     */
    public IFile createFile(String fileName, String content) throws Exception {
        String fullFileName = getFullFileNameInSources(fileName);
        return createFileImpl(fullFileName, content);
    }

    private static IProject createProject(IProject project) throws CoreException {
        if (!project.exists())
            project.create(new NullProgressMonitor());
        project.open(new NullProgressMonitor());
        return project;
    }

    /** Create the container tree.
     *
     * @param container the container
     * @throws InvocationTargetException in case of error.
     * @throws InterruptedException in case of errir.
     */
    public static void create(org.eclipse.core.resources.IContainer container)
            throws InvocationTargetException, InterruptedException {
        new WorkspaceModifyOperation() {

            @SuppressWarnings("synthetic-access")
            @Override
            protected void execute(IProgressMonitor monitor)
                    throws CoreException, InvocationTargetException, InterruptedException {
                if (!container.exists()) {
                    create(container.getParent());
                    if (container instanceof IFolder) {
                        ((IFolder) container).create(true, true, new NullProgressMonitor());
                    } else {
                        IProject iProject = (IProject) container;
                        createProject(iProject);
                    }
                }
            }
        }.run(new NullProgressMonitor());
    }

    /** Create a file.
     * 
     * @param fullFileName the name of the file to create.
     * @param content the content of the file.
     * @return the file.
     * @throws Exception
     */
    public IFile createFileImpl(String fullFileName, String content) throws Exception {
        final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fullFileName));
        new WorkspaceModifyOperation() {
            @Override
            protected void execute(IProgressMonitor monitor)
                    throws CoreException, InvocationTargetException, InterruptedException {
                create(file.getParent());
                file.delete(true, new NullProgressMonitor());
                try (final InputStream is = new StringInputStream(content)) {
                    file.create(is, true, new NullProgressMonitor());
                } catch (Exception exception) {
                    //
                }
            }

        }.run(new NullProgressMonitor());
        getFiles().add(file);
        return file;
    }

    /** Create a file.
     * 
     * @param file the name of the file to create.
     * @param content the content of the file.
     * @return the file.
     * @throws Exception
     */
    public IFile createFileImpl(IFile file, InputStream content) throws Exception {
        new WorkspaceModifyOperation() {
            @Override
            protected void execute(IProgressMonitor monitor)
                    throws CoreException, InvocationTargetException, InterruptedException {
                create(file.getParent());
                file.delete(true, new NullProgressMonitor());
                file.create(content, true, new NullProgressMonitor());
            }

        }.run(new NullProgressMonitor());
        getFiles().add(file);
        return file;
    }

    /** Replies the file with the given name.
     * 
     * @param fileName the name without the file extension.
     * @return the file.
     */
    public IFile getFile(String fileName) {
        return this.workspace.getRoot().getFile(new Path(getFullFileNameInSources(fileName)));
    }

    /** Compute the full filename.
     * 
     * @param fileName the name with the file extension.
     * @return the full filename.
     * @see #getFullFileNameInProject(String)
     */
    public String getFullFileNameInSources(String fileName) {
        final String extension = (fileName.indexOf(".") != -1) ? "" : "." + getFileExtension(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
        final String fullFileName = getProject().getName() + "/src/" + fileName + extension; //$NON-NLS-1$
        return fullFileName;
    }

    /** Replies the file with the given name.
     * 
     * @param fileName the name.
     * @return the file.
     */
    public IFile getFileInProject(String fileName) {
        return getFileInRoot(getProject().getName(), fileName);
    }

    /** Replies the file with the given name.
     * 
     * @param projectName the name of the project
     * @param fileName the name.
     * @return the file.
     */
    public IFile getFileInRoot(String projectName, String fileName) {
        String fullFileName = projectName + "/" + fileName; //$NON-NLS-1$
        return getFile(new Path(fullFileName));
    }

    /** Replies the file with the given name.
     * 
     * @param path the name of the file.
     * @return the file.
     */
    public IFile getFile(IPath path) {
        return this.workspace.getRoot().getFile(path);
    }

    /** Replies the folder with the given name.
     * 
     * @param path the name of the folder.
     * @return the folder.
     */
    public IFolder getFolder(IPath path) {
        return this.workspace.getRoot().getFolder(path);
    }

    /** Compute the full filename inside the proejct root directory.
     * 
     * @param fileName the name without the file extension.
     * @return the full filename.
     * @see #getFullFileNameInSources(String)
     */
    public String getFullFileNameInProject(String fileName) {
        String extension = (fileName.indexOf(".") != -1) ? "" : "." + getFileExtension(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
        String fullFileName = getProject().getName() + "/" + fileName + extension; //$NON-NLS-1$
        return fullFileName;
    }

    /** Replies the file extension.
     *
     * @return the file extension.
     */
    public String getFileExtension() {
        return this.fileExtensionProvider.getFileExtensions().iterator().next();
    }

    /** Compute the URI of the given file.
     *
     * @param file the file.
     * @return the uri.
     */
    public URI uri(IFile file) {
        return URI.createPlatformResourceURI(file.getFullPath().toString(), true);
    }

    /** Create a SARL file.
     *
     * @param fileName the filename.
     * @param content the content.
     * @return the SARL script.
     * @throws Exception
     */
    public SarlScript sarlScript(String fileName, String content) throws Exception {
        return sarlScript(fileName, content, true);
    }

    /** Create a SARL file.
     *
     * @param fileName the filename.
     * @param content the content.
     * @param assertNoError indicates if this function asserts no error in the content.
     * If <code>true</code> this function may fail. Otherwise, it never fails on syntax errors.
     * @return the SARL script.
     * @throws Exception
     */
    public SarlScript sarlScript(String fileName, String content, boolean assertNoError) throws Exception {
        IFile file = createFile(fileName, content);
        Resource resource = getResourceSet().createResource(uri(file));
        try (InputStream is = new StringInputStream(content)) {
            resource.load(is, null);
            if (assertNoError) {
                assertEquals(resource.getErrors().toString(), 0, resource.getErrors().size());
            }
            SarlScript xtendFile = (SarlScript) resource.getContents().get(0);
            return xtendFile;
        }
    }

    /** Create a SARL file.
     *
     * @param file the file.
     * @return the SARL script.
     * @throws Exception
     */
    public SarlScript sarlScript(IFile file) throws Exception {
        Resource resource = getResourceSet().createResource(uri(file));
        resource.load(null);
        return (SarlScript) resource.getContents().get(0);
    }

    /** Create a SARL file.
     *
     * @param project the project.
     * @param fileName the filename in the project.
     * @param content the content.
     * @return the SARL script.
     * @throws Exception
     */
    public SarlScript sarlScript(IProject project, String fileName, String content) throws Exception {
        IFile file = createFileImpl(project.getName() + "/src/" + fileName, content); //$NON-NLS-1$
        Resource resource = this.resourceSetProvider.get(project).createResource(uri(file));
        try (InputStream is = new StringInputStream(content)) {
            resource.load(is, null);
            assertEquals(resource.getErrors().toString(), 0, resource.getErrors().size());
            SarlScript xtendFile = (SarlScript) resource.getContents().get(0);
            return xtendFile;
        }
    }

    /** Replies the resource associated to the given file in the given project.
     *
     * @param file the file in the project.
     * @return the resource.
     */
    public Resource getResourceFor(IFile file) {
        return getResourceFor(getProject(), file);
    }

    /** Replies the resource associated to the given file in the given project.
     *
     * @param project the project.
     * @param file the file in the project.
     * @return the resource.
     */
    public Resource getResourceFor(IProject project, IFile file) {
        URI uri = uri(file);
        ResourceSet resourceSet = this.resourceSetProvider.get(project);
        Resource resource = resourceSet.getResource(uri, false);
        if (resource == null) {
            resource = resourceSet.createResource(uri);
        }
        return resource;
    }

    /** Create a SARL file with a SARL agent inside.
     *
     * @param fileName the filename.
     * @param type the type of the expected element.
     * @param content the content.
     * @return the SARL script.
     * @throws Exception
     */
    public <T extends XtendTypeDeclaration> T sarlTypeDeclaration(String fileName, Class<T> type, String content)
            throws Exception {
        SarlScript script = sarlScript(fileName, content);
        Object latest = null;
        for (XtendTypeDeclaration declaration : script.getXtendTypes()) {
            if (type.isInstance(declaration)) {
                latest = declaration;
            }
        }
        if (latest != null) {
            return type.cast(latest);
        }
        throw new NoSuchElementException();
    }

    /** Create a SARL file with a SARL agent inside.
     *
     * @param type the type of the expected element.
     * @param content the content.
     * @return the SARL script.
     * @throws Exception
     */
    public <T extends XtendTypeDeclaration> T sarlTypeDeclaration(Class<T> type, String content) throws Exception {
        return sarlTypeDeclaration(generateFilename(), type, content);
    }

    /** Replies the resource set for the testing project.
     *
     * @return the resource set.
     */
    public ResourceSet getResourceSet() {
        return this.resourceSetProvider.get(getProject());
    }

    /** Replies the identifier of the editor.
     *
     * @return the identifier of the editor.
     */
    public String getEditorID() {
        return this.editorInfo.getEditorId();
    }

    /** Replies the content of the given file.
     *
     * @param file the name of the file.
     * @return the content of the file.
     * @throws Exception
     */
    public String getContents(IFile file) throws Exception {
        try (InputStream inputStream = file.getContents()) {
            byte[] buffer = new byte[2048];
            int bytesRead = 0;
            StringBuffer b = new StringBuffer();
            do {
                bytesRead = inputStream.read(buffer);
                if (bytesRead != -1)
                    b.append(new String(buffer, 0, bytesRead));
            } while (bytesRead != -1);
            return b.toString();
        }
    }

    /** Close the welcome page.
     *
     * @throws InterruptedException
     */
    public void closeWelcomePage() throws InterruptedException {
        if (PlatformUI.getWorkbench().getIntroManager().getIntro() != null) {
            PlatformUI.getWorkbench().getIntroManager()
                    .closeIntro(PlatformUI.getWorkbench().getIntroManager().getIntro());
        }
    }

    /** Create a project.
     *
     * @param name the name of the project.
     * @param creator the creator of project.
     * @param requiredBundles the bundles to add into the build path.
     * @return the project.
     * @throws CoreException
     */
    public IProject createPluginProject(String name, ProjectCreator creator, String... requiredBundles)
            throws CoreException {
        // If no required bundle is given, use the defaults.
        String[] bundleDependencies;
        if (requiredBundles == null || requiredBundles.length == 0) {
            bundleDependencies = new String[WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.size()];
            WorkbenchTestHelper.DEFAULT_REQ_BUNDLES.toArray(bundleDependencies);
        } else {
            bundleDependencies = requiredBundles;
        }

        // Disable "Build automatically"
        try {
            IWorkspace workspace = ResourcesPlugin.getWorkspace();
            IWorkspaceDescription workspaceDescription = workspace.getDescription();
            workspaceDescription.setAutoBuilding(false);
            workspace.setDescription(workspaceDescription);
        } catch (Throwable exception) {
            throw new CoreException(new Status(IStatus.ERROR, "io.sarl.tests.api", //$NON-NLS-1$
                    exception.getLocalizedMessage(), exception));
        }

        PluginProjectFactory projectFactory = creator.getProjectFactory();
        projectFactory.setProjectName(name);
        this.lastProjectName = name;
        JavaVersion jVersion = creator.getJavaVersion();
        if (jVersion == null) {
            jVersion = JavaVersion.JAVA7;
        }
        String jseVersion;
        switch (jVersion) {
        case JAVA8:
            jseVersion = JavaCore.VERSION_1_8;
            break;
        case JAVA6:
            jseVersion = JavaCore.VERSION_1_6;
            break;
        case JAVA5:
            jseVersion = JavaCore.VERSION_1_5;
            break;
        case JAVA7:
        default:
            jseVersion = JavaCore.VERSION_1_7;
            break;
        }
        projectFactory.setBreeToUse("JavaSE-" + jseVersion); //$NON-NLS-1$
        projectFactory.addFolders(creator.getSourceFolders());
        IPath srcGenFolder = Path.fromPortableString(creator.getGenerationFolder());
        projectFactory.addBuilderIds(creator.getBuilderIds());
        projectFactory.addProjectNatures(creator.getNatures());
        IProject result = projectFactory.createProject(null, null);
        IJavaProject javaProject = JavaCore.create(result);
        makeCompliantFor(javaProject, jVersion);
        creator.addJreClasspathEntry(javaProject);

        IPath workspaceRoot;
        IPath platformLocation;

        try {
            workspaceRoot = ResourcesPlugin.getWorkspace().getRoot().getLocation();
            Location location = Platform.getInstallLocation();
            java.net.URI uri = org.eclipse.core.runtime.URIUtil.toURI(location.getURL());
            platformLocation = URIUtil.toPath(uri);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // Retreive the bundles
        List<IClasspathEntry> classpathEntries = new ArrayList<>(bundleDependencies.length);
        for (String bundleName : bundleDependencies) {
            URL url;
            try {
                url = new URL(bundleName);
            } catch (Throwable exception) {
                url = null;
            }
            if (url != null) {
                URL newUrl;
                try {
                    newUrl = FileLocator.toFileURL(url);
                } catch (Exception exception) {
                    throw new RuntimeException("Reference library not found: " + url, exception); //$NON-NLS-1$
                }
                if (newUrl == null) {
                    throw new RuntimeException("Reference library not found: " + url); //$NON-NLS-1$
                }

                IPath path = Path.fromPortableString(newUrl.getPath());

                IClasspathEntry classPathEntry = JavaCore.newLibraryEntry(path, null, null);
                classpathEntries.add(classPathEntry);
            } else {
                Bundle bundle = Platform.getBundle(bundleName);
                if (bundle == null) {
                    throw new RuntimeException("Reference library not found: " + bundleName); //$NON-NLS-1$
                }

                IPath bundlePath = computeBundlePath(bundle, workspaceRoot, platformLocation);
                if (bundlePath == null) {
                    throw new RuntimeException("Reference library not found: " + bundleName); //$NON-NLS-1$
                }

                IPath binPath = computeBinaryPathForBundle(bundlePath, workspaceRoot);
                if (binPath == null) {
                    throw new RuntimeException("Reference library not found: " + bundleName); //$NON-NLS-1$
                }

                IClasspathEntry classPathEntry = JavaCore.newLibraryEntry(binPath, null, null);
                classpathEntries.add(classPathEntry);
            }
        }

        if (!classpathEntries.isEmpty()) {
            creator.addToClasspath(javaProject, false, classpathEntries);
        }

        // Configure SARL source folder
        SARLPreferences.setSpecificSARLConfigurationFor(result, srcGenFolder);

        // Enable "Build automatically"
        try {
            IWorkspace workspace = ResourcesPlugin.getWorkspace();
            IWorkspaceDescription workspaceDescription = workspace.getDescription();
            workspaceDescription.setAutoBuilding(true);
            workspace.setDescription(workspaceDescription);
        } catch (Throwable exception) {
            throw new CoreException(new Status(IStatus.ERROR, "io.sarl.tests.api", //$NON-NLS-1$
                    exception.getLocalizedMessage(), exception));
        }

        awaitAutoBuild();

        return result;
    }

    private static IPath computeBundlePath(Bundle bundle, IPath workspaceRoot, IPath platformLocation) {
        IPath bundlePath;
        try {
            File file = FileLocator.getBundleFile(bundle);
            bundlePath = Path.fromOSString(file.getPath());
        } catch (IOException e1) {
            throw new RuntimeException(e1);
        }

        // Ensure that the bundle path is absolute (mandatory for beeing a classpath entry)
        if (!bundlePath.isAbsolute()) {
            IPath newBundlePath = workspaceRoot.append(bundlePath);
            if (!newBundlePath.toFile().exists()) {
                newBundlePath = platformLocation.append(bundlePath);
            }
            bundlePath = newBundlePath;
        }
        assert (bundlePath.isAbsolute()) : "The bundle path is not absolute: " + bundlePath; //$NON-NLS-1$
        return bundlePath;
    }

    private static IPath computeBinaryPathForBundle(IPath bundlePath, IPath workspaceRoot) {
        // Determine the path from the output folders of the Java projects in the current workspace.
        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(bundlePath.lastSegment());
        IPath newBundlePath = null;
        try {
            if (project != null && project.hasNature(JavaCore.NATURE_ID)) {
                IJavaProject javaProject = JavaCore.create(project);
                newBundlePath = javaProject.getOutputLocation();
                if (newBundlePath != null) {
                    newBundlePath = workspaceRoot.append(newBundlePath);
                    // Test if the bundle path exists
                    if (newBundlePath != null && !newBundlePath.toFile().exists()) {
                        newBundlePath = null;
                    }
                }
            }
        } catch (Exception e) {
            // Ignore the exceptions since they are not useful (hopefully)
        }

        if (newBundlePath != null) {
            assert (newBundlePath.isAbsolute()) : "The bundle path is not absolute: " + newBundlePath; //$NON-NLS-1$
            return newBundlePath;
        }

        // Detect the binary folder in the bundle.
        //
        // TODO: Replace by a dynamic detection based on Jdt API.
        File localFile = bundlePath.toFile();
        File binFolder = new File(new File(localFile, "target"), "classes"); //$NON-NLS-1$//$NON-NLS-2$
        if (binFolder.exists()) {
            newBundlePath = bundlePath.append("target").append("classes"); //$NON-NLS-1$//$NON-NLS-2$
        } else {
            binFolder = new File(localFile, "bin"); //$NON-NLS-1$
            if (binFolder.exists()) {
                newBundlePath = bundlePath.append("bin"); //$NON-NLS-1$
            } else {
                newBundlePath = bundlePath;
            }
        }

        assert (newBundlePath.isAbsolute()) : "The bundle path is not absolute: " + bundlePath; //$NON-NLS-1$
        return newBundlePath;
    }

    /** Make the given project compliant for the given version.
     *
     * @param javaProject the project.
     * @param javaVersion the Java version.
     */
    public static void makeCompliantFor(IJavaProject javaProject, JavaVersion javaVersion) {
        Map<String, String> options = javaProject.getOptions(false);
        String jreLevel;
        switch (javaVersion) {
        case JAVA8:
            jreLevel = JavaCore.VERSION_1_8;
            break;
        case JAVA6:
            jreLevel = JavaCore.VERSION_1_6;
            break;
        case JAVA5:
            jreLevel = JavaCore.VERSION_1_5;
            break;
        case JAVA7:
        default:
            jreLevel = JavaCore.VERSION_1_7;
        }
        options.put(JavaCore.COMPILER_COMPLIANCE, jreLevel);
        options.put(JavaCore.COMPILER_SOURCE, jreLevel);
        options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, jreLevel);
        options.put(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER, JavaCore.ERROR);
        options.put(JavaCore.COMPILER_PB_ENUM_IDENTIFIER, JavaCore.ERROR);
        options.put(JavaCore.COMPILER_CODEGEN_INLINE_JSR_BYTECODE, JavaCore.ENABLED);
        options.put(JavaCore.COMPILER_LOCAL_VARIABLE_ATTR, JavaCore.GENERATE);
        options.put(JavaCore.COMPILER_LINE_NUMBER_ATTR, JavaCore.GENERATE);
        options.put(JavaCore.COMPILER_SOURCE_FILE_ATTR, JavaCore.GENERATE);
        options.put(JavaCore.COMPILER_CODEGEN_UNUSED_LOCAL, JavaCore.PRESERVE);
        javaProject.setOptions(options);
    }

    /** Add exported packages into the project.
     *
     * @param project the project.
     * @param exportedPackages the exported packages.
     * @throws Exception
     */
    public static void addExportedPackages(IProject project, String... exportedPackages) throws Exception {
        IFile manifest = project.getFile("META-INF/MANIFEST.MF"); //$NON-NLS-1$
        Manifest mf = new Manifest(manifest.getContents());
        String value = mf.getMainAttributes().getValue("Export-Package"); //$NON-NLS-1$
        for (String exported : exportedPackages) {
            if (value == null) {
                value = exported;
            } else {
                value += "," + exported; //$NON-NLS-1$
            }
        }
        mf.getMainAttributes().putValue("Export-Package", value); //$NON-NLS-1$
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        mf.write(stream);
        manifest.setContents(new ByteArrayInputStream(stream.toByteArray()), true, true, null);
    }

    /** Add required plugin into the project.
     *
     * @param project the project.
     * @param requiredBundles the required bundles.
     * @throws Exception
     */
    public static void addRequiredBundle(IProject project, Iterable<String> requiredBundles) throws Exception {
        IFile manifest = project.getFile("META-INF/MANIFEST.MF"); //$NON-NLS-1$
        Manifest mf = new Manifest(manifest.getContents());
        String value = mf.getMainAttributes().getValue("Required-Bundles"); //$NON-NLS-1$
        for (String required : requiredBundles) {
            if (value == null) {
                value = required;
            } else {
                value += "," + required; //$NON-NLS-1$
            }
        }
        mf.getMainAttributes().putValue("Required-Bundles", value); //$NON-NLS-1$
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        mf.write(stream);
        manifest.setContents(new ByteArrayInputStream(stream.toByteArray()), true, true, null);
    }

    /** Delete the given project.
     *
     * @param project the project to delete.
     * @throws CoreException
     */
    public static void deleteProject(IProject project) throws CoreException {
        if (project != null && project.exists()) {
            project.delete(true, true, null);
        }
    }

    /** Open the default editor for the given file.
     *
     * @param file the file to open.
     * @return the editor.
     * @throws Exception
     */
    public XtextEditor openEditor(IFile file) throws Exception {
        IEditorPart openEditor = openEditor(file, getEditorID());
        XtextEditor xtextEditor = EditorUtils.getXtextEditor(openEditor);
        if (xtextEditor != null) {
            xtextEditor.selectAndReveal(0, 0);
            return xtextEditor;
        } else if (openEditor instanceof ErrorEditorPart) {
            Field field = openEditor.getClass().getDeclaredField("error"); //$NON-NLS-1$
            field.setAccessible(true);
            throw new IllegalStateException("Couldn't open the editor.", //$NON-NLS-1$
                    ((Status) field.get(openEditor)).getException());
        } else {
            fail("Opened Editor with id:" + getEditorID() + ", is not an XtextEditor"); //$NON-NLS-1$ //$NON-NLS-2$
        }
        return null;
    }

    /** Open the specific editor for the given file.
     *
     * @param file the file to open.
     * @param editorId the identifier of the editor.
     * @return the editor.
     * @throws PartInitException
     */
    public IEditorPart openEditor(IFile file, String editorId) throws PartInitException {
        return this.workbench.getActiveWorkbenchWindow().getActivePage().openEditor(new FileEditorInput(file),
                editorId);
    }

    /** Open the text editor for the given file.
     *
     * @param file the file to open.
     * @return the editor.
     * @throws PartInitException
     */
    public ITextEditor openLikeTextEditor(IFile file) throws PartInitException {
        IEditorPart editor = IDE.openEditor(this.workbench.getActiveWorkbenchWindow().getActivePage(), file);
        if (editor instanceof ITextEditor) {
            return (ITextEditor) editor;
        }
        return null;
    }

    /** Close the editor.
     *
     * @param editor the editor.
     * @param save indicates if the content must be saved before closing.
     * @return success state.
     */
    public boolean closeEditor(IEditorPart editor, boolean save) {
        return this.workbench.getActiveWorkbenchWindow().getActivePage().closeEditor(editor, save);
    }

    /** Close all the editors.
     *
     * @param save indicates if the contents must be saved before closing.
     * @return success state.
     */
    public boolean closeAllEditors(boolean save) {
        return this.workbench.getActiveWorkbenchWindow().getActivePage().closeAllEditors(save);
    }

    /** Save the content of the given editor.
     *
     * @param editor the editor.
     * @param confirm indicates if the confirmation  box must be shown.
     * @return success state.
     */
    public boolean saveEditor(IEditorPart editor, boolean confirm) {
        return this.workbench.getActiveWorkbenchWindow().getActivePage().saveEditor(editor, confirm);
    }

    /**
     * Wait for an update in the UI.
     * 
     * @param test
     *       tester function that returns true if the target state of the UI has been reached
     * @param timeout
     *       the time after which waiting is canceled
     */
    public void awaitUIUpdate(Functions.Function0<Boolean> test, final long timeout) {
        long startTime = System.currentTimeMillis();
        final Display display = Display.getCurrent();
        new Thread("Display alarm") { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    Thread.sleep(timeout);
                    display.wake();
                } catch (InterruptedException e) {
                    //
                }
            }
        }.start();
        while (!test.apply() && System.currentTimeMillis() - startTime < timeout) {
            boolean hasWork = display.sleep();
            while (hasWork) {
                hasWork = display.readAndDispatch();
            }
        }
    }

    /** Wait for the end of auto-build.
     */
    public void awaitAutoBuild() {
        boolean wasInterrupted = false;
        do {
            try {
                Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null);
                wasInterrupted = false;
            } catch (OperationCanceledException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                wasInterrupted = true;
            }
        } while (wasInterrupted);
    }

    /** Do a full build.
     *
     * @param resources the resources that should be build before launching ann Eclipse full build.
     * @throws CoreException if the build cannot be done.
     */
    public void fullBuild(Resource... resources) throws CoreException {
        if (resources != null) {
            for (final Resource resource : resources) {
                resource.getContents();
            }
        }
        ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
        boolean wasInterrupted = false;
        do {
            try {
                Job.getJobManager().join(ResourcesPlugin.FAMILY_MANUAL_BUILD, null);
                wasInterrupted = false;
            } catch (OperationCanceledException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                wasInterrupted = true;
            }
        } while (wasInterrupted);
    }

    /** Generate a filename for a resource that does not exist yet.
     *
     * @param pathElements - the elements of the path (directories and basename), without the extension.
     * @return the filename.
     */
    public String generateFilename(String... pathElements) {
        int filenameCounter = 0;
        String oFilename = pathStr(pathElements);
        String filename = oFilename;
        boolean foundFile = isFileInSourceFolder(filename);
        while (foundFile) {
            ++filenameCounter;
            filename = oFilename + Integer.toString(filenameCounter);
            foundFile = isFileInSourceFolder(filename);
        }
        return filename;
    }

    /** Generate a filename for a resource that does not exist yet.
     *
     * @return the filename.
     */
    public String generateFilename() {
        String p = getDefaultTestPackage() + ".unittest"; //$NON-NLS-1$
        return generateFilename(p.split("\\.")); //$NON-NLS-1$
    }

    /** Replies the default package name where unit test files are located.
     * 
     * @return the package name.
     */
    public String getDefaultTestPackage() {
        return "io.sarl.tests"; //$NON-NLS-1$
    }

    /** Replies if a file exists in the source folder.
     *
     * @param basename - the basename of the file in the source folder.
     * @return <code>true</code> if the file exists, otherwise <code>false</code>.
     */
    public boolean isFileInSourceFolder(String basename) {
        String fullFileName = getFullFileNameInSources(basename);
        return this.workspace.getRoot().exists(new Path(fullFileName));
    }

    /** Build a path.
     *
     * @param path - path elements.
     * @return the path.
     */
    public static IPath path(String... path) {
        assert (path != null && path.length > 0);
        IPath p = new Path(path[0]);
        for (int i = 1; i < path.length; ++i) {
            p = p.append(path[i]);
        }
        return p;
    }

    /** Build a path.
     *
     * @param path - path elements.
     * @return the path.
     */
    public static String pathStr(String... path) {
        return path(path).toOSString();
    }

    /** Replies the Eclipse shell used by this workbench.
     *
     * @return the shell.
     */
    public Shell getShell() {
        return this.workbench.getActiveWorkbenchWindow().getShell();
    }

    /** Factory of a project.
     *
     * @author $Author: sgalland$
     * @version $FullVersion$
     * @mavengroupid $GroupId$
     * @mavenartifactid $ArtifactId$
     */
    public interface ProjectCreator {

        /** Replies the injector.
         * 
         * @return the injector.
         */
        Injector getInjector();

        /** Replies the project factory.
         *
         * @return the factory.
         */
        PluginProjectFactory getProjectFactory();

        /** Replies the version of Java that is supported by the project.
         *
         * @return the java version.
         */
        JavaVersion getJavaVersion();

        /** Replies the source folders.
         *
         * @return the source folders.
         */
        List<String> getSourceFolders();

        /** Replies the folder in which the sources are generated.
         *
         * @return the generation folder.
         */
        String getGenerationFolder();

        /** Replies the identifiers of the builders.
         *
         * @return the identifiers.
         */
        String[] getBuilderIds();

        /** Replies the identifiers of the natures
         *
         * @return the identifiers.
         */
        String[] getNatures();

        /** Add a JRE library to the class path.
         *
         * @param javaProject the proejct to update.
         * @throws JavaModelException
         */
        void addJreClasspathEntry(IJavaProject javaProject) throws JavaModelException;

        /** Add a library to the class path.
         *
         * @param javaProject the proejct to update.
         * @param newClassPathEntry the entry to add.
         * @throws JavaModelException
         */
        void addToClasspath(IJavaProject javaProject, IClasspathEntry newClassPathEntry) throws JavaModelException;

        /** Add libraries to the class path.
         *
         * @param javaProject the proejct to update.
         * @param autobuild indicates if the function should wait for end of autobuild.
         * @param newClassPathEntry the entry to add.
         * @throws JavaModelException
         */
        void addToClasspath(IJavaProject javaProject, boolean autobuild,
                Iterable<IClasspathEntry> newClassPathEntry) throws JavaModelException;

    }

}