From 4f2b6bef84f75bc17101a17c60251fc0df43aae1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 6 Jan 2018 23:07:17 -0600 Subject: Keyed...: Rename all the things Hooray for diamond interface inheritance. Signed-off-by: Samuel Holland --- .../com/wireguard/android/QuickTileService.java | 4 +- .../android/databinding/BindingAdapters.java | 12 +- .../databinding/KeyedObservableListAdapter.java | 127 --------------------- .../databinding/ObservableKeyedListAdapter.java | 127 +++++++++++++++++++++ .../com/wireguard/android/model/TunnelManager.java | 10 +- .../android/util/KeyedObservableArrayList.java | 100 ---------------- .../android/util/KeyedObservableList.java | 11 -- .../android/util/ObservableKeyedArrayList.java | 100 ++++++++++++++++ .../android/util/ObservableKeyedList.java | 11 ++ .../util/ObservableSortedKeyedArrayList.java | 105 +++++++++++++++++ .../util/SortedKeyedObservableArrayList.java | 106 ----------------- 11 files changed, 356 insertions(+), 357 deletions(-) delete mode 100644 app/src/main/java/com/wireguard/android/databinding/KeyedObservableListAdapter.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/ObservableKeyedListAdapter.java delete mode 100644 app/src/main/java/com/wireguard/android/util/KeyedObservableArrayList.java delete mode 100644 app/src/main/java/com/wireguard/android/util/KeyedObservableList.java create mode 100644 app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java create mode 100644 app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java create mode 100644 app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java delete mode 100644 app/src/main/java/com/wireguard/android/util/SortedKeyedObservableArrayList.java (limited to 'app/src/main/java') diff --git a/app/src/main/java/com/wireguard/android/QuickTileService.java b/app/src/main/java/com/wireguard/android/QuickTileService.java index 10563b6e..3b652a5b 100644 --- a/app/src/main/java/com/wireguard/android/QuickTileService.java +++ b/app/src/main/java/com/wireguard/android/QuickTileService.java @@ -21,7 +21,7 @@ import com.wireguard.android.activity.SettingsActivity; import com.wireguard.android.model.Tunnel; import com.wireguard.android.model.Tunnel.State; import com.wireguard.android.model.TunnelManager; -import com.wireguard.android.util.KeyedObservableList; +import com.wireguard.android.util.ObservableKeyedList; import java.util.Objects; @@ -104,7 +104,7 @@ public class QuickTileService extends TileService implements OnSharedPreferenceC final String currentName = tunnel != null ? tunnel.getName() : null; final String newName = preferences.getString(TunnelManager.KEY_PRIMARY_TUNNEL, null); if (!Objects.equals(currentName, newName)) { - final KeyedObservableList tunnels = tunnelManager.getTunnels(); + final ObservableKeyedList tunnels = tunnelManager.getTunnels(); final Tunnel newTunnel = newName != null ? tunnels.get(newName) : null; if (tunnel != null) tunnel.removeOnPropertyChangedCallback(tunnelCallback); diff --git a/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java b/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java index f4dd2aff..34ddb010 100644 --- a/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java +++ b/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java @@ -11,7 +11,7 @@ import android.widget.TextView; import com.wireguard.android.R; import com.wireguard.android.util.Keyed; -import com.wireguard.android.util.KeyedObservableList; +import com.wireguard.android.util.ObservableKeyedList; import com.wireguard.android.widget.ToggleSwitch; import com.wireguard.android.widget.ToggleSwitch.OnBeforeCheckedChangeListener; @@ -68,13 +68,13 @@ public final class BindingAdapters { @BindingAdapter({"items", "layout"}) public static > void setItems(final ListView view, - final KeyedObservableList oldList, final int oldLayoutId, - final KeyedObservableList newList, final int newLayoutId) { + final ObservableKeyedList oldList, final int oldLayoutId, + final ObservableKeyedList newList, final int newLayoutId) { if (oldList == newList && oldLayoutId == newLayoutId) return; // The ListAdapter interface is not generic, so this cannot be checked. - @SuppressWarnings("unchecked") KeyedObservableListAdapter adapter = - (KeyedObservableListAdapter) view.getAdapter(); + @SuppressWarnings("unchecked") ObservableKeyedListAdapter adapter = + (ObservableKeyedListAdapter) view.getAdapter(); // If the layout changes, any existing adapter must be replaced. if (adapter != null && oldList != null && oldLayoutId != newLayoutId) { adapter.setList(null); @@ -84,7 +84,7 @@ public final class BindingAdapters { if (newList == null || newLayoutId == 0) return; if (adapter == null) { - adapter = new KeyedObservableListAdapter<>(view.getContext(), newLayoutId, newList); + adapter = new ObservableKeyedListAdapter<>(view.getContext(), newLayoutId, newList); view.setAdapter(adapter); } // Either the list changed, or this is an entirely new listener because the layout changed. diff --git a/app/src/main/java/com/wireguard/android/databinding/KeyedObservableListAdapter.java b/app/src/main/java/com/wireguard/android/databinding/KeyedObservableListAdapter.java deleted file mode 100644 index d1d377aa..00000000 --- a/app/src/main/java/com/wireguard/android/databinding/KeyedObservableListAdapter.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.wireguard.android.databinding; - -import android.content.Context; -import android.databinding.DataBindingUtil; -import android.databinding.ObservableList; -import android.databinding.ViewDataBinding; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; - -import com.wireguard.android.BR; -import com.wireguard.android.util.Keyed; -import com.wireguard.android.util.KeyedObservableList; - -import java.lang.ref.WeakReference; - -/** - * A generic {@code ListAdapter} backed by a {@code KeyedObservableList}. - */ - -class KeyedObservableListAdapter> extends BaseAdapter { - private final OnListChangedCallback callback = new OnListChangedCallback<>(this); - private final int layoutId; - private final LayoutInflater layoutInflater; - private KeyedObservableList list; - - KeyedObservableListAdapter(final Context context, final int layoutId, - final KeyedObservableList list) { - this.layoutId = layoutId; - layoutInflater = LayoutInflater.from(context); - setList(list); - } - - @Override - public int getCount() { - return list != null ? list.size() : 0; - } - - @Override - public E getItem(final int position) { - if (list == null || position < 0 || position >= list.size()) - return null; - return list.get(position); - } - - @Override - public long getItemId(final int position) { - final K key = getKey(position); - return key != null ? key.hashCode() : -1; - } - - private K getKey(final int position) { - final E item = getItem(position); - return item != null ? item.getKey() : null; - } - - @Override - public View getView(final int position, final View convertView, final ViewGroup parent) { - ViewDataBinding binding = DataBindingUtil.getBinding(convertView); - if (binding == null) - binding = DataBindingUtil.inflate(layoutInflater, layoutId, parent, false); - binding.setVariable(BR.collection, list); - binding.setVariable(BR.key, getKey(position)); - binding.setVariable(BR.item, getItem(position)); - binding.executePendingBindings(); - return binding.getRoot(); - } - - @Override - public boolean hasStableIds() { - return true; - } - - void setList(final KeyedObservableList newList) { - if (list != null) - list.removeOnListChangedCallback(callback); - list = newList; - if (list != null) { - list.addOnListChangedCallback(callback); - } - notifyDataSetChanged(); - } - - private static final class OnListChangedCallback> - extends ObservableList.OnListChangedCallback> { - - private final WeakReference> weakAdapter; - - private OnListChangedCallback(final KeyedObservableListAdapter adapter) { - weakAdapter = new WeakReference<>(adapter); - } - - @Override - public void onChanged(final ObservableList sender) { - final KeyedObservableListAdapter adapter = weakAdapter.get(); - if (adapter != null) - adapter.notifyDataSetChanged(); - else - sender.removeOnListChangedCallback(this); - } - - @Override - public void onItemRangeChanged(final ObservableList sender, final int positionStart, - final int itemCount) { - onChanged(sender); - } - - @Override - public void onItemRangeInserted(final ObservableList sender, final int positionStart, - final int itemCount) { - onChanged(sender); - } - - @Override - public void onItemRangeMoved(final ObservableList sender, final int fromPosition, - final int toPosition, final int itemCount) { - onChanged(sender); - } - - @Override - public void onItemRangeRemoved(final ObservableList sender, final int positionStart, - final int itemCount) { - onChanged(sender); - } - } -} diff --git a/app/src/main/java/com/wireguard/android/databinding/ObservableKeyedListAdapter.java b/app/src/main/java/com/wireguard/android/databinding/ObservableKeyedListAdapter.java new file mode 100644 index 00000000..b22c8909 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/ObservableKeyedListAdapter.java @@ -0,0 +1,127 @@ +package com.wireguard.android.databinding; + +import android.content.Context; +import android.databinding.DataBindingUtil; +import android.databinding.ObservableList; +import android.databinding.ViewDataBinding; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import com.wireguard.android.BR; +import com.wireguard.android.util.Keyed; +import com.wireguard.android.util.ObservableKeyedList; + +import java.lang.ref.WeakReference; + +/** + * A generic {@code ListAdapter} backed by a {@code ObservableKeyedList}. + */ + +class ObservableKeyedListAdapter> extends BaseAdapter { + private final OnListChangedCallback callback = new OnListChangedCallback<>(this); + private final int layoutId; + private final LayoutInflater layoutInflater; + private ObservableKeyedList list; + + ObservableKeyedListAdapter(final Context context, final int layoutId, + final ObservableKeyedList list) { + this.layoutId = layoutId; + layoutInflater = LayoutInflater.from(context); + setList(list); + } + + @Override + public int getCount() { + return list != null ? list.size() : 0; + } + + @Override + public E getItem(final int position) { + if (list == null || position < 0 || position >= list.size()) + return null; + return list.get(position); + } + + @Override + public long getItemId(final int position) { + final K key = getKey(position); + return key != null ? key.hashCode() : -1; + } + + private K getKey(final int position) { + final E item = getItem(position); + return item != null ? item.getKey() : null; + } + + @Override + public View getView(final int position, final View convertView, final ViewGroup parent) { + ViewDataBinding binding = DataBindingUtil.getBinding(convertView); + if (binding == null) + binding = DataBindingUtil.inflate(layoutInflater, layoutId, parent, false); + binding.setVariable(BR.collection, list); + binding.setVariable(BR.key, getKey(position)); + binding.setVariable(BR.item, getItem(position)); + binding.executePendingBindings(); + return binding.getRoot(); + } + + @Override + public boolean hasStableIds() { + return true; + } + + void setList(final ObservableKeyedList newList) { + if (list != null) + list.removeOnListChangedCallback(callback); + list = newList; + if (list != null) { + list.addOnListChangedCallback(callback); + } + notifyDataSetChanged(); + } + + private static final class OnListChangedCallback> + extends ObservableList.OnListChangedCallback> { + + private final WeakReference> weakAdapter; + + private OnListChangedCallback(final ObservableKeyedListAdapter adapter) { + weakAdapter = new WeakReference<>(adapter); + } + + @Override + public void onChanged(final ObservableList sender) { + final ObservableKeyedListAdapter adapter = weakAdapter.get(); + if (adapter != null) + adapter.notifyDataSetChanged(); + else + sender.removeOnListChangedCallback(this); + } + + @Override + public void onItemRangeChanged(final ObservableList sender, final int positionStart, + final int itemCount) { + onChanged(sender); + } + + @Override + public void onItemRangeInserted(final ObservableList sender, final int positionStart, + final int itemCount) { + onChanged(sender); + } + + @Override + public void onItemRangeMoved(final ObservableList sender, final int fromPosition, + final int toPosition, final int itemCount) { + onChanged(sender); + } + + @Override + public void onItemRangeRemoved(final ObservableList sender, final int positionStart, + final int itemCount) { + onChanged(sender); + } + } +} diff --git a/app/src/main/java/com/wireguard/android/model/TunnelManager.java b/app/src/main/java/com/wireguard/android/model/TunnelManager.java index 123f5b51..68bb541a 100644 --- a/app/src/main/java/com/wireguard/android/model/TunnelManager.java +++ b/app/src/main/java/com/wireguard/android/model/TunnelManager.java @@ -8,8 +8,8 @@ import com.wireguard.android.configStore.ConfigStore; import com.wireguard.android.model.Tunnel.State; import com.wireguard.android.model.Tunnel.Statistics; import com.wireguard.android.util.ExceptionLoggers; -import com.wireguard.android.util.KeyedObservableList; -import com.wireguard.android.util.SortedKeyedObservableArrayList; +import com.wireguard.android.util.ObservableKeyedList; +import com.wireguard.android.util.ObservableSortedKeyedArrayList; import com.wireguard.config.Config; import java.util.Collections; @@ -37,8 +37,8 @@ public final class TunnelManager { private final Backend backend; private final ConfigStore configStore; private final SharedPreferences preferences; - private final KeyedObservableList tunnels = - new SortedKeyedObservableArrayList<>(); + private final ObservableKeyedList tunnels = + new ObservableSortedKeyedArrayList<>(); @Inject public TunnelManager(final Backend backend, final ConfigStore configStore, @@ -82,7 +82,7 @@ public final class TunnelManager { return backend.getStatistics(tunnel).thenApply(tunnel::onStatisticsChanged); } - public KeyedObservableList getTunnels() { + public ObservableKeyedList getTunnels() { return tunnels; } diff --git a/app/src/main/java/com/wireguard/android/util/KeyedObservableArrayList.java b/app/src/main/java/com/wireguard/android/util/KeyedObservableArrayList.java deleted file mode 100644 index 101f0ba7..00000000 --- a/app/src/main/java/com/wireguard/android/util/KeyedObservableArrayList.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.wireguard.android.util; - -import android.databinding.ObservableArrayList; -import android.support.annotation.NonNull; - -import java.util.Collection; -import java.util.ListIterator; -import java.util.Objects; - -/** - * ArrayList that allows looking up elements by some key property. As the key property must always - * be retrievable, this list cannot hold {@code null} elements. Because this class places no - * restrictions on the order or duplication of keys, lookup by key, as well as all list modification - * operations, require O(n) time. - */ - -public class KeyedObservableArrayList> - extends ObservableArrayList implements KeyedObservableList { - @Override - public boolean add(final E e) { - if (e == null) - throw new NullPointerException(); - return super.add(e); - } - - @Override - public void add(final int index, final E e) { - if (e == null) - throw new NullPointerException(); - super.add(index, e); - } - - @Override - public boolean addAll(@NonNull final Collection c) { - if (c.contains(null)) - throw new NullPointerException(); - return super.addAll(c); - } - - @Override - public boolean addAll(final int index, @NonNull final Collection c) { - if (c.contains(null)) - throw new NullPointerException(); - return super.addAll(index, c); - } - - @Override - public boolean containsAllKeys(final Collection keys) { - for (final K key : keys) - if (!containsKey(key)) - return false; - return true; - } - - @Override - public boolean containsKey(final K key) { - return indexOfKey(key) >= 0; - } - - @Override - public E get(final K key) { - final int index = indexOfKey(key); - return index >= 0 ? get(index) : null; - } - - @Override - public E getLast(final K key) { - final int index = lastIndexOfKey(key); - return index >= 0 ? get(index) : null; - } - - @Override - public int indexOfKey(final K key) { - final ListIterator iterator = listIterator(); - while (iterator.hasNext()) { - final int index = iterator.nextIndex(); - if (Objects.equals(iterator.next().getKey(), key)) - return index; - } - return -1; - } - - @Override - public int lastIndexOfKey(final K key) { - final ListIterator iterator = listIterator(size()); - while (iterator.hasPrevious()) { - final int index = iterator.previousIndex(); - if (Objects.equals(iterator.previous().getKey(), key)) - return index; - } - return -1; - } - - @Override - public E set(final int index, final E e) { - if (e == null) - throw new NullPointerException(); - return super.set(index, e); - } -} diff --git a/app/src/main/java/com/wireguard/android/util/KeyedObservableList.java b/app/src/main/java/com/wireguard/android/util/KeyedObservableList.java deleted file mode 100644 index 15df0bba..00000000 --- a/app/src/main/java/com/wireguard/android/util/KeyedObservableList.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wireguard.android.util; - -import android.databinding.ObservableList; - -/** - * A list that is both keyed and observable. - */ - -public interface KeyedObservableList> - extends KeyedList, ObservableList { -} diff --git a/app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java b/app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java new file mode 100644 index 00000000..6ba76207 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java @@ -0,0 +1,100 @@ +package com.wireguard.android.util; + +import android.databinding.ObservableArrayList; +import android.support.annotation.NonNull; + +import java.util.Collection; +import java.util.ListIterator; +import java.util.Objects; + +/** + * ArrayList that allows looking up elements by some key property. As the key property must always + * be retrievable, this list cannot hold {@code null} elements. Because this class places no + * restrictions on the order or duplication of keys, lookup by key, as well as all list modification + * operations, require O(n) time. + */ + +public class ObservableKeyedArrayList> + extends ObservableArrayList implements ObservableKeyedList { + @Override + public boolean add(final E e) { + if (e == null) + throw new NullPointerException(); + return super.add(e); + } + + @Override + public void add(final int index, final E e) { + if (e == null) + throw new NullPointerException(); + super.add(index, e); + } + + @Override + public boolean addAll(@NonNull final Collection c) { + if (c.contains(null)) + throw new NullPointerException(); + return super.addAll(c); + } + + @Override + public boolean addAll(final int index, @NonNull final Collection c) { + if (c.contains(null)) + throw new NullPointerException(); + return super.addAll(index, c); + } + + @Override + public boolean containsAllKeys(final Collection keys) { + for (final K key : keys) + if (!containsKey(key)) + return false; + return true; + } + + @Override + public boolean containsKey(final K key) { + return indexOfKey(key) >= 0; + } + + @Override + public E get(final K key) { + final int index = indexOfKey(key); + return index >= 0 ? get(index) : null; + } + + @Override + public E getLast(final K key) { + final int index = lastIndexOfKey(key); + return index >= 0 ? get(index) : null; + } + + @Override + public int indexOfKey(final K key) { + final ListIterator iterator = listIterator(); + while (iterator.hasNext()) { + final int index = iterator.nextIndex(); + if (Objects.equals(iterator.next().getKey(), key)) + return index; + } + return -1; + } + + @Override + public int lastIndexOfKey(final K key) { + final ListIterator iterator = listIterator(size()); + while (iterator.hasPrevious()) { + final int index = iterator.previousIndex(); + if (Objects.equals(iterator.previous().getKey(), key)) + return index; + } + return -1; + } + + @Override + public E set(final int index, final E e) { + if (e == null) + throw new NullPointerException(); + return super.set(index, e); + } +} diff --git a/app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java b/app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java new file mode 100644 index 00000000..af9fd34d --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java @@ -0,0 +1,11 @@ +package com.wireguard.android.util; + +import android.databinding.ObservableList; + +/** + * A list that is both keyed and observable. + */ + +public interface ObservableKeyedList> + extends KeyedList, ObservableList { +} diff --git a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java new file mode 100644 index 00000000..9ccc1c10 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java @@ -0,0 +1,105 @@ +package com.wireguard.android.util; + +import android.support.annotation.NonNull; + +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * KeyedArrayList that enforces uniqueness and sorted order across the set of keys. This class uses + * binary search to improve lookup and replacement times to O(log(n)). However, due to the + * array-based nature of this class, insertion and removal of elements with anything but the largest + * key still require O(n) time. + */ + +public class ObservableSortedKeyedArrayList, + E extends Keyed> extends ObservableKeyedArrayList { + private final transient List keyList = new KeyList<>(this); + + @Override + public boolean add(final E e) { + final int insertionPoint = getInsertionPoint(e); + if (insertionPoint < 0) { + // Skipping insertion is non-destructive if the new and existing objects are the same. + if (e == get(-insertionPoint - 1)) + return false; + throw new IllegalArgumentException("Element with same key already exists in list"); + } + super.add(insertionPoint, e); + return true; + } + + @Override + public void add(final int index, final E e) { + final int insertionPoint = getInsertionPoint(e); + if (insertionPoint < 0) + throw new IllegalArgumentException("Element with same key already exists in list"); + if (insertionPoint != index) + throw new IndexOutOfBoundsException("Wrong index given for element"); + super.add(index, e); + } + + @Override + public boolean addAll(@NonNull final Collection c) { + boolean didChange = false; + for (final E e : c) + if (add(e)) + didChange = true; + return didChange; + } + + @Override + public boolean addAll(int index, @NonNull final Collection c) { + for (final E e : c) + add(index++, e); + return true; + } + + private int getInsertionPoint(final E e) { + return -Collections.binarySearch(keyList, e.getKey()) - 1; + } + + @Override + public int indexOfKey(final K key) { + final int index = Collections.binarySearch(keyList, key); + return index >= 0 ? index : -1; + } + + @Override + public int lastIndexOfKey(final K key) { + // There can never be more than one element with the same key in the list. + return indexOfKey(key); + } + + @Override + public E set(final int index, final E e) { + if (e.getKey().compareTo(get(index).getKey()) != 0) { + // Allow replacement if the new key would be inserted adjacent to the replaced element. + final int insertionPoint = getInsertionPoint(e); + if (insertionPoint < index || insertionPoint > index + 1) + throw new IndexOutOfBoundsException("Wrong index given for element"); + } + return super.set(index, e); + } + + private static final class KeyList, + E extends Keyed> extends AbstractList { + private final ObservableSortedKeyedArrayList list; + + private KeyList(final ObservableSortedKeyedArrayList list) { + this.list = list; + } + + @Override + public K get(final int index) { + return list.get(index).getKey(); + } + + @Override + public int size() { + return list.size(); + } + } +} diff --git a/app/src/main/java/com/wireguard/android/util/SortedKeyedObservableArrayList.java b/app/src/main/java/com/wireguard/android/util/SortedKeyedObservableArrayList.java deleted file mode 100644 index 21fcccd3..00000000 --- a/app/src/main/java/com/wireguard/android/util/SortedKeyedObservableArrayList.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.wireguard.android.util; - -import android.support.annotation.NonNull; - -import java.util.AbstractList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * KeyedArrayList that enforces uniqueness and sorted order across the set of keys. This class uses - * binary search to improve lookup and replacement times to O(log(n)). However, due to the - * array-based nature of this class, insertion and removal of elements with anything but the largest - * key still require O(n) time. - */ - -public class SortedKeyedObservableArrayList, - E extends Keyed> extends KeyedObservableArrayList { - - private final transient List keyList = new KeyList<>(this); - - @Override - public boolean add(final E e) { - final int insertionPoint = getInsertionPoint(e); - if (insertionPoint < 0) { - // Skipping insertion is non-destructive if the new and existing objects are the same. - if (e == get(-insertionPoint - 1)) - return false; - throw new IllegalArgumentException("Element with same key already exists in list"); - } - super.add(insertionPoint, e); - return true; - } - - @Override - public void add(final int index, final E e) { - final int insertionPoint = getInsertionPoint(e); - if (insertionPoint < 0) - throw new IllegalArgumentException("Element with same key already exists in list"); - if (insertionPoint != index) - throw new IndexOutOfBoundsException("Wrong index given for element"); - super.add(index, e); - } - - @Override - public boolean addAll(@NonNull final Collection c) { - boolean didChange = false; - for (final E e : c) - if (add(e)) - didChange = true; - return didChange; - } - - @Override - public boolean addAll(int index, @NonNull final Collection c) { - for (final E e : c) - add(index++, e); - return true; - } - - private int getInsertionPoint(final E e) { - return -Collections.binarySearch(keyList, e.getKey()) - 1; - } - - @Override - public int indexOfKey(final K key) { - final int index = Collections.binarySearch(keyList, key); - return index >= 0 ? index : -1; - } - - @Override - public int lastIndexOfKey(final K key) { - // There can never be more than one element with the same key in the list. - return indexOfKey(key); - } - - @Override - public E set(final int index, final E e) { - if (e.getKey().compareTo(get(index).getKey()) != 0) { - // Allow replacement if the new key would be inserted adjacent to the replaced element. - final int insertionPoint = getInsertionPoint(e); - if (insertionPoint < index || insertionPoint > index + 1) - throw new IndexOutOfBoundsException("Wrong index given for element"); - } - return super.set(index, e); - } - - private static final class KeyList, - E extends Keyed> extends AbstractList { - private final SortedKeyedObservableArrayList list; - - private KeyList(final SortedKeyedObservableArrayList list) { - this.list = list; - } - - @Override - public K get(final int index) { - return list.get(index).getKey(); - } - - @Override - public int size() { - return list.size(); - } - } -} -- cgit v1.2.3