infobox manager: keep infoboxes in order of insertion

Collections.binarySearch() does not guarantee which element is found if
there are multiple that compare equal, leaving the order of infoboxes
not necessarily in insertion order. This was an unintended side effect of
ba9ffb1d60 and
406c2bc7db.
This commit is contained in:
Adam
2020-07-01 19:18:30 -04:00
parent 67030e38a4
commit 83607f1335
2 changed files with 40 additions and 2 deletions

View File

@@ -29,6 +29,7 @@ import com.google.common.collect.ComparisonChain;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
@@ -71,12 +72,12 @@ public class InfoBoxManager
synchronized (this)
{
int idx = Collections.binarySearch(infoBoxes, infoBox, (b1, b2) -> ComparisonChain
int idx = findInsertionIndex(infoBoxes, infoBox, (b1, b2) -> ComparisonChain
.start()
.compare(b1.getPriority(), b2.getPriority())
.compare(b1.getPlugin().getName(), b2.getPlugin().getName())
.result());
infoBoxes.add(idx < 0 ? -idx - 1 : idx, infoBox);
infoBoxes.add(idx, infoBox);
}
BufferedImage image = infoBox.getImage();
@@ -150,4 +151,38 @@ public class InfoBoxManager
infoBox.setScaledImage(resultImage);
}
/**
* Find insertion point for the given key into the given sorted list. If key already exists in the list,
* return the index after the last occurrence.
* @param list
* @param key
* @param c
* @param <T>
* @return
*/
private static <T> int findInsertionIndex(List<? extends T> list, T key, Comparator<? super T> c)
{
int idx = Collections.binarySearch(list, key, c);
if (idx < 0)
{
// key isn't found in the list
return -idx - 1;
}
// list(idx) is equal to key, so it is not necessary to recheck it
for (int i = idx + 1; i < list.size(); ++i)
{
T cur = list.get(i);
int cmp = c.compare(cur, key);
if (cmp > 0)
{
// this is the first element which is greater
return i;
}
}
return list.size();
}
}

View File

@@ -114,5 +114,8 @@ public class InfoBoxManagerTest
infoBoxManager.addInfoBox(new TestInfobox(InfoBoxPriority.MED, "three"));
assertEquals(3, infoBoxManager.getInfoBoxes().size());
assertEquals("one", infoBoxManager.getInfoBoxes().get(0).getText());
assertEquals("two", infoBoxManager.getInfoBoxes().get(1).getText());
assertEquals("three", infoBoxManager.getInfoBoxes().get(2).getText());
}
}