blob: 1929396c0cc3436aaca821189c94f9a1e57c0148 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2020 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.p2.director;
import java.util.*;
import org.eclipse.equinox.p2.engine.IProvisioningPlan;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.query.*;
public class OperationGenerator {
private static final IInstallableUnit NULL_IU = MetadataFactory.createResolvedInstallableUnit(MetadataFactory.createInstallableUnit(new MetadataFactory.InstallableUnitDescription()), new IInstallableUnitFragment[0]);
private final IProvisioningPlan plan;
public OperationGenerator(IProvisioningPlan plan) {
this.plan = plan;
}
public void generateOperation(Collection<IInstallableUnit> from_, Collection<IInstallableUnit> to_) {
Collection<IInstallableUnit> intersection = new HashSet<>(from_);
intersection.retainAll(to_);
HashSet<IInstallableUnit> tmpFrom = new HashSet<>(from_);
HashSet<IInstallableUnit> tmpTo = new HashSet<>(to_);
tmpFrom.removeAll(intersection);
tmpTo.removeAll(intersection);
List<IInstallableUnit> from = new ArrayList<>(tmpFrom);
from.sort(null);
List<IInstallableUnit> to = new ArrayList<>(tmpTo);
to.sort(null);
generateUpdates(from, to);
generateInstallUninstall(from, to);
generateConfigurationChanges(to_, intersection);
}
//This generates operations that are causing the IUs to be reconfigured.
private void generateConfigurationChanges(Collection<IInstallableUnit> to_, Collection<IInstallableUnit> intersection) {
if (intersection.size() == 0)
return;
//We retain from each set the things that are the same.
//Note that despite the fact that they are the same, a different CU can be attached.
//The objects contained in the intersection are the one that were originally in the from collection.
TreeSet<IInstallableUnit> to = new TreeSet<>(to_);
for (IInstallableUnit fromIU : intersection) {
IInstallableUnit toIU = to.tailSet(fromIU).first();
generateConfigurationOperation(fromIU, toIU);
}
}
private void generateConfigurationOperation(IInstallableUnit fromIU, IInstallableUnit toIU) {
Collection<IInstallableUnitFragment> fromFragments = fromIU.getFragments();
Collection<IInstallableUnitFragment> toFragments = toIU.getFragments();
if (fromFragments == toFragments)
return;
//Check to see if the two arrays are equals independently of the order of the fragments
if (fromFragments.size() == toFragments.size() && fromFragments.containsAll(toFragments))
return;
plan.updateInstallableUnit(fromIU, toIU);
}
private void generateInstallUninstall(List<IInstallableUnit> from, List<IInstallableUnit> to) {
int toIdx = 0;
int fromIdx = 0;
while (fromIdx != from.size() && toIdx != to.size()) {
IInstallableUnit fromIU = from.get(fromIdx);
IInstallableUnit toIU = to.get(toIdx);
int comparison = toIU.compareTo(fromIU);
if (comparison < 0) {
plan.addInstallableUnit(toIU);
toIdx++;
} else if (comparison == 0) {
toIdx++;
fromIdx++;
// System.out.println("same " + fromIU);
} else {
plan.removeInstallableUnit(fromIU);
fromIdx++;
}
}
if (fromIdx != from.size()) {
for (int i = fromIdx; i < from.size(); i++) {
plan.removeInstallableUnit(from.get(i));
}
}
if (toIdx != to.size()) {
for (int i = toIdx; i < to.size(); i++) {
plan.addInstallableUnit(to.get(i));
}
}
}
private void generateUpdates(List<IInstallableUnit> from, List<IInstallableUnit> to) {
if (to.isEmpty() || from.isEmpty())
return;
Set<IInstallableUnit> processed = new HashSet<>();
Set<IInstallableUnit> removedFromTo = new HashSet<>();
QueryableArray indexedFromElements = new QueryableArray(from.toArray(new IInstallableUnit[from.size()]));
for (int toIdx = 0; toIdx < to.size(); toIdx++) {
IInstallableUnit iuTo = to.get(toIdx);
if (iuTo.getId().equals(next(to, toIdx).getId())) { //This handle the case where there are multiple versions of the same IU in the target. Eg we are trying to update from A 1.0.0 to A 1.1.1 and A 1.2.2
toIdx = skip(to, iuTo, toIdx) - 1;
//System.out.println("Can't update " + iuTo + " because another iu with same id is in the target state");
continue;
}
if (iuTo.getUpdateDescriptor() == null)
continue;
//TODO we eventually need to handle the case where an IU is a merge of several others.
IQuery<IInstallableUnit> updateQuery = QueryUtil.createMatchQuery(iuTo.getUpdateDescriptor().getIUsBeingUpdated().iterator().next(), new Object[0]);
iuTo.getUpdateDescriptor().getIUsBeingUpdated();
IQueryResult<IInstallableUnit> updates = indexedFromElements.query(updateQuery, null);
if (updates.isEmpty()) { //Nothing to update from.
continue;
}
Iterator<IInstallableUnit> updatesIterator = updates.iterator();
IInstallableUnit iuFrom = updatesIterator.next();
if (updatesIterator.hasNext()) { //There are multiple IUs to update from
//System.out.println("Can't update " + iuTo + " because there are multiple IUs to update from (" + toString(iusFrom) + ')');
continue;
}
if (iuTo.equals(iuFrom)) {
from.remove(iuFrom);
// fromIdIndexList.remove(iuFrom);
removedFromTo.add(iuTo);
continue;
}
plan.updateInstallableUnit(iuFrom, iuTo);
from.remove(iuFrom);
// fromIdIndexList.remove(iuFrom);
processed.add(iuTo);
}
to.removeAll(processed);
to.removeAll(removedFromTo);
}
private IInstallableUnit next(List<IInstallableUnit> l, int i) {
i++;
if (i >= l.size())
return NULL_IU;
return l.get(i);
}
private int skip(List<IInstallableUnit> c, IInstallableUnit id, int idx) {
int i = idx;
for (; i < c.size(); i++) {
if (!id.getId().equals(c.get(i).getId()))
return i;
}
return i;
}
}