OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 73 | 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;
15
 
16
import org.openconcerto.sql.model.FieldPath;
17
import org.openconcerto.sql.model.SQLRowValues;
18
import org.openconcerto.sql.model.SQLTable;
19
import org.openconcerto.sql.model.graph.Path;
20
import org.openconcerto.utils.CollectionUtils;
21
 
22
import java.util.ArrayList;
23
import java.util.Collection;
24
import java.util.Collections;
25
import java.util.HashSet;
26
import java.util.List;
27
import java.util.Set;
28
 
29
/**
30
 * A line used by SQLTableModelSource, posessing an order and an id. Compare is done on the order.
31
 *
32
 * @author Sylvain
33
 */
34
public final class ListSQLLine implements Comparable<ListSQLLine> {
35
 
36
    public static final int indexFromID(final List<ListSQLLine> l, final int id) {
37
        int foundIndex = -1;
38
        final int size = l.size();
39
        for (int i = 0; i < size; i++) {
40
            final int currentID = l.get(i).getID();
41
            if (currentID == id) {
42
                foundIndex = i;
43
                break;
44
            }
45
        }
46
        return foundIndex;
47
    }
48
 
49
    private final SQLTableModelLinesSource src;
50
    private final SQLRowValues row;
51
    private final int id;
52
    // lists are accessed by Swing (model.getValueAt()) and
53
    // by the search queue (SearchRunnable#matchFilter(ListSQLLine line))
54
    private final List<Object> list;
55
    // count of column values loaded in this.list
56
    // (to avoid loading debug columns, which took more time than the regular columns, ie more than
57
    // half the time was passed on almost never displayed values)
58
    private int loadedCol;
59
    private final List<Object> pubList;
60
 
61
    public ListSQLLine(SQLTableModelLinesSource src, SQLRowValues row, int id) {
62
        super();
63
        this.src = src;
64
        this.row = row;
65
        this.id = id;
66
        this.list = new ArrayList<Object>();
67
        this.pubList = Collections.unmodifiableList(this.list);
68
        this.loadedCol = 0;
69
    }
70
 
71
    // load at least columnCount values
72
    private synchronized void loadCache(int columnCount) {
73
        if (this.loadedCol >= columnCount)
74
            return;
75
 
76
        try {
77
            final List<SQLTableModelColumn> allCols = this.src.getParent().getAllColumns();
78
            for (int i = this.loadedCol; i < columnCount; i++)
79
                this.list.add(allCols.get(i).show(this.row));
80
            this.loadedCol = columnCount;
81
        } catch (RuntimeException e) {
82
            // the list length must be equal to the column count
83
            // if we're interrupted, come back to a safe state
84
            this.list.clear();
85
            this.loadedCol = 0;
86
            throw e;
87
        }
88
    }
89
 
90
    public final SQLTableModelLinesSource getSrc() {
91
        return this.src;
92
    }
93
 
94
    public final SQLRowValues getRow() {
95
        return this.row;
96
    }
97
 
98
    @Override
99
    public int compareTo(ListSQLLine o) {
100
        if (this.src != o.src)
101
            throw new IllegalArgumentException(this.src + " != " + o.src);
102
        return this.src.compare(this, o);
103
    }
104
 
105
    public int getID() {
106
        return this.id;
107
    }
108
 
109
    public synchronized List<Object> getList(int columnCount) {
110
        this.loadCache(columnCount);
111
        return this.pubList;
112
    }
113
 
114
    public final void setValueAt(Object obj, int colIndex) {
115
        this.src.getParent().getColumn(colIndex).put(this, obj);
116
    }
117
 
118
    public final void updateValueAt(Set<Integer> colIndexes) {
119
        synchronized (this) {
120
            final int max = Collections.max(colIndexes).intValue();
121
            final int alreadyLoaded = this.loadedCol;
122
            this.loadCache(max);
123
            for (final int colIndex : colIndexes) {
124
                // no need to update twice colIndex
125
                if (colIndex < alreadyLoaded)
126
                    // MAYBE first iterate to fetch the new values and then merge them to list,
127
                    // otherwise if there's an exn list will be half updated
128
                    this.list.set(colIndex, this.src.getParent().getColumn(colIndex).show(this.getRow()));
129
            }
130
        }
131
        this.src.fireLineChanged(this.getID(), this, colIndexes);
132
    }
133
 
134
    public void clearCache() {
135
        synchronized (this) {
136
            this.list.clear();
137
            this.loadedCol = 0;
138
        }
139
        this.src.fireLineChanged(this.getID(), this, null);
140
    }
141
 
142
    /**
143
     * Load the passed values into this row at the passed path.
144
     *
145
     * @param vals values to load, eg CONTACT.NOM = "Dupont".
146
     * @param p where to load the values, eg "SITE.ID_CONTACT_CHEF".
147
     */
148
    void loadAt(SQLRowValues vals, Path p) {
149
        final SQLRowValues current = this.getRow().assurePath(p);
150
        // load() empties vals, so getFields() before
151
        final Set<Integer> indexes = this.pathToIndex(p, vals.getFields());
152
        // replace our values with the new ones
153
        current.load(vals, null);
154
        // update our cache
155
        if (indexes == null)
156
            this.clearCache();
157
        else
158
            this.updateValueAt(indexes);
159
    }
160
 
161
    /**
162
     * Find the columns that use the modifiedFields for their value.
163
     *
164
     * @param p the path to the modified fields, eg "CPI.ID_LOCAL".
165
     * @param modifiedFields the field modified, eg "DESIGNATION".
166
     * @return the index of columns using "CPI.ID_LOCAL.DESIGNATION", or null for every columns.
167
     */
168
    private Set<Integer> pathToIndex(final Path p, final Collection<String> modifiedFields) {
169
        if (containsFK(p.getLast(), modifiedFields))
170
            // eg CPI.ID_LOCAL, easier to just refresh the whole line, than to search for each
171
            // column affected (that would mean expanding the fk)
172
            return null;
173
        else {
174
            final Set<Integer> res = new HashSet<Integer>();
175
            final Set<FieldPath> modifiedPaths = FieldPath.create(p, modifiedFields);
176
            final List<? extends SQLTableModelColumn> cols = this.src.getParent().getAllColumns();
177
            for (int i = 0; i < cols.size(); i++) {
178
                final SQLTableModelColumn col = cols.get(i);
179
                if (CollectionUtils.containsAny(col.getPaths(), modifiedPaths))
180
                    res.add(i);
181
            }
182
            return res;
183
        }
184
    }
185
 
186
    private static boolean containsFK(final SQLTable t, Collection<String> fields) {
187
        for (final String f : fields) {
188
            if (t.getForeignKeys().contains(t.getField(f)))
189
                return true;
190
        }
191
        return false;
192
    }
193
 
194
    @Override
195
    public String toString() {
196
        return this.getClass().getSimpleName() + " on " + this.row;
197
    }
198
}