OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 17 | Rev 73 | Go to most recent revision | 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
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
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.sql.view.list;
15
 
16
import org.openconcerto.sql.model.SQLTable;
17
import org.openconcerto.sql.model.SQLTableEvent;
61 ilm 18
import org.openconcerto.sql.model.SQLTableEvent.Mode;
17 ilm 19
import org.openconcerto.sql.model.SQLTableModifiedListener;
20
import org.openconcerto.sql.view.list.UpdateRunnable.RmAllRunnable;
21
import org.openconcerto.utils.IFutureTask;
22
import org.openconcerto.utils.SleepingQueue;
23
import org.openconcerto.utils.cc.IClosure;
24
 
25
import java.beans.PropertyChangeEvent;
26
import java.beans.PropertyChangeListener;
61 ilm 27
import java.util.Deque;
17 ilm 28
import java.util.concurrent.FutureTask;
29
 
30
import org.apache.commons.collections.CollectionUtils;
31
 
32
final class UpdateQueue extends SleepingQueue {
33
 
34
    /**
35
     * Whether the passed future performs an update.
36
     *
37
     * @param f a task in this queue, can be <code>null</code>.
38
     * @return <code>true</code> if <code>f</code> loads from the db.
39
     */
40
    static boolean isUpdate(FutureTask<?> f) {
41
        return (f instanceof IFutureTask) && ((IFutureTask<?>) f).getRunnable() instanceof UpdateRunnable;
42
    }
43
 
44
    private static boolean isCancelableUpdate(FutureTask<?> f) {
45
        // don't cancel RmAll so we can put an UpdateAll right after it (the UpdateAll won't be
46
        // executed since RmAll put the queue to sleep)
47
        return isUpdate(f) && !(((IFutureTask<?>) f).getRunnable() instanceof RmAllRunnable);
48
    }
49
 
50
    private final class TableListener implements SQLTableModifiedListener, PropertyChangeListener {
51
        public void tableModified(SQLTableEvent evt) {
52
            if (UpdateQueue.this.alwaysUpdateAll)
53
                putUpdateAll();
54
            else if (evt.getMode() == Mode.ROW_UPDATED) {
55
                rowModified(evt);
56
            } else if (evt.getMode() == Mode.ROW_ADDED) {
57
                rowAdded(evt.getTable(), evt.getId());
58
            } else if (evt.getMode() == Mode.ROW_DELETED) {
59
                rowDeleted(evt.getTable(), evt.getId());
60
            }
61
        }
62
 
63
        @Override
64
        public void propertyChange(PropertyChangeEvent evt) {
65
            // where changed
66
            putUpdateAll();
67
        }
68
    }
69
 
70
    private final ITableModel tableModel;
71
    private final TableListener tableListener;
72
    // TODO rm : needed for now since our optimizations are false if the graph contains referent
73
    // rows, see http://192.168.1.10:3000/issues/show/22
74
    private boolean alwaysUpdateAll = false;
75
 
76
    public UpdateQueue(ITableModel model) {
77
        super(UpdateQueue.class.getSimpleName() + " on " + model);
78
        this.tableModel = model;
79
        this.tableListener = new TableListener();
80
        // savoir quand les tables qu'on affiche changent
81
        addTableListener();
82
    }
83
 
84
    void setAlwaysUpdateAll(boolean b) {
85
        this.alwaysUpdateAll = b;
86
    }
87
 
88
    // *** listeners
89
 
90
    @Override
91
    protected void dying() {
92
        this.rmTableListener();
93
        super.dying();
94
    }
95
 
96
    private void addTableListener() {
97
        for (final SQLTable t : this.tableModel.getReq().getTables()) {
98
            t.addTableModifiedListener(this.tableListener);
99
        }
100
        this.tableModel.getLinesSource().addListener(this.tableListener);
101
    }
102
 
103
    private void rmTableListener() {
104
        for (final SQLTable t : this.tableModel.getReq().getTables()) {
105
            t.removeTableModifiedListener(this.tableListener);
106
        }
107
        this.tableModel.getLinesSource().rmListener(this.tableListener);
108
    }
109
 
110
    // *** une des tables que l'on affiche a changé
111
 
112
    void rowModified(final SQLTableEvent evt) {
113
        final int id = evt.getId();
114
        if (id < 0) {
115
            this.putUpdateAll();
116
        } else if (CollectionUtils.containsAny(this.tableModel.getReq().getLineFields(), evt.getFields())) {
117
            this.put(evt);
118
        }
119
        // si on n'affiche pas le champ ignorer
120
    }
121
 
122
    void rowAdded(SQLTable table, int id) {
123
        if (!table.equals(this.tableModel.getReq().getPrimaryTable())) {
124
            // on ignore
125
        } else {
126
            this.update(id);
127
        }
128
    }
129
 
130
    final void rowDeleted(SQLTable table, int id) {
131
        if (!table.equals(this.tableModel.getReq().getPrimaryTable())) {
132
            // on ignore
133
        } else {
134
            if (id < 0)
135
                // MAYBE faire tout effacer
136
                throw new IllegalArgumentException("remove id:" + id + " < 0");
137
 
138
            this.update(id);
139
        }
140
    }
141
 
142
    // *** puts
143
 
144
    private void update(final int id) {
145
        if (id < 0)
146
            this.putUpdateAll();
147
        else
148
            this.put(new SQLTableEvent(this.tableModel.getTable(), id, Mode.ROW_UPDATED));
149
    }
150
 
151
    private void put(SQLTableEvent evt) {
152
        this.put(UpdateRunnable.create(this.tableModel, evt));
153
    }
154
 
155
    public void putUpdateAll() {
156
        this.put(UpdateRunnable.create(this.tableModel));
157
    }
158
 
159
    /**
160
     * If this is sleeping, empty the list and call {@link #putUpdateAll()} so that the list reload
161
     * itself when this wakes up.
162
     *
163
     * @throws IllegalStateException if not sleeping.
164
     */
165
    void putRemoveAll() {
166
        if (!this.isSleeping())
167
            throw new IllegalStateException("not sleeping");
168
        // no user runnables can come between the RmAll and the UpdateAll since runnableAdded()
169
        // is blocked by our lock, so there won't be any incoherence for them
170
        this.put(UpdateRunnable.createRmAll(this, this.tableModel));
171
        this.setSleeping(false);
172
        // reload the empty list when waking up
173
        this.putUpdateAll();
174
    }
175
 
176
    protected void willPut(final Runnable qr) throws InterruptedException {
177
        if (qr instanceof ChangeAllRunnable) {
178
            // si on met tout à jour, ne sert à rien de garder les maj précédentes.
179
            // ATTN aux runnables qui dépendent des update, si on enlève les maj
180
            // elles vont s'executer sans que sa maj soit faite
61 ilm 181
            this.tasksDo(new IClosure<Deque<FutureTask<?>>>() {
17 ilm 182
                @Override
61 ilm 183
                public void executeChecked(final Deque<FutureTask<?>> tasks) {
17 ilm 184
                    // on part de la fin et on supprime toutes les maj jusqu'a ce qu'on trouve
185
                    // un runnable qui n'est pas un UpdateRunnable
186
                    FutureTask<?> current = tasks.peekLast();
187
                    boolean onlyUpdateRunnable = true;
188
                    while (current != null && onlyUpdateRunnable) {
189
                        onlyUpdateRunnable = isCancelableUpdate(current);
190
                        if (onlyUpdateRunnable) {
191
                            tasks.removeLast();
192
                            current = tasks.peekLast();
193
                        }
194
                    }
195
                    if (onlyUpdateRunnable) {
196
                        final FutureTask<?> br = getBeingRun();
197
                        if (br != null && isCancelableUpdate(br))
198
                            br.cancel(true);
199
                    }
200
                }
201
            });
202
        }
203
    }
204
 
205
}