Bug 530454 - Generics on VertexOrder and family

Change-Id: I5d66215d01148c62ef70bfe55cf572cf37961a62
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ComputeProjectOrder.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ComputeProjectOrder.java
index 1289011..ba65a51 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ComputeProjectOrder.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ComputeProjectOrder.java
@@ -13,7 +13,9 @@
  *******************************************************************************/
 package org.eclipse.core.internal.resources;
 
+import java.lang.reflect.Array;
 import java.util.*;
+import java.util.function.Predicate;
 
 /**
  * Implementation of a sort algorithm for computing the order of vertexes that are part
@@ -41,12 +43,12 @@
 	 * McGraw-Hill, 1990. The depth-first search algorithm is in section 23.3.
 	 * </p>
 	 */
-	private static class Digraph {
+	private static class Digraph<T> {
 		/**
 		 * struct-like object for representing a vertex along with various
 		 * values computed during depth-first search (DFS).
 		 */
-		public static class Vertex {
+		public static class Vertex<T> {
 			/**
 			 * White is for marking vertexes as unvisited.
 			 */
@@ -74,7 +76,7 @@
 			 * The DFS predecessor vertex, or <code>null</code> if there is no
 			 * predecessor. <code>null</code> initially.
 			 */
-			public Vertex predecessor = null;
+			public Vertex<T> predecessor = null;
 
 			/**
 			 * Timestamp indicating when the vertex was finished (became BLACK)
@@ -86,7 +88,7 @@
 			/**
 			 * The id of this vertex.
 			 */
-			public Object id;
+			public T id;
 
 			/**
 			 * Ordered list of adjacent vertexes. In other words, "this" is the
@@ -95,14 +97,14 @@
 			 *
 			 * Element type: <code>Vertex</code>
 			 */
-			public List<Vertex> adjacent = new ArrayList<>(3);
+			public List<Vertex<T>> adjacent = new ArrayList<>(3);
 
 			/**
 			 * Creates a new vertex with the given id.
 			 *
 			 * @param id the vertex id
 			 */
-			public Vertex(Object id) {
+			public Vertex(T id) {
 				this.id = id;
 			}
 		}
@@ -112,14 +114,14 @@
 		 *
 		 * Element type: <code>Vertex</code>
 		 */
-		private List<Vertex> vertexList = new ArrayList<>(100);
+		private List<Vertex<T>> vertexList = new ArrayList<>(100);
 
 		/**
 		 * Map from id to vertex.
 		 *
-		 * Key type: <code>Object</code>; value type: <code>Vertex</code>
+		 * Key type: <code>T</code>; value type: <code>Vertex</code>
 		 */
-		private Map<Object, Vertex> vertexMap = new HashMap<>(100);
+		private Map<T, Vertex<T>> vertexMap = new HashMap<>(100);
 
 		/**
 		 * DFS visit time. Non-negative.
@@ -138,6 +140,8 @@
 		 */
 		private boolean cycles = false;
 
+		private Class<T> clazz;
+
 		/**
 		 * Creates a new empty directed graph object.
 		 * <p>
@@ -148,8 +152,9 @@
 		 * by DFS finish time.
 		 * </p>
 		 */
-		public Digraph() {
+		public Digraph(Class<T> clazz) {
 			super();
+			this.clazz = clazz;
 		}
 
 		/**
@@ -174,12 +179,12 @@
 		 * @exception IllegalArgumentException if the vertex id is
 		 * already defined or if the graph is frozen
 		 */
-		public void addVertex(Object id) throws IllegalArgumentException {
+		public void addVertex(T id) throws IllegalArgumentException {
 			if (initialized) {
 				throw new IllegalArgumentException();
 			}
-			Vertex vertex = new Vertex(id);
-			Object existing = vertexMap.put(id, vertex);
+			Vertex<T> vertex = new Vertex<>(id);
+			Vertex<T> existing = vertexMap.put(id, vertex);
 			// nip problems with duplicate vertexes in the bud
 			if (existing != null) {
 				throw new IllegalArgumentException();
@@ -199,12 +204,12 @@
 		 * @exception IllegalArgumentException if either vertex is undefined or
 		 * if the graph is frozen
 		 */
-		public void addEdge(Object fromId, Object toId) throws IllegalArgumentException {
+		public void addEdge(T fromId, T toId) throws IllegalArgumentException {
 			if (initialized) {
 				throw new IllegalArgumentException();
 			}
-			Vertex fromVertex = vertexMap.get(fromId);
-			Vertex toVertex = vertexMap.get(toId);
+			Vertex<T> fromVertex = vertexMap.get(fromId);
+			Vertex<T> toVertex = vertexMap.get(toId);
 			// nip problems with bogus vertexes in the bud
 			if (fromVertex == null) {
 				throw new IllegalArgumentException();
@@ -227,13 +232,14 @@
 		 * (element type: <code>Object</code>)
 		 * @exception IllegalArgumentException if the graph is not frozen
 		 */
-		public List<Object> idsByDFSFinishTime(boolean increasing) {
+		public List<T> idsByDFSFinishTime(boolean increasing) {
 			if (!initialized) {
 				throw new IllegalArgumentException();
 			}
 			int len = vertexList.size();
-			Object[] r = new Object[len];
-			for (Vertex vertex : vertexList) {
+			@SuppressWarnings("unchecked")
+			T[] r = (T[]) Array.newInstance(clazz, len);
+			for (Vertex<T> vertex : vertexList) {
 				int f = vertex.finishTime;
 				// note that finish times start at 1, not 0
 				if (increasing) {
@@ -269,24 +275,25 @@
 		 * <code>Object[]</code>)
 		 * @exception IllegalArgumentException if the graph is not frozen
 		 */
-		public List<Object[]> nonTrivialComponents() {
+		@SuppressWarnings("unchecked")
+		public List<T[]> nonTrivialComponents() {
 			if (!initialized) {
 				throw new IllegalArgumentException();
 			}
 			// find the roots of each component
 			// Map<Vertex,List<Object>> components
-			Map<Vertex, List<Object>> components = new HashMap<>();
-			for (Vertex vertex : vertexList) {
+			Map<Vertex<T>, List<T>> components = new HashMap<>();
+			for (Vertex<T> vertex : vertexList) {
 				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;
+					Vertex<T> root = vertex;
 					while (root.predecessor != null) {
 						root = root.predecessor;
 					}
-					List<Object> component = components.get(root);
+					List<T> component = components.get(root);
 					if (component == null) {
 						component = new ArrayList<>(2);
 						component.add(root.id);
@@ -295,10 +302,10 @@
 					component.add(vertex.id);
 				}
 			}
-			List<Object[]> result = new ArrayList<>(components.size());
-			for (List<Object> component : components.values()) {
+			List<T[]> result = new ArrayList<>(components.size());
+			for (List<T> component : components.values()) {
 				if (component.size() > 1) {
-					result.add(component.toArray());
+					result.add(component.toArray((T[]) Array.newInstance(clazz, component.size())));
 				}
 			}
 			return result;
@@ -374,9 +381,9 @@
 			time = 0;
 			// for a stack, append to the end of an array-based list
 			List<Object> stack = new ArrayList<>(Math.max(1, vertexList.size()));
-			Iterator<Vertex> allAdjacent = null;
-			Vertex vertex = null;
-			Iterator<Vertex> allV = vertexList.iterator();
+			Iterator<Vertex<T>> allAdjacent = null;
+			Vertex<T> vertex = null;
+			Iterator<Vertex<T>> allV = vertexList.iterator();
 			state = NEXT_VERTEX;
 			nextStateLoop: while (true) {
 				switch (state) {
@@ -386,7 +393,7 @@
 							// all done
 							break nextStateLoop;
 						}
-						Vertex nextVertex = allV.next();
+						Vertex<T> nextVertex = allV.next();
 						if (nextVertex.color == Vertex.WHITE) {
 							stack.add(NEXT_VERTEX_OBJECT);
 							vertex = nextVertex;
@@ -408,7 +415,7 @@
 						// on entry, "allAdjacent" contains adjacent vertexes to
 						// be visited; "vertex" contains vertex being visited
 						if (allAdjacent.hasNext()) {
-							Vertex adjVertex = allAdjacent.next();
+							Vertex<T> adjVertex = allAdjacent.next();
 							if (adjVertex.color == Vertex.WHITE) {
 								// explore edge from vertex to adjVertex
 								adjVertex.predecessor = vertex;
@@ -434,8 +441,8 @@
 						continue nextStateLoop;
 					case AFTER_NEXTED_DFS_VISIT :
 						// on entry, stack contains "vertex" and "allAjacent"
-						vertex = (Vertex) stack.remove(stack.size() - 1);
-						allAdjacent = (Iterator<Vertex>) stack.remove(stack.size() - 1);
+						vertex = (Vertex<T>) stack.remove(stack.size() - 1);
+						allAdjacent = (Iterator<Vertex<T>>) stack.remove(stack.size() - 1);
 						state = NEXT_ADJACENT;
 						continue nextStateLoop;
 				}
@@ -448,14 +455,14 @@
 	 * Data structure for holding the multi-part outcome of
 	 * <code>ComputeVertexOrder.computeVertexOrder</code>.
 	 */
-	static final class VertexOrder {
+	static final class VertexOrder<T> {
 		/**
 		 * Creates an instance with the given values.
 		 * @param vertexes initial value of <code>vertexes</code> field
 		 * @param hasCycles initial value of <code>hasCycles</code> field
 		 * @param knots initial value of <code>knots</code> field
 		 */
-		public VertexOrder(Object[] vertexes, boolean hasCycles, Object[][] knots) {
+		public VertexOrder(T[] vertexes, boolean hasCycles, T[][] knots) {
 			this.vertexes = vertexes;
 			this.hasCycles = hasCycles;
 			this.knots = knots;
@@ -465,7 +472,7 @@
 		 * A list of vertexes ordered so as to honor the reference
 		 * relationships between them wherever possible.
 		 */
-		public Object[] vertexes;
+		public T[] vertexes;
 		/**
 		 * <code>true</code> if any of the vertexes in <code>vertexes</code>
 		 * are involved in non-trivial cycles in the reference graph.
@@ -477,7 +484,7 @@
 		 * contains cycles, each element is a knot of two or more vertexes that
 		 * are involved in a cycle of mutually dependent references.
 		 */
-		public Object[][] knots;
+		public T[][] knots;
 	}
 
 	/**
@@ -504,18 +511,19 @@
 	 * @param references a list of pairs [A,B] meaning that A references B
 	 * @return an object describing the resulting order
 	 */
-	static VertexOrder computeVertexOrder(SortedSet<? extends Object> vertexes, List<? extends Object[]> references) {
+	@SuppressWarnings("unchecked")
+	static <T> VertexOrder<T> computeVertexOrder(SortedSet<? extends T> vertexes, List<? extends T[]> references, Class<T> clazz) {
 
 		// Step 1: Create the graph object.
-		final Digraph g1 = new Digraph();
+		final Digraph<T> g1 = new Digraph<>(clazz);
 		// add vertexes
-		for (Object name : vertexes) {
+		for (T name : vertexes) {
 			g1.addVertex(name);
 		}
 		// add edges
-		for (Object[] ref : references) {
-			Object p = ref[0];
-			Object q = ref[1];
+		for (T[] ref : references) {
+			T p = ref[0];
+			T q = ref[1];
 			// p has a reference to q
 			// therefore create an edge from q to p
 			// to cause q to come before p in eventual result
@@ -526,16 +534,16 @@
 		// 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();
+		final Digraph<T> g2 = new Digraph<>(clazz);
 		// add vertexes
-		List<Object> resortedVertexes = g1.idsByDFSFinishTime(false);
-		for (Object object : resortedVertexes) {
+		List<T> resortedVertexes = g1.idsByDFSFinishTime(false);
+		for (T object : resortedVertexes) {
 			g2.addVertex(object);
 		}
 		// add edges
-		for (Object[] ref : references) {
-			Object p = ref[0];
-			Object q = ref[1];
+		for (T[] ref : references) {
+			T p = ref[0];
+			T q = ref[1];
 			// p has a reference to q
 			// therefore create an edge from p to q
 			// N.B. this is the reverse of step 1
@@ -545,36 +553,34 @@
 
 		// Step 3: Return the vertexes in increasing order of depth-first finish
 		// time in g2
-		List<Object> sortedVertexList = g2.idsByDFSFinishTime(true);
-		Object[] orderedVertexes = new Object[sortedVertexList.size()];
+		List<T> sortedVertexList = g2.idsByDFSFinishTime(true);
+		T[] orderedVertexes = (T[]) Array.newInstance(clazz, sortedVertexList.size());
 		sortedVertexList.toArray(orderedVertexes);
-		Object[][] knots;
+		T[][] knots;
 		boolean hasCycles = g2.containsCycles();
 		if (hasCycles) {
-			List<Object[]> knotList = g2.nonTrivialComponents();
-			knots = new Object[knotList.size()][];
+			List<T[]> knotList = g2.nonTrivialComponents();
+			Class<?> arrayClass = Array.newInstance(clazz, 0).getClass();
+			knots = (T[][]) Array.newInstance(arrayClass, knotList.size());
 			knotList.toArray(knots);
 		} else {
-			knots = new Object[][] {};
+			knots = (T[][]) Array.newInstance(clazz, 0, 0);
 		}
-		return new VertexOrder(orderedVertexes, hasCycles, knots);
-	}
-
-	static interface VertexFilter {
-		boolean matches(Object vertex);
+		return new VertexOrder<>(orderedVertexes, hasCycles, knots);
 	}
 
 	/**
 	 * Given a VertexOrder and a VertexFilter, remove all vertexes
 	 * matching the filter from the ordering.
 	 */
-	static VertexOrder filterVertexOrder(VertexOrder order, VertexFilter filter) {
+	@SuppressWarnings("unchecked")
+	static <T> VertexOrder<T> filterVertexOrder(VertexOrder<T> order, Predicate<T> filter, Class<T> clazz) {
 		// Optimize common case where nothing is to be filtered
 		// and cache the results of applying the filter
 		int filteredCount = 0;
 		boolean[] filterMatches = new boolean[order.vertexes.length];
 		for (int i = 0; i < order.vertexes.length; i++) {
-			filterMatches[i] = filter.matches(order.vertexes[i]);
+			filterMatches[i] = filter.test(order.vertexes[i]);
 			if (filterMatches[i])
 				filteredCount++;
 		}
@@ -586,7 +592,7 @@
 
 		// Otherwise we need to eliminate mention of vertexes matching the filter
 		// from the list of vertexes
-		Object[] reducedVertexes = new Object[order.vertexes.length - filteredCount];
+		T[] reducedVertexes = (T[]) Array.newInstance(clazz, order.vertexes.length - filteredCount);
 		for (int i = 0, j = 0; i < order.vertexes.length; i++) {
 			if (!filterMatches[i]) {
 				reducedVertexes[j] = order.vertexes[i];
@@ -595,21 +601,20 @@
 		}
 
 		// and from the knots list
-		List<Object[]> reducedKnots = new ArrayList<>(order.knots.length);
-		for (Object[] knot : order.knots) {
-			List<Object> knotList = new ArrayList<>(knot.length);
-			for (Object element : knot) {
-				Object vertex = element;
-				if (!filter.matches(vertex)) {
+		List<T[]> reducedKnots = new ArrayList<>(order.knots.length);
+		for (T[] knot : order.knots) {
+			List<T> knotList = new ArrayList<>(knot.length);
+			for (T vertex : knot) {
+				if (!filter.test(vertex)) {
 					knotList.add(vertex);
 				}
 			}
 			// Keep knots containing 2 or more vertexes in the specified subset
 			if (knotList.size() > 1) {
-				reducedKnots.add(knotList.toArray());
+				reducedKnots.add(knotList.toArray((T[]) Array.newInstance(clazz, knotList.size())));
 			}
 		}
-
-		return new VertexOrder(reducedVertexes, reducedKnots.size() > 0, reducedKnots.toArray(new Object[reducedKnots.size()][]));
+		Class<?> arrayClass = Array.newInstance(clazz, 0).getClass();
+		return new VertexOrder<>(reducedVertexes, reducedKnots.size() > 0, reducedKnots.toArray((T[][]) Array.newInstance(arrayClass, reducedKnots.size())));
 	}
 }
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java
index 99c1729..7c17ccb 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java
@@ -21,6 +21,7 @@
 import java.net.URISyntaxException;
 import java.util.*;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Predicate;
 import org.eclipse.core.filesystem.EFS;
 import org.eclipse.core.filesystem.IFileStore;
 import org.eclipse.core.filesystem.URIUtil;
@@ -29,7 +30,6 @@
 import org.eclipse.core.internal.properties.IPropertyManager;
 import org.eclipse.core.internal.properties.PropertyManager2;
 import org.eclipse.core.internal.refresh.RefreshManager;
-import org.eclipse.core.internal.resources.ComputeProjectOrder.VertexFilter;
 import org.eclipse.core.internal.resources.ComputeProjectOrder.VertexOrder;
 import org.eclipse.core.internal.utils.*;
 import org.eclipse.core.internal.watson.*;
@@ -624,7 +624,7 @@
 	 * @return result describing the global project order
 	 * @since 2.1
 	 */
-	private VertexOrder computeFullProjectOrder() {
+	private VertexOrder<IProject> computeFullProjectOrder() {
 		// determine the full set of accessible projects in the workspace
 		// order the set in descending alphabetical order of project name
 		SortedSet<IProject> allAccessibleProjects = new TreeSet<>((px, py) -> py.getName().compareTo(px.getName()));
@@ -648,7 +648,7 @@
 					edges.add(new IProject[] {project, ref});
 			}
 		}
-		return ComputeProjectOrder.computeVertexOrder(allAccessibleProjects, edges);
+		return ComputeProjectOrder.computeVertexOrder(allAccessibleProjects, edges, IProject.class);
 	}
 
 	/**
@@ -676,7 +676,7 @@
 	 *
 	 * @return result describing the global active build configuration order
 	 */
-	private VertexOrder computeActiveBuildConfigOrder() {
+	private VertexOrder<IBuildConfiguration> computeActiveBuildConfigOrder() {
 		// Determine the full set of accessible active project buildConfigs in the workspace,
 		// and all the accessible project buildConfigs that they reference. This forms a set
 		// of all the project buildConfigs that will be returned.
@@ -732,7 +732,7 @@
 				}
 			}
 		}
-		return ComputeProjectOrder.computeVertexOrder(allAccessibleBuildConfigs, edges);
+		return ComputeProjectOrder.computeVertexOrder(allAccessibleBuildConfigs, edges, IBuildConfiguration.class);
 	}
 
 	/**
@@ -759,7 +759,7 @@
 	 *
 	 * @return result describing the global project build configuration order
 	 */
-	private VertexOrder computeFullBuildConfigOrder() {
+	private VertexOrder<IBuildConfiguration> computeFullBuildConfigOrder() {
 		// Compute the order for all accessible project buildConfigs
 		SortedSet<IBuildConfiguration> allAccessibleBuildConfigurations = new TreeSet<>(new BuildConfigurationComparator());
 
@@ -787,10 +787,10 @@
 				}
 			}
 		}
-		return ComputeProjectOrder.computeVertexOrder(allAccessibleBuildConfigurations, edges);
+		return ComputeProjectOrder.computeVertexOrder(allAccessibleBuildConfigurations, edges, IBuildConfiguration.class);
 	}
 
-	private static ProjectOrder vertexOrderToProjectOrder(VertexOrder order) {
+	private static ProjectOrder vertexOrderToProjectOrder(VertexOrder<IProject> order) {
 		IProject[] projects = new IProject[order.vertexes.length];
 		System.arraycopy(order.vertexes, 0, projects, 0, order.vertexes.length);
 		IProject[][] knots = new IProject[order.knots.length][];
@@ -801,7 +801,7 @@
 		return new ProjectOrder(projects, order.hasCycles, knots);
 	}
 
-	private static ProjectBuildConfigOrder vertexOrderToProjectBuildConfigOrder(VertexOrder order) {
+	private static ProjectBuildConfigOrder vertexOrderToProjectBuildConfigOrder(VertexOrder<IBuildConfiguration> order) {
 		IBuildConfiguration[] buildConfigs = new IBuildConfiguration[order.vertexes.length];
 		System.arraycopy(order.vertexes, 0, buildConfigs, 0, order.vertexes.length);
 		IBuildConfiguration[][] knots = new IBuildConfiguration[order.knots.length][];
@@ -864,15 +864,15 @@
 	@Override
 	public ProjectOrder computeProjectOrder(IProject[] projects) {
 		// Compute the full project order for all accessible projects
-		VertexOrder fullProjectOrder = computeFullProjectOrder();
+		VertexOrder<IProject> fullProjectOrder = computeFullProjectOrder();
 
 		// Create a filter to remove all projects that are not in the list asked for
 		final Set<IProject> projectSet = new HashSet<>(projects.length);
 		projectSet.addAll(Arrays.asList(projects));
-		VertexFilter filter = vertex -> !projectSet.contains(vertex);
+		Predicate<IProject> filter = vertex -> !projectSet.contains(vertex);
 
 		// Filter the order and return it
-		return vertexOrderToProjectOrder(ComputeProjectOrder.filterVertexOrder(fullProjectOrder, filter));
+		return vertexOrderToProjectOrder(ComputeProjectOrder.filterVertexOrder(fullProjectOrder, filter, IProject.class));
 	}
 
 	/**
@@ -911,15 +911,15 @@
 	 */
 	public ProjectBuildConfigOrder computeProjectBuildConfigOrder(IBuildConfiguration[] buildConfigs) {
 		// Compute the full project order for all accessible projects
-		VertexOrder fullBuildConfigOrder = computeFullBuildConfigOrder();
+		VertexOrder<IBuildConfiguration> fullBuildConfigOrder = computeFullBuildConfigOrder();
 
 		// Create a filter to remove all project buildConfigs that are not in the list asked for
 		final Set<IBuildConfiguration> projectConfigSet = new HashSet<>(buildConfigs.length);
 		projectConfigSet.addAll(Arrays.asList(buildConfigs));
-		VertexFilter filter = vertex -> !projectConfigSet.contains(vertex);
+		Predicate<IBuildConfiguration> filter = vertex -> !projectConfigSet.contains(vertex);
 
 		// Filter the order and return it
-		return vertexOrderToProjectBuildConfigOrder(ComputeProjectOrder.filterVertexOrder(fullBuildConfigOrder, filter));
+		return vertexOrderToProjectBuildConfigOrder(ComputeProjectOrder.filterVertexOrder(fullBuildConfigOrder, filter, IBuildConfiguration.class));
 	}
 
 	@Override