OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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