Last active
March 31, 2020 07:06
-
-
Save seraphy/bdd6f393095c3f23ab57d527332376cb to your computer and use it in GitHub Desktop.
JavaFXのObservableListの要素をアンラップしたリストとしてアクセスするための読み込み専用の監視可能リスト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.List; | |
import java.util.Objects; | |
import java.util.function.Function; | |
import java.util.stream.Collectors; | |
import javafx.collections.ListChangeListener; | |
import javafx.collections.ObservableList; | |
import javafx.collections.ObservableListBase; | |
import javafx.collections.WeakListChangeListener; | |
/** | |
* ObservableListの要素をアンラップしたObservableListに変換する読み込み専用のアダプタリスト。 | |
* ソースのリストへの変更は、そのままアンラップされてリスナーに通知される。 | |
* リストへのアクセスを行うと自動的にアンラップされた要素へのアクセスとなる。 | |
* このリストへの読み書きは禁止される。 | |
* | |
* @param <T> アンラップされた型 | |
* @param <S> ソースリストの型 | |
*/ | |
public class UnwrappingObservableList<T, S> extends ObservableListBase<T> { | |
/** | |
* ソースリスト | |
*/ | |
private ObservableList<S> backingList; | |
/** | |
* ソースリストの要素をアンラップする関数 | |
*/ | |
private Function<S, T> unwrapper; | |
/** | |
* ソースリストの変更を感知するリスナー | |
*/ | |
private final ListChangeListener<S> listener; | |
private final WeakListChangeListener<S> weakListener; | |
/** | |
* アンラップするObservableListを構築します。 | |
* @param unwrapper ソースリストの要素をアンラップする関数 | |
*/ | |
public UnwrappingObservableList(Function<S, T> unwrapper) { | |
this(null, unwrapper); | |
} | |
/** | |
* アンラップするObservableListを構築します | |
* @param backingList 元となるソースリスト | |
* @param unwrapper ソースリストの要素をアンラップする関数 | |
*/ | |
public UnwrappingObservableList(ObservableList<S> backingList, Function<S, T> unwrapper) { | |
this.backingList = backingList; | |
this.unwrapper = Objects.requireNonNull(unwrapper); | |
listener = c -> { | |
fireChange(new ListChangeListener.Change<T>(this) { | |
private int[] perm; | |
@Override | |
public boolean next() { | |
perm = null; | |
return c.next(); | |
} | |
@Override | |
public void reset() { | |
c.reset(); | |
} | |
@Override | |
public int getFrom() { | |
return c.getFrom(); | |
} | |
@Override | |
public int getTo() { | |
return c.getTo(); | |
} | |
@Override | |
public List<T> getRemoved() { | |
return c.getRemoved().stream().map(item -> unwrap(item)).collect(Collectors.toList()); | |
} | |
@Override | |
public boolean wasUpdated() { | |
return c.wasUpdated(); // 既定実装はfalseを返すため、ソースを明示的に引き継ぐ必要がある | |
} | |
@Override | |
protected int[] getPermutation() { | |
if (perm == null) { | |
if (c.wasPermutated()) { | |
final int from = c.getFrom(); | |
final int n = c.getTo() - from; | |
perm = new int[n]; | |
for (int i = 0; i < n; i++) { | |
perm[i] = c.getPermutation(from + i); | |
} | |
} else { | |
perm = new int[0]; | |
} | |
} | |
return perm; | |
} | |
@Override | |
public String toString() { | |
return c.toString(); | |
} | |
}); | |
}; | |
this.weakListener = new WeakListChangeListener<>(listener); | |
if (this.backingList != null) { | |
this.backingList.addListener(weakListener); | |
} | |
} | |
/** | |
* バッキングリストを設定します。 | |
* 現在設定されているリストは登録解除されます。 | |
* @param backingList | |
*/ | |
public void setBackingList(ObservableList<S> backingList) { | |
ObservableList<S> backingListOld = this.backingList; | |
this.backingList = Objects.requireNonNull(backingList); | |
this.backingList.addListener(weakListener); | |
if (backingListOld != null) { | |
backingListOld.removeListener(weakListener); | |
} | |
} | |
/** | |
* 現在設定されているバッキングリストを取得します。 | |
* 設定されていない場合はnullを返します。 | |
* @return | |
*/ | |
public ObservableList<S> getBackingList() { | |
return backingList; | |
} | |
/** | |
* 変換関数 | |
* @param item | |
* @return | |
*/ | |
protected T unwrap(S item) { | |
return unwrapper.apply(item); | |
} | |
@Override | |
public T get(int index) { | |
if (backingList == null) { | |
throw new IndexOutOfBoundsException(); | |
} | |
return unwrap(backingList.get(index)); | |
} | |
@Override | |
public int size() { | |
if (backingList == null) { | |
return 0; | |
} | |
return backingList.size(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment