OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

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();
    }
}