OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 19 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 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.change;

import java.util.AbstractList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * A class that wraps a list, to detect every change made to it. The changes are available with
 * {@link #getRecipe()}.
 * 
 * @author Sylvain
 * 
 * @param <E> type of items.
 */
public class ListChangeRecorder<E> extends AbstractList<E> {

    private final List<E> delegate;
    private final ListChangeRecipe<E> recipe;

    public ListChangeRecorder(List<E> delegate) {
        this(delegate, false);
    }

    public ListChangeRecorder(List<E> delegate, final boolean keepHistory) {
        super();
        this.delegate = delegate;
        this.recipe = new ListChangeRecipe<E>(keepHistory);
    }

    public ListChangeRecipe<E> getRecipe() {
        return this.recipe;
    }

    // inherit from AbstractList to gain iterators & sublist
    // they only call regular methods of this class, so every change made through them is recorded
    // overload a maximum of methods to keep the original behaviour/optimization of our delegate

    // ** read only

    public E get(int index) {
        return this.delegate.get(index);
    }

    public int size() {
        return this.delegate.size();
    }

    public Object[] toArray() {
        return this.delegate.toArray();
    }

    public <T> T[] toArray(T[] a) {
        return this.delegate.toArray(a);
    }

    public boolean contains(Object o) {
        return this.delegate.contains(o);
    }

    public boolean containsAll(Collection<?> c) {
        return this.delegate.containsAll(c);
    }

    public boolean equals(Object o) {
        return this.delegate.equals(o);
    }

    public int hashCode() {
        return this.delegate.hashCode();
    }

    public int indexOf(Object o) {
        return this.delegate.indexOf(o);
    }

    public boolean isEmpty() {
        return this.delegate.isEmpty();
    }

    public int lastIndexOf(Object o) {
        return this.delegate.lastIndexOf(o);
    }

    // ** write, always change the delegate before notifying the recipe
    // otherwise the listeners will be told of the changes before they even happened

    public boolean add(E e) {
        final boolean res = this.delegate.add(e);
        // -1 since this just grew by one
        this.recipe.add(this.size() - 1, Collections.singleton(e));
        return res;
    }

    public void add(int index, E e) {
        this.delegate.add(index, e);
        this.recipe.add(index, Collections.singleton(e));
    }

    public boolean addAll(Collection<? extends E> c) {
        final int size = this.size();
        final boolean res = this.delegate.addAll(c);
        this.recipe.add(size, c);
        return res;
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        final boolean res = this.delegate.addAll(index, c);
        this.recipe.add(index, c);
        return res;
    }

    public void clear() {
        final List<E> copy = (List<E>) ListChangeIndex.copy(this);
        this.delegate.clear();
        this.recipe.remove(0, copy.size() - 1, copy);
    }

    public E set(int index, E element) {
        final E res = this.delegate.set(index, element);
        this.recipe.set(index, res, element);
        return res;
    }

    public E remove(int index) {
        final E res = this.delegate.remove(index);
        this.recipe.remove(index, index, Collections.singletonList(res));
        return res;
    }

    // objects

    public boolean remove(Object o) {
        final int index = this.indexOf(o);
        if (index < 0)
            return false;
        else {
            this.remove(index);
            return true;
        }
    }

    public boolean removeAll(Collection<?> c) {
        return this.changeAll(c, true);
    }

    public boolean retainAll(Collection<?> c) {
        return this.changeAll(c, false);
    }

    private boolean changeAll(Collection<?> c, boolean remove) {
        boolean modified = false;
        Iterator<?> e = iterator();
        while (e.hasNext()) {
            if (c.contains(e.next()) == remove) {
                e.remove();
                modified = true;
            }
        }
        return modified;
    }

}