blob: 57dcc4d81e7f5d574826e3d8168a37c766f6aaa2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 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
* Daniel Le Berre - Fix in the encoding and the optimization function
* Alban Browaeys - Optimized string concatenation in bug 251357
* Jed Anderson - switch from opb files to API calls to DependencyHelper in bug 200380
******************************************************************************/
package org.eclipse.equinox.internal.p2.director;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.internal.p2.core.helpers.Tracing;
import org.eclipse.equinox.internal.provisional.p2.metadata.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.CapabilityQuery;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.InstallableUnitQuery;
import org.eclipse.equinox.internal.provisional.p2.query.Collector;
import org.eclipse.equinox.internal.provisional.p2.query.IQueryable;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.InvalidSyntaxException;
import org.sat4j.pb.IPBSolver;
import org.sat4j.pb.SolverFactory;
import org.sat4j.pb.tools.DependencyHelper;
import org.sat4j.pb.tools.WeightedObject;
import org.sat4j.specs.*;
/**
* This class is the interface between SAT4J and the planner. It produces a
* boolean satisfiability problem, invokes the solver, and converts the solver result
* back into information understandable by the planner.
*/
public class Projector {
static boolean DEBUG = Tracing.DEBUG_PLANNER_PROJECTOR;
private static boolean DEBUG_ENCODING = false;
private IQueryable picker;
private QueryableArray patches;
private Map noopVariables; //key IU, value AbstractVariable
private List abstractVariables;
private TwoTierMap slice; //The IUs that have been considered to be part of the problem
private Dictionary selectionContext;
DependencyHelper dependencyHelper;
private Collection solution;
private Collection assumptions;
private MultiStatus result;
private Collection alreadyInstalledIUs;
private boolean considerMetaRequirements;
private IInstallableUnit entryPoint;
private Map fragments = new HashMap();
static class AbstractVariable {
public String toString() {
return "AbstractVariable: " + hashCode(); //$NON-NLS-1$
}
}
/**
* Job for computing SAT failure explanation in the background.
*/
class ExplanationJob extends Job {
private Set explanation;
public ExplanationJob() {
super(Messages.Planner_NoSolution);
//explanations cannot be canceled directly, so don't show it to the user
setSystem(true);
}
public boolean belongsTo(Object family) {
return family == ExplanationJob.this;
}
protected void canceling() {
super.canceling();
dependencyHelper.stopExplanation();
}
public Set getExplanationResult() {
return explanation;
}
protected IStatus run(IProgressMonitor monitor) {
long start = 0;
if (DEBUG) {
start = System.currentTimeMillis();
Tracing.debug("Determining cause of failure: " + start); //$NON-NLS-1$
}
try {
explanation = dependencyHelper.why();
if (DEBUG) {
long stop = System.currentTimeMillis();
Tracing.debug("Explanation found: " + (stop - start)); //$NON-NLS-1$
Tracing.debug("Explanation:"); //$NON-NLS-1$
for (Iterator i = explanation.iterator(); i.hasNext();) {
Tracing.debug(i.next().toString());
}
}
} catch (TimeoutException e) {
if (DEBUG)
Tracing.debug("Timeout while computing explanations"); //$NON-NLS-1$
} finally {
//must never have a null result, because caller is waiting on result to be non-null
if (explanation == null)
explanation = Collections.EMPTY_SET;
}
synchronized (this) {
ExplanationJob.this.notify();
}
return Status.OK_STATUS;
}
}
public Projector(IQueryable q, Dictionary context, boolean considerMetaRequirements) {
picker = q;
noopVariables = new HashMap();
slice = new TwoTierMap();
selectionContext = context;
abstractVariables = new ArrayList();
result = new MultiStatus(DirectorActivator.PI_DIRECTOR, IStatus.OK, Messages.Planner_Problems_resolving_plan, null);
assumptions = new ArrayList();
this.considerMetaRequirements = considerMetaRequirements;
}
public void encode(IInstallableUnit entryPointIU, IInstallableUnit[] alreadyExistingRoots, IInstallableUnit[] newRoots, IProgressMonitor monitor) {
alreadyInstalledIUs = Arrays.asList(alreadyExistingRoots);
this.entryPoint = entryPointIU;
try {
long start = 0;
if (DEBUG) {
start = System.currentTimeMillis();
Tracing.debug("Start projection: " + start); //$NON-NLS-1$
}
IPBSolver solver;
if (DEBUG_ENCODING) {
solver = SolverFactory.newOPBStringSolver();
} else {
solver = SolverFactory.newEclipseP2();
}
solver.setTimeoutOnConflicts(1000);
Collector collector = picker.query(InstallableUnitQuery.ANY, new Collector(), null);
dependencyHelper = new DependencyHelper(solver);
Iterator iusToEncode = collector.iterator();
if (DEBUG) {
List iusToOrder = new ArrayList();
while (iusToEncode.hasNext()) {
iusToOrder.add(iusToEncode.next());
}
Collections.sort(iusToOrder);
iusToEncode = iusToOrder.iterator();
}
while (iusToEncode.hasNext()) {
if (monitor.isCanceled()) {
result.merge(Status.CANCEL_STATUS);
throw new OperationCanceledException();
}
IInstallableUnit iuToEncode = (IInstallableUnit) iusToEncode.next();
if (iuToEncode != entryPointIU) {
processIU(iuToEncode, false);
}
}
createConstraintsForSingleton();
createMustHave(entryPointIU, alreadyExistingRoots, newRoots);
createOptimizationFunction(entryPointIU);
if (DEBUG) {
long stop = System.currentTimeMillis();
Tracing.debug("Projection complete: " + (stop - start)); //$NON-NLS-1$
}
if (DEBUG_ENCODING) {
System.out.println(solver.toString());
}
} catch (IllegalStateException e) {
result.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, e.getMessage(), e));
} catch (ContradictionException e) {
result.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Planner_Unsatisfiable_problem));
}
}
//Create an optimization function favoring the highest version of each IU
private void createOptimizationFunction(IInstallableUnit metaIu) {
List weightedObjects = new ArrayList();
Set s = slice.entrySet();
final BigInteger POWER = BigInteger.valueOf(2);
BigInteger maxWeight = POWER;
for (Iterator iterator = s.iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
HashMap conflictingEntries = (HashMap) entry.getValue();
if (conflictingEntries.size() == 1) {
continue;
}
List toSort = new ArrayList(conflictingEntries.values());
Collections.sort(toSort, Collections.reverseOrder());
BigInteger weight = BigInteger.ONE;
int count = toSort.size();
for (int i = 0; i < count; i++) {
weightedObjects.add(WeightedObject.newWO(toSort.get(i), weight));
weight = weight.multiply(POWER);
}
if (weight.compareTo(maxWeight) > 0)
maxWeight = weight;
}
maxWeight = maxWeight.multiply(POWER);
// Weight the no-op variables beneath the abstract variables
for (Iterator iterator = noopVariables.values().iterator(); iterator.hasNext();) {
weightedObjects.add(WeightedObject.newWO(iterator.next(), maxWeight));
}
maxWeight = maxWeight.multiply(POWER);
// Add the abstract variables
BigInteger abstractWeight = maxWeight.negate();
for (Iterator iterator = abstractVariables.iterator(); iterator.hasNext();) {
weightedObjects.add(WeightedObject.newWO(iterator.next(), abstractWeight));
}
maxWeight = maxWeight.multiply(POWER);
BigInteger optionalWeight = maxWeight.negate();
long countOptional = 1;
List requestedPatches = new ArrayList();
IRequiredCapability[] reqs = metaIu.getRequiredCapabilities();
for (int j = 0; j < reqs.length; j++) {
if (!reqs[j].isOptional())
continue;
Collector matches = picker.query(new CapabilityQuery(reqs[j]), new Collector(), null);
for (Iterator iterator = matches.iterator(); iterator.hasNext();) {
IInstallableUnit match = (IInstallableUnit) iterator.next();
if (match instanceof IInstallableUnitPatch) {
requestedPatches.add(match);
countOptional = countOptional + 1;
} else
weightedObjects.add(WeightedObject.newWO(match, optionalWeight));
}
}
BigInteger patchWeight = maxWeight.multiply(POWER).multiply(BigInteger.valueOf(countOptional)).negate();
for (Iterator iterator = requestedPatches.iterator(); iterator.hasNext();) {
weightedObjects.add(WeightedObject.newWO(iterator.next(), patchWeight));
}
if (!weightedObjects.isEmpty()) {
createObjectiveFunction(weightedObjects);
}
}
private void createObjectiveFunction(List weightedObjects) {
if (DEBUG) {
StringBuffer b = new StringBuffer();
for (Iterator i = weightedObjects.iterator(); i.hasNext();) {
WeightedObject object = (WeightedObject) i.next();
if (b.length() > 0)
b.append(", "); //$NON-NLS-1$
b.append(object.getWeight());
b.append(' ');
b.append(object.thing);
}
Tracing.debug("objective function: " + b); //$NON-NLS-1$
}
dependencyHelper.setObjectiveFunction((WeightedObject[]) weightedObjects.toArray(new WeightedObject[weightedObjects.size()]));
}
private void createMustHave(IInstallableUnit iu, IInstallableUnit[] alreadyExistingRoots, IInstallableUnit[] newRoots) throws ContradictionException {
processIU(iu, true);
if (DEBUG) {
Tracing.debug(iu + "=1"); //$NON-NLS-1$
}
// dependencyHelper.setTrue(variable, new Explanation.IUToInstall(iu));
assumptions.add(iu);
}
private void createNegation(IInstallableUnit iu, IRequiredCapability req) throws ContradictionException {
if (DEBUG) {
Tracing.debug(iu + "=0"); //$NON-NLS-1$
}
dependencyHelper.setFalse(iu, new Explanation.MissingIU(iu, req));
}
// Check whether the requirement is applicable
private boolean isApplicable(IRequiredCapability req) {
String filter = req.getFilter();
if (filter == null)
return true;
try {
return DirectorActivator.context.createFilter(filter).match(selectionContext);
} catch (InvalidSyntaxException e) {
return false;
}
}
private boolean isApplicable(IInstallableUnit iu) {
String enablementFilter = iu.getFilter();
if (enablementFilter == null)
return true;
try {
return DirectorActivator.context.createFilter(enablementFilter).match(selectionContext);
} catch (InvalidSyntaxException e) {
return false;
}
}
private void expandRequirement(IRequiredCapability req, IInstallableUnit iu, List optionalAbstractRequirements, boolean isRootIu) throws ContradictionException {
if (!isApplicable(req))
return;
List matches = getApplicableMatches(req);
if (isHostRequirement(iu, req)) {
rememberHostMatches(iu, matches);
}
if (!req.isOptional()) {
if (matches.isEmpty()) {
missingRequirement(iu, req);
} else {
IInstallableUnit reqIu = (IInstallableUnit) picker.query(new CapabilityQuery(req), new Collector(), null).iterator().next();
Explanation explanation;
if (isRootIu) {
if (alreadyInstalledIUs.contains(reqIu)) {
explanation = new Explanation.IUInstalled(reqIu);
} else {
explanation = new Explanation.IUToInstall(reqIu);
}
} else {
explanation = new Explanation.HardRequirement(iu, req);
}
createImplication(iu, matches, explanation);
}
} else {
if (!matches.isEmpty()) {
AbstractVariable abs = getAbstractVariable();
createImplication(new Object[] {abs, iu}, matches, Explanation.OPTIONAL_REQUIREMENT);
optionalAbstractRequirements.add(abs);
}
}
}
private void expandRequirements(IRequiredCapability[] reqs, IInstallableUnit iu, boolean isRootIu) throws ContradictionException {
if (reqs.length == 0) {
return;
}
List optionalAbstractRequirements = new ArrayList();
for (int i = 0; i < reqs.length; i++) {
expandRequirement(reqs[i], iu, optionalAbstractRequirements, isRootIu);
}
createOptionalityExpression(iu, optionalAbstractRequirements);
}
public void processIU(IInstallableUnit iu, boolean isRootIU) throws ContradictionException {
iu = iu.unresolved();
slice.put(iu.getId(), iu.getVersion(), iu);
if (!isApplicable(iu)) {
createNegation(iu, null);
return;
}
Collector applicablePatches = getApplicablePatches(iu);
expandLifeCycle(iu, isRootIU);
//No patches apply, normal code path
if (applicablePatches.size() == 0) {
expandRequirements(getRequiredCapabilities(iu), iu, isRootIU);
} else {
//Patches are applicable to the IU
expandRequirementsWithPatches(iu, applicablePatches, isRootIU);
}
}
private IRequiredCapability[] getRequiredCapabilities(IInstallableUnit iu) {
if (considerMetaRequirements == false || iu.getMetaRequiredCapabilities().length == 0)
return iu.getRequiredCapabilities();
IRequiredCapability[] aggregatedCapabilities = new IRequiredCapability[iu.getRequiredCapabilities().length + iu.getMetaRequiredCapabilities().length];
System.arraycopy(iu.getRequiredCapabilities(), 0, aggregatedCapabilities, 0, iu.getRequiredCapabilities().length);
System.arraycopy(iu.getMetaRequiredCapabilities(), 0, aggregatedCapabilities, iu.getRequiredCapabilities().length, iu.getMetaRequiredCapabilities().length);
return aggregatedCapabilities;
}
private void expandRequirementsWithPatches(IInstallableUnit iu, Collector applicablePatches, boolean isRootIu) throws ContradictionException {
//Unmodified dependencies
Map unchangedRequirements = new HashMap(getRequiredCapabilities(iu).length);
for (Iterator iterator = applicablePatches.iterator(); iterator.hasNext();) {
IInstallableUnitPatch patch = (IInstallableUnitPatch) iterator.next();
IRequiredCapability[][] reqs = mergeRequirements(iu, patch);
if (reqs.length == 0)
return;
// Optional requirements are encoded via:
// ABS -> (match1(req) or match2(req) or ... or matchN(req))
// noop(IU)-> ~ABS
// IU -> (noop(IU) or ABS)
// Therefore we only need one optional requirement statement per IU
List optionalAbstractRequirements = new ArrayList();
for (int i = 0; i < reqs.length; i++) {
//The requirement is unchanged
if (reqs[i][0] == reqs[i][1]) {
if (!isApplicable(reqs[i][0]))
continue;
List patchesAppliedElseWhere = (List) unchangedRequirements.get(reqs[i][0]);
if (patchesAppliedElseWhere == null) {
patchesAppliedElseWhere = new ArrayList();
unchangedRequirements.put(reqs[i][0], patchesAppliedElseWhere);
}
patchesAppliedElseWhere.add(patch);
continue;
}
//Generate dependency when the patch is applied
//P1 -> (A -> D) equiv. (P1 & A) -> D
if (isApplicable(reqs[i][1])) {
IRequiredCapability req = reqs[i][1];
List matches = getApplicableMatches(req);
if (isHostRequirement(iu, req)) {
rememberHostMatches(iu, matches);
}
if (!req.isOptional()) {
if (matches.isEmpty()) {
missingRequirement(patch, req);
} else {
IInstallableUnit reqIu = (IInstallableUnit) picker.query(new CapabilityQuery(req), new Collector(), null).iterator().next();
Explanation explanation;
if (isRootIu) {
if (alreadyInstalledIUs.contains(reqIu)) {
explanation = new Explanation.IUInstalled(reqIu);
} else {
explanation = new Explanation.IUToInstall(reqIu);
}
} else {
explanation = new Explanation.PatchedHardRequirement(iu, req, patch);
}
createImplication(new Object[] {patch, iu}, matches, explanation);
}
} else {
if (!matches.isEmpty()) {
AbstractVariable abs = getAbstractVariable();
createImplication(new Object[] {patch, abs, iu}, matches, Explanation.OPTIONAL_REQUIREMENT);
optionalAbstractRequirements.add(abs);
}
}
}
//Generate dependency when the patch is not applied
//-P1 -> (A -> B) ( equiv. A -> (P1 or B) )
if (isApplicable(reqs[i][0])) {
IRequiredCapability req = reqs[i][0];
List matches = getApplicableMatches(req);
if (isHostRequirement(iu, req)) {
rememberHostMatches(iu, matches);
}
if (!req.isOptional()) {
if (matches.isEmpty()) {
dependencyHelper.implication(new Object[] {iu}).implies(patch).named(new Explanation.HardRequirement(iu, null));
} else {
matches.add(patch);
IInstallableUnit reqIu = (IInstallableUnit) picker.query(new CapabilityQuery(req), new Collector(), null).iterator().next();
Explanation explanation;
if (isRootIu) {
if (alreadyInstalledIUs.contains(reqIu)) {
explanation = new Explanation.IUInstalled(reqIu);
} else {
explanation = new Explanation.IUToInstall(reqIu);
}
} else {
explanation = new Explanation.HardRequirement(iu, req);
}
createImplication(iu, matches, explanation);
}
} else {
if (!matches.isEmpty()) {
AbstractVariable abs = getAbstractVariable();
matches.add(patch);
createImplication(new Object[] {abs, iu}, matches, Explanation.OPTIONAL_REQUIREMENT);
optionalAbstractRequirements.add(abs);
}
}
}
}
createOptionalityExpression(iu, optionalAbstractRequirements);
}
List optionalAbstractRequirements = new ArrayList();
for (Iterator iterator = unchangedRequirements.entrySet().iterator(); iterator.hasNext();) {
Entry entry = (Entry) iterator.next();
List patchesApplied = (List) entry.getValue();
List allPatches = new ArrayList(applicablePatches.toCollection());
allPatches.removeAll(patchesApplied);
List requiredPatches = new ArrayList();
for (Iterator iterator2 = allPatches.iterator(); iterator2.hasNext();) {
IInstallableUnitPatch patch = (IInstallableUnitPatch) iterator2.next();
requiredPatches.add(patch);
}
IRequiredCapability req = (IRequiredCapability) entry.getKey();
List matches = getApplicableMatches(req);
if (isHostRequirement(iu, req)) {
rememberHostMatches(iu, matches);
}
if (!req.isOptional()) {
if (matches.isEmpty()) {
if (requiredPatches.isEmpty()) {
missingRequirement(iu, req);
} else {
createImplication(iu, requiredPatches, new Explanation.HardRequirement(iu, req));
}
} else {
if (!requiredPatches.isEmpty())
matches.addAll(requiredPatches);
IInstallableUnit reqIu = (IInstallableUnit) picker.query(new CapabilityQuery(req), new Collector(), null).iterator().next();
Explanation explanation;
if (isRootIu) {
if (alreadyInstalledIUs.contains(reqIu)) {
explanation = new Explanation.IUInstalled(reqIu);
} else {
explanation = new Explanation.IUToInstall(reqIu);
}
} else {
explanation = new Explanation.HardRequirement(iu, req);
}
createImplication(iu, matches, explanation);
}
} else {
if (!matches.isEmpty()) {
if (!requiredPatches.isEmpty())
matches.addAll(requiredPatches);
AbstractVariable abs = getAbstractVariable();
createImplication(new Object[] {abs, iu}, matches, Explanation.OPTIONAL_REQUIREMENT);
optionalAbstractRequirements.add(abs);
}
}
}
createOptionalityExpression(iu, optionalAbstractRequirements);
}
private void expandLifeCycle(IInstallableUnit iu, boolean isRootIu) throws ContradictionException {
if (!(iu instanceof IInstallableUnitPatch))
return;
IInstallableUnitPatch patch = (IInstallableUnitPatch) iu;
IRequiredCapability req = patch.getLifeCycle();
if (req == null)
return;
expandRequirement(req, iu, Collections.EMPTY_LIST, isRootIu);
}
private void missingRequirement(IInstallableUnit iu, IRequiredCapability req) throws ContradictionException {
result.add(new Status(IStatus.WARNING, DirectorActivator.PI_DIRECTOR, NLS.bind(Messages.Planner_Unsatisfied_dependency, iu, req)));
createNegation(iu, req);
}
/**
* @param req
* @return a list of mandatory requirements if any, an empty list if req.isOptional().
*/
private List getApplicableMatches(IRequiredCapability req) {
List target = new ArrayList();
Collector matches = picker.query(new CapabilityQuery(req), new Collector(), null);
for (Iterator iterator = matches.iterator(); iterator.hasNext();) {
IInstallableUnit match = (IInstallableUnit) iterator.next();
if (isApplicable(match)) {
target.add(match);
}
}
return target;
}
//Return a new array of requirements representing the application of the patch
private IRequiredCapability[][] mergeRequirements(IInstallableUnit iu, IInstallableUnitPatch patch) {
if (patch == null)
return null;
IRequirementChange[] changes = patch.getRequirementsChange();
IRequiredCapability[] originalRequirements = new IRequiredCapability[iu.getRequiredCapabilities().length];
System.arraycopy(iu.getRequiredCapabilities(), 0, originalRequirements, 0, originalRequirements.length);
List rrr = new ArrayList();
boolean found = false;
for (int i = 0; i < changes.length; i++) {
for (int j = 0; j < originalRequirements.length; j++) {
if (originalRequirements[j] != null && changes[i].matches(originalRequirements[j])) {
found = true;
if (changes[i].newValue() != null)
rrr.add(new IRequiredCapability[] {originalRequirements[j], changes[i].newValue()});
else
// case where a requirement is removed
rrr.add(new IRequiredCapability[] {originalRequirements[j], null});
originalRequirements[j] = null;
}
// break;
}
if (!found && changes[i].applyOn() == null && changes[i].newValue() != null) //Case where a new requirement is added
rrr.add(new IRequiredCapability[] {null, changes[i].newValue()});
}
//Add all the unmodified requirements to the result
for (int i = 0; i < originalRequirements.length; i++) {
if (originalRequirements[i] != null)
rrr.add(new IRequiredCapability[] {originalRequirements[i], originalRequirements[i]});
}
return (IRequiredCapability[][]) rrr.toArray(new IRequiredCapability[rrr.size()][]);
}
/**
* Optional requirements are encoded via:
* ABS -> (match1(req) or match2(req) or ... or matchN(req))
* noop(IU)-> ~ABS
* IU -> (noop(IU) or ABS)
* @param iu
* @param optionalRequirements
* @throws ContradictionException
*/
private void createOptionalityExpression(IInstallableUnit iu, List optionalRequirements) throws ContradictionException {
if (optionalRequirements.isEmpty())
return;
AbstractVariable noop = getNoOperationVariable(iu);
for (Iterator i = optionalRequirements.iterator(); i.hasNext();) {
AbstractVariable abs = (AbstractVariable) i.next();
createIncompatibleValues(abs, noop);
}
optionalRequirements.add(noop);
createImplication(iu, optionalRequirements, Explanation.OPTIONAL_REQUIREMENT);
}
private void createImplication(Object left, List right, Explanation name) throws ContradictionException {
if (DEBUG) {
Tracing.debug(name + ": " + left + "->" + right); //$NON-NLS-1$ //$NON-NLS-2$
}
dependencyHelper.implication(new Object[] {left}).implies(right.toArray()).named(name);
}
private void createImplication(Object[] left, List right, Explanation name) throws ContradictionException {
if (DEBUG) {
Tracing.debug(name + ": " + Arrays.asList(left) + "->" + right); //$NON-NLS-1$ //$NON-NLS-2$
}
dependencyHelper.implication(left).implies(right.toArray()).named(name);
}
//Return IUPatches that are applicable for the given iu
private Collector getApplicablePatches(IInstallableUnit iu) {
if (patches == null)
patches = new QueryableArray((IInstallableUnit[]) picker.query(ApplicablePatchQuery.ANY, new Collector(), null).toArray(IInstallableUnit.class));
return patches.query(new ApplicablePatchQuery(iu), new Collector(), null);
}
//Create constraints to deal with singleton
//When there is a mix of singleton and non singleton, several constraints are generated
private void createConstraintsForSingleton() throws ContradictionException {
Set s = slice.entrySet();
for (Iterator iterator = s.iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
HashMap conflictingEntries = (HashMap) entry.getValue();
if (conflictingEntries.size() < 2)
continue;
Collection conflictingVersions = conflictingEntries.values();
List singletons = new ArrayList();
List nonSingletons = new ArrayList();
for (Iterator conflictIterator = conflictingVersions.iterator(); conflictIterator.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) conflictIterator.next();
if (iu.isSingleton()) {
singletons.add(iu);
} else {
nonSingletons.add(iu);
}
}
if (singletons.isEmpty())
continue;
IInstallableUnit[] singletonArray;
if (nonSingletons.isEmpty()) {
singletonArray = (IInstallableUnit[]) singletons.toArray(new IInstallableUnit[singletons.size()]);
createAtMostOne(singletonArray);
} else {
singletonArray = (IInstallableUnit[]) singletons.toArray(new IInstallableUnit[singletons.size() + 1]);
for (Iterator iterator2 = nonSingletons.iterator(); iterator2.hasNext();) {
singletonArray[singletonArray.length - 1] = (IInstallableUnit) iterator2.next();
createAtMostOne(singletonArray);
}
}
}
}
private void createAtMostOne(IInstallableUnit[] ius) throws ContradictionException {
if (DEBUG) {
StringBuffer b = new StringBuffer();
for (int i = 0; i < ius.length; i++) {
b.append(ius[i].toString());
}
Tracing.debug("At most 1 of " + b); //$NON-NLS-1$
}
dependencyHelper.atMost(1, ius).named(new Explanation.Singleton(ius));
}
private void createIncompatibleValues(AbstractVariable v1, AbstractVariable v2) throws ContradictionException {
AbstractVariable[] vars = {v1, v2};
if (DEBUG) {
StringBuffer b = new StringBuffer();
for (int i = 0; i < vars.length; i++) {
b.append(vars[i].toString());
}
Tracing.debug("At most 1 of " + b); //$NON-NLS-1$
}
dependencyHelper.atMost(1, vars).named(Explanation.OPTIONAL_REQUIREMENT);
}
private AbstractVariable getAbstractVariable() {
AbstractVariable abstractVariable = new AbstractVariable();
abstractVariables.add(abstractVariable);
return abstractVariable;
}
private AbstractVariable getNoOperationVariable(IInstallableUnit iu) {
AbstractVariable v = (AbstractVariable) noopVariables.get(iu);
if (v == null) {
v = new AbstractVariable();
noopVariables.put(iu, v);
}
return v;
}
public IStatus invokeSolver(IProgressMonitor monitor) {
if (result.getSeverity() == IStatus.ERROR)
return result;
// CNF filename is given on the command line
long start = System.currentTimeMillis();
if (DEBUG)
Tracing.debug("Invoking solver: " + start); //$NON-NLS-1$
try {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
if (dependencyHelper.hasASolution(assumptions)) {
if (DEBUG) {
Tracing.debug("Satisfiable !"); //$NON-NLS-1$
}
backToIU();
long stop = System.currentTimeMillis();
if (DEBUG)
Tracing.debug("Solver solution found: " + (stop - start)); //$NON-NLS-1$
} else {
long stop = System.currentTimeMillis();
if (DEBUG) {
Tracing.debug("Unsatisfiable !"); //$NON-NLS-1$
Tracing.debug("Solver solution NOT found: " + (stop - start)); //$NON-NLS-1$
}
result.merge(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Planner_Unsatisfiable_problem));
}
} catch (TimeoutException e) {
result.merge(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Planner_Timeout));
} catch (Exception e) {
result.merge(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Planner_Unexpected_problem, e));
}
if (DEBUG)
System.out.println();
return result;
}
private void backToIU() {
solution = new ArrayList();
IVec sat4jSolution = dependencyHelper.getSolution();
for (Iterator i = sat4jSolution.iterator(); i.hasNext();) {
Object var = i.next();
if (var instanceof IInstallableUnit) {
IInstallableUnit iu = (IInstallableUnit) var;
if (iu == entryPoint)
continue;
solution.add(iu);
}
}
}
private void printSolution(Collection state) {
ArrayList l = new ArrayList(state);
Collections.sort(l);
Tracing.debug("Solution:"); //$NON-NLS-1$
Tracing.debug("Numbers of IUs selected: " + l.size()); //$NON-NLS-1$
for (Iterator iterator = l.iterator(); iterator.hasNext();) {
Tracing.debug(iterator.next().toString());
}
}
public Collection extractSolution() {
if (DEBUG)
printSolution(solution);
return solution;
}
public Set getExplanation(IProgressMonitor monitor) {
ExplanationJob job = new ExplanationJob();
job.schedule();
monitor.setTaskName(Messages.Planner_NoSolution);
IProgressMonitor pm = new InfiniteProgress(monitor);
pm.beginTask(Messages.Planner_NoSolution, 1000);
try {
synchronized (job) {
while (job.getExplanationResult() == null && job.getState() != Job.NONE) {
if (monitor.isCanceled()) {
job.cancel();
throw new OperationCanceledException();
}
pm.worked(1);
try {
job.wait(100);
} catch (InterruptedException e) {
if (DEBUG)
Tracing.debug("Interrupted while computing explanations"); //$NON-NLS-1$
}
}
}
} finally {
monitor.done();
}
return job.getExplanationResult();
}
public Map getFragmentAssociation() {
Map resolvedFragments = new HashMap(fragments.size());
for (Iterator iterator = fragments.entrySet().iterator(); iterator.hasNext();) {
Entry fragment = (Entry) iterator.next();
if (!dependencyHelper.getBooleanValueFor(fragment.getKey()))
continue;
Set potentialHosts = (Set) fragment.getValue();
List resolvedHost = new ArrayList(potentialHosts.size());
for (Iterator iterator2 = potentialHosts.iterator(); iterator2.hasNext();) {
Object host = iterator2.next();
if (dependencyHelper.getBooleanValueFor(host))
resolvedHost.add(host);
}
if (resolvedHost.size() != 0)
resolvedFragments.put(fragment.getKey(), resolvedHost);
}
return resolvedFragments;
}
private void rememberHostMatches(IInstallableUnit fragment, List matches) {
Set existingMatches = (Set) fragments.get(fragment);
if (existingMatches == null) {
existingMatches = new HashSet();
fragments.put(fragment, existingMatches);
existingMatches.addAll(matches);
}
existingMatches.retainAll(matches);
}
private boolean isHostRequirement(IInstallableUnit iu, IRequiredCapability req) {
if (!(iu instanceof IInstallableUnitFragment))
return false;
IInstallableUnitFragment fragment = (IInstallableUnitFragment) iu;
IRequiredCapability[] reqs = fragment.getHost();
for (int i = 0; i < reqs.length; i++) {
if (req == reqs[i])
return true;
}
return true;
}
}