Search code examples
javaeclipse-pluginrcpchangelistener

Change project nature in a RCP application


I have in my RCP application a View that extends the CommonNavigator class. The projects in the workspace of my application should have different icons depending on their location on the disk: the projects that exist locally in the workspace should have a different icon from the projects that were imported.

I realised this by defining in the plugin.xml two project natures: MyProjectNature and MyProjectNatureImported with different icons and changing between the natures accordingly with the following method:

private void updateProjectNature(final IWorkspace lf_workspace)
{
    String l_workspacePath = lf_workspace.getRoot().getLocation().toString();
    IProject[] l_projectsInWorkspace = lf_workspace.getRoot().getProjects();
    for (IProject l_project : l_projectsInWorkspace)
    {
        try
        {
            File l_projectFile = new File(l_workspacePath + l_project.getFullPath().toString());
            final IProjectDescription l_projectDescription = l_project.getDescription();
            final String[] l_currentNatures = l_projectDescription.getNatureIds();
            final String[] l_newNatures = new String[l_currentNatures.length];

            int l_index = 0;
            if (l_projectFile.exists())
            {
                for (String l_nature : l_currentNatures)
                {
                    if (l_nature.equals(MyProjectNatureImported.NATURE_ID))
                    {
                        l_newNatures[l_index] = MyProjectNature.NATURE_ID;
                    }
                    else
                    {
                        l_newNatures[l_index] = l_nature;
                    }
                    l_index++;
                }
            }
            else
            {
                for (String l_nature : l_currentNatures)
                {
                    if (l_nature.equals(MyProjectNature.NATURE_ID))
                    {
                        l_newNatures[l_index] = MyProjectNatureImported.NATURE_ID;
                    }
                    else
                    {
                        l_newNatures[l_index] = l_nature;
                    }
                    l_index++;
                }
            }
            l_projectDescription.setNatureIds(l_newNatures);
            l_project.setDescription(l_projectDescription, null);
        }
        catch (CoreException e)
        {
            LOGGER.warning("Error when setting the project nature of the project " + l_project.getName() + ": " + e.getMessage());
        }
    }
}

When I call this method from the ResourceChangeListener that I added to the workspace, I get an error for each project that it is locked and cannot be editted:

final IWorkspace lf_workspace = ResourcesPlugin.getWorkspace();
    lf_workspace.addResourceChangeListener(new IResourceChangeListener()
    {
        @Override
        public void resourceChanged(IResourceChangeEvent event)
        {
            updateProjectNature(lf_workspace);
        }
    });

But when I create a Job that runs each few seconds, then it works:

Job l_testJob = new Job("Update navigator")
    {
        @Override
        protected IStatus run(IProgressMonitor monitor)
        {
            updateProjectNature(lf_workspace);
            schedule(1000);
            return Status.OK_STATUS;
        }

        @Override
        public boolean shouldSchedule()
        {
            // Check if the job should be scheduled / executed or not
            return !PlatformUI.getWorkbench().isClosing();
        }
    };

    l_testJob.schedule(1000);

I would like to call the method only when changes are made to the workspace and not each second (to save the resources), but I don't understand why I get the error and cannot change the nature from the listener while from the job there is no problem.

Any ideas?


Solution

  • The workspace is locked while the resource change event is sent so that listeners can't make any more changes.

    You should be able to submit a single Job from your listener with a scheduling rule to delay the job until the workspace is available. You should use a WorkspaceJob to make sure the update is atomic.

    class UpdateNatureJob extends WorkspaceJob
    {
      UpdateNatureJob()
      {
        // Scheduling rule
        setRule(ResourcesPlugin.getWorkspace().getRoot());
      }
    
      @Override
      public IStatus runInWorkspace(final IProgressMonitor monitor)
      {
        ... your nature update
    
        return Status.OK_STATUS;
      }
    }