|
|
@@ -0,0 +1,513 @@
|
|
|
+package net.ranides.assira.collection.lists;
|
|
|
+
|
|
|
+import net.ranides.assira.generic.SerializableCode;
|
|
|
+
|
|
|
+import java.io.Serializable;
|
|
|
+import java.util.*;
|
|
|
+import java.util.function.Consumer;
|
|
|
+import java.util.function.Predicate;
|
|
|
+import java.util.function.UnaryOperator;
|
|
|
+import java.util.stream.Stream;
|
|
|
+
|
|
|
+public class CopyList<T> implements List<T>, Serializable {
|
|
|
+
|
|
|
+ private transient List<T> source;
|
|
|
+
|
|
|
+ private transient boolean original;
|
|
|
+
|
|
|
+ public CopyList(T[] source) {
|
|
|
+ this.source = Arrays.asList(source);
|
|
|
+ this.original = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public CopyList(List<T> source) {
|
|
|
+ this.source = source;
|
|
|
+ this.original = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return source.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isEmpty() {
|
|
|
+ return source.isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean contains(Object o) {
|
|
|
+ return source.contains(o);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void forEach(Consumer<? super T> action) {
|
|
|
+ source.forEach(action);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object[] toArray() {
|
|
|
+ return source.toArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public <E> E[] toArray(E[] a) {
|
|
|
+ return source.toArray(a);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean add(T t) {
|
|
|
+ detach();
|
|
|
+ return source.add(t);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean remove(Object o) {
|
|
|
+ detach();
|
|
|
+ return source.remove(o);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean containsAll(Collection<?> c) {
|
|
|
+ return source.containsAll(c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean addAll(Collection<? extends T> c) {
|
|
|
+ detach();
|
|
|
+ return source.addAll(c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean addAll(int index, Collection<? extends T> c) {
|
|
|
+ detach();
|
|
|
+ return source.addAll(index, c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean removeAll(Collection<?> c) {
|
|
|
+ detach();
|
|
|
+ return source.removeAll(c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean removeIf(Predicate<? super T> filter) {
|
|
|
+ detach();
|
|
|
+ return source.removeIf(filter);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean retainAll(Collection<?> c) {
|
|
|
+ detach();
|
|
|
+ return source.retainAll(c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void replaceAll(UnaryOperator<T> operator) {
|
|
|
+ detach();
|
|
|
+ source.replaceAll(operator);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void sort(Comparator<? super T> c) {
|
|
|
+ detach();
|
|
|
+ source.sort(c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void clear() {
|
|
|
+ if (!original) {
|
|
|
+ source.clear();
|
|
|
+ } else {
|
|
|
+ source = Collections.emptyList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T get(int index) {
|
|
|
+ return source.get(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T set(int index, T element) {
|
|
|
+ detach();
|
|
|
+ return source.set(index, element);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void add(int index, T element) {
|
|
|
+ detach();
|
|
|
+ source.add(index, element);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T remove(int index) {
|
|
|
+ detach();
|
|
|
+ return source.remove(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int indexOf(Object o) {
|
|
|
+ return source.indexOf(o);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int lastIndexOf(Object o) {
|
|
|
+ return source.lastIndexOf(o);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ if (!original) {
|
|
|
+ return source.iterator();
|
|
|
+ } else {
|
|
|
+ return new CopyIterator(source.listIterator());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ListIterator<T> listIterator() {
|
|
|
+ if (!original) {
|
|
|
+ return source.listIterator();
|
|
|
+ } else {
|
|
|
+ return new CopyIterator(source.listIterator());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ListIterator<T> listIterator(int index) {
|
|
|
+ if (!original) {
|
|
|
+ return source.listIterator(index);
|
|
|
+ } else {
|
|
|
+ return new CopyIterator(source.listIterator(index));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<T> subList(int fromIndex, int toIndex) {
|
|
|
+ if(!original) {
|
|
|
+ return source.subList(fromIndex, toIndex);
|
|
|
+ } else {
|
|
|
+ return new CopyFragment(null, fromIndex, toIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Spliterator<T> spliterator() {
|
|
|
+ return source.spliterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> stream() {
|
|
|
+ return source.stream();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> parallelStream() {
|
|
|
+ return source.parallelStream();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() {
|
|
|
+ return source.hashCode();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object obj) {
|
|
|
+ if (obj == this) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (obj instanceof CopyList) {
|
|
|
+ return source.equals(((CopyList<?>)obj).source);
|
|
|
+ }
|
|
|
+ return source.equals(obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return source.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void writeObject(java.io.ObjectOutputStream ostream) throws java.io.IOException {
|
|
|
+ ostream.defaultWriteObject();
|
|
|
+ ostream.writeInt(source.size());
|
|
|
+ for(T v : source) {
|
|
|
+ ostream.writeObject(v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void readObject(java.io.ObjectInputStream istream) throws java.io.IOException, ClassNotFoundException {
|
|
|
+ istream.defaultReadObject();
|
|
|
+ int size = istream.readInt();
|
|
|
+ source = new ArrayList<>(size);
|
|
|
+ for(int i=0; i<size; i++) {
|
|
|
+ source.add((T)istream.readObject());
|
|
|
+ }
|
|
|
+ original = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void detach() {
|
|
|
+ if(original) {
|
|
|
+ source = new ArrayList<>(source);
|
|
|
+ original = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private class CopyIterator implements ListIterator<T> {
|
|
|
+
|
|
|
+ private ListIterator<T> itr;
|
|
|
+
|
|
|
+ private int index = -1;
|
|
|
+
|
|
|
+ public CopyIterator(ListIterator<T> itr) {
|
|
|
+ this.itr = itr;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean hasNext() {
|
|
|
+ return itr.hasNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T next() {
|
|
|
+ index = itr.nextIndex();
|
|
|
+ return itr.next();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean hasPrevious() {
|
|
|
+ return itr.hasPrevious();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T previous() {
|
|
|
+ index = itr.previousIndex();
|
|
|
+ return itr.previous();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int nextIndex() {
|
|
|
+ return itr.nextIndex();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int previousIndex() {
|
|
|
+ return itr.previousIndex();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void set(T t) {
|
|
|
+ if(!original) {
|
|
|
+ itr.set(t);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(index == -1) {
|
|
|
+ throw new IllegalStateException();
|
|
|
+ }
|
|
|
+ CopyList.this.set(index, t);
|
|
|
+ itr = source.listIterator(index+1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void add(T t) {
|
|
|
+ if(!original) {
|
|
|
+ itr.add(t);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ CopyList.this.add(itr.nextIndex(), t);
|
|
|
+ itr = source.listIterator(itr.nextIndex()+1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void remove() {
|
|
|
+ if(!original) {
|
|
|
+ itr.remove();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(index == -1) {
|
|
|
+ throw new IllegalStateException();
|
|
|
+ }
|
|
|
+ CopyList.this.remove(index);
|
|
|
+ itr = source.listIterator(index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private class CopyFragment extends AbstractList<T> implements Serializable {
|
|
|
+
|
|
|
+ private final CopyFragment parent;
|
|
|
+
|
|
|
+ private final int offset;
|
|
|
+
|
|
|
+ private int size;
|
|
|
+
|
|
|
+ CopyFragment(CopyFragment parent, int fromIndex, int toIndex) {
|
|
|
+ this.parent = parent;
|
|
|
+ if (fromIndex < 0) {
|
|
|
+ throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
|
|
|
+ }
|
|
|
+ if (toIndex > source.size()) {
|
|
|
+ throw new IndexOutOfBoundsException("toIndex = " + toIndex);
|
|
|
+ }
|
|
|
+ if (fromIndex > toIndex) {
|
|
|
+ throw new IllegalArgumentException("fromIndex(" + fromIndex +") > toIndex(" + toIndex + ")");
|
|
|
+ }
|
|
|
+ offset = fromIndex;
|
|
|
+ size = toIndex - fromIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ public T set(int index, T element) {
|
|
|
+ rangeCheck(index);
|
|
|
+ return CopyList.this.set(index+offset, element);
|
|
|
+ }
|
|
|
+
|
|
|
+ public T get(int index) {
|
|
|
+ rangeCheck(index);
|
|
|
+ return source.get(index+offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ public int size() {
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void add(int index, T element) {
|
|
|
+ rangeCheckForAdd(index);
|
|
|
+ CopyList.this.add(index+offset, element);
|
|
|
+ size++;
|
|
|
+ if(parent != null) {
|
|
|
+ parent.size++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public T remove(int index) {
|
|
|
+ rangeCheck(index);
|
|
|
+ T result = CopyList.this.remove(index+offset);
|
|
|
+ size--;
|
|
|
+ if(parent != null) {
|
|
|
+ parent.size--;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void removeRange(int fromIndex, int toIndex) {
|
|
|
+ detach();
|
|
|
+ source.subList(offset+fromIndex, offset+toIndex).clear();
|
|
|
+ size -= (toIndex-fromIndex);
|
|
|
+ if(parent != null) {
|
|
|
+ parent.size -= (toIndex-fromIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean addAll(Collection<? extends T> c) {
|
|
|
+ return addAll(size, c);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean addAll(int index, Collection<? extends T> c) {
|
|
|
+ rangeCheckForAdd(index);
|
|
|
+ int n = c.size();
|
|
|
+ if (n==0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ CopyList.this.addAll(offset+index, c);
|
|
|
+ size += n;
|
|
|
+ if(parent != null) {
|
|
|
+ parent.size += n;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ return listIterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ public ListIterator<T> listIterator(final int index) {
|
|
|
+ rangeCheckForAdd(index);
|
|
|
+ return new FragmentIterator(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<T> subList(int fromIndex, int toIndex) {
|
|
|
+ rangeCheck(fromIndex);
|
|
|
+ rangeCheckForAdd(toIndex);
|
|
|
+ return new CopyFragment(this, offset+fromIndex, offset+toIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected Object writeReplace() {
|
|
|
+ return new CopyList<>(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void rangeCheck(int index) {
|
|
|
+ if (index < 0 || index >= size)
|
|
|
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void rangeCheckForAdd(int index) {
|
|
|
+ if (index < 0 || index > size)
|
|
|
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
|
|
|
+ }
|
|
|
+
|
|
|
+ private String outOfBoundsMsg(int index) {
|
|
|
+ return "Index: "+index+", Size: "+size;
|
|
|
+ }
|
|
|
+
|
|
|
+ private class FragmentIterator implements ListIterator<T> {
|
|
|
+ private final ListIterator<T> i;
|
|
|
+
|
|
|
+ public FragmentIterator(int index) {
|
|
|
+ i = CopyList.this.listIterator(index + offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean hasNext() {
|
|
|
+ return nextIndex() < size;
|
|
|
+ }
|
|
|
+
|
|
|
+ public T next() {
|
|
|
+ if (hasNext()) {
|
|
|
+ return i.next();
|
|
|
+ } else {
|
|
|
+ throw new NoSuchElementException();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean hasPrevious() {
|
|
|
+ return previousIndex() >= 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ public T previous() {
|
|
|
+ if (hasPrevious()) {
|
|
|
+ return i.previous();
|
|
|
+ } else {
|
|
|
+ throw new NoSuchElementException();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public int nextIndex() {
|
|
|
+ return i.nextIndex() - offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int previousIndex() {
|
|
|
+ return i.previousIndex() - offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void remove() {
|
|
|
+ i.remove();
|
|
|
+ size--;
|
|
|
+ if(parent != null) {
|
|
|
+ parent.size--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void set(T e) {
|
|
|
+ i.set(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void add(T e) {
|
|
|
+ i.add(e);
|
|
|
+ size++;
|
|
|
+ if(parent != null) {
|
|
|
+ parent.size++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|