OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 142 | 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
 *
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.request;
15
 
16
import org.openconcerto.sql.Configuration;
93 ilm 17
import org.openconcerto.sql.model.SQLDataSource;
17 ilm 18
import org.openconcerto.sql.model.SQLFilter;
19
import org.openconcerto.sql.model.SQLFilterListener;
20
import org.openconcerto.sql.model.SQLRow;
21
import org.openconcerto.sql.model.SQLRowValues;
67 ilm 22
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
17 ilm 23
import org.openconcerto.sql.model.SQLSelect;
24
import org.openconcerto.sql.model.SQLTable;
65 ilm 25
import org.openconcerto.sql.model.TableRef;
17 ilm 26
import org.openconcerto.sql.model.Where;
27
import org.openconcerto.sql.model.graph.Path;
28
import org.openconcerto.utils.CollectionUtils;
29
import org.openconcerto.utils.CompareUtils;
30
import org.openconcerto.utils.Tuple2;
132 ilm 31
 
32
import java.util.Collection;
33
import java.util.HashSet;
34
import java.util.List;
35
import java.util.Set;
36
 
93 ilm 37
import net.jcip.annotations.GuardedBy;
38
import net.jcip.annotations.ThreadSafe;
39
 
17 ilm 40
// une fill request qui utilise le filtre
93 ilm 41
@ThreadSafe
17 ilm 42
public abstract class FilteredFillSQLRequest extends BaseFillSQLRequest {
43
 
67 ilm 44
    // make sure that all rows are from the same table
45
    static protected final Tuple2<SQLTable, Set<Number>> rows2ids(final Collection<SQLRow> rows) {
46
        final Set<SQLTable> tables = new HashSet<SQLTable>();
47
        final Set<Number> ids = new HashSet<Number>(rows.size());
48
        for (final SQLRow r : rows) {
49
            tables.add(r.getTable());
50
            ids.add(r.getIDNumber());
51
        }
52
        final SQLTable filterTable = CollectionUtils.getSole(tables);
53
        if (filterTable == null)
54
            throw new IllegalStateException("not 1 table: " + rows);
55
        return Tuple2.create(filterTable, ids);
56
    }
57
 
73 ilm 58
    static protected final SQLFilter getDefaultFilter() {
59
        final Configuration conf = Configuration.getInstance();
60
        return conf == null ? null : conf.getFilter();
61
    }
62
 
93 ilm 63
    static protected final int getRowCount(final SQLRowValuesListFetcher fetcher) {
64
        final SQLTable primaryT = fetcher.getGraph().getTable();
132 ilm 65
        return getRowCount(fetcher.getReq(), primaryT.getDBSystemRoot().getDataSource());
93 ilm 66
    }
67
 
68
    static public final int getRowCount(final SQLSelect sel, final SQLDataSource ds) {
132 ilm 69
        return ((Number) ds.executeScalar(sel.getForRowCount())).intValue();
93 ilm 70
    }
71
 
17 ilm 72
    // never null (but can be <null, null>)
93 ilm 73
    @GuardedBy("this")
17 ilm 74
    private Tuple2<Set<SQLRow>, Path> filterInfo;
73 ilm 75
    private final SQLFilter filter;
76
    // initially false since we don't listen to filter before calling setFilterEnabled()
93 ilm 77
    @GuardedBy("this")
73 ilm 78
    private boolean filterEnabled = false;
17 ilm 79
    private final SQLFilterListener filterListener;
80
 
81
    {
82
        this.filterListener = new SQLFilterListener() {
83
            public void filterChanged(Collection<SQLTable> tables) {
93 ilm 84
                // when we remove the listener in wasFrozen(), there might already be threads
85
                // waiting for our lock to notify us. So we must ignore these last few events.
86
                if (!isFrozen() && CollectionUtils.containsAny(getTables(), tables))
87
                    updateFilterWhere(true);
17 ilm 88
            }
89
        };
90
    }
91
 
142 ilm 92
    public FilteredFillSQLRequest(final SQLRowValues graph, Where w) {
93
        super(graph, w);
73 ilm 94
        this.filter = getDefaultFilter();
17 ilm 95
        this.filterInfo = Tuple2.create(null, null);
96
        this.setFilterEnabled(true);
97
    }
98
 
93 ilm 99
    // ATTN if freeze is true freeze() must be called right after (cannot be called here since
100
    // subclasses aren't finished)
101
    protected FilteredFillSQLRequest(FilteredFillSQLRequest req, final boolean freeze) {
17 ilm 102
        super(req);
93 ilm 103
        // otherwise each superclass will lock the source, resulting in an inconsistent view
104
        assert Thread.holdsLock(req);
73 ilm 105
        this.filter = req.filter;
17 ilm 106
        this.filterInfo = req.filterInfo;
93 ilm 107
        // don't add listener and update our info (which leave us inconsistent until
108
        // freeze() is called)
109
        if (freeze)
110
            this.filterEnabled = req.filterEnabled;
111
        else
112
            this.setFilterEnabled(req.filterEnabled);
17 ilm 113
    }
114
 
93 ilm 115
    @Override
116
    protected void wasFrozen() {
117
        super.wasFrozen();
118
        this.getFilter().rmListener(this.filterListener);
119
    }
120
 
17 ilm 121
    protected final SQLFilter getFilter() {
73 ilm 122
        return this.filter;
17 ilm 123
    }
124
 
125
    public final void setFilterEnabled(boolean b) {
73 ilm 126
        final SQLFilter filter = this.getFilter();
127
        b = filter == null ? false : b;
93 ilm 128
        final boolean changed;
129
        synchronized (this) {
130
            checkFrozen();
131
            if (this.filterEnabled != b) {
132
                // since filter is final and filterEnabled is initially false
133
                assert filter != null;
134
                this.filterEnabled = b;
135
                if (this.filterEnabled)
136
                    filter.addWeakListener(this.filterListener);
137
                else
138
                    filter.rmListener(this.filterListener);
139
                changed = updateFilterWhere(false);
140
            } else {
141
                changed = false;
142
            }
73 ilm 143
        }
93 ilm 144
        if (changed)
145
            fireWhereChange();
17 ilm 146
    }
147
 
93 ilm 148
    // fire=false allow to call this method with our lock
149
    private boolean updateFilterWhere(final boolean fire) {
150
        final boolean changed;
151
        synchronized (this) {
152
            if (this.filterEnabled) {
153
                changed = this.setFilterWhere(getFilter().getLeaf(), getFilter().getPath(getPrimaryTable()));
154
            } else {
155
                changed = this.setFilterWhere(null, null);
156
            }
157
        }
158
        if (fire && changed)
159
            fireWhereChange();
160
        return changed;
17 ilm 161
    }
162
 
93 ilm 163
    private synchronized boolean setFilterWhere(final Set<SQLRow> w, final Path p) {
164
        checkFrozen();
165
        final boolean changed = !CompareUtils.equals(this.filterInfo.get0(), w) || !CompareUtils.equals(this.filterInfo.get1(), p);
166
        if (changed) {
17 ilm 167
            // shall we filter : w==null => no filter selected, p==null => filter doesn't affect us
168
            if (w == null || p == null) {
169
                this.filterInfo = Tuple2.create(null, null);
170
            } else {
80 ilm 171
                this.filterInfo = Tuple2.create((Set<SQLRow>) new HashSet<SQLRow>(w), p);
17 ilm 172
            }
173
        }
93 ilm 174
        return changed;
17 ilm 175
    }
176
 
177
    public final SQLRowValues getValues(int id) {
178
        final List<SQLRowValues> res = getValues(new Where(this.getPrimaryTable().getKey(), "=", id));
93 ilm 179
        return getSole(res, id);
180
    }
181
 
182 ilm 182
    public final List<SQLRowValues> getValuesFromIDs(Collection<? extends Number> ids) {
183
        return getValues(ids == null ? null : Where.inValues(this.getPrimaryTable().getKey(), ids));
184
    }
185
 
93 ilm 186
    protected final <T> T getSole(final List<T> res, int id) {
17 ilm 187
        if (res.size() > 1)
188
            throw new IllegalStateException("there's more than one line which has ID " + id + " for " + this + " : " + res);
189
        return CollectionUtils.getFirst(res);
190
    }
191
 
93 ilm 192
    public int getValuesCount() {
193
        return getRowCount(this.getFetcher());
194
    }
195
 
17 ilm 196
    public final List<SQLRowValues> getValues() {
197
        return this.getValues(null);
198
    }
199
 
200
    protected List<SQLRowValues> getValues(final Where w) {
93 ilm 201
        return getFetcher().fetch(w);
17 ilm 202
    }
203
 
67 ilm 204
    protected final List<SQLRowValues> fetchValues(final SQLRowValuesListFetcher f, final Where w) {
93 ilm 205
        return this.setupFetcher(f).fetch(w);
67 ilm 206
    }
207
 
17 ilm 208
    @Override
209
    protected SQLSelect transformSelect(SQLSelect sel) {
210
        final Tuple2<Set<SQLRow>, Path> filterInfo = getFilterInfo();
211
        // the filter is not empty and it concerns us
212
        if (filterInfo.get1() != null) {
67 ilm 213
            final Tuple2<SQLTable, Set<Number>> tableNids = rows2ids(filterInfo.get0());
214
            final SQLTable filterTable = tableNids.get0();
17 ilm 215
 
216
            final Path path = filterInfo.get1();
65 ilm 217
            final TableRef lastAlias = sel.assurePath(getPrimaryTable().getName(), path);
218
            if (filterTable != lastAlias.getTable())
67 ilm 219
                throw new IllegalStateException("table mismatch: " + filterTable + " is not from " + lastAlias + ": " + lastAlias.getTable());
17 ilm 220
 
67 ilm 221
            sel.andWhere(new Where(lastAlias.getKey(), tableNids.get1()));
17 ilm 222
        }
223
        return super.transformSelect(sel);
224
    }
225
 
226
    /**
227
     * The filter row acting on this request.
228
     *
229
     * @return the row or <code>null</code> if there's no filter or the filter do not affect this.
230
     */
231
    public final Set<SQLRow> getFilterRows() {
232
        return this.getFilterInfo().get0();
233
    }
234
 
235
    // when the filter doesn't affect us, do not return the filter row
236
    private synchronized final Tuple2<Set<SQLRow>, Path> getFilterInfo() {
237
        return this.filterInfo;
238
    }
239
 
240
    // the where applied to all request (as opposed to the one passed to getFillRequest())
241
    public final Where getInstanceWhere() {
93 ilm 242
        return this.getFetcher().getReq().getWhere();
17 ilm 243
    }
244
}