Dépôt officiel du code source de l'ERP OpenConcerto
Blame | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.cc;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.UnaryOperator;
import net.jcip.annotations.ThreadSafe;
/**
* A thread-safe list in which all mutative operations (set, remove, and so on) are implemented by
* making a fresh copy of the underlying list. The instance of the underlying list can be customized
* with {@link #copy(Collection)} and {@link #create(int)}. The underlying immutable list is
* available with {@link #getImmutable()}.
*
* @author Sylvain
*
* @param <T> the type of elements in this list
*/
@ThreadSafe
public class CopyOnWriteList<T> extends AbstractList<T> {
private List<T> immutable;
public CopyOnWriteList() {
this.immutable = Collections.emptyList();
}
public CopyOnWriteList(final T single) {
this.immutable = Collections.singletonList(single);
}
public CopyOnWriteList(final Collection<? extends T> m) {
this.immutable = Collections.unmodifiableList(copy(m));
}
/**
* Create a copy of the passed collection.
*
* @param src the collection to copy, not <code>null</code>.
* @return a shallow copy of <code>src</code>.
*/
protected List<T> copy(final Collection<? extends T> src) {
return new ArrayList<>(src);
}
protected List<T> create(int capacity) {
return new ArrayList<>(capacity);
}
// Like CopyOnWriteArrayList, iterate on immutable (otherwise would have to at least increment
// this.modCount).
@Override
public Iterator<T> iterator() {
return this.getImmutable().iterator();
}
@Override
public ListIterator<T> listIterator(int index) {
return this.getImmutable().listIterator(index);
}
// write
private void rangeCheck(final int index, final boolean forAdd, final int size) {
if (index < 0 || index > size || (!forAdd && index == size))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size();
}
@Override
public synchronized T set(int index, T element) {
final List<T> copy = copy(this.immutable);
final T res = copy.set(index, element);
this.immutable = Collections.unmodifiableList(copy);
return res;
}
@Override
public synchronized boolean add(T e) {
return super.add(e);
}
@Override
public void add(int index, T element) {
this.addAll(index, Collections.singletonList(element));
}
@Override
public synchronized boolean addAll(Collection<? extends T> c) {
return this.addAll(this.size(), c);
}
@Override
public synchronized boolean addAll(int index, Collection<? extends T> c) {
final int size = this.size();
this.rangeCheck(index, true, size);
final int cSize = c.size();
if (cSize == 0)
return false;
final List<T> copy = create(size + cSize);
for (int i = 0; i <= size; i++) {
if (i == index)
copy.addAll(c);
if (i < size)
copy.add(this.get(i));
}
assert copy.size() == size + cSize;
this.immutable = Collections.unmodifiableList(copy);
return true;
}
@Override
public synchronized T remove(final int index) {
final int size = this.size();
this.rangeCheck(index, false, size);
final List<T> copy = create(size - 1);
T res = null;
for (int i = 0; i < size; i++) {
if (i == index)
res = this.get(i);
else
copy.add(this.get(i));
}
assert copy.size() == size - 1;
this.immutable = Collections.unmodifiableList(copy);
return res;
}
@Override
public synchronized boolean remove(Object o) {
final int indexOf = this.indexOf(o);
if (indexOf < 0)
return false;
this.remove(indexOf);
return true;
}
@Override
public synchronized boolean removeAll(Collection<?> c) {
final List<T> copy = copy(this.immutable);
final boolean res = copy.removeAll(c);
this.immutable = Collections.unmodifiableList(copy);
return res;
}
@Override
public synchronized void clear() {
this.immutable = Collections.emptyList();
}
@Override
public synchronized boolean retainAll(Collection<?> c) {
final List<T> copy = copy(this.immutable);
final boolean res = copy.retainAll(c);
this.immutable = Collections.unmodifiableList(copy);
return res;
}
@Override
public synchronized void replaceAll(UnaryOperator<T> operator) {
final List<T> copy = copy(this.immutable);
copy.replaceAll(operator);
this.immutable = Collections.unmodifiableList(copy);
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public synchronized void sort(Comparator<? super T> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
final List<T> copy = create(this.size());
for (Object e : a) {
copy.add((T) e);
}
this.immutable = Collections.unmodifiableList(copy);
}
// read
public synchronized final List<T> getImmutable() {
return this.immutable;
}
@Override
public T get(int index) {
return this.getImmutable().get(index);
}
@Override
public int size() {
return this.getImmutable().size();
}
@Override
public boolean isEmpty() {
return this.getImmutable().isEmpty();
}
@Override
public boolean contains(Object o) {
return this.getImmutable().contains(o);
}
// equals
@Override
public boolean equals(final Object o) {
return this.getImmutable().equals(o);
}
@Override
public int hashCode() {
return this.getImmutable().hashCode();
}
}