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;
|
57 |
ilm |
17 |
import org.openconcerto.sql.element.SQLElement;
|
17 |
ilm |
18 |
import org.openconcerto.sql.element.SQLElementDirectory;
|
57 |
ilm |
19 |
import org.openconcerto.sql.element.SQLElementDirectory.DirectoryListener;
|
156 |
ilm |
20 |
import org.openconcerto.sql.element.SQLElementNamesFromXML;
|
17 |
ilm |
21 |
import org.openconcerto.sql.model.DBRoot;
|
|
|
22 |
import org.openconcerto.sql.model.SQLField;
|
57 |
ilm |
23 |
import org.openconcerto.sql.model.SQLRow;
|
|
|
24 |
import org.openconcerto.sql.model.SQLRowListRSH;
|
|
|
25 |
import org.openconcerto.sql.model.SQLRowValues;
|
|
|
26 |
import org.openconcerto.sql.model.SQLSchema;
|
|
|
27 |
import org.openconcerto.sql.model.SQLSelect;
|
|
|
28 |
import org.openconcerto.sql.model.SQLSyntax;
|
17 |
ilm |
29 |
import org.openconcerto.sql.model.SQLTable;
|
57 |
ilm |
30 |
import org.openconcerto.sql.model.Where;
|
73 |
ilm |
31 |
import org.openconcerto.sql.model.graph.SQLKey;
|
57 |
ilm |
32 |
import org.openconcerto.sql.utils.SQLCreateTable;
|
|
|
33 |
import org.openconcerto.sql.utils.SQLUtils;
|
|
|
34 |
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
|
17 |
ilm |
35 |
import org.openconcerto.utils.CollectionUtils;
|
73 |
ilm |
36 |
import org.openconcerto.utils.StringUtils;
|
80 |
ilm |
37 |
import org.openconcerto.utils.Tuple2;
|
156 |
ilm |
38 |
import org.openconcerto.utils.i18n.Phrase;
|
17 |
ilm |
39 |
|
|
|
40 |
import java.io.File;
|
|
|
41 |
import java.io.FileInputStream;
|
|
|
42 |
import java.io.IOException;
|
|
|
43 |
import java.io.InputStream;
|
57 |
ilm |
44 |
import java.sql.SQLException;
|
|
|
45 |
import java.util.Arrays;
|
|
|
46 |
import java.util.Collections;
|
17 |
ilm |
47 |
import java.util.HashMap;
|
57 |
ilm |
48 |
import java.util.HashSet;
|
|
|
49 |
import java.util.Iterator;
|
|
|
50 |
import java.util.LinkedList;
|
17 |
ilm |
51 |
import java.util.List;
|
|
|
52 |
import java.util.Map;
|
156 |
ilm |
53 |
import java.util.Map.Entry;
|
57 |
ilm |
54 |
import java.util.Set;
|
|
|
55 |
import java.util.prefs.Preferences;
|
17 |
ilm |
56 |
|
132 |
ilm |
57 |
import org.jdom2.Document;
|
|
|
58 |
import org.jdom2.Element;
|
|
|
59 |
import org.jdom2.JDOMException;
|
|
|
60 |
import org.jdom2.input.SAXBuilder;
|
17 |
ilm |
61 |
|
142 |
ilm |
62 |
import net.jcip.annotations.GuardedBy;
|
|
|
63 |
import net.jcip.annotations.ThreadSafe;
|
|
|
64 |
|
17 |
ilm |
65 |
/**
|
|
|
66 |
* Class to obtain a RowItemDesc from a table and a name.
|
|
|
67 |
*
|
|
|
68 |
* @author ilm 22 nov. 2004
|
|
|
69 |
* @see #getDescFor(SQLTable, String)
|
|
|
70 |
*/
|
83 |
ilm |
71 |
@ThreadSafe
|
17 |
ilm |
72 |
public class SQLFieldTranslator {
|
|
|
73 |
|
73 |
ilm |
74 |
// OK since RowItemDesc is immutable
|
80 |
ilm |
75 |
/**
|
|
|
76 |
* Instance representing "no description".
|
|
|
77 |
*/
|
73 |
ilm |
78 |
public static final RowItemDesc NULL_DESC = new RowItemDesc(null, null);
|
17 |
ilm |
79 |
|
57 |
ilm |
80 |
private static final String METADATA_TABLENAME = SQLSchema.FWK_TABLENAME_PREFIX + "RIV_METADATA";
|
80 |
ilm |
81 |
|
|
|
82 |
/**
|
|
|
83 |
* Use the code and not the table name, since the same table might be used differently at
|
|
|
84 |
* different times (e.g. dropped then recreated some time later with a different purpose). Or
|
|
|
85 |
* conversely, a table might get renamed.
|
|
|
86 |
*/
|
57 |
ilm |
87 |
private static final String ELEM_FIELDNAME = "ELEMENT_CODE";
|
|
|
88 |
private static final String COMP_FIELDNAME = "COMPONENT_CODE";
|
|
|
89 |
private static final String ITEM_FIELDNAME = "ITEM";
|
|
|
90 |
|
|
|
91 |
private static final String DOC_FIELDNAME = "DOCUMENTATION";
|
|
|
92 |
private static final String COL_TITLE_FIELDNAME = "COLUMN_TITLE";
|
|
|
93 |
private static final String LABEL_FIELDNAME = "LABEL";
|
|
|
94 |
|
|
|
95 |
private static final String CORE_VARIANT = "CORE";
|
|
|
96 |
private static final String DB_VARIANT = "DB";
|
|
|
97 |
|
|
|
98 |
static public SQLTable getMetaTable(final DBRoot root) throws SQLException {
|
|
|
99 |
if (!root.contains(METADATA_TABLENAME)) {
|
|
|
100 |
final SQLCreateTable createValueT = new SQLCreateTable(root, METADATA_TABLENAME);
|
|
|
101 |
createValueT.setPlain(true);
|
|
|
102 |
createValueT.addColumn(SQLSyntax.ID_NAME, createValueT.getSyntax().getPrimaryIDDefinition());
|
|
|
103 |
final String nullableVarChar = "varchar(" + Preferences.MAX_KEY_LENGTH + ")";
|
|
|
104 |
createValueT.addColumn(ELEM_FIELDNAME, nullableVarChar);
|
|
|
105 |
createValueT.addColumn(COMP_FIELDNAME, nullableVarChar);
|
|
|
106 |
createValueT.addColumn(ITEM_FIELDNAME, nullableVarChar + " NOT NULL");
|
|
|
107 |
createValueT.addUniqueConstraint("uniq", Arrays.asList(ELEM_FIELDNAME, COMP_FIELDNAME, ITEM_FIELDNAME));
|
|
|
108 |
createValueT.addVarCharColumn(LABEL_FIELDNAME, 256);
|
|
|
109 |
createValueT.addVarCharColumn(COL_TITLE_FIELDNAME, 256);
|
83 |
ilm |
110 |
createValueT.addVarCharColumn(DOC_FIELDNAME, Preferences.MAX_VALUE_LENGTH, true);
|
57 |
ilm |
111 |
root.createTable(createValueT);
|
|
|
112 |
}
|
|
|
113 |
return root.getTable(METADATA_TABLENAME);
|
|
|
114 |
}
|
|
|
115 |
|
73 |
ilm |
116 |
public static RowItemDesc getDefaultDesc(SQLField f) {
|
|
|
117 |
String name = f.getName(), label = null;
|
|
|
118 |
if (f.isPrimaryKey())
|
|
|
119 |
label = "ID";
|
|
|
120 |
else if (f.getTable().getForeignKeys().contains(f))
|
|
|
121 |
name = name.startsWith(SQLKey.PREFIX) ? name.substring(SQLKey.PREFIX.length()) : name;
|
|
|
122 |
if (label == null)
|
|
|
123 |
label = cleanupName(name);
|
|
|
124 |
return new RowItemDesc(label, label);
|
|
|
125 |
}
|
|
|
126 |
|
|
|
127 |
private static String cleanupName(final String name) {
|
|
|
128 |
return StringUtils.firstUpThenLow(name).replace('_', ' ');
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
public static RowItemDesc getDefaultDesc(SQLTable t, final String name) {
|
|
|
132 |
if (t.contains(name))
|
|
|
133 |
return getDefaultDesc(t.getField(name));
|
|
|
134 |
|
|
|
135 |
final String label = cleanupName(name);
|
|
|
136 |
return new RowItemDesc(label, label);
|
|
|
137 |
}
|
|
|
138 |
|
17 |
ilm |
139 |
// Instance members
|
|
|
140 |
|
57 |
ilm |
141 |
// { SQLTable -> { compCode, variant, item -> RowItemDesc }}
|
83 |
ilm |
142 |
@GuardedBy("this")
|
57 |
ilm |
143 |
private final Map<SQLTable, Map<List<String>, RowItemDesc>> translation;
|
156 |
ilm |
144 |
// { element code -> { variant -> name }}
|
|
|
145 |
@GuardedBy("this")
|
|
|
146 |
private final Map<String, Map<String, Phrase>> elementNames;
|
57 |
ilm |
147 |
private final SQLTable table;
|
17 |
ilm |
148 |
private final SQLElementDirectory dir;
|
83 |
ilm |
149 |
@GuardedBy("this")
|
57 |
ilm |
150 |
private final Set<String> unknownCodes;
|
17 |
ilm |
151 |
|
|
|
152 |
{
|
57 |
ilm |
153 |
this.translation = new HashMap<SQLTable, Map<List<String>, RowItemDesc>>();
|
156 |
ilm |
154 |
this.elementNames = new HashMap<>();
|
57 |
ilm |
155 |
this.unknownCodes = new HashSet<String>();
|
17 |
ilm |
156 |
}
|
|
|
157 |
|
|
|
158 |
/**
|
|
|
159 |
* Create a new instance.
|
|
|
160 |
*
|
|
|
161 |
* @param root the default root for tables.
|
142 |
ilm |
162 |
* @param dir the directory where to look for tables not in <code>root</code>.
|
17 |
ilm |
163 |
*/
|
156 |
ilm |
164 |
public SQLFieldTranslator(DBRoot root, SQLElementDirectory dir) {
|
57 |
ilm |
165 |
try {
|
|
|
166 |
this.table = getMetaTable(root);
|
|
|
167 |
} catch (SQLException e) {
|
|
|
168 |
throw new IllegalStateException("Couldn't get the meta table", e);
|
|
|
169 |
}
|
17 |
ilm |
170 |
this.dir = dir;
|
57 |
ilm |
171 |
this.dir.addListener(new DirectoryListener() {
|
|
|
172 |
@Override
|
|
|
173 |
public void elementRemoved(SQLElement elem) {
|
|
|
174 |
// nothing, SQLElement is not required (only needed for code)
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
@Override
|
|
|
178 |
public void elementAdded(SQLElement elem) {
|
83 |
ilm |
179 |
final boolean isUnknown;
|
|
|
180 |
synchronized (SQLFieldTranslator.this) {
|
|
|
181 |
isUnknown = SQLFieldTranslator.this.unknownCodes.contains(elem.getCode());
|
|
|
182 |
}
|
|
|
183 |
if (isUnknown) {
|
57 |
ilm |
184 |
fetch(Collections.singleton(elem.getCode()));
|
|
|
185 |
}
|
|
|
186 |
}
|
|
|
187 |
});
|
|
|
188 |
fetchAndPut(this.table, null);
|
17 |
ilm |
189 |
}
|
|
|
190 |
|
142 |
ilm |
191 |
public final SQLElementDirectory getDirectory() {
|
|
|
192 |
return this.dir;
|
|
|
193 |
}
|
|
|
194 |
|
17 |
ilm |
195 |
/**
|
|
|
196 |
* Add all translations of <code>o</code> to this, note that if a table is present in both this
|
|
|
197 |
* and <code>o</code> its translations won't be changed.
|
|
|
198 |
*
|
|
|
199 |
* @param o another SQLFieldTranslator to add.
|
|
|
200 |
*/
|
|
|
201 |
public void putAll(SQLFieldTranslator o) {
|
83 |
ilm |
202 |
if (o == this)
|
|
|
203 |
return;
|
|
|
204 |
final int thisHash = System.identityHashCode(this);
|
|
|
205 |
final int oHash = System.identityHashCode(o);
|
|
|
206 |
final SQLFieldTranslator o1, o2;
|
|
|
207 |
if (thisHash < oHash) {
|
|
|
208 |
o1 = this;
|
|
|
209 |
o2 = o;
|
|
|
210 |
} else if (thisHash > oHash) {
|
|
|
211 |
o1 = o;
|
|
|
212 |
o2 = this;
|
|
|
213 |
} else {
|
|
|
214 |
throw new IllegalStateException("Hash equal");
|
|
|
215 |
}
|
|
|
216 |
synchronized (o1) {
|
|
|
217 |
synchronized (o2) {
|
|
|
218 |
CollectionUtils.addIfNotPresent(this.translation, o.translation);
|
|
|
219 |
}
|
|
|
220 |
}
|
17 |
ilm |
221 |
}
|
|
|
222 |
|
156 |
ilm |
223 |
public void load(DBRoot b, File file, final SQLElementNamesFromXML elemNames) {
|
|
|
224 |
try (final InputStream ins = new FileInputStream(file)) {
|
|
|
225 |
this.load(b, ins, elemNames);
|
|
|
226 |
} catch (IOException e) {
|
17 |
ilm |
227 |
e.printStackTrace();
|
|
|
228 |
}
|
|
|
229 |
}
|
|
|
230 |
|
|
|
231 |
private static List<Element> getChildren(final Element elem) {
|
|
|
232 |
return elem.getChildren();
|
|
|
233 |
}
|
|
|
234 |
|
156 |
ilm |
235 |
public Tuple2<Set<SQLTable>, Set<String>> load(DBRoot b, InputStream inputStream, final SQLElementNamesFromXML elemNames) {
|
|
|
236 |
return this.load(b, CORE_VARIANT, inputStream, elemNames);
|
57 |
ilm |
237 |
}
|
|
|
238 |
|
80 |
ilm |
239 |
/**
|
|
|
240 |
* Load translations from the passed stream.
|
|
|
241 |
*
|
|
|
242 |
* @param b the default root for tables.
|
|
|
243 |
* @param variant the variant to use.
|
|
|
244 |
* @param inputStream the XML.
|
156 |
ilm |
245 |
* @param elemNames how to load element names.
|
80 |
ilm |
246 |
* @return the loaded tables and the names not found (and thus not loaded).
|
|
|
247 |
*/
|
156 |
ilm |
248 |
public Tuple2<Set<SQLTable>, Set<String>> load(DBRoot b, final String variant, InputStream inputStream, final SQLElementNamesFromXML elemNames) {
|
17 |
ilm |
249 |
if (inputStream == null)
|
|
|
250 |
throw new NullPointerException("inputStream is null");
|
57 |
ilm |
251 |
final Set<SQLTable> res = new HashSet<SQLTable>();
|
80 |
ilm |
252 |
final Set<String> notFound = new HashSet<String>();
|
17 |
ilm |
253 |
try {
|
|
|
254 |
final Document doc = new SAXBuilder().build(inputStream);
|
|
|
255 |
// System.out.println("Base de donnée:"+base);
|
|
|
256 |
for (final Element elem : getChildren(doc.getRootElement())) {
|
|
|
257 |
final String elemName = elem.getName().toLowerCase();
|
80 |
ilm |
258 |
final DBRoot root;
|
|
|
259 |
final List<Element> tableElems;
|
156 |
ilm |
260 |
if (elemName.equals("table") || elemName.equals("element")) {
|
80 |
ilm |
261 |
root = b;
|
|
|
262 |
tableElems = Collections.singletonList(elem);
|
|
|
263 |
} else {
|
156 |
ilm |
264 |
if (elemName.equals("root")) {
|
|
|
265 |
Log.get().warning("Ignoring deprecated <root> element, use element code to refer to tables outside the default root");
|
|
|
266 |
}
|
80 |
ilm |
267 |
root = null;
|
|
|
268 |
tableElems = null;
|
|
|
269 |
}
|
|
|
270 |
if (tableElems != null) {
|
|
|
271 |
for (final Element tableElem : tableElems) {
|
156 |
ilm |
272 |
final Tuple2<String, SQLTable> t = load(root, variant, tableElem, elemNames, true);
|
80 |
ilm |
273 |
if (t.get1() == null) {
|
|
|
274 |
notFound.add(t.get0());
|
|
|
275 |
} else {
|
|
|
276 |
res.add(t.get1());
|
|
|
277 |
}
|
17 |
ilm |
278 |
}
|
|
|
279 |
}
|
|
|
280 |
}
|
80 |
ilm |
281 |
// load() returns null if no table is found
|
|
|
282 |
assert !res.contains(null);
|
17 |
ilm |
283 |
} catch (JDOMException e) {
|
|
|
284 |
e.printStackTrace();
|
|
|
285 |
} catch (IOException e) {
|
|
|
286 |
e.printStackTrace();
|
|
|
287 |
}
|
80 |
ilm |
288 |
return Tuple2.create(res, notFound);
|
17 |
ilm |
289 |
}
|
|
|
290 |
|
156 |
ilm |
291 |
private Tuple2<String, SQLTable> load(DBRoot b, final String variant, final Element tableElem, final SQLElementNamesFromXML elemNames, final boolean lenient) {
|
17 |
ilm |
292 |
final String tableName = tableElem.getAttributeValue("name");
|
156 |
ilm |
293 |
String elemCode = tableElem.getAttributeValue("refid");
|
|
|
294 |
SQLTable table = null;
|
|
|
295 |
// compatibility mode for files without element names
|
|
|
296 |
final boolean compatMode = tableElem.getName().toLowerCase().equals("table");
|
|
|
297 |
if (compatMode) {
|
|
|
298 |
table = this.dir == null || this.dir.getElement(tableName) == null ? b.getTable(tableName) : this.dir.getElement(tableName).getTable();
|
|
|
299 |
if (elemCode == null)
|
|
|
300 |
elemCode = tableName;
|
|
|
301 |
}
|
|
|
302 |
if (table == null && this.dir != null && this.dir.getElementForCode(elemCode) != null)
|
|
|
303 |
table = this.dir.getElementForCode(elemCode).getTable();
|
80 |
ilm |
304 |
if (table != null) {
|
57 |
ilm |
305 |
for (final Element elem : getChildren(tableElem)) {
|
|
|
306 |
final String elemName = elem.getName().toLowerCase();
|
|
|
307 |
if (elemName.equals("field")) {
|
|
|
308 |
this.load(table, SQLElement.DEFAULT_COMP_ID, variant, elem);
|
|
|
309 |
} else if (elemName.equals("component")) {
|
|
|
310 |
final String compCode = elem.getAttributeValue("code");
|
|
|
311 |
for (final Element fieldElem : getChildren(elem)) {
|
|
|
312 |
this.load(table, compCode, variant, fieldElem);
|
|
|
313 |
}
|
|
|
314 |
}
|
17 |
ilm |
315 |
}
|
156 |
ilm |
316 |
try {
|
|
|
317 |
if (!compatMode) {
|
|
|
318 |
final Entry<String, Phrase> phrase = elemNames.createPhrase(tableElem);
|
|
|
319 |
if (phrase != null)
|
|
|
320 |
this.putElementName(this.dir.getElement(table), phrase.getValue(), variant);
|
|
|
321 |
}
|
|
|
322 |
} catch (IOException e) {
|
|
|
323 |
throw new IllegalStateException("Couldn't parse phrase for " + table, e);
|
|
|
324 |
}
|
80 |
ilm |
325 |
} else if (lenient) {
|
|
|
326 |
// allow to supply the union all tables and ignore those that aren't in a given base
|
|
|
327 |
Log.get().config("Ignore loading of inexistent table " + tableName);
|
|
|
328 |
} else {
|
|
|
329 |
throw new IllegalStateException("Table not found : " + tableName);
|
17 |
ilm |
330 |
}
|
80 |
ilm |
331 |
return Tuple2.create(tableName, table);
|
17 |
ilm |
332 |
}
|
|
|
333 |
|
57 |
ilm |
334 |
private void load(final SQLTable table, final String compCode, final String variant, final Element fieldElem) {
|
|
|
335 |
final String name = fieldElem.getAttributeValue("name");
|
|
|
336 |
final String label = fieldElem.getAttributeValue("label");
|
|
|
337 |
final String title = fieldElem.getAttributeValue("titlelabel", label);
|
|
|
338 |
final String documentation = fieldElem.getText();
|
|
|
339 |
this.setDescFor(table, compCode, variant, name, new RowItemDesc(label, title, documentation));
|
|
|
340 |
}
|
|
|
341 |
|
|
|
342 |
public final void fetch(final Set<String> codes) {
|
|
|
343 |
this.fetchAndPut(this.table, codes);
|
|
|
344 |
}
|
|
|
345 |
|
|
|
346 |
private List<SQLRow> fetchOnly(final SQLTable table, final Where w) {
|
83 |
ilm |
347 |
return SQLRowListRSH.execute(new SQLSelect().addSelectStar(table).setWhere(w));
|
57 |
ilm |
348 |
}
|
|
|
349 |
|
|
|
350 |
private void fetchAndPut(final SQLTable table, final Set<String> codes) {
|
|
|
351 |
final Where w;
|
|
|
352 |
if (codes == null) {
|
|
|
353 |
w = null;
|
|
|
354 |
this.removeTranslation((SQLTable) null, null, DB_VARIANT, null);
|
|
|
355 |
} else {
|
|
|
356 |
w = new Where(table.getField(ELEM_FIELDNAME), codes);
|
|
|
357 |
for (final String elementCode : codes)
|
|
|
358 |
this.removeTranslation(this.dir.getElementForCode(elementCode).getTable(), null, DB_VARIANT, null);
|
17 |
ilm |
359 |
}
|
57 |
ilm |
360 |
for (final SQLRow r : fetchOnly(table, w)) {
|
|
|
361 |
final String elementCode = r.getString(ELEM_FIELDNAME);
|
83 |
ilm |
362 |
// needed since tables can be loaded at any time in SQLElementDirectory
|
|
|
363 |
// MAYBE use code as the map key instead of SQLTable
|
|
|
364 |
final SQLElement elem = this.dir.getElementForCode(elementCode);
|
|
|
365 |
if (elem != null) {
|
|
|
366 |
final String componentCode = r.getString(COMP_FIELDNAME);
|
|
|
367 |
final String item = r.getString(ITEM_FIELDNAME);
|
|
|
368 |
final RowItemDesc desc = new RowItemDesc(r.getString(LABEL_FIELDNAME), r.getString(COL_TITLE_FIELDNAME), r.getString(DOC_FIELDNAME));
|
|
|
369 |
synchronized (this) {
|
57 |
ilm |
370 |
putTranslation(elem.getTable(), componentCode, DB_VARIANT, item, desc);
|
83 |
ilm |
371 |
this.unknownCodes.remove(elementCode);
|
|
|
372 |
}
|
|
|
373 |
} else {
|
|
|
374 |
synchronized (this) {
|
57 |
ilm |
375 |
this.unknownCodes.add(elementCode);
|
|
|
376 |
}
|
|
|
377 |
}
|
|
|
378 |
}
|
17 |
ilm |
379 |
}
|
|
|
380 |
|
83 |
ilm |
381 |
private synchronized final Map<List<String>, RowItemDesc> getMap(final SQLTable t) {
|
57 |
ilm |
382 |
Map<List<String>, RowItemDesc> elemMap = this.translation.get(t);
|
|
|
383 |
if (elemMap == null) {
|
|
|
384 |
elemMap = new HashMap<List<String>, RowItemDesc>();
|
|
|
385 |
this.translation.put(t, elemMap);
|
|
|
386 |
}
|
|
|
387 |
return elemMap;
|
17 |
ilm |
388 |
}
|
|
|
389 |
|
83 |
ilm |
390 |
private synchronized final void putTranslation(SQLTable t, String compCode, String variant, String item, RowItemDesc desc) {
|
57 |
ilm |
391 |
if (t == null)
|
|
|
392 |
throw new IllegalArgumentException("Table cannot be null");
|
|
|
393 |
// needed by remove()
|
|
|
394 |
if (compCode == null || variant == null || item == null)
|
|
|
395 |
throw new IllegalArgumentException("Values cannot be null");
|
|
|
396 |
this.getMap(t).put(Arrays.asList(compCode, variant, item), desc);
|
17 |
ilm |
397 |
}
|
|
|
398 |
|
83 |
ilm |
399 |
private synchronized final void removeTranslation(SQLTable t, String compCode, String variant, String name) {
|
57 |
ilm |
400 |
// null means match everything, OK since we test in putTranslation() that we don't contain
|
|
|
401 |
// null values
|
|
|
402 |
if (t == null) {
|
|
|
403 |
for (final Map<List<String>, RowItemDesc> m : this.translation.values()) {
|
|
|
404 |
this.removeTranslation(m, compCode, variant, name);
|
|
|
405 |
}
|
|
|
406 |
} else {
|
|
|
407 |
this.removeTranslation(this.translation.get(t), compCode, variant, name);
|
|
|
408 |
}
|
|
|
409 |
}
|
|
|
410 |
|
83 |
ilm |
411 |
private synchronized void removeTranslation(Map<List<String>, RowItemDesc> m, String compCode, String variant, String name) {
|
57 |
ilm |
412 |
if (m == null)
|
|
|
413 |
return;
|
|
|
414 |
|
|
|
415 |
if (compCode == null && variant == null && name == null) {
|
|
|
416 |
m.clear();
|
|
|
417 |
} else if (compCode != null && variant != null && name != null) {
|
|
|
418 |
m.remove(Arrays.asList(compCode, variant, name));
|
|
|
419 |
} else {
|
|
|
420 |
final Iterator<List<String>> iter = m.keySet().iterator();
|
|
|
421 |
while (iter.hasNext()) {
|
|
|
422 |
final List<String> l = iter.next();
|
|
|
423 |
if ((compCode == null || compCode.equals(l.get(0))) && (variant == null || variant.equals(l.get(1))) && (name == null || name.equals(l.get(2))))
|
|
|
424 |
iter.remove();
|
|
|
425 |
}
|
|
|
426 |
}
|
|
|
427 |
}
|
|
|
428 |
|
83 |
ilm |
429 |
private synchronized final RowItemDesc getTranslation(SQLTable t, String compCode, String variant, String item) {
|
57 |
ilm |
430 |
return this.getMap(t).get(Arrays.asList(compCode, variant, item));
|
|
|
431 |
}
|
|
|
432 |
|
|
|
433 |
private final RowItemDesc getTranslation(SQLTable t, String compCodeArg, List<String> variants, String name) {
|
|
|
434 |
final LinkedList<String> ll = new LinkedList<String>(variants);
|
|
|
435 |
ll.addFirst(DB_VARIANT);
|
|
|
436 |
ll.addLast(CORE_VARIANT);
|
|
|
437 |
|
|
|
438 |
final String[] compCodes;
|
|
|
439 |
if (compCodeArg == SQLElement.DEFAULT_COMP_ID)
|
|
|
440 |
compCodes = new String[] { SQLElement.DEFAULT_COMP_ID };
|
|
|
441 |
else
|
|
|
442 |
compCodes = new String[] { compCodeArg, SQLElement.DEFAULT_COMP_ID };
|
|
|
443 |
for (final String compCode : compCodes) {
|
|
|
444 |
for (final String variant : ll) {
|
|
|
445 |
final RowItemDesc labeledField = this.getTranslation(t, compCode, variant, name);
|
|
|
446 |
if (labeledField != null)
|
|
|
447 |
return labeledField;
|
|
|
448 |
}
|
|
|
449 |
}
|
|
|
450 |
return null;
|
|
|
451 |
}
|
|
|
452 |
|
17 |
ilm |
453 |
public RowItemDesc getDescFor(SQLTable t, String name) {
|
57 |
ilm |
454 |
return getDescFor(t, SQLElement.DEFAULT_COMP_ID, name);
|
|
|
455 |
}
|
|
|
456 |
|
80 |
ilm |
457 |
/**
|
|
|
458 |
* Find the description for the passed item. This method will search for an SQLElement for
|
|
|
459 |
* <code>t</code> to get its {@link SQLElement#getMDPath() variants path}.
|
|
|
460 |
*
|
|
|
461 |
* @param t the table.
|
|
|
462 |
* @param compCode the component code.
|
|
|
463 |
* @param name the item name.
|
|
|
464 |
* @return the first description that matches the parameters, never <code>null</code> but
|
|
|
465 |
* {@link #NULL_DESC}.
|
|
|
466 |
* @see #getDescFor(SQLTable, String, List, String)
|
|
|
467 |
*/
|
57 |
ilm |
468 |
public RowItemDesc getDescFor(SQLTable t, String compCode, String name) {
|
73 |
ilm |
469 |
final SQLElement element = this.dir == null ? null : this.dir.getElement(t);
|
|
|
470 |
final List<String> variants = element == null ? Collections.<String> emptyList() : element.getMDPath();
|
|
|
471 |
return getDescFor(t, compCode, variants, name);
|
57 |
ilm |
472 |
}
|
|
|
473 |
|
|
|
474 |
public RowItemDesc getDescFor(final String elementCode, String compCode, String name) {
|
73 |
ilm |
475 |
return this.getDescFor(elementCode, compCode, null, name);
|
57 |
ilm |
476 |
}
|
|
|
477 |
|
|
|
478 |
public RowItemDesc getDescFor(final String elementCode, String compCode, List<String> variants, String name) {
|
73 |
ilm |
479 |
final SQLElement elem = this.dir.getElementForCode(elementCode);
|
|
|
480 |
if (variants == null)
|
|
|
481 |
variants = elem.getMDPath();
|
|
|
482 |
return this.getDescFor(elem.getTable(), compCode, variants, name);
|
57 |
ilm |
483 |
}
|
|
|
484 |
|
80 |
ilm |
485 |
/**
|
|
|
486 |
* Find the description for the passed item. This method will try {compCode, variant, item}
|
|
|
487 |
* first with the DB variant, then with each passed variant and last with the default variant,
|
|
|
488 |
* until one description is found. If none is found, it will retry with the code
|
|
|
489 |
* {@link SQLElement#DEFAULT_COMP_ID}. If none is found, it will retry all the above after
|
|
|
490 |
* having refreshed its cache from the DB.
|
|
|
491 |
*
|
|
|
492 |
* @param t the table.
|
|
|
493 |
* @param compCodeArg the component code.
|
|
|
494 |
* @param variants the variants to search, not <code>null</code> but can be empty.
|
|
|
495 |
* @param name the item name.
|
|
|
496 |
* @return the first description that matches the parameters, never <code>null</code> but
|
|
|
497 |
* {@link #NULL_DESC}.
|
|
|
498 |
*/
|
57 |
ilm |
499 |
public RowItemDesc getDescFor(SQLTable t, String compCodeArg, List<String> variants, String name) {
|
|
|
500 |
RowItemDesc labeledField = this.getTranslation(t, compCodeArg, variants, name);
|
|
|
501 |
// if nothing found, re-fetch from the DB
|
73 |
ilm |
502 |
if (labeledField == null && this.dir.getElement(t) != null) {
|
57 |
ilm |
503 |
this.fetchAndPut(this.table, Collections.singleton(this.dir.getElement(t).getCode()));
|
|
|
504 |
labeledField = this.getTranslation(t, compCodeArg, variants, name);
|
17 |
ilm |
505 |
}
|
57 |
ilm |
506 |
if (labeledField == null) {
|
73 |
ilm |
507 |
// we didn't find a requested item
|
57 |
ilm |
508 |
Log.get().info("unknown item " + name + " in " + t);
|
|
|
509 |
return NULL_DESC;
|
|
|
510 |
} else {
|
|
|
511 |
return labeledField;
|
|
|
512 |
}
|
17 |
ilm |
513 |
}
|
|
|
514 |
|
|
|
515 |
private RowItemDesc getDescFor(SQLField f) {
|
73 |
ilm |
516 |
return this.getDescFor(f.getTable(), f.getName());
|
17 |
ilm |
517 |
}
|
|
|
518 |
|
|
|
519 |
public String getLabelFor(SQLField f) {
|
|
|
520 |
return this.getDescFor(f).getLabel();
|
|
|
521 |
}
|
|
|
522 |
|
|
|
523 |
public String getTitleFor(SQLField f) {
|
|
|
524 |
return this.getDescFor(f).getTitleLabel();
|
|
|
525 |
}
|
|
|
526 |
|
57 |
ilm |
527 |
public final void setDescFor(final SQLTable table, final String componentCode, final String variant, final String name, final RowItemDesc desc) {
|
|
|
528 |
if (DB_VARIANT.equals(variant))
|
|
|
529 |
throw new IllegalArgumentException("Use storeDescFor()");
|
|
|
530 |
putTranslation(table, componentCode, variant, name, desc);
|
|
|
531 |
}
|
|
|
532 |
|
|
|
533 |
public final void removeDescFor(SQLTable t, String compCode, String variant, String name) {
|
|
|
534 |
if (DB_VARIANT.equals(variant))
|
|
|
535 |
throw new IllegalArgumentException("Cannot remove DB values, use deleteDescFor()");
|
|
|
536 |
this.removeTranslation(t, compCode, variant, name);
|
|
|
537 |
}
|
|
|
538 |
|
|
|
539 |
public final void storeDescFor(final String elementCode, final String componentCode, final String name, final RowItemDesc desc) throws SQLException {
|
|
|
540 |
this.storeDescFor(this.dir.getElementForCode(elementCode).getTable(), componentCode, name, desc);
|
|
|
541 |
}
|
|
|
542 |
|
|
|
543 |
public final void storeDescFor(final SQLTable table, final String componentCode, final String name, final RowItemDesc desc) throws SQLException {
|
|
|
544 |
final String elementCode = this.dir.getElement(table).getCode();
|
|
|
545 |
final Map<String, Object> m = new HashMap<String, Object>();
|
|
|
546 |
m.put(ELEM_FIELDNAME, elementCode);
|
|
|
547 |
m.put(COMP_FIELDNAME, componentCode);
|
|
|
548 |
m.put(ITEM_FIELDNAME, name);
|
|
|
549 |
final SQLTable mdT = this.table;
|
|
|
550 |
SQLUtils.executeAtomic(this.table.getDBSystemRoot().getDataSource(), new SQLFactory<Object>() {
|
|
|
551 |
@Override
|
|
|
552 |
public Object create() throws SQLException {
|
|
|
553 |
final List<SQLRow> existing = fetchOnly(mdT, Where.and(mdT, m));
|
|
|
554 |
assert existing.size() <= 1 : "Unique constraint failed for " + m;
|
|
|
555 |
final SQLRowValues vals;
|
|
|
556 |
if (existing.size() == 0)
|
|
|
557 |
vals = new SQLRowValues(mdT, m);
|
|
|
558 |
else
|
|
|
559 |
vals = existing.get(0).asRowValues();
|
|
|
560 |
vals.put(LABEL_FIELDNAME, desc.getLabel());
|
|
|
561 |
vals.put(COL_TITLE_FIELDNAME, desc.getTitleLabel());
|
|
|
562 |
vals.put(DOC_FIELDNAME, desc.getDocumentation());
|
|
|
563 |
vals.commit();
|
|
|
564 |
putTranslation(table, componentCode, DB_VARIANT, name, desc);
|
|
|
565 |
return null;
|
|
|
566 |
}
|
|
|
567 |
});
|
|
|
568 |
}
|
|
|
569 |
|
|
|
570 |
public final void deleteDescFor(final SQLTable elemTable, final String componentCode, final String name) throws SQLException {
|
|
|
571 |
Where w = null;
|
|
|
572 |
if (elemTable != null)
|
|
|
573 |
w = new Where(this.table.getField(ELEM_FIELDNAME), "=", this.dir.getElement(elemTable).getCode()).and(w);
|
|
|
574 |
if (componentCode != null)
|
|
|
575 |
w = new Where(this.table.getField(COMP_FIELDNAME), "=", componentCode).and(w);
|
|
|
576 |
if (name != null)
|
|
|
577 |
w = new Where(this.table.getField(ITEM_FIELDNAME), "=", name).and(w);
|
|
|
578 |
final String whereString = w == null ? "" : " where " + w.getClause();
|
|
|
579 |
try {
|
|
|
580 |
this.table.getDBSystemRoot().getDataSource().execute("DELETE FROM " + this.table.getSQLName().quote() + whereString);
|
|
|
581 |
} catch (Exception e) {
|
|
|
582 |
throw new SQLException(e);
|
|
|
583 |
}
|
|
|
584 |
this.removeTranslation(elemTable, componentCode, DB_VARIANT, name);
|
|
|
585 |
}
|
156 |
ilm |
586 |
|
|
|
587 |
// ** element names
|
|
|
588 |
|
|
|
589 |
public final Phrase putElementName(final Class<? extends SQLElement> elemCl, final Phrase phr) {
|
|
|
590 |
return this.putElementName(this.getDirectory().getElement(elemCl), phr);
|
|
|
591 |
}
|
|
|
592 |
|
|
|
593 |
public final Phrase putElementName(final SQLElement elem, final Phrase phr) {
|
|
|
594 |
return this.putElementName(elem, phr, CORE_VARIANT);
|
|
|
595 |
}
|
|
|
596 |
|
|
|
597 |
public synchronized final Phrase putElementName(final SQLElement elem, final Phrase phr, final String variant) {
|
|
|
598 |
return this.getElementNameMap(elem, true).put(variant, phr);
|
|
|
599 |
}
|
|
|
600 |
|
|
|
601 |
private synchronized final Map<String, Phrase> getElementNameMap(final SQLElement elem, final boolean create) {
|
|
|
602 |
Map<String, Phrase> elemMap = this.elementNames.get(elem.getCode());
|
|
|
603 |
if (elemMap == null && create) {
|
|
|
604 |
elemMap = new HashMap<>();
|
|
|
605 |
this.elementNames.put(elem.getCode(), elemMap);
|
|
|
606 |
}
|
|
|
607 |
return elemMap;
|
|
|
608 |
}
|
|
|
609 |
|
|
|
610 |
public final Phrase getElementName(final SQLElement elem) {
|
|
|
611 |
return this.getElementName(elem, elem.getMDPath());
|
|
|
612 |
}
|
|
|
613 |
|
|
|
614 |
public final Phrase getElementName(final SQLElement elem, final List<String> variantPath) {
|
|
|
615 |
for (final String variant : variantPath) {
|
|
|
616 |
final Phrase res = this.getElementName(elem, variant);
|
|
|
617 |
if (res != null)
|
|
|
618 |
return res;
|
|
|
619 |
}
|
|
|
620 |
return this.getElementName(elem, CORE_VARIANT);
|
|
|
621 |
}
|
|
|
622 |
|
|
|
623 |
private synchronized final Phrase getElementName(final SQLElement elem, final String variant) {
|
|
|
624 |
final Map<String, Phrase> map = this.getElementNameMap(elem, false);
|
|
|
625 |
return map == null ? null : map.get(variant);
|
|
|
626 |
}
|
17 |
ilm |
627 |
}
|