Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
1
/*******************************************************************************
2
 * Copyright (c) 2003, 2010 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.ui.internal.navigator.extensions;
12

    
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.ListIterator;
18
import java.util.Set;
19
import java.util.TreeSet;
20

    
21
import org.eclipse.osgi.util.NLS;
22

    
23
import org.eclipse.core.expressions.ElementHandler;
24
import org.eclipse.core.expressions.EvaluationResult;
25
import org.eclipse.core.expressions.Expression;
26
import org.eclipse.core.expressions.ExpressionConverter;
27
import org.eclipse.core.expressions.IEvaluationContext;
28

    
29
import org.eclipse.core.runtime.CoreException;
30
import org.eclipse.core.runtime.IConfigurationElement;
31
import org.eclipse.core.runtime.IStatus;
32

    
33
import org.eclipse.jface.viewers.ILabelProvider;
34
import org.eclipse.jface.viewers.IStructuredSelection;
35
import org.eclipse.jface.viewers.ITreeContentProvider;
36

    
37
import org.eclipse.ui.IPluginContribution;
38
import org.eclipse.ui.WorkbenchException;
39
import org.eclipse.ui.internal.navigator.CommonNavigatorMessages;
40
import org.eclipse.ui.internal.navigator.CustomAndExpression;
41
import org.eclipse.ui.internal.navigator.NavigatorPlugin;
42
import org.eclipse.ui.internal.navigator.Policy;
43
import org.eclipse.ui.navigator.ICommonContentProvider;
44
import org.eclipse.ui.navigator.ICommonLabelProvider;
45
import org.eclipse.ui.navigator.INavigatorContentDescriptor;
46
import org.eclipse.ui.navigator.OverridePolicy;
47
import org.eclipse.ui.navigator.Priority;
48

    
49
/**
50
 * Encapsulates the <code>org.eclipse.ui.navigator.navigatorContent</code>
51
 * extension point.
52
 * 
53
 * @since 3.2
54
 */
55
public final class NavigatorContentDescriptor implements
56
		INavigatorContentDescriptor, INavigatorContentExtPtConstants {
57

    
58
	private static final int HASH_CODE_NOT_COMPUTED = -1;
59
	private String id;
60

    
61
	private String name;
62

    
63
	private IConfigurationElement configElement;
64

    
65
	private int priority = Priority.NORMAL_PRIORITY_VALUE;
66

    
67
	/**
68
	 * This is calculated based on the priority and appearsBeforeId when all of the descriptors
69
	 * are first loaded. This is what's used to sort on after that.
70
	 */
71
	private int sequenceNumber;
72
	
73
	private String appearsBeforeId;
74

    
75
	private Expression enablement;
76

    
77
	private Expression possibleChildren;
78

    
79
	private Expression initialActivation;
80
	
81
	private String icon;
82

    
83
	private boolean activeByDefault;
84

    
85
	private IPluginContribution contribution;
86

    
87
	private boolean sortOnly;
88
	
89
	private Set overridingExtensions;
90
	private List overridingExtensionsList; // FIXME: will replace 'overridingExtensions' in 3.6
91

    
92
	private OverridePolicy overridePolicy;
93

    
94
	private String suppressedExtensionId;
95

    
96
	private INavigatorContentDescriptor overriddenDescriptor;
97

    
98
	private int hashCode = HASH_CODE_NOT_COMPUTED;
99

    
100
	private boolean providesSaveables;
101

    
102
	/**
103
	 * Creates a new content descriptor from a configuration element.
104
	 * 
105
	 * @param configElement
106
	 *            configuration element to create a descriptor from
107
	 * 
108
	 * @throws WorkbenchException
109
	 *             if the configuration element could not be parsed. Reasons
110
	 *             include:
111
	 *             <ul>
112
	 *             <li>A required attribute is missing.</li>
113
	 *             <li>More elements are define than is allowed.</li>
114
	 *             </ul>
115
	 */
116
	/* package */ NavigatorContentDescriptor(IConfigurationElement configElement)
117
			throws WorkbenchException {
118
		super();
119
		this.configElement = configElement;
120
		init();
121
	}
122

    
123
	/*
124
	 * (non-Javadoc)
125
	 * 
126
	 * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentDescriptor#getId()
127
	 */
128
	public String getId() {
129
		return id;
130
	}
131

    
132
	/*
133
	 * (non-Javadoc)
134
	 * 
135
	 * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentDescriptor#getName()
136
	 */
137
	public String getName() {
138
		return name;
139
	}
140

    
141
	/*
142
	 * (non-Javadoc)
143
	 * 
144
	 * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentDescriptor#getPriority()
145
	 */
146
	public int getPriority() {
147
		return priority;
148
	}
149

    
150
	/**
151
	 * @return the sequence number
152
	 */
153
	public int getSequenceNumber() {
154
		return sequenceNumber;
155
	}
156

    
157
	void setSequenceNumber(int num) {
158
		sequenceNumber = num;
159
	}
160
	
161
	/**
162
	 * 
163
	 * @return The value specified by the <i>appearsBefore</i> attribute of the
164
	 *         &lt;navigatorContent/&gt; element.
165
	 */
166
	public String getAppearsBeforeId() {
167
		return appearsBeforeId;
168
	}
169

    
170
	public boolean isSortOnly() {
171
		return sortOnly;
172
	}
173
	
174
	/**
175
	 * Parses the configuration element.
176
	 * 
177
	 * @throws WorkbenchException
178
	 *             if the configuration element could not be parsed. Reasons
179
	 *             include:
180
	 *             <ul>
181
	 *             <li>A required attribute is missing.</li>
182
	 *             <li>More elements are define than is allowed.</li>
183
	 *             </ul>
184
	 */
185
	private void init() throws WorkbenchException {
186
		id = configElement.getAttribute(ATT_ID);
187
		name = configElement.getAttribute(ATT_NAME);
188
		String priorityString = configElement.getAttribute(ATT_PRIORITY);
189
		icon = configElement.getAttribute(ATT_ICON);
190

    
191
		String activeByDefaultString = configElement
192
				.getAttribute(ATT_ACTIVE_BY_DEFAULT);
193
		activeByDefault = (activeByDefaultString != null && activeByDefaultString
194
				.length() > 0) ? Boolean.valueOf(activeByDefaultString)
195
				.booleanValue() : true;
196

    
197
		String providesSaveablesString = configElement
198
			.getAttribute(ATT_PROVIDES_SAVEABLES);
199
		providesSaveables = (providesSaveablesString != null && providesSaveablesString
200
				.length() > 0) ? Boolean.valueOf(providesSaveablesString)
201
						.booleanValue() : false;
202
		appearsBeforeId = configElement.getAttribute(ATT_APPEARS_BEFORE);
203

    
204
		if (priorityString != null) {
205
			try {
206
				Priority p = Priority.get(priorityString);
207
				priority = p != null ? p.getValue()
208
						: Priority.NORMAL_PRIORITY_VALUE;
209
			} catch (NumberFormatException exception) {
210
				priority = Priority.NORMAL_PRIORITY_VALUE;
211
			}
212
		}
213
		
214
		// We start with this because the sort ExtensionPriorityComparator works 
215
		// from the sequenceNumber
216
		sequenceNumber = priority;
217

    
218
		String sortOnlyString = configElement.getAttribute(ATT_SORT_ONLY);
219
		sortOnly = (sortOnlyString != null && sortOnlyString.length() > 0) ? Boolean.valueOf(
220
				sortOnlyString).booleanValue() : false;
221
		
222
		if (id == null) {
223
			throw new WorkbenchException(NLS.bind(
224
					CommonNavigatorMessages.Attribute_Missing_Warning,
225
					new Object[] {
226
							ATT_ID,
227
							id,
228
							configElement.getDeclaringExtension()
229
									.getNamespaceIdentifier() }));
230
		}
231

    
232
		contribution = new IPluginContribution() {
233

    
234
			public String getLocalId() {
235
				return getId();
236
			}
237

    
238
			public String getPluginId() {
239
				return configElement.getDeclaringExtension().getNamespaceIdentifier();
240
			}
241

    
242
		};
243

    
244
		IConfigurationElement[] children;
245
		
246
		children = configElement.getChildren(TAG_INITIAL_ACTIVATION);
247
		if (children.length > 0) {
248
			if (children.length == 1) {
249
				initialActivation = new CustomAndExpression(children[0]);
250
			} else {
251
				throw new WorkbenchException(NLS.bind(
252
						CommonNavigatorMessages.Attribute_Missing_Warning, new Object[] {
253
								TAG_INITIAL_ACTIVATION, id,
254
								configElement.getDeclaringExtension().getNamespaceIdentifier() }));
255
			}
256
		}
257

    
258
		if (sortOnly)
259
			return;
260

    
261
		children = configElement.getChildren(TAG_ENABLEMENT);
262
		if (children.length == 0) {
263

    
264
			children = configElement.getChildren(TAG_TRIGGER_POINTS);
265
			if (children.length == 1) {
266
				enablement = new CustomAndExpression(children[0]);
267
			} else {
268
				throw new WorkbenchException(NLS.bind(
269
						CommonNavigatorMessages.Attribute_Missing_Warning,
270
						new Object[] {
271
								TAG_TRIGGER_POINTS,
272
								id,
273
								configElement.getDeclaringExtension()
274
										.getNamespaceIdentifier() }));
275
			}
276

    
277
			children = configElement.getChildren(TAG_POSSIBLE_CHILDREN);
278
			if (children.length == 1) {
279
				possibleChildren = new CustomAndExpression(children[0]);
280
			} else if(children.length > 1){
281
				throw new WorkbenchException(NLS.bind(
282
						CommonNavigatorMessages.Attribute_Missing_Warning,
283
						new Object[] {
284
								TAG_POSSIBLE_CHILDREN,
285
								id,
286
								configElement.getDeclaringExtension()
287
										.getNamespaceIdentifier() }));
288
			}
289
		} else if (children.length == 1) {
290
			try {
291
				enablement = ElementHandler.getDefault().create(
292
						ExpressionConverter.getDefault(), children[0]);
293
			} catch (CoreException e) {
294
				NavigatorPlugin.log(IStatus.ERROR, 0, e.getMessage(), e);
295
			}
296
		} else if (children.length > 1) {
297
			throw new WorkbenchException(NLS.bind(
298
					CommonNavigatorMessages.Attribute_Missing_Warning,
299
					new Object[] {
300
							TAG_ENABLEMENT,
301
							id,
302
							configElement.getDeclaringExtension()
303
									.getNamespaceIdentifier() }));
304
		}
305

    
306
		children = configElement.getChildren(TAG_OVERRIDE);
307
		if (children.length == 0) {
308
			overridePolicy = OverridePolicy.get(OverridePolicy.InvokeAlwaysRegardlessOfSuppressedExt_LITERAL);
309
		} else if (children.length == 1) {
310
			suppressedExtensionId = children[0]
311
					.getAttribute(ATT_SUPPRESSED_EXT_ID);
312
			overridePolicy = OverridePolicy.get(children[0]
313
					.getAttribute(ATT_POLICY));
314
		} else if (children.length > 1) {
315
			throw new WorkbenchException(NLS.bind(
316
					CommonNavigatorMessages.Too_many_elements_Warning,
317
					new Object[] {
318
							TAG_OVERRIDE,
319
							id,configElement.getDeclaringExtension()
320
							.getNamespaceIdentifier() }));
321
		}
322

    
323
	}
324

    
325
	/**
326
	 * @return Returns the icon.
327
	 */
328
	public String getIcon() {
329
		return icon;
330
	}
331

    
332
	/**
333
	 * @return Returns the suppressedExtensionId or null if none specified.
334
	 */
335
	public String getSuppressedExtensionId() {
336
		return suppressedExtensionId;
337
	}
338

    
339
	/**
340
	 * @return Returns the overridePolicy or null if this extension does not
341
	 *         override another extension.
342
	 */
343
	public OverridePolicy getOverridePolicy() {
344
		return overridePolicy;
345
	}
346

    
347
	/**
348
	 * @return Returns the contribution.
349
	 */
350
	public IPluginContribution getContribution() {
351
		return contribution;
352
	}
353

    
354
	/**
355
	 * @return the configuration element
356
	 */
357
	public IConfigurationElement getConfigElement() {
358
		return configElement;
359
	}
360
	
361
	/**
362
	 * The content provider could be an instance of
363
	 * {@link ICommonContentProvider}, but only {@link ITreeContentProvider} is
364
	 * required.
365
	 * 
366
	 * 
367
	 * @return An instance of the Content provider defined for this extension.
368
	 * @throws CoreException
369
	 *             if an instance of the executable extension could not be
370
	 *             created for any reason
371
	 * 
372
	 */
373
	public ITreeContentProvider createContentProvider() throws CoreException {
374
		if (Policy.DEBUG_EXTENSION_SETUP)
375
			System.out.println("createContentProvider: " + this); //$NON-NLS-1$
376
		return (ITreeContentProvider) configElement
377
				.createExecutableExtension(ATT_CONTENT_PROVIDER);
378
	}
379

    
380
	/**
381
	 * 
382
	 * The content provider could be an instance of {@link ICommonLabelProvider},
383
	 * but only {@link ILabelProvider} is required.
384
	 * 
385
	 * @return An instance of the Label provider defined for this extension
386
	 * @throws CoreException
387
	 *             if an instance of the executable extension could not be
388
	 *             created for any reason
389
	 */
390
	public ILabelProvider createLabelProvider() throws CoreException {
391
		if (Policy.DEBUG_EXTENSION_SETUP)
392
			System.out.println("createLabelProvider: " + this); //$NON-NLS-1$
393
		return (ILabelProvider) configElement
394
				.createExecutableExtension(ATT_LABEL_PROVIDER);
395
	}
396

    
397
	/*
398
	 * (non-Javadoc)
399
	 * 
400
	 * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentDescriptor#isActiveByDefault()
401
	 */
402
	public boolean isActiveByDefault() {
403
		if (activeByDefault)
404
			return true;
405
		if (initialActivation == null)
406
			return false;
407
		IEvaluationContext context = NavigatorPlugin.getEvalContext(new Object());
408
		return NavigatorPlugin.safeEvaluate(initialActivation, context) == EvaluationResult.TRUE;
409
	}
410

    
411
	/**
412
	 * Determine if this content extension would be able to provide children for
413
	 * the given element.
414
	 * 
415
	 * @param anElement
416
	 *            The element that should be used for the evaluation.
417
	 * @return True if and only if the extension is enabled for the element.
418
	 */
419
	public boolean isTriggerPoint(Object anElement) {
420

    
421
		if (enablement == null || anElement == null) {
422
			return false;
423
		}
424

    
425
		IEvaluationContext context = NavigatorPlugin.getEvalContext(anElement);
426
		return NavigatorPlugin.safeEvaluate(enablement, context) == EvaluationResult.TRUE;
427
	}
428

    
429
	/**
430
	 * Determine if this content extension could provide the given element as a
431
	 * child.
432
	 * 
433
	 * <p>
434
	 * This method is used to determine what the parent of an element could be
435
	 * for Link with Editor support.
436
	 * </p>
437
	 * 
438
	 * @param anElement
439
	 *            The element that should be used for the evaluation.
440
	 * @return True if and only if the extension might provide an object of this
441
	 *         type as a child.
442
	 */
443
	public boolean isPossibleChild(Object anElement) {
444

    
445
		if ((enablement == null && possibleChildren == null)
446
				|| anElement == null) {
447
			return false;
448
		} else if(anElement instanceof IStructuredSelection) {
449
			return arePossibleChildren((IStructuredSelection) anElement);
450
		}
451

    
452
		IEvaluationContext context = NavigatorPlugin.getEvalContext(anElement);
453
		if (possibleChildren != null) {
454
			return NavigatorPlugin.safeEvaluate(possibleChildren, context) == EvaluationResult.TRUE;
455
		} else if (enablement != null) {
456
			return NavigatorPlugin.safeEvaluate(enablement, context) == EvaluationResult.TRUE;
457
		}
458
		return false;
459
	}
460
	
461
	/**
462
	 * A convenience method to check all elements in a selection.
463
	 * 
464
	 * @param aSelection A non-null selection
465
	 * @return True if and only if every element in the selection is a possible child.
466
	 */
467
	public boolean arePossibleChildren(IStructuredSelection aSelection) {
468
		if(aSelection.isEmpty()) {
469
			return false;
470
		}
471
		for (Iterator iter = aSelection.iterator(); iter.hasNext();) {
472
			Object element = iter.next();
473
			if(!isPossibleChild(element)) {
474
				return false;
475
			}
476
		}
477
		return true;
478
	}
479

    
480
	/**
481
	 * 
482
	 * Does not force the creation of the set of overriding extensions.
483
	 * 
484
	 * @return True if this extension has overriding extensions.
485
	 */
486
	public boolean hasOverridingExtensions() {
487
		return overridingExtensions != null && overridingExtensions.size() > 0;
488
	}
489

    
490
	/**
491
	 * @return The set of overriding extensions (of type
492
	 *         {@link INavigatorContentDescriptor}
493
	 */
494
	public Set getOverriddingExtensions() {
495
		if (overridingExtensions == null) {
496
			overridingExtensions = new TreeSet(ExtensionSequenceNumberComparator.DESCENDING);
497
		}
498
		return overridingExtensions;
499
	}
500

    
501
	/**
502
	 *  Returns a list iterator over the overriding extensions.
503
	 * 
504
	 * @param fromStart
505
	 *            <code>true</code> if list iterator starts at the beginning and
506
	 *            <code>false</code> if it starts at the end of the list
507
	 * @return a list iterator over the overriding extensions which are ordered
508
	 *         by ExtensionPriorityComparator.DESCENDING
509
	 */
510
	public ListIterator getOverridingExtensionsListIterator(boolean fromStart) {
511
		if (overridingExtensions == null)
512
			return Collections.EMPTY_LIST.listIterator();
513

    
514
		if (overridingExtensionsList == null)
515
			overridingExtensionsList = new ArrayList(overridingExtensions);
516

    
517
		return overridingExtensionsList.listIterator(fromStart ? 0 : overridingExtensionsList.size());
518
	}
519

    
520
	/*
521
	 * (non-Javadoc)
522
	 * 
523
	 * @see java.lang.Object#toString()
524
	 */
525
	public String toString() {
526
		return "Content[" + id  + "(" + sequenceNumber + ") " + ", \"" + name + "\"]"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
527
	}
528
	
529
	/* (non-Javadoc)
530
	 * @see java.lang.Object#hashCode()
531
	 */
532
	public int hashCode() {
533
		if (hashCode == HASH_CODE_NOT_COMPUTED) {
534
			String hashCodeString = configElement.getNamespaceIdentifier() + getId();
535
			hashCode = hashCodeString.hashCode();
536
			if (hashCode == HASH_CODE_NOT_COMPUTED)
537
				hashCode++;
538
		}
539
		return hashCode;
540
	}
541

    
542
	/**
543
	 * @return The descriptor of the <code>suppressedExtensionId</code> if
544
	 *         non-null.
545
	 */
546
	public INavigatorContentDescriptor getOverriddenDescriptor() {
547
		return overriddenDescriptor;
548
	}
549

    
550
	/**
551
	 * @param theOverriddenDescriptor
552
	 *            The overriddenDescriptor to set.
553
	 */
554
	/* package */void setOverriddenDescriptor(
555
			INavigatorContentDescriptor theOverriddenDescriptor) {
556
		overriddenDescriptor = theOverriddenDescriptor;
557
	}
558

    
559
	/* (non-Javadoc)
560
	 * @see org.eclipse.ui.navigator.INavigatorContentDescriptor#hasSaveablesProvider()
561
	 */
562
	public boolean hasSaveablesProvider() {
563
		return providesSaveables;
564
	}
565

    
566
}
(15-15/29)