1 /*******************************************************************************
2  * Copyright (c) 2013, 2018 Red Hat, Inc. and others
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  *  Contributors:
12  *     Red Hat, Inc. - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.equinox.internal.p2.operations;
15 
16 import java.util.*;
17 import java.util.Map.Entry;
18 import org.eclipse.core.runtime.*;
19 import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest;
20 import org.eclipse.equinox.p2.engine.*;
21 import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery;
22 import org.eclipse.equinox.p2.metadata.*;
23 import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
24 import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
25 import org.eclipse.equinox.p2.planner.IPlanner;
26 import org.eclipse.equinox.p2.planner.IProfileChangeRequest;
27 import org.eclipse.equinox.p2.query.*;
28 
29 public class RequestFlexer {
30 	final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$
31 	final String INCLUSION_OPTIONAL = "OPTIONAL"; //$NON-NLS-1$
32 	final String INCLUSION_STRICT = "STRICT"; //$NON-NLS-1$
33 	final String EXPLANATION_ENABLEMENT = "org.eclipse.equinox.p2.director.explain"; //$NON-NLS-1$
34 
35 	IPlanner planner;
36 
37 	private boolean allowInstalledUpdate = false;
38 	private boolean allowInstalledRemoval = false;
39 	private boolean allowDifferentVersion = false;
40 	private boolean allowPartialInstall = false;
41 
42 	private boolean ensureProductPresence = true;
43 	private boolean honorSharedSettings = true;
44 
45 	private ProvisioningContext provisioningContext;
46 
47 	Set<IRequirement> requirementsForElementsBeingInstalled = new HashSet<>();
48 	Set<IRequirement> requirementsForElementsAlreadyInstalled = new HashSet<>();
49 	Map<IRequirement, Map<String, String>> propertiesPerRequirement = new HashMap<>();
50 	Map<IRequirement, List<String>> removedPropertiesPerRequirement = new HashMap<>();
51 
52 	IProfile profile;
53 
54 	private boolean foundDifferentVersionsForElementsToInstall = false;
55 	private boolean foundDifferentVersionsForElementsInstalled = false;
56 	private Set<IInstallableUnit> futureOptionalIUs;
57 
RequestFlexer(IPlanner planner)58 	public RequestFlexer(IPlanner planner) {
59 		this.planner = planner;
60 	}
61 
setAllowInstalledElementChange(boolean allow)62 	public void setAllowInstalledElementChange(boolean allow) {
63 		allowInstalledUpdate = allow;
64 	}
65 
setAllowInstalledElementRemoval(boolean allow)66 	public void setAllowInstalledElementRemoval(boolean allow) {
67 		allowInstalledRemoval = allow;
68 	}
69 
setAllowDifferentVersion(boolean allow)70 	public void setAllowDifferentVersion(boolean allow) {
71 		allowDifferentVersion = allow;
72 	}
73 
setAllowPartialInstall(boolean allow)74 	public void setAllowPartialInstall(boolean allow) {
75 		allowPartialInstall = allow;
76 	}
77 
setProvisioningContext(ProvisioningContext context)78 	public void setProvisioningContext(ProvisioningContext context) {
79 		provisioningContext = context;
80 	}
81 
setEnsureProduct(boolean productPresent)82 	public void setEnsureProduct(boolean productPresent) {
83 		ensureProductPresence = productPresent;
84 	}
85 
getChangeRequest(IProfileChangeRequest request, IProfile prof, IProgressMonitor monitor)86 	public IProfileChangeRequest getChangeRequest(IProfileChangeRequest request, IProfile prof, IProgressMonitor monitor) {
87 		this.profile = prof;
88 		SubMonitor sub = SubMonitor.convert(monitor, 2);
89 		IProfileChangeRequest loosenedRequest = computeLooseRequest(request, sub.newChild(1));
90 		if (canShortCircuit(request)) {
91 			return null;
92 		}
93 		IProvisioningPlan intermediaryPlan = resolve(loosenedRequest, sub.newChild(1));
94 		if (!intermediaryPlan.getStatus().isOK())
95 			return null;
96 		if (intermediaryPlan.getAdditions().query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).isEmpty() && intermediaryPlan.getRemovals().query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).isEmpty())
97 			//No changes, we can't return anything
98 			return null;
99 		if (!productContainmentOK(intermediaryPlan)) {
100 			return null;
101 		}
102 		IProfileChangeRequest effectiveRequest = computeEffectiveChangeRequest(intermediaryPlan, loosenedRequest, request);
103 		if (isRequestUseless(effectiveRequest))
104 			return null;
105 		return effectiveRequest;
106 	}
107 
isRequestUseless(IProfileChangeRequest effectiveRequest)108 	private boolean isRequestUseless(IProfileChangeRequest effectiveRequest) {
109 		if (effectiveRequest.getAdditions().isEmpty() && effectiveRequest.getRemovals().isEmpty())
110 			return true;
111 		if (effectiveRequest.getRemovals().containsAll(effectiveRequest.getAdditions()))
112 			return true;
113 		return false;
114 	}
115 
canShortCircuit(IProfileChangeRequest originalRequest)116 	private boolean canShortCircuit(IProfileChangeRequest originalRequest) {
117 		//Case where the user is asking to install only some of the requested IUs but there is only one IU to install.
118 		if (allowPartialInstall && !allowInstalledUpdate && !allowDifferentVersion && !allowInstalledRemoval)
119 			if (originalRequest.getAdditions().size() == 1)
120 				return true;
121 
122 		//When we can find a different version of the IU but the only version available is the one the user is asking to install
123 		if (allowDifferentVersion && !allowPartialInstall && !allowInstalledRemoval && !allowInstalledUpdate)
124 			if (!foundDifferentVersionsForElementsToInstall)
125 				return true;
126 
127 		if (allowInstalledUpdate && !allowDifferentVersion && !allowPartialInstall && !allowInstalledRemoval)
128 			if (!foundDifferentVersionsForElementsInstalled)
129 				return true;
130 
131 		if (!allowPartialInstall && !allowInstalledUpdate && !allowDifferentVersion && !allowInstalledRemoval)
132 			return true;
133 
134 		return false;
135 	}
136 
137 	//From the loosened request and the plan resulting from its resolution, create a new profile change request representing the delta between where the profile currently is
138 	// and the plan returned.
139 	//To perform this efficiently, this relies on a traversal of the requirements that are part of the loosened request.
computeEffectiveChangeRequest(IProvisioningPlan intermediaryPlan, IProfileChangeRequest loosenedRequest, IProfileChangeRequest originalRequest)140 	private IProfileChangeRequest computeEffectiveChangeRequest(IProvisioningPlan intermediaryPlan, IProfileChangeRequest loosenedRequest, IProfileChangeRequest originalRequest) {
141 		IProfileChangeRequest finalChangeRequest = planner.createChangeRequest(profile);
142 		// We have the following two variables because a IPCRequest can not be muted
143 		Set<IInstallableUnit> iusToAdd = new HashSet<>();
144 		Set<IInstallableUnit> iusToRemove = new HashSet<>();
145 
146 		for (IRequirement beingInstalled : requirementsForElementsBeingInstalled) {
147 			IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(beingInstalled.getMatches());
148 			IQueryResult<IInstallableUnit> matches = intermediaryPlan.getFutureState().query(QueryUtil.createLatestQuery(query), null);
149 			IInstallableUnit replacementIU = null;
150 			if (!matches.isEmpty()) {
151 				replacementIU = matches.iterator().next();
152 				iusToAdd.add(replacementIU);
153 				adaptIUPropertiesToNewIU(beingInstalled, replacementIU, finalChangeRequest);
154 			}
155 		}
156 
157 		for (IRequirement alreadyInstalled : requirementsForElementsAlreadyInstalled) {
158 			IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(alreadyInstalled.getMatches());
159 			IQueryResult<IInstallableUnit> matches = intermediaryPlan.getFutureState().query(QueryUtil.createLatestQuery(query), null);
160 			IInstallableUnit potentialRootChange = null;
161 			if (!matches.isEmpty())
162 				potentialRootChange = matches.iterator().next();
163 
164 			IQueryResult<IInstallableUnit> iuAlreadyInstalled = profile.available(query, new NullProgressMonitor());
165 
166 			if (!iuAlreadyInstalled.isEmpty()) {//This deals with the case where the root has not changed
167 				if (potentialRootChange != null && iuAlreadyInstalled.toUnmodifiableSet().contains(potentialRootChange))
168 					continue;
169 			}
170 			iusToRemove.addAll(iuAlreadyInstalled.toUnmodifiableSet());
171 			if (potentialRootChange != null) {
172 				if (!iusToAdd.contains(potentialRootChange)) {//So we don't add the same IU twice for addition
173 					iusToAdd.add(potentialRootChange);
174 					adaptIUPropertiesToNewIU(alreadyInstalled, potentialRootChange, finalChangeRequest);
175 				}
176 			}
177 		}
178 
179 		iusToRemove.addAll(originalRequest.getRemovals());
180 
181 		//Remove entries that are both in the additions and removals (since this is a no-op)
182 		HashSet<IInstallableUnit> commons = new HashSet<>(iusToAdd);
183 		if (commons.retainAll(iusToRemove)) {
184 			iusToAdd.removeAll(commons);
185 			iusToRemove.removeAll(commons);
186 		}
187 
188 		//Finish construction of the IPCR
189 		finalChangeRequest.addAll(iusToAdd);
190 		finalChangeRequest.removeAll(iusToRemove);
191 		if (originalRequest.getExtraRequirements() != null)
192 			finalChangeRequest.addExtraRequirements(originalRequest.getExtraRequirements());
193 		return finalChangeRequest;
194 	}
195 
adaptIUPropertiesToNewIU(IRequirement beingInstalled, IInstallableUnit newIU, IProfileChangeRequest finalChangeRequest)196 	private void adaptIUPropertiesToNewIU(IRequirement beingInstalled, IInstallableUnit newIU, IProfileChangeRequest finalChangeRequest) {
197 		Map<String, String> associatedProperties = propertiesPerRequirement.get(beingInstalled);
198 		if (associatedProperties != null) {
199 			Set<Entry<String, String>> entries = associatedProperties.entrySet();
200 			for (Entry<String, String> entry : entries) {
201 				finalChangeRequest.setInstallableUnitProfileProperty(newIU, entry.getKey(), entry.getValue());
202 			}
203 		}
204 		List<String> removedProperties = removedPropertiesPerRequirement.get(beingInstalled);
205 		if (removedProperties != null) {
206 			for (String toRemove : removedProperties) {
207 				finalChangeRequest.removeInstallableUnitProfileProperty(newIU, toRemove);
208 			}
209 		}
210 	}
211 
212 	//Create a request where the original requirements are "loosened" according to flags specified in this instance
213 	//The resulting profile change request uses the requirements specified using p2QL and those appear in the extraRequirements.
computeLooseRequest(IProfileChangeRequest originalRequest, IProgressMonitor monitor)214 	private IProfileChangeRequest computeLooseRequest(IProfileChangeRequest originalRequest, IProgressMonitor monitor) {
215 		IProfileChangeRequest loosenedRequest = planner.createChangeRequest(profile);
216 		SubMonitor sub = SubMonitor.convert(monitor, 2);
217 		loosenUpOriginalRequest(loosenedRequest, originalRequest, sub.newChild(1));
218 		loosenUpInstalledSoftware(loosenedRequest, originalRequest, sub.newChild(1));
219 		return loosenedRequest;
220 	}
221 
removalRequested(IInstallableUnit removalRequested, IProfileChangeRequest request)222 	private boolean removalRequested(IInstallableUnit removalRequested, IProfileChangeRequest request) {
223 		return request.getRemovals().contains(removalRequested);
224 	}
225 
resolve(IProfileChangeRequest temporaryRequest, IProgressMonitor monitor)226 	private IProvisioningPlan resolve(IProfileChangeRequest temporaryRequest, IProgressMonitor monitor) {
227 		SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
228 		String explainPropertyBackup = null;
229 		try {
230 			temporaryRequest.setProfileProperty("_internal_user_defined_", "true"); //$NON-NLS-1$//$NON-NLS-2$
231 			if (provisioningContext != null) {
232 				explainPropertyBackup = provisioningContext.getProperty(EXPLANATION_ENABLEMENT);
233 				provisioningContext.setProperty(EXPLANATION_ENABLEMENT, Boolean.FALSE.toString());
234 			}
235 			return planner.getProvisioningPlan(temporaryRequest, provisioningContext, subMonitor.split(1));
236 		} finally {
237 			if (provisioningContext != null) {
238 				if (explainPropertyBackup == null)
239 					provisioningContext.getProperties().remove(EXPLANATION_ENABLEMENT);
240 				else
241 					provisioningContext.setProperty(EXPLANATION_ENABLEMENT, explainPropertyBackup);
242 			}
243 		}
244 	}
245 
246 	//Loosen the request originally emitted.
247 	//For example if the user said "install A 1.0", then a new Requirement is added saying (install A 1.0 or install A 2.0), this depending on the configuration flags
loosenUpOriginalRequest(IProfileChangeRequest newRequest, IProfileChangeRequest originalRequest, IProgressMonitor monitor)248 	private void loosenUpOriginalRequest(IProfileChangeRequest newRequest, IProfileChangeRequest originalRequest, IProgressMonitor monitor) {
249 		//First deal with the IUs that are being added
250 		Collection<IInstallableUnit> requestedAdditions = originalRequest.getAdditions();
251 		SubMonitor subMonitor = SubMonitor.convert(monitor, requestedAdditions.size());
252 		for (IInstallableUnit addedIU : requestedAdditions) {
253 			SubMonitor iterationMonitor = subMonitor.split(1);
254 			Collection<IInstallableUnit> potentialUpdates = allowDifferentVersion ? findAllVersionsAvailable(addedIU, iterationMonitor) : new ArrayList<>();
255 			foundDifferentVersionsForElementsToInstall = (foundDifferentVersionsForElementsToInstall || (potentialUpdates.size() == 0 ? false : true));
256 			potentialUpdates.add(addedIU); //Make sure that we include the IU that we were initially trying to install
257 
258 			Collection<IRequirement> newRequirement = new ArrayList<>(1);
259 			IRequirement req = createORRequirement(potentialUpdates, allowPartialInstall || isRequestedInstallationOptional(addedIU, originalRequest));
260 			newRequirement.add(req);
261 			newRequest.addExtraRequirements(newRequirement);
262 			requirementsForElementsBeingInstalled.addAll(newRequirement);
263 			rememberIUProfileProperties(addedIU, req, originalRequest, false);
264 		}
265 
266 		//Deal with the IUs requested for removal
267 		newRequest.removeAll(originalRequest.getRemovals());
268 
269 		//Deal with extra requirements that could have been specified
270 		if (originalRequest.getExtraRequirements() != null)
271 			newRequest.addExtraRequirements(originalRequest.getExtraRequirements());
272 	}
273 
274 	//This keeps track for each requirement created (those created to loosen the constraint), of the original IU and the properties associated with it in the profile
275 	//This is used for more easily construct the final profile change request
rememberIUProfileProperties(IInstallableUnit iu, IRequirement req, IProfileChangeRequest originalRequest, boolean includeProfile)276 	private void rememberIUProfileProperties(IInstallableUnit iu, IRequirement req, IProfileChangeRequest originalRequest, boolean includeProfile) {
277 		Map<String, String> allProperties = new HashMap<>();
278 		if (includeProfile) {
279 			Map<String, String> tmp = new HashMap<>(profile.getInstallableUnitProperties(iu));
280 			List<String> propertiesToRemove = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToRemove().get(iu);
281 			if (propertiesToRemove != null) {
282 				for (String toRemove : propertiesToRemove) {
283 					tmp.remove(toRemove);
284 				}
285 			}
286 			allProperties.putAll(tmp);
287 		}
288 
289 		Map<String, String> propertiesInRequest = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToAdd().get(iu);
290 		if (propertiesInRequest != null)
291 			allProperties.putAll(propertiesInRequest);
292 
293 		propertiesPerRequirement.put(req, allProperties);
294 
295 		List<String> removalInRequest = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToRemove().get(iu);
296 		if (removalInRequest != null)
297 			removedPropertiesPerRequirement.put(req, removalInRequest);
298 	}
299 
isRequestedInstallationOptional(IInstallableUnit iu, IProfileChangeRequest originalRequest)300 	private boolean isRequestedInstallationOptional(IInstallableUnit iu, IProfileChangeRequest originalRequest) {
301 		Map<String, String> match = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToAdd().get(iu);
302 		if (match == null)
303 			return false;
304 		return INCLUSION_OPTIONAL.equals(match.get(INCLUSION_RULES));
305 	}
306 
findAllVersionsAvailable(IInstallableUnit iu, IProgressMonitor monitor)307 	private Collection<IInstallableUnit> findAllVersionsAvailable(IInstallableUnit iu, IProgressMonitor monitor) {
308 		Collection<IInstallableUnit> allVersions = new HashSet<>();
309 		SubMonitor sub = SubMonitor.convert(monitor, 2);
310 		allVersions.addAll(findIUsWithSameId(iu, sub.newChild(1)));
311 		allVersions.addAll(findUpdates(iu, sub.newChild(1)));
312 		return allVersions;
313 	}
314 
findIUsWithSameId(IInstallableUnit iu, IProgressMonitor monitor)315 	private Collection<IInstallableUnit> findIUsWithSameId(IInstallableUnit iu, IProgressMonitor monitor) {
316 		SubMonitor sub = SubMonitor.convert(monitor, 2);
317 		IQueryable<IInstallableUnit> metadata = provisioningContext.getMetadata(sub.newChild(1));
318 		return metadata.query(QueryUtil.createIUQuery(iu.getId()), sub.newChild(1)).toUnmodifiableSet();
319 	}
320 
findUpdates(IInstallableUnit iu, IProgressMonitor monitor)321 	private Collection<IInstallableUnit> findUpdates(IInstallableUnit iu, IProgressMonitor monitor) {
322 		SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
323 		Collection<IInstallableUnit> availableUpdates = new HashSet<>();
324 		IQueryResult<IInstallableUnit> updatesAvailable = planner.updatesFor(iu, provisioningContext, subMonitor.split(1));
325 		for (IInstallableUnit unit : updatesAvailable) {
326 			availableUpdates.add(unit);
327 		}
328 		return availableUpdates;
329 	}
330 
331 	//Create an OR expression that is matching all the entries from the given collection
createORRequirement(Collection<IInstallableUnit> findUpdates, boolean optional)332 	private IRequirement createORRequirement(Collection<IInstallableUnit> findUpdates, boolean optional) {
333 		StringBuilder expression = new StringBuilder();
334 		Object[] expressionParameters = new Object[findUpdates.size() * 2];
335 		int count = 0;
336 		for (IInstallableUnit iu : findUpdates) {
337 			expression.append("(id == $").append(count * 2).append(" && version == $").append(count * 2 + 1).append(')'); //$NON-NLS-1$//$NON-NLS-2$
338 			if (findUpdates.size() > 1 && count < findUpdates.size() - 1)
339 				expression.append(" || "); //$NON-NLS-1$
340 			expressionParameters[count * 2] = iu.getId();
341 			expressionParameters[count * 2 + 1] = iu.getVersion();
342 			count++;
343 		}
344 		IMatchExpression<IInstallableUnit> iuMatcher = ExpressionUtil.getFactory().matchExpression(ExpressionUtil.parse(expression.toString()), expressionParameters);
345 		return MetadataFactory.createRequirement(iuMatcher, null, optional ? 0 : 1, 1, true);
346 	}
347 
348 	//Loosen up the IUs that are already part of the profile
349 	//Given how we are creating our request, this needs to take into account the removal from the original request as well as the change in inclusion
loosenUpInstalledSoftware(IProfileChangeRequest request, IProfileChangeRequest originalRequest, IProgressMonitor monitor)350 	private IProfileChangeRequest loosenUpInstalledSoftware(IProfileChangeRequest request, IProfileChangeRequest originalRequest, IProgressMonitor monitor) {
351 		if (!allowInstalledRemoval && !allowInstalledUpdate)
352 			return request;
353 		Set<IInstallableUnit> allRoots = getRoots();
354 
355 		for (IInstallableUnit existingIU : allRoots) {
356 			Collection<IInstallableUnit> potentialUpdates = allowInstalledUpdate ? findUpdates(existingIU, monitor) : new HashSet<>();
357 			foundDifferentVersionsForElementsInstalled = (foundDifferentVersionsForElementsInstalled || (potentialUpdates.size() == 0 ? false : true));
358 			potentialUpdates.add(existingIU);
359 			Collection<IRequirement> newRequirement = new ArrayList<>(1);
360 			//when the element is requested for removal or is installed optionally we make sure to mark it optional, otherwise the removal woudl fail
361 			IRequirement req = createORRequirement(potentialUpdates, allowInstalledRemoval || removalRequested(existingIU, originalRequest) || isOptionallyInstalled(existingIU, originalRequest));
362 			newRequirement.add(req);
363 			request.addExtraRequirements(newRequirement);
364 			requirementsForElementsAlreadyInstalled.addAll(newRequirement);
365 			request.remove(existingIU);
366 			rememberIUProfileProperties(existingIU, req, originalRequest, true);
367 		}
368 
369 		return request;
370 	}
371 
getRoots()372 	private Set<IInstallableUnit> getRoots() {
373 		Set<IInstallableUnit> allRoots = profile.query(new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY), null).toSet();
374 		if (!honorSharedSettings)
375 			return allRoots;
376 		IQueryResult<IInstallableUnit> baseRoots = profile.query(new IUProfilePropertyQuery("org.eclipse.equinox.p2.base", Boolean.TRUE.toString()), null);
377 		allRoots.removeAll(baseRoots.toUnmodifiableSet());
378 		return allRoots;
379 	}
380 
381 	//This return whether or not the given IU is installed optionally or not.
382 	//This also take into account the future state
isOptionallyInstalled(IInstallableUnit existingIU, IProfileChangeRequest request)383 	private boolean isOptionallyInstalled(IInstallableUnit existingIU, IProfileChangeRequest request) {
384 		return computeFutureStateOfInclusion((ProfileChangeRequest) request).contains(existingIU);
385 	}
386 
387 	//Given the change request, this returns the collection of optional IUs
computeFutureStateOfInclusion(ProfileChangeRequest profileChangeRequest)388 	private Set<IInstallableUnit> computeFutureStateOfInclusion(ProfileChangeRequest profileChangeRequest) {
389 		if (futureOptionalIUs != null)
390 			return futureOptionalIUs;
391 
392 		futureOptionalIUs = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(INCLUSION_RULES, INCLUSION_OPTIONAL), null).toSet();
393 
394 		Set<Entry<IInstallableUnit, List<String>>> propertiesBeingRemoved = profileChangeRequest.getInstallableUnitProfilePropertiesToRemove().entrySet();
395 		for (Entry<IInstallableUnit, List<String>> propertyRemoved : propertiesBeingRemoved) {
396 			if (propertyRemoved.getValue().contains(INCLUSION_RULES)) {
397 				futureOptionalIUs.remove(propertyRemoved.getKey());
398 			}
399 		}
400 
401 		Set<Entry<IInstallableUnit, Map<String, String>>> propertiesBeingAdded = profileChangeRequest.getInstallableUnitProfilePropertiesToAdd().entrySet();
402 		for (Entry<IInstallableUnit, Map<String, String>> propertyBeingAdded : propertiesBeingAdded) {
403 			String inclusionRule = propertyBeingAdded.getValue().get(INCLUSION_RULES);
404 			if (inclusionRule == null) {
405 				continue;
406 			}
407 			if (INCLUSION_STRICT.equals(inclusionRule)) {
408 				futureOptionalIUs.remove(propertyBeingAdded.getKey());
409 			}
410 			if (INCLUSION_OPTIONAL.equals(inclusionRule)) {
411 				futureOptionalIUs.add(propertyBeingAdded.getKey());
412 			}
413 		}
414 		return futureOptionalIUs;
415 	}
416 
productContainmentOK(IProvisioningPlan intermediaryPlan)417 	private boolean productContainmentOK(IProvisioningPlan intermediaryPlan) {
418 		if (!ensureProductPresence)
419 			return true;
420 		if (!hasProduct())
421 			return true;
422 		//At this point we know we had a product installed and we want to make sure there is one in the resulting solution
423 		if (!intermediaryPlan.getFutureState().query(QueryUtil.createIUProductQuery(), new NullProgressMonitor()).isEmpty())
424 			return true;
425 		//Support for legacy identification of product using the lineUp.
426 		if (!intermediaryPlan.getFutureState().query(QueryUtil.createIUPropertyQuery("lineUp", "true"), new NullProgressMonitor()).isEmpty()) //$NON-NLS-1$//$NON-NLS-2$
427 			return true;
428 		return false;
429 	}
430 
hasProduct()431 	private boolean hasProduct() {
432 		if (!profile.available(QueryUtil.createIUProductQuery(), new NullProgressMonitor()).isEmpty()) {
433 			return true;
434 		}
435 		//Support for legacy identification of product using the lineUp.
436 		if (!profile.available(QueryUtil.createIUPropertyQuery("lineUp", "true"), new NullProgressMonitor()).isEmpty()) //$NON-NLS-1$//$NON-NLS-2$
437 			return true;
438 		return false;
439 	}
440 
441 }
442