17 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
182 |
ilm |
4 |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
|
17 |
ilm |
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.Log;
|
|
|
17 |
import org.openconcerto.sql.model.SQLRow;
|
|
|
18 |
import org.openconcerto.sql.model.SQLRowValues;
|
73 |
ilm |
19 |
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
|
17 |
ilm |
20 |
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
|
|
|
21 |
import org.openconcerto.sql.model.SQLSelect;
|
|
|
22 |
import org.openconcerto.sql.model.graph.Path;
|
20 |
ilm |
23 |
import org.openconcerto.sql.request.BaseFillSQLRequest;
|
17 |
ilm |
24 |
import org.openconcerto.sql.request.ListSQLRequest;
|
73 |
ilm |
25 |
import org.openconcerto.sql.view.list.search.SearchQueue;
|
|
|
26 |
import org.openconcerto.utils.CollectionUtils;
|
83 |
ilm |
27 |
import org.openconcerto.utils.ListMap;
|
93 |
ilm |
28 |
import org.openconcerto.utils.Value;
|
17 |
ilm |
29 |
import org.openconcerto.utils.cc.ITransformer;
|
|
|
30 |
|
|
|
31 |
import java.util.Collection;
|
182 |
ilm |
32 |
import java.util.HashSet;
|
17 |
ilm |
33 |
import java.util.List;
|
|
|
34 |
import java.util.Map.Entry;
|
182 |
ilm |
35 |
import java.util.Set;
|
17 |
ilm |
36 |
|
|
|
37 |
abstract class AbstractUpdateOneRunnable extends UpdateRunnable {
|
|
|
38 |
|
73 |
ilm |
39 |
public AbstractUpdateOneRunnable(ITableModel model, final SQLRow r) {
|
|
|
40 |
super(model, r);
|
17 |
ilm |
41 |
if (this.getID() < SQLRow.MIN_VALID_ID)
|
|
|
42 |
throw new IllegalArgumentException("id is not valid : " + this.getID());
|
|
|
43 |
}
|
|
|
44 |
|
83 |
ilm |
45 |
protected final ListMap<Path, ListSQLLine> getAffectedPaths() {
|
93 |
ilm |
46 |
return this.getUpdateQ().getAffectedPaths(this.getRow());
|
17 |
ilm |
47 |
}
|
|
|
48 |
|
83 |
ilm |
49 |
protected final void updateLines(ListMap<Path, ListSQLLine> paths) {
|
93 |
ilm |
50 |
this.updateLines(paths, Value.<ListSQLLine> getNone());
|
|
|
51 |
}
|
|
|
52 |
|
|
|
53 |
protected final void updateLines(ListMap<Path, ListSQLLine> paths, final Value<ListSQLLine> line) {
|
|
|
54 |
final List<ListSQLLine> fullList = this.getUpdateQ().getFullList();
|
|
|
55 |
synchronized (fullList) {
|
|
|
56 |
this._updateLines(paths, line);
|
|
|
57 |
}
|
|
|
58 |
}
|
|
|
59 |
|
|
|
60 |
private final void _updateLines(ListMap<Path, ListSQLLine> paths, final Value<ListSQLLine> newLine) {
|
|
|
61 |
final boolean isPrimaryTable = getRow().getTable() == getReq().getParent().getPrimaryTable();
|
|
|
62 |
// if we're refreshing the primary table, the new line must be provided
|
|
|
63 |
assert newLine.hasValue() == isPrimaryTable;
|
|
|
64 |
// even if paths is empty we might have to add a line, so this must be done outside the for
|
|
|
65 |
if (isPrimaryTable) {
|
|
|
66 |
final List<ListSQLLine> lines = paths.getNonNull(new Path(getRow().getTable()));
|
|
|
67 |
if (lines.size() > 1)
|
|
|
68 |
throw new IllegalStateException("More than one line for " + this.getRow() + " : " + lines);
|
|
|
69 |
if (!newLine.hasValue())
|
|
|
70 |
throw new IllegalArgumentException("Missing line");
|
|
|
71 |
// update fullList
|
|
|
72 |
final ListSQLLine oldLine = this.getUpdateQ().replaceLine(getRow().getID(), newLine.getValue());
|
|
|
73 |
assert oldLine == CollectionUtils.getSole(lines);
|
|
|
74 |
}
|
83 |
ilm |
75 |
for (final Entry<Path, ? extends Collection<ListSQLLine>> e : paths.entrySet()) {
|
17 |
ilm |
76 |
// eg SITE.ID_CONTACT_CHEF
|
|
|
77 |
final Path p = e.getKey();
|
|
|
78 |
// eg [SQLRowValues(SITE), SQLRowValues(SITE)]
|
|
|
79 |
final List<ListSQLLine> lines = (List<ListSQLLine>) e.getValue();
|
93 |
ilm |
80 |
// primary table already handled above
|
|
|
81 |
if (p.length() > 0 && !lines.isEmpty()) {
|
142 |
ilm |
82 |
final ListSQLRequest updateQueueReq = getModel().getLinesSource().getUpdateQueueReq();
|
17 |
ilm |
83 |
// deepCopy() instead of new SQLRowValues() otherwise the used line's graph will be
|
|
|
84 |
// modified (eg the new instance would be linked to it)
|
142 |
ilm |
85 |
final SQLRowValues proto = updateQueueReq.getGraphToFetch().followPathToOne(p, CreateMode.CREATE_NONE, false).deepCopy();
|
73 |
ilm |
86 |
final String lastReferentField = SearchQueue.getLastReferentField(p);
|
|
|
87 |
// there's only one path from the graph start to proto, and we will graft the newly
|
|
|
88 |
// fetched values at the end of p, so remove other values
|
|
|
89 |
if (lastReferentField != null) {
|
|
|
90 |
proto.put(lastReferentField, null);
|
|
|
91 |
} else {
|
|
|
92 |
proto.clearReferents();
|
|
|
93 |
// keep only what has changed, eg CONTACT.NOM
|
|
|
94 |
proto.retainAll(getModifedFields());
|
|
|
95 |
}
|
80 |
ilm |
96 |
// the modified fields aren't used at the path (e.g. if we display a row and its
|
|
|
97 |
// same-table origin, the event was added by UpdateQueue.rowModified() since the
|
|
|
98 |
// the modified fields are displayed for the primary row, but might not for the
|
|
|
99 |
// origin)
|
|
|
100 |
if (!proto.getFields().isEmpty()) {
|
|
|
101 |
// fetch the changed rowValues
|
|
|
102 |
// ATTN this doesn't use the original fetcher that was used in the updateAll
|
|
|
103 |
// MAYBE add a slower but accurate mode using the updateAll fetcher (and thus
|
|
|
104 |
// reloading rows from the primary table and not just the changed rows)
|
|
|
105 |
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(proto);
|
|
|
106 |
BaseFillSQLRequest.setupForeign(fetcher);
|
142 |
ilm |
107 |
if (updateQueueReq.isLockSelect()) {
|
|
|
108 |
fetcher.appendSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
|
|
|
109 |
@Override
|
|
|
110 |
public SQLSelect transformChecked(SQLSelect input) {
|
132 |
ilm |
111 |
input.addLockedTable(getTable().getName());
|
142 |
ilm |
112 |
return input;
|
|
|
113 |
}
|
|
|
114 |
});
|
|
|
115 |
}
|
|
|
116 |
final SQLRowValues soleFetched = fetcher.fetchOne(getRow().getIDNumber());
|
17 |
ilm |
117 |
|
80 |
ilm |
118 |
// OK if lastReferentField != null : a referent row has been deleted
|
142 |
ilm |
119 |
if (soleFetched == null && lastReferentField == null) {
|
80 |
ilm |
120 |
Log.get().fine("no row fetched for " + this + ", lines have been changed without the TableModel knowing : " + lines + " req :\n" + fetcher.getReq());
|
|
|
121 |
getModel().updateAll();
|
|
|
122 |
} else {
|
|
|
123 |
// copy it to each affected lines
|
|
|
124 |
for (final ListSQLLine line : lines) {
|
93 |
ilm |
125 |
// don't update a part of the line, if the whole has been be passed to
|
|
|
126 |
// UpdateQueue.replaceLine()
|
|
|
127 |
// (if the primary table is in the graph more than once, and a row is
|
|
|
128 |
// deleted, the line might get (rightly) removed from the full list and
|
|
|
129 |
// then searched and added to the table model by the following)
|
|
|
130 |
if (!isPrimaryTable || getRow().getID() != line.getID())
|
|
|
131 |
this.getUpdateQ().updateLine(line, p, getRow().getID(), soleFetched);
|
80 |
ilm |
132 |
}
|
17 |
ilm |
133 |
}
|
|
|
134 |
}
|
|
|
135 |
}
|
|
|
136 |
}
|
|
|
137 |
}
|
|
|
138 |
|
182 |
ilm |
139 |
protected final void updateLines(final Set<Integer> affectedLines, final Integer primaryID) {
|
|
|
140 |
final Set<Integer> idsToFetch;
|
|
|
141 |
if (primaryID != null && !affectedLines.contains(primaryID)) {
|
|
|
142 |
idsToFetch = new HashSet<>(affectedLines);
|
|
|
143 |
idsToFetch.add(primaryID);
|
|
|
144 |
} else {
|
|
|
145 |
idsToFetch = affectedLines;
|
|
|
146 |
}
|
|
|
147 |
final List<ListSQLLine> newLines = getModel().getLinesSource().get(idsToFetch);
|
|
|
148 |
|
|
|
149 |
final Set<Integer> notUpdated = new HashSet<>(affectedLines);
|
|
|
150 |
synchronized (this.getUpdateQ().getFullList()) {
|
|
|
151 |
for (final ListSQLLine newLine : newLines) {
|
|
|
152 |
this.getUpdateQ().replaceLine(newLine.getID(), newLine);
|
|
|
153 |
notUpdated.remove(newLine.getID());
|
|
|
154 |
}
|
|
|
155 |
for (final Integer toRemove : notUpdated) {
|
|
|
156 |
this.getUpdateQ().replaceLine(toRemove, null);
|
|
|
157 |
}
|
|
|
158 |
}
|
|
|
159 |
}
|
|
|
160 |
|
17 |
ilm |
161 |
protected abstract Collection<String> getModifedFields();
|
|
|
162 |
}
|