OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 41 | 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.element;
15
 
16
import org.openconcerto.sql.Log;
73 ilm 17
import org.openconcerto.sql.TM;
17 ilm 18
import org.openconcerto.sql.model.DBStructureItemNotFound;
19
import org.openconcerto.sql.model.SQLName;
20
import org.openconcerto.sql.model.SQLTable;
21
import org.openconcerto.utils.CollectionMap;
22
import org.openconcerto.utils.CollectionUtils;
23
import org.openconcerto.utils.cc.ITransformer;
73 ilm 24
import org.openconcerto.utils.i18n.LocalizedInstances;
25
import org.openconcerto.utils.i18n.Phrase;
26
import org.openconcerto.utils.i18n.TranslationManager;
17 ilm 27
 
73 ilm 28
import java.io.IOException;
29
import java.io.InputStream;
17 ilm 30
import java.lang.reflect.InvocationTargetException;
19 ilm 31
import java.util.ArrayList;
17 ilm 32
import java.util.Collection;
19 ilm 33
import java.util.Collections;
17 ilm 34
import java.util.HashMap;
35
import java.util.HashSet;
19 ilm 36
import java.util.List;
73 ilm 37
import java.util.Locale;
17 ilm 38
import java.util.Map;
39
import java.util.Set;
40
 
73 ilm 41
import org.jdom.JDOMException;
42
 
17 ilm 43
/**
44
 * Directory of SQLElement by table.
45
 *
46
 * @author Sylvain CUAZ
47
 */
48
public final class SQLElementDirectory {
49
 
73 ilm 50
    public static final String BASENAME = SQLElementNames.class.getSimpleName();
51
    private static final LocalizedInstances<SQLElementNames> LOCALIZED_INSTANCES = new LocalizedInstances<SQLElementNames>(SQLElementNames.class, TranslationManager.getControl()) {
52
        @Override
53
        protected SQLElementNames createInstance(String bundleName, Locale candidate, Class<?> cl) throws IOException {
54
            final InputStream ins = cl.getResourceAsStream('/' + getControl().toResourceName(bundleName, "xml"));
55
            if (ins == null)
56
                return null;
57
            final SQLElementNamesFromXML res = new SQLElementNamesFromXML(candidate);
58
            try {
59
                res.load(ins);
60
            } catch (JDOMException e) {
61
                throw new IOException("Invalid XML", e);
62
            } catch (IOException e) {
63
                throw e;
64
            } catch (Exception e) {
65
                throw new IOException(e);
66
            } finally {
67
                ins.close();
68
            }
69
            return res;
70
        }
71
    };
72
 
17 ilm 73
    private final Map<SQLTable, SQLElement> elements;
74
    private final CollectionMap<String, SQLTable> tableNames;
27 ilm 75
    private final CollectionMap<String, SQLTable> byCode;
17 ilm 76
    private final CollectionMap<Class<? extends SQLElement>, SQLTable> byClass;
19 ilm 77
    private final List<DirectoryListener> listeners;
17 ilm 78
 
73 ilm 79
    private String phrasesPkgName;
80
    private final Map<String, SQLElementNames> elementNames;
81
 
17 ilm 82
    public SQLElementDirectory() {
83
        this.elements = new HashMap<SQLTable, SQLElement>();
84
        // to mimic elements behaviour, if we add twice the same table
85
        // the second one should replace the first one
86
        this.tableNames = new CollectionMap<String, SQLTable>(HashSet.class);
27 ilm 87
        this.byCode = new CollectionMap<String, SQLTable>(HashSet.class);
17 ilm 88
        this.byClass = new CollectionMap<Class<? extends SQLElement>, SQLTable>(HashSet.class);
19 ilm 89
 
90
        this.listeners = new ArrayList<DirectoryListener>();
73 ilm 91
 
92
        this.phrasesPkgName = null;
93
        this.elementNames = new HashMap<String, SQLElementNames>();
17 ilm 94
    }
95
 
96
    private static <K> SQLTable getSoleTable(CollectionMap<K, SQLTable> m, K key) throws IllegalArgumentException {
97
        final Collection<SQLTable> res = m.getNonNull(key);
98
        if (res.size() > 1)
99
            throw new IllegalArgumentException(key + " is not unique: " + CollectionUtils.join(res, ",", new ITransformer<SQLTable, SQLName>() {
100
                @Override
101
                public SQLName transformChecked(SQLTable input) {
102
                    return input.getSQLName();
103
                }
104
            }));
105
        return CollectionUtils.getSole(res);
106
    }
107
 
108
    public synchronized final void putAll(SQLElementDirectory o) {
109
        for (final SQLElement elem : o.getElements()) {
110
            if (!this.contains(elem.getTable()))
111
                this.addSQLElement(elem);
112
        }
113
    }
114
 
115
    /**
116
     * Add an element by creating it with the no-arg constructor. If the element cannot find its
117
     * table and thus raise DBStructureItemNotFound, the exception is logged.
118
     *
119
     * @param element the element to add.
120
     */
121
    public final void addSQLElement(final Class<? extends SQLElement> element) {
122
        try {
123
            this.addSQLElement(element.getConstructor().newInstance());
124
        } catch (InvocationTargetException e) {
125
            if (e.getCause() instanceof DBStructureItemNotFound) {
126
                Log.get().config("ignore inexistent tables: " + e.getCause().getLocalizedMessage());
127
                return;
128
            }
129
            throw new IllegalArgumentException("ctor failed", e);
130
        } catch (Exception e) {
131
            throw new IllegalArgumentException("no-arg ctor failed", e);
132
        }
133
    }
134
 
135
    /**
136
     * Adds an already instantiated element.
137
     *
138
     * @param elem the SQLElement to add.
139
     */
140
    public synchronized final void addSQLElement(SQLElement elem) {
141
        this.elements.put(elem.getTable(), elem);
142
        this.tableNames.put(elem.getTable().getName(), elem.getTable());
27 ilm 143
        this.byCode.put(elem.getCode(), elem.getTable());
17 ilm 144
        this.byClass.put(elem.getClass(), elem.getTable());
19 ilm 145
        for (final DirectoryListener dl : this.listeners) {
146
            dl.elementAdded(elem);
147
        }
73 ilm 148
        elem.setDirectory(this);
17 ilm 149
    }
150
 
151
    public synchronized final boolean contains(SQLTable t) {
152
        return this.elements.containsKey(t);
153
    }
154
 
155
    public synchronized final SQLElement getElement(SQLTable t) {
156
        return this.elements.get(t);
157
    }
158
 
159
    /**
160
     * Search for a table whose name is <code>tableName</code>.
161
     *
162
     * @param tableName a table name, e.g. "ADRESSE".
163
     * @return the corresponding SQLElement, or <code>null</code> if there is no table named
164
     *         <code>tableName</code>.
165
     * @throws IllegalArgumentException if more than one table match.
166
     */
19 ilm 167
    public synchronized final SQLElement getElement(String tableName) {
17 ilm 168
        return this.getElement(getSoleTable(this.tableNames, tableName));
169
    }
170
 
171
    /**
172
     * Search for an SQLElement whose class is <code>clazz</code>.
173
     *
174
     * @param <S> type of SQLElement
175
     * @param clazz the class.
176
     * @return the corresponding SQLElement, or <code>null</code> if none can be found.
177
     * @throws IllegalArgumentException if there's more than one match.
178
     */
19 ilm 179
    public synchronized final <S extends SQLElement> S getElement(Class<S> clazz) {
17 ilm 180
        return clazz.cast(this.getElement(getSoleTable(this.byClass, clazz)));
181
    }
182
 
27 ilm 183
    public synchronized final SQLElement getElementForCode(String code) {
184
        return this.getElement(getSoleTable(this.byCode, code));
185
    }
186
 
17 ilm 187
    public synchronized final Set<SQLTable> getTables() {
19 ilm 188
        return this.getElementsMap().keySet();
17 ilm 189
    }
190
 
191
    public synchronized final Collection<SQLElement> getElements() {
19 ilm 192
        return this.getElementsMap().values();
17 ilm 193
    }
19 ilm 194
 
195
    public final Map<SQLTable, SQLElement> getElementsMap() {
196
        return Collections.unmodifiableMap(this.elements);
197
    }
198
 
199
    /**
200
     * Remove the passed instance. NOTE: this method only remove the specific instance passed, so
201
     * it's a conditional <code>removeSQLElement(elem.getTable())</code>.
202
     *
203
     * @param elem the instance to remove.
204
     * @see #removeSQLElement(SQLTable)
205
     */
206
    public synchronized void removeSQLElement(SQLElement elem) {
207
        if (this.getElement(elem.getTable()) == elem)
208
            this.removeSQLElement(elem.getTable());
209
    }
210
 
211
    /**
212
     * Remove the element for the passed table.
213
     *
214
     * @param t the table to remove.
215
     * @return the removed element, can be <code>null</code>.
216
     */
217
    public synchronized SQLElement removeSQLElement(SQLTable t) {
218
        final SQLElement elem = this.elements.remove(t);
219
        if (elem != null) {
220
            this.tableNames.remove(elem.getTable().getName(), elem.getTable());
27 ilm 221
            this.byCode.remove(elem.getCode(), elem.getTable());
19 ilm 222
            this.byClass.remove(elem.getClass(), elem.getTable());
223
            // MAYBE only reset neighbours.
224
            for (final SQLElement otherElem : this.elements.values())
225
                otherElem.resetRelationships();
226
            for (final DirectoryListener dl : this.listeners) {
227
                dl.elementRemoved(elem);
228
            }
229
        }
230
        return elem;
231
    }
232
 
73 ilm 233
    public synchronized final void initL18nPackageName(final String baseName) {
234
        if (this.phrasesPkgName != null)
235
            throw new IllegalStateException("Already initialized : " + this.getL18nPackageName());
236
        this.phrasesPkgName = baseName;
237
    }
238
 
239
    public synchronized final String getL18nPackageName() {
240
        return this.phrasesPkgName;
241
    }
242
 
243
    protected synchronized final SQLElementNames getElementNames(final String pkgName, final Locale locale, final Class<?> cl) {
244
        if (pkgName == null)
245
            return null;
246
        final char sep = ' ';
247
        final String key = pkgName + sep + locale.toString();
248
        assert pkgName.indexOf(sep) < 0 : "ambiguous key : " + key;
249
        SQLElementNames res = this.elementNames.get(key);
250
        if (res == null) {
251
            final List<SQLElementNames> l = LOCALIZED_INSTANCES.createInstances(pkgName + "." + BASENAME, locale, cl).get1();
252
            if (!l.isEmpty()) {
253
                for (int i = 1; i < l.size(); i++) {
254
                    l.get(i - 1).setParent(l.get(i));
255
                }
256
                res = l.get(0);
257
            }
258
            this.elementNames.put(key, res);
259
        }
260
        return res;
261
    }
262
 
263
    /**
264
     * Search a name for the passed instance and the {@link TM#getTranslationsLocale() current
265
     * locale}. Search for {@link SQLElementNames} using {@link LocalizedInstances} and
266
     * {@link SQLElementNamesFromXML} first in {@link SQLElement#getL18nPackageName()} then in
267
     * {@link #getL18nPackageName()}. E.g. this could load SQLElementNames_en.class and
268
     * SQLElementNames_en_UK.xml.
269
     *
270
     * @param elem the element.
271
     * @return the name if found, <code>null</code> otherwise.
272
     */
273
    public final Phrase getName(final SQLElement elem) {
274
        final String elemBaseName = elem.getL18nPackageName();
275
        final String pkgName = elemBaseName == null ? getL18nPackageName() : elemBaseName;
276
        final SQLElementNames elementNames = getElementNames(pkgName, TM.getInstance().getTranslationsLocale(), elem.getClass());
277
        return elementNames == null ? null : elementNames.getName(elem);
278
    }
279
 
19 ilm 280
    public synchronized final void addListener(DirectoryListener dl) {
281
        this.listeners.add(dl);
282
    }
283
 
284
    public synchronized final void removeListener(DirectoryListener dl) {
285
        this.listeners.remove(dl);
286
    }
287
 
288
    static public interface DirectoryListener {
289
        void elementAdded(SQLElement elem);
290
 
291
        void elementRemoved(SQLElement elem);
292
    }
17 ilm 293
}