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
|
* <navigatorContent/> 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
|
}
|