OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 19 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 19 Rev 182
Line 1... Line 1...
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
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
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
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.
9
 * language governing permissions and limitations under the License.
Line 20... Line 20...
20
import java.util.ArrayList;
20
import java.util.ArrayList;
21
import java.util.Collection;
21
import java.util.Collection;
22
import java.util.IdentityHashMap;
22
import java.util.IdentityHashMap;
23
import java.util.List;
23
import java.util.List;
24
import java.util.Map;
24
import java.util.Map;
-
 
25
import java.util.concurrent.CopyOnWriteArrayList;
-
 
26
 
-
 
27
import net.jcip.annotations.GuardedBy;
25
 
28
 
26
/**
29
/**
27
 * Allow to propagate ListChange to listeners and bound lists. Can also store all changes that it
30
 * Allow to propagate ListChange to listeners and bound lists. Can also store all changes that it
28
 * was notified and replay them with {@link #apply(List, ITransformer)}.
31
 * was notified and replay them with {@link #apply(List, ITransformer)}.
29
 * 
32
 *
Line 37... Line 40...
37
 
40
 
38
    private final List<ListChangeIndex<T>> changes;
41
    private final List<ListChangeIndex<T>> changes;
39
    // don't use PropertyChangeSupport since it isn't type safe and we're only interested in the
42
    // don't use PropertyChangeSupport since it isn't type safe and we're only interested in the
40
    // last change (and not the whole property, i.e. the whole list)
43
    // last change (and not the whole property, i.e. the whole list)
41
    private final List<IClosure<? super ListChangeIndex<T>>> listeners;
44
    private final List<IClosure<? super ListChangeIndex<T>>> listeners;
-
 
45
    @GuardedBy("this")
42
    private final Map<List<?>, Pair<?>> boundLists;
46
    private final Map<List<?>, Pair<?>> boundLists;
43
 
47
 
44
    /**
48
    /**
45
     * Create a new instance. Recording is only necessary for {@link #apply(List, ITransformer)}.
49
     * Create a new instance. Recording is only necessary for {@link #apply(List, ITransformer)}.
-
 
50
     * Thread-safe only if not recording.
46
     * 
51
     *
47
     * @param record <code>true</code> if all changes should be kept (this will leak memory until
52
     * @param record <code>true</code> if all changes should be kept (this will leak memory until
48
     *        {@link #clear()} is called).
53
     *        {@link #clear()} is called).
49
     */
54
     */
50
    public ListChangeRecipe(final boolean record) {
55
    public ListChangeRecipe(final boolean record) {
51
        super();
56
        super();
52
        this.changes = record ? new ArrayList<ListChangeIndex<T>>() : null;
57
        this.changes = record ? new ArrayList<ListChangeIndex<T>>() : null;
53
        this.listeners = new ArrayList<IClosure<? super ListChangeIndex<T>>>();
58
        this.listeners = new CopyOnWriteArrayList<>();
54
        // need IdentityHashMap since List.equals() depend on its items
59
        // need IdentityHashMap since List.equals() depend on its items
55
        // which will change
60
        // which will change
56
        this.boundLists = new IdentityHashMap<List<?>, Pair<?>>();
61
        this.boundLists = new IdentityHashMap<List<?>, Pair<?>>();
57
    }
62
    }
58
 
63
 
59
    public final boolean recordChanges() {
64
    public final boolean recordChanges() {
60
        return this.changes != null;
65
        return this.changes != null;
61
    }
66
    }
62
 
67
 
-
 
68
    /**
-
 
69
     * The list of changes since the last {@link #clear()}. Not thread-safe.
-
 
70
     * 
-
 
71
     * @return the list of changes
-
 
72
     */
63
    public final List<ListChangeIndex<T>> getChanges() {
73
    public final List<ListChangeIndex<T>> getChanges() {
64
        if (!this.recordChanges())
74
        if (!this.recordChanges())
65
            throw new IllegalStateException("This instance wasn't created to record changes");
75
            throw new IllegalStateException("This instance wasn't created to record changes");
66
        return this.changes;
76
        return this.changes;
67
    }
77
    }
68
 
78
 
69
    public void addListener(IClosure<? super ListChangeIndex<T>> l) {
79
    public void addListener(final IClosure<? super ListChangeIndex<T>> l) {
70
        this.listeners.add(l);
80
        this.listeners.add(l);
71
    }
81
    }
72
 
82
 
73
    public void rmListener(IClosure<? super ListChangeIndex<T>> l) {
83
    public void rmListener(final IClosure<? super ListChangeIndex<T>> l) {
74
        this.listeners.remove(l);
84
        this.listeners.remove(l);
75
    }
85
    }
76
 
86
 
77
    public void bind(List<T> l) {
87
    public void bind(final List<T> l) {
78
        this.bind(l, Transformer.<T> nopTransformer());
88
        this.bind(l, Transformer.<T> nopTransformer());
79
    }
89
    }
80
 
90
 
81
    /**
91
    /**
82
     * From now on, every change added to this will be applied immediately to <code>l</code>.
92
     * From now on, every change added to this will be applied immediately to <code>l</code>.
83
     * 
93
     *
84
     * @param <U> type of items of <code>l</code>.
94
     * @param <U> type of items of <code>l</code>.
85
     * @param l the list to keep in sync.
95
     * @param l the list to keep in sync.
86
     * @param transf the transformer.
96
     * @param transf the transformer.
87
     */
97
     */
88
    public <U> void bind(List<U> l, ITransformer<T, U> transf) {
98
    public synchronized <U> void bind(final List<U> l, final ITransformer<T, U> transf) {
89
        this.boundLists.put(l, new Pair<U>(l, transf));
99
        this.boundLists.put(l, new Pair<U>(l, transf));
90
    }
100
    }
91
 
101
 
92
    public <U> void unbind(List<U> l) {
102
    public synchronized <U> void unbind(final List<U> l) {
93
        this.boundLists.remove(l);
103
        this.boundLists.remove(l);
94
    }
104
    }
95
 
105
 
96
    private final void add(ListChangeIndex<T> change) {
106
    private final void add(final ListChangeIndex<T> change) {
97
        if (this.recordChanges())
107
        if (this.recordChanges())
98
            this.changes.add(change);
108
            this.changes.add(change);
99
        // must change bounded lists first, otherwise listeners couldn't access them
109
        // must change bounded lists first, otherwise listeners couldn't access them
-
 
110
        synchronized (this) {
100
        for (final Pair<?> p : this.boundLists.values()) {
111
            for (final Pair<?> p : this.boundLists.values()) {
101
            p.apply(change);
112
                p.apply(change);
102
        }
113
            }
103
        for (final IClosure<? super ListChangeIndex<T>> l : this.listeners)
114
            for (final IClosure<? super ListChangeIndex<T>> l : this.listeners)
104
            l.executeChecked(change);
115
                l.executeChecked(change);
105
    }
116
        }
-
 
117
    }
106
 
118
 
107
    public void add(int index0, Collection<? extends T> c) {
119
    public void add(final int index0, final Collection<? extends T> c) {
108
        this.add(new ListChangeIndex.Add<T>(index0, c));
120
        this.add(new ListChangeIndex.Add<T>(index0, c));
109
    }
121
    }
110
 
122
 
111
    public void remove(int index0, int index1, List<T> removed) {
123
    public void remove(final int index0, final int index1, final List<T> removed) {
112
        this.add(new ListChangeIndex.Rm<T>(index0, index1, removed));
124
        this.add(new ListChangeIndex.Rm<T>(index0, index1, removed));
113
    }
125
    }
114
 
126
 
115
    public void set(int index0, T old, T newItem) {
127
    public void set(final int index0, final T old, final T newItem) {
116
        this.add(new ListChangeIndex.Set<T>(index0, old, newItem));
128
        this.add(new ListChangeIndex.Set<T>(index0, old, newItem));
117
    }
129
    }
118
 
130
 
119
    /**
131
    /**
120
     * Clear all recorded changes. In general should be called after
132
     * Clear all recorded changes. In general should be called after
Line 132... Line 144...
132
     * @param transf transform items between this and <code>l</code>.
144
     * @param transf transform items between this and <code>l</code>.
133
     * @throws IllegalStateException if this instance doesn't {@link #recordChanges() record
145
     * @throws IllegalStateException if this instance doesn't {@link #recordChanges() record
134
     *         changes}.
146
     *         changes}.
135
     */
147
     */
136
    @Override
148
    @Override
137
    public <U> void apply(List<U> l, ITransformer<T, U> transf) throws IllegalStateException {
149
    public <U> void apply(final List<U> l, final ITransformer<T, U> transf) throws IllegalStateException {
138
        for (final ListChange<T> change : this.getChanges()) {
150
        for (final ListChange<T> change : this.getChanges()) {
139
            change.apply(l, transf);
151
            change.apply(l, transf);
140
        }
152
        }
141
    }
153
    }
142
 
154
 
143
    private final class Pair<U> {
155
    private final class Pair<U> {
144
 
156
 
145
        private final List<U> l;
157
        private final List<U> l;
146
        private final ITransformer<T, U> transf;
158
        private final ITransformer<T, U> transf;
147
 
159
 
148
        public Pair(List<U> l, ITransformer<T, U> transf) {
160
        public Pair(final List<U> l, final ITransformer<T, U> transf) {
149
            super();
161
            super();
150
            this.l = l;
162
            this.l = l;
151
            this.transf = transf;
163
            this.transf = transf;
152
        }
164
        }
153
 
165
 
154
        void apply(ListChange<T> change) {
166
        void apply(final ListChange<T> change) {
155
            change.apply(this.l, this.transf);
167
            change.apply(this.l, this.transf);
156
        }
168
        }
157
    }
169
    }
158
}
170
}