OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Rev 180 | 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.element;
15
 
16
import org.openconcerto.sql.Log;
132 ilm 17
import org.openconcerto.sql.ShowAs;
18
import org.openconcerto.sql.model.DBRoot;
17 ilm 19
import org.openconcerto.sql.model.DBStructureItemNotFound;
20
import org.openconcerto.sql.model.SQLName;
21
import org.openconcerto.sql.model.SQLTable;
142 ilm 22
import org.openconcerto.sql.request.SQLFieldTranslator;
132 ilm 23
import org.openconcerto.utils.CollectionMap2;
17 ilm 24
import org.openconcerto.utils.CollectionUtils;
83 ilm 25
import org.openconcerto.utils.SetMap;
17 ilm 26
import org.openconcerto.utils.cc.ITransformer;
27
 
28
import java.lang.reflect.InvocationTargetException;
19 ilm 29
import java.util.ArrayList;
17 ilm 30
import java.util.Collection;
19 ilm 31
import java.util.Collections;
17 ilm 32
import java.util.HashMap;
19 ilm 33
import java.util.List;
17 ilm 34
import java.util.Map;
132 ilm 35
import java.util.Map.Entry;
17 ilm 36
import java.util.Set;
37
 
142 ilm 38
import net.jcip.annotations.GuardedBy;
39
 
17 ilm 40
/**
41
 * Directory of SQLElement by table.
42
 *
43
 * @author Sylvain CUAZ
44
 */
45
public final class SQLElementDirectory {
46
 
47
    private final Map<SQLTable, SQLElement> elements;
83 ilm 48
    private final SetMap<String, SQLTable> tableNames;
49
    private final SetMap<String, SQLTable> byCode;
50
    private final SetMap<Class<? extends SQLElement>, SQLTable> byClass;
19 ilm 51
    private final List<DirectoryListener> listeners;
17 ilm 52
 
73 ilm 53
    private String phrasesPkgName;
54
 
142 ilm 55
    @GuardedBy("this")
56
    private SQLFieldTranslator translator;
132 ilm 57
    private final ShowAs showAs;
58
 
17 ilm 59
    public SQLElementDirectory() {
60
        this.elements = new HashMap<SQLTable, SQLElement>();
61
        // to mimic elements behaviour, if we add twice the same table
62
        // the second one should replace the first one
83 ilm 63
        this.tableNames = new SetMap<String, SQLTable>();
64
        this.byCode = new SetMap<String, SQLTable>();
65
        this.byClass = new SetMap<Class<? extends SQLElement>, SQLTable>();
19 ilm 66
 
67
        this.listeners = new ArrayList<DirectoryListener>();
73 ilm 68
 
69
        this.phrasesPkgName = null;
132 ilm 70
 
71
        this.showAs = new ShowAs((DBRoot) null);
17 ilm 72
    }
73
 
144 ilm 74
    public synchronized final void destroy() {
75
        for (final SQLElement elem : this.elements.values()) {
76
            elem.destroy();
77
        }
78
    }
79
 
132 ilm 80
    public final ShowAs getShowAs() {
81
        return this.showAs;
82
    }
83
 
142 ilm 84
    public final synchronized void setTranslator(SQLFieldTranslator translator) {
85
        if (translator.getDirectory() != this)
86
            throw new IllegalArgumentException("Not for this : " + translator);
87
        this.translator = translator;
88
    }
89
 
90
    public synchronized final SQLFieldTranslator getTranslator() {
91
        return this.translator;
92
    }
93
 
83 ilm 94
    private static <K> SQLTable getSoleTable(SetMap<K, SQLTable> m, K key) throws IllegalArgumentException {
17 ilm 95
        final Collection<SQLTable> res = m.getNonNull(key);
96
        if (res.size() > 1)
97
            throw new IllegalArgumentException(key + " is not unique: " + CollectionUtils.join(res, ",", new ITransformer<SQLTable, SQLName>() {
98
                @Override
99
                public SQLName transformChecked(SQLTable input) {
100
                    return input.getSQLName();
101
                }
102
            }));
103
        return CollectionUtils.getSole(res);
104
    }
105
 
106
    public synchronized final void putAll(SQLElementDirectory o) {
107
        for (final SQLElement elem : o.getElements()) {
108
            if (!this.contains(elem.getTable()))
109
                this.addSQLElement(elem);
110
        }
142 ilm 111
        this.translator.putAll(o.getTranslator());
17 ilm 112
    }
113
 
114
    /**
115
     * Add an element by creating it with the no-arg constructor. If the element cannot find its
116
     * table and thus raise DBStructureItemNotFound, the exception is logged.
117
     *
118
     * @param element the element to add.
119
     */
120
    public final void addSQLElement(final Class<? extends SQLElement> element) {
156 ilm 121
        this.addSQLElement(element, null);
122
    }
123
 
124
    public final void addSQLElement(final Class<? extends SQLElement> element, final DBRoot root) {
125
        final SQLElement newInstance;
17 ilm 126
        try {
156 ilm 127
            if (root == null)
128
                newInstance = element.getConstructor().newInstance();
129
            else
130
                newInstance = element.getConstructor(DBRoot.class).newInstance(root);
17 ilm 131
        } catch (InvocationTargetException e) {
132
            if (e.getCause() instanceof DBStructureItemNotFound) {
133
                Log.get().config("ignore inexistent tables: " + e.getCause().getLocalizedMessage());
134
                return;
135
            }
156 ilm 136
            throw new IllegalArgumentException("Constructor failed", e);
17 ilm 137
        } catch (Exception e) {
156 ilm 138
            throw new IllegalArgumentException("Couldn't use constructor", e);
17 ilm 139
        }
156 ilm 140
        this.addSQLElement(newInstance);
17 ilm 141
    }
142
 
143
    /**
144
     * Adds an already instantiated element.
145
     *
146
     * @param elem the SQLElement to add.
80 ilm 147
     * @return the previously added element.
17 ilm 148
     */
80 ilm 149
    public synchronized final SQLElement addSQLElement(SQLElement elem) {
150
        final SQLElement res = this.removeSQLElement(elem.getTable());
17 ilm 151
        this.elements.put(elem.getTable(), elem);
83 ilm 152
        this.tableNames.add(elem.getTable().getName(), elem.getTable());
153
        this.byCode.add(elem.getCode(), elem.getTable());
154
        this.byClass.add(elem.getClass(), elem.getTable());
132 ilm 155
 
156
        final CollectionMap2<String, ? extends List<String>, String> sa = elem.getShowAs();
157
        if (sa != null) {
158
            for (final Entry<String, ? extends List<String>> e : sa.entrySet()) {
159
                try {
160
                    if (e.getKey() == null)
161
                        this.showAs.show(elem.getTable(), e.getValue());
162
                    else
163
                        this.showAs.show(elem.getTable().getField(e.getKey()), e.getValue());
164
                } catch (RuntimeException exn) {
165
                    throw new IllegalStateException("Couldn't add showAs for " + elem + " : " + e, exn);
166
                }
167
            }
168
        }
169
 
19 ilm 170
        for (final DirectoryListener dl : this.listeners) {
171
            dl.elementAdded(elem);
172
        }
73 ilm 173
        elem.setDirectory(this);
80 ilm 174
        return res;
17 ilm 175
    }
176
 
177
    public synchronized final boolean contains(SQLTable t) {
178
        return this.elements.containsKey(t);
179
    }
180
 
181
    public synchronized final SQLElement getElement(SQLTable t) {
182
        return this.elements.get(t);
183
    }
184
 
185
    /**
186
     * Search for a table whose name is <code>tableName</code>.
187
     *
188
     * @param tableName a table name, e.g. "ADRESSE".
189
     * @return the corresponding SQLElement, or <code>null</code> if there is no table named
190
     *         <code>tableName</code>.
191
     * @throws IllegalArgumentException if more than one table match.
192
     */
19 ilm 193
    public synchronized final SQLElement getElement(String tableName) {
17 ilm 194
        return this.getElement(getSoleTable(this.tableNames, tableName));
195
    }
196
 
197
    /**
198
     * Search for an SQLElement whose class is <code>clazz</code>.
199
     *
200
     * @param <S> type of SQLElement
201
     * @param clazz the class.
202
     * @return the corresponding SQLElement, or <code>null</code> if none can be found.
203
     * @throws IllegalArgumentException if there's more than one match.
204
     */
174 ilm 205
    public final <S extends SQLElement> S getElement(Class<S> clazz) {
206
        return this.getElementOfClass(clazz, false);
17 ilm 207
    }
208
 
174 ilm 209
    public final <S extends SQLElement> S getElementOfClass(Class<S> clazz, final boolean allowSubclass) {
210
        final List<S> res = getElementsOfClass(clazz, allowSubclass);
211
        if (res.size() > 1)
212
            throw new IllegalArgumentException(clazz + " is not unique: " + res);
213
        return res.isEmpty() ? null : res.get(0);
214
    }
215
 
216
    /**
217
     * Search for SQLElement whose class is <code>clazz</code>.
218
     *
219
     * @param clazz the class.
220
     * @param allowSubclass <code>true</code> to include {@link Class#isInstance(Object)
221
     *        subclasses}, <code>false</code> to only return exact match.
222
     * @return the corresponding elements.
223
     */
224
    public final <S extends SQLElement> List<S> getElementsOfClass(final Class<S> clazz, final boolean allowSubclass) {
225
        final List<S> res;
226
        synchronized (this) {
227
            if (allowSubclass) {
228
                res = new ArrayList<>();
229
                for (final SQLElement elem : this.elements.values()) {
230
                    if (clazz.isInstance(elem))
231
                        res.add(clazz.cast(elem));
232
                }
233
            } else {
234
                final Set<SQLTable> tables = this.byClass.get(clazz);
235
                if (tables != null) {
236
                    res = new ArrayList<>(tables.size());
237
                    for (final SQLTable t : tables) {
238
                        res.add(clazz.cast(this.getElement(t)));
239
                    }
240
                } else {
241
                    res = Collections.emptyList();
242
                }
243
            }
244
        }
245
        if (res.isEmpty()) {
246
            return Collections.emptyList();
247
        } else if (res.size() == 1) {
248
            return Collections.singletonList(res.get(0));
249
        } else {
250
            return Collections.unmodifiableList(res);
251
        }
252
    }
253
 
27 ilm 254
    public synchronized final SQLElement getElementForCode(String code) {
255
        return this.getElement(getSoleTable(this.byCode, code));
256
    }
257
 
17 ilm 258
    public synchronized final Set<SQLTable> getTables() {
19 ilm 259
        return this.getElementsMap().keySet();
17 ilm 260
    }
261
 
262
    public synchronized final Collection<SQLElement> getElements() {
19 ilm 263
        return this.getElementsMap().values();
17 ilm 264
    }
19 ilm 265
 
266
    public final Map<SQLTable, SQLElement> getElementsMap() {
267
        return Collections.unmodifiableMap(this.elements);
268
    }
269
 
270
    /**
271
     * Remove the passed instance. NOTE: this method only remove the specific instance passed, so
272
     * it's a conditional <code>removeSQLElement(elem.getTable())</code>.
273
     *
274
     * @param elem the instance to remove.
275
     * @see #removeSQLElement(SQLTable)
276
     */
277
    public synchronized void removeSQLElement(SQLElement elem) {
278
        if (this.getElement(elem.getTable()) == elem)
279
            this.removeSQLElement(elem.getTable());
280
    }
281
 
282
    /**
283
     * Remove the element for the passed table.
284
     *
285
     * @param t the table to remove.
286
     * @return the removed element, can be <code>null</code>.
287
     */
288
    public synchronized SQLElement removeSQLElement(SQLTable t) {
289
        final SQLElement elem = this.elements.remove(t);
290
        if (elem != null) {
144 ilm 291
            this.tableNames.removeOne(elem.getTable().getName(), elem.getTable());
292
            this.byCode.removeOne(elem.getCode(), elem.getTable());
293
            this.byClass.removeOne(elem.getClass(), elem.getTable());
19 ilm 294
            // MAYBE only reset neighbours.
295
            for (final SQLElement otherElem : this.elements.values())
296
                otherElem.resetRelationships();
80 ilm 297
            elem.setDirectory(null);
132 ilm 298
            this.showAs.removeTable(elem.getTable());
19 ilm 299
            for (final DirectoryListener dl : this.listeners) {
300
                dl.elementRemoved(elem);
301
            }
302
        }
303
        return elem;
304
    }
305
 
73 ilm 306
    public synchronized final void initL18nPackageName(final String baseName) {
307
        if (this.phrasesPkgName != null)
308
            throw new IllegalStateException("Already initialized : " + this.getL18nPackageName());
309
        this.phrasesPkgName = baseName;
310
    }
311
 
312
    public synchronized final String getL18nPackageName() {
313
        return this.phrasesPkgName;
314
    }
315
 
19 ilm 316
    public synchronized final void addListener(DirectoryListener dl) {
317
        this.listeners.add(dl);
318
    }
319
 
320
    public synchronized final void removeListener(DirectoryListener dl) {
321
        this.listeners.remove(dl);
322
    }
323
 
324
    static public interface DirectoryListener {
325
        void elementAdded(SQLElement elem);
326
 
327
        void elementRemoved(SQLElement elem);
328
    }
17 ilm 329
}