blob: 85910f22d0a6733ddfba3c164f72ea2506497024 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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
*******************************************************************************/
package org.eclipse.equinox.internal.p2.director;
import java.util.*;
import org.eclipse.equinox.internal.provisional.p2.engine.InstallableUnitOperand;
import org.eclipse.equinox.internal.provisional.p2.metadata.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.InstallableUnitQuery;
import org.eclipse.equinox.internal.provisional.p2.query.Collector;
public class OperationGenerator {
private static final IInstallableUnit NULL_IU = MetadataFactory.createResolvedInstallableUnit(MetadataFactory.createInstallableUnit(new InstallableUnitDescription()), new IInstallableUnitFragment[0]);
public InstallableUnitOperand[] generateOperation(Collection from_, Collection to_) {
Collection intersection = new HashSet(from_);
intersection.retainAll(to_);
HashSet tmpFrom = new HashSet(from_);
HashSet tmpTo = new HashSet(to_);
tmpFrom.removeAll(intersection);
tmpTo.removeAll(intersection);
List from = new ArrayList(tmpFrom);
Collections.sort(from);
List to = new ArrayList(tmpTo);
Collections.sort(to);
ArrayList operations = new ArrayList();
generateUpdates(from, to, operations);
generateInstallUninstall(from, to, operations);
generateConfigurationChanges(to_, intersection, operations);
InstallableUnitOperand[] ops = (InstallableUnitOperand[]) operations.toArray(new InstallableUnitOperand[operations.size()]);
return ops;
}
//This generates operations that are causing the IUs to be reconfigured.
private void generateConfigurationChanges(Collection to_, Collection intersection, ArrayList operations) {
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 to = new TreeSet(to_);
for (Iterator iterator = intersection.iterator(); iterator.hasNext();) {
IInstallableUnit fromIU = (IInstallableUnit) iterator.next();
IInstallableUnit toIU = (IInstallableUnit) to.tailSet(fromIU).first();
generateConfigurationOperation(fromIU, toIU, operations);
}
}
private void generateConfigurationOperation(IInstallableUnit fromIU, IInstallableUnit toIU, ArrayList operations) {
IInstallableUnitFragment[] fromFragments = fromIU.getFragments();
IInstallableUnitFragment[] toFragments = toIU.getFragments();
if (fromFragments == toFragments)
return;
//Check to see if the two arrays are equals independently of the order of the fragments
boolean different = false;
if (fromFragments != null && toFragments != null) {
if (fromFragments.length != toFragments.length)
different = true;
else {
for (int i = 0; i < fromFragments.length; i++) {
boolean found = false;
for (int j = 0; j < toFragments.length; j++) {
if (fromFragments[i].equals(toFragments[j]))
found = true;
}
if (!found) {
different = true;
break;
}
}
}
} else {
//One of the two array is null
different = true;
}
if (!different)
return;
operations.add(new InstallableUnitOperand(fromIU, toIU));
}
private void generateInstallUninstall(List from, List to, ArrayList operations) {
int toIdx = 0;
int fromIdx = 0;
while (fromIdx != from.size() && toIdx != to.size()) {
IInstallableUnit fromIU = (IInstallableUnit) from.get(fromIdx);
IInstallableUnit toIU = (IInstallableUnit) to.get(toIdx);
int comparison = toIU.compareTo(fromIU);
if (comparison < 0) {
operations.add(createInstallOperation(toIU));
toIdx++;
} else if (comparison == 0) {
toIdx++;
fromIdx++;
// System.out.println("same " + fromIU);
} else {
operations.add(createUninstallOperation(fromIU));
fromIdx++;
}
}
if (fromIdx != from.size()) {
for (int i = fromIdx; i < from.size(); i++) {
operations.add(createUninstallOperation((IInstallableUnit) from.get(i)));
}
}
if (toIdx != to.size()) {
for (int i = toIdx; i < to.size(); i++) {
operations.add(createInstallOperation((IInstallableUnit) to.get(i)));
}
}
}
private void generateUpdates(List from, List to, ArrayList operations) {
Set processed = new HashSet();
Set removedFromTo = new HashSet();
if (to.isEmpty() || from.isEmpty())
return;
Map fromById = new HashMap();
for (Iterator iterator = from.iterator(); iterator.hasNext();) {
IInstallableUnit iuFrom = (IInstallableUnit) iterator.next();
List ius = (List) fromById.get(iuFrom.getId());
if (ius == null) {
ius = new ArrayList();
fromById.put(iuFrom.getId(), ius);
}
ius.add(iuFrom);
}
for (int toIdx = 0; toIdx < to.size(); toIdx++) {
IInstallableUnit iuTo = (IInstallableUnit) 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;
List fromIdIndexList = (List) fromById.get(iuTo.getUpdateDescriptor().getId());
if (fromIdIndexList == null)
continue;
//when the ui we update from is in the new state, skip (for example FROM is A, C, B & TO is C (update of
InstallableUnitQuery updateQuery = new InstallableUnitQuery(iuTo.getUpdateDescriptor().getId(), iuTo.getUpdateDescriptor().getRange());
Iterator updates = updateQuery.perform(fromIdIndexList.iterator(), new Collector()).iterator();
if (!updates.hasNext()) { //Nothing to update from.
continue;
}
IInstallableUnit iuFrom = (IInstallableUnit) updates.next();
if (updates.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;
}
operations.add(createUpdateOperation(iuFrom, iuTo));
from.remove(iuFrom);
fromIdIndexList.remove(iuFrom);
processed.add(iuTo);
}
to.removeAll(processed);
to.removeAll(removedFromTo);
}
private InstallableUnitOperand createUninstallOperation(IInstallableUnit iu) {
return new InstallableUnitOperand(iu, null);
}
private InstallableUnitOperand createInstallOperation(IInstallableUnit iu) {
return new InstallableUnitOperand(null, iu);
}
private InstallableUnitOperand createUpdateOperation(IInstallableUnit from, IInstallableUnit to) {
return new InstallableUnitOperand(from, to);
}
private IInstallableUnit next(List l, int i) {
i++;
if (i >= l.size())
return NULL_IU;
return (IInstallableUnit) l.get(i);
}
private int skip(List c, IInstallableUnit id, int idx) {
int i = idx;
for (; i < c.size(); i++) {
if (!id.getId().equals(((IInstallableUnit) c.get(i)).getId()))
return i;
}
return i;
}
}