1
votes

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?

2

2 Answers

1
votes

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;
  }
} 
0
votes

You are misusing project natures. While your actual problem can be worked around with an extra job, it is an indicator that project nature are likely the wrong means to achieve different images.

The designated way to emphasize those differences like local and imported projects are decorators. Decorators allow you to decorate or exchange image and text of an element (almost) irrespective where it is shown.