/******************************************************************************* * Copyright (c) 2010 Neil Bartlett. * 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: * Neil Bartlett - initial API and implementation ******************************************************************************/ package bndtools.utils; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.TreeSet; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; public class DependencyUtils { public static interface Processor<T> { void process(T obj, IProgressMonitor monitor) throws CoreException; } public static <T> void processDependencyMap(Collection<T> input, Map<T,Set<T>> dependencies, Processor<T> processor, IProgressMonitor monitor) throws CoreException, CircularDependencyException { SubMonitor progress = SubMonitor.convert(monitor, input.size()); Set<T> processed = new TreeSet<T>(); Stack<T> callStack = new Stack<T>(); for (T selected : input) { processDependencyMap(selected, callStack, dependencies, processed, processor, progress); } } private static <T> void processDependencyMap(T selected, Stack<T> callStack, Map<T,Set<T>> dependencies, Set<T> processed, Processor<T> processor, SubMonitor subMonitor) throws CoreException, CircularDependencyException { if (processed.contains(selected)) return; // Check for cycles int stackIndex = callStack.indexOf(selected); if (stackIndex != -1) { List<T> subList = callStack.subList(stackIndex, callStack.size()); List<T> cycle = new ArrayList<T>(subList.size() + 1); cycle.addAll(subList); cycle.add(selected); throw new CircularDependencyException(cycle); } try { callStack.push(selected); Set<T> selectedDeps = dependencies.get(selected); // Process dependencies first if (selectedDeps != null) { for (T dep : selectedDeps) { processDependencyMap(dep, callStack, dependencies, processed, processor, subMonitor); } } // Process the selection processor.process(selected, subMonitor.newChild(1)); processed.add(selected); } finally { callStack.pop(); } } }