1. Eclipse Resources API for manipulating files

The Eclipse resource API allows to access, create, delete and update resources like files and folders. It also allows to register for resource changes.

1.1. Resource API

The Eclipse resource model provides API to access and modify files (resources). For example you can create a project via the following API.

IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
IProject project = root.getProject("performancetest");
try {
    project.create(new NullProgressMonitor());
    project.open(null);
    } catch (CoreException e) {
    // nothing to do
}

1.2. Resource change notification

You can register to resource changes via a resource change listener. Listeners are informed what resources are changed and how they changed. The update is proportional to the size of the change and not the size of the workspace. The object passed to a resource change listener is an instance of IResourceChangeEvent.

It contains:

  • the event type, an integer that describes what kind of event occurred, e.g., the POST_CHANGE event

  • the kind of modification (added, removed, or changed)

  • the precise nature of the change (the change flags)

  • a summary of what markers changed on the resource.

  • Deltas for any added, removed, or changed children.

The tree of deltas is structured like the tree of workspace IResource objects. Each delta object corresponds to exactly one resource. The top-most delta object, provided by the event object, corresponds to the IWorkspaceRoot resource obtained by IWorkspace.getRoot. The resource delta hierarchy includes deltas for all affected resources that existed prior to the resource changing operation, and all affected resources that existed after the operation. Think of it as the union of the workspace contents before and after a particular operation, with all unchanged sub-trees pruned out.

Change notifications may not be immediate, resources change operation maybe be nested into other operations. For example, calling IFile.move uses IFile.create to create the new file, and then IFile.delete to remove the old file. Since the creation and deletion operations are nested inside the move operation, there will only be one notification.

If your code performs batches changes, you should wrap the operation into a ICoreRunnable and pass it to IWorkspace.run. Wrapping high-level operations inside an ICoreRunnable can lead to a substantial performance improvement, because it ensures that only one resource change broadcast occurs, instead of potentially thousands.

A WorkspaceJob is the asynchronous equivalent of ICoreRunnable.

2. Exercise: Create project with lots of files

Use a plug-in called called com.vogella.resources for this exercise. You have have already created this in anther exercise, if you did re-use it, otherwise create it.

2.1. Add dependency

Add the following dependencies to your plug-in so that the MANIFEST.MF looks similar to the following code.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Resources
Bundle-SymbolicName: com.vogella.resources
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: VOGELLA
Automatic-Module-Name: com.vogella.resources
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.eclipse.jface,
 org.eclipse.core.runtime,
 org.eclipse.core.resources,
 org.eclipse.ui.workbench,
 org.eclipse.ui.ide,
 org.eclipse.ui,
 org.eclipse.e4.ui.model.workbench,
 org.eclipse.e4.core.di,
 org.eclipse.e4.ui.services,
 org.eclipse.core.filesystem

2.2. Add handler

Add an e4 menu extension pointing to a valid command and handler. Adjust the handler to create a new project with lots of files.

package com.vogella.resources.handlers;

import java.io.ByteArrayInputStream;
import java.util.Random;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
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.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;

public class CreateLargeProjectHandler {
    private final Random random = new Random();

    private static final String CHARSFORCREATION = "abcdefghijklmnopqrstuvwxyz";

    @Execute
    public void execute(IWorkbenchPage page) {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        IWorkspaceRoot root = workspace.getRoot();
        IProject project = root.getProject("performancetest");
        try {
            project.create(new NullProgressMonitor());
            project.open(null);
            for (int i = 0; i < 30; i++) {
                IFolder folder = project.getFolder("test" + i);
                folder.create(true, true, null);
                for (int j = 0; j < 30; j++) {
                    IFile file = folder.getFile(createString(10));
                    file.create(new ByteArrayInputStream(createBytes(5000)), IResource.NONE, null);
                    IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.getFullPath());
                    if (!fileStore.fetchInfo().isDirectory()) {
                        try {
                            IDE.openEditorOnFileStore(page, fileStore);
                        } catch (PartInitException e) {
                            /* some code */
                        }
                    }
                }
            }
        } catch (CoreException e) {
            // nothing to do
        }
    }

    private byte[] createBytes(int length) {
        byte[] bytes = new byte[length];
        random.nextBytes(bytes);
        return bytes;
    }

    private String createString(int length) {
        StringBuilder buf = new StringBuilder(length);
        // fill the string with random characters up to the desired length
        for (int i = 0; i < length; i++) {
            buf.append(CHARSFORCREATION.charAt(random.nextInt(CHARSFORCREATION.length())));
        }
        return buf.toString();
    }

}

Such code can for example be useful for a test setup.

To make the code efficient with regards to resource change notifications, wrap it into a ICoreRunnable and pass it to IWorkspace.run.

2.3. Verify

Start your runtime Eclipse and validate that you can create a new project with your handler.

3. Exercise: Close all editors for all projects in your workspace

Write a new handler which closes all open editors. The following example code can be used for that.

package com.vogella.eclipse.resourcesapi.handlers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

public class CloseAllOpenEditors {

    @Execute
    public void execute() {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        IWorkspaceRoot root = workspace.getRoot();
        IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        IEditorReference[] editorReferences = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
                .getEditorReferences();
        List<IEditorReference> editorsToBeClosed = new ArrayList<>();
        Map<IFile, IEditorReference> fileEditors = new HashMap<>();


        for (IEditorReference editorReference : editorReferences) {
            try {
                IEditorInput editorInput = editorReference.getEditorInput();
                if (editorInput instanceof IFileEditorInput) {
                    IFileEditorInput fileEditorInput = (IFileEditorInput) editorInput;
                    IFile file = fileEditorInput.getFile();
                    fileEditors.put(file, editorReference);
                }
            } catch (PartInitException e) {
                e.printStackTrace();
            }

        }
        activePage.closeEditors(editorReferences, true);

        // Get all projects in the workspace
        IProject[] projects = root.getProjects();
        // Loop over all projects
        for (IProject project : projects) {
            try {
                List<IFile> projectfiles = findAllProjectFiles(project);
                for (IResource resource : projectfiles) {
                    if (fileEditors.containsKey(resource)) {
                        editorsToBeClosed.add(fileEditors.get(resource));
                    }
                }
            } catch (CoreException e) {
                e.printStackTrace();
            }
        }

        IEditorReference[] editorToBeClosed = new IEditorReference[editorsToBeClosed.size()];
        editorToBeClosed = editorsToBeClosed.toArray(editorToBeClosed);
        activePage.closeEditors(editorToBeClosed, true);

        return null;
    }

    private List<IFile> findAllProjectFiles(IContainer container) throws CoreException {
        IResource[] members = container.members();
        List<IFile> list = new ArrayList<>();

        for (IResource member : members) {
            if (member instanceof IContainer) {
                IContainer c = (IContainer) member;
                list.addAll(findAllProjectFiles(c));
            } else if (member instanceof IFile) {
                list.add((IFile) member);
            }
        }
        return list;
    }

}

4. Additional information Eclipse Resources API