OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 20 | Rev 80 | 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.search;
15
 
16
import org.openconcerto.sql.model.SQLRow;
17
import org.openconcerto.sql.model.SQLRowValues;
73 ilm 18
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
20 ilm 19
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
17 ilm 20
import org.openconcerto.sql.model.SQLTable;
73 ilm 21
import org.openconcerto.sql.model.graph.Link.Direction;
17 ilm 22
import org.openconcerto.sql.model.graph.Path;
73 ilm 23
import org.openconcerto.sql.model.graph.Step;
17 ilm 24
import org.openconcerto.sql.view.list.ITableModel;
25
import org.openconcerto.sql.view.list.LineListener;
26
import org.openconcerto.sql.view.list.ListAccess;
27
import org.openconcerto.sql.view.list.ListSQLLine;
28
import org.openconcerto.sql.view.search.SearchSpec;
29
import org.openconcerto.utils.CollectionMap;
30
import org.openconcerto.utils.IFutureTask;
31
import org.openconcerto.utils.RTInterruptedException;
73 ilm 32
import org.openconcerto.utils.RecursionType;
17 ilm 33
import org.openconcerto.utils.SleepingQueue;
34
import org.openconcerto.utils.cc.IPredicate;
35
import org.openconcerto.utils.cc.ITransformer;
36
 
37
import java.util.ArrayList;
38
import java.util.Collection;
39
import java.util.List;
40
import java.util.Set;
41
import java.util.concurrent.Callable;
42
import java.util.concurrent.ExecutionException;
43
import java.util.concurrent.FutureTask;
44
 
45
public final class SearchQueue extends SleepingQueue {
46
 
47
    /**
48
     * Whether the passed future performs a search.
49
     *
50
     * @param f a task in this queue, can be <code>null</code>.
51
     * @return <code>true</code> if <code>f</code> searches.
52
     */
53
    public static boolean isSearch(FutureTask<?> f) {
54
        return (f instanceof IFutureTask) && ((IFutureTask<?>) f).getRunnable() instanceof SearchRunnable;
55
    }
56
 
73 ilm 57
    /**
58
     * The last referent step of the passed path.
59
     *
60
     * @param p a path.
61
     * @return the name of the field of the last referent step, <code>null</code> if it doesn't
62
     *         exist.
63
     */
64
    public static String getLastReferentField(final Path p) {
65
        final Step lastStep = p.length() == 0 ? null : p.getStep(-1);
66
        final boolean lastIsForeign = lastStep == null || lastStep.getDirection() == Direction.FOREIGN;
67
        return lastIsForeign ? null : lastStep.getSingleField().getName();
68
    }
69
 
17 ilm 70
    private final ITableModel model;
71
    SearchSpec search;
72
    private final List<ListSQLLine> fullList;
73
    private final ListAccess listAccess;
74
    private final LineListener lineListener;
75
 
76
    public SearchQueue(final ListAccess la) {
77
        super(SearchQueue.class.getName() + " on " + la.getModel());
78
        this.listAccess = la;
79
        this.model = la.getModel();
80
        this.search = null;
81
        this.fullList = new ArrayList<ListSQLLine>();
82
 
83
        this.lineListener = new LineListener() {
84
            @Override
85
            public void lineChanged(int id, ListSQLLine l, Set<Integer> colIndex) {
86
                changeFullList(id, l, colIndex);
87
            }
88
        };
89
        this.getModel().getLinesSource().addLineListener(this.lineListener);
90
    }
91
 
92
    @Override
93
    protected void dying() {
94
        super.dying();
95
        this.getModel().getLinesSource().rmLineListener(this.lineListener);
96
    }
97
 
98
    /**
99
     * The lines and their path affected by a change of the passed row.
100
     *
73 ilm 101
     * @param r the row that has changed.
17 ilm 102
     * @return the refreshed lines and their changed paths.
103
     */
73 ilm 104
    public CollectionMap<ListSQLLine, Path> getAffectedLines(final SQLRow r) {
105
        return this.execGetAffected(r, new CollectionMap<ListSQLLine, Path>(), true);
17 ilm 106
    }
107
 
73 ilm 108
    public CollectionMap<Path, ListSQLLine> getAffectedPaths(final SQLRow r) {
109
        return this.execGetAffected(r, new CollectionMap<Path, ListSQLLine>(), false);
17 ilm 110
    }
111
 
73 ilm 112
    private <K, V> CollectionMap<K, V> execGetAffected(final SQLRow r, final CollectionMap<K, V> res, final boolean byLine) {
17 ilm 113
        return this.execute(new Callable<CollectionMap<K, V>>() {
114
            @Override
115
            public CollectionMap<K, V> call() throws Exception {
73 ilm 116
                return getAffected(r, res, byLine);
17 ilm 117
            }
118
        });
119
    }
120
 
121
    /**
122
     * Executes <code>c</code> in this queue, blocking the current thread.
123
     *
124
     * @param <R> type of result
125
     * @param c what to do.
126
     * @return the result of <code>c</code>.
127
     */
128
    private <R> R execute(final Callable<R> c) {
129
        try {
130
            return this.execute(new FutureTask<R>(c)).get();
131
        } catch (InterruptedException e) {
132
            throw new RTInterruptedException(e);
133
        } catch (ExecutionException e) {
134
            throw new IllegalStateException(e);
135
        }
136
    }
137
 
138
    // must be called from within this queue, as this method use fullList
73 ilm 139
    private <K, V> CollectionMap<K, V> getAffected(final SQLRow r, CollectionMap<K, V> res, boolean byLine) {
140
        final SQLTable t = r.getTable();
141
        final int id = r.getID();
17 ilm 142
        if (id < SQLRow.MIN_VALID_ID)
143
            throw new IllegalArgumentException("invalid ID: " + id);
144
        if (!this.fullList.isEmpty()) {
20 ilm 145
            final SQLRowValues proto = this.getModel().getLinesSource().getParent().getMaxGraph();
17 ilm 146
            final List<Path> pathsToT = new ArrayList<Path>();
73 ilm 147
            proto.getGraph().walk(proto, pathsToT, new ITransformer<State<List<Path>>, List<Path>>() {
17 ilm 148
                @Override
149
                public List<Path> transformChecked(State<List<Path>> input) {
150
                    if (input.getCurrent().getTable() == t) {
151
                        input.getAcc().add(input.getPath());
152
                    }
153
                    return input.getAcc();
154
                }
73 ilm 155
            }, RecursionType.BREADTH_FIRST, null);
17 ilm 156
            for (final Path p : pathsToT) {
73 ilm 157
                final String lastReferentField = getLastReferentField(p);
17 ilm 158
                for (final ListSQLLine line : this.fullList) {
73 ilm 159
                    boolean put = false;
160
                    for (final SQLRowValues current : line.getRow().followPath(p, CreateMode.CREATE_NONE, false)) {
161
                        // works for rowValues w/o any ID
162
                        if (current != null && current.getID() == id) {
163
                            put = true;
164
                        }
165
                    }
166
                    // if the modified row isn't in the existing line, it might still affect it if
167
                    // it's a referent row insertion
168
                    if (!put && lastReferentField != null && r.exists()) {
169
                        final int foreignID = r.getInt(lastReferentField);
170
                        for (final SQLRowValues current : line.getRow().followPath(p.minusLast(), CreateMode.CREATE_NONE, false)) {
171
                            if (current.getID() == foreignID) {
172
                                put = true;
173
                            }
174
                        }
175
                    }
176
                    if (put) {
17 ilm 177
                        // add to the list of paths that have been refreshed
178
                        if (byLine)
179
                            res.put(line, p);
180
                        else
181
                            res.put(p, line);
182
                    }
183
                }
184
            }
185
        }
186
        return res;
187
    }
188
 
189
    private synchronized void changeFullList(final int id, final ListSQLLine modifiedLine, final Collection<Integer> modifiedCols) {
190
        final SearchOne oneSearchRunnable = new SearchOne(this, id, modifiedLine, modifiedCols);
191
        this.putTask(new ChangeListOne("changeFullList " + id + " newLine: " + modifiedLine, this, modifiedLine, id, oneSearchRunnable));
192
        this.putTask(oneSearchRunnable);
193
    }
194
 
195
    public synchronized void setFullList(final List<ListSQLLine> l) {
196
        if (l == null)
197
            throw new NullPointerException();
198
        this.putTask(new ChangeListAll("setFullList", this, l));
199
        fullDataChange();
200
    }
201
 
202
    public synchronized void setSearch(final SearchSpec s) {
203
        this.putTask(new Runnable() {
204
            public void run() {
205
                SearchQueue.this.search = s;
206
            }
207
        });
208
        fullDataChange();
209
    }
210
 
211
    private synchronized void fullDataChange() {
212
        this.clearCompute();
213
        this.putTask(new SearchAll(this));
214
    }
215
 
216
    private synchronized void putTask(final Runnable r) {
217
        this.execute(new IFutureTask<Object>(r, null));
218
    }
219
 
220
    private synchronized void clearCompute() {
221
        this.cancel(new IPredicate<FutureTask<?>>() {
222
            @Override
223
            public boolean evaluateChecked(FutureTask<?> f) {
224
                return isSearch(f);
225
            }
226
        });
227
    }
228
 
229
    public String toString() {
230
        return this.getClass().getName() + " for " + this.getModel();
231
    }
232
 
233
    final SearchSpec getSearch() {
234
        return this.search;
235
    }
236
 
237
    final List<ListSQLLine> getFullList() {
238
        return this.fullList;
239
    }
240
 
241
    final ListAccess getAccess() {
242
        return this.listAccess;
243
    }
244
 
245
    public final int getFullListSize() {
246
        return this.fullList.size();
247
    }
248
 
249
    public final ITableModel getModel() {
250
        return this.model;
251
    }
252
}