From 92847759213fdad8237ab54132665a0b7a91aea6 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 7 Sep 2025 13:54:52 +0800 Subject: [PATCH] update ImageView group, piles should fix concurrentmodification and npe prevention --- .../forge/itemmanager/views/ImageView.java | 110 +++++++++++++++--- 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 56bea0c8c92..2e571e88a78 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -78,6 +78,59 @@ public class ImageView extends ItemView { private Supplier> groups = Suppliers.memoize(ArrayList::new); private Function, ?> fnIsFavorite = ColumnDef.FAVORITE.fnDisplay, fnPrice = null; + private class SafeList { + private final List internalList; + private final Object lock = new Object(); // Object for synchronization + + private SafeList() { + this.internalList = new ArrayList<>(); + } + + private void add(T element) { + synchronized (lock) { + internalList.add(element); + } + } + + private T get(int index) { + synchronized (lock) { + return internalList.get(index); + } + } + + private T remove(int index) { + synchronized (lock) { + return internalList.remove(index); + } + } + + private int size() { + synchronized (lock) { + return internalList.size(); + } + } + + private void clear() { + synchronized (lock) { + internalList.clear(); + } + } + + private boolean isEmpty() { + synchronized (lock) { + return internalList.isEmpty(); + } + } + + private boolean addAll(Collection c) { + synchronized (lock) { + return internalList.addAll(c); + } + } + + // Add other list operations as needed, ensuring synchronization + } + private class ExpandCollapseButton extends FLabel { private boolean isAllCollapsed; @@ -330,11 +383,17 @@ public class ImageView extends ItemView { if (group.getBottom() < visibleTop) { continue; } - for (Pile pile : group.piles) { + for (int i = 0; i < group.piles.size(); i++) { + Pile pile = group.piles.get(i); + if (pile == null) + continue; if (group.getBottom() < visibleTop) { continue; } - for (ItemInfo item : pile.items) { + for (int j = 0; j < pile.items.size(); j++) { + ItemInfo item = pile.items.get(j); + if (item == null) + continue; if (item.getTop() >= visibleTop) { return item; } @@ -454,7 +513,8 @@ public class ImageView extends ItemView { //use TreeMap to build pile set so iterating below sorts on key ColumnDef groupPileBy = groupBy == null ? pileBy : groupBy.getGroupPileBy(i, pileBy); Map, Pile> piles = new TreeMap<>(); - for (ItemInfo itemInfo : group.items) { + for (int j = 0; j < group.items.size(); j++) { + ItemInfo itemInfo = group.items.get(j); if (itemInfo == null) continue; Comparable key = groupPileBy.fnSort.apply(itemInfo); @@ -492,7 +552,10 @@ public class ImageView extends ItemView { Pile pile = new Pile(); x = 0; - for (ItemInfo itemInfo : group.items) { + for (int j = 0; j < group.items.size(); j++) { + ItemInfo itemInfo = group.items.get(j); + if (itemInfo == null) + continue; itemInfo.pos = CardStackPosition.Top; if (pile.items.size() == columnCount) { @@ -519,7 +582,10 @@ public class ImageView extends ItemView { for (int j = 0; j < group.piles.size(); j++) { Pile pile = group.piles.get(j); y = pileY; - for (ItemInfo itemInfo : pile.items) { + for (int k = 0; k < pile.items.size(); k++) { + ItemInfo itemInfo = pile.items.get(k); + if (itemInfo == null) + continue; itemInfo.pos = CardStackPosition.BehindVert; itemInfo.setBounds(x, y, itemWidth, itemHeight); y += dy; @@ -551,15 +617,24 @@ public class ImageView extends ItemView { if (group.isCollapsed && pileBy == null) { //Piles won't have been generated in this case. - for(ItemInfo itemInfo : group.items) { + for (int i = 0; i < group.items.size(); i++) { + ItemInfo itemInfo = group.items.get(i); + if (itemInfo == null) + continue; itemInfo.index = index++; orderedItems.get().add(itemInfo); } continue; } - for (Pile pile : group.piles) { - for (ItemInfo itemInfo : pile.items) { + for (int i = 0; i < group.piles.size(); i++) { + Pile pile = group.piles.get(i); + if (pile == null) + continue; + for (int j = 0; j < pile.items.size(); j++) { + ItemInfo itemInfo = pile.items.get(j); + if (itemInfo == null) + continue; itemInfo.index = index++; orderedItems.get().add(itemInfo); } @@ -657,7 +732,10 @@ public class ImageView extends ItemView { @Override public int getIndexOfItem(T item) { for (Group group : groups.get()) { - for (ItemInfo itemInfo : group.items) { + for (int i = 0; i < group.items.size(); i++) { + ItemInfo itemInfo = group.items.get(i); + if (itemInfo == null) + continue; if (itemInfo.item == item) { //if group containing item is collapsed, expand it so the item can be selected and has a valid index if (group.isCollapsed) { @@ -840,8 +918,8 @@ public class ImageView extends ItemView { } private class Group extends FScrollPane { - private final List items = new ArrayList<>(); - private final List piles = new ArrayList<>(); + private final SafeList items = new SafeList<>(); + private final SafeList piles = new SafeList<>(); private final String name; private boolean isCollapsed; private float scrollWidth; @@ -898,7 +976,8 @@ public class ImageView extends ItemView { float visibleLeft = getScrollLeft(); float visibleRight = visibleLeft + getWidth(); - for (Pile pile : piles) { + for (int i = 0; i < piles.size(); i++) { + Pile pile = piles.get(i); if (pile == null) continue; if (pile.getRight() < visibleLeft) { @@ -966,7 +1045,7 @@ public class ImageView extends ItemView { } private class Pile extends FDisplayObject { - private final List items = new ArrayList<>(); + private final SafeList items = new SafeList<>(); @Override public void draw(Graphics g) { @@ -974,7 +1053,10 @@ public class ImageView extends ItemView { final float visibleBottom = visibleTop + getScroller().getHeight(); ItemInfo skippedItem = null; - for (ItemInfo itemInfo : items) { + for (int i = 0; i < items.size(); i++) { + ItemInfo itemInfo = items.get(i); + if (itemInfo == null) + continue; if (itemInfo.getBottom() < visibleTop) { continue; }