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