| /******************************************************************************* |
| * 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(); |
| } |
| |
| } |