View | Details | Raw Unified | Return to bug 69640 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/core/internal/resources/ContentDescriptionManager.java (-22 / +103 lines)
Lines 11-16 Link Here
11
package org.eclipse.core.internal.resources;
11
package org.eclipse.core.internal.resources;
12
12
13
import java.io.*;
13
import java.io.*;
14
import org.eclipse.core.internal.events.ILifecycleListener;
15
import org.eclipse.core.internal.events.LifecycleEvent;
14
import org.eclipse.core.internal.utils.*;
16
import org.eclipse.core.internal.utils.*;
15
import org.eclipse.core.internal.watson.*;
17
import org.eclipse.core.internal.watson.*;
16
import org.eclipse.core.resources.*;
18
import org.eclipse.core.resources.*;
Lines 18-24 Link Here
18
import org.eclipse.core.runtime.content.*;
20
import org.eclipse.core.runtime.content.*;
19
import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
21
import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
20
import org.eclipse.core.runtime.jobs.ISchedulingRule;
22
import org.eclipse.core.runtime.jobs.ISchedulingRule;
21
import org.eclipse.core.runtime.jobs.Job;
22
import org.eclipse.osgi.util.NLS;
23
import org.eclipse.osgi.util.NLS;
23
24
24
/**
25
/**
Lines 27-39 Link Here
27
 * @since 3.0
28
 * @since 3.0
28
 * @see IFile#getContentDescription()
29
 * @see IFile#getContentDescription()
29
 */
30
 */
30
public class ContentDescriptionManager implements IManager, IRegistryChangeListener, IContentTypeManager.IContentTypeChangeListener {
31
public class ContentDescriptionManager implements IManager, IRegistryChangeListener, IContentTypeManager.IContentTypeChangeListener, ILifecycleListener {
31
32
	/**
32
	/**
33
	 * This job causes the content description cache and the related flags 
33
	 * This job causes the content description cache and the related flags 
34
	 * in the resource tree to be flushed. 
34
	 * in the resource tree to be flushed. 
35
	 */
35
	 */
36
	private class FlushJob extends WorkspaceJob {
36
	private class FlushJob extends WorkspaceJob {
37
		private Queue toFlush;
38
		private boolean fullFlush;
37
39
38
		public FlushJob() {
40
		public FlushJob() {
39
			super(Messages.resources_flushingContentDescriptionCache);
41
			super(Messages.resources_flushingContentDescriptionCache);
Lines 41-46 Link Here
41
			setUser(false);
43
			setUser(false);
42
			setPriority(LONG);
44
			setPriority(LONG);
43
			setRule(workspace.getRoot());
45
			setRule(workspace.getRoot());
46
			toFlush = new Queue();
44
		}
47
		}
45
48
46
		/* (non-Javadoc)
49
		/* (non-Javadoc)
Lines 64-70 Link Here
64
				try {
67
				try {
65
					workspace.prepareOperation(rule, monitor);
68
					workspace.prepareOperation(rule, monitor);
66
					workspace.beginOperation(true);
69
					workspace.beginOperation(true);
67
					doFlushCache(monitor);
70
					doFlushCache(monitor, getPathsToFlush());
68
				} finally {
71
				} finally {
69
					workspace.endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
72
					workspace.endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
70
				}
73
				}
Lines 77-82 Link Here
77
			}
80
			}
78
			return Status.OK_STATUS;
81
			return Status.OK_STATUS;
79
		}
82
		}
83
84
		private IPath[] getPathsToFlush() {
85
			synchronized (toFlush) {
86
				if (fullFlush)
87
					return null;
88
				int size = toFlush.size();
89
				return (size == 0) ? null : (IPath[]) toFlush.copyTo(new IPath[size]);
90
			}
91
		}
92
93
		/**
94
		 * @param project project to flush, or null for a full flush
95
		 */
96
		void flush(IProject project) {
97
			synchronized (toFlush) {
98
				if (!fullFlush)
99
					if (project != null) {
100
						fullFlush = true;
101
						toFlush.clear();
102
					} else
103
						toFlush.add(project.getFullPath());
104
			}
105
			schedule(1000);
106
		}
107
80
	}
108
	}
81
109
82
	/** 
110
	/** 
Lines 144-150 Link Here
144
172
145
	private byte cacheState;
173
	private byte cacheState;
146
174
147
	private Job flushJob;
175
	private FlushJob flushJob;
176
	private ProjectContentTypes projectContentTypes;
148
177
149
	Workspace workspace;
178
	Workspace workspace;
150
179
Lines 152-167 Link Here
152
	 * @see IContentTypeManager.IContentTypeChangeListener#contentTypeChanged(ContentTypeChangeEvent)
181
	 * @see IContentTypeManager.IContentTypeChangeListener#contentTypeChanged(ContentTypeChangeEvent)
153
	 */
182
	 */
154
	public void contentTypeChanged(ContentTypeChangeEvent event) {
183
	public void contentTypeChanged(ContentTypeChangeEvent event) {
155
		invalidateCache(true);
184
		invalidateCache(true, null);
156
	}
185
	}
157
186
158
	synchronized void doFlushCache(final IProgressMonitor monitor) throws CoreException {
187
	synchronized void doFlushCache(final IProgressMonitor monitor, IPath[] toClean) throws CoreException {
159
		// nothing to be done if no information cached
188
		// nothing to be done if no information cached
160
		if (getCacheState() == EMPTY_CACHE)
189
		if (getCacheState() != INVALID_CACHE)
161
			return;
190
			return;
162
		setCacheState(FLUSHING_CACHE);
191
		try {
163
		// flush the MRU cache
192
			setCacheState(FLUSHING_CACHE);
164
		cache.discardAll();
193
			// flush the MRU cache
194
			cache.discardAll();
195
			if (toClean == null || toClean.length == 0)
196
				// no project was added, must be a global flush
197
				clearContentFlags(Path.ROOT, monitor);
198
			else {
199
				// flush a project at a time								
200
				MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.resources_errorFlushingContentDescriptionCache, null);
201
				for (int i = 0; i < toClean.length; i++)
202
					try {
203
						clearContentFlags(toClean[i], monitor);
204
					} catch (CoreException ce) {
205
						result.add(ce.getStatus());
206
					}
207
				if (!result.isOK())
208
					throw new CoreException(result);
209
			}
210
		} catch (CoreException ce) {
211
			setCacheState(INVALID_CACHE);
212
			throw ce;
213
		}
214
		// done cleaning (only if we didn't fail)
215
		setCacheState(EMPTY_CACHE);
216
	}
217
218
	/**
219
	 * Clears the content related flags for every file under the given root.
220
	 */
221
	private void clearContentFlags(IPath root, final IProgressMonitor monitor) throws CoreException {
165
		// discard content type related flags for all files in the tree 
222
		// discard content type related flags for all files in the tree 
166
		IElementContentVisitor visitor = new IElementContentVisitor() {
223
		IElementContentVisitor visitor = new IElementContentVisitor() {
167
			public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
224
			public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
Lines 180-191 Link Here
180
			}
237
			}
181
		};
238
		};
182
		try {
239
		try {
183
			new ElementTreeIterator(workspace.getElementTree(), Path.ROOT).iterate(visitor);
240
			new ElementTreeIterator(workspace.getElementTree(), root).iterate(visitor);
184
		} catch (WrappedRuntimeException e) {
241
		} catch (WrappedRuntimeException e) {
185
			throw (CoreException) e.getTargetException();
242
			throw (CoreException) e.getTargetException();
186
		}
243
		}
187
		// done cleaning
188
		setCacheState(EMPTY_CACHE);
189
	}
244
	}
190
245
191
	Cache getCache() {
246
	Cache getCache() {
Lines 288-294 Link Here
288
		return Platform.getPlatformAdmin().getState(false).getTimeStamp();
343
		return Platform.getPlatformAdmin().getState(false).getTimeStamp();
289
	}
344
	}
290
345
291
	public synchronized void invalidateCache(boolean flush) {
346
	/**
347
	 * Marks the cache as invalid. Does not do anything if the cache is new.
348
	 * Optionally causes the cached information to be actually flushed.
349
	 *  
350
	 * @param flush whether the cached information should be flushed 
351
	 * @see #doFlushCache(IProgressMonitor, IPath[])
352
	 */
353
	public synchronized void invalidateCache(boolean flush, IProject project) {
292
		if (getCacheState() == EMPTY_CACHE)
354
		if (getCacheState() == EMPTY_CACHE)
293
			// cache has not been touched, nothing to do			
355
			// cache has not been touched, nothing to do			
294
			return;
356
			return;
Lines 298-306 Link Here
298
		} catch (CoreException e) {
360
		} catch (CoreException e) {
299
			ResourcesPlugin.getPlugin().getLog().log(e.getStatus());
361
			ResourcesPlugin.getPlugin().getLog().log(e.getStatus());
300
		}
362
		}
301
		if (!flush)
363
		if (flush)
302
			return;
364
			flushJob.flush(project);
303
		flushJob.schedule(1000);
304
	}
365
	}
305
366
306
	/**
367
	/**
Lines 310-317 Link Here
310
		// tries to obtain a description for this file contents
371
		// tries to obtain a description for this file contents
311
		InputStream contents = new LazyFileInputStream(file.getLocation());
372
		InputStream contents = new LazyFileInputStream(file.getLocation());
312
		try {
373
		try {
313
			IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
374
			IContentTypeMatcher matcher = projectContentTypes.getMatcherFor((Project) file.getProject());
314
			return contentTypeManager.getDescriptionFor(contents, file.getName(), IContentDescription.ALL);
375
			return matcher.getDescriptionFor(contents, file.getName(), IContentDescription.ALL);
315
		} catch (IOException e) {
376
		} catch (IOException e) {
316
			String message = NLS.bind(Messages.resources_errorContentDescription, file.getFullPath());
377
			String message = NLS.bind(Messages.resources_errorContentDescription, file.getFullPath());
317
			throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, file.getFullPath(), message, e);
378
			throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, file.getFullPath(), message, e);
Lines 327-333 Link Here
327
		// no changes related to the content type registry
388
		// no changes related to the content type registry
328
		if (event.getExtensionDeltas(Platform.PI_RUNTIME, PT_CONTENTTYPES).length == 0)
389
		if (event.getExtensionDeltas(Platform.PI_RUNTIME, PT_CONTENTTYPES).length == 0)
329
			return;
390
			return;
330
		invalidateCache(true);
391
		invalidateCache(true, null);
392
	}
393
394
	/**
395
	 * @see ILifecycleListener#handleEvent(LifecycleEvent)
396
	 */
397
	public void handleEvent(LifecycleEvent event) {
398
		//TODO are these the only events we care about?
399
		switch (event.kind) {
400
			case LifecycleEvent.PRE_PROJECT_CHANGE :
401
			case LifecycleEvent.PRE_PROJECT_CLOSE :
402
			case LifecycleEvent.PRE_PROJECT_DELETE :
403
			case LifecycleEvent.PRE_PROJECT_MOVE :
404
			case LifecycleEvent.PRE_PROJECT_OPEN :
405
				invalidateCache(true, (IProject) event.resource);
406
		}
331
	}
407
	}
332
408
333
	synchronized void setCacheState(byte newCacheState) throws CoreException {
409
	synchronized void setCacheState(byte newCacheState) throws CoreException {
Lines 351-368 Link Here
351
		cache = null;
427
		cache = null;
352
		flushJob.cancel();
428
		flushJob.cancel();
353
		flushJob = null;
429
		flushJob = null;
430
		projectContentTypes = null;
354
	}
431
	}
355
432
356
	public void startup(IProgressMonitor monitor) throws CoreException {
433
	public void startup(IProgressMonitor monitor) throws CoreException {
357
		workspace = (Workspace) ResourcesPlugin.getWorkspace();
434
		workspace = (Workspace) ResourcesPlugin.getWorkspace();
358
		cache = new Cache(100, 1000, 0.1);
435
		cache = new Cache(100, 1000, 0.1);
436
		projectContentTypes = new ProjectContentTypes(workspace);
359
		getCacheState();
437
		getCacheState();
360
		if (cacheState == FLUSHING_CACHE)
438
		if (cacheState == FLUSHING_CACHE)
361
			// in case we died before completing the last flushing 
439
			// in case we died before completing the last flushing 
362
			setCacheState(INVALID_CACHE);
440
			setCacheState(INVALID_CACHE);
363
		flushJob = new FlushJob();
441
		flushJob = new FlushJob();
442
		// the cache is stale (plug-ins that might be contributing content types were added/removed)
364
		if (getCacheTimestamp() != getPlatformTimeStamp())
443
		if (getCacheTimestamp() != getPlatformTimeStamp())
365
			invalidateCache(false);
444
			invalidateCache(false, null);
445
		// register a lifecycle listener
446
		workspace.addLifecycleListener(this);
366
		// register a content type change listener
447
		// register a content type change listener
367
		Platform.getContentTypeManager().addContentTypeChangeListener(this);
448
		Platform.getContentTypeManager().addContentTypeChangeListener(this);
368
		// register a registry change listener		
449
		// register a registry change listener		
(-)src/org/eclipse/core/internal/resources/NatureManager.java (-8 / +10 lines)
Lines 322-331 Link Here
322
	 * Returns the cached array of enabled natures for this project,
322
	 * Returns the cached array of enabled natures for this project,
323
	 * or null if there is nothing in the cache.
323
	 * or null if there is nothing in the cache.
324
	 */
324
	 */
325
	protected String[] getEnabledNatures(IProject project) {
325
	protected String[] getEnabledNatures(Project project) {
326
		if (natureEnablements != null)
326
		String[] enabled;		
327
			return (String[]) natureEnablements.get(project);
327
		if (natureEnablements != null) {
328
		return null;
328
			enabled = (String[]) natureEnablements.get(project);
329
			if (enabled != null)
330
				return enabled;
331
		}
332
		enabled = computeNatureEnablements(project);
333
		setEnabledNatures(project, enabled);
334
		return enabled;
329
	}
335
	}
330
336
331
	/**
337
	/**
Lines 425-434 Link Here
425
	 */
431
	 */
426
	public boolean isNatureEnabled(Project project, String id) {
432
	public boolean isNatureEnabled(Project project, String id) {
427
		String[] enabled = getEnabledNatures(project);
433
		String[] enabled = getEnabledNatures(project);
428
		if (enabled == null) {
429
			enabled = computeNatureEnablements(project);
430
			setEnabledNatures(project, enabled);
431
		}
432
		for (int i = 0; i < enabled.length; i++) {
434
		for (int i = 0; i < enabled.length; i++) {
433
			if (enabled[i].equals(id))
435
			if (enabled[i].equals(id))
434
				return true;
436
				return true;
(-)src/org/eclipse/core/internal/resources/ProjectNatureDescriptor.java (+16 lines)
Lines 25-30 Link Here
25
	protected String[] requiredNatures;
25
	protected String[] requiredNatures;
26
	protected String[] natureSets;
26
	protected String[] natureSets;
27
	protected String[] builderIds;
27
	protected String[] builderIds;
28
	protected String[] contentTypeIds;
28
	protected boolean allowLinking = true;
29
	protected boolean allowLinking = true;
29
30
30
	//descriptors that are in a dependency cycle are never valid
31
	//descriptors that are in a dependency cycle are never valid
Lines 57-62 Link Here
57
	}
58
	}
58
59
59
	/**
60
	/**
61
	 * Returns the IDs of the content types this nature declares to
62
	 * have affinity with.  These content types do not necessarily exist in the registry.
63
	 */
64
	public String[] getContentTypeIds() {
65
		return contentTypeIds;
66
	}
67
68
	/**
60
	 * @see IProjectNatureDescriptor#getNatureId()
69
	 * @see IProjectNatureDescriptor#getNatureId()
61
	 */
70
	 */
62
	public String getNatureId() {
71
	public String getNatureId() {
Lines 106-111 Link Here
106
		ArrayList requiredList = new ArrayList(count);
115
		ArrayList requiredList = new ArrayList(count);
107
		ArrayList setList = new ArrayList(count);
116
		ArrayList setList = new ArrayList(count);
108
		ArrayList builderList = new ArrayList(count);
117
		ArrayList builderList = new ArrayList(count);
118
		ArrayList contentTypeList = new ArrayList(count);
109
		for (int i = 0; i < count; i++) {
119
		for (int i = 0; i < count; i++) {
110
			IConfigurationElement element = elements[i];
120
			IConfigurationElement element = elements[i];
111
			String name = element.getName();
121
			String name = element.getName();
Lines 124-129 Link Here
124
				if (attribute == null)
134
				if (attribute == null)
125
					fail();
135
					fail();
126
				builderList.add(attribute);
136
				builderList.add(attribute);
137
			} else if (name.equalsIgnoreCase("content-type")) { //$NON-NLS-1$
138
				String attribute = element.getAttribute("id"); //$NON-NLS-1$
139
				if (attribute == null)
140
					fail();
141
				contentTypeList.add(attribute);
127
			} else if (name.equalsIgnoreCase("options")) { //$NON-NLS-1$
142
			} else if (name.equalsIgnoreCase("options")) { //$NON-NLS-1$
128
				String attribute = element.getAttribute("allowLinking"); //$NON-NLS-1$
143
				String attribute = element.getAttribute("allowLinking"); //$NON-NLS-1$
129
				//when in doubt (missing attribute, wrong value) default to allow linking
144
				//when in doubt (missing attribute, wrong value) default to allow linking
Lines 133-138 Link Here
133
		requiredNatures = (String[]) requiredList.toArray(new String[requiredList.size()]);
148
		requiredNatures = (String[]) requiredList.toArray(new String[requiredList.size()]);
134
		natureSets = (String[]) setList.toArray(new String[setList.size()]);
149
		natureSets = (String[]) setList.toArray(new String[setList.size()]);
135
		builderIds = (String[]) builderList.toArray(new String[builderList.size()]);
150
		builderIds = (String[]) builderList.toArray(new String[builderList.size()]);
151
		contentTypeIds = (String[]) contentTypeList.toArray(new String[contentTypeList.size()]);
136
	}
152
	}
137
153
138
	/**
154
	/**
(-)src/org/eclipse/core/internal/utils/Messages.java (+1 lines)
Lines 156-161 Link Here
156
	public static String resources_destNotNull;
156
	public static String resources_destNotNull;
157
	public static String resources_errorContentDescription;
157
	public static String resources_errorContentDescription;
158
	public static String resources_errorDeleting;
158
	public static String resources_errorDeleting;
159
	public static String resources_errorFlushingContentDescriptionCache;
159
	public static String resources_errorMarkersDelete;
160
	public static String resources_errorMarkersDelete;
160
	public static String resources_errorMarkersMove;
161
	public static String resources_errorMarkersMove;
161
	public static String resources_errorMembers;
162
	public static String resources_errorMembers;
(-)src/org/eclipse/core/internal/utils/Queue.java (+17 lines)
Lines 61-66 Link Here
61
		}
61
		}
62
		tail = head = 0;
62
		tail = head = 0;
63
	}
63
	}
64
	
65
	/**
66
	 * Copies all elements to the given array. Returns the same array as 
67
	 * convenience.
68
	 */
69
	public Object[] copyTo(Object[] newElements) {
70
		if (head <= tail) {
71
			// trivial case
72
			System.arraycopy(elements, head, newElements, 0, newElements.length);
73
			return newElements;
74
		}
75
		// queue wrapped array - has to reassemble segments
76
		int end = (elements.length - head);
77
		System.arraycopy(elements, head, newElements, 0, end);
78
		System.arraycopy(elements, 0, newElements, end, tail);
79
		return newElements;
80
	}
64
81
65
	/**
82
	/**
66
	 * This method does not affect the queue itself. It is only a
83
	 * This method does not affect the queue itself. It is only a
(-)src/org/eclipse/core/internal/utils/messages.properties (+1 lines)
Lines 112-117 Link Here
112
resources_destNotNull = Destination path should not be null.
112
resources_destNotNull = Destination path should not be null.
113
resources_errorContentDescription = Error retrieving content description for resource: {0}.
113
resources_errorContentDescription = Error retrieving content description for resource: {0}.
114
resources_errorDeleting = Error deleting resource {0} from the workspace tree.
114
resources_errorDeleting = Error deleting resource {0} from the workspace tree.
115
resources_errorFlushingContentDescriptionCache = Errors occurred while flushing content description cache.
115
resources_errorMarkersDelete = Error deleting markers for resource: {0}.
116
resources_errorMarkersDelete = Error deleting markers for resource: {0}.
116
resources_errorMarkersMove = Error moving markers from resource: {0} to: {1}.
117
resources_errorMarkersMove = Error moving markers from resource: {0} to: {1}.
117
resources_errorMembers = Error retrieving members of container: {0}.
118
resources_errorMembers = Error retrieving members of container: {0}.
(-)src/org/eclipse/core/internal/resources/ProjectContentTypes.java (+141 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-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.utils.Cache;
15
import org.eclipse.core.resources.ResourcesPlugin;
16
import org.eclipse.core.runtime.*;
17
import org.eclipse.core.runtime.content.IContentType;
18
import org.eclipse.core.runtime.content.IContentTypeMatcher;
19
import org.eclipse.core.runtime.content.IContentTypeManager.ISelectionPolicy;
20
21
/**
22
 * Manages project-specific content type behavior.
23
 * 
24
 * @see ContentDescriptionManager
25
 * @see ISelectionPolicy
26
 * @since 3.1
27
 */
28
public class ProjectContentTypes {
29
30
	/**
31
	 * A project-aware content type selection policy.   
32
	 */
33
	private class ProjectContentTypeSelectionPolicy implements ISelectionPolicy {
34
35
		private Project project;
36
37
		public ProjectContentTypeSelectionPolicy(Project project) {
38
			this.project = project;
39
		}
40
41
		public IContentType[] select(IContentType[] candidates, boolean fileName, boolean content) {
42
			return ProjectContentTypes.this.select(project, candidates, fileName, content);
43
		}
44
45
	}
46
47
	private static final QualifiedName MATCHER_PROPERTY = new QualifiedName(ResourcesPlugin.PI_RESOURCES, "contentTypeMatcher"); //$NON-NLS-1$
48
	private Cache contentTypesPerProject;
49
	private Workspace workspace;
50
51
	private static void swap(Object[] array, int i1, int i2) {
52
		Object temp = array[i1];
53
		array[i1] = array[i2];
54
		array[i2] = temp;
55
	}
56
57
	public ProjectContentTypes(Workspace workspace) {
58
		this.workspace = workspace;
59
		// keep cache small
60
		this.contentTypesPerProject = new Cache(5, 30, 0.4);
61
	}
62
63
	private Set getAssociatedContentTypes(Project project) {
64
		final ResourceInfo info = project.getResourceInfo(false, false);
65
		final String projectName = project.getName();
66
		synchronized (contentTypesPerProject) {
67
			Cache.Entry entry = contentTypesPerProject.getEntry(projectName);
68
			if (entry != null)
69
				// we have an entry...
70
				if (info != null && entry.getTimestamp() == info.getContentId())
71
					// ...and it is not stale, so just return it
72
					return (Set) entry.getCached();
73
			// no cached information found, have to collect associated content types  
74
			Set result = collectAssociatedContentTypes(project);
75
			if (entry == null)
76
				// there was no entry before - create one
77
				entry = contentTypesPerProject.addEntry(projectName, result, info.getContentId());
78
			else {
79
				// just update the existing entry
80
				entry.setTimestamp(info.getContentId());
81
				entry.setCached(result);
82
			}
83
			return result;
84
		}
85
	}
86
87
	IContentTypeMatcher getMatcherFor(Project project) throws CoreException {
88
		//TODO is ProjectInfo a better place to keep the content type matcher?
89
		IContentTypeMatcher matcher = (IContentTypeMatcher) project.getSessionProperty(MATCHER_PROPERTY);
90
		if (matcher != null)
91
			return matcher;
92
		matcher = Platform.getContentTypeManager().getMatcher(new ProjectContentTypeSelectionPolicy(project), null);
93
		project.setSessionProperty(MATCHER_PROPERTY, matcher);
94
		return matcher;
95
	}
96
97
	/**
98
	 * Collect content types associated to the natures configured for the given project.
99
	 */
100
	private Set collectAssociatedContentTypes(Project project) {
101
		String[] enabledNatures = workspace.getNatureManager().getEnabledNatures(project);
102
		if (enabledNatures.length == 0)
103
			return Collections.EMPTY_SET;
104
		Set related = new HashSet(enabledNatures.length);
105
		for (int i = 0; i < enabledNatures.length; i++) {
106
			ProjectNatureDescriptor descriptor = (ProjectNatureDescriptor) workspace.getNatureDescriptor(enabledNatures[i]);
107
			if (descriptor == null)
108
				// no descriptor found for the nature, skip it
109
				continue;
110
			String[] natureContentTypes = descriptor.getContentTypeIds();
111
			for (int j = 0; j < natureContentTypes.length; j++)
112
				// collect associate content types
113
				related.add(natureContentTypes[j]);
114
		}
115
		return related;
116
	}
117
118
	/**
119
	 * Implements project specific, nature-based selection policy. No content types are vetoed.
120
	 * 
121
	 *  @see ISelectionPolicy
122
	 */
123
	final IContentType[] select(Project project, IContentType[] candidates, boolean fileName, boolean content) {
124
		// since no vetoing is done here, don't go further if there is nothing to sort
125
		if (candidates.length < 2)
126
			return candidates;
127
		final Set associated = getAssociatedContentTypes(project);
128
		if (associated == null || associated.isEmpty())
129
			// project has no content types associated
130
			return candidates;
131
		// put content types that appear in related natures before those who don't
132
		int relatedCount = 0;
133
		for (int i = 0; i < candidates.length; i++)
134
			if (associated.contains(candidates[i].getId())) {
135
				if (relatedCount < i)
136
					swap(candidates, i, relatedCount);
137
				relatedCount++;
138
			}
139
		return candidates;
140
	}
141
}

Return to bug 69640