/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.scheduler.core.db; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.ow2.proactive.scheduler.task.internal.InternalTask; /** * Perform topological sort on a directed acyclic graph represented by * a collection of {@link Entry} instances (an entry corresponds to a * graph vertex). For each entry, its parent entries should be * returned by {@link Entry#getParents()}. The {@link #sort()} method * produces an ordering where every entry appears before any of its * children. * * The algorithm is the one based on depth-first search from this * wikipedia page: * <a href="http://en.wikipedia.org/w/index.php?title=Topological_sorting&oldid=642496240">Topological sorting</a>. */ public class TopologicalTaskSorter { private final Set<Entry> unmarked; private final Set<Entry> markedTemporarily; private final Map<Entry, Set<Entry>> entryChildren; private final List<Entry> result; public interface Entry { Collection<Entry> getParents(); } /** * Sort a collection of {@link Entry} instances in topological order * @return a new List containing the entries in topological order * @throws NullPointerException if entries is null * @throws IllegalArgumentException if the graph of entries contains a cycle */ public static List<Entry> sort(Collection<Entry> entries) { return new TopologicalTaskSorter(entries).sort(); } /** * Sort a list of {@link InternalTask} instances in topological order * @return a new ArrayList containing the tasks in topological order * @throws NullPointerException if taskList is null * @throws IllegalArgumentException if the task graph contains a cycle */ public static ArrayList<InternalTask> sortInternalTasks(List<InternalTask> taskList) { Collection<Entry> entries = InternalTaskEntry.fromInternalTasks(taskList); return InternalTaskEntry.toInternalTasks(sort(entries)); } private TopologicalTaskSorter(Collection<Entry> entries) { unmarked = new HashSet<>(entries); markedTemporarily = new HashSet<>(); entryChildren = findEntryChildren(entries); result = new LinkedList<>(); } private List<Entry> sort() { while (!unmarked.isEmpty()) { visit(unmarked.iterator().next()); } return result; } private void visit(Entry selected) { if (markedTemporarily.contains(selected)) { throw new IllegalArgumentException("The graph contains a cycle"); } if (unmarked.contains(selected)) { markedTemporarily.add(selected); Set<Entry> children = entryChildren.get(selected); if (children != null) { for (Entry child : children) { visit(child); } } unmarked.remove(selected); markedTemporarily.remove(selected); result.add(0, selected); } } private static Map<Entry, Set<Entry>> findEntryChildren(Collection<Entry> entries) { Map<Entry, Set<Entry>> entryChildren = new HashMap<>(); for (Entry task : entries) { for (Entry parent : task.getParents()) { Set<Entry> children = entryChildren.get(parent); if (children == null) { children = new HashSet<>(); entryChildren.put(parent, children); } children.add(task); } } return entryChildren; } public static class InternalTaskEntry implements Entry { private final InternalTask task; public InternalTaskEntry(InternalTask task) { this.task = task; } @Override public Collection<Entry> getParents() { ArrayList<InternalTask> allParents = new ArrayList<>(); List<InternalTask> parents = task.getIDependences(); if (parents != null) { allParents.addAll(parents); } List<InternalTask> joinParents = task.getJoinedBranches(); if (joinParents != null) { allParents.addAll(joinParents); } InternalTask ifParent = task.getIfBranch(); if (ifParent != null) { allParents.add(ifParent); } return fromInternalTasks(allParents); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; InternalTaskEntry that = (InternalTaskEntry) o; return !(task != null ? !task.equals(that.task) : that.task != null); } @Override public int hashCode() { return task != null ? task.hashCode() : 0; } public static Collection<Entry> fromInternalTasks(List<InternalTask> tasks) { Collection<Entry> result = new ArrayList<>(tasks.size()); for (InternalTask task : tasks) { result.add(new InternalTaskEntry(task)); } return result; } public static ArrayList<InternalTask> toInternalTasks(List<Entry> entries) { ArrayList<InternalTask> result = new ArrayList<>(entries.size()); for (Entry entry : entries) { result.add(((InternalTaskEntry) entry).task); } return result; } } }