OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 73 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
17 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils.change;
15
 
16
import java.util.AbstractList;
17
import java.util.Collection;
18
import java.util.Collections;
19
import java.util.Iterator;
20
import java.util.List;
21
 
22
/**
19 ilm 23
 * A class that wraps a list, to detect every change made to it. The changes are available with
182 ilm 24
 * {@link #getRecipe()}. Thread-safe if the delegate is itself thread-safe and not keeping history.
25
 *
17 ilm 26
 * @author Sylvain
182 ilm 27
 *
17 ilm 28
 * @param <E> type of items.
29
 */
30
public class ListChangeRecorder<E> extends AbstractList<E> {
31
 
32
    private final List<E> delegate;
33
    private final ListChangeRecipe<E> recipe;
34
 
182 ilm 35
    public ListChangeRecorder(final List<E> delegate) {
19 ilm 36
        this(delegate, false);
37
    }
38
 
182 ilm 39
    public ListChangeRecorder(final List<E> delegate, final boolean keepHistory) {
17 ilm 40
        super();
41
        this.delegate = delegate;
19 ilm 42
        this.recipe = new ListChangeRecipe<E>(keepHistory);
17 ilm 43
    }
44
 
45
    public ListChangeRecipe<E> getRecipe() {
46
        return this.recipe;
47
    }
48
 
49
    // inherit from AbstractList to gain iterators & sublist
50
    // they only call regular methods of this class, so every change made through them is recorded
51
    // overload a maximum of methods to keep the original behaviour/optimization of our delegate
52
 
53
    // ** read only
54
 
182 ilm 55
    @Override
56
    public E get(final int index) {
17 ilm 57
        return this.delegate.get(index);
58
    }
59
 
182 ilm 60
    @Override
17 ilm 61
    public int size() {
62
        return this.delegate.size();
63
    }
64
 
182 ilm 65
    @Override
17 ilm 66
    public Object[] toArray() {
67
        return this.delegate.toArray();
68
    }
69
 
182 ilm 70
    @Override
71
    public <T> T[] toArray(final T[] a) {
17 ilm 72
        return this.delegate.toArray(a);
73
    }
74
 
182 ilm 75
    @Override
76
    public boolean contains(final Object o) {
17 ilm 77
        return this.delegate.contains(o);
78
    }
79
 
182 ilm 80
    @Override
81
    public boolean containsAll(final Collection<?> c) {
17 ilm 82
        return this.delegate.containsAll(c);
83
    }
84
 
182 ilm 85
    @Override
86
    public boolean equals(final Object o) {
17 ilm 87
        return this.delegate.equals(o);
88
    }
89
 
182 ilm 90
    @Override
17 ilm 91
    public int hashCode() {
92
        return this.delegate.hashCode();
93
    }
94
 
182 ilm 95
    @Override
96
    public int indexOf(final Object o) {
17 ilm 97
        return this.delegate.indexOf(o);
98
    }
99
 
182 ilm 100
    @Override
17 ilm 101
    public boolean isEmpty() {
102
        return this.delegate.isEmpty();
103
    }
104
 
182 ilm 105
    @Override
106
    public int lastIndexOf(final Object o) {
17 ilm 107
        return this.delegate.lastIndexOf(o);
108
    }
109
 
110
    // ** write, always change the delegate before notifying the recipe
111
    // otherwise the listeners will be told of the changes before they even happened
112
 
182 ilm 113
    @Override
114
    public synchronized boolean add(final E e) {
17 ilm 115
        final boolean res = this.delegate.add(e);
116
        // -1 since this just grew by one
117
        this.recipe.add(this.size() - 1, Collections.singleton(e));
118
        return res;
119
    }
120
 
182 ilm 121
    @Override
122
    public synchronized void add(final int index, final E e) {
17 ilm 123
        this.delegate.add(index, e);
124
        this.recipe.add(index, Collections.singleton(e));
125
    }
126
 
182 ilm 127
    @Override
128
    public synchronized boolean addAll(final Collection<? extends E> c) {
73 ilm 129
        final int size = this.size();
17 ilm 130
        final boolean res = this.delegate.addAll(c);
73 ilm 131
        this.recipe.add(size, c);
17 ilm 132
        return res;
133
    }
134
 
182 ilm 135
    @Override
136
    public synchronized boolean addAll(final int index, final Collection<? extends E> c) {
17 ilm 137
        final boolean res = this.delegate.addAll(index, c);
138
        this.recipe.add(index, c);
139
        return res;
140
    }
141
 
182 ilm 142
    @Override
143
    public synchronized void clear() {
17 ilm 144
        final List<E> copy = (List<E>) ListChangeIndex.copy(this);
145
        this.delegate.clear();
146
        this.recipe.remove(0, copy.size() - 1, copy);
147
    }
148
 
182 ilm 149
    @Override
150
    public synchronized E set(final int index, final E element) {
17 ilm 151
        final E res = this.delegate.set(index, element);
152
        this.recipe.set(index, res, element);
153
        return res;
154
    }
155
 
182 ilm 156
    @Override
157
    public synchronized E remove(final int index) {
17 ilm 158
        final E res = this.delegate.remove(index);
159
        this.recipe.remove(index, index, Collections.singletonList(res));
160
        return res;
161
    }
162
 
163
    // objects
164
 
182 ilm 165
    @Override
166
    public synchronized boolean remove(final Object o) {
17 ilm 167
        final int index = this.indexOf(o);
168
        if (index < 0)
169
            return false;
170
        else {
171
            this.remove(index);
172
            return true;
173
        }
174
    }
175
 
182 ilm 176
    @Override
177
    public boolean removeAll(final Collection<?> c) {
17 ilm 178
        return this.changeAll(c, true);
179
    }
180
 
182 ilm 181
    @Override
182
    public boolean retainAll(final Collection<?> c) {
17 ilm 183
        return this.changeAll(c, false);
184
    }
185
 
182 ilm 186
    private synchronized boolean changeAll(final Collection<?> c, final boolean remove) {
17 ilm 187
        boolean modified = false;
182 ilm 188
        final Iterator<?> e = iterator();
17 ilm 189
        while (e.hasNext()) {
190
            if (c.contains(e.next()) == remove) {
191
                e.remove();
192
                modified = true;
193
            }
194
        }
195
        return modified;
196
    }
197
 
198
}