OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 17 | 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.request;
15
 
16
import org.openconcerto.sql.Log;
17
import org.openconcerto.sql.model.SQLField;
18
import org.openconcerto.sql.model.SQLRow;
19
import org.openconcerto.sql.model.SQLRowAccessor;
20
import org.openconcerto.sql.model.SQLRowValues;
21
import org.openconcerto.sql.model.SQLTable;
25 ilm 22
import org.openconcerto.sql.model.SQLTableEvent;
23
import org.openconcerto.sql.model.SQLTableEvent.Mode;
24
import org.openconcerto.sql.model.SQLTableModifiedListener;
17 ilm 25
import org.openconcerto.ui.SwingThreadUtils;
26
 
27
import java.awt.Component;
28
import java.beans.PropertyChangeListener;
29
import java.beans.PropertyChangeSupport;
30
import java.sql.SQLException;
31
import java.util.Collection;
32
import java.util.HashMap;
33
import java.util.HashSet;
34
import java.util.Map;
35
import java.util.Set;
36
 
37
import javax.swing.SwingUtilities;
38
 
39
/**
40
 * A view of a row as a collection of SQLRowItemView. NOTE: all methods of this class are expected
41
 * to be called from within the EDT (unless otherwise stated).
42
 *
43
 *
44
 * @author Sylvain CUAZ
45
 * @see #add(SQLRowItemView)
46
 * @see #select(SQLRowAccessor)
47
 * @see #insert()
48
 * @see #update()
49
 */
50
public class SQLRowView extends BaseSQLRequest {
51
 
52
    // la table cible de cette requete
53
    private final SQLTable table;
25 ilm 54
    private final SQLTableModifiedListener tableListener;
17 ilm 55
    // l'id affiché ou SQLRow.NONEXISTANT_ID si les valeurs affichées ne sont pas liées à une ligne
56
    // dans la base
57
    private int selectedID;
58
    // les valeurs affichées
59
    private final Map<String, SQLRowItemView> views;
60
 
61
    private boolean filling;
62
    private boolean updating;
63
 
64
    private final PropertyChangeSupport supp;
65
 
66
    // ne peut contenir des champs que de cette table
67
    public SQLRowView(SQLTable t) {
68
        if (t == null)
69
            throw new NullPointerException("null SQLTable");
70
        else if (!t.isRowable())
71
            throw new IllegalArgumentException(t + " is not rowable");
72
 
73
        this.supp = new PropertyChangeSupport(this);
74
        this.table = t;
75
        this.views = new HashMap<String, SQLRowItemView>();
76
        this.filling = false;
77
        this.updating = false;
78
        this.selectedID = SQLRow.NONEXISTANT_ID;
25 ilm 79
        this.tableListener = new SQLTableModifiedListener() {
80
            @Override
81
            public void tableModified(SQLTableEvent evt) {
82
                if (evt.getMode() == Mode.ROW_UPDATED)
83
                    this.rowModified(evt.getTable(), evt.getId());
84
                else if (evt.getMode() == Mode.ROW_DELETED)
85
                    this.rowDeleted(evt.getTable(), evt.getId());
86
                // else don't care
87
            }
17 ilm 88
 
89
            public void rowModified(SQLTable t, int id) {
90
                if (!isUpdating() && existsInDB()) {
91
                    if (!t.equals(getTable())) {
92
                        // TODO trouver si ca nous concerne => select
93
                        Thread.dumpStack();
94
                    } else if (getSelectedID() == id) {
95
                        // rafraichir
96
                        select(id);
97
                    }
98
                }
99
            }
100
 
101
            public void rowDeleted(SQLTable t, int id) {
102
                if (!isUpdating() && existsInDB()) {
103
                    if (!t.equals(getTable())) {
104
                        // TODO trouver si ca nous concerne
105
                        Thread.dumpStack();
106
                    } else if (getSelectedID() == id) {
107
                        SwingThreadUtils.invoke(new Runnable() {
108
                            public void run() {
109
                                select(null);
110
                            }
111
                        });
112
                    }
113
                }
114
            }
115
        };
116
    }
117
 
118
    public synchronized boolean isUpdating() {
119
        return this.updating;
120
    }
121
 
122
    public final void activate(boolean b) {
123
        if (b) {
25 ilm 124
            this.table.addTableModifiedListener(this.tableListener);
17 ilm 125
            if (this.existsInDB())
126
                // to catch up to the changes which happened while we weren't listening
127
                this.select(this.getSelectedID());
128
        } else
25 ilm 129
            this.table.removeTableModifiedListener(this.tableListener);
17 ilm 130
    }
131
 
132
    /**
133
     * Add a view.
134
     *
135
     * @param obj object to add.
136
     */
137
    public void add(SQLRowItemView obj) {
138
        if (obj.getSQLName() == null)
139
            throw new IllegalArgumentException("null SQL name for " + obj);
140
        if (this.views.containsKey(obj.getSQLName()))
141
            throw new IllegalStateException("2 views named " + obj.getSQLName() + ": " + this.views.get(obj.getSQLName()) + " " + obj);
142
        this.views.put(obj.getSQLName(), obj);
143
    }
144
 
145
    /**
146
     * Display the passed id. As an exception, this can be called from outside the EDT
147
     *
148
     * @param id id of the row to display.
149
     */
150
    public void select(int id) {
151
        final SQLRow row = this.getTable().getRow(id);
152
        SwingThreadUtils.invoke(new Runnable() {
153
            public void run() {
154
                select(row);
155
            }
156
        });
157
    }
158
 
159
    /**
160
     * Fill the item views with values from <code>r</code>. If r is <code>null</code> this will be
161
     * reset. If r has no ID, the selectedID doesn't change.
162
     *
163
     * @param r the row to display, can be <code>null</code>.
164
     */
165
    public void select(SQLRowAccessor r) {
166
        this.setFilling(true);
167
        try {
168
            if (r == null) {
169
                this.setSelectedID(SQLRow.NONEXISTANT_ID);
170
                for (final SQLRowItemView view : this.getViewsFast()) {
171
                    view.resetValue();
172
                }
173
            } else {
174
                if (!this.getTable().equals(r.getTable()))
175
                    throw new IllegalArgumentException("r is not of table " + this.getTable() + " : " + r);
176
                // set selectedID before show() since some views might need it (eg for validation)
177
                if (r.getID() != SQLRow.NONEXISTANT_ID)
178
                    this.setSelectedID(r.getID());
179
                for (final SQLRowItemView view : this.getViewsFast()) {
180
                    view.show(r);
181
                }
182
            }
183
        } finally {
184
            this.setFilling(false);
185
        }
186
    }
187
 
188
    public final void detach() {
189
        this.setSelectedID(SQLRow.NONEXISTANT_ID);
190
    }
191
 
192
    /**
193
     * Mets à jour la ligne sélectionnée avec les nouvelles valeurs des SQLObjects.
194
     *
195
     * @throws SQLException if the update couldn't complete.
196
     */
197
    public void update() throws SQLException {
198
        // this ship is sailed, don't accept updates from the db anymore
199
        // this allows to archive a private (thus changing our fk) without
200
        // overwriting our values
201
        synchronized (this) {
202
            this.updating = true;
203
        }
204
        this.update(this.getSelectedID());
205
        synchronized (this) {
206
            this.updating = false;
207
        }
208
    }
209
 
210
    /**
211
     * Permet de mettre à jour une ligne existante avec les valeurs courantes.
212
     *
213
     * @param id l'id à mettre à jour.
214
     * @throws SQLException if the update couldn't complete.
215
     */
216
    private void update(int id) throws SQLException {
217
        if (id == this.getTable().getUndefinedID())
218
            throw new IllegalArgumentException("can't update undefined");
219
        if (id == SQLRow.NONEXISTANT_ID)
220
            throw new IllegalArgumentException("NONEXISTANT_ID");
221
        Log.get().fine("updating " + this.getTable() + " " + id);
222
 
223
        final SQLRowValues vals = new SQLRowValues(this.getTable());
224
        for (final SQLRowItemView view : this.getViewsFast()) {
225
            view.update(vals);
226
        }
227
        vals.update(id);
228
    }
229
 
230
    public SQLRow insert() throws SQLException {
231
        return fillVals().insert();
232
    }
233
 
234
    private SQLRowValues fillVals() {
235
        final SQLRowValues vals = new SQLRowValues(this.getTable());
236
        for (final SQLRowItemView view : this.getViewsFast()) {
237
            view.insert(vals);
238
        }
239
        return vals;
240
    }
241
 
242
    public SQLRow insert(SQLRow order) throws SQLException {
243
        final SQLRowValues vals = fillVals();
244
        vals.setOrder(order, true);
245
        return vals.insertVerbatim();
246
    }
247
 
248
    public SQLTable getTable() {
249
        return this.table;
250
    }
251
 
252
    public Set<SQLRowItemView> getViews() {
253
        return new HashSet<SQLRowItemView>(this.getViewsFast());
254
    }
255
 
256
    private final Collection<SQLRowItemView> getViewsFast() {
257
        return this.views.values();
258
    }
259
 
260
    public String toString() {
261
        return this.getClass() + " with " + this.getViewsFast();
262
    }
263
 
264
    /**
265
     * Returns the corresponding view.
266
     *
267
     * @param name name of the desired view.
268
     * @return the corresponding view, or <code>null</code> if none exists.
269
     */
270
    public SQLRowItemView getView(String name) {
271
        return this.views.get(name);
272
    }
273
 
274
    /**
275
     * Returns the view whose component is a parent of the passed one.
276
     *
277
     * @param comp a Component used by a view (or a descendant of one).
278
     * @return the corresponding view, or <code>null</code> if none exists.
279
     */
280
    public SQLRowItemView getView(Component comp) {
281
        for (final SQLRowItemView view : this.views.values()) {
282
            if (SwingUtilities.isDescendingFrom(comp, view.getComp()))
283
                return view;
284
        }
285
        return null;
286
    }
287
 
288
    public void resetValue() {
289
        this.select(null);
290
    }
291
 
292
    /*
293
     * (non-Javadoc)
294
     *
295
     * @see org.openconcerto.devis.request.BaseSQLRequest#getAllFields()
296
     */
297
    public Collection<SQLField> getAllFields() {
298
        final Set<SQLField> res = new HashSet<SQLField>();
299
        for (final SQLRowItemView view : this.getViewsFast()) {
300
            if (view.getField() != null)
301
                res.add(view.getField());
302
        }
303
        return res;
304
    }
305
 
306
    private void setSelectedID(int selectedID) {
307
        this.selectedID = selectedID;
308
        this.supp.firePropertyChange("selectedID", null, this.selectedID);
309
    }
310
 
311
    public final int getSelectedID() {
312
        return this.selectedID;
313
    }
314
 
315
    /**
316
     * If this request represents an actual row in the database.
317
     *
318
     * @return <code>true</code> if this request is linked with the base.
319
     */
320
    public final boolean existsInDB() {
321
        return this.getSelectedID() != SQLRow.NONEXISTANT_ID;
322
    }
323
 
324
    /**
325
     * Is this view filing in its items.
326
     *
327
     * @return <code>true</code> if items values are being set.
328
     */
329
    public final boolean isFilling() {
330
        return this.filling;
331
    }
332
 
333
    private final void setFilling(boolean filling) {
334
        this.filling = filling;
335
        this.supp.firePropertyChange("filling", null, this.filling);
336
    }
337
 
338
    public final void addListener(PropertyChangeListener l) {
339
        this.supp.addPropertyChangeListener(l);
340
    }
341
 
342
    public final void rmListener(PropertyChangeListener l) {
343
        this.supp.removePropertyChangeListener(l);
344
    }
345
 
346
    public final void addListener(PropertyChangeListener l, final String name) {
347
        this.supp.addPropertyChangeListener(name, l);
348
    }
349
 
350
    public final void rmListener(PropertyChangeListener l, final String name) {
351
        this.supp.removePropertyChangeListener(name, l);
352
    }
353
}