KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > resources > NatureManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.resources;
12
13 import java.util.*;
14 import org.eclipse.core.internal.events.ILifecycleListener;
15 import org.eclipse.core.internal.events.LifecycleEvent;
16 import org.eclipse.core.internal.utils.Messages;
17 import org.eclipse.core.internal.utils.Policy;
18 import org.eclipse.core.resources.*;
19 import org.eclipse.core.runtime.*;
20 import org.eclipse.osgi.util.NLS;
21
22 /**
23  * Maintains collection of known nature descriptors, and implements
24  * nature-related algorithms provided by the workspace.
25  */

26 public class NatureManager implements ILifecycleListener, IManager {
27     //maps String (nature ID) -> descriptor objects
28
protected Map descriptors;
29
30     //maps IProject -> String[] of enabled natures for that project
31
protected Map natureEnablements;
32
33     //maps String (builder ID) -> String (nature ID)
34
protected Map buildersToNatures = null;
35     //colour constants used in cycle detection algorithm
36
private static final byte WHITE = 0;
37     private static final byte GREY = 1;
38     private static final byte BLACK = 2;
39
40     protected NatureManager() {
41         super();
42     }
43
44     /**
45      * Computes the list of natures that are enabled for the given project.
46      * Enablement computation is subtlely different from nature set
47      * validation, because it must find and remove all inconsistencies.
48      */

49     protected String JavaDoc[] computeNatureEnablements(Project project) {
50         final ProjectDescription description = project.internalGetDescription();
51         if (description == null)
52             return new String JavaDoc[0];//project deleted concurrently
53
String JavaDoc[] natureIds = description.getNatureIds();
54         int count = natureIds.length;
55         if (count == 0)
56             return natureIds;
57
58         //set of the nature ids being validated (String (id))
59
HashSet candidates = new HashSet(count * 2);
60         //table of String(set ID) -> ArrayList (nature IDs that belong to that set)
61
HashMap setsToNatures = new HashMap(count);
62         for (int i = 0; i < count; i++) {
63             String JavaDoc id = natureIds[i];
64             ProjectNatureDescriptor desc = (ProjectNatureDescriptor) getNatureDescriptor(id);
65             if (desc == null)
66                 continue;
67             if (!desc.hasCycle)
68                 candidates.add(id);
69             //build set to nature map
70
String JavaDoc[] setIds = desc.getNatureSetIds();
71             for (int j = 0; j < setIds.length; j++) {
72                 String JavaDoc set = setIds[j];
73                 ArrayList current = (ArrayList) setsToNatures.get(set);
74                 if (current == null) {
75                     current = new ArrayList(5);
76                     setsToNatures.put(set, current);
77                 }
78                 current.add(id);
79             }
80         }
81         //now remove all natures that belong to sets with more than one member
82
for (Iterator it = setsToNatures.values().iterator(); it.hasNext();) {
83             ArrayList setMembers = (ArrayList) it.next();
84             if (setMembers.size() > 1) {
85                 candidates.removeAll(setMembers);
86             }
87         }
88         //now walk over the set and ensure all pre-requisite natures are present
89
//need to walk in prereq order because if A requires B and B requires C, and C is
90
//disabled for some other reason, we must ensure both A and B are disabled
91
String JavaDoc[] orderedCandidates = (String JavaDoc[]) candidates.toArray(new String JavaDoc[candidates.size()]);
92         orderedCandidates = sortNatureSet(orderedCandidates);
93         for (int i = 0; i < orderedCandidates.length; i++) {
94             String JavaDoc id = orderedCandidates[i];
95             IProjectNatureDescriptor desc = getNatureDescriptor(id);
96             String JavaDoc[] required = desc.getRequiredNatureIds();
97             for (int j = 0; j < required.length; j++) {
98                 if (!candidates.contains(required[j])) {
99                     candidates.remove(id);
100                     break;
101                 }
102             }
103         }
104         //remaining candidates are enabled
105
return (String JavaDoc[]) candidates.toArray(new String JavaDoc[candidates.size()]);
106     }
107
108     /* (non-Javadoc)
109      * @see IWorkspace#getNatureDescriptor(String)
110      */

111     public IProjectNatureDescriptor getNatureDescriptor(String JavaDoc natureId) {
112         lazyInitialize();
113         return (IProjectNatureDescriptor) descriptors.get(natureId);
114     }
115
116     /* (non-Javadoc)
117      * @see IWorkspace#getNatureDescriptors()
118      */

119     public IProjectNatureDescriptor[] getNatureDescriptors() {
120         lazyInitialize();
121         Collection values = descriptors.values();
122         return (IProjectNatureDescriptor[]) values.toArray(new IProjectNatureDescriptor[values.size()]);
123     }
124
125     public void handleEvent(LifecycleEvent event) {
126         switch (event.kind) {
127             case LifecycleEvent.PRE_PROJECT_CHANGE :
128             case LifecycleEvent.PRE_PROJECT_CLOSE :
129             case LifecycleEvent.PRE_PROJECT_DELETE :
130             case LifecycleEvent.PRE_PROJECT_MOVE :
131             case LifecycleEvent.PRE_PROJECT_OPEN :
132                 flushEnablements((IProject) event.resource);
133         }
134     }
135
136     /**
137      * Configures the nature with the given ID for the given project.
138      */

139     protected void configureNature(final Project project, final String JavaDoc natureID, final MultiStatus errors) {
140         ISafeRunnable code = new ISafeRunnable() {
141             public void run() throws Exception JavaDoc {
142                 IProjectNature nature = createNature(project, natureID);
143                 nature.configure();
144                 ProjectInfo info = (ProjectInfo) project.getResourceInfo(false, true);
145                 info.setNature(natureID, nature);
146             }
147
148             public void handleException(Throwable JavaDoc exception) {
149                 if (exception instanceof CoreException)
150                     errors.add(((CoreException) exception).getStatus());
151                 else
152                     errors.add(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, project.getFullPath(), NLS.bind(Messages.resources_errorNature, natureID), exception));
153             }
154         };
155         if (Policy.DEBUG_NATURES) {
156             System.out.println("Configuring nature: " + natureID + " on project: " + project.getName()); //$NON-NLS-1$ //$NON-NLS-2$
157
}
158         SafeRunner.run(code);
159     }
160
161     /**
162      * Configures the natures for the given project. Natures found in the new description
163      * that weren't present in the old description are added, and natures missing from the
164      * new description are removed. Updates the old description so that it reflects
165      * the new set of the natures. Errors are added to the given multi-status.
166      */

167     public void configureNatures(Project project, ProjectDescription oldDescription, ProjectDescription newDescription, MultiStatus status) {
168         // Be careful not to rely on much state because (de)configuring a nature
169
// may well result in recursive calls to this method.
170
HashSet oldNatures = new HashSet(Arrays.asList(oldDescription.getNatureIds(false)));
171         HashSet newNatures = new HashSet(Arrays.asList(newDescription.getNatureIds(false)));
172         if (oldNatures.equals(newNatures))
173             return;
174         HashSet deletions = (HashSet) oldNatures.clone();
175         HashSet additions = (HashSet) newNatures.clone();
176         additions.removeAll(oldNatures);
177         deletions.removeAll(newNatures);
178         //do validation of the changes. If any single change is invalid, fail the whole operation
179
IStatus result = validateAdditions(newNatures, additions, project);
180         if (!result.isOK()) {
181             status.merge(result);
182             return;
183         }
184         result = validateRemovals(newNatures, deletions);
185         if (!result.isOK()) {
186             status.merge(result);
187             return;
188         }
189         // set the list of nature ids BEFORE (de)configuration so recursive calls will
190
// not try to do the same work.
191
oldDescription.setNatureIds(newDescription.getNatureIds(true));
192         flushEnablements(project);
193         //(de)configure in topological order to maintain consistency of configured set
194
String JavaDoc[] ordered = null;
195         if (deletions.size() > 0) {
196             ordered = sortNatureSet((String JavaDoc[]) deletions.toArray(new String JavaDoc[deletions.size()]));
197             for (int i = ordered.length; --i >= 0;)
198                 deconfigureNature(project, ordered[i], status);
199         }
200         if (additions.size() > 0) {
201             ordered = sortNatureSet((String JavaDoc[]) additions.toArray(new String JavaDoc[additions.size()]));
202             for (int i = 0; i < ordered.length; i++)
203                 configureNature(project, ordered[i], status);
204         }
205     }
206
207     /**
208      * Finds the nature extension, and initializes and returns an instance.
209      */

210     protected IProjectNature createNature(Project project, String JavaDoc natureID) throws CoreException {
211         IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_NATURES, natureID);
212         if (extension == null) {
213             String JavaDoc message = NLS.bind(Messages.resources_natureExtension, natureID);
214             throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, null);
215         }
216         IConfigurationElement[] configs = extension.getConfigurationElements();
217         if (configs.length < 1) {
218             String JavaDoc message = NLS.bind(Messages.resources_natureClass, natureID);
219             throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, null);
220         }
221         //find the runtime configuration element
222
IConfigurationElement config = null;
223         for (int i = 0; config == null && i < configs.length; i++)
224             if ("runtime".equalsIgnoreCase(configs[i].getName())) //$NON-NLS-1$
225
config = configs[i];
226         if (config == null) {
227             String JavaDoc message = NLS.bind(Messages.resources_natureFormat, natureID);
228             throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, null);
229         }
230         try {
231             IProjectNature nature = (IProjectNature) config.createExecutableExtension("run"); //$NON-NLS-1$
232
nature.setProject(project);
233             return nature;
234         } catch (ClassCastException JavaDoc e) {
235             String JavaDoc message = NLS.bind(Messages.resources_natureImplement, natureID);
236             throw new ResourceException(Platform.PLUGIN_ERROR, project.getFullPath(), message, e);
237         }
238     }
239
240     /**
241      * Deconfigures the nature with the given ID for the given project.
242      */

243     protected void deconfigureNature(final Project project, final String JavaDoc natureID, final MultiStatus status) {
244         final ProjectInfo info = (ProjectInfo) project.getResourceInfo(false, true);
245         IProjectNature existingNature = info.getNature(natureID);
246         if (existingNature == null) {
247             // if there isn't a nature then create one so we can deconfig it.
248
try {
249                 existingNature = createNature(project, natureID);
250             } catch (CoreException e) {
251                 // have to swallow the exception because it must be possible
252
//to remove a nature that no longer exists in the install
253
Policy.log(e.getStatus());
254                 return;
255             }
256         }
257         final IProjectNature nature = existingNature;
258         ISafeRunnable code = new ISafeRunnable() {
259             public void run() throws Exception JavaDoc {
260                 nature.deconfigure();
261                 info.setNature(natureID, null);
262             }
263
264             public void handleException(Throwable JavaDoc exception) {
265                 if (exception instanceof CoreException)
266                     status.add(((CoreException) exception).getStatus());
267                 else
268                     status.add(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, project.getFullPath(), NLS.bind(Messages.resources_natureDeconfig, natureID), exception));
269             }
270         };
271         if (Policy.DEBUG_NATURES) {
272             System.out.println("Deconfiguring nature: " + natureID + " on project: " + project.getName()); //$NON-NLS-1$ //$NON-NLS-2$
273
}
274         SafeRunner.run(code);
275     }
276
277     /**
278      * Marks all nature descriptors that are involved in cycles
279      */

280     protected void detectCycles() {
281         Collection values = descriptors.values();
282         ProjectNatureDescriptor[] natures = (ProjectNatureDescriptor[]) values.toArray(new ProjectNatureDescriptor[values.size()]);
283         for (int i = 0; i < natures.length; i++)
284             if (natures[i].colour == WHITE)
285                 hasCycles(natures[i]);
286     }
287
288     /**
289      * Returns a status indicating failure to configure natures.
290      */

291     protected IStatus failure(String JavaDoc reason) {
292         return new ResourceStatus(IResourceStatus.INVALID_NATURE_SET, reason);
293     }
294
295     /**
296      * Returns the ID of the project nature that claims ownership of the
297      * builder with the given ID. Returns null if no nature owns that builder.
298      */

299     public String JavaDoc findNatureForBuilder(String JavaDoc builderID) {
300         if (buildersToNatures == null) {
301             buildersToNatures = new HashMap(10);
302             IProjectNatureDescriptor[] descs = getNatureDescriptors();
303             for (int i = 0; i < descs.length; i++) {
304                 String JavaDoc natureId = descs[i].getNatureId();
305                 String JavaDoc[] builders = ((ProjectNatureDescriptor) descs[i]).getBuilderIds();
306                 for (int j = 0; j < builders.length; j++) {
307                     //FIXME: how to handle multiple natures specifying same builder
308
buildersToNatures.put(builders[j], natureId);
309                 }
310             }
311         }
312         return (String JavaDoc) buildersToNatures.get(builderID);
313     }
314
315     protected void flushEnablements(IProject project) {
316         if (natureEnablements != null) {
317             natureEnablements.remove(project);
318             if (natureEnablements.size() == 0) {
319                 natureEnablements = null;
320             }
321         }
322     }
323
324     /**
325      * Returns the cached array of enabled natures for this project,
326      * or null if there is nothing in the cache.
327      */

328     protected String JavaDoc[] getEnabledNatures(Project project) {
329         String JavaDoc[] enabled;
330         if (natureEnablements != null) {
331             enabled = (String JavaDoc[]) natureEnablements.get(project);
332             if (enabled != null)
333                 return enabled;
334         }
335         enabled = computeNatureEnablements(project);
336         setEnabledNatures(project, enabled);
337         return enabled;
338     }
339
340     /**
341      * Returns true if there are cycles in the graph of nature
342      * dependencies starting at root i. Returns false otherwise.
343      * Marks all descriptors that are involved in the cycle as invalid.
344      */

345     protected boolean hasCycles(ProjectNatureDescriptor desc) {
346         if (desc.colour == BLACK) {
347             //this subgraph has already been traversed, so we know the answer
348
return desc.hasCycle;
349         }
350         //if we are already grey, then we have found a cycle
351
if (desc.colour == GREY) {
352             desc.hasCycle = true;
353             desc.colour = BLACK;
354             return true;
355         }
356         //colour current descriptor GREY to indicate it is being visited
357
desc.colour = GREY;
358
359         //visit all dependents of nature i
360
String JavaDoc[] required = desc.getRequiredNatureIds();
361         for (int i = 0; i < required.length; i++) {
362             ProjectNatureDescriptor dependency = (ProjectNatureDescriptor) getNatureDescriptor(required[i]);
363             //missing dependencies cannot create cycles
364
if (dependency != null && hasCycles(dependency)) {
365                 desc.hasCycle = true;
366                 desc.colour = BLACK;
367                 return true;
368             }
369         }
370         desc.hasCycle = false;
371         desc.colour = BLACK;
372         return false;
373     }
374
375     /**
376      * Returns true if the given project has linked resources, and false otherwise.
377      */

378     protected boolean hasLinks(IProject project) {
379         try {
380             IResource[] children = project.members();
381             for (int i = 0; i < children.length; i++)
382                 if (children[i].isLinked())
383                     return true;
384         } catch (CoreException e) {
385             //not possible for project to be inaccessible
386
Policy.log(e.getStatus());
387         }
388         return false;
389     }
390
391     /**
392      * Checks if the two natures have overlapping "one-of-nature" set
393      * memberships. Returns the name of one such overlap, or null if
394      * there is no set overlap.
395      */

396     protected String JavaDoc hasSetOverlap(IProjectNatureDescriptor one, IProjectNatureDescriptor two) {
397         if (one == null || two == null) {
398             return null;
399         }
400         //efficiency not so important because these sets are very small
401
String JavaDoc[] setsOne = one.getNatureSetIds();
402         String JavaDoc[] setsTwo = two.getNatureSetIds();
403         for (int iOne = 0; iOne < setsOne.length; iOne++) {
404             for (int iTwo = 0; iTwo < setsTwo.length; iTwo++) {
405                 if (setsOne[iOne].equals(setsTwo[iTwo])) {
406                     return setsOne[iOne];
407                 }
408             }
409         }
410         return null;
411     }
412
413     /**
414      * Perform depth-first insertion of the given nature ID into the result list.
415      */

416     protected void insert(ArrayList list, Set seen, String JavaDoc id) {
417         if (seen.contains(id))
418             return;
419         seen.add(id);
420         //insert prerequisite natures
421
IProjectNatureDescriptor desc = getNatureDescriptor(id);
422         if (desc != null) {
423             String JavaDoc[] prereqs = desc.getRequiredNatureIds();
424             for (int i = 0; i < prereqs.length; i++)
425                 insert(list, seen, prereqs[i]);
426         }
427         list.add(id);
428     }
429
430     /* (non-Javadoc)
431      * Returns true if the given nature is enabled for the given project.
432      *
433      * @see IProject#isNatureEnabled(String)
434      */

435     public boolean isNatureEnabled(Project project, String JavaDoc id) {
436         String JavaDoc[] enabled = getEnabledNatures(project);
437         for (int i = 0; i < enabled.length; i++) {
438             if (enabled[i].equals(id))
439                 return true;
440         }
441         return false;
442     }
443
444     /**
445      * Only initialize the descriptor cache when we know it is actually needed.
446      * Running programs may never need to refer to this cache.
447      */

448     protected void lazyInitialize() {
449         if (descriptors != null)
450             return;
451         IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_NATURES);
452         IExtension[] extensions = point.getExtensions();
453         descriptors = new HashMap(extensions.length * 2 + 1);
454         for (int i = 0, imax = extensions.length; i < imax; i++) {
455             IProjectNatureDescriptor desc = null;
456             try {
457                 desc = new ProjectNatureDescriptor(extensions[i]);
458             } catch (CoreException e) {
459                 Policy.log(e.getStatus());
460             }
461             if (desc != null)
462                 descriptors.put(desc.getNatureId(), desc);
463         }
464         //do cycle detection now so it only has to be done once
465
//cycle detection on a graph subset is a pain
466
detectCycles();
467     }
468
469     /**
470      * Sets the cached array of enabled natures for this project.
471      */

472     protected void setEnabledNatures(IProject project, String JavaDoc[] enablements) {
473         if (natureEnablements == null)
474             natureEnablements = new HashMap(20);
475         natureEnablements.put(project, enablements);
476     }
477
478     public void shutdown(IProgressMonitor monitor) {
479         // do nothing
480
}
481
482     /* (non-Javadoc)
483      * @see IWorkspace#sortNatureSet(String[])
484      */

485     public String JavaDoc[] sortNatureSet(String JavaDoc[] natureIds) {
486         int count = natureIds.length;
487         if (count == 0)
488             return natureIds;
489         ArrayList result = new ArrayList(count);
490         HashSet seen = new HashSet(count);//for cycle and duplicate detection
491
for (int i = 0; i < count; i++)
492             insert(result, seen, natureIds[i]);
493         //remove added prerequisites that didn't exist in original list
494
seen.clear();
495         seen.addAll(Arrays.asList(natureIds));
496         for (Iterator it = result.iterator(); it.hasNext();) {
497             Object JavaDoc id = it.next();
498             if (!seen.contains(id))
499                 it.remove();
500         }
501         return (String JavaDoc[]) result.toArray(new String JavaDoc[result.size()]);
502     }
503
504     public void startup(IProgressMonitor monitor) {
505         ((Workspace) ResourcesPlugin.getWorkspace()).addLifecycleListener(this);
506     }
507
508     /**
509      * Validates the given nature additions in the nature set for this
510      * project. Tolerates existing inconsistencies in the nature set.
511      * @param newNatures the complete new set of nature IDs for the project,
512      * including additions
513      * @param additions the subset of newNatures that represents natures
514      * being added
515      * @return An OK status if all additions are valid, and an error status
516      * if any of the additions introduce new inconsistencies.
517      */

518     protected IStatus validateAdditions(HashSet newNatures, HashSet additions, IProject project) {
519         Boolean JavaDoc hasLinks = null;//three states: true, false, null (not yet computed)
520
//perform checks in order from least expensive to most expensive
521
for (Iterator added = additions.iterator(); added.hasNext();) {
522             String JavaDoc id = (String JavaDoc) added.next();
523             // check for adding a nature that is not available.
524
IProjectNatureDescriptor desc = getNatureDescriptor(id);
525             if (desc == null) {
526                 return failure(NLS.bind(Messages.natures_missingNature, id));
527             }
528             // check for adding a nature that creates a circular dependency
529
if (((ProjectNatureDescriptor) desc).hasCycle) {
530                 return failure(NLS.bind(Messages.natures_hasCycle, id));
531             }
532             // check for adding a nature that has a missing prerequisite.
533
String JavaDoc[] required = desc.getRequiredNatureIds();
534             for (int i = 0; i < required.length; i++) {
535                 if (!newNatures.contains(required[i])) {
536                     return failure(NLS.bind(Messages.natures_missingPrerequisite, id, required[i]));
537                 }
538             }
539             // check for adding a nature that creates a duplicated set member.
540
for (Iterator all = newNatures.iterator(); all.hasNext();) {
541                 String JavaDoc current = (String JavaDoc) all.next();
542                 if (!current.equals(id)) {
543                     String JavaDoc overlap = hasSetOverlap(desc, getNatureDescriptor(current));
544                     if (overlap != null) {
545                         return failure(NLS.bind(Messages.natures_multipleSetMembers, overlap));
546                     }
547                 }
548             }
549             //check for adding a nature that has linked resource veto
550
if (!desc.isLinkingAllowed()) {
551                 if (hasLinks == null) {
552                     hasLinks = hasLinks(project) ? Boolean.TRUE : Boolean.FALSE;
553                 }
554                 if (hasLinks.booleanValue())
555                     return failure(NLS.bind(Messages.links_vetoNature, project.getName(), id));
556             }
557         }
558         return Status.OK_STATUS;
559     }
560
561     /**
562      * Validates whether a project with the given set of natures should allow
563      * linked resources. Returns an OK status if linking is allowed,
564      * otherwise a non-OK status indicating why linking is not allowed.
565      * Linking is allowed if there is no project nature that explicitly disallows it.
566      * No validation is done on the nature ids themselves (ids that don't have
567      * a corresponding nature definition will be ignored).
568      */

569     public IStatus validateLinkCreation(String JavaDoc[] natureIds) {
570         for (int i = 0; i < natureIds.length; i++) {
571             IProjectNatureDescriptor desc = getNatureDescriptor(natureIds[i]);
572             if (desc != null && !desc.isLinkingAllowed()) {
573                 String JavaDoc msg = NLS.bind(Messages.links_natureVeto, desc.getLabel());
574                 return new ResourceStatus(IResourceStatus.LINKING_NOT_ALLOWED, msg);
575             }
576         }
577         return Status.OK_STATUS;
578     }
579
580     /**
581      * Validates the given nature removals in the nature set for this
582      * project. Tolerates existing inconsistencies in the nature set.
583      *
584      * @param newNatures the complete new set of nature IDs for the project,
585      * excluding deletions
586      * @param deletions the nature IDs that are being removed from the set.
587      * @return An OK status if all removals are valid, and a not OK status
588      * if any of the deletions introduce new inconsistencies.
589      */

590     protected IStatus validateRemovals(HashSet newNatures, HashSet deletions) {
591         //iterate over new nature set, and ensure that none of their prerequisites are being deleted
592
for (Iterator it = newNatures.iterator(); it.hasNext();) {
593             String JavaDoc currentID = (String JavaDoc) it.next();
594             IProjectNatureDescriptor desc = getNatureDescriptor(currentID);
595             if (desc != null) {
596                 String JavaDoc[] required = desc.getRequiredNatureIds();
597                 for (int i = 0; i < required.length; i++) {
598                     if (deletions.contains(required[i])) {
599                         return failure(NLS.bind(Messages.natures_invalidRemoval, required[i], currentID));
600                     }
601                 }
602             }
603         }
604         return Status.OK_STATUS;
605     }
606
607     /* (non-Javadoc)
608      * @see IWorkspace#validateNatureSet(String[])
609      */

610     public IStatus validateNatureSet(String JavaDoc[] natureIds) {
611         int count = natureIds.length;
612         if (count == 0)
613             return Status.OK_STATUS;
614         String JavaDoc msg = Messages.natures_invalidSet;
615         MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_NATURE_SET, msg, null);
616
617         //set of the nature ids being validated (String (id))
618
HashSet natures = new HashSet(count * 2);
619         //set of nature sets for which a member nature has been found (String (id))
620
HashSet sets = new HashSet(count);
621         for (int i = 0; i < count; i++) {
622             String JavaDoc id = natureIds[i];
623             ProjectNatureDescriptor desc = (ProjectNatureDescriptor) getNatureDescriptor(id);
624             if (desc == null) {
625                 result.add(failure(NLS.bind(Messages.natures_missingNature, id)));
626                 continue;
627             }
628             if (desc.hasCycle)
629                 result.add(failure(NLS.bind(Messages.natures_hasCycle, id)));
630             if (!natures.add(id))
631                 result.add(failure(NLS.bind(Messages.natures_duplicateNature, id)));
632             //validate nature set one-of constraint
633
String JavaDoc[] setIds = desc.getNatureSetIds();
634             for (int j = 0; j < setIds.length; j++) {
635                 if (!sets.add(setIds[j]))
636                     result.add(failure(NLS.bind(Messages.natures_multipleSetMembers, setIds[j])));
637             }
638         }
639         //now walk over the set and ensure all pre-requisite natures are present
640
for (int i = 0; i < count; i++) {
641             IProjectNatureDescriptor desc = getNatureDescriptor(natureIds[i]);
642             if (desc == null)
643                 continue;
644             String JavaDoc[] required = desc.getRequiredNatureIds();
645             for (int j = 0; j < required.length; j++)
646                 if (!natures.contains(required[j]))
647                     result.add(failure(NLS.bind(Messages.natures_missingPrerequisite, natureIds[i], required[j])));
648         }
649         //if there are no problems we must return a status whose code is OK
650
return result.isOK() ? Status.OK_STATUS : (IStatus) result;
651     }
652 }
653
Popular Tags