14.3. NaturesA nature is used to associate a project with functionality such as a builder, a tool, or a process. A nature can also be used to determine whether an action should be visible (see Section 6.3.2.3, The filter element, on page 230). Whereas a marker has only limited functionality but can be applied to any resource, a nature is designed to contain additional functionality but can only be applied to projects. A marker applies only to a resource in a single workspace, while a nature is part of a project and thus is shared by multiple developers. The Java nature is what makes a project a Java project, distinguishing it from all other types of projects. When a nature, such as the Java nature, is added to a project, the project's .project file (see Section 1.4.2, .classpath and .project files, on page 22) is modified to include the nature's identifier (see Section 14.1.4, Associating a builder with a project, on page 509) and the nature has the opportunity to configure the project. For example, the Java nature configures a project by adding the Java compiler as a build command. A nature also causes a project to be treated differently by the workbench; for instance, only projects that possess the Java nature are displayed by the Package Explorer view. When a nature is removed, it has the opportunity to deconfigure or remove aspects of itself from the project. The following are several natures defined within Eclipse that provide various types of behavior.
14.3.1. Declaring a natureFor the Favorites product, you want a new propertiesAuditor nature to associate the property file audit builder with a project. Begin by creating a new org.eclipse.core.resources.natures extension in the Favorites plug-in manifest. Switch to the Extensions page, click the Add... button, select org.eclipse.core.resources.natures, and then click Finish (see Figure 14-11). Figure 14-11. The New Extension wizard showing the natures extension point selected.
Click on the new extension to edit the properties, change the id to "propertiesAuditor", and change the name to "Favorites Properties Auditor" (see Figure 14-12). The nature declaration should look like this: < extension id="propertiesAuditor" name="Favorites Properties Auditor" point="org.eclipse.core.resources.natures"> </extension> Figure 14-12. The extension details showing the nature's attributes.Similar to build declarations, the nature declaration contains the nature's local identifier. The nature's full identifier is the plug-in identifier containing the nature concatenated with the nature's local identifier, or in this case, com.qualityeclipse.favorites.propertiesAuditor. 14.3.2. Associating builders and naturesNow you want to associate your builder with your nature. Click on the org.eclipse.core.resources.natures extension point and select New > builder. Enter the builder id, which in this case is "com.qualityeclipse.favorites.propertiesFileAuditor" (see Figure 14-13). Figure 14-13. The extension element details showing the builder's attributes.In addition, return to the builder declaration (see Section 14.1.1, Declaring a builder, on page 499) and modify the hasNature attribute to be "true." After this has been accomplished, the nature declaration should look like this: <extension id="propertiesAuditor" name="Favorites Properties Auditor" point="org.eclipse.core.resources.natures"> <builder id="com.qualityeclipse.favorites .propertiesFileAuditor"/> </extension> These changes ensure that the builder will be omitted from a project's build process if the nature is not associated with the project. If you want your builder to work regardless of whether your nature is present, then omit this from your nature's declaration. 14.3.3. IProjectNatureNatures can have behavior to configure and deconfigure a project. Similar to the Java nature, you want the nature to add your builder to the project's build spec. Right-click on the org.eclipse.core.resources.natures extension point and select New > runtime, then right-click on the (runtime) nested element and select New > run. In the plug-in manifest editor, click on the "class" label at the left of the class field, then use the Java Attribute Editor to generate a new class named PropertiesAuditorNature in the com.qualityeclipse.favorites.builder package. When this is complete, the nature declaration should look like this: <extension id="propertiesAuditor" name="Favorites Properties Auditor" point="org.eclipse.core.resources.natures"> <builder id="com.qualityeclipse.favorites .propertiesFileAuditor"/> <runtime> <run class="com.qualityeclipse.favorites.builder .PropertiesAuditorNature"/> </runtime> </extension> The class specified in the nature declaration must implement the org.eclipse.core.resources.IProjectNature interface. When the nature is added to a project, this class is instantiated and the setProject() method is called, followed by the configure() method; the deconfigure() method is called when the nature is removed from a project. Similar to the Java nature, you want the nature to add your builder to the project's build spec via the addBuilderToProject() method (see Section 14.1.4, Associating a builder with a project, on page 509) and trigger a build in the background (see Section 20.8, Background TasksJobs API, on page 739) when the project is configured. When the nature is removed from the project, the build spec is modified and all audit markers are removed. package com.qualityeclipse.favorites.builder; import ... public class PropertiesAuditorNature implements IProjectNature { private IProject project; public IProject getProject() { return project; } public void setProject(IProject project) { this.project = project; } public void configure() throws CoreException { PropertiesFileAuditor.addBuilderToProject(project); new Job("Properties File Audit") { protected IStatus run(IProgressMonitor monitor) { try { project.build( PropertiesFileAuditor.FULL_BUILD, PropertiesFileAuditor.BUILDER_ID, null, monitor); } catch (CoreException e) { FavoritesLog.logError(e); } return Status.OK_STATUS; } }.schedule(); } public void deconfigure() throws CoreException { PropertiesFileAuditor.removeBuilderFromProject(project); PropertiesFileAuditor.deleteAuditMarkers(project); } } 14.3.4. Required naturesA dependency of one nature on another nature can be expressed in the nature's declaration (see Section 14.3.1, Declaring a nature, on page 526). When the required nature is not present or not enabled, Eclipse disables the nature having the requirement. For example, the propertiesAuditor nature depends on the Java nature and PDE nature. If you were to express this in your nature's declaration, it would look like this: <extension id="propertiesAuditor" name="Favorites Properties Auditor" point="org.eclipse.core.resources.natures"> <builder id="com.qualityeclipse.favorites .propertiesFileAuditor"> </builder> <runtime> <run class="com.qualityeclipse.favorites.builder .PropertiesAuditorNature"/> </runtime> <requires-nature id="org.eclipse.jdt.core.javanature"/> <requires-nature id="org.eclipse.pde.PluginNature"/> </extension> 14.3.5. Conflicting naturesThe conflict of one nature with one or more other natures can also be expressed in the nature's declaration. In your nature's declaration, add a one-of-nature nested element specifying the name of a set of natures. If any other nature specifies the same string in a one-of-nature nested element and is added to the same project as your nature, then Eclipse will disable both natures. <extension
id="propertiesAuditor"
name="Favorites Properties Auditor"
point="org.eclipse.core.resources.natures">
<builder id="com.qualityeclipse.favorites
.propertiesFileAuditor">
</builder>
<runtime>
<run class="com.qualityeclipse.favorites.builder
.PropertiesAuditorNature"/>
</runtime>
<requires-nature id="org.eclipse.jdt.core.javanature"/>
<requires-nature id="org.eclipse.pde.PluginNature"/>
<one-of-nature id="pluginAuditors">
</extension>
14.3.6. Nature imageA project nature can have an image associated with it using the org.eclipse.ui.ide.projectNatureImages extension point. The specified image is displayed over the top right corner of the standard project image. For example, the org.eclipse.jdt.ui plug-in associates an image of a "J" with the Java nature so that the icon for all Java projects has a small blue "J" at the top right corner. <extension point="org.eclipse.ui.ide.projectNatureImages"> <image icon="icons/full/ovr16/java_ovr.gif" natureId="org.eclipse.jdt.core.javanature" id="org.eclipse.ui.javaProjectNatureImage"/> </extension> The nature here does not define the type of project so much as associate the properties audit tool with the project, so it is not appropriate to supply a project nature image. 14.3.7. Associating a nature with a projectSimilar to associating a builder with a project (see Section 14.1.4, Associating a builder with a project, on page 509), you can associate a nature with a project by modifying the project's description. To demonstrate this, build an action delegate that toggles the propertiesAuditor nature for a project. First, create an action declaration to add a new command in the top-level Favorites menu (see Section 6.2.1, Defining a workbench window menu, on page 209). <extension point="org.eclipse.ui.actionSets"> <actionSet label="Favorites ActionSet" visible="true" id="com.qualityeclipse.favorites.workbenchActionSet"> ... <action label="Add/Remove propertiesAuditor project nature" class="com.qualityeclipse.favorites.actions .ToggleProjectNatureActionDelegate" menubarPath="com.qualityeclipse.favorites .workbenchMenu/content" id="com.qualityeclipse.favorites.toggleProjectNature"\> </actionSet> </extension> Next, create an action delegate (see Section 6.2.6, Creating an action delegate, on page 216) that checks the natures associated with every selected project and adds the propertiesAuditor nature to each one that does not have that nature associated with it and removes that nature from all other selected projects. Typically, a nature is added to or removed from a project as part of a larger process such as creating a Java project, but this action delegate suffices to show the mechanics of how it is accomplished. package com.qualityeclipse.favorites.actions; import ... public class ToggleProjectNatureActionDelegate extends ActionDelegate implements IWorkbenchWindowActionDelegate { private static final String NATURE_ID = FavoritesPlugin.ID + ".propertiesAuditor"; private final Set projects = new HashSet(); public void init(IWorkbenchWindow window) { // Ignored. } } When the selection changes, the selectionChanged() method caches the currently selected projects and updates the action enablement and checked state based on that seletion. public void selectionChanged(IAction action, ISelection selection) { updateSelectedProjects(selection); if (projects.size() > 0) { action.setEnabled(true); boolean checked; try { checked = project.isOpen() && project.hasNature(NATURE_ID); } catch (CoreException e) { checked = false; FavoritesLog.logError(e); } action.setEnabled(true); action.setChecked(checked); } else { action.setEnabled(false); action.setChecked(false); } } private void updateSelectedProjects(ISelection selection) { projects.clear(); if (!(selection instanceof IStructuredSelection)) return; for ( Iterator iter = ((IStructuredSelection) selection).iterator(); iter.hasNext(); ) { Object elem = iter.next(); if (!(elem instanceof IResource)) { if (!(elem instanceof IAdaptable)) continue; elem = ((IAdaptable) elem).getAdapter(IResource.class); if (!(elem instanceof IResource)) continue; } if (!(elem instanceof IProject)) { elem = ((IResource) elem).getProject(); if (!(elem instanceof IProject)) continue; } projects.add(elem); } } When the action delegate is selected, it adds the nature to or removes the nature from the selected projects so that the project's plug-in properties are audited. public void run(IAction action) { for (Iterator iter = projects.iterator(); iter.hasNext();) { final IProject project = (IProject) iter.next(); // Cannot modify closed projects. if (!project.isOpen()) { continue; } // Get the description. IProjectDescription description; try { description = project.getDescription(); } catch (CoreException e) { FavoritesLog.logError(e); continue; } // Toggle the nature. List newIds = new ArrayList(); newIds.addAll(Arrays.asList(description.getNatureIds())); int index = newIds.indexOf(NATURE_ID); if (index == -1) { newIds.add(NATURE_ID); } else { newIds.remove(index); } description.setNatureIds( (String[]) newIds.toArray(new String[newIds.size()])); // Save the description. try { project.setDescription(description, null); } catch (CoreException e) { FavoritesLog.logError(e); } } } |