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 12... |
Line 12... |
12 |
*/
|
12 |
*/
|
13 |
|
13 |
|
14 |
package org.openconcerto.sql.view.list;
|
14 |
package org.openconcerto.sql.view.list;
|
15 |
|
15 |
|
16 |
import org.openconcerto.sql.Log;
|
16 |
import org.openconcerto.sql.Log;
|
- |
|
17 |
import org.openconcerto.sql.model.RowRef;
|
17 |
import org.openconcerto.sql.model.SQLRow;
|
18 |
import org.openconcerto.sql.model.SQLRow;
|
- |
|
19 |
import org.openconcerto.sql.model.SQLRowAccessor;
|
18 |
import org.openconcerto.sql.model.SQLRowValues;
|
20 |
import org.openconcerto.sql.model.SQLRowValues;
|
19 |
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
|
- |
|
20 |
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
|
21 |
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
|
21 |
import org.openconcerto.sql.model.SQLTable;
|
22 |
import org.openconcerto.sql.model.SQLTable;
|
22 |
import org.openconcerto.sql.model.SQLTableEvent;
|
23 |
import org.openconcerto.sql.model.SQLTableEvent;
|
23 |
import org.openconcerto.sql.model.SQLTableModifiedListener;
|
24 |
import org.openconcerto.sql.model.SQLTableModifiedListener;
|
24 |
import org.openconcerto.sql.model.graph.Link.Direction;
|
25 |
import org.openconcerto.sql.model.graph.Link.Direction;
|
25 |
import org.openconcerto.sql.model.graph.Path;
|
26 |
import org.openconcerto.sql.model.graph.Path;
|
- |
|
27 |
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
|
26 |
import org.openconcerto.sql.view.list.UpdateRunnable.RmAllRunnable;
|
28 |
import org.openconcerto.sql.view.list.UpdateRunnable.RmAllRunnable;
|
27 |
import org.openconcerto.sql.view.list.search.SearchOne;
|
29 |
import org.openconcerto.sql.view.list.search.SearchOne;
|
28 |
import org.openconcerto.sql.view.list.search.SearchOne.Mode;
|
30 |
import org.openconcerto.sql.view.list.search.SearchOne.Mode;
|
29 |
import org.openconcerto.sql.view.list.search.SearchQueue;
|
31 |
import org.openconcerto.sql.view.list.search.SearchQueue;
|
30 |
import org.openconcerto.sql.view.list.search.SearchQueue.SetStateRunnable;
|
32 |
import org.openconcerto.sql.view.list.search.SearchQueue.SetStateRunnable;
|
Line 45... |
Line 47... |
45 |
import java.util.HashSet;
|
47 |
import java.util.HashSet;
|
46 |
import java.util.Iterator;
|
48 |
import java.util.Iterator;
|
47 |
import java.util.List;
|
49 |
import java.util.List;
|
48 |
import java.util.Set;
|
50 |
import java.util.Set;
|
49 |
import java.util.concurrent.RunnableFuture;
|
51 |
import java.util.concurrent.RunnableFuture;
|
- |
|
52 |
import java.util.function.BiConsumer;
|
50 |
import java.util.logging.Level;
|
53 |
import java.util.logging.Level;
|
51 |
|
54 |
|
52 |
import net.jcip.annotations.GuardedBy;
|
55 |
import net.jcip.annotations.GuardedBy;
|
53 |
|
56 |
|
54 |
public final class UpdateQueue extends SleepingQueue {
|
57 |
public final class UpdateQueue extends SleepingQueue {
|
Line 197... |
Line 200... |
197 |
}
|
200 |
}
|
198 |
return res;
|
201 |
return res;
|
199 |
}
|
202 |
}
|
200 |
|
203 |
|
201 |
/**
|
204 |
/**
|
202 |
* The lines and their path affected by a change of the passed row.
|
205 |
* The lines affected by a change of the passed row.
|
203 |
*
|
206 |
*
|
204 |
* @param r the row that has changed.
|
207 |
* @param r the row that has changed.
|
205 |
* @return the refreshed lines and their changed paths.
|
208 |
* @return the refreshed lines.
|
206 |
*/
|
209 |
*/
|
207 |
protected final ListMap<ListSQLLine, Path> getAffectedLines(final SQLRow r) {
|
210 |
protected final Set<Integer> getAffectedLines(final SQLRow r) {
|
- |
|
211 |
final Set<Integer> res = new HashSet<>();
|
208 |
return this.getAffected(r, new ListMap<ListSQLLine, Path>(), true);
|
212 |
this.getAffected(r, (p, l) -> res.add(l.getID()));
|
- |
|
213 |
return res;
|
209 |
}
|
214 |
}
|
210 |
|
215 |
|
211 |
protected final ListMap<Path, ListSQLLine> getAffectedPaths(final SQLRow r) {
|
216 |
protected final ListMap<Path, ListSQLLine> getAffectedPaths(final SQLRow r) {
|
212 |
return this.getAffected(r, new ListMap<Path, ListSQLLine>(), false);
|
217 |
final ListMap<Path, ListSQLLine> res = new ListMap<>();
|
- |
|
218 |
this.getAffected(r, res::add);
|
- |
|
219 |
return res;
|
213 |
}
|
220 |
}
|
214 |
|
221 |
|
215 |
// must be called from within this queue, as this method use fullList
|
222 |
// must be called from within this queue, as this method use fullList
|
216 |
private <K, V> ListMap<K, V> getAffected(final SQLRow r, final ListMap<K, V> res, final boolean byLine) {
|
223 |
private void getAffected(final SQLRow r, final BiConsumer<Path, ListSQLLine> cons) {
|
217 |
final List<ListSQLLine> fullList = this.getFullList();
|
224 |
final List<ListSQLLine> fullList = this.getFullList();
|
218 |
synchronized (fullList) {
|
225 |
synchronized (fullList) {
|
219 |
final SQLTable t = r.getTable();
|
226 |
final SQLTable t = r.getTable();
|
220 |
final int id = r.getID();
|
227 |
final int id = r.getID();
|
221 |
if (id < SQLRow.MIN_VALID_ID)
|
228 |
if (id < SQLRow.MIN_VALID_ID)
|
222 |
throw new IllegalArgumentException("invalid ID: " + id);
|
229 |
throw new IllegalArgumentException("invalid ID: " + id);
|
223 |
if (!fullList.isEmpty()) {
|
230 |
if (!fullList.isEmpty()) {
|
- |
|
231 |
final boolean keepGraph = this.getModel().getReq().getKeepMode() == KeepMode.GRAPH;
|
224 |
final SQLRowValues proto = this.getState().getReq().getGraphToFetch();
|
232 |
final SQLRowValues proto = this.getState().getReq().getGraphToFetch();
|
225 |
final List<Path> pathsToT = new ArrayList<Path>();
|
233 |
final List<Path> pathsToT = new ArrayList<Path>();
|
226 |
proto.getGraph().walk(proto, pathsToT, new ITransformer<State<List<Path>>, List<Path>>() {
|
234 |
proto.getGraph().walk(proto, pathsToT, new ITransformer<State<List<Path>>, List<Path>>() {
|
227 |
@Override
|
235 |
@Override
|
228 |
public List<Path> transformChecked(State<List<Path>> input) {
|
236 |
public List<Path> transformChecked(State<List<Path>> input) {
|
Line 230... |
Line 238... |
230 |
input.getAcc().add(input.getPath());
|
238 |
input.getAcc().add(input.getPath());
|
231 |
}
|
239 |
}
|
232 |
return input.getAcc();
|
240 |
return input.getAcc();
|
233 |
}
|
241 |
}
|
234 |
}, RecursionType.BREADTH_FIRST, Direction.ANY);
|
242 |
}, RecursionType.BREADTH_FIRST, Direction.ANY);
|
- |
|
243 |
// paths aren't stored when !keepGraph
|
- |
|
244 |
if (!keepGraph) {
|
- |
|
245 |
final RowRef idRef = r.getRowRef();
|
- |
|
246 |
for (final ListSQLLine line : fullList) {
|
- |
|
247 |
if (line.getPKs().contains(idRef))
|
- |
|
248 |
cons.accept(null, line);
|
- |
|
249 |
}
|
- |
|
250 |
}
|
235 |
for (final Path p : pathsToT) {
|
251 |
for (final Path p : pathsToT) {
|
236 |
final String lastReferentField = SearchQueue.getLastReferentField(p);
|
252 |
final String lastReferentField = SearchQueue.getLastReferentField(p);
|
- |
|
253 |
final RowRef foreignRef;
|
- |
|
254 |
if (lastReferentField != null && r.exists()) {
|
- |
|
255 |
final SQLRowAccessor foreign = r.getNonEmptyForeign(lastReferentField);
|
- |
|
256 |
foreignRef = foreign != null ? foreign.getRowRef() : null;
|
- |
|
257 |
} else {
|
- |
|
258 |
foreignRef = null;
|
- |
|
259 |
}
|
- |
|
260 |
// fullList already checked above, so if there's no foreignRef, there's nothing
|
- |
|
261 |
// to do
|
- |
|
262 |
if (!keepGraph && foreignRef == null)
|
- |
|
263 |
continue;
|
237 |
for (final ListSQLLine line : fullList) {
|
264 |
for (final ListSQLLine line : fullList) {
|
238 |
boolean put = false;
|
265 |
boolean put = false;
|
- |
|
266 |
if (keepGraph) {
|
239 |
for (final SQLRowValues current : line.getRow().followPath(p, CreateMode.CREATE_NONE, false)) {
|
267 |
for (final SQLRowValues current : line.getRow().getDistantRows(p)) {
|
240 |
// works for rowValues w/o any ID
|
268 |
// works for rowValues w/o any ID
|
241 |
if (current != null && current.getID() == id) {
|
269 |
if (current != null && current.getID() == id) {
|
242 |
put = true;
|
270 |
put = true;
|
243 |
}
|
271 |
}
|
244 |
}
|
272 |
}
|
- |
|
273 |
}
|
- |
|
274 |
/*
|
245 |
// if the modified row isn't in the existing line, it might still affect it
|
275 |
* If the modified row isn't in the existing line, it might still affect it
|
246 |
// if it's a referent row insertion
|
276 |
* if it's a referent row insertion (a referent row update or deletion would
|
247 |
if (!put && lastReferentField != null && r.exists() && !r.isForeignEmpty(lastReferentField)) {
|
277 |
* mean than the current line contains the modified row and so "put" would
|
248 |
// no NPE, even without an undefined ID since we tested isForeignEmpty()
|
278 |
* already be true).
|
- |
|
279 |
*/
|
249 |
final int foreignID = r.getInt(lastReferentField);
|
280 |
if (!put && foreignRef != null) {
|
- |
|
281 |
if (keepGraph) {
|
250 |
for (final SQLRowValues current : line.getRow().followPath(p.minusLast(), CreateMode.CREATE_NONE, false)) {
|
282 |
for (final SQLRowValues current : line.getRow().getDistantRows(p.minusLast())) {
|
251 |
if (current.getID() == foreignID) {
|
283 |
if (current.getID() == foreignRef.getID().intValue()) {
|
252 |
put = true;
|
284 |
put = true;
|
253 |
}
|
285 |
}
|
254 |
}
|
286 |
}
|
- |
|
287 |
} else {
|
- |
|
288 |
put = line.getPKs().contains(foreignRef);
|
- |
|
289 |
}
|
255 |
}
|
290 |
}
|
256 |
if (put) {
|
291 |
if (put) {
|
257 |
// add to the list of paths that have been refreshed
|
292 |
// add to the list of paths that have been refreshed
|
258 |
add(byLine, res, p, line);
|
293 |
cons.accept(p, line);
|
259 |
}
|
294 |
}
|
260 |
}
|
295 |
}
|
261 |
}
|
296 |
}
|
262 |
}
|
297 |
}
|
263 |
}
|
298 |
}
|
264 |
return res;
|
- |
|
265 |
}
|
- |
|
266 |
|
- |
|
267 |
@SuppressWarnings("unchecked")
|
- |
|
268 |
<V, K> void add(boolean byLine, ListMap<K, V> res, final Path p, final ListSQLLine line) {
|
- |
|
269 |
if (byLine)
|
- |
|
270 |
res.add((K) line, (V) p);
|
- |
|
271 |
else
|
- |
|
272 |
res.add((K) p, (V) line);
|
- |
|
273 |
}
|
299 |
}
|
274 |
|
300 |
|
275 |
final void setFullList(final List<ListSQLLine> tmp, final SQLTableModelColumns cols) {
|
301 |
final void setFullList(final List<ListSQLLine> tmp, final SQLTableModelColumns cols) {
|
276 |
final List<ListSQLLine> fullList = this.getFullList();
|
302 |
final List<ListSQLLine> fullList = this.getFullList();
|
277 |
synchronized (fullList) {
|
303 |
synchronized (fullList) {
|