Skip to main content
summaryrefslogtreecommitdiffstats
blob: 34566b09f3443b996d9f4251c65a6a6ae44246af (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
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
/*******************************************************************************
 * Copyright (c) 2007, 2015 IBM Corporation 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:
 * 	IBM Corporation - initial API and implementation
 * 	Genuitec - bug fixes
 *  Sonatype, Inc. - ongoing development
 *  Red Hat, Inc. - support for remediation page
 ******************************************************************************/
package org.eclipse.equinox.internal.p2.director;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.Tracing;
import org.eclipse.equinox.internal.p2.director.Explanation.MissingIU;
import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
import org.eclipse.equinox.internal.p2.metadata.query.UpdateQuery;
import org.eclipse.equinox.internal.p2.rollback.FormerState;
import org.eclipse.equinox.internal.provisional.p2.director.PlannerStatus;
import org.eclipse.equinox.internal.provisional.p2.director.RequestStatus;
import org.eclipse.equinox.p2.core.IAgentLocation;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.engine.*;
import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.planner.*;
import org.eclipse.equinox.p2.query.*;
import org.eclipse.osgi.util.NLS;

public class SimplePlanner implements IPlanner {
	private static boolean DEBUG = Tracing.DEBUG_PLANNER_OPERANDS;

	private static final int ExpandWork = 12;
	private static final String INCLUDE_PROFILE_IUS = "org.eclipse.equinox.p2.internal.profileius"; //$NON-NLS-1$
	public static final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$
	private static final String ID_IU_FOR_ACTIONS = "org.eclipse.equinox.p2.engine.actions.root"; //$NON-NLS-1$
	private static final String EXPLANATION = "org.eclipse.equinox.p2.director.explain"; //$NON-NLS-1$
	private static final String CONSIDER_METAREQUIREMENTS = "org.eclipse.equinox.p2.planner.resolveMetaRequirements"; //$NON-NLS-1$

	static final int UNSATISFIABLE = 1; //status code indicating that the problem is not satisfiable

	private final IProvisioningAgent agent;
	private final IProfileRegistry profileRegistry;
	private final IEngine engine;

	private IProvisioningPlan generateProvisioningPlan(Collection<IInstallableUnit> fromState, Collection<IInstallableUnit> toState, ProfileChangeRequest changeRequest, IProvisioningPlan installerPlan, ProvisioningContext context) {
		IProvisioningPlan plan = engine.createPlan(changeRequest.getProfile(), context);
		plan.setFuturePlan(new CollectionResult<IInstallableUnit>(toState));
		planIUOperations(plan, fromState, toState);
		planPropertyOperations(plan, changeRequest, toState);

		if (DEBUG) {
			Object[] operands = new Object[0];
			try {
				Method getOperands = plan.getClass().getMethod("getOperands", new Class[0]); //$NON-NLS-1$
				operands = (Object[]) getOperands.invoke(plan, new Object[0]);
			} catch (Throwable e) {
				// ignore
			}
			for (int i = 0; i < operands.length; i++) {
				Tracing.debug(operands[i].toString());
			}
		}

		Map<IInstallableUnit, RequestStatus>[] changes = computeActualChangeRequest(toState, changeRequest);
		Map<IInstallableUnit, RequestStatus> requestChanges = (changes == null) ? null : changes[0];
		Map<IInstallableUnit, RequestStatus> requestSideEffects = (changes == null) ? null : changes[1];
		QueryableArray plannedState = new QueryableArray(toState.toArray(new IInstallableUnit[toState.size()]));
		PlannerStatus plannerStatus = new PlannerStatus(Status.OK_STATUS, null, requestChanges, requestSideEffects, plannedState);
		plan.setStatus(plannerStatus);
		plan.setInstallerPlan(installerPlan);
		return plan;
	}

	private Map<IInstallableUnit, RequestStatus>[] buildDetailedErrors(ProfileChangeRequest changeRequest) {
		Collection<IInstallableUnit> requestedAdditions = changeRequest.getAdditions();
		Collection<IInstallableUnit> requestedRemovals = changeRequest.getRemovals();
		Map<IInstallableUnit, RequestStatus> requestStatus = new HashMap<IInstallableUnit, RequestStatus>(requestedAdditions.size() + requestedAdditions.size());
		for (IInstallableUnit added : requestedAdditions) {
			requestStatus.put(added, new RequestStatus(added, RequestStatus.ADDED, IStatus.ERROR, null));
		}
		for (IInstallableUnit removed : requestedRemovals) {
			requestStatus.put(removed, new RequestStatus(removed, RequestStatus.REMOVED, IStatus.ERROR, null));
		}
		@SuppressWarnings("unchecked")
		Map<IInstallableUnit, RequestStatus>[] maps = new Map[] {requestStatus, null};
		return maps;
	}

	private Map<IInstallableUnit, RequestStatus>[] computeActualChangeRequest(Collection<IInstallableUnit> toState, ProfileChangeRequest changeRequest) {
		Collection<IInstallableUnit> requestedAdditions = changeRequest.getAdditions();
		Collection<IInstallableUnit> requestedRemovals = new ArrayList<IInstallableUnit>(changeRequest.getRemovals());
		requestedRemovals.removeAll(requestedAdditions);

		Map<IInstallableUnit, RequestStatus> requestStatus = new HashMap<IInstallableUnit, RequestStatus>(requestedAdditions.size() + requestedRemovals.size());
		for (IInstallableUnit added : requestedAdditions) {
			if (toState.contains(added))
				requestStatus.put(added, new RequestStatus(added, RequestStatus.ADDED, IStatus.OK, null));
			else
				requestStatus.put(added, new RequestStatus(added, RequestStatus.ADDED, IStatus.ERROR, null));
		}

		for (IInstallableUnit removed : requestedRemovals) {
			if (!toState.contains(removed))
				requestStatus.put(removed, new RequestStatus(removed, RequestStatus.REMOVED, IStatus.OK, null));
			else
				requestStatus.put(removed, new RequestStatus(removed, RequestStatus.REMOVED, IStatus.ERROR, null));
		}

		//Compute the side effect changes (e.g. things installed optionally going away)
		Iterator<IInstallableUnit> includedIUs = changeRequest.getProfile().query(new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY), null).iterator();
		Map<IInstallableUnit, RequestStatus> sideEffectStatus = new HashMap<IInstallableUnit, RequestStatus>();
		while (includedIUs.hasNext()) {
			IInstallableUnit removal = includedIUs.next();
			if (!toState.contains(removal) && !requestStatus.containsKey(removal)) {
				sideEffectStatus.put(removal, new RequestStatus(removal, RequestStatus.REMOVED, IStatus.INFO, null));
			}
		}
		@SuppressWarnings("unchecked")
		Map<IInstallableUnit, RequestStatus>[] maps = new Map[] {requestStatus, sideEffectStatus};
		return maps;
	}

	/**
	 * Converts a set containing a list of resolver explanations into a human-readable status object.
	 */
	private IStatus convertExplanationToStatus(Set<Explanation> explanations) {
		if (explanations == null)
			return new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Director_Unsatisfied_Dependencies);

		// hack to create a useful message when a user installs something intended for a target platform into the IDE
		ArrayList<IStatus> forTargets = new ArrayList<IStatus>(0);
		for (Explanation next : explanations) {
			if (next instanceof Explanation.MissingIU) {
				Explanation.MissingIU missingIU = (MissingIU) next;
				if (missingIU.req instanceof IRequiredCapability && missingIU.req.getMatches().getParameters().length == 3 && "A.PDE.Target.Platform".equals(((IRequiredCapability) missingIU.req).getNamespace())) //$NON-NLS-1$
					forTargets.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, missingIU.getUserReadableName(missingIU.iu)));
			}
		}
		if (forTargets.size() > 0) {
			// add a blurb about disabling 'include required software'.  The following line could be removed if bug 309863 is fixed
			forTargets.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Director_For_Target_Unselect_Required));
			// return a multi status with all the IUs that require A.PDE.Target.Platform
			return new MultiStatus(DirectorActivator.PI_DIRECTOR, 1, forTargets.toArray(new IStatus[forTargets.size()]), Messages.Director_For_Target, null);
		}
		MultiStatus root = new MultiStatus(DirectorActivator.PI_DIRECTOR, 1, Messages.Director_Unsatisfied_Dependencies, null);
		//try to find a more specific root message if possible
		String specificMessage = null;
		int errorCode = 0;
		for (Explanation next : explanations) {
			root.add(next.toStatus());
			if (specificMessage == null && next instanceof Explanation.MissingIU) {
				specificMessage = Messages.Explanation_rootMissing;
				errorCode = 10053;
			} else if (specificMessage == null && next instanceof Explanation.Singleton) {
				specificMessage = Messages.Explanation_rootSingleton;
				errorCode = 10054;
			}
		}
		//use a more specific root message if available
		if (specificMessage != null) {
			MultiStatus newRoot = new MultiStatus(DirectorActivator.PI_DIRECTOR, errorCode, specificMessage, null);
			newRoot.merge(root);
			root = newRoot;
		}
		return root;
	}

	private void planPropertyOperations(IProvisioningPlan plan, ProfileChangeRequest profileChangeRequest, Collection<IInstallableUnit> toState) {

		// First deal with profile properties to remove.
		String[] toRemove = profileChangeRequest.getPropertiesToRemove();
		for (int i = 0; i < toRemove.length; i++) {
			plan.setProfileProperty(toRemove[i], null);
		}
		// Now deal with profile property changes/additions
		Map<String, String> propertyChanges = profileChangeRequest.getPropertiesToAdd();
		for (Map.Entry<String, String> entry : propertyChanges.entrySet()) {
			plan.setProfileProperty(entry.getKey(), entry.getValue());
		}

		// Now deal with iu property changes/additions.
		Map<IInstallableUnit, Map<String, String>> allIUPropertyChanges = profileChangeRequest.getInstallableUnitProfilePropertiesToAdd();
		for (Map.Entry<IInstallableUnit, Map<String, String>> entry : allIUPropertyChanges.entrySet()) {
			IInstallableUnit iu = entry.getKey();
			if (!toState.contains(iu))
				continue;
			for (Map.Entry<String, String> entry2 : entry.getValue().entrySet()) {
				plan.setInstallableUnitProfileProperty(iu, entry2.getKey(), entry2.getValue());
			}
		}
		// Now deal with iu property removals.
		Map<IInstallableUnit, List<String>> allIUPropertyDeletions = profileChangeRequest.getInstallableUnitProfilePropertiesToRemove();
		for (Map.Entry<IInstallableUnit, List<String>> entry : allIUPropertyDeletions.entrySet()) {
			IInstallableUnit iu = entry.getKey();
			List<String> iuPropertyRemovals = entry.getValue();
			for (String key : iuPropertyRemovals) {
				plan.setInstallableUnitProfileProperty(iu, key, null);
			}

		}
	}

	private void planIUOperations(IProvisioningPlan plan, Collection<IInstallableUnit> fromState, Collection<IInstallableUnit> toState) {
		new OperationGenerator(plan).generateOperation(fromState, toState);
	}

	public IProvisioningPlan getDiffPlan(IProfile currentProfile, IProfile targetProfile, IProgressMonitor monitor) {
		SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
		sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
		try {
			IProfileChangeRequest profileChangeRequest = FormerState.generateProfileDeltaChangeRequest(currentProfile, targetProfile);
			ProvisioningContext context = new ProvisioningContext(agent);
			if (context.getProperty(INCLUDE_PROFILE_IUS) == null)
				context.setProperty(INCLUDE_PROFILE_IUS, Boolean.FALSE.toString());
			context.setExtraInstallableUnits(Arrays.asList(targetProfile.available(QueryUtil.createIUAnyQuery(), null).toArray(IInstallableUnit.class)));
			return getProvisioningPlan(profileChangeRequest, context, sub.newChild(ExpandWork / 2));
		} finally {
			sub.done();
		}
	}

	public static Collection<IInstallableUnit> findPlannerMarkedIUs(final IProfile profile) {
		IQuery<IInstallableUnit> markerQuery = new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY);
		return profile.query(markerQuery, null).toUnmodifiableSet();
	}

	public static Map<String, String> createSelectionContext(Map<String, String> properties) {
		HashMap<String, String> result = new HashMap<String, String>(properties);
		String environments = properties.get(IProfile.PROP_ENVIRONMENTS);
		if (environments == null)
			return result;
		for (StringTokenizer tokenizer = new StringTokenizer(environments, ","); tokenizer.hasMoreElements();) { //$NON-NLS-1$
			String entry = tokenizer.nextToken();
			int i = entry.indexOf('=');
			String key = entry.substring(0, i).trim();
			String value = entry.substring(i + 1).trim();
			result.put(key, value);
		}
		return result;
	}

	private IInstallableUnit[] gatherAvailableInstallableUnits(IInstallableUnit[] additionalSource, ProvisioningContext context, IProgressMonitor monitor) {
		Map<String, IInstallableUnit> resultsMap = new HashMap<String, IInstallableUnit>();
		if (additionalSource != null) {
			for (int i = 0; i < additionalSource.length; i++) {
				String key = additionalSource[i].getId() + "_" + additionalSource[i].getVersion().toString(); //$NON-NLS-1$
				resultsMap.put(key, additionalSource[i]);
			}
		}
		if (context == null) {
			context = new ProvisioningContext(agent);
		} else {
			for (IInstallableUnit iu : context.getExtraInstallableUnits()) {
				String key = iu.getId() + '_' + iu.getVersion().toString();
				resultsMap.put(key, iu);
			}
		}
		SubMonitor sub = SubMonitor.convert(monitor, 1000);
		IQueryable<IInstallableUnit> queryable = context.getMetadata(sub.newChild(500));
		IQueryResult<IInstallableUnit> matches = queryable.query(QueryUtil.createIUQuery(null, VersionRange.emptyRange), sub.newChild(500));
		for (Iterator<IInstallableUnit> it = matches.iterator(); it.hasNext();) {
			IInstallableUnit iu = it.next();
			String key = iu.getId() + "_" + iu.getVersion().toString(); //$NON-NLS-1$
			IInstallableUnit currentIU = resultsMap.get(key);
			if (currentIU == null || hasHigherFidelity(iu, currentIU))
				resultsMap.put(key, iu);
		}
		sub.done();
		Collection<IInstallableUnit> results = resultsMap.values();
		return results.toArray(new IInstallableUnit[results.size()]);
	}

	private static boolean hasHigherFidelity(IInstallableUnit iu, IInstallableUnit currentIU) {
		if (Boolean.parseBoolean(currentIU.getProperty(IInstallableUnit.PROP_PARTIAL_IU)) && !Boolean.parseBoolean(iu.getProperty(IInstallableUnit.PROP_PARTIAL_IU)))
			return true;
		return false;
	}

	public SimplePlanner(IProvisioningAgent agent) {
		Assert.isNotNull(agent);
		this.agent = agent;
		this.engine = (IEngine) agent.getService(IEngine.SERVICE_NAME);
		this.profileRegistry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
		Assert.isNotNull(engine);
		Assert.isNotNull(profileRegistry);
	}

	private boolean satisfyMetaRequirements(Map<String, String> props) {
		if (props == null)
			return true;
		if (props.get(CONSIDER_METAREQUIREMENTS) == null || "true".equalsIgnoreCase(props.get(CONSIDER_METAREQUIREMENTS))) //$NON-NLS-1$
			return true;
		return false;
	}

	private boolean satisfyMetaRequirements(IProfile p) {
		return satisfyMetaRequirements(p.getProperties());
	}

	// Return the set of IUs representing the complete future state of the profile to satisfy the request or return a 
	// ProvisioningPlan when the request can not be satisfied
	private Object getSolutionFor(ProfileChangeRequest profileChangeRequest, ProvisioningContext context, IProgressMonitor monitor) {
		SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
		sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
		try {
			IProfile profile = profileChangeRequest.getProfile();

			Object[] updatedPlan = updatePlannerInfo(profileChangeRequest, context);

			Map<String, String> newSelectionContext = createSelectionContext(profileChangeRequest.getProfileProperties());

			List<IInstallableUnit> extraIUs = new ArrayList<IInstallableUnit>(profileChangeRequest.getAdditions());
			extraIUs.addAll(profileChangeRequest.getRemovals());
			if (context == null || context.getProperty(INCLUDE_PROFILE_IUS) == null || context.getProperty(INCLUDE_PROFILE_IUS).equalsIgnoreCase(Boolean.TRUE.toString())) {
				Iterator<IInstallableUnit> itor = profile.available(QueryUtil.createIUAnyQuery(), null).iterator();
				while (itor.hasNext())
					extraIUs.add(itor.next());
			}

			IInstallableUnit[] availableIUs = gatherAvailableInstallableUnits(extraIUs.toArray(new IInstallableUnit[extraIUs.size()]), context, sub.newChild(ExpandWork / 4));

			Slicer slicer = new Slicer(new QueryableArray(availableIUs), newSelectionContext, satisfyMetaRequirements(profileChangeRequest.getProfileProperties()));
			IQueryable<IInstallableUnit> slice = slicer.slice(new IInstallableUnit[] {(IInstallableUnit) updatedPlan[0]}, sub.newChild(ExpandWork / 4));
			if (slice == null) {
				IProvisioningPlan plan = engine.createPlan(profile, context);
				plan.setStatus(slicer.getStatus());
				return plan;
			}
			@SuppressWarnings("unchecked")
			final IQueryable<IInstallableUnit>[] queryables = new IQueryable[] {slice, new QueryableArray(profileChangeRequest.getAdditions().toArray(new IInstallableUnit[profileChangeRequest.getAdditions().size()]))};
			slice = new CompoundQueryable<IInstallableUnit>(queryables);
			Projector projector = new Projector(slice, newSelectionContext, slicer.getNonGreedyIUs(), satisfyMetaRequirements(profileChangeRequest.getProfileProperties()));
			projector.setUserDefined(profileChangeRequest.getPropertiesToAdd().containsKey("_internal_user_defined_"));
			projector.encode((IInstallableUnit) updatedPlan[0], (IInstallableUnit[]) updatedPlan[1], profile, profileChangeRequest.getAdditions(), sub.newChild(ExpandWork / 4));
			IStatus s = projector.invokeSolver(sub.newChild(ExpandWork / 4));
			if (s.getSeverity() == IStatus.CANCEL) {
				IProvisioningPlan plan = engine.createPlan(profile, context);
				plan.setStatus(s);
				return plan;
			}
			if (s.getSeverity() == IStatus.ERROR) {
				sub.setTaskName(Messages.Planner_NoSolution);
				if (s.getCode() != UNSATISFIABLE || (context != null && !(context.getProperty(EXPLANATION) == null || Boolean.TRUE.toString().equalsIgnoreCase(context.getProperty(EXPLANATION))))) {
					IProvisioningPlan plan = engine.createPlan(profile, context);
					plan.setStatus(s);
					return plan;
				}

				//Extract the explanation
				Set<Explanation> explanation = projector.getExplanation(sub.newChild(ExpandWork / 4));
				IStatus explanationStatus = convertExplanationToStatus(explanation);

				Map<IInstallableUnit, RequestStatus>[] changes = buildDetailedErrors(profileChangeRequest);
				Map<IInstallableUnit, RequestStatus> requestChanges = (changes == null) ? null : changes[0];
				Map<IInstallableUnit, RequestStatus> requestSideEffects = (changes == null) ? null : changes[1];
				PlannerStatus plannerStatus = new PlannerStatus(explanationStatus, new RequestStatus(null, RequestStatus.REMOVED, IStatus.ERROR, explanation), requestChanges, requestSideEffects, null);

				IProvisioningPlan plan = engine.createPlan(profile, context);
				plan.setStatus(plannerStatus);
				return plan;
			}
			//The resolution succeeded. We can forget about the warnings since there is a solution.
			if (Tracing.DEBUG && s.getSeverity() != IStatus.OK)
				LogHelper.log(s);
			s = Status.OK_STATUS;

			return projector;
		} finally {
			sub.done();
		}
	}

	public IProvisioningPlan getProvisioningPlan(IProfileChangeRequest request, ProvisioningContext context, IProgressMonitor monitor) {
		ProfileChangeRequest pcr = (ProfileChangeRequest) request;
		SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
		sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
		try {
			//Get the solution for the initial request
			Object resolutionResult = getSolutionFor(pcr, context, sub.newChild(ExpandWork / 2));
			// a return value of a plan indicates failure when resolving so return.
			if (resolutionResult instanceof IProvisioningPlan)
				return (IProvisioningPlan) resolutionResult;

			Collection<IInstallableUnit> newState = ((Projector) resolutionResult).extractSolution();
			Collection<IInstallableUnit> fullState = new ArrayList<IInstallableUnit>();
			fullState.addAll(newState);
			newState = AttachmentHelper.attachFragments(newState.iterator(), ((Projector) resolutionResult).getFragmentAssociation());

			IProvisioningPlan temporaryPlan = generatePlan((Projector) resolutionResult, newState, pcr, context);

			//Create a plan for installing necessary pieces to complete the installation (e.g touchpoint actions)
			return createInstallerPlan(pcr.getProfile(), pcr, fullState, newState, temporaryPlan, context, sub.newChild(ExpandWork / 2));
		} catch (OperationCanceledException e) {
			IProvisioningPlan plan = engine.createPlan(pcr.getProfile(), context);
			plan.setStatus(Status.CANCEL_STATUS);
			return plan;
		} finally {
			sub.done();
		}
	}

	//	private IProvisioningPlan generateAbsoluteProvisioningPlan(ProfileChangeRequest profileChangeRequest, ProvisioningContext context, IProgressMonitor monitor) {
	//		Set<IInstallableUnit> toState = profileChangeRequest.getProfile().query(QueryUtil.createIUAnyQuery(), null).toSet();
	//		HashSet<IInstallableUnit> fromState = new HashSet<IInstallableUnit>(toState);
	//		toState.removeAll(profileChangeRequest.getRemovals());
	//		toState.addAll(profileChangeRequest.getAdditions());
	//
	//		IProvisioningPlan plan = engine.createPlan(profileChangeRequest.getProfile(), context);
	//		planIUOperations(plan, fromState, toState);
	//		planPropertyOperations(plan, profileChangeRequest);
	//
	//		if (DEBUG) {
	//			Object[] operands = new Object[0];
	//			try {
	//				Method getOperands = plan.getClass().getMethod("getOperands", new Class[0]); //$NON-NLS-1$
	//				operands = (Object[]) getOperands.invoke(plan, new Object[0]);
	//			} catch (Throwable e) {
	//				// ignore
	//			}
	//			for (int i = 0; i < operands.length; i++) {
	//				Tracing.debug(operands[i].toString());
	//			}
	//		}
	//		Map<IInstallableUnit, RequestStatus>[] changes = computeActualChangeRequest(toState, profileChangeRequest);
	//		Map<IInstallableUnit, RequestStatus> requestChanges = (changes == null) ? null : changes[0];
	//		Map<IInstallableUnit, RequestStatus> requestSideEffects = (changes == null) ? null : changes[1];
	//		QueryableArray plannedState = new QueryableArray(toState.toArray(new IInstallableUnit[toState.size()]));
	//		PlannerStatus plannerStatus = new PlannerStatus(Status.OK_STATUS, null, requestChanges, requestSideEffects, plannedState);
	//		plan.setStatus(plannerStatus);
	//		return plan;
	//	}

	//Verify that all the meta requirements necessary to perform the uninstallation (if necessary) and all t
	private Collection<IRequirement> areMetaRequirementsSatisfied(IProfile oldProfile, Collection<IInstallableUnit> newProfile, IProvisioningPlan initialPlan) {
		Collection<IRequirement> allMetaRequirements = extractMetaRequirements(newProfile, initialPlan);
		for (IRequirement requirement : allMetaRequirements) {
			if (oldProfile.query(QueryUtil.createLimitQuery(QueryUtil.createMatchQuery(requirement.getMatches()), 1), null).isEmpty())
				return allMetaRequirements;
		}
		return null;
	}

	//Return all the meta requirements for the list of IU specified and all the meta requirements listed necessary to satisfy the uninstallation 
	private Collection<IRequirement> extractMetaRequirements(Collection<IInstallableUnit> ius, IProvisioningPlan plan) {
		Set<IRequirement> allMetaRequirements = new HashSet<IRequirement>();
		for (IInstallableUnit iu : ius) {
			allMetaRequirements.addAll(iu.getMetaRequirements());
		}
		IQueryResult<IInstallableUnit> queryResult = plan.getRemovals().query(QueryUtil.createIUAnyQuery(), null);
		for (Iterator<IInstallableUnit> iterator = queryResult.iterator(); iterator.hasNext();) {
			IInstallableUnit iu = iterator.next();
			allMetaRequirements.addAll(iu.getMetaRequirements());
		}
		return allMetaRequirements;
	}

	private IProvisioningPlan createInstallerPlan(IProfile profile, ProfileChangeRequest initialRequest, Collection<IInstallableUnit> unattachedState, Collection<IInstallableUnit> expectedState, IProvisioningPlan initialPlan, ProvisioningContext initialContext, IProgressMonitor monitor) {
		SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);

		try {
			sub.setTaskName(Messages.Director_Task_installer_plan);
			if (profileRegistry == null) {
				IProvisioningPlan plan = engine.createPlan(initialRequest.getProfile(), initialContext);
				plan.setStatus(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Planner_no_profile_registry));
				return plan;
			}

			//No installer agent set
			if (agent.getService(IProvisioningAgent.INSTALLER_AGENT) == null) {
				return initialPlan;
			}

			IProfile installerProfile = ((IProfileRegistry) ((IProvisioningAgent) agent.getService(IProvisioningAgent.INSTALLER_AGENT)).getService(IProfileRegistry.SERVICE_NAME)).getProfile((String) agent.getService(IProvisioningAgent.INSTALLER_PROFILEID));
			if (installerProfile == null)
				return initialPlan;

			//The target and the installer are in the same agent / profile registry
			if (haveSameLocation(agent, (IProvisioningAgent) agent.getService(IProvisioningAgent.INSTALLER_AGENT))) {
				//The target and the installer are the same profile (e.g. the eclipse SDK)
				if (profile.getProfileId().equals(installerProfile.getProfileId())) {
					if (profile.getTimestamp() != installerProfile.getTimestamp()) {
						IProvisioningPlan plan = engine.createPlan(initialRequest.getProfile(), initialContext);
						plan.setStatus(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, NLS.bind(Messages.Planner_profile_out_of_sync, profile.getProfileId())));
						return plan;
					}
					return createInstallerPlanForCohostedCase(profile, initialRequest, initialPlan, unattachedState, expectedState, initialContext, sub);
				}

			}

			if (satisfyMetaRequirements(profile) && !profile.getProfileId().equals(installerProfile.getProfileId())) {
				return createInstallerPlanForCohostedCaseFromExternalInstaller(profile, initialRequest, initialPlan, expectedState, initialContext, installerProfile, sub);
			}

			return createInstallerPlanForExternalInstaller(profile, initialRequest, initialPlan, expectedState, initialContext, installerProfile, sub);

		} finally {
			sub.done();
		}
	}

	private boolean haveSameLocation(IProvisioningAgent agent1, IProvisioningAgent agent2) {
		if (agent1 == null || agent2 == null)
			return false;
		if (agent1 == agent2)
			return true;
		IAgentLocation thisLocation = (IAgentLocation) agent1.getService(IAgentLocation.SERVICE_NAME);
		IAgentLocation otherLocation = (IAgentLocation) agent2.getService(IAgentLocation.SERVICE_NAME);
		if (thisLocation == null || otherLocation == null || (thisLocation == null && otherLocation == null))
			return false;
		return thisLocation.getRootLocation().equals(otherLocation.getRootLocation());
	}

	private IProvisioningPlan createInstallerPlanForCohostedCaseFromExternalInstaller(IProfile profile, ProfileChangeRequest initialRequest, IProvisioningPlan initialPlan, Collection<IInstallableUnit> newState, ProvisioningContext initialContext, IProfile agentProfile, SubMonitor sub) {
		IProvisioningPlan planForProfile = generatePlan(null, newState, initialRequest, initialContext);
		return createInstallerPlanForExternalInstaller(profile, initialRequest, planForProfile, newState, initialContext, agentProfile, sub);
	}

	//Deal with the case where the agent profile is different than the one being provisioned
	private IProvisioningPlan createInstallerPlanForExternalInstaller(IProfile targetedProfile, ProfileChangeRequest initialRequest, IProvisioningPlan initialPlan, Collection<IInstallableUnit> expectedState, ProvisioningContext initialContext, IProfile agentProfile, SubMonitor sub) {
		IProfileRegistry installerRegistry = (IProfileRegistry) ((IProvisioningAgent) agent.getService(IProvisioningAgent.INSTALLER_AGENT)).getService(IProfileRegistry.SERVICE_NAME);
		IProfile installerProfile = installerRegistry.getProfile((String) agent.getService(IProvisioningAgent.INSTALLER_PROFILEID));

		Collection<IRequirement> metaRequirements = areMetaRequirementsSatisfied(installerProfile, expectedState, initialPlan);
		if (metaRequirements == null)
			return initialPlan;

		IInstallableUnit actionsIU = createIUForMetaRequirements(targetedProfile, metaRequirements);
		IInstallableUnit previousActionsIU = getPreviousIUForMetaRequirements(installerProfile, getActionGatheringIUId(targetedProfile), sub);

		ProfileChangeRequest agentRequest = new ProfileChangeRequest(installerProfile);
		agentRequest.add(actionsIU);
		if (previousActionsIU != null)
			agentRequest.remove(previousActionsIU);
		Object externalInstallerPlan = getSolutionFor(agentRequest, initialContext, sub.newChild(10));
		if (externalInstallerPlan instanceof IProvisioningPlan && ((IProvisioningPlan) externalInstallerPlan).getStatus().getSeverity() == IStatus.ERROR) {
			MultiStatus externalInstallerStatus = new MultiStatus(DirectorActivator.PI_DIRECTOR, 0, Messages.Planner_can_not_install_preq, null);
			externalInstallerStatus.add(((IProvisioningPlan) externalInstallerPlan).getStatus());
			IProvisioningPlan plan = engine.createPlan(initialRequest.getProfile(), initialContext);
			plan.setStatus(externalInstallerStatus);
			IProvisioningPlan installerPlan = engine.createPlan(agentProfile, initialContext);
			installerPlan.setStatus(externalInstallerStatus);
			plan.setInstallerPlan(installerPlan);
			return plan;
		}

		initialPlan.setInstallerPlan(generatePlan((Projector) externalInstallerPlan, null, agentRequest, initialContext));
		return initialPlan;
	}

	//Deal with the case where the actions needs to be installed in the same profile than the one we are performing the initial request
	//The expectedState represents the result of the initialRequest where the metaRequirements have been satisfied.
	private IProvisioningPlan createInstallerPlanForCohostedCase(IProfile profile, ProfileChangeRequest initialRequest, IProvisioningPlan initialPlan, Collection<IInstallableUnit> unattachedState, Collection<IInstallableUnit> expectedState, ProvisioningContext initialContext, SubMonitor monitor) {
		Collection<IRequirement> metaRequirements = initialRequest.getRemovals().size() == 0 ? areMetaRequirementsSatisfied(profile, expectedState, initialPlan) : extractMetaRequirements(expectedState, initialPlan);
		if (metaRequirements == null || metaRequirements.isEmpty())
			return initialPlan;

		//Let's compute a plan that satisfy all the metaRequirements. We limit ourselves to only the IUs that were part of the previous solution.
		IInstallableUnit metaRequirementIU = createIUForMetaRequirements(profile, metaRequirements);
		IInstallableUnit previousMetaRequirementIU = getPreviousIUForMetaRequirements(profile, getActionGatheringIUId(profile), monitor);

		//Create an agent request from the initial request
		ProfileChangeRequest agentRequest = new ProfileChangeRequest(profile);
		for (Map.Entry<String, String> entry : initialRequest.getPropertiesToAdd().entrySet()) {
			agentRequest.setProfileProperty(entry.getKey(), entry.getValue());
		}
		String[] removedProperties = initialRequest.getPropertiesToRemove();
		for (int i = 0; i < removedProperties.length; i++) {
			agentRequest.removeProfileProperty(removedProperties[i]);
		}
		Map<IInstallableUnit, List<String>> removedIUProperties = initialRequest.getInstallableUnitProfilePropertiesToRemove();
		for (Map.Entry<IInstallableUnit, List<String>> entry : removedIUProperties.entrySet()) {
			for (String propKey : entry.getValue()) {
				agentRequest.removeInstallableUnitProfileProperty(entry.getKey(), propKey);
			}
		}

		if (previousMetaRequirementIU != null)
			agentRequest.remove(previousMetaRequirementIU);
		agentRequest.add(metaRequirementIU);

		ProvisioningContext agentCtx = new ProvisioningContext(agent);
		agentCtx.setMetadataRepositories(new URI[0]);
		ArrayList<IInstallableUnit> extraIUs = new ArrayList<IInstallableUnit>(unattachedState);
		agentCtx.setExtraInstallableUnits(extraIUs);
		Object agentSolution = getSolutionFor(agentRequest, agentCtx, monitor.newChild(3));
		if (agentSolution instanceof IProvisioningPlan && ((IProvisioningPlan) agentSolution).getStatus().getSeverity() == IStatus.ERROR) {
			MultiStatus agentStatus = new MultiStatus(DirectorActivator.PI_DIRECTOR, 0, Messages.Planner_actions_and_software_incompatible, null);
			agentStatus.add(((IProvisioningPlan) agentSolution).getStatus());
			IProvisioningPlan plan = engine.createPlan(initialRequest.getProfile(), initialContext);
			plan.setStatus(agentStatus);
			IProvisioningPlan installerPlan = engine.createPlan(initialRequest.getProfile(), initialContext);
			installerPlan.setStatus(agentStatus);
			plan.setInstallerPlan(installerPlan);
			return plan;
		}

		//Compute the installer plan. It is the difference between what is currently in the profile and the solution we just computed
		Collection<IInstallableUnit> agentState = ((Projector) agentSolution).extractSolution();
		agentState.remove(metaRequirementIU); //Remove the fake IU
		agentState = AttachmentHelper.attachFragments(agentState.iterator(), ((Projector) agentSolution).getFragmentAssociation());

		ProvisioningContext noRepoContext = createNoRepoContext(initialRequest);
		//...This computes the attachment of what is currently in the profile 
		Object initialSolution = getSolutionFor(new ProfileChangeRequest(new EverythingOptionalProfile(initialRequest.getProfile())), noRepoContext, new NullProgressMonitor());
		if (initialSolution instanceof IProvisioningPlan) {
			LogHelper.log(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, "The resolution of the previous state contained in profile " + initialRequest.getProfile().getProfileId() + " version " + initialRequest.getProfile().getTimestamp() + " failed.")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
			return (IProvisioningPlan) initialSolution;
		}
		Iterator<IInstallableUnit> profileState = initialRequest.getProfile().query(QueryUtil.createIUAnyQuery(), null).iterator();
		Collection<IInstallableUnit> initialState = AttachmentHelper.attachFragments(profileState, ((Projector) initialSolution).getFragmentAssociation());

		IProvisioningPlan agentPlan = generateProvisioningPlan(initialState, agentState, initialRequest, null, initialContext);

		//Compute the installation plan. It is the difference between the state after the installer plan has run and the expectedState.
		return generateProvisioningPlan(agentState, expectedState, initialRequest, agentPlan, initialContext);
	}

	//Compute the set of operands based on the solution obtained previously
	private IProvisioningPlan generatePlan(Projector newSolution, Collection<IInstallableUnit> newState, ProfileChangeRequest request, ProvisioningContext context) {
		//Compute the attachment of the new state if not provided
		if (newState == null) {
			newState = newSolution.extractSolution();
			newState = AttachmentHelper.attachFragments(newState.iterator(), newSolution.getFragmentAssociation());
		}
		ProvisioningContext noRepoContext = createNoRepoContext(request);

		//Compute the attachment of the previous state
		Object initialSolution = getSolutionFor(new ProfileChangeRequest(new EverythingOptionalProfile(request.getProfile())), noRepoContext, new NullProgressMonitor());
		if (initialSolution instanceof IProvisioningPlan) {
			LogHelper.log(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, "The resolution of the previous state contained in profile " + request.getProfile().getProfileId() + " version " + request.getProfile().getTimestamp() + " failed.")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
			return (IProvisioningPlan) initialSolution;
		}
		Iterator<IInstallableUnit> profileState = request.getProfile().query(QueryUtil.createIUAnyQuery(), null).iterator();
		Collection<IInstallableUnit> initialState = AttachmentHelper.attachFragments(profileState, ((Projector) initialSolution).getFragmentAssociation());

		//Generate the plan
		return generateProvisioningPlan(initialState, newState, request, null, context);
	}

	private ProvisioningContext createNoRepoContext(ProfileChangeRequest request) {
		ProvisioningContext noRepoContext = new ProvisioningContext(agent);
		noRepoContext.setMetadataRepositories(new URI[0]);
		noRepoContext.setArtifactRepositories(new URI[0]);
		noRepoContext.setProperty(INCLUDE_PROFILE_IUS, Boolean.FALSE.toString());
		noRepoContext.setExtraInstallableUnits(new ArrayList<IInstallableUnit>(request.getProfile().query(QueryUtil.createIUAnyQuery(), new NullProgressMonitor()).toUnmodifiableSet()));
		return noRepoContext;
	}

	private IInstallableUnit getPreviousIUForMetaRequirements(IProfile profile, String iuId, IProgressMonitor monitor) {
		IQueryResult<IInstallableUnit> c = profile.query(QueryUtil.createIUQuery(iuId), monitor);
		if (c.isEmpty())
			return null;
		return c.iterator().next();
	}

	private String getActionGatheringIUId(IProfile profile) {
		return ID_IU_FOR_ACTIONS + '.' + profile.getProfileId();
	}

	private IInstallableUnit createIUForMetaRequirements(IProfile profile, Collection<IRequirement> metaRequirements) {
		InstallableUnitDescription description = new InstallableUnitDescription();
		String id = getActionGatheringIUId(profile);
		description.setId(id);
		Version version = Version.createOSGi(1, 0, 0, Long.toString(profile.getTimestamp()));
		description.setVersion(version);
		description.addRequirements(metaRequirements);

		ArrayList<IProvidedCapability> providedCapabilities = new ArrayList<IProvidedCapability>();
		IProvidedCapability providedCapability = MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, id, version);
		providedCapabilities.add(providedCapability);
		description.addProvidedCapabilities(providedCapabilities);

		IInstallableUnit actionsIU = MetadataFactory.createInstallableUnit(description);
		return actionsIU;
	}

	private IInstallableUnit createIURepresentingTheProfile(Set<IRequirement> allRequirements) {
		InstallableUnitDescription iud = new MetadataFactory.InstallableUnitDescription();
		String time = Long.toString(System.currentTimeMillis());
		iud.setId(time);
		iud.setVersion(Version.createOSGi(0, 0, 0, time));
		iud.setRequirements(allRequirements.toArray(new IRequirement[allRequirements.size()]));
		return MetadataFactory.createInstallableUnit(iud);
	}

	//The planner uses installable unit properties to keep track of what it has been asked to install. This updates this information
	//It returns at index 0 a meta IU representing everything that needs to be installed
	//It returns at index 1 all the IUs that are in the profile after the removal have been done, but before the addition have been done 
	private Object[] updatePlannerInfo(ProfileChangeRequest profileChangeRequest, ProvisioningContext context) {
		IQueryResult<IInstallableUnit> alreadyInstalled = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY), null);

		Collection<IInstallableUnit> additionRequested = profileChangeRequest.getAdditions();
		Collection<IInstallableUnit> removalRequested = profileChangeRequest.getRemovals();

		for (Map.Entry<IInstallableUnit, List<String>> object : profileChangeRequest.getInstallableUnitProfilePropertiesToRemove().entrySet()) {
			if (object.getValue().contains(INCLUSION_RULES))
				profileChangeRequest.setInstallableUnitProfileProperty(object.getKey(), INCLUSION_RULES, ProfileInclusionRules.createStrictInclusionRule(object.getKey()));
		}
		//Remove the iu properties associated to the ius removed and the iu properties being removed as well
		if (removalRequested.size() != 0) {
			for (Iterator<IInstallableUnit> iterator = alreadyInstalled.iterator(); iterator.hasNext();) {
				IInstallableUnit iu = iterator.next();
				for (IInstallableUnit removed : removalRequested) {
					if (iu.equals(removed)) {
						profileChangeRequest.removeInstallableUnitProfileProperty(removed, INCLUSION_RULES);
						iterator.remove();
						break;
					}
				}
			}
		}
		Set<IRequirement> gatheredRequirements = new HashSet<IRequirement>();

		//Process all the IUs being added
		Map<IInstallableUnit, Map<String, String>> iuPropertiesToAdd = profileChangeRequest.getInstallableUnitProfilePropertiesToAdd();
		for (IInstallableUnit added : additionRequested) {
			Map<String, String> propertiesForIU = iuPropertiesToAdd.get(added);
			IRequirement profileRequirement = null;
			if (propertiesForIU != null) {
				profileRequirement = createRequirement(added, propertiesForIU.get(INCLUSION_RULES));
			}
			if (profileRequirement == null) {
				profileChangeRequest.setInstallableUnitProfileProperty(added, INCLUSION_RULES, ProfileInclusionRules.createStrictInclusionRule(added));
				profileRequirement = createStrictRequirement(added);
			}
			gatheredRequirements.add(profileRequirement);
		}

		//Process the IUs that were already there
		for (Iterator<IInstallableUnit> iterator = alreadyInstalled.iterator(); iterator.hasNext();) {
			IInstallableUnit iu = iterator.next();
			Map<String, String> propertiesForIU = iuPropertiesToAdd.get(iu);
			IRequirement profileRequirement = null;
			//Test if the value has changed
			if (propertiesForIU != null) {
				profileRequirement = createRequirement(iu, propertiesForIU.get(INCLUSION_RULES));
			}
			if (profileRequirement == null) {
				profileRequirement = createRequirement(iu, profileChangeRequest.getProfile().getInstallableUnitProperty(iu, INCLUSION_RULES));
			}
			gatheredRequirements.add(profileRequirement);
		}

		//Now add any other requirement that we need to see satisfied
		if (profileChangeRequest.getExtraRequirements() != null)
			gatheredRequirements.addAll(profileChangeRequest.getExtraRequirements());
		IInstallableUnit[] existingRoots = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY), null).toArray(IInstallableUnit.class);
		return new Object[] {createIURepresentingTheProfile(gatheredRequirements), existingRoots};
	}

	private IRequirement createRequirement(IInstallableUnit iu, String rule) {
		if (rule == null)
			return null;
		if (rule.equals(ProfileInclusionRules.createStrictInclusionRule(iu))) {
			return createStrictRequirement(iu);
		}
		if (rule.equals(ProfileInclusionRules.createOptionalInclusionRule(iu))) {
			return createOptionalRequirement(iu);
		}
		return null;
	}

	private IRequirement createOptionalRequirement(IInstallableUnit iu) {
		return MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), new VersionRange(iu.getVersion(), true, iu.getVersion(), true), null, true, false, true);
	}

	private IRequirement createStrictRequirement(IInstallableUnit iu) {
		return MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), new VersionRange(iu.getVersion(), true, iu.getVersion(), true), null, false, false, true);
	}

	public IQueryResult<IInstallableUnit> updatesFor(IInstallableUnit toUpdate, ProvisioningContext context, IProgressMonitor monitor) {
		Map<String, IInstallableUnit> resultsMap = new HashMap<String, IInstallableUnit>();

		SubMonitor sub = SubMonitor.convert(monitor, 1000);
		IQueryable<IInstallableUnit> queryable = context.getMetadata(sub.newChild(500));
		IQueryResult<IInstallableUnit> matches = queryable.query(new UpdateQuery(toUpdate), sub.newChild(500));
		for (Iterator<IInstallableUnit> it = matches.iterator(); it.hasNext();) {
			IInstallableUnit iu = it.next();
			String key = iu.getId() + "_" + iu.getVersion().toString(); //$NON-NLS-1$
			IInstallableUnit currentIU = resultsMap.get(key);
			if (currentIU == null || hasHigherFidelity(iu, currentIU))
				resultsMap.put(key, iu);
		}
		sub.done();
		return new CollectionResult<IInstallableUnit>(resultsMap.values());
	}

	//helper class to trick the resolver to believe that everything is optional
	private static class EverythingOptionalProfile implements IProfile {
		private IProfile profile;

		public EverythingOptionalProfile(IProfile p) {
			profile = p;
		}

		public IQueryResult<IInstallableUnit> available(IQuery<IInstallableUnit> query, IProgressMonitor monitor) {
			return profile.available(query, monitor);
		}

		public Map<String, String> getInstallableUnitProperties(IInstallableUnit iu) {
			return profile.getInstallableUnitProperties(iu);
		}

		public String getInstallableUnitProperty(IInstallableUnit iu, String key) {
			if (INCLUSION_RULES.equals(key))
				return ProfileInclusionRules.createOptionalInclusionRule(iu);
			return profile.getInstallableUnitProperty(iu, key);
		}

		public String getProfileId() {
			return profile.getProfileId();
		}

		public Map<String, String> getProperties() {
			return profile.getProperties();
		}

		public String getProperty(String key) {
			return profile.getProperty(key);
		}

		public IProvisioningAgent getProvisioningAgent() {
			return profile.getProvisioningAgent();
		}

		public long getTimestamp() {
			return profile.getTimestamp();
		}

		public IQueryResult<IInstallableUnit> query(IQuery<IInstallableUnit> query, IProgressMonitor monitor) {
			return profile.query(query, monitor);
		}
	}

	public IProfileChangeRequest createChangeRequest(IProfile profileToChange) {
		return new ProfileChangeRequest(profileToChange);
	}
}

Back to the top