edu.umd.cs.findbugs.util.TopologicalSort Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2006, University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.log.Profiler;
/**
* @author pugh
*/
public class TopologicalSort {
final static boolean DEBUG = SystemProperties.getBoolean("tsort.debug");
public interface OutEdges {
Collection getOutEdges(E e);
}
public interface OutEdges2 extends OutEdges {
int score(E e);
}
public static class OutEdgesCache implements OutEdges {
final Map> map = new IdentityHashMap<>();
final OutEdges base;
public OutEdgesCache(OutEdges base) {
this.base = base;
}
@Override
public Collection getOutEdges(E e) {
Collection result = map.get(e);
if (result == null) {
result = base.getOutEdges(e);
map.put(e, result);
}
return result;
}
}
public static List sortByCallGraph(Collection elements, OutEdges outEdges) {
Profiler profile = Global.getAnalysisCache().getProfiler();
profile.start(TopologicalSort.class);
try {
SortAlgorithm instance = new Worker2<>(elements, outEdges);
return instance.compute();
} finally {
profile.end(TopologicalSort.class);
}
}
public static void countBadEdges(List elements, OutEdges outEdges) {
if (!DEBUG) {
return;
}
HashSet seen = new HashSet<>();
HashSet all = new HashSet<>(elements);
int result = 0;
int total = 0;
for (E e : elements) {
for (E e2 : outEdges.getOutEdges(e)) {
if (e != e2 && all.contains(e2) && !outEdges.getOutEdges(e2).contains(e)) {
total++;
if (!seen.contains(e2)) {
result++;
}
}
}
seen.add(e);
}
System.out.println(" bad edges are " + result + "/" + total);
}
interface SortAlgorithm {
List compute();
}
static class Worker implements SortAlgorithm {
Worker(Collection consider, OutEdges outEdges) {
this.consider = new LinkedHashSet<>(consider);
this.outEdges = outEdges;
this.result = new ArrayList<>(consider.size());
}
OutEdges outEdges;
List result;
HashSet visited = new HashSet<>();
Set consider = new HashSet<>();
@Override
public List compute() {
for (E e : consider) {
visit(e);
}
return result;
}
void visit(E e) {
if (!consider.contains(e)) {
return;
}
if (!visited.add(e)) {
return;
}
for (E e2 : outEdges.getOutEdges(e)) {
visit(e2);
}
result.add(e);
}
}
static class Worker2 implements SortAlgorithm {
Worker2(Collection consider, OutEdges outEdges) {
if (outEdges == null) {
throw new IllegalArgumentException("outEdges must not be null");
}
this.consider = new LinkedHashSet<>(consider);
this.outEdges = outEdges;
}
OutEdges outEdges;
Set consider = new HashSet<>();
MultiMap iEdges, oEdges;
private void removeVertex(E e) {
Collection outEdges = oEdges.get(e);
Collection inEdges = iEdges.get(e);
for (E e2 : outEdges) {
iEdges.remove(e2, e);
}
for (E e2 : inEdges) {
oEdges.remove(e2, e);
}
iEdges.removeAll(e);
oEdges.removeAll(e);
}
@Override
public List compute() {
ArrayList doFirst = new ArrayList<>(consider.size());
ArrayList doLast = new ArrayList<>(consider.size());
HashSet remaining = new HashSet<>(consider);
iEdges = new MultiMap<>(LinkedList.class);
oEdges = new MultiMap<>(LinkedList.class);
for (E e : consider) {
for (E e2 : outEdges.getOutEdges(e)) {
if (e != e2 && consider.contains(e2)) {
iEdges.add(e2, e);
oEdges.add(e, e2);
}
}
}
for (E e : consider) {
HashSet both = new HashSet<>(iEdges.get(e));
both.retainAll(oEdges.get(e));
for (E e2 : both) {
iEdges.remove(e, e2);
oEdges.remove(e, e2);
}
}
while (!remaining.isEmpty()) {
boolean foundSomething = false;
E best = null;
int bestScore = Integer.MIN_VALUE;
for (Iterator i = remaining.iterator(); i.hasNext();) {
E e = i.next();
if (oEdges.get(e).isEmpty()) {
doFirst.add(e);
removeVertex(e);
if (DEBUG) {
System.out.println("do " + e + " first");
}
i.remove();
foundSomething = true;
} else if (iEdges.get(e).isEmpty()) {
doLast.add(e);
removeVertex(e);
if (DEBUG) {
System.out.println("do " + e + " last");
}
i.remove();
foundSomething = true;
} else {
// Higher score: more likely to choose
int myScore = getScore(e);
// myScore -= oEdges.get(e).size(); // more needs, more
// reluctant
// myScore += iEdges.get(e).size(); // needed more, more
// eager
if (bestScore < myScore) {
// my score is better than the best seen so far
bestScore = myScore;
best = e;
}
}
} // iterator
if (!foundSomething) {
if (DEBUG) {
System.out.println("do " + best + " first, reluctantly");
System.out.println(" score: " + bestScore);
System.out.println(" needs: " + oEdges.get(best));
System.out.println(" needed by: " + iEdges.get(best));
}
doFirst.add(best);
removeVertex(best);
remaining.remove(best);
}
} // while
Collections.reverse(doLast);
doFirst.addAll(doLast);
return doFirst;
}
/**
* @param e
* @return
*/
private int getScore(E e) {
int myScore = score(e);
if (outEdges instanceof OutEdges2) {
int score2 = ((OutEdges2) outEdges).score(e);
if (score2 > 1) {
score2 += 11;
}
myScore = 5 * myScore + score2;
}
return myScore;
}
/**
* @param e
* @return
*/
private int score(E e) {
int myScore = 0;
for (E e2 : oEdges.get(e)) {
if (iEdges.get(e2).size() == 1) {
myScore -= 2;
} else {
myScore -= 1;
}
}
for (E e2 : iEdges.get(e)) {
if (oEdges.get(e2).size() == 1) {
myScore += 2;
} else {
myScore += 1;
}
}
return myScore;
}
}
}