progress on sorting operands
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ComputeNodeOrder.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ComputeNodeOrder.java new file mode 100644 index 0000000..68be277 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ComputeNodeOrder.java
@@ -0,0 +1,503 @@ +package org.eclipse.equinox.internal.p2.engine; + +import java.util.*; + +/** + * Borrowed from org.eclipse.core.internal.resources.ComputeProjectOrder + * to be used when computing the stop order. + * Implementation of a sort algorithm for computing the node order. This + * algorithm handles cycles in the node reference graph in a reasonable way. + * + * @since 3.0 + */ +public class ComputeNodeOrder { + + /* + * Prevent class from being instantiated. + */ + private ComputeNodeOrder() { + // not allowed + } + + /** + * A directed graph. Once the vertexes and edges of the graph have been + * defined, the graph can be queried for the depth-first finish time of each + * vertex. + * <p> + * Ref: Cormen, Leiserson, and Rivest <it>Introduction to Algorithms</it>, + * McGraw-Hill, 1990. The depth-first search algorithm is in section 23.3. + * </p> + */ + private static class Digraph { + /** + * struct-like object for representing a vertex along with various + * values computed during depth-first search (DFS). + */ + public static class Vertex { + /** + * White is for marking vertexes as unvisited. + */ + public static final String WHITE = "white"; //$NON-NLS-1$ + + /** + * Grey is for marking vertexes as discovered but visit not yet + * finished. + */ + public static final String GREY = "grey"; //$NON-NLS-1$ + + /** + * Black is for marking vertexes as visited. + */ + public static final String BLACK = "black"; //$NON-NLS-1$ + + /** + * Color of the vertex. One of <code>WHITE</code> (unvisited), + * <code>GREY</code> (visit in progress), or <code>BLACK</code> + * (visit finished). <code>WHITE</code> initially. + */ + public String color = WHITE; + + /** + * The DFS predecessor vertex, or <code>null</code> if there is no + * predecessor. <code>null</code> initially. + */ + public Vertex predecessor = null; + + /** + * Timestamp indicating when the vertex was finished (became BLACK) + * in the DFS. Finish times are between 1 and the number of + * vertexes. + */ + public int finishTime; + + /** + * The id of this vertex. + */ + public Object id; + + /** + * Ordered list of adjacent vertexes. In other words, "this" is the + * "from" vertex and the elements of this list are all "to" + * vertexes. + * + * Element type: <code>Vertex</code> + */ + public List<Vertex> adjacent = new ArrayList<Vertex>(3); + + /** + * Creates a new vertex with the given id. + * + * @param id the vertex id + */ + public Vertex(Object id) { + this.id = id; + } + } + + /** + * Ordered list of all vertexes in this graph. + * + * Element type: <code>Vertex</code> + */ + private List<Vertex> vertexList = new ArrayList<Vertex>(100); + + /** + * Map from id to vertex. + * + * Key type: <code>Object</code>; value type: <code>Vertex</code> + */ + private Map<Object, Vertex> vertexMap = new HashMap<Object, Vertex>(100); + + /** + * DFS visit time. Non-negative. + */ + private int time; + + /** + * Indicates whether the graph has been initialized. Initially + * <code>false</code>. + */ + private boolean initialized = false; + + /** + * Indicates whether the graph contains cycles. Initially + * <code>false</code>. + */ + private boolean cycles = false; + + /** + * Creates a new empty directed graph object. + * <p> + * After this graph's vertexes and edges are defined with + * <code>addVertex</code> and <code>addEdge</code>, call + * <code>freeze</code> to indicate that the graph is all there, and then + * call <code>idsByDFSFinishTime</code> to read off the vertexes ordered + * by DFS finish time. + * </p> + */ + public Digraph() { + super(); + } + + /** + * Freezes this graph. No more vertexes or edges can be added to this + * graph after this method is called. Has no effect if the graph is + * already frozen. + */ + public void freeze() { + if (!initialized) { + initialized = true; + // only perform depth-first-search once + DFS(); + } + } + + /** + * Defines a new vertex with the given id. The depth-first search is + * performed in the relative order in which vertexes were added to the + * graph. + * + * @param id the id of the vertex + * @exception IllegalArgumentException if the vertex id is + * already defined or if the graph is frozen + */ + public void addVertex(Object id) throws IllegalArgumentException { + if (initialized) { + throw new IllegalArgumentException(); + } + Vertex vertex = new Vertex(id); + Object existing = vertexMap.put(id, vertex); + // nip problems with duplicate vertexes in the bud + if (existing != null) { + throw new IllegalArgumentException(); + } + vertexList.add(vertex); + } + + /** + * Adds a new directed edge between the vertexes with the given ids. + * Vertexes for the given ids must be defined beforehand with + * <code>addVertex</code>. The depth-first search is performed in the + * relative order in which adjacent "to" vertexes were added to a given + * "from" index. + * + * @param fromId the id of the "from" vertex + * @param toId the id of the "to" vertex + * @exception IllegalArgumentException if either vertex is undefined or + * if the graph is frozen + */ + public void addEdge(Object fromId, Object toId) throws IllegalArgumentException { + if (initialized) { + throw new IllegalArgumentException(); + } + Vertex fromVertex = vertexMap.get(fromId); + Vertex toVertex = vertexMap.get(toId); + // ignore edges when one of the vertices is unknown + if (fromVertex == null || toVertex == null) + return; + fromVertex.adjacent.add(toVertex); + } + + /** + * Returns the ids of the vertexes in this graph ordered by depth-first + * search finish time. The graph must be frozen. + * + * @param increasing <code>true</code> if objects are to be arranged + * into increasing order of depth-first search finish time, and + * <code>false</code> if objects are to be arranged into decreasing + * order of depth-first search finish time + * @return the list of ids ordered by depth-first search finish time + * (element type: <code>Object</code>) + * @exception IllegalArgumentException if the graph is not frozen + */ + public List<Object> idsByDFSFinishTime(boolean increasing) { + if (!initialized) { + throw new IllegalArgumentException(); + } + int len = vertexList.size(); + Object[] r = new Object[len]; + for (Iterator<Vertex> allV = vertexList.iterator(); allV.hasNext();) { + Vertex vertex = allV.next(); + int f = vertex.finishTime; + // note that finish times start at 1, not 0 + if (increasing) { + r[f - 1] = vertex.id; + } else { + r[len - f] = vertex.id; + } + } + return Arrays.asList(r); + } + + /** + * Returns whether the graph contains cycles. The graph must be frozen. + * + * @return <code>true</code> if this graph contains at least one cycle, + * and <code>false</code> if this graph is cycle free + * @exception IllegalArgumentException if the graph is not frozen + */ + public boolean containsCycles() { + if (!initialized) { + throw new IllegalArgumentException(); + } + return cycles; + } + + /** + * Returns the non-trivial components of this graph. A non-trivial + * component is a set of 2 or more vertexes that were traversed + * together. The graph must be frozen. + * + * @return the possibly empty list of non-trivial components, where + * each component is an array of ids (element type: + * <code>Object[]</code>) + * @exception IllegalArgumentException if the graph is not frozen + */ + public List<Object[]> nonTrivialComponents() { + if (!initialized) { + throw new IllegalArgumentException(); + } + // find the roots of each component + // Map<Vertex,List<Object>> components + Map<Vertex, List<Object>> components = new HashMap<Vertex, List<Object>>(); + for (Iterator<Vertex> it = vertexList.iterator(); it.hasNext();) { + Vertex vertex = it.next(); + if (vertex.predecessor == null) { + // this vertex is the root of a component + // if component is non-trivial we will hit a child + } else { + // find the root ancestor of this vertex + Vertex root = vertex; + while (root.predecessor != null) { + root = root.predecessor; + } + List<Object> component = components.get(root); + if (component == null) { + component = new ArrayList<Object>(2); + component.add(root.id); + components.put(root, component); + } + component.add(vertex.id); + } + } + List<Object[]> result = new ArrayList<Object[]>(components.size()); + for (Iterator<List<Object>> it = components.values().iterator(); it.hasNext();) { + List<Object> component = it.next(); + if (component.size() > 1) { + result.add(component.toArray()); + } + } + return result; + } + + // /** + // * Performs a depth-first search of this graph and records interesting + // * info with each vertex, including DFS finish time. Employs a recursive + // * helper method <code>DFSVisit</code>. + // * <p> + // * Although this method is not used, it is the basis of the + // * non-recursive <code>DFS</code> method. + // * </p> + // */ + // private void recursiveDFS() { + // // initialize + // // all vertex.color initially Vertex.WHITE; + // // all vertex.predecessor initially null; + // time = 0; + // for (Iterator allV = vertexList.iterator(); allV.hasNext();) { + // Vertex nextVertex = (Vertex) allV.next(); + // if (nextVertex.color == Vertex.WHITE) { + // DFSVisit(nextVertex); + // } + // } + // } + // + // /** + // * Helper method. Performs a depth first search of this graph. + // * + // * @param vertex the vertex to visit + // */ + // private void DFSVisit(Vertex vertex) { + // // mark vertex as discovered + // vertex.color = Vertex.GREY; + // List adj = vertex.adjacent; + // for (Iterator allAdjacent=adj.iterator(); allAdjacent.hasNext();) { + // Vertex adjVertex = (Vertex) allAdjacent.next(); + // if (adjVertex.color == Vertex.WHITE) { + // // explore edge from vertex to adjVertex + // adjVertex.predecessor = vertex; + // DFSVisit(adjVertex); + // } else if (adjVertex.color == Vertex.GREY) { + // // back edge (grey vertex means visit in progress) + // cycles = true; + // } + // } + // // done exploring vertex + // vertex.color = Vertex.BLACK; + // time++; + // vertex.finishTime = time; + // } + + /** + * Performs a depth-first search of this graph and records interesting + * info with each vertex, including DFS finish time. Does not employ + * recursion. + */ + private void DFS() { + // state machine rendition of the standard recursive DFS algorithm + int state; + final int NEXT_VERTEX = 1; + final int START_DFS_VISIT = 2; + final int NEXT_ADJACENT = 3; + final int AFTER_NEXTED_DFS_VISIT = 4; + // use precomputed objects to avoid garbage + final Integer NEXT_VERTEX_OBJECT = new Integer(NEXT_VERTEX); + final Integer AFTER_NEXTED_DFS_VISIT_OBJECT = new Integer(AFTER_NEXTED_DFS_VISIT); + // initialize + // all vertex.color initially Vertex.WHITE; + // all vertex.predecessor initially null; + time = 0; + // for a stack, append to the end of an array-based list + List<Object> stack = new ArrayList<Object>(Math.max(1, vertexList.size())); + Iterator<Vertex> allAdjacent = null; + Vertex vertex = null; + Iterator<Vertex> allV = vertexList.iterator(); + state = NEXT_VERTEX; + nextStateLoop: while (true) { + switch (state) { + case NEXT_VERTEX : + // on entry, "allV" contains vertexes yet to be visited + if (!allV.hasNext()) { + // all done + break nextStateLoop; + } + Vertex nextVertex = allV.next(); + if (nextVertex.color == Vertex.WHITE) { + stack.add(NEXT_VERTEX_OBJECT); + vertex = nextVertex; + state = START_DFS_VISIT; + continue nextStateLoop; + } + state = NEXT_VERTEX; + continue nextStateLoop; + case START_DFS_VISIT : + // on entry, "vertex" contains the vertex to be visited + // top of stack is return code + // mark the vertex as discovered + vertex.color = Vertex.GREY; + allAdjacent = vertex.adjacent.iterator(); + state = NEXT_ADJACENT; + continue nextStateLoop; + case NEXT_ADJACENT : + // on entry, "allAdjacent" contains adjacent vertexes to + // be visited; "vertex" contains vertex being visited + if (allAdjacent.hasNext()) { + Vertex adjVertex = allAdjacent.next(); + if (adjVertex.color == Vertex.WHITE) { + // explore edge from vertex to adjVertex + adjVertex.predecessor = vertex; + stack.add(allAdjacent); + stack.add(vertex); + stack.add(AFTER_NEXTED_DFS_VISIT_OBJECT); + vertex = adjVertex; + state = START_DFS_VISIT; + continue nextStateLoop; + } + if (adjVertex.color == Vertex.GREY) { + // back edge (grey means visit in progress) + cycles = true; + } + state = NEXT_ADJACENT; + continue nextStateLoop; + } + // done exploring vertex + vertex.color = Vertex.BLACK; + time++; + vertex.finishTime = time; + state = ((Integer) stack.remove(stack.size() - 1)).intValue(); + continue nextStateLoop; + case AFTER_NEXTED_DFS_VISIT : + // on entry, stack contains "vertex" and "allAjacent" + vertex = (Vertex) stack.remove(stack.size() - 1); + @SuppressWarnings("unchecked") + Iterator<Vertex> unchecked = (Iterator<Vertex>) stack.remove(stack.size() - 1); + allAdjacent = unchecked; + state = NEXT_ADJACENT; + continue nextStateLoop; + } + } + } + + } + + /** + * Sorts the given list of projects in a manner that honors the given + * project reference relationships. That is, if project A references project + * B, then the resulting order will list B before A if possible. For graphs + * that do not contain cycles, the result is the same as a conventional + * topological sort. For graphs containing cycles, the order is based on + * ordering the strongly connected components of the graph. This has the + * effect of keeping each knot of projects together without otherwise + * affecting the order of projects not involved in a cycle. For a graph G, + * the algorithm performs in O(|G|) space and time. + * <p> + * When there is an arbitrary choice, vertexes are ordered as supplied. + * Arranged projects in descending alphabetical order generally results in + * an order that builds "A" before "Z" when there are no other constraints. + * </p> + * <p> Ref: Cormen, Leiserson, and Rivest <it>Introduction to + * Algorithms</it>, McGraw-Hill, 1990. The strongly-connected-components + * algorithm is in section 23.5. + * </p> + * + * @param objects a list of projects (element type: + * <code>IProject</code>) + * @param references a list of project references [A,B] meaning that A + * references B (element type: <code>IProject[]</code>) + * @return an object describing the resulting project order + */ + public static Object[][] computeNodeOrder(Object[] objects, Object[][] references) { + + // Step 1: Create the graph object. + final Digraph g1 = new Digraph(); + // add vertexes + for (int i = 0; i < objects.length; i++) + g1.addVertex(objects[i]); + // add edges + for (int i = 0; i < references.length; i++) + // create an edge from q to p + // to cause q to come before p in eventual result + g1.addEdge(references[i][1], references[i][0]); + g1.freeze(); + + // Step 2: Create the transposed graph. This time, define the vertexes + // in decreasing order of depth-first finish time in g1 + // interchange "to" and "from" to reverse edges from g1 + final Digraph g2 = new Digraph(); + // add vertexes + List<Object> resortedVertexes = g1.idsByDFSFinishTime(false); + for (Iterator<Object> it = resortedVertexes.iterator(); it.hasNext();) + g2.addVertex(it.next()); + // add edges + for (int i = 0; i < references.length; i++) + g2.addEdge(references[i][0], references[i][1]); + g2.freeze(); + + // Step 3: Return the vertexes in increasing order of depth-first finish + // time in g2 + List<Object> sortedProjectList = g2.idsByDFSFinishTime(true); + Object[] orderedNodes = new Object[sortedProjectList.size()]; + sortedProjectList.toArray(orderedNodes); + Object[][] knots; + boolean hasCycles = g2.containsCycles(); + if (hasCycles) { + List<Object[]> knotList = g2.nonTrivialComponents(); + knots = knotList.toArray(new Object[knotList.size()][]); + } else { + knots = new Object[0][]; + } + for (int i = 0; i < orderedNodes.length; i++) + objects[i] = orderedNodes[i]; + return knots; + } +}
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Engine.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Engine.java index abe5c56..1d833f3 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Engine.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/Engine.java
@@ -62,7 +62,7 @@ = ((ProvisioningPlan) plan).getOperands(); // = ((ProvisioningPlan) plan).getOperands(); // new OperandSorter(plan.getAdditions(), true).sortBundles(installOrder); - Operand[] operandsSortedForInstall, Operand[] operandsSortedForUninstall, ProvisioningContext context, +// Operand[] operandsSortedForInstall, Operand[] operandsSortedForUninstall, ProvisioningContext context, PhaseSet phaseSet = (PhaseSet) phases; checkArguments(iprofile, phaseSet, operandsSortedForUninstall, operandsSortedForUninstall, context, monitor);
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/OperandSorter.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/OperandSorter.java new file mode 100644 index 0000000..fc1bdc3 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/OperandSorter.java
@@ -0,0 +1,137 @@ +package org.eclipse.equinox.internal.p2.engine; + +import java.util.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IRequirement; +import org.eclipse.equinox.p2.query.*; + +public class OperandSorter { + IQueryable<IInstallableUnit> allIUs; + IInstallableUnit[] sortedIUs; + boolean doingInstall = true; + Operand[] operandsToSort = null; + + Map<IInstallableUnit, Operand> iuToIUOperand = new HashMap<IInstallableUnit, Operand>(); + Map<IInstallableUnit, Operand> iuToIUPropertyOperand = new HashMap<IInstallableUnit, Operand>(); + + public OperandSorter(IQueryable<IInstallableUnit> allIUs, boolean installOrUninstall) { + this.allIUs = allIUs; + doingInstall = installOrUninstall; + } + + private void indexOperands() { + for (int i = 0; i < operandsToSort.length; i++) { + if (operandsToSort[i] instanceof InstallableUnitOperand) { + InstallableUnitOperand iuo = (InstallableUnitOperand) operandsToSort[i]; + if (iuo.first() != null) + iuToIUOperand.put(iuo.first(), iuo); + if (iuo.second() != null) + iuToIUOperand.put(iuo.second(), iuo); + } + + if (operandsToSort[i] instanceof InstallableUnitPropertyOperand) { + InstallableUnitPropertyOperand iupo = (InstallableUnitPropertyOperand) operandsToSort[i]; + iuToIUPropertyOperand.put(iupo.getInstallableUnit(), iupo); + } + } + } + + public void sortOperands(Operand[] operands) { + operandsToSort = operands; + sortedIUs = allIUs.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().toArray(new IInstallableUnit[0]); + sortIUs(); + indexOperands(); + computeInstallOrder(); + computeUninstallOrder(); + } + + private ArrayList<Operand> installOrder; + private ArrayList<Operand> uninstallOrder; + + private void computeInstallOrder() { + installOrder = new ArrayList<Operand>(); //TODO size + for (int i = 0; i < sortedIUs.length; i++) { + IInstallableUnit iInstallableUnit = sortedIUs[i]; + + Operand candidate = iuToIUPropertyOperand.get(iInstallableUnit); + if (candidate != null) + installOrder.add(iuToIUPropertyOperand.get(iInstallableUnit)); + + candidate = iuToIUOperand.get(iInstallableUnit); + if (candidate != null) + installOrder.add(iuToIUOperand.get(iInstallableUnit)); + } + } + + private void computeUninstallOrder() { + uninstallOrder = new ArrayList<Operand>(); //TODO size + for (int i = (sortedIUs.length - 1); i != 0; i--) { + IInstallableUnit iInstallableUnit = sortedIUs[i]; + + Operand candidate = iuToIUPropertyOperand.get(iInstallableUnit); + if (candidate != null) + uninstallOrder.add(iuToIUPropertyOperand.get(iInstallableUnit)); + + candidate = iuToIUOperand.get(iInstallableUnit); + if (candidate != null) + uninstallOrder.add(iuToIUOperand.get(iInstallableUnit)); + } + } + + public ArrayList<Operand> getUninstallOrder() { + assert (operandsToSort.length == uninstallOrder.size()); + return uninstallOrder; + } + + public ArrayList<Operand> getInstallOrder() { + assert (operandsToSort.length == installOrder.size()); + return installOrder; + } + + private Object[][] sortIUs() { + List<Object[]> references = new ArrayList<Object[]>(sortedIUs.length); + for (int i = 0; i < sortedIUs.length; i++) { + buildReferences(sortedIUs[i], references); + } + Object[][] cycles = ComputeNodeOrder.computeNodeOrder(sortedIUs, references.toArray(new Object[references.size()][])); + if (cycles.length == 0) + return cycles; + // // fix up host/fragment orders (bug 184127) + // for (int i = 0; i < cycles.length; i++) { + // for (int j = 0; j < cycles[i].length; j++) { + // BundleDescription fragment = (BundleDescription) cycles[i][j]; + // if (fragment.getHost() == null) + // continue; + // BundleDescription host = (BundleDescription) fragment.getHost().getSupplier(); + // if (host == null) + // continue; + // fixFragmentOrder(host, fragment, toSort); + // } + // } + return cycles; + } + + private void buildReferences(IInstallableUnit description, List<Object[]> references) { + //TODO Do we need to deal with patches in a special way? + //TODO Do we need to deal with fragmnets? + //TODO Do we need to deal with + + buildReferences(description, description.getRequirements(), references); + } + + private void buildReferences(IInstallableUnit description, Collection<IRequirement> dependencies, List<Object[]> references) { + for (IRequirement req : dependencies) { + IQueryResult<IInstallableUnit> matches = allIUs.query(QueryUtil.createMatchQuery(req.getMatches()), null); + for (Iterator<IInstallableUnit> iterator = matches.iterator(); iterator.hasNext();) { + addReference(description, iterator.next(), references); + } + } + } + + private void addReference(IInstallableUnit description, IInstallableUnit reference, List<Object[]> references) { + // build the reference from the description + if (description == reference || reference == null) + return; + references.add(new Object[] {description, reference}); + } +}
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/PhaseSet.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/PhaseSet.java index b02cc38..3f7d6d7 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/PhaseSet.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/PhaseSet.java
@@ -29,7 +29,7 @@ public final MultiStatus perform(EngineSession session, Operand[] operandsSortedForInstall, Operand[] operandsSortedForUninstall, IProgressMonitor monitor) { MultiStatus status = new MultiStatus(EngineActivator.ID, IStatus.OK, null, null); - int[] weights = getProgressWeights(operands); + int[] weights = getProgressWeights(operandsSortedForInstall); int totalWork = getTotalWork(weights); SubMonitor pm = SubMonitor.convert(monitor, totalWork); try { @@ -41,7 +41,7 @@ Phase phase = phases[i]; phase.actionManager = (ActionManager) session.getAgent().getService(ActionManager.SERVICE_NAME); try { - phase.perform(status, session, phase.getProcessingOrder() == Phase.INSTALL_ORDER ? operandsSortedForInstall : operandsSortedForUninstall, pm.newChild(weights[i])); + phase.perform(status, session, phase.getProcessingOrder() == Phase.ORDER_INSTALL ? operandsSortedForInstall : operandsSortedForUninstall, pm.newChild(weights[i])); } catch (OperationCanceledException e) { // propagate operation cancellation status.add(new Status(IStatus.CANCEL, EngineActivator.ID, e.getMessage(), e));
diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ProvisioningPlan.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ProvisioningPlan.java index 22c19a5..88e4196 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ProvisioningPlan.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/ProvisioningPlan.java
@@ -10,8 +10,6 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.engine; -import org.eclipse.equinox.internal.p2.director.OperandSorter; - import java.util.*; import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; @@ -198,12 +196,18 @@ Operand[] getOrderedPlanForInstall() { Operand[] sortedPlan = getOperands(); - new OperandSorter(new CompoundQueryable<IInstallableUnit>(new IQueryable[] { profile }new QueryablePlan(true), true).sortBundles(sortedPlan); + if (!sortOperands) + return sortedPlan; + + //TODO To change for futureState + new OperandSorter(new CompoundQueryable<IInstallableUnit>(new IQueryable[] {profile}), true).sortBundles(sortedPlan); return sortedPlan; } Operand[] getOrderedPlanForUninstall() { Operand[] sortedPlan = getOperands(); + if (!sortOperands) + return sortedPlan; new OperandSorter(new QueryablePlan(false), false).sortBundles(sortedPlan); return sortedPlan; }
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/OperandSorterTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/OperandSorterTest.java new file mode 100644 index 0000000..9784da1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/OperandSorterTest.java
@@ -0,0 +1,46 @@ +package org.eclipse.equinox.p2.tests.engine; + +import org.eclipse.equinox.internal.p2.engine.*; +import org.eclipse.equinox.p2.metadata.*; +import org.eclipse.equinox.p2.query.QueryUtil; +import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; + +public class OperandSorterTest extends AbstractProvisioningTest { + + public void testSort() { + IRequirement[] req = new IRequirement[1]; + req[0] = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "B", VersionRange.emptyRange, null, false, false, true); + IInstallableUnit a = createIU("A", req); + IInstallableUnit b = createIU("B"); + IInstallableUnit c = createIU("C"); + + { + InstallableUnitOperand op = new InstallableUnitOperand(createResolvedIU(a), null); + InstallableUnitOperand op2 = new InstallableUnitOperand(createResolvedIU(b), null); + InstallableUnitOperand[] operands = new InstallableUnitOperand[] {op, op2}; + + OperandSorter sorter = new OperandSorter(createTestMetdataRepository(new IInstallableUnit[] {a, b, c}).query(QueryUtil.ALL_UNITS, null), true); + sorter.sortOperands(operands); + System.out.println(sorter.getInstallOrder()); + System.out.println(sorter.getUninstallOrder()); + } + + { + InstallableUnitOperand op = new InstallableUnitOperand(createResolvedIU(a), null); + // InstallableUnitOperand op2 = new InstallableUnitOperand(createResolvedIU(b), null); + // InstallableUnitPropertyOperand op3 = new InstallableUnitPropertyOperand(b, "key", null, "val2"); + InstallableUnitPropertyOperand op4 = new InstallableUnitPropertyOperand(a, "key", "addVal", null); + Operand[] operands = new Operand[] {op, op4}; + + OperandSorter sorter = new OperandSorter(createTestMetdataRepository(new IInstallableUnit[] {a, b, c}).query(QueryUtil.ALL_UNITS, null), true); + sorter.sortOperands(operands); + System.out.println(sorter.getInstallOrder()); + System.out.println(sorter.getUninstallOrder()); + } + } + + //Nothing to do + //Try sorting with property related operands + + //Try sorting with dep order different between the pre and the post +}