Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 9c621a2da6f787ae7df2be3ffc85f7c5bb021881 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
/*******************************************************************************
 * Copyright (c) 2012 Wind River Systems, Inc. and others. All rights reserved.
 * This program and the accompanying materials are made available under the terms
 * of the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tcf.te.launch.core.lm.delegates;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.tcf.te.launch.core.activator.CoreBundleActivator;
import org.eclipse.tcf.te.launch.core.bindings.LaunchConfigTypeBindingsManager;
import org.eclipse.tcf.te.launch.core.exceptions.LaunchServiceException;
import org.eclipse.tcf.te.launch.core.interfaces.tracing.ITraceIds;
import org.eclipse.tcf.te.launch.core.lm.LaunchConfigSorter;
import org.eclipse.tcf.te.launch.core.lm.LaunchSpecification;
import org.eclipse.tcf.te.launch.core.lm.interfaces.ICommonLaunchAttributes;
import org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchAttribute;
import org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate;
import org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchSpecification;
import org.eclipse.tcf.te.launch.core.nls.Messages;
import org.eclipse.tcf.te.launch.core.preferences.IPreferenceKeys;
import org.eclipse.tcf.te.launch.core.selection.interfaces.ILaunchSelection;
import org.eclipse.tcf.te.launch.core.selection.interfaces.ISelectionContext;
import org.eclipse.tcf.te.runtime.extensions.ExecutableExtension;

/**
 * Default launch manager delegate implementation.
 */
public class DefaultLaunchManagerDelegate extends ExecutableExtension implements ILaunchManagerDelegate {
	protected final static int NO_MATCH = 0;
	protected final static int PARTIAL_MATCH = 1;
	protected final static int FULL_MATCH = 2;

	protected String errorMessage = null;

	/**
	 * Constructor.
	 */
	public DefaultLaunchManagerDelegate() {
		super();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#initLaunchConfigAttributes(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy, org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchSpecification)
	 */
	@Override
	public void initLaunchConfigAttributes(ILaunchConfigurationWorkingCopy wc, ILaunchSpecification launchSpec) {
		Assert.isNotNull(wc);
		Assert.isNotNull(launchSpec);
		validateLaunchSpecification(launchSpec);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#updateLaunchConfigAttributes(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy, org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchSpecification)
	 */
	@Override
	public void updateLaunchConfigAttributes(ILaunchConfigurationWorkingCopy wc, ILaunchSpecification launchSpec) {
		Assert.isNotNull(wc);
		Assert.isNotNull(launchSpec);
		validateLaunchSpecification(launchSpec);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#isDefaultAttribute(java.lang.String, java.lang.Object, org.eclipse.debug.core.ILaunchConfiguration, java.lang.String)
	 */
	@Override
	public boolean isDefaultAttribute(String attributeKey, Object attributeValue, ILaunchConfiguration launchConfig, String launchMode) {
		Assert.isNotNull(attributeKey);
		Assert.isNotNull(launchConfig);
		Assert.isNotNull(launchMode);
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#getMatchingLaunchConfigurations(org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchSpecification, org.eclipse.debug.core.ILaunchConfiguration[])
	 */
	@Override
	public final ILaunchConfiguration[] getMatchingLaunchConfigurations(ILaunchSpecification launchSpec, ILaunchConfiguration[] launchConfigs) throws LaunchServiceException {
		if (launchConfigs == null || launchConfigs.length == 0) {
			return new ILaunchConfiguration[0];
		}
		else if (launchSpec != null) {
			List<LaunchConfigSorter> rankedList = new ArrayList<LaunchConfigSorter>();
			for (ILaunchConfiguration launchConfig : launchConfigs) {
				if (CoreBundleActivator.getTraceHandler()
								.isSlotEnabled(0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING)) {
					System.out.println("\n***\n"); //$NON-NLS-1$
				}

				int ranking = 0;
				try {
					ranking = getLaunchConfigRanking(launchSpec, launchConfig);
				}
				catch (LaunchServiceException e) {
					switch (e.getType()) {
					case LaunchServiceException.TYPE_MISSING_LAUNCH_CONFIG_ATTR:
						ranking = 0;
						break;
					default:
						throw e;
					}
				}

				int fullMatchRanking = getFullMatchRanking();

				if (CoreBundleActivator.getTraceHandler()
								.isSlotEnabled(0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING)) {
					StringBuilder message = new StringBuilder("Ranking launch spec ("); //$NON-NLS-1$
					message.append(launchSpec.getLaunchConfigName());
					message.append(") vs launch configuration ("); //$NON-NLS-1$
					message.append(launchConfig.getName());
					message.append(") = "); //$NON-NLS-1$
					message.append(ranking);
					message.append(" ; full match ranking = "); //$NON-NLS-1$
					message.append(fullMatchRanking);

					CoreBundleActivator
					.getTraceHandler()
					.trace(message.toString(), 0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING, IStatus.INFO, this);
				}

				if (ranking >= fullMatchRanking) {
					rankedList.add(new LaunchConfigSorter(launchConfig, ranking));
				}
			}

			// sort results and write back into array
			Collections.sort(rankedList);
			ILaunchConfiguration[] matchingConfigs = new ILaunchConfiguration[rankedList.size()];
			for (int i = 0; i < rankedList.size(); i++) {
				matchingConfigs[i] = rankedList.get(i).getConfig();
			}

			return matchingConfigs;
		}
		else {
			return launchConfigs;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#getLaunchSpecification(java.lang.String, org.eclipse.tcf.te.launch.core.selection.interfaces.ILaunchSelection)
	 */
	@Override
	public final ILaunchSpecification getLaunchSpecification(String launchConfigTypeId, ILaunchSelection launchSelection) {
		ILaunchSpecification spec = null;

		if (isValidLaunchSelection(launchSelection)) {
			spec = new LaunchSpecification(launchConfigTypeId, launchSelection.getLaunchMode());

			for (ISelectionContext selectionContext : launchSelection.getSelectedContexts()) {
				// For launch specifications, all selection contexts needs to be set as preferred
				// for full validation.
				// otherwise "not preferred" contexts are valid even if they are not for a give
				// launch configuration type id.
				selectionContext.setIsPreferredContext(true);
				if (LaunchConfigTypeBindingsManager
								.getInstance()
								.isValidLaunchConfigType(launchConfigTypeId, launchSelection.getLaunchMode(), selectionContext)) {
					spec = addLaunchSpecAttributes(spec, launchConfigTypeId, selectionContext);
				}
			}

			// If the number of selected contexts is 0, we have to call addLaunchSpecAttributes
			// one time to add the selection independent attributes.
			if (launchSelection.getSelectedContexts().length == 0) {
				spec = addLaunchSpecAttributes(spec, launchConfigTypeId, null);
			}
		}

		return spec;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#getDefaultLaunchName(org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchSpecification)
	 */
	@Override
	public String getDefaultLaunchName(ILaunchSpecification launchSpec) {
		return Messages.DefaultLaunchManagerDelegate_defaultLaunchName;
	}

	/**
	 * Add all needed selections to the launch specification.
	 * <p>
	 * Subclasses needs to override and super-call this method!
	 * <p>
	 * <b>Note:</b> The selection context can be <code>null</code>. In this case, the method
	 * implementation is expected to add only selection independent attributes!
	 *
	 * @param launchSpec The launch specification to add selections to. Must not be
	 *            <code>null</code>.
	 * @param launchConfigTypeId The launch configuration type id. Must not be <code>null</code>.
	 * @param selectionContext The validated selection context with the selection(s) or
	 *            <code>null</code>
	 *
	 * @return The launch specification with attributes from the selection context.
	 */
	protected ILaunchSpecification addLaunchSpecAttributes(ILaunchSpecification launchSpec, String launchConfigTypeId, ISelectionContext selectionContext) {
		Assert.isNotNull(launchSpec);
		Assert.isNotNull(launchConfigTypeId);
		return launchSpec;
	}

	/**
	 * Returns the ranking of the launch configuration compared to the launch specification.
	 * <p>
	 * If all attributes of the launch specification matches with the corresponding attributes of
	 * the launch configuration, the ranking should be at least as high as the number of attributes
	 * in the launch specification. The ranking should grow with every additional attribute value
	 * that is set to default.
	 * <p>
	 * If only a set of attributes matches, the ranking should be less than the number of attributes
	 * in the launch specification.
	 * <p>
	 * If no attribute matches zero should be returned.
	 *
	 * @param launchSpec The launch specification the launch configuration should be compared to.
	 * @param launchConfig The launch configuration to find a ranking for.
	 * @return The ranking of the launch configuration.
	 * @throws <code>LaunchServiceException</code> exception when mandatory attributes are missing
	 */
	private int getLaunchConfigRanking(ILaunchSpecification launchSpec, ILaunchConfiguration launchConfig) throws LaunchServiceException {
		int ranking = 0;
		int mandatorys = 0;
		Map<?, ?> configAttributes = null;
		Set<?> configKeys = null;
		try {
			configAttributes = launchConfig.getAttributes();
			configKeys = configAttributes.keySet();
		}
		catch (Exception e) {
			return 0;
		}

		// Read the launch configuration matching mode from the preferences
		int mode = CoreBundleActivator.getScopedPreferences()
						.getInt(IPreferenceKeys.PREF_LAUNCH_CONFIG_FIND_CREATE_MODE);
		if (launchSpec.getAttribute(IPreferenceKeys.PREF_LAUNCH_CONFIG_FIND_CREATE_MODE, null) instanceof Integer) {
			mode = ((Integer) launchSpec
							.getAttribute(IPreferenceKeys.PREF_LAUNCH_CONFIG_FIND_CREATE_MODE, null))
							.intValue();
		}

		if (CoreBundleActivator.getTraceHandler()
						.isSlotEnabled(0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING)) {
			StringBuilder message = new StringBuilder("Ranking launch spec ("); //$NON-NLS-1$
			message.append(launchSpec.getLaunchConfigName());
			message.append(") vs launch configuration ("); //$NON-NLS-1$
			message.append(launchConfig.getName());
			message.append("): Matching mode = "); //$NON-NLS-1$
			if (mode == IPreferenceKeys.MODE_ALWAYS_NEW) {
				message.append(" ALWAYS_NEW "); //$NON-NLS-1$
			}
			else if (mode == IPreferenceKeys.MODE_FIRST_MATCHING) {
				message.append(" FIRST_MATCHING "); //$NON-NLS-1$
			}
			else if (mode == IPreferenceKeys.MODE_FULL_MATCH_TARGET) {
				message.append(" FULL_MATCH_TARGET "); //$NON-NLS-1$
			}
			else if (mode == IPreferenceKeys.MODE_FULL_MATCH_LAUNCH_CONFIG) {
				message.append(" FULL_MATCH_LAUNCH_CONFIG "); //$NON-NLS-1$
			}

			CoreBundleActivator
			.getTraceHandler()
			.trace(message.toString(), 0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING, IStatus.INFO, this);
		}

		for (ILaunchAttribute specAttribute : launchSpec.getAllAttributes()) {
			if (!specAttribute.isCreateOnlyAttribute()) {
				String key = specAttribute.getKey();
				Object specValue = specAttribute.getValue();
				Object configValue = configAttributes.get(key);
				int match = compareAttributeValues(key, specValue, configValue, launchSpec, launchConfig);

				if (CoreBundleActivator.getTraceHandler()
								.isSlotEnabled(0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING)) {
					StringBuilder message = new StringBuilder("Launch spec attribute '"); //$NON-NLS-1$
					message.append(specAttribute.getKey());
					message.append("': mandatory = "); //$NON-NLS-1$
					message.append(isMandatoryAttribute(key));
					message.append("; match = "); //$NON-NLS-1$
					if (match == NO_MATCH) {
						message.append("NO_MATCH"); //$NON-NLS-1$
					}
					else if (match == PARTIAL_MATCH) {
						message.append("PARTIAL_MATCH"); //$NON-NLS-1$
					}
					else if (match == FULL_MATCH) {
						message.append("FULL_MATCH"); //$NON-NLS-1$
					}
					if (match != FULL_MATCH) {
						message.append("\n\t\tspecValue = "); //$NON-NLS-1$
						message.append(specValue != null ? specValue.toString() : "null"); //$NON-NLS-1$
						message.append("\n\t\tconfigValue = "); //$NON-NLS-1$
						message.append(configValue != null ? configValue.toString() : "null"); //$NON-NLS-1$
					}

					CoreBundleActivator
					.getTraceHandler()
					.trace(message.toString(), 0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING, IStatus.INFO, this);
				}

				if (match == PARTIAL_MATCH && mode == IPreferenceKeys.MODE_FULL_MATCH_LAUNCH_CONFIG) {
					return 0;
				}
				if (match > NO_MATCH) {
					int attrRanking = getAttributeRanking(key);
					ranking += attrRanking * (attrRanking > 1 ? 4 : 1);
					if (match == FULL_MATCH) {
						ranking += attrRanking * (attrRanking > 1 ? 2 : 1);
					}
					configKeys.remove(key);
					if (isMandatoryAttribute(key)) {
						mandatorys++;
					}
				}
				else {
					return 0;
				}
			}
			else {
				if (CoreBundleActivator.getTraceHandler()
								.isSlotEnabled(0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING)) {
					StringBuilder message = new StringBuilder("Skipped launch spec attribute '"); //$NON-NLS-1$
					message.append(specAttribute.getKey());
					message.append("': is create only attribute"); //$NON-NLS-1$

					CoreBundleActivator
					.getTraceHandler()
					.trace(message.toString(), 0, ITraceIds.TRACE_LAUNCHCONFIGURATIONMATCHING, IStatus.INFO, this);
				}
			}
		}
		if (mandatorys < getNumMandatoryAttributes()) {
			throw new LaunchServiceException("missing mandatory attribute in ILaunchSpecification", //$NON-NLS-1$
							LaunchServiceException.TYPE_MISSING_LAUNCH_SPEC_ATTR);
		}

		Iterator<?> configIt = configKeys.iterator();
		while (configIt.hasNext()) {
			String key = (String) configIt.next();
			Object specValue = launchSpec.getAttribute(key, null);
			Object configValue = configAttributes.get(key);

			int match = compareAttributeValues(key, specValue, configValue, launchSpec, launchConfig);
			if (match > NO_MATCH) {
				int attrRanking = getAttributeRanking(key);
				ranking += attrRanking * (attrRanking > 1 ? 4 : 1);
				if (match == FULL_MATCH) {
					ranking += attrRanking * (attrRanking > 1 ? 2 : 1);
				}
			}
		}

		return ranking;
	}

	/**
	 * Returns the number of defined attributes for this launch manager delegate.
	 *
	 * @see #getAttributeRanking(String)
	 */
	protected int getNumAttributes() {
		return 0;
	}

	/**
	 * Returns the list of mandatory attributes for a launch specification handled by this launch
	 * manager delegate.
	 * <p>
	 * The returned value must not be <code>null</code>, if no attributes are mandatory, an empty
	 * list should be returned.
	 *
	 * @see #getAttributeRanking(String)
	 */
	protected List<String> getMandatoryAttributes() {
		return Collections.emptyList();
	}

	private boolean validateLaunchSpecification(ILaunchSpecification launchSpec) {
		boolean valid = true;
		if (launchSpec == null || !launchSpec.isValid()) {
			return false;
		}
		for (String attribute : getMandatoryAttributes()) {
			if (!launchSpec.hasAttribute(attribute)) {
				valid = false;
				break;
			}
		}
		launchSpec.setIsValid(valid);
		return valid;
	}

	/**
	 * Method for basic launch selection validations like checking the number of project or target
	 * contexts.
	 * <p>
	 * The default implementation returns always <code>true</code>.
	 *
	 * @param launchSelection The launch selection to check.
	 * @return <code>True</code> if the launch selection is valid, <code>false</code> otherwise.
	 */
	protected boolean isValidLaunchSelection(ILaunchSelection launchSelection) {
		return true;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#validate(java.lang.String, org.eclipse.debug.core.ILaunchConfiguration)
	 */
	@Override
	public void validate(String launchMode, ILaunchConfiguration launchConfig) throws LaunchServiceException {
		String missingAttributes = null;
		for (String attribute : getMandatoryAttributes()) {
			if (!isValidAttribute(attribute, launchConfig, launchMode)) {
				missingAttributes = (missingAttributes == null) ? attribute : missingAttributes + ", " + attribute; //$NON-NLS-1$
			}
		}
		if (missingAttributes != null) {
			throw new LaunchServiceException("Missing launch configuration attributes: " + '\n' + missingAttributes, LaunchServiceException.TYPE_MISSING_LAUNCH_CONFIG_ATTR); //$NON-NLS-1$
		}
	}

	/**
	 * Validate a single attribute for a given launch configuration and a launch mode. This method
	 * needs to be overwritten when contributing new launch modes which does not need all attributes.
	 *
	 * @param attributeKey The attribute key.
	 * @param launchConfig The launch configuration.
	 * @param launchMode The launch mode.
	 *
	 * @return <code>True</code>, if the attribute value is valid.
	 */
	protected boolean isValidAttribute(String attributeKey, ILaunchConfiguration launchConfig, String launchMode) {
		try {
			if (launchConfig == null || !launchConfig.hasAttribute(attributeKey)) {
				return false;
			}
		}
		catch (CoreException e) {
			return false;
		}
		return true;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#validate(org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchSpecification)
	 */
	@Override
	public void validate(ILaunchSpecification launchSpec) throws LaunchServiceException {
		String missingAttributes = null;
		for (String attribute : getMandatoryAttributes()) {
			if (launchSpec == null || !launchSpec.hasAttribute(attribute)) {
				// Remember the missing attribute for adding the list to the exception.
				missingAttributes = (missingAttributes == null) ? attribute : missingAttributes + ", " + attribute; //$NON-NLS-1$
			}
		}
		if (missingAttributes != null) {
			throw new LaunchServiceException("Missing launch specification attributes: " + '\n' + missingAttributes, LaunchServiceException.TYPE_MISSING_LAUNCH_SPEC_ATTR); //$NON-NLS-1$
		}
	}

	/**
	 * Returns the number of defined mandatory attributes.
	 *
	 * @see #getMandatoryAttributes()
	 * @see #getAttributeRanking(String)
	 */
	protected final int getNumMandatoryAttributes() {
		return getMandatoryAttributes().size();
	}

	/**
	 * Returns true if the attribute key is in the list of mandatory attributes.
	 *
	 * @see #getMandatoryAttributes()
	 * @see #getAttributeRanking(String)
	 */
	protected final boolean isMandatoryAttribute(String attributeKey) {
		return getMandatoryAttributes().contains(attributeKey);
	}

	/**
	 * Returns the ranking for the given attribute key.
	 * <p>
	 * The default ranking is 1, ranking of mandatory and other fundamental attributes should be
	 * coded as <code>2^n</code>.
	 * <p>
	 * The more important an attribute is the higher <code>n</code> should be, <code>n</code> should
	 * never be less than the number of attributes with a lower ranking.
	 * <p>
	 * Multiple attributes can have an equal ranking when one attribute can compensate the absence
	 * of an other attribute with the same ranking.
	 *
	 * <pre>
	 * Example:
	 *
	 *   Attributes a through f
	 *   Attributes a and b are mandatory, b is more important than a.
	 *   The attributes c and d should be higher prior than e and f, but should have equal ranking.
	 *
	 *   The ranking looks like the following:
	 *    Attribute      Ranking
	 *     b              32  (2^5)
	 *     a              16  (2^4)
	 *     c, d           4   (2^2)
	 *     e, f           1   (2^0)
	 *
	 *   With this rankings it is not possible to compensate a missing higher prior attribute with
	 *   one or more lower prior attributes.
	 *
	 *   Additional methods returns the following values for this example:
	 *
	 *    getNumAttributes()          == 6
	 *    getMandatoryAttibutes()     == {a, b}
	 *    getNumMandatoryAttributes() == 2
	 *    getFullMatchRanking()       >= 48 (the value can be greater than 48 depending
	 *                                       on the implementation of the launch manager delegate)
	 * </pre>
	 *
	 * @param attributeKey The attribute key for which the ranking should be returned
	 */
	protected int getAttributeRanking(String attributeKey) {
		return 1;
	}

	/**
	 * Minimum ranking for a launch configuration to be handled as full match when comparing to a
	 * launch specification.
	 * <p>
	 * Should be overwritten when the method {@link #getAttributeRanking(String)} was overwritten.
	 *
	 * @see #getAttributeRanking(String)
	 */
	protected int getFullMatchRanking() {
		return 0;
	}

	/**
	 * Compares an attribute value of launch configuration and specification.
	 *
	 * If both values are not null, calls {@link #equals(String, Object, Object, ILaunchSpecification, ILaunchConfiguration, String)}
	 * to compare the values. If one of the values is <code>null</code>, and the not <code>null</code> value is default, <code>true</code>
	 * is returned. If both values are <code>null</code>, <code>true</code> is returned.
	 *
	 * @param attributeKey The attribute key. Must not be <code>null</code>.
	 * @param specValue The launch specification value.
	 * @param confValue The launch configuration value.
	 * @param launchSpec The launch specification which is the source of the <code>specValue</code>. Must not be <code>null</code>.
	 * @param launchConfig The launch configuration which is the source of the <code>confValue</code>. Must not be <code>null</code>.
	 * @return NO_MATCH, PARTIAL_MATCH or FULL_MATCH
	 */
	protected int compareAttributeValues(String attributeKey, Object specValue, Object confValue, ILaunchSpecification launchSpec, ILaunchConfiguration launchConfig) {
		Assert.isNotNull(attributeKey);
		Assert.isNotNull(launchSpec);
		Assert.isNotNull(launchConfig);

		// values are equal if both are null
		if (specValue == null && confValue == null) {
			return FULL_MATCH;
		}
		// if launch specification value is null,
		// values are equal if launch configuration value is default
		else if (specValue == null) {
			Assert.isNotNull(confValue);
			return isDefaultAttribute(attributeKey, confValue, launchConfig, launchSpec.getLaunchMode()) ? FULL_MATCH : NO_MATCH;
		}
		// if launch configuration value is default,
		// values are equal if launch specification value is default too
		else if (isDefaultAttribute(attributeKey, confValue, launchConfig, launchSpec.getLaunchMode()) || confValue == null) {
			Assert.isNotNull(specValue);
			return isDefaultAttribute(attributeKey, specValue, launchConfig, launchSpec.getLaunchMode()) ? FULL_MATCH : NO_MATCH;
		}
		// use object.equals as default
		else {
			Assert.isNotNull(specValue);
			Assert.isNotNull(confValue);
			return equals(attributeKey, specValue, confValue, launchSpec, launchConfig, launchSpec.getLaunchMode());
		}
	}

	/**
	 * Compares the attribute value of launch configuration and launch specification.
	 * <p>
	 * The handling of null values is implemented in the calling private method
	 * {@link #compareAttributeValues(String, Object, Object, ILaunchSpecification, ILaunchConfiguration)}.
	 * When overwriting this method the implementor can be certain both values are not <code>null</code>.
	 *
	 * @param attributeKey The attribute key
	 * @param specValue The launch specification value. Must not be <code>null</code>.
	 * @param confValue The launch configuration value. Must not be <code>null</code>.
	 * @param launchSpec The launch specification.
	 * @param launchConfig The launch configuration which is the source of the <code>confValue</code>.
	 * @param launchMode The current launch mode.
	 *
	 * @return NO_MATCH, PARTIAL_MATCH or FULL_MATCH
	 */
	protected int equals(String attributeKey, Object specValue, Object confValue, ILaunchSpecification launchSpec, ILaunchConfiguration launchConfig, String launchMode) {
		Assert.isNotNull(specValue);
		Assert.isNotNull(confValue);

		if (ICommonLaunchAttributes.ATTR_PROJECTS_FOR_BUILD.equals(attributeKey)) {
			// get match of list objects
			int match = specValue.equals(confValue) ? FULL_MATCH : NO_MATCH;
			// compare objects in the list when they are not already equal
			if (match != FULL_MATCH && specValue instanceof List<?> && confValue instanceof List<?>) {
				List<?> specProject = (List<?>) specValue;
				List<?> confProject = (List<?>) confValue;
				match = (specProject.isEmpty() || confProject.isEmpty()) ? PARTIAL_MATCH : NO_MATCH;
				List<?> refProjects = null;
				try {
					refProjects = launchConfig.getAttribute(ICommonLaunchAttributes.ATTR_REFERENCED_PROJECTS, (List<?>) null);
				}
				catch (CoreException e) {
					// ignore
				}
				for (int i = 0; i < specProject.size(); i++) {
					Object specObject = specProject.get(i);
					if (specObject != null && confProject.contains(specObject)) {
						// spec object can be found in the configuration
						if (match == NO_MATCH) {
							// full match on first element in the spec list,
							// otherwise partial match
							match = (i == 0) ? FULL_MATCH : PARTIAL_MATCH;
						}
					}
					else if (specObject != null && refProjects != null && refProjects.contains(specObject)) {
						// spec object can be found in the configuration
						if (match == NO_MATCH) {
							match = PARTIAL_MATCH;
						}
					}
					else if (match == FULL_MATCH) {
						// reduce full to partial match when spec object wasn't found
						match = PARTIAL_MATCH;
					}
				}
				// reduce full to partial match when list size is not equal
				// but all spec values where found in the configuration project list
				if (match == FULL_MATCH && specProject.size() != confProject.size()) {
					match = PARTIAL_MATCH;
				}
			}
			return match;
		}

		return specValue.equals(confValue) ? FULL_MATCH : NO_MATCH;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#showLaunchDialog(int)
	 */
	@Override
	public boolean showLaunchDialog(int situation) {
		return true;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#showLaunchConfigOnly()
	 */
	@Override
	public boolean showLaunchConfigOnly() {
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#showLaunchConfigSelectionDialog(org.eclipse.debug.core.ILaunchConfigurationType, org.eclipse.debug.core.ILaunchConfiguration[])
	 */
	@Override
	public boolean showLaunchConfigSelectionDialog(ILaunchConfigurationType type, ILaunchConfiguration[] launchConfigs) {
		if (type != null) {
			String key = type.getIdentifier() + ".hideLaunchConfigSelectionDialog"; //$NON-NLS-1$
			return !CoreBundleActivator.getScopedPreferences().getBoolean(key);
		}
		return true;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#getErrorMessage()
	 */
	@Override
	public String getErrorMessage() {
		return errorMessage;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#equals(org.eclipse.tcf.te.launch.core.selection.interfaces.ISelectionContext, org.eclipse.tcf.te.launch.core.selection.interfaces.ISelectionContext)
	 */
	@Override
	public boolean equals(ISelectionContext ctx1, ISelectionContext ctx2) {
		return (ctx1 == null && ctx2 == null) || (ctx1 != null && ctx1.equals(ctx2));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.te.launch.core.lm.interfaces.ILaunchManagerDelegate#useDefaultConnection()
	 */
	@Override
	public boolean useDefaultConnection() {
		return true;
	}
}

Back to the top