OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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