blob: 1d3cc1e808d6bac79e7821c23b9fcd35ef5e15e6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2018 Rapicorp Inc. and others.
*
* This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License 2.0 which accompanies this distribution, and is
* available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Rapicorp, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.equinox.internal.p2.director;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.equinox.internal.p2.director.Projector.AbstractVariable;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.query.*;
import org.sat4j.pb.tools.WeightedObject;
public class OptimizationFunction {
private IQueryable<IInstallableUnit> picker;
private IInstallableUnit selectionContext;
protected Map<String, Map<Version, IInstallableUnit>> slice; //The IUs that have been considered to be part of the problem
private int numberOfInstalledIUs; //TODO this should be renamed to consideredIUs or sliceSize
private IQueryable<IInstallableUnit> lastState;
private List<AbstractVariable> optionalRequirementVariable;
public OptimizationFunction(IQueryable<IInstallableUnit> lastState, List<AbstractVariable> abstractVariables, List<AbstractVariable> optionalRequirementVariable, IQueryable<IInstallableUnit> picker, IInstallableUnit selectionContext, Map<String, Map<Version, IInstallableUnit>> slice) {
this.lastState = lastState;
this.optionalRequirementVariable = optionalRequirementVariable;
this.picker = picker;
this.selectionContext = selectionContext;
this.slice = slice;
}
//Create an optimization function favoring the highest version of each IU
public List<WeightedObject<? extends Object>> createOptimizationFunction(IInstallableUnit metaIu, Collection<IInstallableUnit> newRoots) {
numberOfInstalledIUs = sizeOf(lastState);
List<WeightedObject<? extends Object>> weightedObjects = new ArrayList<>();
Set<IInstallableUnit> transitiveClosure; //The transitive closure of the IUs we are adding (this also means updating)
if (newRoots.isEmpty()) {
transitiveClosure = Collections.emptySet();
} else {
IQueryable<IInstallableUnit> queryable = new Slicer(picker, selectionContext, false).slice(newRoots.toArray(new IInstallableUnit[newRoots.size()]), new NullProgressMonitor());
if (queryable == null) {
transitiveClosure = Collections.emptySet();
} else {
transitiveClosure = queryable.query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).toSet();
}
}
Set<Entry<String, Map<Version, IInstallableUnit>>> s = slice.entrySet();
final BigInteger POWER = BigInteger.valueOf(numberOfInstalledIUs > 0 ? numberOfInstalledIUs + 1 : 2);
BigInteger maxWeight = POWER;
for (Entry<String, Map<Version, IInstallableUnit>> entry : s) {
List<IInstallableUnit> conflictingEntries = new ArrayList<>(entry.getValue().values());
if (conflictingEntries.size() == 1) {
//Only one IU exists with the namespace.
IInstallableUnit iu = conflictingEntries.get(0);
if (iu != metaIu) {
weightedObjects.add(WeightedObject.newWO(iu, POWER));
}
continue;
}
// Set the weight such that things that are already installed are not updated
conflictingEntries.sort(Collections.reverseOrder());
BigInteger weight = POWER;
// have we already found a version that is already installed?
boolean foundInstalled = false;
// have we already found a version that is in the new roots?
boolean foundRoot = false;
for (IInstallableUnit iu : conflictingEntries) {
if (!foundRoot && isInstalled(iu) && !transitiveClosure.contains(iu)) {
foundInstalled = true;
weightedObjects.add(WeightedObject.newWO(iu, BigInteger.ONE));
} else if (!foundInstalled && !foundRoot && isRoot(iu, newRoots)) {
foundRoot = true;
weightedObjects.add(WeightedObject.newWO(iu, BigInteger.ONE));
} else {
weightedObjects.add(WeightedObject.newWO(iu, weight));
}
weight = weight.multiply(POWER);
}
if (weight.compareTo(maxWeight) > 0)
maxWeight = weight;
}
// no need to add one here, since maxWeight is strictly greater than the
// maximal weight used so far.
maxWeight = maxWeight.multiply(POWER).multiply(BigInteger.valueOf(s.size()));
// Add the optional variables
BigInteger optionalVarWeight = maxWeight.negate();
for (AbstractVariable var : optionalRequirementVariable) {
weightedObjects.add(WeightedObject.newWO(var, optionalVarWeight));
}
maxWeight = maxWeight.multiply(POWER).add(BigInteger.ONE);
//Now we deal the optional IUs,
long countOptional = 1;
List<IInstallableUnit> requestedPatches = new ArrayList<>();
for (IRequirement req : metaIu.getRequirements()) {
if (req.getMin() > 0 || !req.isGreedy())
continue;
for (IInstallableUnit match : picker.query(QueryUtil.createMatchQuery(req.getMatches()), null)) {
if (match instanceof IInstallableUnitPatch) {
requestedPatches.add(match);
countOptional = countOptional + 1;
}
}
}
// and we make sure that patches are always favored
BigInteger patchWeight = maxWeight.multiply(POWER).multiply(BigInteger.valueOf(countOptional)).negate();
for (IInstallableUnit iu : requestedPatches) {
weightedObjects.add(WeightedObject.newWO(iu, patchWeight));
}
return weightedObjects;
}
protected boolean isInstalled(IInstallableUnit iu) {
return !lastState.query(QueryUtil.createIUQuery(iu), null).isEmpty();
}
private boolean isRoot(IInstallableUnit iu, Collection<IInstallableUnit> newRoots) {
return newRoots.contains(iu);
}
/**
* Efficiently compute the size of a queryable
*/
private int sizeOf(IQueryable<IInstallableUnit> installedIUs) {
IQueryResult<IInstallableUnit> qr = installedIUs.query(QueryUtil.createIUAnyQuery(), null);
if (qr instanceof Collector<?>)
return ((Collector<?>) qr).size();
return qr.toUnmodifiableSet().size();
}
}